Skip to content

Commit dc562a7

Browse files
committed
feat: add visual reaction indicators for command status
- Add emoji reactions (⏳, ✅, ❌) to show real-time processing status of commands and file uploads. - Add COMMAND_REACTION_ENABLED and related .env variables to customize or disable reactions. - Enhance UX by ensuring users know their webhook is actively processing. - Update documentation and .env.example with new logic and configuration.
1 parent ccdc3e5 commit dc562a7

3 files changed

Lines changed: 69 additions & 13 deletions

File tree

.env.example

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,20 @@ DISCORD_TOKEN=your_discord_bot_token_here
22

33
# Shared secret sent in webhook headers for auth (you validate this in your automation tool)
44
DASHCORD_SHARED_SECRET=my_super_secret_string
5-
DASHCORD_DEBUG=1
6-
7-
# Prefix
8-
COMMAND_PREFIX=!
95

106
# Timezone metadata
117
TIMEZONE=America/New_York
128

9+
# Chat Commands
10+
COMMAND_PREFIX=!
1311
DISPLAY_UNKNOWN_COMMAND_ERROR=true
1412
DISPLAY_UNKNOWN_COMMAND_ERROR_SILENT_CHANNELS=123456789,987654321
1513

16-
VERIFY_TLS=false
17-
DEBUG_WEBHOOK=1
18-
PYTHONUNBUFFERED=1
19-
14+
# Visual reaction feedback for commands and file uploads
15+
COMMAND_REACTION_ENABLED=true
16+
COMMAND_REACTION_PENDING=
17+
COMMAND_REACTION_SUCCESS=
18+
COMMAND_REACTION_FAIL=
2019

2120
# panel behavior
2221
PANEL_REPOST_ON_STARTUP=true
@@ -34,4 +33,12 @@ PANEL_PERSIST_DEFAULT=true
3433
PANEL_PERSIST_INTERVAL_SECONDS=120
3534
PANEL_PERSIST_CLEANUP_OLD_ACTIVE=true
3635

37-
HTTP_TIMEOUT_SECONDS=120
36+
# Webhook Connectivity & API Troubleshooting
37+
DASHCORD_DEBUG=false
38+
DEBUG_WEBHOOK=false
39+
VERIFY_TLS=false
40+
HTTP_TIMEOUT_SECONDS=120
41+
42+
# Docker / Console Output
43+
# Set to 1 to ensure logs appear immediately in docker logs
44+
PYTHONUNBUFFERED=1

README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ While platforms like n8n and Node-RED have native Discord nodes, they are often
4444
- **🎭 Dynamic Body Templating:** Inject Discord metadata (like `{{discord.user_display}}` or `{{discord.channel_id}}`) directly into the JSON payload sent to your webhook, molding the data to fit your API perfectly.
4545
- **🔒 Security Built-In:** Restrict specific commands to specific Discord channels or user IDs. Secures outbound requests with a custom `X-DashCord-Token` header.
4646
- **💬 Native Discord Replies:** Your webhook can respond with JSON containing plain text or rich Discord Embeds, and the bot will cleanly post it back to the channel.
47-
47+
- **👁️ Visual Status Indicators:** Real-time emoji reactions (⏳, ✅, ❌) let users know exactly when a command is processing, succeeded, or failed without needing extra text replies.
48+
-
4849
---
4950

