diff --git a/.gitignore b/.gitignore index 6a18ad4..5fddf1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +settings.cfg + # ---> Python # Byte-compiled / optimized / DLL files __pycache__/ diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..9d60ade --- /dev/null +++ b/bot.py @@ -0,0 +1,143 @@ +import configparser +import logging +import os +import re +import string + +import dateutil.parser +import irc.bot +import requests + + +config = configparser.ConfigParser() +config.read('settings.cfg') + + +log = logging.getLogger('irc.client') +log.addHandler(logging.StreamHandler()) + +if os.getenv('DEBUG', False): + log.setLevel(logging.DEBUG) + + +QUOTE_ADDED_PATTERN = re.compile(r'''^ + (?P.+)\s+-->\s+ + Sweet!\s+Thanks\s+for\s+the\s+quote!\s+ + \#(?P\d+):\s+ + "(?P.+)"\s+ + \[(?P.+)\]\s+ + \[(?P.+)\]$''', re.VERBOSE) + +# TODO: quote_edited, quote_removed + + +class TwitchBot(irc.bot.SingleServerIRCBot): + def __init__(self): + self.actions = [ + (re.compile(r'^!lastquote$'), self.last_quote), + (re.compile(r'^!findquote\s+(?P")?(?P.+)(?(q)")$'), self.find_quote), + (QUOTE_ADDED_PATTERN, self.add_quote), + ] + self.server = config['IRC'].get('server', 'irc.chat.twitch.tv') + self.port = config['IRC'].getint('port', 6667) + self.nickname = config['IRC'].get('nickname') + self.channel = '#{0}'.format(config['IRC'].get('channel')) + self.token = config['Twitch'].get('token') + self.api_url = config['Quotes'].get('api_url') + self.api_key = config['Quotes'].get('api_key') + print('Connecting to {0}:{1}'.format(self.server, self.port)) + super(TwitchBot, self).__init__([(self.server, self.port, self.token)], + self.nickname, self.nickname) + + def on_welcome(self, connection, event): + connection.cap('REQ', ':twitch.tv/membership') + connection.cap('REQ', ':twitch.tv/tags') + connection.cap('REQ', ':twitch.tv/commands') + print('Joining {0}'.format(self.channel)) + connection.join(self.channel) + + def on_join(self, connection, event): + print('Joined {0}'.format(event.target)) + + def on_pubmsg(self, connection, event): + self.process_message(connection, event) + + def on_whisper(self, connection, event): + self.process_message(connection, event) + + def process_message(self, connection, event): + sources = [t['value'] for t in event.tags if t['key'] == 'display-name'] + source = sources.pop() + message = ''.join([c for c in event.arguments[0] if c in string.printable]) + for pattern, action in self.actions: + m = pattern.match(message) + if m: + action(connection, source, **m.groupdict()) + + def get(self, params): + r = requests.get('{0}/quotes'.format(self.api_url), params=params) + r.raise_for_status() + return r.json() + + def post(self, data): + r = requests.post('{0}/quotes'.format(self.api_url), data=data, + headers={'X-Quotes-API-Key': self.api_key}) + r.raise_for_status() + return r.json() + + def delete(self): + r = requests.post('{0}/quotes'.format(self.api_url), + headers={'X-Quotes-API-Key': self.api_key}) + r.raise_for_status() + return r.json() + + def last_quote(self, connection, source, **kwargs): + try: + quotes = self.get(dict( + sort_by='id', + sort_order='desc', + page_size=1)) + quote = quotes[0] + except (requests.exceptions.HTTPError, IndexError): + msg = 'Sorry @{0}, no quotes found'.format(source) + else: + msg = '!quote {0}'.format(quote['id']) + connection.privmsg(self.channel, msg) + + def find_quote(self, connection, source, filter, **kwargs): + if len(filter) < 3: + msg = 'Sorry @{0}, the search phrase is too short'.format(source) + connection.privmsg(self.channel, msg) + return + try: + quotes = self.get(dict( + filter=filter, + sort_by='id', + sort_order='desc', + page_size=1)) + quote = quotes[0] + except (requests.exceptions.HTTPError, IndexError): + msg = 'Sorry @{0}, no quotes found'.format(source) + else: + msg = '!quote {0}'.format(quote['id']) + connection.privmsg(self.channel, msg) + + def add_quote(self, connection, source, user, id, text, game, date, **kwargs): + print('Adding quote {0}: {1}'.format(id, text)) + try: + self.post(dict( + id=int(id) + 10000, # FIXME + date=dateutil.parser.parse(date, dayfirst=True).date().isoformat(), + game=game, + text=text)) + except requests.exceptions.HTTPError as e: + print('Failed to add quote: ', e) + + +def main(): + bot = TwitchBot() + bot.start() + + +if __name__ == "__main__": + main()