diff --git a/README.md b/README.md index 85eb0dd..e189aff 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ using [HTTP API](https://gitea.brno.mraveniste.cc/turbotraktor/ladylilia.com/src ## Supported commands * `!bellagram`, `!bellapics`, `!instabella`, `!instagram` - posts a link to a random Instagram picture of Bella +* `!yt QUERY` - queries Lady Lilia's Youtube channel and posts a link to the most relevant result + - `QUERY` can contain `-` (*NOT*) and `|` (*OR*) operators, for example: `!yt oblivion -nehrim` * `!lastquote` - requests the most recent quote * `!findquote PATTERN` - searches for quotes matching `PATTERN` and in case multiple matches are found, requests one of them randomly - `PATTERN` has to be at least 3 characters long and it can be enclosed in double quotes in case it contains spaces diff --git a/bot.py b/bot.py index 65debd1..0d85e12 100644 --- a/bot.py +++ b/bot.py @@ -11,6 +11,7 @@ import requests from instagram import Instagram from twitch import Twitch +from youtube import Youtube config = configparser.ConfigParser() @@ -62,6 +63,7 @@ class TwitchBot(irc.bot.SingleServerIRCBot): (re.compile(r'^!lastquote$'), self.last_quote), (re.compile(r'^!findquote\s+(?P")?(?P.+)(?(q)")$'), self.find_quote), (re.compile(r'^!sync(\s+(?P.+))?$'), self.sync), + (re.compile(r'^!yt\s+(?P")?(?P.+)(?(q)")$'), self.query_youtube), ] self.server = config['IRC'].get('server', 'irc.chat.twitch.tv') self.port = config['IRC'].getint('port', 6667) @@ -198,6 +200,19 @@ class TwitchBot(irc.bot.SingleServerIRCBot): 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] diff --git a/requirements.txt b/requirements.txt index d1c130e..b7104f4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +google-api-python-client irc python-dateutil requests diff --git a/settings.cfg.example b/settings.cfg.example index a2b8d6a..c8979c8 100644 --- a/settings.cfg.example +++ b/settings.cfg.example @@ -8,6 +8,11 @@ channel = lilialil username = lilialovesgames keywords = bella,teeny,kitty,cat,😹,😻,🐱,🐈 +[Youtube] +api_key = __GOOGLE_API_KEY__ +# ladylilia +channel_id = UC5970RJMoEcRNZl0MNp8tlQ + [Twitch] api_url = https://api.twitch.tv/v5 client_id = __TWITCH_CLIENT_ID__ diff --git a/youtube.py b/youtube.py new file mode 100644 index 0000000..4d6675d --- /dev/null +++ b/youtube.py @@ -0,0 +1,50 @@ +import googleapiclient +import googleapiclient.discovery + + +BASE_URL = 'https://www.youtube.com' + + +class Youtube(object): + def __init__(self, api_key, log=None): + self.client = googleapiclient.discovery.build('youtube', 'v3', developerKey=api_key) + self.log = log + + def _search(self, channel_id, query, playlists, limit): + result = [] + count = limit + token = '' + while True: + resp = self.client.search().list( + channelId=channel_id, + q=query, + safeSearch='none', + type='playlist' if playlists else 'video', + maxResults=min(count, 50), + part='id,snippet', + pageToken=token).execute() + for item in resp.get('items', []): + kind = item['id']['kind'].split('youtube#')[1] + if kind == 'playlist': + url = '{0}/view_play_list?p={1}'.format(BASE_URL, item['id']['playlistId']) + else: + url = '{0}/watch?v={1}'.format(BASE_URL, item['id']['videoId']) + result.append(dict( + kind=kind, + url=url, + title=item['snippet']['title'])) + count -= resp['pageInfo']['resultsPerPage'] + if count <= 0: + break + token = resp.get('nextPageToken') + if not token: + break + return result + + def search(self, channel_id, query, playlists=True, limit=None): + try: + return self._search(channel_id, query, playlists, limit) + except googleapiclient.errors.HttpError as e: + if self.log: + self.log.error('Failed to query Youtube API: %s', str(e)) + return None