Add webhook trigger
This commit is contained in:
89
quotes/discord_utils.py
Normal file
89
quotes/discord_utils.py
Normal 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
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user