agent-messenger 2.12.0 → 2.12.2

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 (121) hide show
  1. package/.claude-plugin/README.md +11 -1
  2. package/.claude-plugin/marketplace.json +14 -1
  3. package/.claude-plugin/plugin.json +4 -2
  4. package/CONTRIBUTING.md +12 -0
  5. package/README.md +30 -4
  6. package/dist/package.json +10 -2
  7. package/dist/src/cli.d.ts.map +1 -1
  8. package/dist/src/cli.js +3 -0
  9. package/dist/src/cli.js.map +1 -1
  10. package/dist/src/platforms/kakaotalk/client.d.ts +22 -0
  11. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  12. package/dist/src/platforms/kakaotalk/client.js +93 -7
  13. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  14. package/dist/src/platforms/kakaotalk/listener.d.ts +4 -7
  15. package/dist/src/platforms/kakaotalk/listener.d.ts.map +1 -1
  16. package/dist/src/platforms/kakaotalk/listener.js +43 -72
  17. package/dist/src/platforms/kakaotalk/listener.js.map +1 -1
  18. package/dist/src/platforms/telegrambot/cli.d.ts +5 -0
  19. package/dist/src/platforms/telegrambot/cli.d.ts.map +1 -0
  20. package/dist/src/platforms/telegrambot/cli.js +29 -0
  21. package/dist/src/platforms/telegrambot/cli.js.map +1 -0
  22. package/dist/src/platforms/telegrambot/client.d.ts +85 -0
  23. package/dist/src/platforms/telegrambot/client.d.ts.map +1 -0
  24. package/dist/src/platforms/telegrambot/client.js +282 -0
  25. package/dist/src/platforms/telegrambot/client.js.map +1 -0
  26. package/dist/src/platforms/telegrambot/commands/auth.d.ts +31 -0
  27. package/dist/src/platforms/telegrambot/commands/auth.d.ts.map +1 -0
  28. package/dist/src/platforms/telegrambot/commands/auth.js +173 -0
  29. package/dist/src/platforms/telegrambot/commands/auth.js.map +1 -0
  30. package/dist/src/platforms/telegrambot/commands/chat.d.ts +25 -0
  31. package/dist/src/platforms/telegrambot/commands/chat.d.ts.map +1 -0
  32. package/dist/src/platforms/telegrambot/commands/chat.js +69 -0
  33. package/dist/src/platforms/telegrambot/commands/chat.js.map +1 -0
  34. package/dist/src/platforms/telegrambot/commands/index.d.ts +6 -0
  35. package/dist/src/platforms/telegrambot/commands/index.d.ts.map +1 -0
  36. package/dist/src/platforms/telegrambot/commands/index.js +6 -0
  37. package/dist/src/platforms/telegrambot/commands/index.js.map +1 -0
  38. package/dist/src/platforms/telegrambot/commands/message.d.ts +39 -0
  39. package/dist/src/platforms/telegrambot/commands/message.d.ts.map +1 -0
  40. package/dist/src/platforms/telegrambot/commands/message.js +145 -0
  41. package/dist/src/platforms/telegrambot/commands/message.js.map +1 -0
  42. package/dist/src/platforms/telegrambot/commands/reaction.d.ts +16 -0
  43. package/dist/src/platforms/telegrambot/commands/reaction.d.ts.map +1 -0
  44. package/dist/src/platforms/telegrambot/commands/reaction.js +49 -0
  45. package/dist/src/platforms/telegrambot/commands/reaction.js.map +1 -0
  46. package/dist/src/platforms/telegrambot/commands/shared.d.ts +12 -0
  47. package/dist/src/platforms/telegrambot/commands/shared.d.ts.map +1 -0
  48. package/dist/src/platforms/telegrambot/commands/shared.js +21 -0
  49. package/dist/src/platforms/telegrambot/commands/shared.js.map +1 -0
  50. package/dist/src/platforms/telegrambot/commands/whoami.d.ts +17 -0
  51. package/dist/src/platforms/telegrambot/commands/whoami.d.ts.map +1 -0
  52. package/dist/src/platforms/telegrambot/commands/whoami.js +30 -0
  53. package/dist/src/platforms/telegrambot/commands/whoami.js.map +1 -0
  54. package/dist/src/platforms/telegrambot/credential-manager.d.ts +17 -0
  55. package/dist/src/platforms/telegrambot/credential-manager.d.ts.map +1 -0
  56. package/dist/src/platforms/telegrambot/credential-manager.js +113 -0
  57. package/dist/src/platforms/telegrambot/credential-manager.js.map +1 -0
  58. package/dist/src/platforms/telegrambot/index.d.ts +7 -0
  59. package/dist/src/platforms/telegrambot/index.d.ts.map +1 -0
  60. package/dist/src/platforms/telegrambot/index.js +5 -0
  61. package/dist/src/platforms/telegrambot/index.js.map +1 -0
  62. package/dist/src/platforms/telegrambot/listener.d.ts +30 -0
  63. package/dist/src/platforms/telegrambot/listener.d.ts.map +1 -0
  64. package/dist/src/platforms/telegrambot/listener.js +186 -0
  65. package/dist/src/platforms/telegrambot/listener.js.map +1 -0
  66. package/dist/src/platforms/telegrambot/types.d.ts +256 -0
  67. package/dist/src/platforms/telegrambot/types.d.ts.map +1 -0
  68. package/dist/src/platforms/telegrambot/types.js +96 -0
  69. package/dist/src/platforms/telegrambot/types.js.map +1 -0
  70. package/docs/content/docs/cli/meta.json +1 -0
  71. package/docs/content/docs/cli/telegrambot.mdx +149 -0
  72. package/docs/content/docs/index.mdx +10 -9
  73. package/docs/content/docs/quick-start.mdx +2 -0
  74. package/docs/content/docs/sdk/meta.json +1 -0
  75. package/docs/content/docs/sdk/telegrambot.mdx +216 -0
  76. package/e2e/config.ts +24 -0
  77. package/e2e/helpers.ts +1 -0
  78. package/e2e/telegrambot.e2e.test.ts +185 -0
  79. package/examples/telegrambot-listen.ts +54 -0
  80. package/package.json +10 -2
  81. package/scripts/postbuild.ts +1 -0
  82. package/skills/agent-channeltalk/SKILL.md +1 -1
  83. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  84. package/skills/agent-discord/SKILL.md +1 -1
  85. package/skills/agent-discordbot/SKILL.md +1 -1
  86. package/skills/agent-instagram/SKILL.md +1 -1
  87. package/skills/agent-kakaotalk/SKILL.md +12 -5
  88. package/skills/agent-line/SKILL.md +1 -1
  89. package/skills/agent-slack/SKILL.md +1 -1
  90. package/skills/agent-slackbot/SKILL.md +1 -1
  91. package/skills/agent-teams/SKILL.md +1 -1
  92. package/skills/agent-telegram/SKILL.md +1 -1
  93. package/skills/agent-telegrambot/SKILL.md +357 -0
  94. package/skills/agent-webex/SKILL.md +1 -1
  95. package/skills/agent-wechatbot/SKILL.md +1 -1
  96. package/skills/agent-whatsapp/SKILL.md +1 -1
  97. package/skills/agent-whatsappbot/SKILL.md +1 -1
  98. package/src/cli.ts +4 -0
  99. package/src/platforms/kakaotalk/client-listener-integration.test.ts +411 -0
  100. package/src/platforms/kakaotalk/client.test.ts +3 -0
  101. package/src/platforms/kakaotalk/client.ts +108 -9
  102. package/src/platforms/kakaotalk/listener.test.ts +155 -149
  103. package/src/platforms/kakaotalk/listener.ts +46 -80
  104. package/src/platforms/telegrambot/cli.ts +34 -0
  105. package/src/platforms/telegrambot/client.test.ts +454 -0
  106. package/src/platforms/telegrambot/client.ts +404 -0
  107. package/src/platforms/telegrambot/commands/auth.test.ts +244 -0
  108. package/src/platforms/telegrambot/commands/auth.ts +220 -0
  109. package/src/platforms/telegrambot/commands/chat.ts +96 -0
  110. package/src/platforms/telegrambot/commands/index.ts +5 -0
  111. package/src/platforms/telegrambot/commands/message.ts +235 -0
  112. package/src/platforms/telegrambot/commands/reaction.ts +70 -0
  113. package/src/platforms/telegrambot/commands/shared.ts +32 -0
  114. package/src/platforms/telegrambot/commands/whoami.ts +45 -0
  115. package/src/platforms/telegrambot/credential-manager.test.ts +196 -0
  116. package/src/platforms/telegrambot/credential-manager.ts +141 -0
  117. package/src/platforms/telegrambot/index.ts +44 -0
  118. package/src/platforms/telegrambot/listener.test.ts +398 -0
  119. package/src/platforms/telegrambot/listener.ts +198 -0
  120. package/src/platforms/telegrambot/types.test.ts +128 -0
  121. package/src/platforms/telegrambot/types.ts +282 -0
