Switch Discord notifications from webhook to user ID DMs
Uses the existing bot token to send DMs to users by their Discord user ID instead of posting to a channel webhook. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -89,14 +89,14 @@ def register(app):
|
||||
settings = notifications.getNotificationSettings(user_uuid)
|
||||
if not settings:
|
||||
return flask.jsonify({
|
||||
"discord_webhook": "",
|
||||
"discord_user_id": "",
|
||||
"discord_enabled": False,
|
||||
"ntfy_topic": "",
|
||||
"ntfy_enabled": False,
|
||||
"web_push_enabled": False,
|
||||
}), 200
|
||||
return flask.jsonify({
|
||||
"discord_webhook": settings.get("discord_webhook") or "",
|
||||
"discord_user_id": settings.get("discord_user_id") or "",
|
||||
"discord_enabled": bool(settings.get("discord_enabled")),
|
||||
"ntfy_topic": settings.get("ntfy_topic") or "",
|
||||
"ntfy_enabled": bool(settings.get("ntfy_enabled")),
|
||||
|
||||
@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS users (
|
||||
CREATE TABLE IF NOT EXISTS notifications (
|
||||
id UUID PRIMARY KEY,
|
||||
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
||||
discord_webhook VARCHAR(500),
|
||||
discord_user_id VARCHAR(100),
|
||||
discord_enabled BOOLEAN DEFAULT FALSE,
|
||||
ntfy_topic VARCHAR(255),
|
||||
ntfy_enabled BOOLEAN DEFAULT FALSE,
|
||||
|
||||
@@ -19,8 +19,8 @@ def _sendToEnabledChannels(notif_settings, message, user_uuid=None):
|
||||
"""Send message to all enabled channels. Returns True if at least one succeeded."""
|
||||
sent = False
|
||||
|
||||
if notif_settings.get("discord_enabled") and notif_settings.get("discord_webhook"):
|
||||
if discord.send(notif_settings["discord_webhook"], message):
|
||||
if notif_settings.get("discord_enabled") and notif_settings.get("discord_user_id"):
|
||||
if discord.send_dm(notif_settings["discord_user_id"], message):
|
||||
sent = True
|
||||
|
||||
if notif_settings.get("ntfy_enabled") and notif_settings.get("ntfy_topic"):
|
||||
@@ -44,7 +44,7 @@ def getNotificationSettings(userUUID):
|
||||
def setNotificationSettings(userUUID, data_dict):
|
||||
existing = postgres.select_one("notifications", {"user_uuid": userUUID})
|
||||
allowed = [
|
||||
"discord_webhook",
|
||||
"discord_user_id",
|
||||
"discord_enabled",
|
||||
"ntfy_topic",
|
||||
"ntfy_enabled",
|
||||
@@ -64,11 +64,33 @@ def setNotificationSettings(userUUID, data_dict):
|
||||
|
||||
class discord:
|
||||
@staticmethod
|
||||
def send(webhook_url, message):
|
||||
def send_dm(user_id, message):
|
||||
"""Send a DM to a Discord user via the bot."""
|
||||
bot_token = os.environ.get("DISCORD_BOT_TOKEN")
|
||||
if not bot_token:
|
||||
logger.warning("DISCORD_BOT_TOKEN not set, skipping Discord DM")
|
||||
return False
|
||||
headers = {"Authorization": f"Bot {bot_token}", "Content-Type": "application/json"}
|
||||
try:
|
||||
response = requests.post(webhook_url, json={"content": message})
|
||||
return response.status_code == 204
|
||||
except:
|
||||
# Open/get DM channel with the user
|
||||
dm_resp = requests.post(
|
||||
"https://discord.com/api/v10/users/@me/channels",
|
||||
headers=headers,
|
||||
json={"recipient_id": user_id},
|
||||
)
|
||||
if dm_resp.status_code != 200:
|
||||
logger.error(f"Failed to open DM channel for user {user_id}: {dm_resp.status_code}")
|
||||
return False
|
||||
channel_id = dm_resp.json()["id"]
|
||||
# Send the message
|
||||
msg_resp = requests.post(
|
||||
f"https://discord.com/api/v10/channels/{channel_id}/messages",
|
||||
headers=headers,
|
||||
json={"content": message},
|
||||
)
|
||||
return msg_resp.status_code == 200
|
||||
except Exception as e:
|
||||
logger.error(f"Discord DM error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ interface Preferences {
|
||||
}
|
||||
|
||||
interface NotifSettings {
|
||||
discord_webhook: string;
|
||||
discord_user_id: string;
|
||||
discord_enabled: boolean;
|
||||
ntfy_topic: string;
|
||||
ntfy_enabled: boolean;
|
||||
@@ -29,7 +29,7 @@ export default function SettingsPage() {
|
||||
celebration_style: 'standard',
|
||||
});
|
||||
const [notif, setNotif] = useState<NotifSettings>({
|
||||
discord_webhook: '',
|
||||
discord_user_id: '',
|
||||
discord_enabled: false,
|
||||
ntfy_topic: '',
|
||||
ntfy_enabled: false,
|
||||
@@ -41,7 +41,7 @@ export default function SettingsPage() {
|
||||
Promise.all([
|
||||
api.preferences.get().then((data: Preferences) => setPrefs(data)),
|
||||
api.notifications.getSettings().then((data) => setNotif({
|
||||
discord_webhook: data.discord_webhook,
|
||||
discord_user_id: data.discord_user_id,
|
||||
discord_enabled: data.discord_enabled,
|
||||
ntfy_topic: data.ntfy_topic,
|
||||
ntfy_enabled: data.ntfy_enabled,
|
||||
@@ -214,7 +214,7 @@ export default function SettingsPage() {
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-medium text-gray-900">Discord</p>
|
||||
<p className="text-sm text-gray-500">Send notifications to a Discord channel</p>
|
||||
<p className="text-sm text-gray-500">Get DMs from the Synculous bot</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => updateNotif({ discord_enabled: !notif.discord_enabled })}
|
||||
@@ -229,11 +229,11 @@ export default function SettingsPage() {
|
||||
</div>
|
||||
{notif.discord_enabled && (
|
||||
<input
|
||||
type="url"
|
||||
placeholder="Discord webhook URL"
|
||||
value={notif.discord_webhook}
|
||||
onChange={(e) => setNotif({ ...notif, discord_webhook: e.target.value })}
|
||||
onBlur={() => updateNotif({ discord_webhook: notif.discord_webhook })}
|
||||
type="text"
|
||||
placeholder="Your Discord user ID"
|
||||
value={notif.discord_user_id}
|
||||
onChange={(e) => setNotif({ ...notif, discord_user_id: e.target.value })}
|
||||
onBlur={() => updateNotif({ discord_user_id: notif.discord_user_id })}
|
||||
className="w-full px-3 py-2 text-sm border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent text-gray-900 placeholder-gray-400"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -649,7 +649,7 @@ export const api = {
|
||||
|
||||
getSettings: async () => {
|
||||
return request<{
|
||||
discord_webhook: string;
|
||||
discord_user_id: string;
|
||||
discord_enabled: boolean;
|
||||
ntfy_topic: string;
|
||||
ntfy_enabled: boolean;
|
||||
@@ -658,7 +658,7 @@ export const api = {
|
||||
},
|
||||
|
||||
updateSettings: async (data: {
|
||||
discord_webhook?: string;
|
||||
discord_user_id?: string;
|
||||
discord_enabled?: boolean;
|
||||
ntfy_topic?: string;
|
||||
ntfy_enabled?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user