agent-messenger 2.30.0 → 2.31.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.
- package/.claude-plugin/plugin.json +1 -1
- package/bun.lock +42 -2
- package/dist/package.json +2 -2
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/platforms/channeltalk/cli.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/client.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
- package/dist/src/platforms/channeltalk/commands/bot.js.map +1 -1
- package/dist/src/platforms/channeltalk/commands/chat.js.map +1 -1
- package/dist/src/platforms/channeltalk/commands/group.js.map +1 -1
- package/dist/src/platforms/channeltalk/commands/manager.js.map +1 -1
- package/dist/src/platforms/channeltalk/commands/message.js.map +1 -1
- package/dist/src/platforms/channeltalk/commands/shared.js.map +1 -1
- package/dist/src/platforms/channeltalk/commands/snapshot.js.map +1 -1
- package/dist/src/platforms/channeltalk/commands/whoami.js.map +1 -1
- package/dist/src/platforms/channeltalk/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
- package/dist/src/platforms/channeltalk/types.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/client.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/client.js.map +1 -1
- package/dist/src/platforms/channeltalkbot/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/channeltalkbot/types.d.ts +1 -1
- package/dist/src/platforms/channeltalkbot/types.d.ts.map +1 -1
- package/dist/src/platforms/discord/cli.d.ts.map +1 -1
- package/dist/src/platforms/discord/client.d.ts.map +1 -1
- package/dist/src/platforms/discord/client.js +36 -17
- package/dist/src/platforms/discord/client.js.map +1 -1
- package/dist/src/platforms/discord/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/discord/listener.d.ts.map +1 -1
- package/dist/src/platforms/discord/listener.js.map +1 -1
- package/dist/src/platforms/discord/remote-auth.js.map +1 -1
- package/dist/src/platforms/discord/super-properties.js.map +1 -1
- package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/discord/token-extractor.js.map +1 -1
- package/dist/src/platforms/discord/types.d.ts +13 -0
- package/dist/src/platforms/discord/types.d.ts.map +1 -1
- package/dist/src/platforms/discord/types.js +6 -0
- package/dist/src/platforms/discord/types.js.map +1 -1
- package/dist/src/platforms/discordbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/client.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/client.js.map +1 -1
- package/dist/src/platforms/discordbot/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/listener.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/listener.js.map +1 -1
- package/dist/src/platforms/discordbot/types.d.ts.map +1 -1
- package/dist/src/platforms/imessage/cli.d.ts.map +1 -1
- package/dist/src/platforms/imessage/client.d.ts.map +1 -1
- package/dist/src/platforms/imessage/client.js.map +1 -1
- package/dist/src/platforms/imessage/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/imessage/errors.js.map +1 -1
- package/dist/src/platforms/imessage/rpc.d.ts.map +1 -1
- package/dist/src/platforms/imessage/rpc.js.map +1 -1
- package/dist/src/platforms/imessage/types.d.ts.map +1 -1
- package/dist/src/platforms/imessage/types.js.map +1 -1
- package/dist/src/platforms/instagram/cli.d.ts.map +1 -1
- package/dist/src/platforms/instagram/client.d.ts.map +1 -1
- package/dist/src/platforms/instagram/client.js.map +1 -1
- package/dist/src/platforms/instagram/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/instagram/hybrid-listener.d.ts.map +1 -1
- package/dist/src/platforms/instagram/hybrid-listener.js.map +1 -1
- package/dist/src/platforms/instagram/listener.d.ts.map +1 -1
- package/dist/src/platforms/instagram/mqtt/thrift.d.ts.map +1 -1
- package/dist/src/platforms/instagram/mqtt/transport.d.ts.map +1 -1
- package/dist/src/platforms/instagram/mqtt/transport.js.map +1 -1
- package/dist/src/platforms/instagram/realtime-listener.d.ts.map +1 -1
- package/dist/src/platforms/instagram/realtime-listener.js.map +1 -1
- package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/instagram/types.d.ts.map +1 -1
- package/dist/src/platforms/instagram/types.js.map +1 -1
- package/dist/src/platforms/kakaotalk/cli.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.d.ts +5 -1
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +55 -0
- package/dist/src/platforms/kakaotalk/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/chat.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/chat.js +16 -1
- package/dist/src/platforms/kakaotalk/commands/chat.js.map +1 -1
- package/dist/src/platforms/kakaotalk/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/index.d.ts +2 -2
- package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/index.js +1 -1
- package/dist/src/platforms/kakaotalk/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/listener.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/listener.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/crypto.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.d.ts +23 -0
- package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js +50 -0
- package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
- package/dist/src/platforms/kakaotalk/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/types.d.ts +30 -10
- package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/types.js +10 -0
- package/dist/src/platforms/kakaotalk/types.js.map +1 -1
- package/dist/src/platforms/line/cli.d.ts.map +1 -1
- package/dist/src/platforms/line/client.d.ts.map +1 -1
- package/dist/src/platforms/line/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/line/listener.d.ts.map +1 -1
- package/dist/src/platforms/line/types.d.ts +6 -6
- package/dist/src/platforms/line/types.d.ts.map +1 -1
- package/dist/src/platforms/slack/cli.d.ts.map +1 -1
- package/dist/src/platforms/slack/client.d.ts.map +1 -1
- package/dist/src/platforms/slack/client.js.map +1 -1
- package/dist/src/platforms/slack/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/slack/ensure-auth.js.map +1 -1
- package/dist/src/platforms/slack/listener.d.ts.map +1 -1
- package/dist/src/platforms/slack/qr-http-login.js.map +1 -1
- package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/slack/token-extractor.js.map +1 -1
- package/dist/src/platforms/slackbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/client.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/listener.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/listener.js.map +1 -1
- package/dist/src/platforms/slackbot/types.d.ts.map +1 -1
- package/dist/src/platforms/teams/app-config.js.map +1 -1
- package/dist/src/platforms/teams/cli.d.ts.map +1 -1
- package/dist/src/platforms/teams/client.d.ts.map +1 -1
- package/dist/src/platforms/teams/client.js.map +1 -1
- package/dist/src/platforms/teams/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/teams/device-code.js.map +1 -1
- package/dist/src/platforms/teams/device-login.d.ts.map +1 -1
- package/dist/src/platforms/teams/device-login.js.map +1 -1
- package/dist/src/platforms/teams/listener.d.ts.map +1 -1
- package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/teams/token-extractor.js.map +1 -1
- package/dist/src/platforms/teams/token-provider.d.ts.map +1 -1
- package/dist/src/platforms/teams/token-provider.js.map +1 -1
- package/dist/src/platforms/teams/types.d.ts +14 -14
- package/dist/src/platforms/teams/types.d.ts.map +1 -1
- package/dist/src/platforms/telegram/cli.d.ts.map +1 -1
- package/dist/src/platforms/telegram/client.d.ts.map +1 -1
- package/dist/src/platforms/telegram/client.js.map +1 -1
- package/dist/src/platforms/telegram/commands/shared.js.map +1 -1
- package/dist/src/platforms/telegram/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/telegram/tdlib.d.ts.map +1 -1
- package/dist/src/platforms/telegram/types.d.ts.map +1 -1
- package/dist/src/platforms/telegram/types.js.map +1 -1
- package/dist/src/platforms/telegrambot/cli.d.ts.map +1 -1
- package/dist/src/platforms/telegrambot/client.d.ts.map +1 -1
- package/dist/src/platforms/telegrambot/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/telegrambot/listener.d.ts.map +1 -1
- package/dist/src/platforms/telegrambot/types.d.ts.map +1 -1
- package/dist/src/platforms/webex/cli.d.ts.map +1 -1
- package/dist/src/platforms/webex/client.d.ts.map +1 -1
- package/dist/src/platforms/webex/client.js.map +1 -1
- package/dist/src/platforms/webex/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/webex/encryption.d.ts.map +1 -1
- package/dist/src/platforms/webex/kms-key-provider.d.ts.map +1 -1
- package/dist/src/platforms/webex/listener.d.ts.map +1 -1
- package/dist/src/platforms/webex/listener.js.map +1 -1
- package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/webex/types.d.ts +4 -4
- package/dist/src/platforms/webex/types.d.ts.map +1 -1
- package/dist/src/platforms/webexbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/webexbot/client.d.ts.map +1 -1
- package/dist/src/platforms/webexbot/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/webexbot/listener.d.ts.map +1 -1
- package/dist/src/platforms/webexbot/types.d.ts.map +1 -1
- package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/wechatbot/client.d.ts.map +1 -1
- package/dist/src/platforms/wechatbot/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/wechatbot/types.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/cli.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/types.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/types.js.map +1 -1
- package/dist/src/platforms/whatsappbot/cli.d.ts.map +1 -1
- package/dist/src/platforms/whatsappbot/client.d.ts.map +1 -1
- package/dist/src/platforms/whatsappbot/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/whatsappbot/types.d.ts.map +1 -1
- package/dist/src/shared/chromium/browsers.js.map +1 -1
- package/dist/src/shared/chromium/cli-options.js.map +1 -1
- package/dist/src/shared/chromium/cookie-reader.d.ts.map +1 -1
- package/dist/src/shared/chromium/decryptor.d.ts.map +1 -1
- package/dist/src/shared/sqlite.js.map +1 -1
- package/dist/src/shared/utils/derived-key-cache.d.ts.map +1 -1
- package/dist/src/tui/adapters/channeltalk-adapter.d.ts.map +1 -1
- package/dist/src/tui/adapters/discord-adapter.d.ts.map +1 -1
- package/dist/src/tui/adapters/imessage-adapter.d.ts.map +1 -1
- package/dist/src/tui/adapters/instagram-adapter.d.ts.map +1 -1
- package/dist/src/tui/adapters/kakaotalk-adapter.d.ts.map +1 -1
- package/dist/src/tui/adapters/line-adapter.d.ts.map +1 -1
- package/dist/src/tui/adapters/slack-adapter.d.ts.map +1 -1
- package/dist/src/tui/adapters/teams-adapter.d.ts.map +1 -1
- package/dist/src/tui/adapters/telegram-adapter.d.ts.map +1 -1
- package/dist/src/tui/adapters/telegram-adapter.js.map +1 -1
- package/dist/src/tui/adapters/webex-adapter.d.ts.map +1 -1
- package/dist/src/tui/adapters/whatsapp-adapter.d.ts.map +1 -1
- package/dist/src/tui/app.js.map +1 -1
- package/dist/src/tui/views/channel-picker.d.ts.map +1 -1
- package/dist/src/tui/views/workspace-picker.d.ts.map +1 -1
- package/docs/content/docs/cli/kakaotalk.mdx +16 -1
- package/docs/content/docs/sdk/kakaotalk.mdx +39 -0
- package/package.json +2 -2
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +1 -1
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-imessage/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +19 -2
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-telegrambot/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-webexbot/SKILL.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +1 -1
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/discord/client.test.ts +121 -0
- package/src/platforms/discord/client.ts +45 -22
- package/src/platforms/discord/types.ts +16 -0
- package/src/platforms/kakaotalk/client.test.ts +194 -1
- package/src/platforms/kakaotalk/client.ts +58 -0
- package/src/platforms/kakaotalk/commands/chat.test.ts +24 -0
- package/src/platforms/kakaotalk/commands/chat.ts +23 -0
- package/src/platforms/kakaotalk/index.test.ts +10 -0
- package/src/platforms/kakaotalk/index.ts +4 -0
- package/src/platforms/kakaotalk/protocol/session.test.ts +113 -0
- package/src/platforms/kakaotalk/protocol/session.ts +56 -0
- package/src/platforms/kakaotalk/types.ts +24 -0
- package/tsconfig.json +8 -8
|
@@ -126,6 +126,43 @@ await client.sendAttachment('9876543210', [
|
|
|
126
126
|
|
|
127
127
|
The typed helpers `sendPhoto`, `sendVideo`, `sendAudio`, `sendFile`, and `sendMultiPhoto` remain available when you want to bypass MIME sniffing and dispatch to a specific message_type explicitly.
|
|
128
128
|
|
|
129
|
+
### Leave Chat
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
const result = await client.leaveChat('9876543210')
|
|
133
|
+
// → { success: true, status_code: 0, chat_id: '9876543210' }
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Leaves a chat room by its chat ID. Works on 1:1, group, open, and PlusChat rooms. Returns `success: true` on a successful leave. The departed chat is immediately evicted from the client's cached chat list — a subsequent `getChats()` call will not include it.
|
|
137
|
+
|
|
138
|
+
### Typing Indicator
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// Fire a single typing pulse — the recipient sees the animated "…" dots
|
|
142
|
+
const result = await client.sendTyping('9876543210')
|
|
143
|
+
// → KakaoTypingResult { success: true, status_code: 0, chat_id: '9876543210' }
|
|
144
|
+
|
|
145
|
+
// For OpenChat rooms, pass the linkId
|
|
146
|
+
await client.sendTyping('9876543210', { linkId: '77777' })
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
The KakaoTalk client-side lifetime is ~5s per pulse — to keep the indicator visible while generating a long reply, re-fire faster than that (e.g. every 3s):
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
const interval = setInterval(() => {
|
|
153
|
+
client.sendTyping('9876543210').catch(() => {}) // ignore transient errors
|
|
154
|
+
}, 3000)
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const reply = await generateReply()
|
|
158
|
+
await client.sendMessage('9876543210', reply)
|
|
159
|
+
} finally {
|
|
160
|
+
clearInterval(interval)
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Errors follow the same shape as `markRead`: transport-level failures (non-zero `statusCode`, including the synthetic `-1` LocoConnection emits on disconnect) throw `KakaoTalkError('send_typing_failed')` so `executeWithReconnect` can retry on a fresh session. Non-numeric `chatId` / `linkId` throw `invalid_chat_id` / `invalid_link_id`.
|
|
165
|
+
|
|
129
166
|
### Cleanup
|
|
130
167
|
|
|
131
168
|
```typescript
|
|
@@ -239,6 +276,7 @@ import type {
|
|
|
239
276
|
KakaoMember,
|
|
240
277
|
KakaoMessage,
|
|
241
278
|
KakaoSendResult,
|
|
279
|
+
KakaoTypingResult,
|
|
242
280
|
KakaoAccountCredentials,
|
|
243
281
|
KakaoConfig,
|
|
244
282
|
KakaoDeviceType,
|
|
@@ -256,6 +294,7 @@ import {
|
|
|
256
294
|
KakaoMemberSchema,
|
|
257
295
|
KakaoMessageSchema,
|
|
258
296
|
KakaoSendResultSchema,
|
|
297
|
+
KakaoTypingResultSchema,
|
|
259
298
|
KakaoAccountCredentialsSchema,
|
|
260
299
|
KakaoConfigSchema,
|
|
261
300
|
} from 'agent-messenger/kakaotalk'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-messenger",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.31.0",
|
|
4
4
|
"description": "Multi-platform messaging CLI for AI agents (Slack, Discord, Teams, Webex, Telegram, Telegram Bot, WhatsApp, LINE, iMessage, Instagram, KakaoTalk, Channel Talk)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
"oxlint": "^1.51.0",
|
|
212
212
|
"tsc-alias": "^1.8.16",
|
|
213
213
|
"tsx": "^4.21.0",
|
|
214
|
-
"typescript": "^
|
|
214
|
+
"typescript": "^7.0.2"
|
|
215
215
|
},
|
|
216
216
|
"optionalDependencies": {
|
|
217
217
|
"koffi": "^2.15.2",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-channeltalk
|
|
3
3
|
description: Interact with Channel Talk using extracted desktop app or browser credentials - read chats, send messages, search messages, manage groups
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.31.0
|
|
5
5
|
allowed-tools: Bash(agent-channeltalk:*)
|
|
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.
|
|
4
|
+
version: 2.31.0
|
|
5
5
|
allowed-tools: Bash(agent-kakaotalk:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -303,6 +303,23 @@ Output includes:
|
|
|
303
303
|
- `unread_count` — unread message count
|
|
304
304
|
- `last_message` — most recent message preview, including `author_name` when the sender's nickname is known from the chat list (otherwise `null`)
|
|
305
305
|
|
|
306
|
+
### Chat Leave
|
|
307
|
+
|
|
308
|
+
```bash
|
|
309
|
+
# Leave a chat room (works on 1:1, group, open, and PlusChat rooms)
|
|
310
|
+
agent-kakaotalk chat leave <chat-id>
|
|
311
|
+
agent-kakaotalk chat leave <chat-id> --pretty
|
|
312
|
+
agent-kakaotalk chat leave <chat-id> --account <account-id>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
Output (JSON by default; `--pretty` pretty-prints the same JSON):
|
|
316
|
+
|
|
317
|
+
```json
|
|
318
|
+
{ "success": true, "status_code": 0, "chat_id": "9876543210" }
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
The process exits non-zero when `success` is `false`.
|
|
322
|
+
|
|
306
323
|
### Member Commands
|
|
307
324
|
|
|
308
325
|
```bash
|
|
@@ -623,7 +640,7 @@ See the [KakaoTalk SDK documentation](https://agent-messenger.dev/docs/sdk/kakao
|
|
|
623
640
|
## Limitations
|
|
624
641
|
|
|
625
642
|
- Auto-extraction of email/password from the desktop app is **macOS and Windows only** (KakaoTalk desktop is not available on Linux). Linux users must pass `--email` and `--password` (or `--password-file`) explicitly — the LOCO protocol, login flow, and all messaging features work on Linux.
|
|
626
|
-
- No
|
|
643
|
+
- No chat room creation
|
|
627
644
|
- No friend list management
|
|
628
645
|
- No reactions or emoji
|
|
629
646
|
- No message editing or deletion
|
|
@@ -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.
|
|
4
|
+
version: 2.31.0
|
|
5
5
|
allowed-tools: Bash(agent-webexbot:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -447,6 +447,127 @@ describe('DiscordClient', () => {
|
|
|
447
447
|
})
|
|
448
448
|
})
|
|
449
449
|
|
|
450
|
+
describe('searchMessages', () => {
|
|
451
|
+
const searchResult = {
|
|
452
|
+
id: 'msg1',
|
|
453
|
+
channel_id: 'ch1',
|
|
454
|
+
guild_id: 'guild1',
|
|
455
|
+
author: { id: 'user1', username: 'user1' },
|
|
456
|
+
content: 'matching message',
|
|
457
|
+
timestamp: '2026-01-01T00:00:00.000Z',
|
|
458
|
+
hit: true,
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
it('flattens grouped search results and keeps hits', async () => {
|
|
462
|
+
mockResponse({
|
|
463
|
+
doing_deep_historical_index: false,
|
|
464
|
+
total_results: 1,
|
|
465
|
+
messages: [[searchResult, { ...searchResult, id: 'context', hit: false }]],
|
|
466
|
+
})
|
|
467
|
+
|
|
468
|
+
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
469
|
+
const result = await client.searchMessages('guild1', 'matching', { limit: 25 })
|
|
470
|
+
|
|
471
|
+
expect(result).toEqual({ results: [searchResult], total: 1 })
|
|
472
|
+
expect(fetchCalls[0].url).toBe(
|
|
473
|
+
'https://discord.com/api/v10/guilds/guild1/messages/search?content=matching&limit=25',
|
|
474
|
+
)
|
|
475
|
+
})
|
|
476
|
+
|
|
477
|
+
it('retries when the search index is not ready', async () => {
|
|
478
|
+
mockResponse(
|
|
479
|
+
{
|
|
480
|
+
message: 'Index not yet available. Try again later',
|
|
481
|
+
code: 110000,
|
|
482
|
+
documents_indexed: 0,
|
|
483
|
+
retry_after: 0,
|
|
484
|
+
},
|
|
485
|
+
202,
|
|
486
|
+
)
|
|
487
|
+
mockResponse({
|
|
488
|
+
doing_deep_historical_index: false,
|
|
489
|
+
total_results: 1,
|
|
490
|
+
messages: [[searchResult]],
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
494
|
+
const startTime = Date.now()
|
|
495
|
+
const result = await client.searchMessages('guild1', 'matching')
|
|
496
|
+
const elapsed = Date.now() - startTime
|
|
497
|
+
|
|
498
|
+
expect(result).toEqual({ results: [searchResult], total: 1 })
|
|
499
|
+
expect(elapsed).toBeGreaterThanOrEqual(50)
|
|
500
|
+
expect(fetchCalls).toHaveLength(2)
|
|
501
|
+
})
|
|
502
|
+
|
|
503
|
+
it('throws a clear error when the search index remains unavailable', async () => {
|
|
504
|
+
for (let attempt = 0; attempt < 4; attempt++) {
|
|
505
|
+
mockResponse(
|
|
506
|
+
{
|
|
507
|
+
message: 'Index not yet available. Try again later',
|
|
508
|
+
code: 110000,
|
|
509
|
+
documents_indexed: 0,
|
|
510
|
+
retry_after: 0.001,
|
|
511
|
+
},
|
|
512
|
+
202,
|
|
513
|
+
)
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
517
|
+
|
|
518
|
+
await expect(client.searchMessages('guild1', 'matching')).rejects.toThrow(
|
|
519
|
+
'Search index is not ready after retries',
|
|
520
|
+
)
|
|
521
|
+
expect(fetchCalls).toHaveLength(4)
|
|
522
|
+
})
|
|
523
|
+
|
|
524
|
+
it('rejects unexpected successful response payloads', async () => {
|
|
525
|
+
mockResponse(
|
|
526
|
+
{
|
|
527
|
+
message: 'Unexpected response',
|
|
528
|
+
code: 999999,
|
|
529
|
+
retry_after: 0,
|
|
530
|
+
},
|
|
531
|
+
202,
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
535
|
+
|
|
536
|
+
try {
|
|
537
|
+
await client.searchMessages('guild1', 'matching')
|
|
538
|
+
throw new Error('Expected searchMessages to throw')
|
|
539
|
+
} catch (error) {
|
|
540
|
+
expect(error).toBeInstanceOf(DiscordError)
|
|
541
|
+
if (!(error instanceof DiscordError)) throw error
|
|
542
|
+
expect(error.code).toBe('invalid_search_response')
|
|
543
|
+
}
|
|
544
|
+
expect(fetchCalls).toHaveLength(1)
|
|
545
|
+
})
|
|
546
|
+
|
|
547
|
+
it('rejects malformed message and retry payloads', async () => {
|
|
548
|
+
mockResponse({ total_results: 1, messages: null })
|
|
549
|
+
mockResponse(
|
|
550
|
+
{
|
|
551
|
+
message: 'Index not yet available. Try again later',
|
|
552
|
+
code: 110000,
|
|
553
|
+
documents_indexed: 0,
|
|
554
|
+
retry_after: -1,
|
|
555
|
+
},
|
|
556
|
+
202,
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
const client = await new DiscordClient().login({ token: 'test-token' })
|
|
560
|
+
|
|
561
|
+
await expect(client.searchMessages('guild1', 'matching')).rejects.toThrow(
|
|
562
|
+
'Discord returned an invalid search response',
|
|
563
|
+
)
|
|
564
|
+
await expect(client.searchMessages('guild1', 'matching')).rejects.toThrow(
|
|
565
|
+
'Discord returned an invalid search response',
|
|
566
|
+
)
|
|
567
|
+
expect(fetchCalls).toHaveLength(2)
|
|
568
|
+
})
|
|
569
|
+
})
|
|
570
|
+
|
|
450
571
|
describe('rate limiting', () => {
|
|
451
572
|
it('waits when bucket is exhausted before making request', async () => {
|
|
452
573
|
mockResponse({ id: '1', username: 'user1' }, 200, {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises'
|
|
2
2
|
|
|
3
3
|
import { getDiscordHeaders } from './super-properties'
|
|
4
|
+
import { DiscordSearchIndexNotReadyResponseSchema, DiscordSearchResponseSchema } from './types'
|
|
4
5
|
import type {
|
|
5
6
|
DiscordChannel,
|
|
6
7
|
DiscordDMChannel,
|
|
@@ -11,7 +12,6 @@ import type {
|
|
|
11
12
|
DiscordMessage,
|
|
12
13
|
DiscordRelationship,
|
|
13
14
|
DiscordSearchOptions,
|
|
14
|
-
DiscordSearchResponse,
|
|
15
15
|
DiscordSearchResult,
|
|
16
16
|
DiscordUser,
|
|
17
17
|
DiscordUserNote,
|
|
@@ -37,6 +37,7 @@ interface RateLimitBucket {
|
|
|
37
37
|
const BASE_URL = 'https://discord.com/api/v10'
|
|
38
38
|
const MAX_RETRIES = 3
|
|
39
39
|
const BASE_BACKOFF_MS = 100
|
|
40
|
+
const MAX_SEARCH_INDEX_RETRY_MS = 30_000
|
|
40
41
|
|
|
41
42
|
export class DiscordClient {
|
|
42
43
|
private token: string | null = null
|
|
@@ -393,28 +394,50 @@ export class DiscordClient {
|
|
|
393
394
|
params.set('offset', options.offset.toString())
|
|
394
395
|
}
|
|
395
396
|
|
|
396
|
-
const
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
.
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
397
|
+
const path = `/guilds/${guildId}/messages/search?${params.toString()}`
|
|
398
|
+
|
|
399
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
400
|
+
const response: unknown = await this.request('GET', path)
|
|
401
|
+
const searchResponse = DiscordSearchResponseSchema.safeParse(response)
|
|
402
|
+
|
|
403
|
+
if (searchResponse.success) {
|
|
404
|
+
const results = searchResponse.data.messages
|
|
405
|
+
.flat()
|
|
406
|
+
.filter((msg) => msg.hit)
|
|
407
|
+
.map((msg) => ({
|
|
408
|
+
id: msg.id,
|
|
409
|
+
channel_id: msg.channel_id,
|
|
410
|
+
guild_id: msg.guild_id,
|
|
411
|
+
content: msg.content,
|
|
412
|
+
author: msg.author,
|
|
413
|
+
timestamp: msg.timestamp,
|
|
414
|
+
hit: msg.hit,
|
|
415
|
+
}))
|
|
416
|
+
|
|
417
|
+
return {
|
|
418
|
+
results,
|
|
419
|
+
total: searchResponse.data.total_results,
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const indexNotReadyResponse = DiscordSearchIndexNotReadyResponseSchema.safeParse(response)
|
|
424
|
+
if (!indexNotReadyResponse.success) {
|
|
425
|
+
throw new DiscordError('Discord returned an invalid search response', 'invalid_search_response')
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (attempt === MAX_RETRIES) {
|
|
429
|
+
throw new DiscordError('Search index is not ready after retries', 'search_index_not_ready')
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const requestedDelayMs = indexNotReadyResponse.data.retry_after * 1000
|
|
433
|
+
const retryDelayMs = Math.min(
|
|
434
|
+
requestedDelayMs === 0 ? BASE_BACKOFF_MS : requestedDelayMs,
|
|
435
|
+
MAX_SEARCH_INDEX_RETRY_MS,
|
|
436
|
+
)
|
|
437
|
+
await this.sleep(retryDelayMs)
|
|
417
438
|
}
|
|
439
|
+
|
|
440
|
+
throw new DiscordError('Search failed after retries', 'max_retries')
|
|
418
441
|
}
|
|
419
442
|
|
|
420
443
|
async getUserProfile(userId: string): Promise<DiscordUserProfile> {
|
|
@@ -109,6 +109,15 @@ export interface DiscordSearchResponse {
|
|
|
109
109
|
messages: DiscordSearchResult[][]
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
export interface DiscordSearchIndexNotReadyResponse {
|
|
113
|
+
message: string
|
|
114
|
+
code: 110000
|
|
115
|
+
documents_indexed: number
|
|
116
|
+
retry_after: number
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export type DiscordSearchApiResponse = DiscordSearchResponse | DiscordSearchIndexNotReadyResponse
|
|
120
|
+
|
|
112
121
|
export interface DiscordSearchOptions {
|
|
113
122
|
channelId?: string
|
|
114
123
|
authorId?: string
|
|
@@ -267,6 +276,13 @@ export const DiscordSearchResponseSchema = z.object({
|
|
|
267
276
|
messages: z.array(z.array(DiscordSearchResultSchema)),
|
|
268
277
|
})
|
|
269
278
|
|
|
279
|
+
export const DiscordSearchIndexNotReadyResponseSchema = z.object({
|
|
280
|
+
message: z.string(),
|
|
281
|
+
code: z.literal(110000),
|
|
282
|
+
documents_indexed: z.number(),
|
|
283
|
+
retry_after: z.number().nonnegative(),
|
|
284
|
+
})
|
|
285
|
+
|
|
270
286
|
export const DiscordCredentialsSchema = z.object({
|
|
271
287
|
token: z.string(),
|
|
272
288
|
})
|