{"id":"add-discord","name":"add-discord","summary":"Chat SDKを通じてDiscordボットのチャンネル統合を追加してください。","body":"# Add Discord Channel\n\nAdds Discord bot support via the Chat SDK bridge. NanoClaw doesn't ship channels\nin trunk — this skill copies the Discord adapter in from the `channels` branch.\n\nThe mechanical steps under **Apply** carry `nc:` directive fences: an agent\nreads the prose and applies them, and a parser can apply them deterministically\nfrom the same document. Every directive is idempotent, so the whole skill is\nsafe to re-run; anything a parser can't apply falls back to the prose beside it.\n\n## Apply\n\n### 1. Copy the adapter and its registration test\n\nFetch the `channels` branch and copy the Discord adapter and its registration\ntest into `src/channels/` (overwrite — the branch is canonical):\n\n```nc:copy from-branch:channels\nsrc/channels/discord.ts\nsrc/channels/discord-registration.test.ts\n```\n\n### 2. Register the adapter\n\nAppend the self-registration import to the channel barrel (skipped if the line\nis already present). This one line is the skill's only reach-in into core:\n\n```nc:append to:src/channels/index.ts\nimport './discord.js';\n```\n\n### 3. Install the adapter package\n\nPinned to an exact version — the supply-chain policy rejects ranges and `latest`:\n\n```nc:dep\n@chat-adapter/discord@4.29.0\n```\n\n### 4. Build and validate\n\nBuild first: it guards the typed `createChatSdkBridge(...)` core call and proves\nthe dependency is installed. Then run the one integration test.\n\n```nc:run effect:build\npnpm run build\n```\n```nc:run effect:test\npnpm exec vitest run src/channels/discord-registration.test.ts\n```\n\n`discord-registration.test.ts` imports the real channel barrel and asserts the\nregistry contains `discord`. It goes red if the import line is deleted or drifts,\nif the barrel fails to evaluate, or if `@chat-adapter/discord` isn't installed\n(the import throws) — so it also covers the dependency from step 3. End-to-end\ndelivery against a real server is verified manually once the service runs.\n\n## Credentials\n\nDiscord app setup is human and interactive — no parser can click through the\nDiscord Developer Portal. The adapter is installed and registered, but it can't\nreceive a message until the bot exists, has Message Content Intent, and shares a\nserver with you. Tell the user:\n\n```nc:operator\nCreate the Discord bot:\n1. Go to https://discord.com/developers/applications → New Application. Name it (e.g. \"NanoClaw Assistant\").\n2. Bot tab → Add Bot if needed → Reset Token, then copy the Bot Token (it's shown only once).\n3. Bot tab → Privileged Gateway Intents → enable Message Content Intent.\n4. OAuth2 → URL Generator → Scopes: bot; Bot Permissions: Send Messages, Read Message History, Add Reactions, Attach Files, Use Slash Commands.\n5. Open the generated URL and invite the bot to a server you're also in (a personal server is fine) — the bot can only DM you once you share a server.\n```\n\nPaste the Bot Token (it's shown only once). You don't paste the Application ID or\nthe Public Key by hand — the bot's own application record carries both, so a\nsingle call derives them from the token:\n\n```nc:prompt bot_token secret validate:^[A-Za-z0-9._-]{50,}$\nPaste the Bot Token — Bot tab. Click `Reset Token` if you need a new one.\n```\n\nRead the application's own record. `GET /oauth2/applications/@me` returns the\nApplication ID (`id`), the Public Key (`verify_key`), and your own account as the\napp's owner (`owner.id`) — so the App ID, the Public Key, and your Discord user ID\nall come from this one call instead of being copied by hand. A bad token fails\nhere, before the restart, rather than silently later:\n\n```nc:run capture:application_id=.id,public_key=.verify_key,owner_handle=.owner.id effect:fetch\ncurl -sf https://discord.com/api/v10/oauth2/applications/@me -H \"Authorization: Bot {{bot_token}}\"\n```\n\nStore the token and the two derived credentials — the adapter reads them from\n`.env` and fails to start without `DISCORD_PUBLIC_KEY` and `DISCORD_APPLICATION_ID`\n(set-if-absent, so a value you've already filled in is never overwritten):\n\n```nc:env-set\nDISCORD_BOT_TOKEN={{bot_token}}\nDISCORD_APPLICATION_ID={{application_id}}\nDISCORD_PUBLIC_KEY={{public_key}}\n```\n## Restart\n\nRestart the service so it loads the Discord adapter and the credentials you just\nstored, and wait for its CLI socket before resolving:\n\n```nc:run effect:restart\nbash setup/lib/restart.sh\n```\n\n## Invite the bot to a shared server\n\nThe bot can only DM you once it shares a server with you. If you didn't already\ninvite it via the OAuth2 URL Generator while setting up the app, do it now: add\nthe bot to a server you're also in (a personal server is fine). Tell the user:\n\n```nc:operator\nOpen the invite link — https://discord.com/oauth2/authorize?client_id={{application_id}}&scope=bot&permissions=2147584064 — and add the bot to a server you're also in (a personal server works fine); the bot can only DM you once you share a server. If you already invited it while setting up the app, you can skip this.\n```\n\n## Resolve your DM channel\n\nThe agent talks to you in your direct-message channel with the bot. Your Discord\nuser ID was already derived as the application's owner (`owner_handle`), so all\nthat's left is to open the DM and read back its channel id.\n\nOpen the DM with `POST /users/@me/channels` and take the channel id it returns as\nthe conversation address `discord:@me:<channelId>` (if Discord refuses, the bot\ndoesn't share a server with you yet — invite it, then retry):\n\n```nc:run capture:platform_id effect:fetch\ncurl -s -X POST https://discord.com/api/v10/users/@me/channels -H \"Authorization: Bot {{bot_token}}\" -H \"Content-Type: application/json\" -d '{\"recipient_id\":\"{{owner_handle}}\"}' | jq -er '\"discord:@me:\" + .id'\n```\n\n`owner_handle` and `platform_id` are what the owner-wiring step needs. The\ngreeting goes out over the DM channel, which works as soon as the bot shares a\nserver with you.\n\n## Next Steps\n\nIf you're in the middle of `/setup`, return to the setup flow now. Otherwise wire\nthis channel with `/init-first-agent` (or `/manage-channels`).\n\n## Channel Info\n\n- **type**: `discord`\n- **terminology**: Discord has \"servers\" (also called \"guilds\") containing \"channels.\" Text channels start with #. The bot can also receive direct messages.\n- **platform-id-format**: `discord:@me:{dmChannelId}` for the owner DM (e.g. `discord:@me:1399...`), `discord:{guildId}:{channelId}` for server channels — both IDs required for channels.\n- **how-to-find-id**: Enable Developer Mode in Discord (Settings > App Settings > Advanced > Developer Mode). Then right-click a server and select \"Copy Server ID\" for the guild ID, and right-click the text channel and select \"Copy Channel ID.\" The platform ID format used in registration is `discord:{guildId}:{channelId}` — both IDs are required.\n- **supports-threads**: yes\n- **typical-use**: Interactive chat — server channels or direct messages\n- **default-isolation**: Same agent group for your personal server. Separate agent group for servers with different communities or where different members have different information boundaries.\n\n## Troubleshooting\n\n**The Bot Token paste is rejected.** The token must be at least 50 characters of letters, digits, dots, underscores, and hyphens — a real Bot Token has two `.` separators. It lives under **Bot → Reset Token** in the Developer Portal and is shown only once; reset to get a fresh one. The short numeric **Application ID** and the **OAuth2 Client Secret** are different values and won't pass.\n\n**`applications/@me` returns 401.** The token was reset since you copied it, or a stray space/newline came along with the paste. Reset the token in the Bot tab and re-run the check — it fails here on purpose, before the restart, while the credential is still cheap to fix.\n\n**The bot is online but never sees your messages.** Two usual causes: Message Content Intent is off (Bot tab → Privileged Gateway Intents), so message bodies arrive empty and nothing triggers; or the bot doesn't share a server with you — in which case `POST /users/@me/channels` also refuses. Open the invite URL and add the bot to a server you're in, then retry.\n\n**Adapter looks installed but Discord never connects.** Run `pnpm exec vitest run src/channels/discord-registration.test.ts` — red means the barrel import or the `@chat-adapter/discord` install drifted, so re-run the Apply steps. If it's green, the service probably hasn't restarted since the credentials were stored: `bash setup/lib/restart.sh`, then check `logs/nanoclaw.error.log` for missing `DISCORD_PUBLIC_KEY` / `DISCORD_APPLICATION_ID` complaints.","author":"@nanocoai","ownerProfile":null,"authorContacts":null,"sourceUrl":"https://github.com/nanocoai/nanoclaw/tree/main/.claude/skills/add-discord","license":"MIT","category":"writing","lang":"en","tokens":2050,"stars":0,"calls30d":2,"claimed":false,"visibility":"public","origin":"crawler","version":"0.1.0","createdAt":"2026-08-22","updatedAt":"2026-08-22","files":[{"path":"apply-fixtures.json","size":760,"sha256":"e20957472e9a1ba9988343cbb58bbb70b4112fc8e89934685f94964d91f805e3"},{"path":"REMOVE.md","size":760,"sha256":"6a29583c08b9d0d10e4d674702c502a6c69e0f6ee5bf11c9a55b8b3f477cc96b"}],"requires":{"mcp":[],"tools":[]},"safety":{"flags":[],"scannedAt":"2026-08-22","hasScripts":false,"networkEndpoints":["discord.com"]}}