diff options
| author | Leonardo Bishop <me@leonardobishop.net> | 2026-02-13 17:07:34 +0000 |
|---|---|---|
| committer | Leonardo Bishop <me@leonardobishop.net> | 2026-02-13 17:08:01 +0000 |
| commit | bbe07c2788b00711e011f7805e14fa0933bf2d73 (patch) | |
| tree | 7a80a05fd793fe572b901e442576ccbf5f3accc2 /presenter.py | |
Diffstat (limited to 'presenter.py')
| -rw-r--r-- | presenter.py | 67 |
1 files changed, 67 insertions, 0 deletions
diff --git a/presenter.py b/presenter.py new file mode 100644 index 0000000..613bd34 --- /dev/null +++ b/presenter.py @@ -0,0 +1,67 @@ +import json +import queue +from flask import Blueprint, Response, render_template +from sqlalchemy import event + +from CTFd.models import Solves, Challenges, Teams +from CTFd.utils.scores import get_standings + +presenter_blueprint = Blueprint( + "presenter", + __name__, + template_folder="templates" +) + +listeners = [] + + +def sse_format(data): + return f"data: {json.dumps(data)}\n\n" + + +@presenter_blueprint.route("/presenter") +def presenter_view(): + return render_template("presenter.html") + + +@presenter_blueprint.route("/presenter/events") +def presenter_events(): + def stream(): + q = queue.Queue() + listeners.append(q) + try: + while True: + data = q.get() + yield sse_format(data) + except GeneratorExit: + listeners.remove(q) + + return Response(stream(), mimetype="text/event-stream") + + +def broadcast(event): + for q in listeners: + q.put(event) + + +@event.listens_for(Solves, "after_insert") +def on_solve(mapper, connection, solve): + challenge = Challenges.query.get(solve.challenge_id) + team = Teams.query.get(solve.team_id) + + if not challenge or not team: + return + + first_blood = False + if Solves.query.filter_by(challenge_id=solve.challenge_id).count() == 1: + first_blood = True + + broadcast( + { + "type": "solve", + "team": team.name, + "challenge": challenge.name, + "value": challenge.value, + "first": first_blood + } + ) |
