From f56df5749082e40e6b34037aa14068b658e063f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nikola=20Forr=C3=B3?= Date: Fri, 15 Jun 2018 10:48:48 +0200 Subject: [PATCH] Synchronize and provide also Twitch channel emotes --- comments-api/app.py | 27 ++++++++++++++++++++++++++- comments-api/db.py | 7 +++++++ comments-api/sync.py | 18 +++++++++++++++--- comments-api/twitch.py | 19 ++++++++++++++++++- docker-compose.yaml.example | 5 +++-- 5 files changed, 69 insertions(+), 7 deletions(-) diff --git a/comments-api/app.py b/comments-api/app.py index a20846d..31cf899 100644 --- a/comments-api/app.py +++ b/comments-api/app.py @@ -9,7 +9,7 @@ import flask_restful.reqparse import sqlalchemy import sqlalchemy.engine -from db import db, Video, Comment, Association +from db import db, Video, Comment, Association, Emote app = flask.Flask(__name__) @@ -74,6 +74,11 @@ comment_fields = { 'updated_at': flask_restful.fields.DateTime(dt_format='iso8601', attribute='comment.updated_at'), } +emote_fields = { + 'id': flask_restful.fields.Integer(), + 'code': flask_restful.fields.String(), +} + filter_parser = flask_restful.reqparse.RequestParser() filter_parser.add_argument('filter', type=str) @@ -169,10 +174,30 @@ class CommentsResource(flask_restful.Resource): return assocs, 200, {'X-Total-Count': count} +class EmoteResource(flask_restful.Resource): + @flask_restful.marshal_with(emote_fields) + def get(self, id): + q = db.session.query(Emote).filter(Emote.id == id) + emote = q.first() + if not emote: + flask_restful.abort(404, message='Emote {0} does not exist'.format(id)) + return emote, 200 + + +class EmotesResource(flask_restful.Resource): + @flask_restful.marshal_with(emote_fields) + def get(self): + q = db.session.query(Emote) + emotes = q.all() + return emotes, 200 + + api.add_resource(VideoResource, '/videos/') api.add_resource(VideosResource, '/videos') api.add_resource(CommentResource, '/videos//comments/') api.add_resource(CommentsResource, '/videos//comments') +api.add_resource(EmoteResource, '/emotes/') +api.add_resource(EmotesResource, '/emotes') if __name__ == '__main__': diff --git a/comments-api/db.py b/comments-api/db.py index 3ae2b7f..06f583a 100644 --- a/comments-api/db.py +++ b/comments-api/db.py @@ -46,3 +46,10 @@ class Association(db.Model): comment_id = db.Column(db.String, db.ForeignKey('comments.id'), primary_key=True) offset = db.Column(db.Float) comment = db.relationship('Comment') + + +class Emote(db.Model): + __tablename__ = 'emotes' + + id = db.Column(db.Integer, primary_key=True) + code = db.Column(db.String) diff --git a/comments-api/sync.py b/comments-api/sync.py index 2501ab8..134eb4b 100644 --- a/comments-api/sync.py +++ b/comments-api/sync.py @@ -3,7 +3,7 @@ import os import flask_restful.inputs -from db import Video, Comment, Association +from db import Video, Comment, Association, Emote from twitch import Twitch @@ -35,9 +35,10 @@ class Sync(object): def perform(cls, app, db): app.logger.info('Starting synchronization') with app.app_context(): - twitch = Twitch(os.getenv('TWITCH_CLIENT_ID')) + twitch = Twitch(os.getenv('TWITCH_CLIENT_ID'), os.getenv('TWITCH_OAUTH_TOKEN')) + channel = os.getenv('TWITCH_CHANNEL_ID') updated = [] - for vid in twitch.fetch_videos(os.getenv('TWITCH_CHANNEL_ID')): + for vid in twitch.fetch_videos(channel): id = cls._get(vid, '_id', default='').lstrip('v') if not id: continue @@ -102,4 +103,15 @@ class Sync(object): video.associations.append(assoc) db.session.add(video) db.session.commit() + for em in twitch.fetch_emotes(channel): + id = cls._get(em, 'id') + if not id: + continue + q = db.session.query(Emote).filter(Emote.id == id) + emote = q.first() + if not emote: + emote = Emote(id=id) + emote.code = cls._get(em, 'code') + db.session.add(emote) + db.session.commit() app.logger.info('Synchronization completed') diff --git a/comments-api/twitch.py b/comments-api/twitch.py index d8501f8..6ab956f 100644 --- a/comments-api/twitch.py +++ b/comments-api/twitch.py @@ -2,8 +2,9 @@ from requests_futures.sessions import FuturesSession class Twitch(object): - def __init__(self, client_id): + def __init__(self, client_id, oauth_token): self.client_id = client_id + self.oauth_token = oauth_token def fetch_videos(self, channel_id): if not channel_id: @@ -54,3 +55,19 @@ class Twitch(object): if cursor: pairs.append((video_id, cursor)) return result + + def fetch_emotes(self, channel_id): + if not channel_id: + return [] + session = FuturesSession() + def get_emotes(): + url = 'https://api.twitch.tv/v5/users/{0}/emotes'.format(channel_id) + params = dict(client_id=self.client_id) + headers = dict(authorization='OAuth {0}'.format(self.oauth_token)) + return session.get(url, params=params, headers=headers) + request = get_emotes() + data = request.result().json() + result = [] + for val in data.get('emoticon_sets', {}).values(): + result.extend(val) + return result diff --git a/docker-compose.yaml.example b/docker-compose.yaml.example index 493f3f4..91d25fc 100644 --- a/docker-compose.yaml.example +++ b/docker-compose.yaml.example @@ -28,8 +28,8 @@ services: - 5000 # Twitch comments API service with /data/comments mounted as database storage - # TWITCH_CLIENT_ID and TWITCH_CHANNEL_ID are needed for Twitch API access - # and synchronization + # TWITCH_CLIENT_ID, TWITCH_OAUTH_TOKEN and TWITCH_CHANNEL_ID are needed for + # Twitch API access and synchronization comments-api: build: context: ./comments-api @@ -38,6 +38,7 @@ services: environment: - SQLALCHEMY_DATABASE_URI=sqlite:////comments/comments.db - TWITCH_CLIENT_ID=__TWITCH_CLIENT_ID__ + - TWITCH_OAUTH_TOKEN=__TWITCH_OAUTH_TOKEN__ - TWITCH_CHANNEL_ID=__TWITCH_CHANNEL_ID__ expose: - 5000