@@ -0,0 +1,216 @@
1
+ ---
2
+ title: Telegram Bot
3
+ description: TypeScript SDK reference for Telegram Bot — client, real-time long-polling listener, credential management, and types.
4
+ ---
5
+
6
+ ## Installation
7
+
8
+ ```bash
9
+ npm install agent-messenger
10
+ ```
11
+
12
+ ```typescript
13
+ import {
14
+ TelegramBotClient,
15
+ TelegramBotCredentialManager,
16
+ TelegramBotError,
17
+ TelegramBotListener,
18
+ } from 'agent-messenger/telegrambot'
19
+ ```
20
+
21
+ ## TelegramBotClient
22
+
23
+ The main client wrapping Telegram's HTTP [Bot API](https://core.telegram.org/bots/api). Includes automatic 429 (rate-limit) handling using `retry_after`, network retries with exponential backoff, and structured errors via `TelegramBotError`.
24
+
25
+ ```typescript
26
+ const client = await new TelegramBotClient().login({ token: 'YOUR_BOT_TOKEN' })
27
+ ```
28
+
29
+ Or use stored credentials — read from `~/.config/agent-messenger/telegrambot-credentials.json` (managed by `agent-telegrambot auth set`):
30
+
31
+ ```typescript
32
+ const client = await new TelegramBotClient().login()
33
+ ```
34
+
35
+ ### Authentication
36
+
37
+ ```typescript
38
+ const me = await client.getMe()
39
+ // → TelegramBotUser { id, is_bot, first_name, username?, ... }
40
+ ```
41
+
42
+ ### Messages
43
+
44
+ ```typescript
45
+ // Send text message
46
+ const msg = await client.sendMessage(chatId, 'Hello', {
47
+ parse_mode: 'HTML',
48
+ reply_to_message_id: 42,
49
+ disable_notification: true,
50
+ })
51
+
52
+ // Edit a chat message
53
+ await client.editMessageText({ chat_id: chatId, message_id: msg.message_id }, 'Edited text')
54
+
55
+ // Edit an inline-mode message (returned by inline query results)
56
+ await client.editMessageText({ inline_message_id: 'abc123' }, 'Edited text')
57
+
58
+ // Delete
59
+ await client.deleteMessage(chatId, msg.message_id)
60
+
61
+ // Forward
62
+ await client.forwardMessage(toChatId, fromChatId, messageId)
63
+
64
+ // Upload document (multipart)
65
+ await client.sendDocument(chatId, '/path/to/file.pdf', { caption: 'Report' })
66
+ ```
67
+
68
+ ### Chats
69
+
70
+ ```typescript
71
+ // Full chat info (title, description, member count, etc.)
72
+ const chat = await client.getChat(chatId)
73
+
74
+ // Single member's status
75
+ const member = await client.getChatMember(chatId, userId)
76
+
77
+ // Member count for groups, supergroups, channels
78
+ const count = await client.getChatMemberCount(chatId)
79
+ ```
80
+
81
+ ### Reactions
82
+
83
+ ```typescript
84
+ // Set a reaction (Telegram replaces previous bot reaction)
85
+ await client.setMessageReaction(chatId, messageId, [{ type: 'emoji', emoji: '👍' }])
86
+
87
+ // Clear all bot reactions
88
+ await client.setMessageReaction(chatId, messageId, [])
89
+ ```
90
+
91
+ ### Updates (Manual Polling)
92
+
93
+ If you don't need the listener abstraction, you can call `getUpdates` directly:
94
+
95
+ ```typescript
96
+ const updates = await client.getUpdates({
97
+ offset: 0,
98
+ limit: 100,
99
+ timeout: 30,
100
+ allowed_updates: ['message', 'callback_query'],
101
+ })
102
+ ```
103
+
104
+ ## TelegramBotListener
105
+
106
+ A long-polling listener for real-time updates. Telegram Bot API does not support WebSockets — long-polling is the canonical streaming mode used by frameworks like grammy and telegraf. No public HTTPS endpoint required.
107
+
108
+ ```typescript
109
+ import { TelegramBotClient, TelegramBotListener } from 'agent-messenger/telegrambot'
110
+
111
+ const client = await new TelegramBotClient().login({ token: 'YOUR_BOT_TOKEN' })
112
+ const listener = new TelegramBotListener(client, {
113
+ timeoutSeconds: 30,
114
+ limit: 100,
115
+ allowedUpdates: ['message', 'callback_query'],
116
+ dropPendingUpdates: false,
117
+ })
118
+
119
+ listener.on('connected', ({ user }) => {
120
+ console.log(`Connected as @${user.username}`)
121
+ })
122
+
123
+ listener.on('message', (message) => {
124
+ console.log(`${message.chat.id}: ${message.text}`)
125
+ })
126
+
127
+ listener.on('callback_query', (query) => {
128
+ console.log(`Button clicked: ${query.data}`)
129
+ })
130
+
131
+ listener.on('disconnected', () => {
132
+ console.log('Disconnected, will retry…')
133
+ })
134
+
135
+ listener.on('error', (error) => {
136
+ console.error('Fatal error:', error)
137
+ })
138
+
139
+ await listener.start()
140
+ ```
141
+
142
+ ### Lifecycle
143
+
144
+ - `start()` — Removes any active webhook (mutex with polling), calls `getMe` to confirm auth, then enters the polling loop.
145
+ - `stop()` — Aborts the in-flight `getUpdates` request and stops the loop. Safe to call multiple times.
146
+
147
+ ### Events
148
+
149
+ | Event | Payload | Notes |
150
+ | --------------------- | ----------------------------- | ----------------------------------------------------------------------- |
151
+ | `connected` | `{ user: TelegramBotUser }` | Once after `start()` succeeds |
152
+ | `disconnected` | `[]` | After a recoverable error; listener will retry with exponential backoff |
153
+ | `error` | `[Error]` | Fatal error (Unauthorized 401, Conflict 409). Listener stops. |
154
+ | `message` | `[TelegramMessage]` | New text/media messages in private chats and groups |
155
+ | `edited_message` | `[TelegramMessage]` | Message edited |
156
+ | `channel_post` | `[TelegramMessage]` | New post in a channel where the bot is admin |
157
+ | `edited_channel_post` | `[TelegramMessage]` | Channel post edited |
158
+ | `callback_query` | `[TelegramCallbackQuery]` | Inline keyboard button pressed |
159
+ | `inline_query` | `[TelegramInlineQuery]` | Inline-mode query (`@yourbot ...`) |
160
+ | `my_chat_member` | `[TelegramChatMemberUpdated]` | Bot's own membership status changed |
161
+ | `chat_member` | `[TelegramChatMemberUpdated]` | Other member's status changed (requires opt-in) |
162
+ | `telegram_update` | `[TelegramUpdate]` | Catch-all — every raw update |
163
+
164
+ ### Reconnection
165
+
166
+ On recoverable errors (network failure, 5xx, transient API errors), the listener emits `disconnected` and retries with exponential backoff capped at 30 seconds. On `Unauthorized` (401) or `Conflict` (409 — another polling instance is running), it emits `error` and stops; restart your process after fixing the cause.
167
+
168
+ ## TelegramBotCredentialManager
169
+
170
+ ```typescript
171
+ const manager = new TelegramBotCredentialManager()
172
+
173
+ // Store credentials
174
+ await manager.setCredentials({ token, bot_id, bot_name })
175
+
176
+ // Retrieve current
177
+ const creds = await manager.getCredentials()
178
+
179
+ // Multi-bot support
180
+ const all = await manager.listAll()
181
+ await manager.setCurrent('deploy')
182
+ await manager.removeBot('alert')
183
+ await manager.clearCredentials()
184
+ ```
185
+
186
+ Credentials are stored under `~/.config/agent-messenger/telegrambot-credentials.json` with `0600` file permissions. Override the directory with the `AGENT_MESSENGER_CONFIG_DIR` environment variable, or pass `new TelegramBotCredentialManager(customDir)`.
187
+
188
+ ## Error Handling
189
+
190
+ All client methods throw `TelegramBotError` with a `code` property:
191
+
192
+ | Code | Meaning |
193
+ | ------------------- | ----------------------------------------------- |
194
+ | `missing_token` | Empty token passed to `login` |
195
+ | `not_authenticated` | Used a method before `.login()` |
196
+ | `no_credentials` | No stored credentials and none provided |
197
+ | `unauthorized` | 401 — invalid token |
198
+ | `conflict` | 409 — another polling instance running |
199
+ | `forbidden` | 403 — bot kicked or user hasn't started the bot |
200
+ | `bad_request` | 400 — usually invalid chat ID or message ID |
201
+ | `not_found` | 404 |
202
+ | `rate_limited` | 429 (after retries exhausted) |
203
+ | `network_error` | Fetch failed after retries |
204
+ | `invalid_response` | Telegram returned non-JSON |
205
+
206
+ ```typescript
207
+ import { TelegramBotError } from 'agent-messenger/telegrambot'
208
+
209
+ try {
210
+ await client.sendMessage(chatId, 'Hello')
211
+ } catch (error) {
212
+ if (error instanceof TelegramBotError && error.code === 'forbidden') {
213
+ console.log('User has not started the bot yet')
214
+ }
215
+ }
216
+ ```
package/e2e/config.ts CHANGED
@@ -242,6 +242,30 @@ export async function validateTelegramEnvironment(): Promise<boolean> {
242
242
  return true
243
243
  }
