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.

257 lines
9.4 KiB

import configparser
import logging
import os
import random
import re
import string
import dateutil.parser
import irc.bot
import requests
from instagram import Instagram
from twitch import Twitch
from youtube import Youtube
config = configparser.ConfigParser()
config.read('settings.cfg')
log = logging.getLogger('irc.client')
log.addHandler(logging.StreamHandler())
log.setLevel(logging.DEBUG if bool(int(os.getenv('DEBUG', 0))) else logging.INFO)
# $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 TwitchBot(irc.bot.SingleServerIRCBot):
def __init__(self):
self.patterns = [
(QUOTE_ADDED_PATTERN, self.add_quote),
(QUOTE_EDITED_PATTERN, self.edit_quote),
(QUOTE_REMOVED_PATTERN, self.remove_quote),
]
self.commands = [
(re.compile(r'^!bellagram$'), self.bellagram),
(re.compile(r'^!bellapics$'), self.bellagram),
(re.compile(r'^!instabella$'), self.bellagram),
(re.compile(r'^!instagram$'), self.bellagram),
(re.compile(r'^!lastquote$'), self.last_quote),
(re.compile(r'^!findquote\s+(?P<q>")?(?P<filter>.+)(?(q)")$'), self.find_quote),
(re.compile(r'^!sync(\s+(?P<since>.+))?$'), self.sync),
(re.compile(r'^!yt\s+(?P<q>")?(?P<query>.+)(?(q)")$'), self.query_youtube),
]
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.master_user_id = config['Twitch'].getint('master_user_id')
self.api_url = config['Quotes'].get('api_url')
self.api_key = config['Quotes'].get('api_key')
self.bellagrams = self._get_bellagrams()
log.info('Connecting to %s:%d', self.server, self.port)
super(TwitchBot, self).__init__([(self.server, self.port, self.token)],
self.nickname, self.nickname)
def _get_bellagrams(self):
username = config['Instagram'].get('username')
keywords = config['Instagram'].get('keywords').split(',')
media = Instagram(username, log).get_media()
if not media:
return None
return [m for m in media if [k for k in keywords if k.lower() in m['text'].lower()]]
def on_welcome(self, connection, event):
connection.cap('REQ', ':twitch.tv/membership')
connection.cap('REQ', ':twitch.tv/tags')
connection.cap('REQ', ':twitch.tv/commands')
log.info('Joining %s', self.channel)
connection.join(self.channel)
def on_join(self, connection, event):
log.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 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 c in string.printable])
message = message.rstrip()
for pattern, action in self.patterns + self.commands:
m = pattern.match(message)
if m:
action(connection, tags, **m.groupdict())
def get(self, params):
r = requests.get('{0}/quotes'.format(self.api_url), params=params)
r.raise_for_status()
return r
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
def delete(self, id):
r = requests.delete('{0}/quotes/{1}'.format(self.api_url, id),
headers={'X-Quotes-API-Key': self.api_key})
r.raise_for_status()
return r
def bellagram(self, connection, tags, **kwargs):
if not self.bellagrams:
msg = 'Sorry @{0}, couldn\'t get any media from Instagram'.format(tags['display-name'])
else:
msg = random.choice(self.bellagrams)['url']
connection.privmsg(self.channel, msg)
def last_quote(self, connection, tags, **kwargs):
try:
quotes = self.get(dict(
sort_by='id',
sort_order='desc',
page_size=1)).json()
quote = quotes[0]
except (requests.exceptions.HTTPError, IndexError):
msg = 'Sorry @{0}, no quotes found'.format(tags['display-name'])
else:
msg = '!quote {0}'.format(quote['id'])
connection.privmsg(self.channel, msg)
def find_quote(self, connection, tags, filter, **kwargs):
if len(filter) < 3:
msg = 'Sorry @{0}, the search phrase is too short'.format(tags['display-name'])
connection.privmsg(self.channel, msg)
return
try:
quotes = self.get(dict(
filter=filter,
sort_order='random',
page_size=1)).json()
quote = quotes[0]
except (requests.exceptions.HTTPError, IndexError):
msg = 'Sorry @{0}, no quotes found'.format(tags['display-name'])
else:
msg = '!quote {0}'.format(quote['id'])
connection.privmsg(self.channel, msg)
def sync(self, connection, tags, since=None, **kwargs):
if int(tags['user-id']) != self.master_user_id:
msg = 'Sorry @{0}, you are not allowed to do this'.format(tags['display-name'])
connection.privmsg(self.channel, msg)
return
if since is None:
try:
quotes = self.get(dict(
sort_by='id',
sort_order='desc',
page_size=1)).json()
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'].getint('target_user_id')
since = dateutil.parser.parse(since).date()
messages = Twitch(api_url, client_id, log).get_messages(user_id, since)
if not messages:
return
for message in messages:
for pattern, action in self.patterns:
m = pattern.match(message)
if m:
action(connection, None, **m.groupdict())
msg = '@{0}, sync completed'.format(tags['display-name'])
connection.privmsg(self.channel, msg)
def query_youtube(self, connection, tags, query, **kwargs):
api_key = config['Youtube'].get('api_key')
channel_id = config['Youtube'].get('channel_id')
yt = Youtube(api_key)
items = yt.search(channel_id, query, playlists=True, limit=1)
if not items:
items = yt.search(channel_id, query, playlists=False, limit=1)
if not items:
msg = 'Sorry @{0}, couldn\'t find anything on Youtube'.format(tags['display-name'])
else:
msg = '{0} - {1}'.format(items[0]['title'], items[0]['url'])
connection.privmsg(self.channel, msg)
def add_quote(self, connection, tags, user, id, text, game, date, **kwargs):
if text[0] == text[-1] == '"':
text = text[1:-1]
log.info('Adding quote %s: %s', id, text)
try:
self.post(dict(
id=int(id),
date=dateutil.parser.parse(date, dayfirst=True).date().isoformat(),
game=game,
text=text))
except requests.exceptions.HTTPError as e:
log.error('Failed to add quote: %s', str(e))
def edit_quote(self, connection, tags, user, id, text, game, date, **kwargs):
if text[0] == text[-1] == '"':
text = text[1:-1]
log.info('Editing quote %s: %s', id, text)
try:
self.post(dict(
id=int(id),
date=dateutil.parser.parse(date, dayfirst=True).date().isoformat(),
game=game,
text=text))
except requests.exceptions.HTTPError as e:
log.error('Failed to edit quote: %s', str(e))
def remove_quote(self, connection, tags, user, id, **kwargs):
log.info('Removing quote %s', id)
try:
self.delete(int(id))
except requests.exceptions.HTTPError as e:
log.error('Failed to remove quote: %s', str(e))
def main():
bot = TwitchBot()
bot.start()
if __name__ == "__main__":
main()