parent
3e23b2d65c
commit
60cf61d81a
@ -0,0 +1,56 @@
|
||||
import re
|
||||
|
||||
import discord
|
||||
|
||||
from commands import CommandError
|
||||
|
||||
|
||||
class DiscordClient(discord.Client):
|
||||
|
||||
def __init__(self, config, logger, commands):
|
||||
self.config = config
|
||||
self.logger = logger
|
||||
self.commands = commands
|
||||
self.supported_commands = [
|
||||
(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),
|
||||
]
|
||||
super(DiscordClient, self).__init__()
|
||||
|
||||
async def start_(self):
|
||||
token = self.config['Discord'].get('token')
|
||||
await self.start(token)
|
||||
|
||||
async def on_ready(self):
|
||||
self.logger.info('Logged in as {0}'.format(self.user.name))
|
||||
|
||||
async def on_message(self, message):
|
||||
for pattern, action in self.supported_commands:
|
||||
m = pattern.match(message.content)
|
||||
if m:
|
||||
await action(message, **m.groupdict())
|
||||
|
||||
async def _do_bellagram(self, message, **kwargs):
|
||||
try:
|
||||
bellagram = self.commands.bellagram()
|
||||
except CommandError as e:
|
||||
await self.send_message(message.channel, 'Sorry {0}, {1}'.format(message.author.mention, e))
|
||||
else:
|
||||
embed = discord.Embed(title=bellagram['title'], url=bellagram['url'], color=0x8545bc)
|
||||
embed.set_image(url=bellagram['display_url'])
|
||||
embed.set_author(name=bellagram['owner'], url=bellagram['owner_url'],
|
||||
icon_url=bellagram['owner_pic_url'])
|
||||
await self.send_message(message.channel, embed=embed)
|
||||
|
||||
async def _do_yt(self, message, query, **kwargs):
|
||||
try:
|
||||
result = self.commands.query_youtube(query)
|
||||
except CommandError as e:
|
||||
await self.send_message(message.channel, 'Sorry {0}, {1}'.format(message.author.mention, e))
|
||||
else:
|
||||
embed = discord.Embed(title=result['title'], url=result['url'],
|
||||
description=result['description'], color=0xff0000)
|
||||
embed.set_thumbnail(url=result['thumbnail_url'])
|
||||
embed.set_author(name=result['channel_title'], url=result['channel_url'],
|
||||
icon_url=result['channel_thumbnail_url'])
|
||||
await self.send_message(message.channel, embed=embed)
|
@ -0,0 +1,170 @@
|
||||
import functools
|
||||
import re
|
||||
import string
|
||||
|
||||
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'^!sync(\s+(?P<since>.+))?$'), self._do_sync),
|
||||
(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),
|
||||
]
|
||||
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 c in string.printable])
|
||||
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())
|
||||
|
||||
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:
|
||||
send_response('!quote {0}'.format(quote['id']))
|
||||
|
||||
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:
|
||||
send_response('!quote {0}'.format(quote['id']))
|
||||
|
||||
def _do_sync(self, tags, send_response, since=None, **kwargs):
|
||||
master_user_id = self.config['Twitch'].getint('master_user_id')
|
||||
if int(tags['user-id']) != self.master_user_id:
|
||||
respond('Sorry @{0}, you are not allowed to do this'.format(tags['display-name']))
|
||||
return
|
||||
try:
|
||||
messages = self.commands.get_twitch_messages(since)
|
||||
except CommandError as e:
|
||||
self.logger.error('Failed to get Twitch 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']))
|
@ -0,0 +1,149 @@
|
||||
import random
|
||||
|
||||
import dateutil.parser
|
||||
import requests
|
||||
|
||||
from services.instagram import Instagram, InstagramError
|
||||
from services.twitch import Twitch, TwitchError
|
||||
from services.youtube import Youtube, YoutubeError
|
||||
|
||||
|
||||
class CommandError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Commands(object):
|
||||
def __init__(self, config, logger):
|
||||
self.config = config
|
||||
self.logger = logger
|
||||
self._bellagrams = self._collect_bellagrams()
|
||||
|
||||
def add_quote(self, id, text, game, date):
|
||||
try:
|
||||
self._post_quotes(dict(
|
||||
id=int(id),
|
||||
date=dateutil.parser.parse(date, dayfirst=True).date().isoformat(),
|
||||
game=game,
|
||||
text=text))
|
||||
except requests.exceptions.HTTPError as e:
|
||||
raise CommandError(e)
|
||||
|
||||
def edit_quote(self, id, text, game, date):
|
||||
try:
|
||||
self._post_quotes(dict(
|
||||
id=int(id),
|
||||
date=dateutil.parser.parse(date, dayfirst=True).date().isoformat(),
|
||||
game=game,
|
||||
text=text))
|
||||
except requests.exceptions.HTTPError as e:
|
||||
raise CommandError(e)
|
||||
|
||||
def remove_quote(self, id):
|
||||
try:
|
||||
self._delete_quotes(int(id))
|
||||
except requests.exceptions.HTTPError as e:
|
||||
raise CommandError(e)
|
||||
|
||||
def get_twitch_messages(self, since):
|
||||
if since is None:
|
||||
try:
|
||||
quotes = self._get_quotes(dict(
|
||||
sort_by='id',
|
||||
sort_order='desc',
|
||||
page_size=1)).json()
|
||||
quote = quotes.pop(0)
|
||||
except requests.exceptions.HTTPError:
|
||||
raise CommandError(e)
|
||||
except IndexError:
|
||||
raise CommandError(e)
|
||||
else:
|
||||
since = quote['date']
|
||||
api_url = self.config['Twitch'].get('api_url')
|
||||
client_id = self.config['Twitch'].get('client_id')
|
||||
user_id = self.config['Twitch'].getint('target_user_id')
|
||||
since = dateutil.parser.parse(since).date()
|
||||
twitch = Twitch(api_url, client_id)
|
||||
try:
|
||||
return twitch.get_messages(user_id, since)
|
||||
except TwitchError as e:
|
||||
raise CommandError(e)
|
||||
|
||||
def last_quote(self):
|
||||
try:
|
||||
quotes = self._get_quotes(dict(
|
||||
sort_by='id',
|
||||
sort_order='desc',
|
||||
page_size=1)).json()
|
||||
quote = quotes.pop(0)
|
||||
except (requests.exceptions.HTTPError, IndexError):
|
||||
raise CommandError('no quotes found')
|
||||
else:
|
||||
return quote
|
||||
|
||||
def find_quote(self, filter):
|
||||
if len(filter) < 3:
|
||||
raise CommandError('the search phrase is too short')
|
||||
try:
|
||||
quotes = self._get_quotes(dict(
|
||||
filter=filter,
|
||||
sort_order='random',
|
||||
page_size=1)).json()
|
||||
quote = quotes.pop(0)
|
||||
except (requests.exceptions.HTTPError, IndexError):
|
||||
raise CommandError('no quotes found')
|
||||
else:
|
||||
return quote
|
||||
|
||||
def bellagram(self):
|
||||
if not self._bellagrams:
|
||||
raise CommandError('couldn\'t get any media from Instagram')
|
||||
return random.choice(self._bellagrams)
|
||||
|
||||
def query_youtube(self, query):
|
||||
api_key = self.config['Youtube'].get('api_key')
|
||||
channel_id = self.config['Youtube'].get('channel_id')
|
||||
yt = Youtube(api_key)
|
||||
try:
|
||||
results = yt.search(channel_id, query, playlists=True, limit=1)
|
||||
if not results:
|
||||
results = yt.search(channel_id, query, playlists=False, limit=1)
|
||||
result = results.pop(0)
|
||||
except (YoutubeError, IndexError):
|
||||
raise CommandError('couldn\'t find anything on Youtube')
|
||||
else:
|
||||
return result
|
||||
|
||||
def _get_quotes(self, params):
|
||||
api_url = self.config['Quotes'].get('api_url')
|
||||
r = requests.get('{0}/quotes'.format(api_url), params=params)
|
||||
r.raise_for_status()
|
||||
return r
|
||||
|
||||
def _post_quotes(self, data):
|
||||
api_url = self.config['Quotes'].get('api_url')
|
||||
api_key = self.config['Quotes'].get('api_key')
|
||||
r = requests.post('{0}/quotes'.format(api_url), data=data,
|
||||
headers={'X-Quotes-API-Key': api_key})
|
||||
r.raise_for_status()
|
||||
return r
|
||||
|
||||
def _delete_quotes(self, id):
|
||||
api_url = self.config['Quotes'].get('api_url')
|
||||
api_key = self.config['Quotes'].get('api_key')
|
||||
r = requests.delete('{0}/quotes/{1}'.format(api_url, id),
|
||||
headers={'X-Quotes-API-Key': api_key})
|
||||
r.raise_for_status()
|
||||
return r
|
||||
|
||||
def _collect_bellagrams(self):
|
||||
username = self.config['Instagram'].get('username')
|
||||
keywords = self.config['Instagram'].get('keywords').split(',')
|
||||
instagram = Instagram(username)
|
||||
try:
|
||||
media = instagram.get_media()
|
||||
media = [m for m in media if [k for k in keywords \
|
||||
if m['type'] == 'Image' and k.lower() in m['title'].lower()]]
|
||||
except (InstagramError, IndexError):
|
||||
return None
|
||||
else:
|
||||
return media
|
Loading…
Reference in new issue