244
244
 
245
+ // Telegram Bot Test Environment
246
+ export const TELEGRAMBOT_TEST_CHAT_ID = process.env.E2E_TELEGRAMBOT_CHAT_ID || ''
247
+
248
+ export async function validateTelegramBotEnvironment(): Promise<boolean> {
249
+ if (!TELEGRAMBOT_TEST_CHAT_ID) {
250
+ console.warn('Skipping Telegram Bot E2E: set E2E_TELEGRAMBOT_CHAT_ID to run against a dedicated test chat.')
251
+ return false
252
+ }
253
+
254
+ const { runCLI, parseJSON } = await import('./helpers')
255
+
256
+ const result = await runCLI('telegrambot', ['auth', 'status'])
257
+ if (result.exitCode !== 0) {
258
+ throw new Error('Telegram Bot authentication failed. Run: agent-telegrambot auth set <token>')
259
+ }
260
+
261
+ const data = parseJSON<{ valid: boolean }>(result.stdout)
262
+ if (!data?.valid) {
263
+ throw new Error('Telegram Bot credentials invalid or expired. Run: agent-telegrambot auth set <token>')
264
+ }
265
+
266
+ return true
267
+ }
268
+
245
269
  // WhatsApp Test Environment
246
270
  export const WHATSAPP_TEST_CHAT_ID = process.env.E2E_WHATSAPP_CHAT_ID || ''
