agent-messenger 2.12.0 → 2.12.1

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 (108) 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/telegrambot/cli.d.ts +5 -0
  11. package/dist/src/platforms/telegrambot/cli.d.ts.map +1 -0
  12. package/dist/src/platforms/telegrambot/cli.js +29 -0
  13. package/dist/src/platforms/telegrambot/cli.js.map +1 -0
  14. package/dist/src/platforms/telegrambot/client.d.ts +85 -0
  15. package/dist/src/platforms/telegrambot/client.d.ts.map +1 -0
  16. package/dist/src/platforms/telegrambot/client.js +282 -0
  17. package/dist/src/platforms/telegrambot/client.js.map +1 -0
  18. package/dist/src/platforms/telegrambot/commands/auth.d.ts +31 -0
  19. package/dist/src/platforms/telegrambot/commands/auth.d.ts.map +1 -0
  20. package/dist/src/platforms/telegrambot/commands/auth.js +173 -0
  21. package/dist/src/platforms/telegrambot/commands/auth.js.map +1 -0
  22. package/dist/src/platforms/telegrambot/commands/chat.d.ts +25 -0
  23. package/dist/src/platforms/telegrambot/commands/chat.d.ts.map +1 -0
  24. package/dist/src/platforms/telegrambot/commands/chat.js +69 -0
  25. package/dist/src/platforms/telegrambot/commands/chat.js.map +1 -0
  26. package/dist/src/platforms/telegrambot/commands/index.d.ts +6 -0
  27. package/dist/src/platforms/telegrambot/commands/index.d.ts.map +1 -0
  28. package/dist/src/platforms/telegrambot/commands/index.js +6 -0
  29. package/dist/src/platforms/telegrambot/commands/index.js.map +1 -0
  30. package/dist/src/platforms/telegrambot/commands/message.d.ts +39 -0
  31. package/dist/src/platforms/telegrambot/commands/message.d.ts.map +1 -0
  32. package/dist/src/platforms/telegrambot/commands/message.js +145 -0
  33. package/dist/src/platforms/telegrambot/commands/message.js.map +1 -0
  34. package/dist/src/platforms/telegrambot/commands/reaction.d.ts +16 -0
  35. package/dist/src/platforms/telegrambot/commands/reaction.d.ts.map +1 -0
  36. package/dist/src/platforms/telegrambot/commands/reaction.js +49 -0
  37. package/dist/src/platforms/telegrambot/commands/reaction.js.map +1 -0
  38. package/dist/src/platforms/telegrambot/commands/shared.d.ts +12 -0
  39. package/dist/src/platforms/telegrambot/commands/shared.d.ts.map +1 -0
  40. package/dist/src/platforms/telegrambot/commands/shared.js +21 -0
  41. package/dist/src/platforms/telegrambot/commands/shared.js.map +1 -0
  42. package/dist/src/platforms/telegrambot/commands/whoami.d.ts +17 -0
  43. package/dist/src/platforms/telegrambot/commands/whoami.d.ts.map +1 -0
  44. package/dist/src/platforms/telegrambot/commands/whoami.js +30 -0
  45. package/dist/src/platforms/telegrambot/commands/whoami.js.map +1 -0
  46. package/dist/src/platforms/telegrambot/credential-manager.d.ts +17 -0
  47. package/dist/src/platforms/telegrambot/credential-manager.d.ts.map +1 -0
  48. package/dist/src/platforms/telegrambot/credential-manager.js +113 -0
  49. package/dist/src/platforms/telegrambot/credential-manager.js.map +1 -0
  50. package/dist/src/platforms/telegrambot/index.d.ts +7 -0
  51. package/dist/src/platforms/telegrambot/index.d.ts.map +1 -0
  52. package/dist/src/platforms/telegrambot/index.js +5 -0
  53. package/dist/src/platforms/telegrambot/index.js.map +1 -0
  54. package/dist/src/platforms/telegrambot/listener.d.ts +30 -0
  55. package/dist/src/platforms/telegrambot/listener.d.ts.map +1 -0
  56. package/dist/src/platforms/telegrambot/listener.js +186 -0
  57. package/dist/src/platforms/telegrambot/listener.js.map +1 -0
  58. package/dist/src/platforms/telegrambot/types.d.ts +256 -0
  59. package/dist/src/platforms/telegrambot/types.d.ts.map +1 -0
  60. package/dist/src/platforms/telegrambot/types.js +96 -0
  61. package/dist/src/platforms/telegrambot/types.js.map +1 -0
  62. package/docs/content/docs/cli/meta.json +1 -0
  63. package/docs/content/docs/cli/telegrambot.mdx +149 -0
  64. package/docs/content/docs/index.mdx +10 -9
  65. package/docs/content/docs/quick-start.mdx +2 -0
  66. package/docs/content/docs/sdk/meta.json +1 -0
  67. package/docs/content/docs/sdk/telegrambot.mdx +216 -0
  68. package/e2e/config.ts +24 -0
  69. package/e2e/helpers.ts +1 -0
  70. package/e2e/telegrambot.e2e.test.ts +185 -0
  71. package/examples/telegrambot-listen.ts +54 -0
  72. package/package.json +10 -2
  73. package/scripts/postbuild.ts +1 -0
  74. package/skills/agent-channeltalk/SKILL.md +1 -1
  75. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  76. package/skills/agent-discord/SKILL.md +1 -1
  77. package/skills/agent-discordbot/SKILL.md +1 -1
  78. package/skills/agent-instagram/SKILL.md +1 -1
  79. package/skills/agent-kakaotalk/SKILL.md +12 -5
  80. package/skills/agent-line/SKILL.md +1 -1
  81. package/skills/agent-slack/SKILL.md +1 -1
  82. package/skills/agent-slackbot/SKILL.md +1 -1
  83. package/skills/agent-teams/SKILL.md +1 -1
  84. package/skills/agent-telegram/SKILL.md +1 -1
  85. package/skills/agent-telegrambot/SKILL.md +357 -0
  86. package/skills/agent-webex/SKILL.md +1 -1
  87. package/skills/agent-wechatbot/SKILL.md +1 -1
  88. package/skills/agent-whatsapp/SKILL.md +1 -1
  89. package/skills/agent-whatsappbot/SKILL.md +1 -1
  90. package/src/cli.ts +4 -0
  91. package/src/platforms/telegrambot/cli.ts +34 -0
  92. package/src/platforms/telegrambot/client.test.ts +454 -0
  93. package/src/platforms/telegrambot/client.ts +404 -0
  94. package/src/platforms/telegrambot/commands/auth.test.ts +244 -0
  95. package/src/platforms/telegrambot/commands/auth.ts +220 -0
  96. package/src/platforms/telegrambot/commands/chat.ts +96 -0
  97. package/src/platforms/telegrambot/commands/index.ts +5 -0
  98. package/src/platforms/telegrambot/commands/message.ts +235 -0
  99. package/src/platforms/telegrambot/commands/reaction.ts +70 -0
  100. package/src/platforms/telegrambot/commands/shared.ts +32 -0
  101. package/src/platforms/telegrambot/commands/whoami.ts +45 -0
  102. package/src/platforms/telegrambot/credential-manager.test.ts +196 -0
  103. package/src/platforms/telegrambot/credential-manager.ts +141 -0
  104. package/src/platforms/telegrambot/index.ts +44 -0
  105. package/src/platforms/telegrambot/listener.test.ts +398 -0
  106. package/src/platforms/telegrambot/listener.ts +198 -0
  107. package/src/platforms/telegrambot/types.test.ts +128 -0
  108. package/src/platforms/telegrambot/types.ts +282 -0
@@ -0,0 +1,45 @@
1
+ import { Command } from 'commander'
2
+
3
+ import { cliOutput } from '@/shared/utils/cli-output'
4
+
5
+ import type { BotOption } from './shared'
6
+ import { getClient } from './shared'
7
+
8
+ interface WhoamiResult {
9
+ id?: number
10
+ username?: string
11
+ first_name?: string
12
+ last_name?: string
13
+ is_bot?: boolean
14
+ can_join_groups?: boolean
15
+ can_read_all_group_messages?: boolean
16
+ supports_inline_queries?: boolean
17
+ error?: string
18
+ }
19
+
20
+ export async function whoamiAction(options: BotOption): Promise<WhoamiResult> {
21
+ try {
22
+ const client = await getClient(options)
23
+ const me = await client.getMe()
24
+ return {
25
+ id: me.id,
26
+ username: me.username,
27
+ first_name: me.first_name,
28
+ last_name: me.last_name,
29
+ is_bot: me.is_bot,
30
+ can_join_groups: me.can_join_groups,
31
+ can_read_all_group_messages: me.can_read_all_group_messages,
32
+ supports_inline_queries: me.supports_inline_queries,
33
+ }
34
+ } catch (error) {
35
+ return { error: (error as Error).message }
36
+ }
37
+ }
38
+
39
+ export const whoamiCommand = new Command('whoami')
40
+ .description('Show current authenticated bot')
41
+ .option('--bot <id>', 'Bot ID to use')
42
+ .option('--pretty', 'Pretty print JSON output')
43
+ .action(async (opts: BotOption) => {
44
+ cliOutput(await whoamiAction(opts), opts.pretty)
45
+ })
@@ -0,0 +1,196 @@
1
+ import { afterEach, beforeEach, describe, expect, it } from 'bun:test'
2
+ import { existsSync, rmSync } from 'node:fs'
3
+ import { mkdir, stat } from 'node:fs/promises'
4
+ import { tmpdir } from 'node:os'
5
+ import { join } from 'node:path'
6
+
7
+ import { TelegramBotCredentialManager } from './credential-manager'
8
+
9
+ const CREDS_A = {
10
+ token: '123456789:ABC-A',
11
+ bot_id: 'bot-a',
12
+ bot_name: 'Bot A',
13
+ }
14
+
15
+ const CREDS_B = {
16
+ token: '987654321:XYZ-B',
17
+ bot_id: 'bot-b',
18
+ bot_name: 'Bot B',
19
+ }
20
+
21
+ describe('TelegramBotCredentialManager', () => {
22
+ let tempDir: string
23
+ let manager: TelegramBotCredentialManager
24
+
25
+ beforeEach(async () => {
26
+ tempDir = join(tmpdir(), `telegrambot-cred-test-${Date.now()}-${Math.random().toString(36).slice(2)}`)
27
+ await mkdir(tempDir, { recursive: true })
28
+ manager = new TelegramBotCredentialManager(tempDir)
29
+ })
30
+
31
+ afterEach(() => {
32
+ if (existsSync(tempDir)) {
33
+ rmSync(tempDir, { recursive: true })
34
+ }
35
+ delete process.env.E2E_TELEGRAMBOT_TOKEN
36
+ })
37
+
38
+ describe('load', () => {
39
+ it('returns empty config when no file exists', async () => {
40
+ const config = await manager.load()
41
+ expect(config.current).toBeNull()
42
+ expect(config.bots).toEqual({})
43
+ })
44
+ })
45
+
46
+ describe('save and load', () => {
47
+ it('persists config to file', async () => {
48
+ const config = {
49
+ current: { bot_id: 'bot-a' },
50
+ bots: {
51
+ 'bot-a': { bot_id: 'bot-a', bot_name: 'Bot A', token: '123:abc' },
52
+ },
53
+ }
54
+ await manager.save(config)
55
+ const loaded = await manager.load()
56
+ expect(loaded).toEqual(config)
57
+ })
58
+ })
59
+
60
+ describe('getCredentials', () => {
61
+ it('returns null when no credentials exist', async () => {
62
+ expect(await manager.getCredentials()).toBeNull()
63
+ })
64
+
65
+ it('returns current bot credentials', async () => {
66
+ await manager.setCredentials(CREDS_A)
67
+ const creds = await manager.getCredentials()
68
+ expect(creds).toEqual(CREDS_A)
69
+ })
70
+
71
+ it('returns specific bot by id', async () => {
72
+ await manager.setCredentials(CREDS_A)
73
+ await manager.setCredentials(CREDS_B)
74
+ const creds = await manager.getCredentials('bot-a')
75
+ expect(creds).toEqual(CREDS_A)
76
+ })
77
+
78
+ it('returns null for non-existent bot id', async () => {
79
+ await manager.setCredentials(CREDS_A)
80
+ const creds = await manager.getCredentials('nope')
81
+ expect(creds).toBeNull()
82
+ })
83
+
84
+ it('env var takes precedence when no botId specified', async () => {
85
+ await manager.setCredentials(CREDS_A)
86
+ process.env.E2E_TELEGRAMBOT_TOKEN = 'env-token'
87
+ const creds = await manager.getCredentials()
88
+ expect(creds?.token).toBe('env-token')
89
+ expect(creds?.bot_id).toBe('env')
90
+ })
91
+
92
+ it('env var ignored when botId explicitly provided', async () => {
93
+ await manager.setCredentials(CREDS_A)
94
+ process.env.E2E_TELEGRAMBOT_TOKEN = 'env-token'
95
+ const creds = await manager.getCredentials('bot-a')
96
+ expect(creds?.token).toBe(CREDS_A.token)
97
+ })
98
+ })
99
+
100
+ describe('setCredentials', () => {
101
+ it('stores bot and sets as current', async () => {
102
+ await manager.setCredentials(CREDS_A)
103
+ const config = await manager.load()
104
+ expect(config.current).toEqual({ bot_id: 'bot-a' })
105
+ expect(config.bots['bot-a'].token).toBe(CREDS_A.token)
106
+ })
107
+
108
+ it('stores multiple bots', async () => {
109
+ await manager.setCredentials(CREDS_A)
110
+ await manager.setCredentials(CREDS_B)
111
+ const config = await manager.load()
112
+ expect(Object.keys(config.bots).sort()).toEqual(['bot-a', 'bot-b'])
113
+ expect(config.current).toEqual({ bot_id: 'bot-b' })
114
+ })
115
+
116
+ it('overwrites existing bot with same id', async () => {
117
+ await manager.setCredentials(CREDS_A)
118
+ await manager.setCredentials({ ...CREDS_A, bot_name: 'Updated' })
119
+ const config = await manager.load()
120
+ expect(Object.keys(config.bots)).toHaveLength(1)
121
+ expect(config.bots['bot-a'].bot_name).toBe('Updated')
122
+ })
123
+ })
124
+
125
+ describe('listAll', () => {
126
+ it('returns all bots with current flag', async () => {
127
+ await manager.setCredentials(CREDS_A)
128
+ await manager.setCredentials(CREDS_B)
129
+ const all = await manager.listAll()
130
+ expect(all).toHaveLength(2)
131
+ expect(all.find((b) => b.bot_id === 'bot-a')?.is_current).toBe(false)
132
+ expect(all.find((b) => b.bot_id === 'bot-b')?.is_current).toBe(true)
133
+ })
134
+
135
+ it('returns empty array when no bots exist', async () => {
136
+ expect(await manager.listAll()).toEqual([])
137
+ })
138
+ })
139
+
140
+ describe('setCurrent', () => {
141
+ it('switches current bot', async () => {
142
+ await manager.setCredentials(CREDS_A)
143
+ await manager.setCredentials(CREDS_B)
144
+ const switched = await manager.setCurrent('bot-a')
145
+ expect(switched).toBe(true)
146
+ const creds = await manager.getCredentials()
147
+ expect(creds?.bot_id).toBe('bot-a')
148
+ })
149
+
150
+ it('returns false for unknown bot', async () => {
151
+ expect(await manager.setCurrent('nope')).toBe(false)
152
+ })
153
+ })
154
+
155
+ describe('removeBot', () => {
156
+ it('removes a bot by id', async () => {
157
+ await manager.setCredentials(CREDS_A)
158
+ await manager.setCredentials(CREDS_B)
159
+ const removed = await manager.removeBot('bot-a')
160
+ expect(removed).toBe(true)
161
+ const config = await manager.load()
162
+ expect(Object.keys(config.bots)).toEqual(['bot-b'])
163
+ })
164
+
165
+ it('clears current when current bot removed', async () => {
166
+ await manager.setCredentials(CREDS_A)
167
+ await manager.removeBot('bot-a')
168
+ const config = await manager.load()
169
+ expect(config.current).toBeNull()
170
+ })
171
+
172
+ it('returns false for unknown bot', async () => {
173
+ expect(await manager.removeBot('nope')).toBe(false)
174
+ })
175
+ })
176
+
177
+ describe('clearCredentials', () => {
178
+ it('removes all credentials', async () => {
179
+ await manager.setCredentials(CREDS_A)
180
+ await manager.setCredentials(CREDS_B)
181
+ await manager.clearCredentials()
182
+ const config = await manager.load()
183
+ expect(config.current).toBeNull()
184
+ expect(config.bots).toEqual({})
185
+ })
186
+ })
187
+
188
+ describe('file permissions', () => {
189
+ it('saves file with secure permissions (600)', async () => {
190
+ await manager.setCredentials(CREDS_A)
191
+ const credPath = join(tempDir, 'telegrambot-credentials.json')
192
+ const stats = await stat(credPath)
193
+ expect(stats.mode & 0o777).toBe(0o600)
194
+ })
195
+ })
196
+ })
@@ -0,0 +1,141 @@
1
+ import { existsSync } from 'node:fs'
2
+ import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises'
3
+ import { join } from 'node:path'
4
+
5
+ import { getConfigDir } from '../../shared/utils/config-dir'
6
+ import type { TelegramBotConfig, TelegramBotCredentials } from './types'
7
+ import { TelegramBotConfigSchema } from './types'
8
+
9
+ export class TelegramBotCredentialManager {
10
+ private configDir: string
11
+ private credentialsPath: string
12
+
13
+ constructor(configDir?: string) {
14
+ this.configDir = configDir ?? getConfigDir()
15
+ this.credentialsPath = join(this.configDir, 'telegrambot-credentials.json')
16
+ }
17
+
18
+ async load(): Promise<TelegramBotConfig> {
19
+ if (!existsSync(this.credentialsPath)) {
20
+ return { current: null, bots: {} }
21
+ }
22
+
23
+ const content = await readFile(this.credentialsPath, 'utf-8')
24
+ const parsed = TelegramBotConfigSchema.safeParse(JSON.parse(content))
25
+ if (!parsed.success) {
26
+ return { current: null, bots: {} }
27
+ }
28
+ return parsed.data
29
+ }
30
+
31
+ async save(config: TelegramBotConfig): Promise<void> {
32
+ await mkdir(this.configDir, { recursive: true })
33
+ await writeFile(this.credentialsPath, JSON.stringify(config, null, 2), { mode: 0o600 })
34
+ await chmod(this.credentialsPath, 0o600)
35
+ }
36
+
37
+ async getCredentials(botId?: string): Promise<TelegramBotCredentials | null> {
38
+ const envToken = process.env.E2E_TELEGRAMBOT_TOKEN
39
+
40
+ if (envToken && !botId) {
41
+ return {
42
+ token: envToken,
43
+ bot_id: 'env',
44
+ bot_name: 'env',
45
+ }
46
+ }
47
+
48
+ const config = await this.load()
49
+
50
+ if (botId) {
51
+ const bot = config.bots[botId]
52
+ if (!bot) return null
53
+ return {
54
+ token: bot.token,
55
+ bot_id: bot.bot_id,
56
+ bot_name: bot.bot_name,
57
+ }
58
+ }
59
+
60
+ if (!config.current) {
61
+ return null
62
+ }
63
+
64
+ const bot = config.bots[config.current.bot_id]
65
+ if (!bot) return null
66
+
67
+ return {
68
+ token: bot.token,
69
+ bot_id: bot.bot_id,
70
+ bot_name: bot.bot_name,
71
+ }
72
+ }
73
+
74
+ async setCredentials(creds: TelegramBotCredentials): Promise<void> {
75
+ const config = await this.load()
76
+
77
+ config.bots[creds.bot_id] = {
78
+ bot_id: creds.bot_id,
79
+ bot_name: creds.bot_name,
80
+ token: creds.token,
81
+ }
82
+
83
+ config.current = {
84
+ bot_id: creds.bot_id,
85
+ }
86
+
87
+ await this.save(config)
88
+ }
89
+
90
+ async removeBot(botId: string): Promise<boolean> {
91
+ const config = await this.load()
92
+
93
+ if (!config.bots[botId]) {
94
+ return false
95
+ }
96
+
97
+ delete config.bots[botId]
98
+
99
+ if (config.current?.bot_id === botId) {
100
+ config.current = null
101
+ }
102
+
103
+ await this.save(config)
104
+ return true
105
+ }
106
+
107
+ async setCurrent(botId: string): Promise<boolean> {
108
+ const config = await this.load()
109
+
110
+ if (!config.bots[botId]) {
111
+ return false
112
+ }
113
+
114
+ config.current = {
115
+ bot_id: botId,
116
+ }
117
+
118
+ await this.save(config)
119
+ return true
120
+ }
121
+
122
+ async listAll(): Promise<Array<TelegramBotCredentials & { is_current: boolean }>> {
123
+ const config = await this.load()
124
+ const results: Array<TelegramBotCredentials & { is_current: boolean }> = []
125
+
126
+ for (const bot of Object.values(config.bots)) {
127
+ results.push({
128
+ token: bot.token,
129
+ bot_id: bot.bot_id,
130
+ bot_name: bot.bot_name,
131
+ is_current: config.current?.bot_id === bot.bot_id,
132
+ })
133
+ }
134
+
135
+ return results
136
+ }
137
+
138
+ async clearCredentials(): Promise<void> {
139
+ await this.save({ current: null, bots: {} })
140
+ }
141
+ }
@@ -0,0 +1,44 @@
1
+ export { TelegramBotClient } from './client'
2
+ export type {
3
+ BotReactionType,
4
+ ChatId,
5
+ EditMessageTextChat,
6
+ EditMessageTextInline,
7
+ EditMessageTextTarget,
8
+ GetUpdatesOptions,
9
+ SendMessageOptions,
10
+ } from './client'
11
+ export { TelegramBotCredentialManager } from './credential-manager'
12
+ export { TelegramBotListener } from './listener'
13
+ export type {
14
+ TelegramBotConfig,
15
+ TelegramBotCredentials,
16
+ TelegramBotEntry,
17
+ TelegramBotListenerEventMap,
18
+ TelegramBotListenerOptions,
19
+ TelegramBotUser,
20
+ TelegramCallbackQuery,
21
+ TelegramChat,
22
+ TelegramChatFullInfo,
23
+ TelegramChatMember,
24
+ TelegramChatMemberUpdated,
25
+ TelegramDocument,
26
+ TelegramInlineQuery,
27
+ TelegramMessage,
28
+ TelegramMessageEntity,
29
+ TelegramPhotoSize,
30
+ TelegramReactionType,
31
+ TelegramUpdate,
32
+ } from './types'
33
+ export {
34
+ TelegramBotConfigSchema,
35
+ TelegramBotCredentialsSchema,
36
+ TelegramBotEntrySchema,
37
+ TelegramBotError,
38
+ TelegramBotUserSchema,
39
+ TelegramChatSchema,
40
+ TelegramDocumentSchema,
41
+ TelegramMessageEntitySchema,
42
+ TelegramMessageSchema,
43
+ TelegramPhotoSizeSchema,
44
+ } from './types'