Files
meetups/legacy.py
T

344 lines
14 KiB
Python

from flask import Flask
from flask import make_response
from flask import jsonify
from flask import request
from flask import g
from flask import redirect
from markupsafe import escape
import json
import datetime
import logging
import os
import sqlite3
import random
MEETUP_INVITE_CLIENT_VERSION='v3.0.0'
DATABASE = '/.hmc-meetups/hmc-meetups.db'
AGREEMENT_SUCCESS_URL = 'https://handmadecities.com/meetups-success'
# NOTE(jmug): This was a large list of invite codes, request the "invite-codes"
# string array secret from secrets server, client at:
# https://git.handmadecities.com/HMC/shhh/src/commit/36af43779460dcfd818d1481abeded14fa45e1a4/pkg/secretsclient/cli.go#L12-L34
INVITE_CODES = ["<redacted>"]
# NOTE(jmug): This was a map of "cities" to a shared secret corresponding to each city, request the "city-secrets"
# string map secret from secrets server, client at:
# https://git.handmadecities.com/HMC/shhh/src/commit/36af43779460dcfd818d1481abeded14fa45e1a4/pkg/secretsclient/cli.go#L12-L34
shared_secrets = {
'<redacted>': '<redacted>',
}
# Flask config
def home():
if os.name == 'nt':
return os.getenv('HOMEPATH')
else:
return os.getenv('HOME')
app = Flask(__name__)
# NOTE(jmug): Flask uses this secret_key to validate cookies, among other things,
# we hsould not have a need for this in a Go port.
app.secret_key = '<redacted>'
start_time = datetime.datetime.now()
logname = start_time.strftime('flask-%Y-%m-%d-%Hh-%Mm-%Ss.log')
logging.basicConfig(filename=home() + '/.hmc-meetups/logs/' + logname, level=logging.INFO, format=f'%(asctime)s %(levelname)s %(name)s %(threadName)s : %(message)s')
@app.before_request
def before_request():
g.db = sqlite3.connect(home() + DATABASE)
g.db.row_factory = sqlite3.Row
@app.teardown_request
def teardown_request(exception):
if hasattr(g, 'db'):
g.db.close()
def insert_subscriber(email, agent, city):
cur = g.db.cursor()
creation_date = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
query = '''
INSERT INTO subscribers (
email, creation_date, suppressed, suppressed_date, user_agent, city
)
VALUES (?, ?, ?, ?, ?, ?)
'''
cur.execute(query, (email, creation_date, 0, None, agent, city))
g.db.commit() # @robustness @abner catch exceptions
@app.route('/v1/meetups/wishlist', methods=["POST"])
def meetups_wishlist():
address = request.form['email'] # @robustness @abner what if it's empty?
city = request.form['city'] # @robustness @abner
insert_subscriber(address, request.headers.get('User-Agent'), city);
app.logger.info("'" + address + "' has wishlisted '" + city + "'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/vienna', methods=["POST"])
def meetups_vienna():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Vienna');
app.logger.info("'" + address + "' has subscribed to 'Vienna'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/denmark', methods=["POST"])
def meetups_denmark():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Denmark');
app.logger.info("'" + address + "' has subscribed to 'Denmark'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/montreal', methods=["POST"])
def meetups_montreal():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Montreal');
app.logger.info("'" + address + "' has subscribed to 'Montreal'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/revolt', methods=["POST"])
def meetups_revolt():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Revolt');
app.logger.info("'" + address + "' has subscribed to 'Revolt'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/dc', methods=["POST"])
def meetups_dc():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'D.C.');
app.logger.info("'" + address + "' has subscribed to 'D.C.'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/vancouver', methods=["POST"])
def meetups_vancouver():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Vancouver');
app.logger.info("'" + address + "' has subscribed to 'Vancouver'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/cambridge', methods=["POST"])
def meetups_cambridge():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Cambridge');
app.logger.info("'" + address + "' has subscribed to 'Cambridge'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/atlanta', methods=["POST"])
def meetups_atlanta():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Atlanta');
app.logger.info("'" + address + "' has subscribed to 'Atlanta'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/australia', methods=["POST"])
def meetups_australia():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Australia');
app.logger.info("'" + address + "' has subscribed to 'Australia'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/tokyo', methods=["POST"])
def meetups_tokyo():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Tokyo');
app.logger.info("'" + address + "' has subscribed to 'Tokyo'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/berlin', methods=["POST"])
def meetups_berlin():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Berlin');
app.logger.info("'" + address + "' has subscribed to 'Berlin'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/termina', methods=["POST"])
def meetups_termina():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Termina');
app.logger.info("'" + address + "' has subscribed to 'Termina'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/london', methods=["POST"])
def meetups_london():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'London');
app.logger.info("'" + address + "' has subscribed to 'London'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/seattle', methods=["POST"])
def meetups_seattle():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Seattle');
app.logger.info("'" + address + "' has subscribed to 'Seattle'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/portland', methods=["POST"])
def meetups_portland():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Portland');
app.logger.info("'" + address + "' has subscribed to 'Portland'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/boston', methods=["POST"])
def meetups_boston():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Boston');
app.logger.info("'" + address + "' has subscribed to 'Boston'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/las_vegas', methods=["POST"])
def meetups_las_vegas():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Las Vegas');
app.logger.info("'" + address + "' has subscribed to 'Las Vegas'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/san_francisco', methods=["POST"])
def meetups_san_francisco():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'San Francisco');
app.logger.info("'" + address + "' has subscribed to 'San Francisco'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/new_york_city', methods=["POST"])
def meetups_new_york_city():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'New York City');
app.logger.info("'" + address + "' has subscribed to 'New York City'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/detroit', methods=["POST"])
def meetups_detroit():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Detroit');
app.logger.info("'" + address + "' has subscribed to 'Detroit'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/stockholm', methods=["POST"])
def meetups_stockholm():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Stockholm');
app.logger.info("'" + address + "' has subscribed to 'Stockholm'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/gothenburg', methods=["POST"])
def meetups_gothenburg():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Gothenburg');
app.logger.info("'" + address + "' has subscribed to 'Gothenburg'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/grenoble', methods=["POST"])
def meetups_grenoble():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Grenoble');
app.logger.info("'" + address + "' has subscribed to 'Grenoble'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/israel', methods=["POST"])
def meetups_israel():
address = request.form['email'] # @robustness @abner what if it's empty?
insert_subscriber(address, request.headers.get('User-Agent'), 'Israel');
app.logger.info("'" + address + "' has subscribed to 'Israel'")
return redirect(AGREEMENT_SUCCESS_URL);
@app.route('/v1/meetups/subscribers', methods=["POST"])
def meetups_subscribers():
# Verify headers
content_type = (
request.headers['Content-Type'] if 'Content-Type' in request.headers else ''
)
if 'application/json' in content_type:
payload = request.json
else:
resp = { 'error' : 'JSON payload expected'}
return make_response(jsonify(resp), 415)
# Confirm client version is compatible
if 'client' not in payload or payload['client'] != MEETUP_INVITE_CLIENT_VERSION:
resp = { 'error' : 'Outdated client. Download latest version: https://git.handmadecities.com/meetups/meetupinvite2000/releases' }
return make_response(jsonify(resp), 403)
# Extract city from POST request
if 'city' not in payload:
resp = { 'error' : 'city field missing' }
return make_response(jsonify(resp), 400)
city = payload['city']
app.logger.info('Received subscribers request with city \'' + city + '\'')
if city not in shared_secrets:
resp = { 'error' : 'There are no official Handmade meetups in ' + city }
return make_response(jsonify(resp), 400)
auth_header = ( request.headers['Authorization'] if 'Authorization' in request.headers else '')
if shared_secrets[city] not in auth_header:
resp = { 'error' : 'wrong shared secret for ' + city }
return make_response(jsonify(resp), 401)
# Query emails given the city
cur = g.db.cursor()
cur.execute('SELECT email FROM Subscribers WHERE city="' + city + '"')
rows = cur.fetchall()
emails = [row[0] for row in rows]
resp = { 'emails' : emails }
return make_response(jsonify(resp), 200)
@app.route('/v1/meetups/chat/invite', methods=["POST"])
def meetups_chat_invite():
content_type = (
request.headers['Content-Type'] if 'Content-Type' in request.headers else ''
)
if 'application/json' not in content_type:
resp = { 'error': 'JSON payload expected' }
return make_response(jsonify(resp), 415)
payload = request.json
if not payload or 'city' not in payload:
resp = { 'error': 'city field missing' }
return make_response(jsonify(resp), 400)
city = payload['city']
app.logger.info('Received chat invite request for city \'' + city + '\'')
if city not in shared_secrets:
resp = { 'error' : 'There are no official Handmade meetups in ' + city }
return make_response(jsonify(resp), 400)
auth_header = ( request.headers['Authorization'] if 'Authorization' in request.headers else '')
if shared_secrets[city] not in auth_header:
resp = { 'error' : 'wrong shared secret for ' + city }
return make_response(jsonify(resp), 401)
# Pick a random invite code and return it
code = random.choice(INVITE_CODES)
app.logger.info('Issued chat invite for ' + city + ': ' + code)
resp = {
'invite_code': code,
# TODO(jmug): Verify with Abner is this is an actual secret.
# NOTE(jmug): Request "revolt-invite-link" secret.
'invite_link': '<redacted>',
# NOTE(jmug): Request postmark-server-token secret.
'postmark_server_token': '<redacted>',
'postmark_template_id_register': 41923378,
'postmark_template_id_join': 41930444
}
return make_response(jsonify(resp), 200)