247
271
 
package/e2e/helpers.ts CHANGED
@@ -15,6 +15,7 @@ export async function runCLI(platform: string, args: string[]): Promise<CLIResul
15
15
  channeltalk: 'agent-channeltalk',
16
16
  webex: 'agent-webex',
17
17
  telegram: 'agent-telegram',
18
+ telegrambot: 'agent-telegrambot',
18
19
  whatsapp: 'agent-whatsapp',
19
20
  whatsappbot: 'agent-whatsappbot',
20
21
  line: 'agent-line',
@@ -0,0 +1,185 @@
1
+ import { afterEach, beforeAll, describe, expect, it } from 'bun:test'
2
+
3
+ import { TELEGRAMBOT_TEST_CHAT_ID, validateTelegramBotEnvironment } from './config'
4
+ import { generateTestId, parseJSON, runCLI, waitForRateLimit } from './helpers'
5
+
6
+ let telegramBotAvailable = false
7
+ let trackedMessageIds: number[] = []
8
+
9
+ async function deleteTrackedMessages(): Promise<void> {
10
+ for (const messageId of trackedMessageIds) {
11
+ try {
12
+ await runCLI('telegrambot', ['message', 'delete', TELEGRAMBOT_TEST_CHAT_ID, String(messageId), '--force'])
13
+ await waitForRateLimit(300)
14
+ } catch {
15
+ // best-effort cleanup
16
+ }
17
+ }
18
+ trackedMessageIds = []
19
+ }
20
+
21
+ describe('Telegram Bot E2E Tests', () => {
22
+ beforeAll(async () => {
23
+ telegramBotAvailable = await validateTelegramBotEnvironment()
24
+ })
25
+
26
+ afterEach(async () => {
27
+ if (trackedMessageIds.length > 0) {
28
+ await deleteTrackedMessages()
29
+ }
30
+ await waitForRateLimit()
31
+ })
32
+
33
+ describe('auth', () => {
34
+ it('auth status returns valid bot info', async () => {
35
+ if (!telegramBotAvailable) return
36
+
37
+ const result = await runCLI('telegrambot', ['auth', 'status'])
38
+ expect(result.exitCode).toBe(0)
39
+
40
+ const data = parseJSON<{ valid: boolean; bot_id: string }>(result.stdout)
41
+ expect(data?.valid).toBe(true)
42
+ expect(data?.bot_id).toBeTruthy()
43
+ })
44
+
45
+ it('auth list returns bots array', async () => {
46
+ if (!telegramBotAvailable) return
47
+
48
+ const result = await runCLI('telegrambot', ['auth', 'list'])
49
+ expect(result.exitCode).toBe(0)
50
+
51
+ const data = parseJSON<{ bots: Array<{ bot_id: string }> }>(result.stdout)
52
+ expect(Array.isArray(data?.bots)).toBe(true)
53
+ })
54
+ })
55
+
56
+ describe('whoami', () => {
57
+ it('whoami returns bot identity', async () => {
58
+ if (!telegramBotAvailable) return
59
+
60
+ const result = await runCLI('telegrambot', ['whoami'])
61
+ expect(result.exitCode).toBe(0)
62
+
63
+ const data = parseJSON<{ id: number; is_bot: boolean }>(result.stdout)
64
+ expect(data?.id).toBeTruthy()
65
+ expect(data?.is_bot).toBe(true)
66
+ })
67
+ })
68
+
69
+ describe('chat', () => {
70
+ it('chat info returns chat details', async () => {
71
+ if (!telegramBotAvailable) return
72
+
73
+ const result = await runCLI('telegrambot', ['chat', 'info', TELEGRAMBOT_TEST_CHAT_ID])
74
+ expect(result.exitCode).toBe(0)
75
+
76
+ const data = parseJSON<{ id: number; type: string }>(result.stdout)
77
+ expect(data?.id).toBeTruthy()
78
+ expect(data?.type).toBeTruthy()
79
+ })
80
+ })
81
+
82
+ describe('message', () => {
83
+ it('message send delivers message and returns id', async () => {
84
+ if (!telegramBotAvailable) return
85
+
86
+ const testId = generateTestId()
87
+ const result = await runCLI('telegrambot', ['message', 'send', TELEGRAMBOT_TEST_CHAT_ID, `Bot e2e ${testId}`])
88
+ expect(result.exitCode).toBe(0)
89
+
90
+ const data = parseJSON<{ message: { message_id: number; chat_id: number; text: string } }>(result.stdout)
91
+ expect(data?.message?.message_id).toBeTruthy()
92
+ expect(data?.message?.text).toContain(testId)
93
+ if (data?.message?.message_id) trackedMessageIds.push(data.message.message_id)
94
+ })
95
+
96
+ it('message update edits previously sent message', async () => {
97
+ if (!telegramBotAvailable) return
98
+
99
+ const testId = generateTestId()
100
+ const sendResult = await runCLI('telegrambot', [
101
+ 'message',
102
+ 'send',
103
+ TELEGRAMBOT_TEST_CHAT_ID,
104
+ `Original ${testId}`,
105
+ ])
106
+ const sent = parseJSON<{ message: { message_id: number } }>(sendResult.stdout)
107
+ expect(sent?.message?.message_id).toBeTruthy()
108
+ if (sent?.message?.message_id) trackedMessageIds.push(sent.message.message_id)
109
+
110
+ await waitForRateLimit()
111
+
112
+ const updateResult = await runCLI('telegrambot', [
113
+ 'message',
114
+ 'update',
115
+ TELEGRAMBOT_TEST_CHAT_ID,
116
+ String(sent!.message.message_id),
117
+ `Edited ${testId}`,
118
+ ])
119
+ expect(updateResult.exitCode).toBe(0)
120
+ })
121
+
122
+ it('message delete removes message', async () => {
123
+ if (!telegramBotAvailable) return
124
+
125
+ const testId = generateTestId()
126
+ const sendResult = await runCLI('telegrambot', [
127
+ 'message',
128
+ 'send',
129
+ TELEGRAMBOT_TEST_CHAT_ID,
130
+ `Delete me ${testId}`,
131
+ ])
132
+ const sent = parseJSON<{ message: { message_id: number } }>(sendResult.stdout)
133
+ expect(sent?.message?.message_id).toBeTruthy()
134
+
135
+ await waitForRateLimit()
136
+
137
+ const deleteResult = await runCLI('telegrambot', [
138
+ 'message',
139
+ 'delete',
140
+ TELEGRAMBOT_TEST_CHAT_ID,
141
+ String(sent!.message.message_id),
142
+ '--force',
143
+ ])
144
+ expect(deleteResult.exitCode).toBe(0)
145
+ })
146
+ })
147
+
148
+ describe('reaction', () => {
149
+ it('reaction set and clear lifecycle', async () => {
150
+ if (!telegramBotAvailable) return
151
+
152
+ const testId = generateTestId()
153
+ const sendResult = await runCLI('telegrambot', [
154
+ 'message',
155
+ 'send',
156
+ TELEGRAMBOT_TEST_CHAT_ID,
157
+ `Reaction test ${testId}`,
158
+ ])
159
+ const sent = parseJSON<{ message: { message_id: number } }>(sendResult.stdout)
160
+ expect(sent?.message?.message_id).toBeTruthy()
161
+ if (sent?.message?.message_id) trackedMessageIds.push(sent.message.message_id)
162
+
163
+ await waitForRateLimit(2000)
164
+
165
+ const setResult = await runCLI('telegrambot', [
166
+ 'reaction',
167
+ 'set',
168
+ TELEGRAMBOT_TEST_CHAT_ID,
169
+ String(sent!.message.message_id),
170
+ '👍',
171
+ ])
172
+ expect(setResult.exitCode).toBe(0)
173
+
174
+ await waitForRateLimit(2000)
175
+
176
+ const clearResult = await runCLI('telegrambot', [
177
+ 'reaction',
178
+ 'clear',
179
+ TELEGRAMBOT_TEST_CHAT_ID,
180
+ String(sent!.message.message_id),
181
+ ])
182
+ expect(clearResult.exitCode).toBe(0)
183
+ }, 15000)
184
+ })
185
+ })
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env bun
2
+ // Run `agent-telegrambot auth set <token>` first so login() can pick up stored creds.
3
+ import { TelegramBotClient } from '../src/platforms/telegrambot/client'
4
+ import { TelegramBotListener } from '../src/platforms/telegrambot/listener'
5
+
6
+ async function main() {
7
+ const client = await new TelegramBotClient().login()
8
+
9
+ const listener = new TelegramBotListener(client, {
10
+ allowedUpdates: ['message', 'edited_message', 'callback_query', 'my_chat_member'],
11
+ })
12
+
13
+ listener.on('connected', ({ user }) => {
14
+ console.log(`Connected as @${user.username ?? user.first_name} (id: ${user.id})`)
15
+ console.log('Listening for events. Press Ctrl+C to stop.\n')
16
+ })
17
+
18
+ listener.on('disconnected', () => {
19
+ console.log('[disconnected] retrying...')
20
+ })
21
+
22
+ listener.on('message', (message) => {
23
+ if (message.from?.is_bot) return
24
+ const time = new Date(message.date * 1000).toLocaleTimeString()
25
+ const sender = message.from?.username ?? message.from?.first_name ?? 'unknown'
26
+ console.log(`[${time}] message in ${message.chat.id} <${sender}>: ${message.text ?? '(non-text)'}`)
27
+ })
28
+
29
+ listener.on('edited_message', (message) => {
30
+ console.log(`[edit] message ${message.message_id} in ${message.chat.id}`)
31
+ })
32
+
33
+ listener.on('callback_query', (query) => {
34
+ console.log(`[callback] data=${query.data ?? '(none)'} from ${query.from.username ?? query.from.id}`)
35
+ })
36
+
37
+ listener.on('my_chat_member', (event) => {
38
+ console.log(`[my_chat_member] ${event.chat.id}: ${event.old_chat_member.status} -> ${event.new_chat_member.status}`)
39
+ })
40
+
41
+ listener.on('error', (err) => {
42
+ console.error(`[error] ${err.message}`)
43
+ })
44
+
45
+ process.on('SIGINT', () => {
46
+ console.log('\nStopping...')
47
+ listener.stop()
48
+ process.exit(130)
49
+ })
50
+
51
+ await listener.start()
52
+ }
53
+
54
+ main()
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agent-messenger",
3
- "version": "2.12.0",
4
- "description": "Multi-platform messaging CLI for AI agents (Slack, Discord, Teams, Webex, Telegram, WhatsApp, LINE, Instagram, KakaoTalk, Channel Talk)",
3
+ "version": "2.12.2",
4
+ "description": "Multi-platform messaging CLI for AI agents (Slack, Discord, Teams, Webex, Telegram, Telegram Bot, WhatsApp, LINE, Instagram, KakaoTalk, Channel Talk)",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/agent-messenger/agent-messenger"
@@ -19,6 +19,7 @@
19
19
  "agent-slackbot": "dist/src/platforms/slackbot/cli.js",
