agent-messenger 2.25.0 → 2.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.claude-plugin/README.md +16 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +28 -1
  4. package/dist/package.json +1 -1
  5. package/dist/src/platforms/discord/commands/auth.d.ts +4 -0
  6. package/dist/src/platforms/discord/commands/auth.d.ts.map +1 -1
  7. package/dist/src/platforms/discord/commands/auth.js +61 -1
  8. package/dist/src/platforms/discord/commands/auth.js.map +1 -1
  9. package/dist/src/platforms/discord/index.d.ts +2 -0
  10. package/dist/src/platforms/discord/index.d.ts.map +1 -1
  11. package/dist/src/platforms/discord/index.js +1 -0
  12. package/dist/src/platforms/discord/index.js.map +1 -1
  13. package/dist/src/platforms/discord/listener.d.ts +1 -4
  14. package/dist/src/platforms/discord/listener.d.ts.map +1 -1
  15. package/dist/src/platforms/discord/listener.js +33 -15
  16. package/dist/src/platforms/discord/listener.js.map +1 -1
  17. package/dist/src/platforms/discord/remote-auth.d.ts +42 -0
  18. package/dist/src/platforms/discord/remote-auth.d.ts.map +1 -0
  19. package/dist/src/platforms/discord/remote-auth.js +208 -0
  20. package/dist/src/platforms/discord/remote-auth.js.map +1 -0
  21. package/dist/src/platforms/slack/qr-http-login.d.ts.map +1 -1
  22. package/dist/src/platforms/slack/qr-http-login.js +105 -6
  23. package/dist/src/platforms/slack/qr-http-login.js.map +1 -1
  24. package/docs/content/docs/sdk/discord.mdx +23 -9
  25. package/e2e/README.md +3 -1
  26. package/package.json +1 -1
  27. package/skills/agent-channeltalk/SKILL.md +1 -1
  28. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  29. package/skills/agent-discord/SKILL.md +43 -7
  30. package/skills/agent-discord/references/authentication.md +46 -8
  31. package/skills/agent-discordbot/SKILL.md +1 -1
  32. package/skills/agent-instagram/SKILL.md +1 -1
  33. package/skills/agent-kakaotalk/SKILL.md +1 -1
  34. package/skills/agent-line/SKILL.md +1 -1
  35. package/skills/agent-slack/SKILL.md +7 -4
  36. package/skills/agent-slack/references/authentication.md +8 -3
  37. package/skills/agent-slackbot/SKILL.md +1 -1
  38. package/skills/agent-teams/SKILL.md +1 -1
  39. package/skills/agent-telegram/SKILL.md +1 -1
  40. package/skills/agent-telegrambot/SKILL.md +1 -1
  41. package/skills/agent-webex/SKILL.md +1 -1
  42. package/skills/agent-webexbot/SKILL.md +1 -1
  43. package/skills/agent-wechatbot/SKILL.md +1 -1
  44. package/skills/agent-whatsapp/SKILL.md +1 -1
  45. package/skills/agent-whatsappbot/SKILL.md +1 -1
  46. package/src/platforms/discord/commands/auth.ts +81 -1
  47. package/src/platforms/discord/index.ts +2 -0
  48. package/src/platforms/discord/listener.test.ts +12 -4
  49. package/src/platforms/discord/listener.ts +36 -16
  50. package/src/platforms/discord/remote-auth.test.ts +261 -0
  51. package/src/platforms/discord/remote-auth.ts +317 -0
  52. package/src/platforms/slack/qr-http-login.test.ts +47 -0
  53. package/src/platforms/slack/qr-http-login.ts +112 -7
