128 lines
4.4 KiB
Python
128 lines
4.4 KiB
Python
"""Outbound notifications.
|
|
|
|
Extracted from app/routes/users.py, where it sat between two route
|
|
definitions and pulled `requests`, `logging` and the Discord bot into a
|
|
module whose subject is HTTP handlers (ARCH-003).
|
|
|
|
Failures here are swallowed and logged on purpose: a notification that does
|
|
not reach Discord must not roll back the session it was announcing. That is
|
|
a property of the caller — a Flask request whose work is already committed —
|
|
not of the failure, which is why the breadth is argued for at each of the
|
|
two boundaries below rather than assumed (ARCH-008 / QUA-004).
|
|
|
|
The webhook branch is narrower than it was: `requests.RequestException`
|
|
covers every way an HTTP call can fail, and anything else coming out of it
|
|
is a defect worth seeing.
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
|
|
import requests
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
#: Either a webhook URL, or a bare Discord user id to DM instead.
|
|
DISCORD_WEBHOOK_URL = os.environ.get('DISCORD_WEBHOOK_URL', '')
|
|
|
|
|
|
def send_discord_notification(
|
|
player_name,
|
|
points,
|
|
date_str,
|
|
start_time_str,
|
|
end_time_str,
|
|
team_name,
|
|
coach_name,
|
|
coach_discord,
|
|
coach_discord_id,
|
|
request_id=None,
|
|
):
|
|
"""Send a Discord notification for a One on One request.
|
|
|
|
Args:
|
|
player_name: Who is asking.
|
|
points: Free-text discussion points, possibly empty.
|
|
date_str, start_time_str, end_time_str: Already formatted for display.
|
|
team_name: The player's team, or None.
|
|
coach_name: Who is being asked.
|
|
coach_discord: The coach's Discord handle, for the webhook footer.
|
|
coach_discord_id: The coach's Discord snowflake, for a direct message.
|
|
request_id: OneOnOneRequest primary key, so reactions can find it back.
|
|
"""
|
|
|
|
if coach_discord_id:
|
|
try:
|
|
from app.discord import send_one_on_one_dm
|
|
|
|
send_one_on_one_dm(
|
|
coach_name=coach_name,
|
|
coach_discord_id=coach_discord_id,
|
|
player_name=player_name,
|
|
team_name=team_name,
|
|
date_str=date_str,
|
|
start_time=start_time_str,
|
|
end_time=end_time_str,
|
|
points=points,
|
|
request_id=request_id,
|
|
)
|
|
except Exception: # noqa: BLE001 — the request that booked the meeting is already committed
|
|
logger.warning('Failed to hand the One on One DM to the bot', exc_info=True)
|
|
|
|
if not DISCORD_WEBHOOK_URL:
|
|
return
|
|
|
|
# A bare snowflake here means "DM this person instead", and only when the
|
|
# coach has no id of their own. Queueing it cannot raise (see _enqueue).
|
|
if DISCORD_WEBHOOK_URL.isdigit():
|
|
if not coach_discord_id:
|
|
from app.discord import send_one_on_one_dm
|
|
|
|
send_one_on_one_dm(
|
|
coach_name=coach_name,
|
|
coach_discord_id=DISCORD_WEBHOOK_URL,
|
|
player_name=player_name,
|
|
team_name=team_name,
|
|
date_str=date_str,
|
|
start_time=start_time_str,
|
|
end_time=end_time_str,
|
|
points=points,
|
|
)
|
|
return
|
|
|
|
embed = {
|
|
"embeds": [
|
|
{
|
|
"title": "One on One Request",
|
|
"color": 3447003,
|
|
"fields": [
|
|
{"name": "Player", "value": player_name, "inline": True},
|
|
{
|
|
"name": "Team",
|
|
"value": team_name or "Unknown Team",
|
|
"inline": True,
|
|
},
|
|
{"name": "Date", "value": date_str, "inline": True},
|
|
{
|
|
"name": "Time",
|
|
"value": f"{start_time_str} - {end_time_str}",
|
|
"inline": True,
|
|
},
|
|
{
|
|
"name": "Discussion Points",
|
|
"value": points or "No specific points provided",
|
|
"inline": False,
|
|
},
|
|
],
|
|
"footer": {
|
|
"text": f"Coach: {coach_name}"
|
|
+ (f" (Discord: {coach_discord})" if coach_discord else ""),
|
|
},
|
|
}
|
|
],
|
|
}
|
|
try:
|
|
requests.post(DISCORD_WEBHOOK_URL, json=embed, timeout=5)
|
|
except requests.RequestException as exc:
|
|
logger.warning('Failed to post the One on One webhook: %s', exc)
|