20
20
  "agent-teams": "dist/src/platforms/teams/cli.js",
21
21
  "agent-telegram": "dist/src/platforms/telegram/cli.js",
22
+ "agent-telegrambot": "dist/src/platforms/telegrambot/cli.js",
22
23
  "agent-webex": "dist/src/platforms/webex/cli.js",
23
24
  "agent-wechatbot": "dist/src/platforms/wechatbot/cli.js",
24
25
  "agent-whatsapp": "dist/src/platforms/whatsapp/cli.js",
@@ -71,6 +72,9 @@
71
72
  ],
72
73
  "channeltalkbot": [
73
74
  "./dist/src/platforms/channeltalkbot/index.d.ts"
75
+ ],
76
+ "telegrambot": [
77
+ "./dist/src/platforms/telegrambot/index.d.ts"
74
78
  ]
75
79
  }
76
80
  },
@@ -135,6 +139,10 @@
135
139
  "./channeltalkbot": {
136
140
  "types": "./dist/src/platforms/channeltalkbot/index.d.ts",
137
141
  "default": "./dist/src/platforms/channeltalkbot/index.js"
142
+ },
143
+ "./telegrambot": {
144
+ "types": "./dist/src/platforms/telegrambot/index.d.ts",
145
+ "default": "./dist/src/platforms/telegrambot/index.js"
138
146
  }