@@ -2,7 +2,40 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- agent-discord uses Discord's user token extracted from the Discord desktop application, with automatic fallback to Chromium browser profiles (Chrome, Chrome Canary, Edge, Arc, Brave, Vivaldi, Chromium) when the desktop app isn't installed.
5
+ agent-discord supports two ways to authenticate:
6
+
7
+ - **QR code sign-in (`auth qr`) — recommended.** Scan a QR code with the Discord mobile app to sign in through Discord's own Remote Auth flow. This is the safest and most reliable method: it never reads stored credentials off disk, works without a desktop app or browser, and uses Discord's official login confirmation on your phone.
8
+ - **Token extraction (`auth extract`).** Reads Discord's user token from the desktop application, with automatic fallback to Chromium browser profiles (Chrome, Chrome Canary, Edge, Arc, Brave, Vivaldi, Chromium). Best for automated/headless environments where no phone is available to scan.
9
+
10
+ > **Recommendation:** Prefer `agent-discord auth qr` whenever you can scan a QR code. Use `auth extract` for headless/CI setups where interactive scanning isn't possible.
11
+
12
+ ## QR Code Sign-In (Recommended)
13
+
14
+ Sign in by scanning a QR code with the Discord mobile app — no desktop app or browser token extraction required. This runs Discord's official Remote Auth protocol, so you authenticate through Discord's own login confirmation rather than by reading stored credentials.
15
+
16
+ ```bash
17
+ # Generate a QR code and wait for you to scan it
18
+ agent-discord auth qr
19
+
20
+ # Show protocol details while debugging
21
+ agent-discord auth qr --debug
22
+
23
+ # Human-readable output
24
+ agent-discord auth qr --pretty
25
+ ```
26
+
27
+ How it works:
28
+
29
+ 1. Opens a WebSocket to Discord's remote-auth gateway and performs an RSA key exchange
30
+ 2. Renders a QR code in your terminal (and opens it in the browser as a fallback)
31
+ 3. You scan it with the Discord mobile app: **Settings → Scan QR Code**, then confirm on your phone
32
+ 4. Discord returns an encrypted ticket, which is exchanged for your user token, validated, and stored like `auth extract` — along with all discovered servers
33
+
34
+ Notes:
35
+
36
+ - **Requires the Discord mobile app** (logged in) to scan. Because it's interactive, it cannot run headlessly — use `auth extract` for CI.
37
+ - The QR code expires after about 150 seconds. Re-run `agent-discord auth qr` to generate a fresh one.
38
+ - If Discord presents a captcha challenge during the token exchange, the CLI surfaces a typed error directing you to fall back to `auth extract`.
6
39
 
7
40
  ## Token Extraction
8
41
 
@@ -186,7 +219,10 @@ Discord user tokens can be invalidated when:
186
219
  If commands start failing with auth errors:
187
220
 