5051
## 🚀 Quick Start (Docker)
@@ -323,7 +324,11 @@ DashCord is highly customizable. You can fine-tune exactly how the bot, your web
323324
- `DISPLAY_UNKNOWN_COMMAND_ERROR_SILENT_CHANNELS`: A comma-separated list of channel IDs where the bot will never post an "Unknown command" error. Use this if you have other bots in the same channel so DashCord doesn't interrupt their commands (Default: empty).
324325
- `DASHCORD_DEBUG`: Enables verbose internal debug logging in the console (Default: `false`).
325326
- `ROUTES_PATH`: The file path to your routing configuration (Default: `routes.json` in the bot's root directory).
326-
327+
- `COMMAND_REACTION_ENABLED`: Automatically add emoji reactions to user messages to show command status (pending, success, fail) (Default: `true`).
328+
- `COMMAND_REACTION_PENDING`: The emoji to show while a command or file upload is being processed by your webhook (Default: ``).
329+
- `COMMAND_REACTION_SUCCESS`: The emoji to show when a command succeeds (Default: ``).
330+
- `COMMAND_REACTION_FAIL`: The emoji to show when a command or webhook fails (Default: ``).
331+
327332
#### 🌐 Webhook & API Settings
328333
- `DASHCORD_SHARED_SECRET`: A secret string sent as the `X-DashCord-Token` HTTP header to secure your webhooks from unauthorized requests.
329334
- `HTTP_TIMEOUT_SECONDS`: How long the bot waits for your webhook to respond before throwing a timeout error (Default: `20`).

bot.py

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def get_env_bool(key: str, default: str = "false") -> bool:
2525
"""Helper to parse boolean environment variables."""
2626
return os.getenv(key, default).strip().lower() in ("1", "true", "yes", "y", "on")
2727

28-
DASHCORD_DEBUG = get_env_bool("DASHCORD_DEBUG", "0")
28+
DASHCORD_DEBUG = get_env_bool("DASHCORD_DEBUG", "false")
2929

3030
log = logging.getLogger("dashcord")
3131
log.setLevel(logging.DEBUG if DASHCORD_DEBUG else logging.INFO)
@@ -55,12 +55,21 @@ def _dbg(msg: str, *args):
5555
ROUTES_PATH = os.getenv("ROUTES_PATH", os.path.join(BASE_DIR, "routes.json"))
5656

5757
VERIFY_TLS = get_env_bool("VERIFY_TLS", "true")
58-
DEBUG_WEBHOOK = get_env_bool("DEBUG_WEBHOOK", "0")
58+
DEBUG_WEBHOOK = get_env_bool("DEBUG_WEBHOOK", "false")
5959
DISPLAY_UNKNOWN_COMMAND_ERROR = get_env_bool("DISPLAY_UNKNOWN_COMMAND_ERROR", "true")
6060

6161
DISPLAY_UNKNOWN_COMMAND_ERROR_SILENT_CHANNELS = set(
6262
cid.strip() for cid in os.getenv("DISPLAY_UNKNOWN_COMMAND_ERROR_SILENT_CHANNELS", "").split(",") if cid.strip()
6363
)
64+
65+
# ----------------------------
66+
# REACTION OPTIONS (.env)
67+
# ----------------------------
68+
COMMAND_REACTION_ENABLED = get_env_bool("COMMAND_REACTION_ENABLED", "true")
69+
COMMAND_REACTION_PENDING = os.getenv("COMMAND_REACTION_PENDING", "⏳")
70+
COMMAND_REACTION_SUCCESS = os.getenv("COMMAND_REACTION_SUCCESS", "✅")
71+
COMMAND_REACTION_FAIL = os.getenv("COMMAND_REACTION_FAIL", "❌")
72+
6473
# ----------------------------
6574
# PANEL OPTIONS (.env)
6675
# ----------------------------
@@ -112,6 +121,22 @@ def _dbg(msg: str, *args):
112121
# HELPERS
113122
# ----------------------------
114123

124+
async def _add_reaction_safe(message: discord.Message, emoji: str) -> None:
125+
if not COMMAND_REACTION_ENABLED:
126+
return
127+
try:
128+
await message.add_reaction(emoji)
129+
except Exception as e:
130+
_dbg("Could not add reaction %s to message %s: %s", emoji, message.id, e)
131+
132+
async def _remove_reaction_safe(message: discord.Message, emoji: str) -> None:
133+
if not COMMAND_REACTION_ENABLED or not bot.user:
134+
return
135+
try:
136+
await message.remove_reaction(emoji, bot.user)
137+
except Exception as e:
138+
_dbg("Could not remove reaction %s from message %s: %s", emoji, message.id, e)
139+
115140
def _message_time_utc(message: discord.Message) -> datetime:
116141
# created_at is UTC-aware in discord.py
117142
if getattr(message, "created_at", None):
@@ -181,6 +206,7 @@ async def _fanout_attachments_to_command(message: discord.Message, command: str,
181206
await message.reply(f"❌ No matching attachment found. Expected: {want}. Got: {got}")
182207
return
183208

209+
await _add_reaction_safe(message, COMMAND_REACTION_PENDING)
184210

185211
ok = 0
186212
bad = 0
@@ -234,6 +260,12 @@ async def _fanout_attachments_to_command(message: discord.Message, command: str,
234260
total = len(atts)
235261
has_errors = (bad > 0)
236262

263+
await _remove_reaction_safe(message, COMMAND_REACTION_PENDING)
264+
if has_errors:
265+
await _add_reaction_safe(message, COMMAND_REACTION_FAIL)
266+
elif ok > 0:
267+
await _add_reaction_safe(message, COMMAND_REACTION_SUCCESS)
268+
237269
# Decide whether to reply at all
238270
should_reply = (
239271
(mode == "always") or
@@ -1140,11 +1172,23 @@ async def on_message(message: discord.Message):
11401172

11411173
log.info(f"⚡ User '{message.author.display_name}' triggered command '{command}' in channel {message.channel.id}")
11421174

1175+
await _add_reaction_safe(message, COMMAND_REACTION_PENDING)
1176+
11431177
try:
11441178
data = await post_to_webhook_async(command, payload)
1179+
1180+
await _remove_reaction_safe(message, COMMAND_REACTION_PENDING)
1181+
if data is not None:
1182+
await _add_reaction_safe(message, COMMAND_REACTION_SUCCESS)
1183+
else:
1184+
await _add_reaction_safe(message, COMMAND_REACTION_FAIL)
1185+
# ------------------------------
1186+
11451187
await send_reply(message.channel, data)
11461188
except Exception as e:
11471189
log.error(f"⚠️ Exception triggering command '{command}': {e}", exc_info=True)
1190+
await _remove_reaction_safe(message, COMMAND_REACTION_PENDING)
1191+
await _add_reaction_safe(message, COMMAND_REACTION_FAIL)
11481192
await message.reply(f"⚠️ Trigger failed: {type(e).__name__}: {e}")
11491193

11501194

0 commit comments

Comments
 (0)