Skip to content
This repository was archived by the owner on Dec 8, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CodeChallenge/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from . import core
from .api.eb import bp as eb_bp
from .api.questions import bp as questions_bp
from .api.slack import bp as slack_bp
from .api.users import bp as users_bp
from .api.vote import bp as vote_bp
from .auth import jwt
Expand Down Expand Up @@ -62,6 +63,7 @@ def create_app(config):
app.register_blueprint(q_cli_bp)
app.register_blueprint(clock_cli_bp)
app.register_blueprint(vote_bp)
app.register_blueprint(slack_bp)

@app.errorhandler(429)
def ratelimit_handler(e):
Expand Down
7 changes: 7 additions & 0 deletions CodeChallenge/api/eb.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from hmac import compare_digest

import requests
from flask import Blueprint, request, current_app, render_template
from flask_mail import Message

Expand Down Expand Up @@ -41,4 +42,10 @@ def worker():

mail.send(msg)

webhook = current_app.config.get("SLACK_WEBHOOK")
if webhook is not None:
requests.post(webhook, json=dict(
text=f"*NEW RANK* {core.current_rank()}"
))

return "", 200
75 changes: 75 additions & 0 deletions CodeChallenge/api/slack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import hmac
import re
import time

import requests
from flask import Blueprint, request, jsonify, current_app, abort

from .. import core

bp = Blueprint("slackapi", __name__, url_prefix="/api/v1/slack")

CMDRE = re.compile(r"^!([^ ]+)", re.I)


@bp.before_request
def slack_verify():
ts = request.headers.get("X-Slack-Request-Timestamp")
secret = bytes(current_app.config.get("SLACK_SIGNING_SECRET"), "utf8")
body = request.data

if abs(time.time() - int(ts)) > 60 * 5:
abort(401)

sig_basestring = bytes(f"v0:{ts}:", "utf8")
sig_basestring += body

my_sig = "v0=" + hmac.new(secret, sig_basestring, digestmod="SHA256").hexdigest()
slack_sig = request.headers.get("X-Slack-Signature")
if not hmac.compare_digest(my_sig, slack_sig):
abort(401)


def post_message(channel, text):
rv = requests.post("https://slack.com/api/chat.postMessage",
headers=dict(
Authorization="Bearer " + current_app.config["SLACK_OAUTH_TOKEN"]
),
json=dict(
channel=channel,
text=text
))

rv.raise_for_status()


def handle_message(text, channel):
match = CMDRE.search(text)

if match is None:
return

command = match.group(1)

if command == "status":
rank = core.current_rank()
resp = f"*Current Rank:* {rank if rank != -1 else '(challenge not started)'}\n"
resp += f"*Max Rank:* {core.max_rank()}\n"
resp += f"*Next Rank:* {core.time_until_next_rank()}\n"
resp += f"*Total Users:* {core.user_count()}"

post_message(channel, resp)


@bp.route("/event", methods=["POST"])
def slack_event():
data = request.get_json()

if "challenge" in data:
return jsonify(challenge=data["challenge"])

event = data["event"]
if event["type"] == "message":
handle_message(event["text"], event["channel"])

return "", 200
3 changes: 3 additions & 0 deletions CodeChallenge/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class DefaultConfig:
MG_LIST = "codechallenge@school.codewizardshq.com"
WORKER_PASSWORD = os.getenv("WORKER_PASSWORD")
SLACK_WEBHOOK = os.getenv("SLACK_WEBHOOK")
SLACK_SIGNING_SECRET = os.getenv("SLACK_SIGNING_SECRET")
SLACK_OAUTH_TOKEN = os.getenv("SLACK_OAUTH_TOKEN")
SLACK_CHANNEL = os.getenv("SLACK_CHANNEL")

# no trailing /
EXTERNAL_URL = "https://challenge.codewizardshq.com"
Expand Down
5 changes: 5 additions & 0 deletions CodeChallenge/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from flask import current_app
from sqlalchemy import func

from .auth import Users
from .models import Question, db


Expand Down Expand Up @@ -68,3 +69,7 @@ def challenge_ended() -> bool:
return True

return False


def user_count() -> int:
return db.session.query(func.count(Users)).scalar()