188
221
  ```bash
189
- # Re-extract credentials
222
+ # Recommended: re-authenticate with a QR code
223
+ agent-discord auth qr
224
+
225
+ # Or re-extract credentials from the desktop app / browser
190
226
  agent-discord auth extract
191
227
 
192
228
  # Verify it worked
@@ -216,8 +252,9 @@ This shows:
216
252
 
217
253
  **Solution**:
218
254
 
219
- 1. Log in to discord.com in a Chromium browser (Chrome, Edge, Arc, Brave)the CLI will extract from browser automatically
220
- 2. Or install the Discord desktop app, log in, and run `agent-discord auth extract` again
255
+ 1. Recommended: run `agent-discord auth qr` and scan the QR code with the Discord mobile app no desktop app or browser required
256
+ 2. Or log in to discord.com in a Chromium browser (Chrome, Edge, Arc, Brave) the CLI will extract from browser automatically
257
+ 3. Or install the Discord desktop app, log in, and run `agent-discord auth extract` again
221
258
 
222
259
  ### "No Discord token found"
223
260
 
@@ -275,10 +312,11 @@ With extracted credentials, agent-discord has the same permissions as you in Dis
275
312
 
276
313
  ### Best Practices
277
314
 
278
- 1. **Protect credentials.json**: Never commit to version control
279
- 2. **Use server switching**: Keep different contexts separate
280
- 3. **Re-extract periodically**: Keep tokens fresh
281
- 4. **Revoke if compromised**: Change your Discord password to invalidate tokens
315
+ 1. **Prefer QR sign-in**: Use `auth qr` when possible — it authenticates through Discord's official flow instead of reading credentials off disk
316
+ 2. **Protect credentials.json**: Never commit to version control
317
+ 3. **Use server switching**: Keep different contexts separate
318
+ 4. **Re-authenticate periodically**: Keep tokens fresh
319
+ 5. **Revoke if compromised**: Change your Discord password to invalidate tokens
282
320
 
283
321
  ## Manual Token Management (Advanced)
284
322
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-discordbot
3
3
  description: Interact with Discord servers using bot tokens - send messages, read channels, manage reactions
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-discordbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-instagram
3
3
  description: Interact with Instagram DMs - send messages, read conversations, manage accounts
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-instagram:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-kakaotalk
3
3
  description: Interact with KakaoTalk - send messages, read chats, manage conversations
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-kakaotalk:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-line
3
3
  description: Interact with LINE - send messages, read chats, manage conversations
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-line:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-slack
3
3
  description: Interact with Slack workspaces - send messages, read channels, manage reactions
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-slack:*)
6
6
  metadata:
7
7
  openclaw:
@@ -37,11 +37,14 @@ Credentials are extracted automatically from the Slack desktop app (or Chromium
37
37
 
38
38
  On macOS, the system may prompt for your Keychain password the first time (required to decrypt Slack's stored token). This is a one-time prompt.
39
39
 
40
- **IMPORTANT**: Always use `agent-slack auth extract` to obtain tokens. The CLI extracts from the desktop app first, falling back to Chromium browsers if the app isn't installed.
40
+ **Two ways to obtain tokens:**
41
41
 
42
- ### QR Code Sign-In (No Desktop App Required)
42
+ - **`agent-slack auth qr` — recommended.** Sign in with a QR code from Slack's "Sign in on mobile" screen. It's the safest and most reliable method: it authenticates through Slack's own login flow over plain HTTP instead of reading credentials off disk, and needs no desktop app or browser.
43
+ - **`agent-slack auth extract`.** Extracts tokens from the Slack desktop app first, falling back to Chromium browsers if the app isn't installed. Best for automated/headless environments.
43
44
 
44
- If the Slack desktop app isn't installed (or extraction fails), you can sign in with a QR code from any device where you're already logged into Slack — no browser automation, fully over HTTP:
45
+ ### QR Code Sign-In (Recommended)
46
+
47
+ Sign in with a QR code from any device where you're already logged into Slack — no desktop app, no browser automation, fully over HTTP:
45
48
 
46
49
  ```bash
47
50
  # In Slack (desktop or web): your name (top-left) → "Sign in on mobile".
@@ -2,7 +2,12 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- agent-slack uses Slack's web client credentials (xoxc token + xoxd cookie) extracted from the Slack desktop application, with automatic fallback to Chromium browser profiles (Chrome, Chrome Canary, Edge, Arc, Brave, Vivaldi, Chromium) when the desktop app isn't installed.
5
+ agent-slack supports two ways to authenticate:
6
+
7
+ - **QR code sign-in (`auth qr`) — recommended.** Sign in with a QR code from Slack's "Sign in on mobile" screen. This is the safest and most reliable method: it authenticates through Slack's own login flow over plain HTTP instead of reading stored credentials off disk, and works without a desktop app or browser.
8
+ - **Token extraction (`auth extract`).** Reads Slack's web client credentials (xoxc token + xoxd cookie) from the desktop application, with automatic fallback to Chromium browser profiles (Chrome, Chrome Canary, Edge, Arc, Brave, Vivaldi, Chromium). Best for automated/headless environments.
9
+
10
+ > **Recommendation:** Prefer `agent-slack auth qr`. Use `auth extract` for headless/CI setups or when you'd rather reuse an existing desktop/browser session.
6
11
 
7
12
  ## Token Extraction
8
13
 
@@ -34,9 +39,9 @@ This command:
34
39
 
35
40
  Use `--browser-profile <path>` for agent-browser profiles, custom Chrome user data dirs, or portable browser profiles. The option can be repeated or given comma-separated paths, and explicit paths are included even when desktop credentials are also present.
36
41
 
37
- ### QR Code Sign-In
42
+ ### QR Code Sign-In (Recommended)
38
43
 
39
- When the desktop app isn't installed and browser extraction isn't an option, you can sign in with a QR code from a device where you're already logged into Slack. This runs entirely over HTTP — no browser automation:
44
+ The recommended way to authenticate: sign in with a QR code from a device where you're already logged into Slack. This runs entirely over HTTP — no browser automation, and no desktop app required:
40
45
 
