1
0
mirror of http://aero2k.de/t/repos/urlbot-native.git synced 2017-09-06 15:25:38 +02:00

configobj for runtime_config

This commit is contained in:
Thorsten S
2015-12-20 15:24:42 +01:00
parent c29ce94a3d
commit 6c3ba11eb6
9 changed files with 228 additions and 245 deletions

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
.*swp .*swp
*.pyc *.pyc
local_config.ini local_config.ini
persistent_config.ini
# legacy # legacy
local_config.py local_config.py

View File

@@ -2,14 +2,10 @@
""" Common functions for urlbot """ """ Common functions for urlbot """
import html.parser import html.parser
import logging import logging
import os
import pickle
import re import re
import sys
import time import time
import urllib.request import urllib.request
from collections import namedtuple from collections import namedtuple
import config
RATE_NO_LIMIT = 0x00 RATE_NO_LIMIT = 0x00
RATE_GLOBAL = 0x01 RATE_GLOBAL = 0x01
@@ -25,32 +21,6 @@ EVENTLOOP_DELAY = 0.100 # seconds
USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64; rv:31.0) ' \ USER_AGENT = 'Mozilla/5.0 (X11; Linux x86_64; rv:31.0) ' \
'Gecko/20100101 Firefox/31.0 Iceweasel/31.0' 'Gecko/20100101 Firefox/31.0 Iceweasel/31.0'
def conf_save(obj):
with open(config.get('persistent_storage'), 'wb') as config_file:
return pickle.dump(obj, config_file)
def conf_load():
path = config.get('persistent_storage')
if os.path.isfile(path):
with open(path, 'rb') as fd:
fd.seek(0)
return pickle.load(fd)
else:
return {}
def conf_set(key, value):
blob = conf_load()
blob[key] = value
conf_save(blob)
def conf_get(key, default=None):
blob = conf_load()
return blob.get(key, default)
Bucket = namedtuple("BucketConfig", ["history", "period", "max_hist_len"]) Bucket = namedtuple("BucketConfig", ["history", "period", "max_hist_len"])
buckets = { buckets = {

View File

@@ -18,9 +18,11 @@ from validate import Validator
__initialized = False __initialized = False
__config_store = ConfigObj('local_config.ini', configspec='local_config.ini.spec') __config_store = ConfigObj('local_config.ini', configspec='local_config.ini.spec')
runtime_config_store = ConfigObj('persistent_config.ini', configspec='persistent_config.ini.spec')
validator = Validator() validator = Validator()
result = __config_store.validate(validator) result = __config_store.validate(validator)
runtime_config_store.validate(validator)
if not result: if not result:
print('Config file validation failed!') print('Config file validation failed!')
@@ -30,7 +32,7 @@ else:
__config_store.write() __config_store.write()
def get(key): def conf_get(key):
if not __initialized: if not __initialized:
raise RuntimeError("not __initialized") raise RuntimeError("not __initialized")
try: try:
@@ -42,7 +44,32 @@ def get(key):
raise raise
def set(key, val): def conf_set(key, val):
__config_store[key] = val __config_store[key] = val
__config_store.write() __config_store.write()
return None return None
def runtimeconf_set(key, value):
runtime_config_store[key] = value
runtime_config_store.write()
def runtimeconf_get(key, default=None):
if key is None:
return runtime_config_store
else:
return runtime_config_store.get(key, default=default)
def runtimeconf_deepget(key, default=None):
if '.' not in key:
return runtimeconf_get(key, default)
else:
path = key.split('.')
value = runtimeconf_get(path.pop(0))
for p in path:
value = value.get(p)
if value is None:
return None
return value

View File

@@ -3,14 +3,11 @@
import logging import logging
import time import time
import sys import sys
from common import VERSION, EVENTLOOP_DELAY, conf_load from common import VERSION, EVENTLOOP_DELAY
import config import config
from sleekxmpp import ClientXMPP from sleekxmpp import ClientXMPP
# got_hangup = False
class IdleBot(ClientXMPP): class IdleBot(ClientXMPP):
def __init__(self, jid, password, rooms, nick): def __init__(self, jid, password, rooms, nick):
@@ -49,13 +46,13 @@ class IdleBot(ClientXMPP):
# don't talk to yourself # don't talk to yourself
if msg_obj['mucnick'] == self.nick or 'groupchat' != msg_obj['type']: if msg_obj['mucnick'] == self.nick or 'groupchat' != msg_obj['type']:
return False return False
elif msg_obj['body'].startswith(config.get('bot_nickname')) and 'hangup' in msg_obj['body']: elif msg_obj['body'].startswith(config.conf_get('bot_nickname')) and 'hangup' in msg_obj['body']:
self.logger.warn("got 'hangup' from '%s': '%s'" % ( self.logger.warn("got 'hangup' from '%s': '%s'" % (
msg_obj['mucnick'], msg_obj['body'] msg_obj['mucnick'], msg_obj['body']
)) ))
self.hangup() self.hangup()
return False return False
elif msg_obj['mucnick'] in conf_load().get("other_bots", ()): elif msg_obj['mucnick'] in config.runtime_config_store["other_bots"]:
# not talking to the other bot. # not talking to the other bot.
return False return False
else: else:
@@ -71,28 +68,28 @@ class IdleBot(ClientXMPP):
def start(botclass, active=False): def start(botclass, active=False):
logging.basicConfig( logging.basicConfig(
level=config.get('loglevel'), level=config.conf_get('loglevel'),
format=sys.argv[0] + ' %(asctime)s %(levelname).1s %(funcName)-15s %(message)s' format=sys.argv[0] + ' %(asctime)s %(levelname).1s %(funcName)-15s %(message)s'
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.info(VERSION) logger.info(VERSION)
jid = config.get('jid') jid = config.conf_get('jid')
if '/' not in jid: if '/' not in jid:
jid = '%s/%s' % (jid, botclass.__name__) jid = '%s/%s' % (jid, botclass.__name__)
bot = botclass( bot = botclass(
jid=jid, jid=jid,
password=config.get('password'), password=config.conf_get('password'),
rooms=config.get('rooms'), rooms=config.conf_get('rooms'),
nick=config.get('bot_nickname') nick=config.conf_get('bot_nickname')
) )
import plugins import plugins
if active: if active:
plugins.register_all() plugins.register_all()
if plugins.plugin_enabled_get(plugins.command_dsa_watcher): # if plugins.plugin_enabled_get(plugins.command_dsa_watcher):
# first result is lost. # first result is lost.
plugins.command_dsa_watcher(['dsa-watcher', 'crawl']) # plugins.command_dsa_watcher(['dsa-watcher', 'crawl'])
bot.connect() bot.connect()
bot.register_plugin('xep_0045') bot.register_plugin('xep_0045')

View File

@@ -29,7 +29,6 @@ tea_steep_time = integer(default=220)
image_preview = boolean(default=true) image_preview = boolean(default=true)
dsa_watcher_interval = integer(default=900) dsa_watcher_interval = integer(default=900)
last_dsa = integer # TODO broken
loglevel = option('ERROR', WARN', 'INFO', 'DEBUG', default='INFO') loglevel = option('ERROR', WARN', 'INFO', 'DEBUG', default='INFO')
debug_mode = boolean(default=false) debug_mode = boolean(default=false)

View File

@@ -0,0 +1,11 @@
# [main]
other_bots = string_list(default=list())
[plugins]
[[info]]
enabled = boolean(default=true)
last_dsa = integer(default=0) # TODO broken
[user_pref]
[user_records]

View File

@@ -10,8 +10,9 @@ import unicodedata
import urllib.parse import urllib.parse
import urllib.request import urllib.request
from common import conf_load, conf_save, RATE_GLOBAL, RATE_NO_SILENCE, VERSION, RATE_INTERACTIVE, BUFSIZ, \ from common import RATE_GLOBAL, RATE_NO_SILENCE, VERSION, RATE_INTERACTIVE, BUFSIZ, \
USER_AGENT, extract_title, RATE_FUN, RATE_NO_LIMIT, conf_get, RATE_URL USER_AGENT, extract_title, RATE_FUN, RATE_NO_LIMIT, RATE_URL
from config import runtimeconf_get
import config import config
from string_constants import excuses, moin_strings_hi, moin_strings_bye, cakes from string_constants import excuses, moin_strings_hi, moin_strings_bye, cakes
@@ -27,35 +28,26 @@ log = logging.getLogger(__name__)
def plugin_enabled_get(urlbot_plugin): def plugin_enabled_get(urlbot_plugin):
blob = conf_load() is_enabled = config.runtimeconf_deepget('plugins.{}.enabled'.format(urlbot_plugin.plugin_name), None)
if is_enabled is None:
if 'plugin_conf' in blob:
if urlbot_plugin.plugin_name in blob['plugin_conf']:
return blob['plugin_conf'][urlbot_plugin.plugin_name].get('enabled', urlbot_plugin.is_enabled)
return urlbot_plugin.is_enabled return urlbot_plugin.is_enabled
else:
return is_enabled
def plugin_enabled_set(plugin, enabled): def plugin_enabled_set(plugin, enabled):
if config.get('persistent_locked'): if config.conf_get('persistent_locked'):
log.warn("couldn't get exclusive lock") log.warn("couldn't get exclusive lock")
return False
config.set('persistent_locked', True) config.conf_set('persistent_locked', True)
blob = conf_load() # blob = conf_load()
if 'plugin_conf' not in blob: if plugin.plugin_name not in config.runtime_config_store['plugins']:
blob['plugin_conf'] = {} config.runtime_config_store['plugins'][plugin.plugin_name] = {}
if plugin.plugin_name not in blob['plugin_conf']: config.runtime_config_store['plugins'][plugin.plugin_name]['enabled'] = enabled
blob['plugin_conf'][plugin.plugin_name] = {} config.runtime_config_store.write()
config.conf_set('persistent_locked', False)
blob['plugin_conf'][plugin.plugin_name]['enabled'] = enabled
conf_save(blob)
config.set('persistent_locked', False)
return True
def pluginfunction(name, desc, plugin_type, ratelimit_class=RATE_GLOBAL, enabled=True): def pluginfunction(name, desc, plugin_type, ratelimit_class=RATE_GLOBAL, enabled=True):
@@ -105,7 +97,7 @@ def parse_mental_ill(**args):
log.info('sent mental illness reply') log.info('sent mental illness reply')
return { return {
'msg': ( 'msg': (
'''Multiple exclamation/question marks are a sure sign of mental disease, with %s as a living example.''' % 'Multiple exclamation/question marks are a sure sign of mental disease, with %s as a living example.' %
args['reply_user'] args['reply_user']
) )
} }
@@ -180,11 +172,11 @@ def parse_moin(**args):
for w in words: for w in words:
if d.lower() == w.lower(): if d.lower() == w.lower():
if args['reply_user'] in config.get('moin-disabled-user'): if args['reply_user'] in config.conf_get('moin-disabled-user'):
log.info('moin blacklist match') log.info('moin blacklist match')
return return
if args['reply_user'] in config.get('moin-modified-user'): if args['reply_user'] in config.conf_get('moin-modified-user'):
log.info('being "quiet" for %s' % w) log.info('being "quiet" for %s' % w)
return { return {
'msg': '/me %s' % random.choice([ 'msg': '/me %s' % random.choice([
@@ -215,7 +207,7 @@ def parse_latex(**args):
@pluginfunction('me-action', 'reacts to /me.*%{bot_nickname}', ptypes_PARSE, ratelimit_class=RATE_FUN | RATE_GLOBAL) @pluginfunction('me-action', 'reacts to /me.*%{bot_nickname}', ptypes_PARSE, ratelimit_class=RATE_FUN | RATE_GLOBAL)
def parse_slash_me(**args): def parse_slash_me(**args):
if args['data'].lower().startswith('/me') and (config.get('bot_nickname') in args['data'].lower()): if args['data'].lower().startswith('/me') and (config.conf_get('bot_nickname') in args['data'].lower()):
log.info('sent /me reply') log.info('sent /me reply')
me_replys = [ me_replys = [
@@ -324,7 +316,7 @@ def command_source(argv, **_):
log.info('sent source URL') log.info('sent source URL')
return { return {
'msg': 'My source code can be found at %s' % config.get('src-url') 'msg': 'My source code can be found at %s' % config.conf_get('src-url')
} }
@@ -353,7 +345,7 @@ def command_dice(argv, **args):
) )
for i in range(count): for i in range(count):
if args['reply_user'] in config.get('enhanced-random-user'): if args['reply_user'] in config.conf_get('enhanced-random-user'):
rnd = 0 # this might confuse users. good. rnd = 0 # this might confuse users. good.
log.info('sent random (enhanced)') log.info('sent random (enhanced)')
else: else:
@@ -394,19 +386,19 @@ def command_uptime(argv, **args):
if 'uptime' != argv[0]: if 'uptime' != argv[0]:
return return
u = int(config.get('uptime') + time.time()) u = int(config.conf_get('uptime') + time.time())
plural_uptime = 's' plural_uptime = 's'
plural_request = 's' plural_request = 's'
if 1 == u: if 1 == u:
plural_uptime = '' plural_uptime = ''
if 1 == config.get('request_counter'): if 1 == config.conf_get('request_counter'):
plural_request = '' plural_request = ''
log.info('sent statistics') log.info('sent statistics')
return { return {
'msg': args['reply_user'] + (''': happily serving for %d second%s, %d request%s so far.''' % ( 'msg': args['reply_user'] + (''': happily serving for %d second%s, %d request%s so far.''' % (
u, plural_uptime, int(config.get('request_counter')), plural_request)) u, plural_uptime, int(config.conf_get('request_counter')), plural_request))
} }
@@ -443,16 +435,16 @@ def command_info(argv, **args):
questions, please talk to my master %s. I'm rate limited. questions, please talk to my master %s. I'm rate limited.
To make me exit immediately, highlight me with 'hangup' in the message To make me exit immediately, highlight me with 'hangup' in the message
(emergency only, please). For other commands, highlight me with 'help'.''' % ( (emergency only, please). For other commands, highlight me with 'help'.''' % (
config.get('bot_owner'))) config.conf_get('bot_owner')))
} }
@pluginfunction('teatimer', 'sets a tea timer to $1 or currently %d seconds' % config.get('tea_steep_time'), ptypes_COMMAND) @pluginfunction('teatimer', 'sets a tea timer to $1 or currently %d seconds' % config.conf_get('tea_steep_time'), ptypes_COMMAND)
def command_teatimer(argv, **args): def command_teatimer(argv, **args):
if 'teatimer' != argv[0]: if 'teatimer' != argv[0]:
return return
steep = config.get('tea_steep_time') steep = config.conf_get('tea_steep_time')
if len(argv) > 1: if len(argv) > 1:
try: try:
@@ -544,18 +536,16 @@ def command_show_blacklist(argv, **args):
'' if not argv1 else ' (limited to %s)' % argv1 '' if not argv1 else ' (limited to %s)' % argv1
) )
] + [ ] + [
b for b in config.get('url_blacklist') if not argv1 or argv1 in b b for b in config.conf_get('url_blacklist') if not argv1 or argv1 in b
] ]
} }
def usersetting_get(argv, args): def usersetting_get(argv, args):
blob = conf_load()
arg_user = args['reply_user'] arg_user = args['reply_user']
arg_key = argv[1] arg_key = argv[1]
if arg_user not in blob['user_pref']: if arg_user not in config.runtime_config_store['user_pref']:
return { return {
'msg': args['reply_user'] + ': user key not found' 'msg': args['reply_user'] + ': user key not found'
} }
@@ -563,7 +553,7 @@ def usersetting_get(argv, args):
return { return {
'msg': args['reply_user'] + ': %s == %s' % ( 'msg': args['reply_user'] + ': %s == %s' % (
arg_key, arg_key,
'on' if blob['user_pref'][arg_user][arg_key] else 'off' 'on' if config.runtime_config_store['user_pref'][arg_user][arg_key] else 'off'
) )
} }
@@ -592,24 +582,20 @@ def command_usersetting(argv, **args):
# display current value # display current value
return usersetting_get(argv, args) return usersetting_get(argv, args)
if config.get('persistent_locked'): if config.conf_get('persistent_locked'):
return { return {
'msg': args['reply_user'] + ''': couldn't get exclusive lock''' 'msg': args['reply_user'] + ''': couldn't get exclusive lock'''
} }
config.set('persistent_locked', True) config.conf_set('persistent_locked', True)
blob = conf_load()
if 'user_pref' not in blob: if arg_user not in config.runtime_config_store['user_pref']:
blob['user_pref'] = {} config.runtime_config_store['user_pref'][arg_user] = {}
if arg_user not in blob['user_pref']: config.runtime_config_store['user_pref'][arg_user][arg_key] = 'on' == arg_val
blob['user_pref'][arg_user] = {}
blob['user_pref'][arg_user][arg_key] = 'on' == arg_val config.runtime_config_store.write()
config.conf_set('persistent_locked', False)
conf_save(blob)
config.set('persistent_locked', False)
# display value written to db # display value written to db
return usersetting_get(argv, args) return usersetting_get(argv, args)
@@ -824,24 +810,20 @@ def command_record(argv, **args):
message = '%s (%s): ' % (args['reply_user'], time.strftime('%F.%T')) message = '%s (%s): ' % (args['reply_user'], time.strftime('%F.%T'))
message += ' '.join(argv[2:]) message += ' '.join(argv[2:])
if config.get('persistent_locked'): if config.conf_get('persistent_locked'):
return { return {
'msg': "%s: couldn't get exclusive lock" % args['reply_user'] 'msg': "%s: couldn't get exclusive lock" % args['reply_user']
} }
config.set('persistent_locked', True) config.conf_set('persistent_locked', True)
blob = conf_load()
if 'user_records' not in blob: if target_user not in config.runtime_config_store['user_records']:
blob['user_records'] = {} config.runtime_config_store['user_records'][target_user] = []
if target_user not in blob['user_records']: config.runtime_config_store['user_records'][target_user].append(message)
blob['user_records'][target_user] = []
blob['user_records'][target_user].append(message) config.runtime_config_store.write()
config.conf_set('persistent_locked', False)
conf_save(blob)
config.set('persistent_locked', False)
return { return {
'msg': '%s: message saved for %s' % (args['reply_user'], target_user) 'msg': '%s: message saved for %s' % (args['reply_user'], target_user)
@@ -862,97 +844,102 @@ def command_show_recordlist(argv, **args):
'%s: offline records%s: %s' % ( '%s: offline records%s: %s' % (
args['reply_user'], args['reply_user'],
'' if not argv1 else ' (limited to %s)' % argv1, '' if not argv1 else ' (limited to %s)' % argv1,
', '.join([ ', '.join(
'%s (%d)' % (key, len(val)) for key, val in conf_load().get('user_records').items() [
'%s (%d)' % (key, len(val)) for key, val in config.runtime_config_store['user_records'].items()
if not argv1 or argv1.lower() in key.lower() if not argv1 or argv1.lower() in key.lower()
]) ]
)
) )
} }
@pluginfunction('dsa-watcher', 'automatically crawls for newly published Debian Security Announces', ptypes_COMMAND, # TODO: disabled until rewrite
ratelimit_class=RATE_NO_SILENCE) # @pluginfunction('dsa-watcher', 'automatically crawls for newly published Debian Security Announces', ptypes_COMMAND,
def command_dsa_watcher(argv, **_): # ratelimit_class=RATE_NO_SILENCE)
""" # def command_dsa_watcher(argv, **_):
TODO: rewrite so that a last_dsa_date is used instead, then all DSAs since then printed and the date set to now() # """
""" # TODO: rewrite so that a last_dsa_date is used instead, then all DSAs since then printed and the date set to now()
if 'dsa-watcher' != argv[0]: # """
return # if 'dsa-watcher' != argv[0]:
# return
if 2 != len(argv): #
msg = 'wrong number of arguments' # if 2 != len(argv):
log.warn(msg) # msg = 'wrong number of arguments'
return {'msg': msg} # log.warn(msg)
# return {'msg': msg}
if 'crawl' == argv[1]: #
out = [] # if 'crawl' == argv[1]:
dsa = conf_load().get('plugin_conf', {}).get('last_dsa', 1000) # out = []
# # TODO: this is broken... the default should neither be part of the code,
url = 'https://security-tracker.debian.org/tracker/DSA-%d-1' % dsa # # but rather be determined at runtime (like "latest" or similar)
# dsa = config.runtime_config_store.deepget('plugins.last_dsa', 1000)
try: #
request = urllib.request.Request(url) # url = 'https://security-tracker.debian.org/tracker/DSA-%d-1' % dsa
request.add_header('User-Agent', USER_AGENT) #
response = urllib.request.urlopen(request) # try:
html_text = response.read(BUFSIZ) # ignore more than BUFSIZ # request = urllib.request.Request(url)
except Exception as e: # request.add_header('User-Agent', USER_AGENT)
err = e # response = urllib.request.urlopen(request)
if '404' not in str(err): # html_text = response.read(BUFSIZ) # ignore more than BUFSIZ
msg = 'error for %s: %s' % (url, err) # except Exception as e:
log.warn(msg) # err = e
out.append(msg) # if '404' not in str(err):
else: # msg = 'error for %s: %s' % (url, err)
if str != type(html_text): # log.warn(msg)
html_text = str(html_text) # out.append(msg)
# else:
result = re.match(r'.*?Description</b></td><td>(.*?)</td>.*?', html_text, re.S | re.M | re.IGNORECASE) # if str != type(html_text):
# html_text = str(html_text)
package = 'error extracting package name' #
if result: # result = re.match(r'.*?Description</b></td><td>(.*?)</td>.*?', html_text, re.S | re.M | re.IGNORECASE)
package = result.groups()[0] #
# package = 'error extracting package name'
if config.get('persistent_locked'): # if result:
msg = "couldn't get exclusive lock" # package = result.groups()[0]
log.warn(msg) #
out.append(msg) # if config.get('persistent_locked'):
else: # msg = "couldn't get exclusive lock"
config.set('persistent_locked', True) # log.warn(msg)
blob = conf_load() # out.append(msg)
# else:
if 'plugin_conf' not in blob: # config.set('persistent_locked', True)
blob['plugin_conf'] = {} # blob = conf_load()
#
if 'last_dsa' not in blob['plugin_conf']: # if 'plugin_conf' not in blob:
blob['plugin_conf']['last_dsa'] = 3308 # FIXME: fixed value # blob['plugin_conf'] = {}
#
blob['plugin_conf']['last_dsa'] += 1 # if 'last_dsa' not in blob['plugin_conf']:
# blob['plugin_conf']['last_dsa'] = 3308 # FIXME: fixed value
conf_save(blob) #
config.set('persistent_locked', False) # blob['plugin_conf']['last_dsa'] += 1
#
msg = ( # runtimeconf_save(blob)
'new Debian Security Announce found (%s): %s' % (str(package).replace(' - security update', ''), url)) # config.set('persistent_locked', False)
out.append(msg) #
# msg = (
log.info('no dsa for %d, trying again...' % dsa) # 'new Debian Security Announce found (%s): %s' % (str(package).replace(' - security update', ''), url))
# that's good, no error, just 404 -> DSA not released yet # out.append(msg)
#
crawl_at = time.time() + config.get('dsa_watcher_interval') # log.info('no dsa for %d, trying again...' % dsa)
# register_event(crawl_at, command_dsa_watcher, (['dsa-watcher', 'crawl'],)) # # that's good, no error, just 404 -> DSA not released yet
#
msg = 'next crawl set to %s' % time.strftime('%F.%T', time.localtime(crawl_at)) # crawl_at = time.time() + config.get('dsa_watcher_interval')
out.append(msg) # # register_event(crawl_at, command_dsa_watcher, (['dsa-watcher', 'crawl'],))
return { #
'msg': out, # msg = 'next crawl set to %s' % time.strftime('%F.%T', time.localtime(crawl_at))
'event': { # out.append(msg)
'time': crawl_at, # return {
'command': (command_dsa_watcher, (['dsa-watcher', 'crawl'],)) # 'msg': out,
} # 'event': {
} # 'time': crawl_at,
else: # 'command': (command_dsa_watcher, (['dsa-watcher', 'crawl'],))
msg = 'wrong argument' # }
log.warn(msg) # }
return {'msg': msg} # else:
# msg = 'wrong argument'
# log.warn(msg)
# return {'msg': msg}
@pluginfunction("provoke-bots", "search for other bots", ptypes_COMMAND) @pluginfunction("provoke-bots", "search for other bots", ptypes_COMMAND)
@@ -972,13 +959,9 @@ def recognize_bots(**args):
) )
def _add_to_list(username, message): def _add_to_list(username, message):
blob = conf_load() if username not in config.runtime_config_store['other_bots']:
config.runtime_config_store['other_bots'].append(username)
if 'other_bots' not in blob: config.runtime_config_store.write()
blob['other_bots'] = []
if username not in blob['other_bots']:
blob['other_bots'].append(username)
conf_save(blob)
return { return {
'event': { 'event': {
'time': time.time() + 3, 'time': time.time() + 3,
@@ -1000,15 +983,12 @@ def remove_from_botlist(argv, **args):
if len(argv) != 2: if len(argv) != 2:
return {'msg': "wrong number of arguments!"} return {'msg': "wrong number of arguments!"}
blob = conf_load() if args['reply_user'] != config.conf_get('bot_owner'):
return {'msg': "only %s may do this!" % config.conf_get('bot_owner')}
if args['reply_user'] != config.get('bot_owner'): if argv[1] in config.runtime_config_store['other_bots']:
return {'msg': "only %s may do this!" % config.get('bot_owner')} config.runtime_config_store['other_bots'].remove(argv[1])
config.runtime_config_store.write()
if argv[1] in blob.get('other_bots', ()):
blob['other_bots'].pop(blob['other_bots'].index(argv[1]))
conf_save(blob)
return {'msg': '%s was removed from the botlist.' % argv[1]} return {'msg': '%s was removed from the botlist.' % argv[1]}
else: else:
return False return False
@@ -1019,14 +999,14 @@ def set_status(argv, **args):
if 'set-status' != argv[0] or len(argv) != 2: if 'set-status' != argv[0] or len(argv) != 2:
return return
if argv[1] == 'mute' and args['reply_user'] == config.get('bot_owner'): if argv[1] == 'mute' and args['reply_user'] == config.conf_get('bot_owner'):
return { return {
'presence': { 'presence': {
'status': 'xa', 'status': 'xa',
'msg': 'I\'m muted now. You can unmute me with "%s: set_status unmute"' % config.get("bot_nickname") 'msg': 'I\'m muted now. You can unmute me with "%s: set_status unmute"' % config.conf_get("bot_nickname")
} }
} }
elif argv[1] == 'unmute' and args['reply_user'] == config.get('bot_owner'): elif argv[1] == 'unmute' and args['reply_user'] == config.conf_get('bot_owner'):
return { return {
'presence': { 'presence': {
'status': None, 'status': None,
@@ -1037,7 +1017,7 @@ def set_status(argv, **args):
@pluginfunction('reset-jobs', "reset joblist", ptypes_COMMAND, ratelimit_class=RATE_NO_LIMIT) @pluginfunction('reset-jobs', "reset joblist", ptypes_COMMAND, ratelimit_class=RATE_NO_LIMIT)
def reset_jobs(argv, **args): def reset_jobs(argv, **args):
if 'reset-jobs' != argv[0] or args['reply_user'] != config.get('bot_owner'): if 'reset-jobs' != argv[0] or args['reply_user'] != config.conf_get('bot_owner'):
return return
else: else:
joblist.clear() joblist.clear()
@@ -1047,7 +1027,7 @@ def reset_jobs(argv, **args):
@pluginfunction('resolve-url-title', 'extract titles from urls', ptypes_PARSE, ratelimit_class=RATE_URL) @pluginfunction('resolve-url-title', 'extract titles from urls', ptypes_PARSE, ratelimit_class=RATE_URL)
def resolve_url_title(**args): def resolve_url_title(**args):
user = args['reply_user'] user = args['reply_user']
user_pref_nospoiler = conf_get('user_pref', {}).get(user, {}).get('spoiler', False) user_pref_nospoiler = runtimeconf_get('user_pref', {}).get(user, {}).get('spoiler', False)
if user_pref_nospoiler: if user_pref_nospoiler:
log.info('nospoiler in userconf') log.info('nospoiler in userconf')
return return
@@ -1093,7 +1073,7 @@ def resolve_url_title(**args):
title = title.strip() title = title.strip()
message = 'Title: %s' % title message = 'Title: %s' % title
elif 1 == status: elif 1 == status:
if config.get('image_preview'): if config.conf_get('image_preview'):
# of course it's fake, but it looks interesting at least # of course it's fake, but it looks interesting at least
char = r""",._-+=\|/*`~"'""" char = r""",._-+=\|/*`~"'"""
message = 'No text but %s, 1-bit ASCII art preview: [%c]' % ( message = 'No text but %s, 1-bit ASCII art preview: [%c]' % (

View File

@@ -1,5 +1,7 @@
""" """
To be executed with nose To be executed with nose
TODO: test all plugins, maybe declare their sample input somewhere near the code
""" """
import unittest import unittest
import time import time

View File

@@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
The URLBot - ready to strive for desaster in YOUR jabber MUC The URLBot - ready to strive for desaster in YOUR jabber MUC
@@ -8,14 +8,13 @@ import sys
import time import time
from common import ( from common import (
conf_load, conf_save,
rate_limit_classes, rate_limit_classes,
RATE_GLOBAL, RATE_GLOBAL,
RATE_CHAT, RATE_CHAT,
RATE_EVENT, RATE_EVENT,
rate_limit, rate_limit,
conf_set
) )
from config import runtimeconf_set
from idlebot import IdleBot, start from idlebot import IdleBot, start
from plugins import ( from plugins import (
plugins as plugin_storage, plugins as plugin_storage,
@@ -74,10 +73,10 @@ class UrlBot(IdleBot):
# TODO: move this to a undirected plugin, maybe new plugin type # TODO: move this to a undirected plugin, maybe new plugin type
arg_user = msg_obj['muc']['nick'] arg_user = msg_obj['muc']['nick']
arg_user_key = arg_user.lower() arg_user_key = arg_user.lower()
blob_userrecords = conf_load().get('user_records', {}) user_records = config.runtime_config_store['user_records']
if arg_user_key in blob_userrecords: if arg_user_key in user_records:
records = blob_userrecords[arg_user_key] records = user_records[arg_user_key]
if not records: if not records:
return return
@@ -96,21 +95,16 @@ class UrlBot(IdleBot):
self.logger.info('sent %d offline records to room %s', self.logger.info('sent %d offline records to room %s',
len(records), msg_obj['from'].bare) len(records), msg_obj['from'].bare)
if config.get('persistent_locked'): if config.conf_get('persistent_locked'):
self.logger.warning("couldn't get exclusive lock") self.logger.warning("couldn't get exclusive lock")
return False return False
config.set('persistent_locked', True) config.conf_set('persistent_locked', True)
blob = conf_load()
if 'user_records' not in blob: user_records['user_records'].pop(arg_user_key)
blob['user_records'] = {} config.runtime_config_store.write()
if arg_user_key in blob['user_records']: config.conf_set('persistent_locked', False)
blob['user_records'].pop(arg_user_key)
conf_save(blob)
config.set('persistent_locked', False)
# @rate_limited(10) # @rate_limited(10)
def send_reply(self, message, msg_obj=None): def send_reply(self, message, msg_obj=None):
@@ -121,7 +115,7 @@ class UrlBot(IdleBot):
self.logger.warning("I'm muted! (status: %s)", self.show) self.logger.warning("I'm muted! (status: %s)", self.show)
return return
config.set('request_counter', config.get('request_counter') + 1) config.conf_set('request_counter', config.conf_get('request_counter') + 1)
if str is not type(message): if str is not type(message):
message = '\n'.join(message) message = '\n'.join(message)
@@ -146,20 +140,19 @@ class UrlBot(IdleBot):
@cached @cached
def get_bots_present(room): def get_bots_present(room):
other_bots = conf_load().get("other_bots", ()) other_bots = config.runtime_config_store["other_bots"]
if not other_bots:
return False
users = self.plugin['xep_0045'].getRoster(room) users = self.plugin['xep_0045'].getRoster(room)
return set(users).intersection(set(other_bots)) return set(users).intersection(set(other_bots))
def _prevent_panic(message, room): def _prevent_panic(message, room):
"""check other bots, add nospoiler with urls""" """check other bots, add nospoiler with urls"""
if 'http' in message: if 'http' in message and get_bots_present(room):
other_bots = conf_load().get("other_bots", ())
users = self.plugin['xep_0045'].getRoster(room)
if set(users).intersection(set(other_bots)):
message = '(nospoiler) %s' % message message = '(nospoiler) %s' % message
return message return message
if config.get('debug_mode'): if config.conf_get('debug_mode'):
print(message) print(message)
else: else:
if msg_obj: if msg_obj:
@@ -202,8 +195,11 @@ class UrlBot(IdleBot):
self.logger.info('no spoiler for: ' + content) self.logger.info('no spoiler for: ' + content)
return return
try:
self.data_parse_commands(msg_obj) self.data_parse_commands(msg_obj)
self.data_parse_other(msg_obj) self.data_parse_other(msg_obj)
except Exception as e:
self.logger.exception(e)
def data_parse_commands(self, msg_obj): def data_parse_commands(self, msg_obj):
""" """
@@ -219,7 +215,7 @@ class UrlBot(IdleBot):
return None return None
# don't reply if beginning of the text matches bot_nickname # don't reply if beginning of the text matches bot_nickname
if not data.startswith(config.get('bot_nickname')): if not data.startswith(config.conf_get('bot_nickname')):
return None return None
if 'hangup' in data: if 'hangup' in data:
@@ -296,7 +292,7 @@ class UrlBot(IdleBot):
if 'presence' in action: if 'presence' in action:
presence = action['presence'] presence = action['presence']
conf_set('presence', presence) runtimeconf_set('presence', presence)
self.status = presence.get('msg') self.status = presence.get('msg')
self.show = presence.get('status') self.show = presence.get('status')