4.2 KiB
name, description
| name | description |
|---|---|
| discord-dm | Send OR read Discord messages to/from an org member DM or team channel via the ClaudeTools bot - copy-paste-friendly delivery of links/commands the terminal would mangle, and READ replies (coord/repo sync does not carry Discord answers); address people by name (mike/howard/rob/winter). Triggers: DM <person> in discord, send that link to my discord, ping <person>, did <person> reply/answer, read discord replies, check discord DMs. |
discord-dm — direct Discord messaging to the org
Thin wrapper over the Discord bot API. The engine is
.claude/scripts/discord-dm.sh; this doc is the usage contract.
When to use
- Wrapped command lines — Mike's standing rule: any wrapped/long single-line
output (M365 consent links, long CLI one-liners, URLs with query strings) gets
DM'd to him in Discord so it's cleanly copy-pasteable, not mangled by terminal
wrapping. Default target for that is
mike. - Pinging a specific teammate with something actionable.
- Posting to a team channel when you want to choose the channel explicitly
(
#bot-alerts/#dev-alerts).
For routine one-line [SYNCRO] / [RMM] status alerts, keep using
post-bot-alert.sh (it auto-routes by prefix). This skill is for person-targeted
DMs and deliberate channel posts.
Usage
bash .claude/scripts/discord-dm.sh <recipient> "message text"
echo "message text" | bash .claude/scripts/discord-dm.sh <recipient>
bash .claude/scripts/discord-dm.sh read <recipient> [limit] # READ recent messages — SEE replies (default 15)
bash .claude/scripts/discord-dm.sh list # print known users + channels
Reading replies (read)
Sends are one-way, but people answer — always read before assuming silence. The bot
participates in every DM channel it opened, so GET /channels/<id>/messages returns full
content (the Message Content privileged intent only gates gateway events, not this REST
fetch). Works for users and channels:
bash .claude/scripts/discord-dm.sh read mike 10 # last 10 in the mike DM
bash .claude/scripts/discord-dm.sh read #dev-alerts # last 15 in #dev-alerts
Output is oldest-first; lines from them are marked >>>, lines from us are indented.
After DMing someone a question, read that user to check for their answer — coord/repo sync
does NOT carry Discord replies, so this is the only way to see them.
<recipient>:
| Form | Effect |
|---|---|
mike howard rob winter |
opens/uses that user's DM channel |
#bot-alerts / bot |
posts to #bot-alerts (whole team) |
#dev-alerts / dev |
posts to #dev-alerts (Howard + Mike, private) |
chan:<id> |
posts to an arbitrary channel by ID |
<17-19 digit id> |
treated as a user DM |
Prepopulated directory (from .claude/users.json + post-bot-alert.sh)
| Name | Discord user ID | Channel | ID | |
|---|---|---|---|---|
| mike | 264814939619721216 | #bot-alerts | 624710699771232265 | |
| howard | 624667664501178379 | #dev-alerts | 1509998508198068484 | |
| rob | 261978810713505792 | |||
| winter | 624666486362996755 |
Keep this table and the USERS/CHANNELS maps in the script in sync with
.claude/users.json when people are added/removed.
Implementation notes / gotchas
- Bot token resolves from the SOPS vault
(
projects/discord-bot/bot-token.sops.yamlfieldcredentials.bot_token), falling back toprojects/discord-bot/.envDISCORD_TOKEN. Works from any machine, not just BEAST. - DMs require opening the user's DM channel first
(
POST /users/@me/channels {recipient_id}) then posting to that channel id. - Multiline payloads: always build JSON with
jq -nc --argand feed curl viaprintf '%s' "$payload" | curl ... --data-binary @-. Passing the JSON directly with-d "$(...)"mangled multiline content and returned Discord error50109 invalid JSON body(hit 2026-06-15 sending the CryoWeave consent link). - Not soft-fail: unlike
post-bot-alert.sh, a send failure exits non-zero — a requested DM is load-bearing, so surface the error. - The bot must share a server with the user and the user must allow DMs from server members, or the DM-channel open / send can 403.