41
46
  ```bash
42
47
  # In Slack (desktop or web): your name (top-left) → "Sign in on mobile".
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-slackbot
3
3
  description: Interact with Slack workspaces using bot tokens - send messages, read channels, manage reactions
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-slackbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-teams
3
3
  description: Interact with Microsoft Teams - send messages, read channels, manage reactions
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-teams:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-telegram
3
3
  description: Interact with Telegram through TDLib - authenticate, inspect chats, and send messages
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-telegram:*)
6
6
  ---
7
7
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-telegrambot
3
3
  description: Interact with Telegram using bot tokens - send messages, read chats, manage reactions
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-telegrambot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-webex
3
3
  description: Interact with Cisco Webex - send messages, read spaces, manage memberships
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-webex:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-webexbot
3
3
  description: Interact with Cisco Webex using bot tokens - send messages, reply in threads, upload and download files, look up people, read spaces, manage memberships, stream real-time events
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-webexbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-wechatbot
3
3
  description: Interact with WeChat Official Account using API credentials - send messages, manage templates, list followers
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-wechatbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-whatsapp
3
3
  description: Interact with WhatsApp - send messages, read chats, manage conversations
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-whatsapp:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-whatsappbot
3
3
  description: Interact with WhatsApp using Cloud API credentials - send messages, manage templates
4
- version: 2.25.0
4
+ version: 2.27.0
5
5
  allowed-tools: Bash(agent-whatsappbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -3,10 +3,12 @@ import { Command } from 'commander'
3
3
  import { collectBrowserProfileOption } from '@/shared/chromium'
4
4
  import { handleError } from '@/shared/utils/error-handler'
5
5
  import { formatOutput } from '@/shared/utils/output'
6
- import { debug } from '@/shared/utils/stderr'
6
+ import { displayQR } from '@/shared/utils/qr'
7
+ import { debug, info } from '@/shared/utils/stderr'
7
8
 
8
9
  import { DiscordClient } from '../client'
9
10
  import { DiscordCredentialManager } from '../credential-manager'
11
+ import { loginWithRemoteAuth } from '../remote-auth'
10
12
  import { DiscordTokenExtractor } from '../token-extractor'
11
13
 
12
14
  export async function extractAction(options: {
@@ -137,6 +139,77 @@ export async function extractAction(options: {
137
139
  }
138
140
  }
139
141
 
142
+ async function saveTokenWithServers(
143
+ token: string,
144
+ credManager: DiscordCredentialManager,
145
+ ): Promise<{ username: string; servers: { id: string; name: string }[]; current: string } | null> {
146
+ const client = await new DiscordClient().login({ token })
147
+ const authInfo = await client.testAuth()
148
+ const servers = await client.listServers()
149
+ if (servers.length === 0) return null
150
+
151
+ const serverMap: Record<string, { server_id: string; server_name: string }> = {}
152
+ for (const server of servers) {
153
+ serverMap[server.id] = { server_id: server.id, server_name: server.name }
154
+ }
155
+
156
+ await credManager.save({ token, current_server: servers[0].id, servers: serverMap })
157
+ return {
158
+ username: authInfo.username,
159
+ servers: servers.map((s) => ({ id: s.id, name: s.name })),
160
+ current: servers[0].id,
161
+ }
162
+ }
163
+
164
+ export async function qrAction(options: { pretty?: boolean; debug?: boolean }): Promise<void> {
165
+ try {
166
+ const interactive = Boolean(process.stdout.isTTY)
167
+ const debugLog = options.debug ? (msg: string) => debug(`[debug] ${msg}`) : undefined
168
+
169
+ const session = await loginWithRemoteAuth({
170
+ debug: debugLog,
171
+ onQrUrl: async (url) => {
172
+ await displayQR(url, {
173
+ platform: 'Discord',
174
+ brandColor: '#5865F2',
175
+ scanInstruction: 'Scan with the Discord mobile app (Settings → Scan QR Code)',
176
+ interactive,
177
+ formatOutput,
178
+ pretty: options.pretty,
179
+ })
180
+ },
181
+ onPendingLogin: (user) => {
182
+ if (interactive) info(`\nQR scanned by ${user.username}. Confirm the login on your phone...\n`)
183
+ },
184
+ })
185
+
186
+ const credManager = new DiscordCredentialManager()
187
+ const saved = await saveTokenWithServers(session.token, credManager)
188
+
189
+ if (!saved) {
190
+ console.log(
191
+ formatOutput(
192
+ {
193
+ error: 'Logged in, but this account is not a member of any server.',
194
+ hint: 'Join at least one server, then run this command again.',
195
+ },
196
+ options.pretty,
197
+ ),
198
+ )
199
+ process.exit(1)
200
+ }
201
+
202
+ console.log(
203
+ formatOutput(
204
+ { user: saved.username, servers: saved.servers.map((s) => `${s.id}/${s.name}`), current: saved.current },
205
+ options.pretty,
206
+ ),
207
+ )
208
+ } catch (error) {
209
+ handleError(error as Error)
210
+ }
211
+ }
212
+
140
213
  export async function logoutAction(options: { pretty?: boolean }): Promise<void> {
141
214
  try {
142
215
  const credManager = new DiscordCredentialManager()
@@ -208,6 +281,13 @@ export const authCommand = new Command('auth')
208
281
  )
209
282
  .action(extractAction),
210
283
  )
284
+ .addCommand(
285
+ new Command('qr')
286
+ .description('Sign in by scanning a QR code with the Discord mobile app')
287
+ .option('--pretty', 'Pretty print JSON output')
288
+ .option('--debug', 'Show debug output for troubleshooting')
289
+ .action(qrAction),
290
+ )
211
291
  .addCommand(
212
292
  new Command('logout')
213
293
  .description('Logout from Discord')
@@ -1,6 +1,8 @@
1
1
  export { DiscordClient, DiscordError } from './client'
2
2
  export { DiscordCredentialManager } from './credential-manager'
3
3
  export { DiscordListener } from './listener'
4
+ export { loginWithRemoteAuth } from './remote-auth'
5
+ export type { RemoteAuthOptions, RemoteAuthSession } from './remote-auth'
4
6
  export type {
5
7
  DiscordChannel,
6
8
  DiscordConfig,
@@ -171,10 +171,9 @@ describe('DiscordListener', () => {
171
171
  expect(identifyMsg.d.token).toBe('fake-token')
172
172
  })
173
173
 
174
- it('sends Identify with custom intents', async () => {
174
+ it('sends a user-account Identify with the load-bearing default shape', async () => {
175
175
  const client = createMockClient()
176
- const customIntents = 1 << 9
177
- listener = new DiscordListener(client, { intents: customIntents })
176
+ listener = new DiscordListener(client)
178
177
 
179
178
  await listener.start()
180
179
  mockWsInstance.simulateOpen()
@@ -184,7 +183,16 @@ describe('DiscordListener', () => {
184
183
  const identifyMsg = sentMessages.find((m) => m.op === 2)
185
184
 
186
185
  expect(identifyMsg).toBeDefined()
187
- expect(identifyMsg.d.intents).toBe(customIntents)
186
+ expect(identifyMsg.d.capabilities).toBe(16381)
187
+ expect(identifyMsg.d.client_state).toEqual({ guild_versions: {} })
188
+ expect(identifyMsg.d.compress).toBe(false)
189
+ expect(identifyMsg.d.presence).toEqual({ status: 'online', since: 0, activities: [], afk: false })
190
+ expect(identifyMsg.d.properties.browser).toBe('Chrome')
191
+ expect(identifyMsg.d.properties.client_build_number).toBe(648814)
192
+
193
+ // All intents incl. MESSAGE_CONTENT (1<<15); user sessions blank other users' content without it
194
+ expect(identifyMsg.d.intents).toBe(33_554_431)
195
+ expect(identifyMsg.d.intents & (1 << 15)).toBe(1 << 15)
188
196
  })
189
197
  })
190
198
 
@@ -4,7 +4,7 @@ import WebSocket from 'ws'
4
4
 
5
5
  import type { DiscordClient } from './client'
6
6
  import type { DiscordListenerEventMap, DiscordGatewayGenericEvent } from './types'
7
- import { DiscordGatewayOpcode, DiscordIntent } from './types'
7
+ import { DiscordGatewayOpcode } from './types'
8
8
 
9
9
  const GATEWAY_URL = 'wss://gateway.discord.gg/?v=10&encoding=json'
10
10
  const GATEWAY_QUERY = '?v=10&encoding=json'
@@ -13,20 +13,25 @@ const RECONNECT_MAX_DELAY = 30_000
13
13
  const NON_RECOVERABLE_CLOSE_CODES = [4004, 4010, 4011, 4012, 4013, 4014]
14
14
  const SESSION_RESET_CLOSE_CODES = [4007, 4009]
15
15
 
16
- const DEFAULT_INTENTS =
17
- DiscordIntent.Guilds |
18
- DiscordIntent.GuildMessages |
19
- DiscordIntent.GuildMessageReactions |
20
- DiscordIntent.GuildMessageTyping |
21
- DiscordIntent.DirectMessages |
22
- DiscordIntent.DirectMessageReactions |
23
- DiscordIntent.DirectMessageTyping
16
+ // User (non-bot) gateway capabilities bitmask, mirroring discord.py-self's default set.
17
+ // Capabilities shape the READY payload; bit 10 (client_state_v2) requires client_state.guild_versions.
18
+ const USER_GATEWAY_CAPABILITIES = 16381
19
+
20
+ // Without MESSAGE_CONTENT (1<<15), Discord blanks `content`/`embeds`/`attachments` on messages
21
+ // from OTHER users (self/DM/mention content still arrives). User sessions get all intents only
22
+ // when `intents` is omitted OR explicitly set, so we send an all-intents value to guarantee content.
23
+ const USER_GATEWAY_INTENTS = 33_554_431
24
+
25
+ // Discord validates client_build_number against recent web-client builds; a stale value can
26
+ // yield a connected-but-degraded session that fires READY yet delivers no message events.
27
+ // Overridable via env so it can be refreshed without a release when it eventually goes stale.
28
+ const DEFAULT_CLIENT_BUILD_NUMBER = 648814
29
+ const USER_GATEWAY_BUILD_NUMBER = Number(process.env.AGENT_DISCORD_BUILD_NUMBER) || DEFAULT_CLIENT_BUILD_NUMBER
24
30
 
25
31
  type EventKey = keyof DiscordListenerEventMap
26
32
 
27
33
  export class DiscordListener {
28
34
  private client: DiscordClient
29
- private intents: number
30
35
  private running = false
31
36
  private ws: WebSocket | null = null
32
37
  private emitter = new EventEmitter()
@@ -43,9 +48,8 @@ export class DiscordListener {
43
48
  private cachedUser: { id: string; username: string } | null = null
44
49
  private generation = 0
45
50
 
46
- constructor(client: DiscordClient, options?: { intents?: number }) {
51
+ constructor(client: DiscordClient) {
47
52
  this.client = client
48
- this.intents = options?.intents ?? DEFAULT_INTENTS
49
53
  }
50
54
 
51
55
  async start(): Promise<void> {
@@ -233,12 +237,28 @@ export class DiscordListener {
233
237
  op: DiscordGatewayOpcode.Identify,
234
238
  d: {
235
239
  token: this.token,
236
- intents: this.intents,
240
+ capabilities: USER_GATEWAY_CAPABILITIES,
241
+ intents: USER_GATEWAY_INTENTS,
237
242
  properties: {
238
- os: 'linux',
239
- browser: 'agent-messenger',
240
- device: 'agent-messenger',
243
+ os: 'Linux',
244
+ browser: 'Chrome',
245
+ device: '',
246
+ system_locale: 'en-US',
247
+ browser_user_agent:
248
+ 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
249
+ browser_version: '131.0.0.0',
250
+ os_version: '',
251
+ referrer: '',
252
+ referring_domain: '',
253
+ referrer_current: '',
254
+ referring_domain_current: '',
255
+ release_channel: 'stable',
256
+ client_build_number: USER_GATEWAY_BUILD_NUMBER,
257
+ client_event_source: null,
241
258
  },
259
+ presence: { status: 'online', since: 0, activities: [], afk: false },
260
+ compress: false,
261
+ client_state: { guild_versions: {} },
242
262
  },
243
263
  }),
244
264
  )