Introduce a BackendInterface

This change replaces object inheritance with object composition to
enable adding backend other than WikiPage.

Change-Id: Id1a5453a1635f17c67594696d58e1d5c17b9566f
This commit is contained in:
Tristan Cacqueray 2021-09-08 13:13:09 +00:00
parent 2ce45fe35d
commit ffa9604027
1 changed files with 67 additions and 50 deletions

117
statusbot/bot.py Normal file → Executable file
View File

@ -78,7 +78,21 @@ irc.client.ServerConnection.buffer_class.errors = 'replace'
ANTI_FLOOD_SLEEP = 2 ANTI_FLOOD_SLEEP = 2
class WikiPage(object): class BackendInterface(object):
def __init__(self, config):
pass
def login(self):
pass
def load(self) -> str:
pass
def save(self, data: str):
pass
class WikiPage(BackendInterface):
def __init__(self, config): def __init__(self, config):
self.url = config.get('wiki', 'url') self.url = config.get('wiki', 'url')
self.pageid = config.get('wiki', 'pageid') self.pageid = config.get('wiki', 'pageid')
@ -97,11 +111,6 @@ class WikiPage(object):
format='json')) format='json'))
return data['query']['pages'][str(self.pageid)]['revisions'][0]['*'] return data['query']['pages'][str(self.pageid)]['revisions'][0]['*']
def timestamp(self, ts=None):
if not ts:
ts = datetime.datetime.now()
return ts.strftime("%Y-%m-%d %H:%M:%S UTC")
def save(self, text): def save(self, text):
data = self.wiki.call(dict(action='query', data = self.wiki.call(dict(action='query',
prop='info', prop='info',
@ -115,26 +124,36 @@ class WikiPage(object):
token=token)) token=token))
class SuccessPage(WikiPage): def timestamp(ts=None):
def __init__(self, config): if not ts:
super(SuccessPage, self).__init__(config) ts = datetime.datetime.now()
if config.has_option('wiki', 'successpageid'): return ts.strftime("%Y-%m-%d %H:%M:%S UTC")
self.pageid = config.get('wiki', 'successpageid')
else:
self.pageid = None def get_opt(config, section, name):
if config.has_option('wiki', 'successpageurl'): """Return an option value when present or None"""
self.pageurl = config.get('wiki', 'successpageurl') if config.has_option(section, name):
else: return config.get(section, name)
self.pageurl = None return None
class SuccessPage(object):
def __init__(self, config, backend):
self.backend = backend(config)
self.ready = False
if isinstance(self.backend, WikiPage):
self.backend.pageid = get_opt(config, 'wiki', 'successpageid')
self.backend.pageurl = get_opt(config, 'wiki', 'successpageurl')
self.ready = self.backend.pageid is not None
if config.has_option('irclogs', 'url'): if config.has_option('irclogs', 'url'):
self.irclogs_url = config.get('irclogs', 'url') self.irclogs_url = config.get('irclogs', 'url')
else: else:
self.irclogs_url = None self.irclogs_url = None
def log(self, channel, nick, msg): def log(self, channel, nick, msg):
if self.pageid: if self.ready:
self.login() self.backend.login()
ts = self.timestamp() ts = timestamp()
if self.irclogs_url: if self.irclogs_url:
url = self.irclogs_url % { url = self.irclogs_url % {
'chan': urllib.parse.quote(channel), 'chan': urllib.parse.quote(channel),
@ -142,33 +161,29 @@ class SuccessPage(WikiPage):
onchan = "[%s %s]" % (url, channel) onchan = "[%s %s]" % (url, channel)
else: else:
onchan = channel onchan = channel
data = self.load() data = self.backend.load()
current = data.split("\n") current = data.split("\n")
newtext = "%s\n|-\n| %s || %s (on %s) || %s\n%s" % ( newtext = "%s\n|-\n| %s || %s (on %s) || %s\n%s" % (
current[0], ts, nick, onchan, msg, '\n'.join(current[1:])) current[0], ts, nick, onchan, msg, '\n'.join(current[1:]))
self.save(newtext) self.backend.save(newtext)
class ThanksPage(WikiPage): class ThanksPage(object):
def __init__(self, config): def __init__(self, config, backend):
super(ThanksPage, self).__init__(config) self.backend = backend(config)
if config.has_option('wiki', 'thankspageid'): if isinstance(self.backend, WikiPage):
self.pageid = config.get('wiki', 'thankspageid') self.backend.pageid = get_opt(config, 'wiki', 'thankspageid')
else: self.backend.pageurl = get_opt(config, 'wiki', 'thankspageurl')
self.pageid = None self.ready = self.backend.pageid is not None
if config.has_option('wiki', 'thankspageurl'):
self.pageurl = config.get('wiki', 'thankspageurl')
else:
self.pageurl = None
if config.has_option('irclogs', 'url'): if config.has_option('irclogs', 'url'):
self.irclogs_url = config.get('irclogs', 'url') self.irclogs_url = config.get('irclogs', 'url')
else: else:
self.irclogs_url = None self.irclogs_url = None
def log(self, channel, nick, msg): def log(self, channel, nick, msg):
if self.pageid: if self.ready:
self.login() self.backend.login()
ts = self.timestamp() ts = timestamp()
if self.irclogs_url: if self.irclogs_url:
url = self.irclogs_url % { url = self.irclogs_url % {
'chan': urllib.parse.quote(channel), 'chan': urllib.parse.quote(channel),
@ -176,11 +191,11 @@ class ThanksPage(WikiPage):
onchan = "[%s %s]" % (url, channel) onchan = "[%s %s]" % (url, channel)
else: else:
onchan = channel onchan = channel
data = self.load() data = self.backend.load()
current = data.split("\n") current = data.split("\n")
newtext = "%s\n|-\n| %s || %s (on %s) || %s\n%s" % ( newtext = "%s\n|-\n| %s || %s (on %s) || %s\n%s" % (
current[0], ts, nick, onchan, msg, '\n'.join(current[1:])) current[0], ts, nick, onchan, msg, '\n'.join(current[1:]))
self.save(newtext) self.backend.save(newtext)
class UpdateInterface(object): class UpdateInterface(object):
@ -229,12 +244,12 @@ class Tweet(UpdateInterface):
self.update("Everything back to normal") self.update("Everything back to normal")
class StatusPage(WikiPage, UpdateInterface): class StatusPage(UpdateInterface):
alert_re = re.compile(r'{{CI Alert\|(.*?)}}') alert_re = re.compile(r'{{CI Alert\|(.*?)}}')
item_re = re.compile(r'^\* (.*)$') item_re = re.compile(r'^\* (.*)$')
def __init__(self, config): def __init__(self, config, backend):
super(StatusPage, self).__init__(config) self.backend = backend(config)
self.current_alert = None self.current_alert = None
self.items = [] self.items = []
@ -251,7 +266,7 @@ class StatusPage(WikiPage, UpdateInterface):
self.update(clear_alert=True, msg=msg) self.update(clear_alert=True, msg=msg)
def update(self, set_alert=None, clear_alert=None, msg=None): def update(self, set_alert=None, clear_alert=None, msg=None):
self.login() self.backend.login()
self.loadItems() self.loadItems()
if set_alert: if set_alert:
self.setAlert(msg) self.setAlert(msg)
@ -264,7 +279,7 @@ class StatusPage(WikiPage, UpdateInterface):
def loadItems(self): def loadItems(self):
self.current_alert = None self.current_alert = None
self.items = [] self.items = []
text = self.load() text = self.backend.load()
for line in text.split('\n'): for line in text.split('\n'):
m = self.alert_re.match(line) m = self.alert_re.match(line)
if m: if m:
@ -279,10 +294,10 @@ class StatusPage(WikiPage, UpdateInterface):
text += '{{CI Alert|%s}}\n\n' % self.current_alert text += '{{CI Alert|%s}}\n\n' % self.current_alert
for item in self.items: for item in self.items:
text += '* %s\n' % item text += '* %s\n' % item
self.save(text) self.backend.save(text)
def addItem(self, item, ts=None): def addItem(self, item, ts=None):
text = '%s %s' % (self.timestamp(ts=ts), item) text = '%s %s' % (timestamp(ts=ts), item)
self.items.insert(0, text) self.items.insert(0, text)
def setAlert(self, current_alert): def setAlert(self, current_alert):
@ -345,7 +360,7 @@ class BaseStatusBot(SSL, irc.bot.SingleServerIRCBot):
self.successlog.log(channel, nick, text) self.successlog.log(channel, nick, text)
self.send(channel, "%s: Added success to Success page " self.send(channel, "%s: Added success to Success page "
"(%s)" "(%s)"
% (nick, self.successlog.pageurl)) % (nick, self.successlog.backend.pageurl))
def handle_thanks_command(self, channel, nick, msg): def handle_thanks_command(self, channel, nick, msg):
parts = msg.split() parts = msg.split()
@ -354,7 +369,7 @@ class BaseStatusBot(SSL, irc.bot.SingleServerIRCBot):
self.thankslog.log(channel, nick, text) self.thankslog.log(channel, nick, text)
self.send(channel, "%s: Added your thanks to Thanks page " self.send(channel, "%s: Added your thanks to Thanks page "
"(%s)" "(%s)"
% (nick, self.thankslog.pageurl)) % (nick, self.thankslog.backend.pageurl))
def handle_status_command(self, channel, nick, msg): def handle_status_command(self, channel, nick, msg):
parts = msg.split() parts = msg.split()
@ -495,14 +510,16 @@ def _main(configpath):
config.read(configpath) config.read(configpath)
setup_logging(config) setup_logging(config)
backend = WikiPage
channels = ['#' + name.strip() for name in channels = ['#' + name.strip() for name in
config.get('ircbot', 'channels').split(',')] config.get('ircbot', 'channels').split(',')]
nicks = [name.strip() for name in nicks = [name.strip() for name in
config.get('ircbot', 'nicks').split(',')] config.get('ircbot', 'nicks').split(',')]
publishers = [StatusPage(config), publishers = [StatusPage(config, backend),
AlertFile(config)] AlertFile(config)]
successlog = SuccessPage(config) successlog = SuccessPage(config, backend)
thankslog = ThanksPage(config) thankslog = ThanksPage(config, backend)
if config.has_section('twitter'): if config.has_section('twitter'):
publishers.append(Tweet(config)) publishers.append(Tweet(config))