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.

198 lines
7.8 KiB

import functools
import re
import unicodedata
import irc.bot
from commands import CommandError
# $username --> Sweet! Thanks for the quote! #$id: $response
QUOTE_ADDED_PATTERN = re.compile(r'''^
(?P<user>.+)\s+-->\s+
Sweet!\s+Thanks\s+for\s+the\s+quote!\s+
\#(?P<id>\d+):\s+
(?P<text>.+)\s+
\[(?P<game>.+)\]\s+
\[(?P<date>.+)\]$''', re.VERBOSE)
# $username --> Successfully edited Quote #$id: $response
QUOTE_EDITED_PATTERN = re.compile(r'''^
(?P<user>.+)\s+-->\s+
Successfully\s+edited\s+Quote\s+
\#(?P<id>\d+):\s+
(?P<text>.+)\s+
\[(?P<game>.+)\]\s+
\[(?P<date>.+)\]$''', re.VERBOSE)
# $username --> Successfully deleted Quote #$id.
QUOTE_REMOVED_PATTERN = re.compile(r'''^
(?P<user>.+)\s+-->\s+
Successfully\s+deleted\s+Quote\s+
\#(?P<id>\d+)\.$''', re.VERBOSE)
class TwitchClient(irc.bot.SingleServerIRCBot):
def __init__(self, config, logger, commands):
self.config = config
self.logger = logger
self.commands = commands
self.patterns = [
(QUOTE_ADDED_PATTERN, self._add_quote),
(QUOTE_EDITED_PATTERN, self._edit_quote),
(QUOTE_REMOVED_PATTERN, self._remove_quote),
]
self.supported_commands = [
(re.compile(r'^!lastquote$'), self._do_lastquote),
(re.compile(r'^!findquote\s+(?P<q>")?(?P<filter>.+)(?(q)")$'), self._do_findquote),
(re.compile(r'^!syncquotes$'), self._do_syncquotes),
(re.compile(r'^!(bella(gram|pics)|insta(gram|bella))$'), self._do_bellagram),
(re.compile(r'^!yt\s+(?P<q>")?(?P<query>.+)(?(q)")$'), self._do_yt),
(re.compile(r'^!clip\s+(?P<q>")?(?P<filter>.+)(?(q)")$'), self._do_clip),
(re.compile(r'^!reload$'), self._do_reload),
]
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(TwitchClient, self).__init__([(server, port, token)], nickname, nickname)
def connect_(self):
self._connect()
def process_data(self):
self.reactor.process_once()
def on_welcome(self, connection, event):
connection.cap('REQ', ':twitch.tv/membership')
connection.cap('REQ', ':twitch.tv/tags')
connection.cap('REQ', ':twitch.tv/commands')
for channel in self.config['IRC'].get('channels').split(','):
channel = '#{0}'.format(channel)
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)
for pattern, action in self.patterns + self.supported_commands:
m = pattern.match(message)
if m:
action(tags, send_response, **m.groupdict())
for cmd, resp in self.config['Extra Commands'].items():
if cmd == message:
send_response(resp)
def _add_quote(self, tags, send_response, user, id, text, game, date, **kwargs):
if text[0] == text[-1] == '"':
text = text[1:-1]
self.logger.info('Adding quote %s: %s', id, text)
try:
self.commands.add_quote(id, text, game, date)
except CommandError as e:
self.logger.error('Failed to add quote: %s', e)
def _edit_quote(self, tags, send_response, user, id, text, game, date, **kwargs):
if text[0] == text[-1] == '"':
text = text[1:-1]
self.logger.info('Editing quote %s: %s', id, text)
try:
self.commands.edit_quote(id, text, game, date)
except CommandError as e:
self.logger.error('Failed to add quote: %s', e)
def _remove_quote(self, tags, send_response, user, id, **kwargs):
self.logger.info('Removing quote %s', id)
try:
self.commands.remove_quote(id)
except CommandError as e:
self.logger.error('Failed to remove quote: %s', e)
def _do_lastquote(self, tags, send_response, **kwargs):
try:
quote = self.commands.last_quote()
except CommandError as e:
send_response('Sorry @{0}, {1}'.format(tags['display-name'], e))
else:
if self.config['Quotes'].getboolean('act_as_proxy'):
send_response('!quote {0}'.format(quote['id']))
else:
send_response('One time, Lilia said this... #{id}: "{text}" [{game}] [{date}]'.format(**quote))
def _do_findquote(self, tags, send_response, filter, **kwargs):
try:
quote = self.commands.find_quote(filter)
except CommandError as e:
send_response('Sorry @{0}, {1}'.format(tags['display-name'], e))
else:
if self.config['Quotes'].getboolean('act_as_proxy'):
send_response('!quote {0}'.format(quote['id']))
else:
send_response('One time, Lilia said this... #{id}: "{text}" [{game}] [{date}]'.format(**quote))
def _do_syncquotes(self, tags, send_response, **kwargs):
master_user_id = self.config['Twitch'].getint('master_user_id')
if int(tags['user-id']) != master_user_id:
respond('Sorry @{0}, you are not allowed to do this'.format(tags['display-name']))
return
try:
messages = self.commands.get_quote_messages()
except CommandError as e:
self.logger.error('Failed to get quote messages: %s', e)
else:
for message in messages:
for pattern, action in self.patterns:
m = pattern.match(message)
if m:
action(tags, send_response, **m.groupdict())
def _do_bellagram(self, tags, send_response, **kwargs):
try:
bellagram = self.commands.bellagram()
except CommandError as e:
send_response('Sorry @{0}, {1}'.format(tags['display-name'], e))
else:
send_response(bellagram['url'])
def _do_yt(self, tags, send_response, query, **kwargs):
try:
result = self.commands.query_youtube(query)
except CommandError as e:
send_response('Sorry @{0}, {1}'.format(tags['display-name'], e))
else:
send_response('{0}: {1}'.format(result['title'], result['url']))
def _do_clip(self, tags, send_response, filter, **kwargs):
try:
result = self.commands.find_clip(filter)
except CommandError as e:
send_response('Sorry @{0}, {1}'.format(tags['display-name'], e))
else:
send_response('{0}: {1}'.format(result['title'], result['url']))
def _do_reload(self, tags, send_response, **kwargs):
master_user_id = self.config['Twitch'].getint('master_user_id')
if int(tags['user-id']) != master_user_id:
respond('Sorry @{0}, you are not allowed to do this'.format(tags['display-name']))
return
self.config.remove_section('Extra Commands')
self.config.read('settings.cfg')