139
147
  },
140
148
  "scripts": {
@@ -9,6 +9,7 @@ const cliFiles = [
9
9
  'dist/src/platforms/channeltalk/cli.js',
10
10
  'dist/src/platforms/channeltalkbot/cli.js',
11
11
  'dist/src/platforms/telegram/cli.js',
12
+ 'dist/src/platforms/telegrambot/cli.js',
12
13
  ]
13
14
 
14
15
  for (const file of cliFiles) {
@@ -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.12.0
4
+ version: 2.12.2
5
5
  allowed-tools: Bash(agent-channeltalk:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-channeltalkbot
3
3
  description: Interact with Channel Talk workspaces using API credentials - send messages, read chats, manage groups and bots
4
- version: 2.12.0
4
+ version: 2.12.2
5
5
  allowed-tools: Bash(agent-channeltalkbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-discord
3
3
  description: Interact with Discord servers - send messages, read channels, manage reactions
4
- version: 2.12.0
4
+ version: 2.12.2
5
5
  allowed-tools: Bash(agent-discord:*)
6
6
  metadata:
7
7
  openclaw:
@@ -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.12.0
4
+ version: 2.12.2
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.12.0
4
+ version: 2.12.2
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.12.0
4
+ version: 2.12.2
5
5
  allowed-tools: Bash(agent-kakaotalk:*)
6
6
  metadata:
7
7
  openclaw:
@@ -56,7 +56,7 @@ Registers the CLI as a sub-device (tablet slot by default). Your desktop app kee
56
56
  agent-kakaotalk auth login
57
57
  ```
58
58
 
59
- In interactive mode, this prompts for email and password. The CLI first tries to extract cached credentials from the desktop app so you may not need to type anything.
59
+ In interactive mode, this prompts for email and password. On macOS and Windows, the CLI first tries to extract cached credentials from the desktop app so you may not need to type anything. On Linux there is no desktop app, so always pass credentials explicitly via `--email` and `--password` (or `--password-file`).
60
60
 
61
61
  For AI agents (non-interactive), provide credentials via flags:
62
62
 
@@ -488,8 +488,7 @@ See the [KakaoTalk SDK documentation](https://agent-messenger.dev/docs/sdk/kakao
488
488
 
489
489
  ## Limitations
490
490
 
491
- - macOS and Windows only (desktop app needed for auto-extracting email/password during login)
492
- - No Linux support (KakaoTalk desktop not available on Linux)
491
+ - 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.
493
492
  - No file upload or download
494
493
  - No channel/chat room creation or management
495
494
  - No friend list management
@@ -526,7 +525,15 @@ pnpm dlx --package agent-messenger agent-kakaotalk chat list --pretty
526
525
 
527
526
  ### Password prompt on fresh install
528
527
 
529
- On fresh installs, the desktop app (macOS or Windows) may hash or omit the password from its cache, so the CLI cannot extract it automatically. The CLI will prompt for the password once to register the device — via a native dialog on macOS (AppKit) and Windows (PowerShell WinForms), or via a TTY prompt if a terminal is available. After registration, the password is never needed again.
528
+ On fresh installs, the desktop app (macOS or Windows) may hash or omit the password from its cache, so the CLI cannot extract it automatically. The CLI will prompt for the password once to register the device — via a native dialog on macOS (AppKit), Windows (PowerShell WinForms), or Linux (`zenity` / `kdialog`), or via a TTY prompt if a terminal is available. After registration, the password is never needed again.
529
+
530
+ On Linux there is no desktop app to extract from, so always provide credentials explicitly:
531
+
532
+ ```bash
533
+ agent-kakaotalk auth login --email user@example.com --password-file /tmp/.kakao-pw
534
+ ```
535
+
536
+ `--password-file` reads the file then immediately deletes it, so the password never appears in shell history or process listings.
530
537
 
531
538
  When the CLI returns `{"next_action": "run_interactive", ...}`, use a tmux session to let the user type their password securely. See "Handling `run_interactive`" above for the exact steps.
532
539
 
@@ -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.12.0
4
+ version: 2.12.2
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.12.0
4
+ version: 2.12.2
5
5
  allowed-tools: Bash(agent-slack:*)
6
6
  metadata:
7
7
  openclaw:
@@ -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.12.0
4
+ version: 2.12.2
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.12.0
4
+ version: 2.12.2
5
5
  allowed-tools: Bash(agent-teams:*)
6
6
  metadata:
7
7
  openclaw: