Add webhook trigger

This commit is contained in:
KenwoodFox
2025-12-10 22:15:06 -05:00
parent d9208e277c
commit 3e4022e1d5
7 changed files with 340 additions and 14 deletions

89
quotes/discord_utils.py Normal file
View File

@@ -0,0 +1,89 @@
"""
Discord webhook utilities for sending submission notifications.
"""
import os
import json
import requests
from django.conf import settings
def send_discord_webhook(submission):
"""
Sends a discord webhook notification if DISCORD_WEBHOOK is configured.
Joe wrote this!
"""
webhook_url = os.environ.get("DISCORD_WEBHOOK")
if not webhook_url:
return False
try:
files = submission.files.all()
file_urls = []
# Get file URLs
from .email_utils import get_file_urls_with_base_url
file_url_data = get_file_urls_with_base_url(files)
for item in file_url_data:
file_urls.append(
{
"name": item["file"].original_filename,
"url": item["url"],
}
)
# Build description with file links
description_parts = []
if submission.description:
description_parts.append(f"**Description:** {submission.description}")
if file_urls:
description_parts.append("\n**Uploaded Files:**")
for file_info in file_urls:
description_parts.append(f"- [{file_info['name']}]({file_info['url']})")
description = (
"\n".join(description_parts)
if description_parts
else "No description or files provided."
)
# Discord webhook payload
payload = {
"content": "Somebody submitted a quote!",
"embeds": [
{
"title": f"New Quote Request - Submission #{submission.id}",
"color": 3447003, # Blue color
"fields": [
{
"name": "Email",
"value": submission.email,
"inline": True,
},
{
"name": "Submitted At",
"value": submission.submitted_at.strftime(
"%Y-%m-%d %H:%M:%S"
),
"inline": True,
},
],
"description": description,
"footer": {
"text": f"Submission ID: #{submission.id}",
},
}
],
}
response = requests.post(webhook_url, json=payload, timeout=10)
response.raise_for_status()
return True
except Exception as e:
print(f"Error sending Discord webhook: {e}")
return False

View File

@@ -2,10 +2,86 @@
Email utilities for sending submission notifications.
"""
import signal
import threading
import logging
from django.core.mail import send_mail, EmailMultiAlternatives
from django.template.loader import render_to_string
from django.conf import settings
logger = logging.getLogger(__name__)
class TimeoutError(Exception):
"""Custom timeout exception"""
pass
def timeout_handler(signum, frame):
"""Signal handler for timeout"""
raise TimeoutError("Operation timed out")
def send_email_with_timeout(msg, timeout_seconds=5):
"""
Send email with a timeout to prevent hanging.
Uses signal-based timeout on Unix, threading fallback otherwise.
Args:
msg: EmailMultiAlternatives instance
timeout_seconds: Maximum time to wait for email to send
Returns:
bool: True if sent successfully, False otherwise
"""
result = [None] # Use list to allow modification in nested function
exception = [None]
def send_email():
try:
msg.send()
result[0] = True
except Exception as e:
exception[0] = e
result[0] = False
# Try signal-based timeout first (Unix/Linux)
try:
signal.signal(signal.SIGALRM, timeout_handler)
signal.alarm(timeout_seconds)
try:
msg.send()
signal.alarm(0) # Cancel the alarm
return True
except TimeoutError:
logger.error(f"Email sending timed out after {timeout_seconds} seconds")
signal.alarm(0) # Cancel the alarm
return False
except Exception as e:
signal.alarm(0) # Cancel the alarm
logger.error(f"Error sending email: {e}", exc_info=True)
return False
except (AttributeError, ValueError, OSError):
# Windows doesn't support SIGALRM, or signal already in use
# Use threading-based timeout as fallback
email_thread = threading.Thread(target=send_email)
email_thread.daemon = True
email_thread.start()
email_thread.join(timeout=timeout_seconds)
if email_thread.is_alive():
logger.error(
f"Email sending timed out after {timeout_seconds} seconds (threading)"
)
return False
if exception[0]:
logger.error(f"Error sending email: {exception[0]}", exc_info=True)
return False
return result[0] if result[0] is not None else False
def get_file_urls_with_base_url(files):
"""
@@ -120,10 +196,12 @@ Files uploaded: {files.count()}
to=[owner_email],
)
msg.attach_alternative(html_message, "text/html")
msg.send()
owner_sent = True
owner_sent = send_email_with_timeout(msg, timeout_seconds=30)
if not owner_sent:
logger.error(f"Failed to send owner email to {owner_email}")
except Exception as e:
print(f"Error sending owner email: {e}")
logger.error(f"Error sending owner email: {e}", exc_info=True)
owner_sent = False
# Email 2: Confirmation to submitter
try:
@@ -168,9 +246,11 @@ Files uploaded: {files.count()}
to=[submission.email],
)
msg.attach_alternative(html_message, "text/html")
msg.send()
submitter_sent = True
submitter_sent = send_email_with_timeout(msg, timeout_seconds=30)
if not submitter_sent:
logger.error(f"Failed to send submitter email to {submission.email}")
except Exception as e:
print(f"Error sending submitter email: {e}")
logger.error(f"Error sending submitter email: {e}", exc_info=True)
submitter_sent = False
return (owner_sent, submitter_sent)