import datetime import hashlib import hmac import json import logging import os import flask import flask_apscheduler import flask_restful import flask_restful.reqparse from sender import Sender subscriptions = {} app = flask.Flask(__name__) app.logger.setLevel(logging.INFO) app.config.update( ERROR_404_HELP=False, SCHEDULER_TIMEZONE='UTC', SCHEDULER_JOBS=[ dict(id='subscribe', func='twitch:Twitch.subscribe', args=(app, subscriptions, 86400), max_instances=1, trigger='interval', seconds=3600, next_run_time=datetime.datetime.utcnow() + datetime.timedelta(seconds=10))]) scheduler = flask_apscheduler.APScheduler() scheduler.init_app(app) api = flask_restful.Api(app) sender = Sender(app) verification_parser = flask_restful.reqparse.RequestParser() verification_parser.add_argument('hub.challenge', type=str, required=True) verification_parser.add_argument('hub.lease_seconds', type=int, required=True) verification_parser.add_argument('hub.mode', type=str, required=True) verification_parser.add_argument('hub.topic', type=str, required=True) class WebhooksResource(flask_restful.Resource): def get(self): args = verification_parser.parse_args() verified = False for sub in subscriptions.values(): h = '{0[hub.mode]}|{0[hub.topic]}|{0[hub.lease_seconds]}'.format(args) if hashlib.sha256(h.encode()).hexdigest() == sub['request_hash']: verified = True break if not verified: flask_restful.abort(400, message='Verification failed') r = app.make_response(args['hub.challenge']) r.mimetype = 'text/plain' r.status_code = 200 return r def post(self): sha, signature = flask.request.headers.get('X-Hub-Signature').split('=') data = flask.request.data for topic, sub in subscriptions.items(): mac = hmac.new(sub['secret'].encode(), data, getattr(hashlib, sha)) if not hmac.compare_digest(mac.hexdigest(), signature): continue message = flask.request.get_json(force=True) message['topic'] = topic sender.send(json.dumps(message).encode()) return None, 204 api.add_resource(WebhooksResource, '/webhooks') if __name__ == '__main__': scheduler.start() app.run(host='0.0.0.0', threaded=True, debug=False)