diff --git a/bot.py b/bot.py index abcc5e2..e31c4f0 100644 --- a/bot.py +++ b/bot.py @@ -8,6 +8,8 @@ import dateutil.parser import irc.bot import requests +from twitch import Twitch + config = configparser.ConfigParser() config.read('settings.cfg') @@ -33,10 +35,13 @@ QUOTE_ADDED_PATTERN = re.compile(r'''^ class TwitchBot(irc.bot.SingleServerIRCBot): def __init__(self): - self.actions = [ + self.patterns = [ + (QUOTE_ADDED_PATTERN, self.add_quote), + ] + self.commands = [ (re.compile(r'^!lastquote$'), self.last_quote), (re.compile(r'^!findquote\s+(?P")?(?P.+)(?(q)")$'), self.find_quote), - (QUOTE_ADDED_PATTERN, self.add_quote), + (re.compile(r'^!sync(\s+(?P.+))?$'), self.sync), ] self.server = config['IRC'].get('server', 'irc.chat.twitch.tv') self.port = config['IRC'].getint('port', 6667) @@ -45,7 +50,7 @@ class TwitchBot(irc.bot.SingleServerIRCBot): 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)) + log.info('Connecting to %s:%d', self.server, self.port) super(TwitchBot, self).__init__([(self.server, self.port, self.token)], self.nickname, self.nickname) @@ -53,11 +58,11 @@ class TwitchBot(irc.bot.SingleServerIRCBot): connection.cap('REQ', ':twitch.tv/membership') connection.cap('REQ', ':twitch.tv/tags') connection.cap('REQ', ':twitch.tv/commands') - print('Joining {0}'.format(self.channel)) + log.info('Joining %s', self.channel) connection.join(self.channel) def on_join(self, connection, event): - print('Joined {0}'.format(event.target)) + log.info('Joined %s', event.target) def on_pubmsg(self, connection, event): self.process_message(connection, event) @@ -69,7 +74,7 @@ class TwitchBot(irc.bot.SingleServerIRCBot): sources = [t['value'] for t in event.tags if t['key'] == 'display-name'] message = ''.join([c for c in event.arguments[0] if c in string.printable]) message = message.rstrip() - for pattern, action in self.actions: + for pattern, action in self.patterns + self.commands: m = pattern.match(message) if m: action(connection, sources[0], **m.groupdict()) @@ -122,8 +127,35 @@ class TwitchBot(irc.bot.SingleServerIRCBot): msg = '!quote {0}'.format(quote['id']) connection.privmsg(self.channel, msg) + def sync(self, connection, source, since=None, **kwargs): + if since is None: + try: + quotes = self.get(dict( + sort_by='id', + sort_order='desc', + page_size=1)) + quote = quotes[0] + except requests.exceptions.HTTPError as e: + log.error('Failed to get quotes: %s', str(e)) + return + except IndexError: + log.error('No quotes available') + return + else: + since = quote['date'] + api_url = config['Twitch'].get('api_url') + client_id = config['Twitch'].get('client_id') + user_id = config['Twitch'].get('target_user_id') + since = dateutil.parser.parse(since).date() + messages = Twitch(api_url, client_id, log).get_messages(user_id, since) + for message in messages: + for pattern, action in self.patterns: + m = pattern.match(message) + if m: + action(connection, m.group('user'), **m.groupdict()) + def add_quote(self, connection, source, user, id, text, game, date, **kwargs): - print('Adding quote {0}: {1}'.format(id, text)) + log.info('Adding quote %s: %s', id, text) try: self.post(dict( id=int(id) + 10000, # FIXME @@ -131,7 +163,7 @@ class TwitchBot(irc.bot.SingleServerIRCBot): game=game, text=text)) except requests.exceptions.HTTPError as e: - print('Failed to add quote: ', e) + log.error('Failed to add quote: %s', str(e)) def main(): diff --git a/twitch.py b/twitch.py new file mode 100644 index 0000000..03e5f04 --- /dev/null +++ b/twitch.py @@ -0,0 +1,57 @@ +import dateutil.parser +import requests + + +class Twitch(object): + def __init__(self, api_url, client_id, log=None): + self.api_url = api_url + self.client_id = client_id + self.log = log + + def _get_videos(self, user_id): + def request(offset, limit): + url = '{0}/channels/{1}/videos'.format(self.api_url, user_id) + params = dict(client_id=self.client_id, offset=offset, limit=limit) + r = requests.get(url, params=params) + r.raise_for_status() + return r.json() + result = [] + data = request(0, 1) + total = data.get('_total', 0) + limit = 100 + for offset in range(0, total, limit): + data = request(offset, limit) + for vid in data.get('videos', []): + result.append(dict( + id=int(vid['_id'].lstrip('v')), + title=vid['title'], + date=dateutil.parser.parse(vid['recorded_at']).date())) + return result + + def _get_comments(self, video_id): + def request(cursor): + url = '{0}/videos/{1}/comments'.format(self.api_url, video_id) + params = dict(client_id=self.client_id, cursor=cursor) + r = requests.get(url, params=params) + r.raise_for_status() + return r.json() + result = [] + cursor = '' + while True: + data = request(cursor) + for comment in data.get('comments', []): + result.append(comment['message']['body']) + cursor = data.get('_next') + if not cursor: + break + return result + + def get_messages(self, user_id, since): + videos = self._get_videos(user_id) + result = [] + for video in [v for v in videos if v['date'] >= since]: + if self.log: + self.log.info('Processing VOD %d (%s)', video['id'], video['title']) + comments = self._get_comments(video['id']) + result.extend(comments) + return result