You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

126 lines
4.9 KiB

6 years ago
import configparser
import datetime
import functools
import logging
import os
import re
import unicodedata
import irc.bot
import requests
class ReplayBot(irc.bot.SingleServerIRCBot):
def __init__(self):
self.logger = logging.getLogger('irc.client')
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
self.logger.addHandler(handler)
level = logging.DEBUG if bool(int(os.getenv('DEBUG', 0))) else logging.INFO
self.logger.setLevel(level)
self.config = configparser.ConfigParser()
self.config.read('settings.cfg')
self.supported_commands = [
(re.compile(r'^!load\s+(?P<id>\d+)$'), self._do_load),
(re.compile(r'^!start$'), self._do_start),
(re.compile(r'^!stop$'), self._do_stop),
]
self.messages = []
self.output_channel = '#{0}'.format(self.config['IRC'].get('output_channel'))
control_channel = self.config['IRC'].get('control_channel')
self.control_channel = '#{0}'.format(control_channel) if control_channel else None
self.control_user = self.config['IRC'].getint('control_user')
server = self.config['IRC'].get('server')
port = self.config['IRC'].getint('port')
nickname = self.config['IRC'].get('nickname')
token = self.config['Twitch'].get('token')
self.logger.info('Connecting to %s:%d', server, port)
super(ReplayBot, self).__init__([(server, port, token)], nickname, 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')
channels = set([self.output_channel])
if self.control_channel:
channels.add(self.control_channel)
for channel in channels:
self.logger.info('Joining %s', channel)
connection.join(channel)
def on_join(self, connection, event):
self.logger.info('Joined %s', 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 _send_response(self, connection, event, msg):
if event.target.startswith('#'):
connection.privmsg(event.target, msg)
else:
connection.privmsg('#jtv', '/w {0} {1}'.format(event.source.nick, msg))
def _process_message(self, connection, event):
tags = {t['key']: t['value'] for t in event.tags}
message = ''.join([c for c in event.arguments[0] if not unicodedata.category(c).startswith('C')])
message = message.rstrip()
send_response = functools.partial(self._send_response, connection, event)
if int(tags['user-id']) != self.control_user:
send_response('Sorry @{0}, you are not allowed to do this'.format(tags['display-name']))
return
for pattern, action in self.supported_commands:
m = pattern.match(message)
if m:
action(tags, send_response, **m.groupdict())
def _do_load(self, tags, send_response, id, **kwargs):
client_id = self.config['Twitch'].get('client_id')
def get_comments(cursor):
url = 'https://api.twitch.tv/v5/videos/{0}/comments'.format(id)
params = dict(client_id=client_id, cursor=cursor)
r = requests.get(url, params=params)
r.raise_for_status()
return r.json()
self.messages = []
cursor = ''
while True:
data = get_comments(cursor)
for comment in data.get('comments', []):
self.messages.append((
datetime.timedelta(seconds=float(comment['content_offset_seconds'])),
comment['commenter']['display_name'],
comment['message']['body']))
cursor = data.get('_next')
if not cursor:
break
if self.messages:
send_response('@{0}: loaded {1} messages, first message at {2}'.format(
tags['display-name'], len(self.messages), str(self.messages[0][0])))
else:
send_response('@{0}: failed to load messages'.format(tags['display-name']))
def _do_start(self, tags, send_response, **kwargs):
now = datetime.datetime.utcnow().replace(tzinfo=datetime.timezone.utc)
for offset, user, msg in self.messages:
def cb(user=user, msg=msg):
return self._post_message(user, msg)
self.reactor.scheduler.execute_at(now + offset, cb)
def _do_stop(self, tags, send_response, **kwargs):
self.reactor.scheduler.queue.clear()
def _post_message(self, user, msg):
self.connection.privmsg(self.output_channel, '[{0}]: {1}'.format(user, msg))
def main():
bot = ReplayBot()
bot.start()
if __name__ == "__main__":
main()