]> git.gir.st - subscriptionfeed.git/blob - app/webhooks/__init__.py
fix loading webhooks as frontend module, simplify startup.sh
[subscriptionfeed.git] / app / webhooks / __init__.py
1 """
2 This is the webhook server that interfaces with Google's WebSub (formerly
3 pubsubhubbub) server. This can be run either as a module on the main frontend,
4 or as its own server, so it can live on a different host than the frontend.
5 """
6
7 import time
8 import sqlite3
9 from flask import Flask, Blueprint, request
10 from urllib.parse import parse_qs, urlparse
11
12 from ..common.common import *
13
14 frontend = Blueprint('webhooks', __name__)
15
16 def app():
17 app = Flask(__name__)
18 app.register_blueprint(frontend)
19 return app
20
21 @frontend.route('/websub/v1/<int:timestamp>/<nonce>/<subject>/<sig>', methods=["GET"])
22 def websub(timestamp, nonce, subject, sig):
23 hmackey = cf['websub']['hmac_key']
24 mode = request.args.get('hub.mode', '')
25 topic = request.args.get('hub.topic', '')
26 challenge = request.args.get('hub.challenge', '')
27 until = int(request.args.get('hub.lease_seconds', '0'))
28
29 if sig != websub_url_hmac(hmackey, subject, timestamp, nonce):
30 return '', 400
31
32 if time.time() - timestamp > int(cf['websub']['lease']):
33 return '', 400
34
35 if mode != "subscribe":
36 return '', 200
37 # Note: channels are not purged from the websub dbtable.
38
39 with sqlite3.connect(cf['global']['database']) as conn:
40 c = conn.cursor()
41 c.execute("""
42 INSERT OR REPLACE INTO websub (channel_id, subscribed_until)
43 VALUES (?, datetime(?, 'unixepoch'))
44 """, (subject, time.time()+until))
45 return challenge, 200
46
47 @frontend.route('/websub/v1/<int:timestamp>/<nonce>/<subject>/<sig>', methods=["POST"])
48 def websub_post(timestamp, nonce, subject, sig):
49 lease = cf['websub']['lease']
50 hmackey = cf['websub']['hmac_key']
51 hub_signature = request.headers.get('X-Hub-Signature').replace("sha1=", "")
52
53 if sig != websub_url_hmac(hmackey, subject, timestamp, nonce):
54 return '', 400
55 if hub_signature != websub_body_hmac(hmackey, request.data):
56 return '', 400
57
58 with sqlite3.connect(cf['global']['database']) as conn:
59 c = conn.cursor()
60 try:
61 update_channel(conn, request.data, from_webhook=True)
62 except Exception as e:
63 app.logger.error(e)
64 with open('/tmp/websub-subscriptions.err', 'ab') as f:
65 f.write(f"<!-- {time.ctime()} ({int(time.time())}) -->\n".encode('ascii'))
66 f.write(request.data + b"\n")
67 return '', 200
68
69 if __name__ == '__main__':
70 app().run(debug=True)
Imprint / Impressum