agent-messenger 2.19.0 → 2.19.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.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/package.json +1 -1
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +51 -1
- package/dist/src/platforms/kakaotalk/client.js.map +1 -1
- package/dist/src/platforms/line/client.d.ts +27 -0
- package/dist/src/platforms/line/client.d.ts.map +1 -1
- package/dist/src/platforms/line/client.js +85 -7
- package/dist/src/platforms/line/client.js.map +1 -1
- package/dist/src/platforms/line/listener.d.ts +3 -0
- package/dist/src/platforms/line/listener.d.ts.map +1 -1
- package/dist/src/platforms/line/listener.js +57 -47
- package/dist/src/platforms/line/listener.js.map +1 -1
- package/docs/content/docs/cli/line.mdx +10 -9
- package/package.json +1 -1
- 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-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +2 -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-wechatbot/SKILL.md +1 -1
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/kakaotalk/client.test.ts +39 -0
- package/src/platforms/kakaotalk/client.ts +59 -1
- package/src/platforms/line/client.test.ts +174 -1
- package/src/platforms/line/client.ts +105 -7
- package/src/platforms/line/listener.test.ts +106 -31
- package/src/platforms/line/listener.ts +56 -51
- package/src/platforms/webex/commands/auth.test.ts +8 -2
- package/src/platforms/webex/commands/member.test.ts +29 -27
- package/src/platforms/webex/commands/message.test.ts +36 -38
- package/src/platforms/webex/commands/snapshot.test.ts +25 -26
- package/src/platforms/webex/commands/space.test.ts +31 -29
- package/src/platforms/webex/commands/whoami.test.ts +3 -1
- package/src/platforms/webex/credential-manager.test.ts +3 -0
- package/src/platforms/whatsapp/commands/auth.test.ts +14 -20
- package/src/platforms/whatsapp/commands/chat.test.ts +17 -24
- package/src/platforms/whatsapp/commands/message.test.ts +31 -41
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { EventEmitter } from 'events';
|
|
2
|
-
import { LineError } from './types.js';
|
|
3
2
|
const RECONNECT_BASE_DELAY = 1_000;
|
|
4
3
|
const RECONNECT_MAX_DELAY = 30_000;
|
|
5
4
|
export class LineListener {
|
|
@@ -48,55 +47,13 @@ export class LineListener {
|
|
|
48
47
|
await this.lineClient.login();
|
|
49
48
|
if (!this.running)
|
|
50
49
|
return;
|
|
51
|
-
const
|
|
52
|
-
if (!
|
|
53
|
-
|
|
54
|
-
}
|
|
50
|
+
const profile = await this.lineClient.getProfile();
|
|
51
|
+
if (!this.running)
|
|
52
|
+
return;
|
|
55
53
|
this.abortController = new AbortController();
|
|
56
|
-
internalClient.on('message', (msg) => {
|
|
57
|
-
try {
|
|
58
|
-
const toType = msg.raw.toType;
|
|
59
|
-
const isGroupOrRoom = toType === 'GROUP' || toType === 'ROOM' || toType === 0 || toType === 1;
|
|
60
|
-
const chatId = isGroupOrRoom ? msg.to.id : msg.isMyMessage ? msg.to.id : msg.from.id;
|
|
61
|
-
const event = {
|
|
62
|
-
type: 'message',
|
|
63
|
-
chat_id: chatId,
|
|
64
|
-
message_id: String(msg.raw.id),
|
|
65
|
-
author_id: msg.from.id,
|
|
66
|
-
text: msg.text ?? null,
|
|
67
|
-
content_type: String(msg.raw.contentType ?? 'NONE'),
|
|
68
|
-
sent_at: new Date(Number(msg.raw.createdTime)).toISOString(),
|
|
69
|
-
};
|
|
70
|
-
this.emitter.emit('message', event);
|
|
71
|
-
this.emitter.emit('line_event', { ...event });
|
|
72
|
-
}
|
|
73
|
-
catch (error) {
|
|
74
|
-
this.emitter.emit('error', error instanceof Error ? error : new Error(String(error)));
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
internalClient.on('event', (op) => {
|
|
78
|
-
const event = {
|
|
79
|
-
type: String(op.type ?? 'unknown'),
|
|
80
|
-
...(op.revision !== undefined && { revision: String(op.revision) }),
|
|
81
|
-
...(op.createdTime !== undefined && {
|
|
82
|
-
created_time: new Date(Number(op.createdTime)).toISOString(),
|
|
83
|
-
}),
|
|
84
|
-
};
|
|
85
|
-
this.emitter.emit('line_event', event);
|
|
86
|
-
});
|
|
87
|
-
internalClient.listen({ signal: this.abortController.signal })?.catch((error) => {
|
|
88
|
-
if (!this.running)
|
|
89
|
-
return;
|
|
90
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
91
|
-
if (err.name === 'AbortError')
|
|
92
|
-
return;
|
|
93
|
-
this.emitter.emit('error', err);
|
|
94
|
-
this.emitter.emit('disconnected');
|
|
95
|
-
this.scheduleReconnect();
|
|
96
|
-
});
|
|
97
54
|
this.reconnectAttempts = 0;
|
|
98
|
-
const profile = await internalClient.base.talk.getProfile();
|
|
99
55
|
this.emitter.emit('connected', { account_id: profile.mid });
|
|
56
|
+
void this.pump(this.abortController.signal);
|
|
100
57
|
}
|
|
101
58
|
catch (error) {
|
|
102
59
|
this.emitter.emit('error', error instanceof Error ? error : new Error(String(error)));
|
|
@@ -105,6 +62,59 @@ export class LineListener {
|
|
|
105
62
|
}
|
|
106
63
|
}
|
|
107
64
|
}
|
|
65
|
+
async pump(signal) {
|
|
66
|
+
try {
|
|
67
|
+
for await (const event of this.lineClient.streamEvents(signal)) {
|
|
68
|
+
if (event.kind === 'message') {
|
|
69
|
+
this.emitMessage(event.message);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
this.emitOperation(event.op);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
if (!this.running || signal.aborted)
|
|
78
|
+
return;
|
|
79
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
80
|
+
if (err.name === 'AbortError')
|
|
81
|
+
return;
|
|
82
|
+
this.emitter.emit('error', err);
|
|
83
|
+
this.emitter.emit('disconnected');
|
|
84
|
+
this.scheduleReconnect();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
emitMessage(msg) {
|
|
88
|
+
try {
|
|
89
|
+
const toType = msg.raw.toType;
|
|
90
|
+
const isGroupOrRoom = toType === 'GROUP' || toType === 'ROOM' || toType === 0 || toType === 1;
|
|
91
|
+
const chatId = isGroupOrRoom ? msg.to.id : msg.isMyMessage ? msg.to.id : msg.from.id;
|
|
92
|
+
const event = {
|
|
93
|
+
type: 'message',
|
|
94
|
+
chat_id: chatId,
|
|
95
|
+
message_id: String(msg.raw.id),
|
|
96
|
+
author_id: msg.from.id,
|
|
97
|
+
text: msg.text ?? null,
|
|
98
|
+
content_type: String(msg.raw.contentType ?? 'NONE'),
|
|
99
|
+
sent_at: new Date(Number(msg.raw.createdTime)).toISOString(),
|
|
100
|
+
};
|
|
101
|
+
this.emitter.emit('message', event);
|
|
102
|
+
this.emitter.emit('line_event', { ...event });
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
this.emitter.emit('error', error instanceof Error ? error : new Error(String(error)));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
emitOperation(op) {
|
|
109
|
+
const event = {
|
|
110
|
+
type: String(op.type ?? 'unknown'),
|
|
111
|
+
...(op.revision !== undefined && { revision: String(op.revision) }),
|
|
112
|
+
...(op.createdTime !== undefined && {
|
|
113
|
+
created_time: new Date(Number(op.createdTime)).toISOString(),
|
|
114
|
+
}),
|
|
115
|
+
};
|
|
116
|
+
this.emitter.emit('line_event', event);
|
|
117
|
+
}
|
|
108
118
|
scheduleReconnect() {
|
|
109
119
|
this.clearTimers();
|
|
110
120
|
const delay = Math.min(RECONNECT_BASE_DELAY * 2 ** this.reconnectAttempts, RECONNECT_MAX_DELAY);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"listener.js","sourceRoot":"","sources":["../../../../src/platforms/line/listener.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;
|
|
1
|
+
{"version":3,"file":"listener.js","sourceRoot":"","sources":["../../../../src/platforms/line/listener.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,14 +9,14 @@ description: Complete reference for the agent-line CLI.
|
|
|
9
9
|
|
|
10
10
|
Before diving in, a few things about LINE's architecture:
|
|
11
11
|
|
|
12
|
-
| Term | Description
|
|
13
|
-
| ------------------------- |
|
|
14
|
-
| **MID** | LINE's unique identifier for users, chats, and rooms. Prefixed with `u` (user), `c` (group chat), or `r` (room).
|
|
15
|
-
| **QR code login** | The primary authentication method. Scan a QR code with the LINE mobile app to authorize the CLI.
|
|
16
|
-
| **ANDROIDSECONDARY** | The default device type. Registers as a secondary Android device so your phone and desktop sessions stay active.
|
|
17
|
-
| **E2EE (Letter Sealing)** | LINE's end-to-end encryption. The CLI attempts E2EE first and falls back to plaintext if keys aren't available.
|
|
18
|
-
| **Device types** | Controls which device slot the CLI occupies. Secondary devices coexist with your other sessions.
|
|
19
|
-
| **PIN verification** | During login, LINE may display a PIN that you confirm on your phone to authorize the new device.
|
|
12
|
+
| Term | Description |
|
|
13
|
+
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
14
|
+
| **MID** | LINE's unique identifier for users, chats, and rooms. Prefixed with `u` (user), `c` (group chat), or `r` (room). |
|
|
15
|
+
| **QR code login** | The primary authentication method. Scan a QR code with the LINE mobile app to authorize the CLI. |
|
|
16
|
+
| **ANDROIDSECONDARY** | The default device type. Registers as a secondary Android device so your phone and desktop sessions stay active. |
|
|
17
|
+
| **E2EE (Letter Sealing)** | LINE's end-to-end encryption. The CLI attempts E2EE first and falls back to plaintext if keys aren't available. Chats that require E2EE reject the fallback and fail with `e2ee_required`. |
|
|
18
|
+
| **Device types** | Controls which device slot the CLI occupies. Secondary devices coexist with your other sessions. |
|
|
19
|
+
| **PIN verification** | During login, LINE may display a PIN that you confirm on your phone to authorize the new device. |
|
|
20
20
|
|
|
21
21
|
## Quick Start
|
|
22
22
|
|
|
@@ -230,6 +230,7 @@ agent-line message list c7a8b9c0d1e2f3a4b5c6d7e8 -n 200
|
|
|
230
230
|
- No message editing or deletion
|
|
231
231
|
- No search across chats
|
|
232
232
|
- Plain text messages only (no photos, videos, or rich content)
|
|
233
|
+
- Sending to chats that **require** E2EE (Letter Sealing) is not supported on auth-token sessions; such sends fail with `e2ee_required`
|
|
233
234
|
- Chat IDs are MID strings, not human-readable — use `chat list` to discover them
|
|
234
235
|
|
|
235
236
|
## Troubleshooting
|
|
@@ -266,7 +267,7 @@ If the QR code doesn't open in your browser:
|
|
|
266
267
|
|
|
267
268
|
### E2EE errors
|
|
268
269
|
|
|
269
|
-
The CLI tries E2EE (Letter Sealing) first and falls back
|
|
270
|
+
The CLI tries E2EE (Letter Sealing) first and falls back to plaintext when possible, so most messages still send. Chats that **require** E2EE reject the plaintext fallback, and the send fails with `e2ee_required` — auth-token sessions have no local E2EE key and cannot encrypt for those chats.
|
|
270
271
|
|
|
271
272
|
### PIN verification timeout
|
|
272
273
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-messenger",
|
|
3
|
-
"version": "2.19.
|
|
3
|
+
"version": "2.19.2",
|
|
4
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",
|
|
@@ -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.19.
|
|
4
|
+
version: 2.19.2
|
|
5
5
|
allowed-tools: Bash(agent-channeltalk:*)
|
|
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.19.
|
|
4
|
+
version: 2.19.2
|
|
5
5
|
allowed-tools: Bash(agent-line:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -429,6 +429,7 @@ See the [LINE SDK documentation](https://agent-messenger.dev/docs/sdk/line) for
|
|
|
429
429
|
|
|
430
430
|
- No auto-extraction of credentials (requires interactive login via QR code or email/password)
|
|
431
431
|
- E2EE (Letter Sealing) may prevent reading some message content
|
|
432
|
+
- Sending to chats that **require** E2EE (Letter Sealing) is not supported on auth-token sessions; such sends fail with `e2ee_required`
|
|
432
433
|
- No file upload support yet
|
|
433
434
|
- No sticker or rich message sending (text only)
|
|
434
435
|
- No group creation or management
|
|
@@ -1265,6 +1265,45 @@ describe('KakaoTalkClient', () => {
|
|
|
1265
1265
|
client.close()
|
|
1266
1266
|
})
|
|
1267
1267
|
|
|
1268
|
+
it('merges CHATONROOM members when GETMEM returns a partial member list', async () => {
|
|
1269
|
+
mockGetAllMembers.mockResolvedValueOnce({
|
|
1270
|
+
statusCode: 0,
|
|
1271
|
+
body: {
|
|
1272
|
+
members: [
|
|
1273
|
+
{
|
|
1274
|
+
userId: makeLong(42),
|
|
1275
|
+
nickName: 'Alice',
|
|
1276
|
+
type: 100,
|
|
1277
|
+
profileImageUrl: 'https://kakao.com/p/alice.jpg',
|
|
1278
|
+
},
|
|
1279
|
+
],
|
|
1280
|
+
token: 0,
|
|
1281
|
+
},
|
|
1282
|
+
})
|
|
1283
|
+
mockGetChatInfo.mockResolvedValueOnce({
|
|
1284
|
+
statusCode: 0,
|
|
1285
|
+
body: {
|
|
1286
|
+
status: 0,
|
|
1287
|
+
m: [
|
|
1288
|
+
{ userId: makeLong(42), nickName: 'Alice From Room', type: 100 },
|
|
1289
|
+
{ userId: makeLong(43), nickName: 'Bob', type: 100 },
|
|
1290
|
+
{ userId: makeLong(44), nickName: 'Carol', type: 100 },
|
|
1291
|
+
],
|
|
1292
|
+
},
|
|
1293
|
+
})
|
|
1294
|
+
|
|
1295
|
+
const client = await new KakaoTalkClient().login({ oauthToken: 'token', userId: 'user1', deviceUuid: 'device1' })
|
|
1296
|
+
const members = await client.getMembers('100')
|
|
1297
|
+
|
|
1298
|
+
expect(members.map((member) => member.user_id)).toEqual(['42', '43', '44'])
|
|
1299
|
+
expect(members[0].nickname).toBe('Alice')
|
|
1300
|
+
expect(members[0].profile_image_url).toBe('https://kakao.com/p/alice.jpg')
|
|
1301
|
+
expect(members[1].nickname).toBe('Bob')
|
|
1302
|
+
expect(members[2].nickname).toBe('Carol')
|
|
1303
|
+
|
|
1304
|
+
client.close()
|
|
1305
|
+
})
|
|
1306
|
+
|
|
1268
1307
|
it('returns empty array when GETMEM returns no members', async () => {
|
|
1269
1308
|
mockGetAllMembers.mockResolvedValueOnce({ statusCode: 0, body: {} })
|
|
1270
1309
|
|
|
@@ -428,6 +428,54 @@ function formatMember(member: Record<string, unknown>): KakaoMember {
|
|
|
428
428
|
}
|
|
429
429
|
}
|
|
430
430
|
|
|
431
|
+
function memberIdKey(member: Record<string, unknown>): string | null {
|
|
432
|
+
if (member.userId === undefined || member.userId === null) return null
|
|
433
|
+
const key = longToString(member.userId)
|
|
434
|
+
return key === '0' ? null : key
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
function mergeMemberRecords(
|
|
438
|
+
primaryMembers: Array<Record<string, unknown>>,
|
|
439
|
+
fallbackMembers: Array<Record<string, unknown>>,
|
|
440
|
+
): Array<Record<string, unknown>> {
|
|
441
|
+
const merged: Array<Record<string, unknown>> = []
|
|
442
|
+
const indexByUserId = new Map<string, number>()
|
|
443
|
+
|
|
444
|
+
const upsert = (member: Record<string, unknown>) => {
|
|
445
|
+
const userId = memberIdKey(member)
|
|
446
|
+
if (!userId) return
|
|
447
|
+
|
|
448
|
+
const existingIndex = indexByUserId.get(userId)
|
|
449
|
+
if (existingIndex === undefined) {
|
|
450
|
+
indexByUserId.set(userId, merged.length)
|
|
451
|
+
merged.push(member)
|
|
452
|
+
return
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
merged[existingIndex] = { ...member, ...merged[existingIndex] }
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
for (const member of primaryMembers) upsert(member)
|
|
459
|
+
for (const member of fallbackMembers) upsert(member)
|
|
460
|
+
|
|
461
|
+
return merged
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function extractChatInfoMembers(body: unknown): Array<Record<string, unknown>> {
|
|
465
|
+
if (!body || typeof body !== 'object') return []
|
|
466
|
+
|
|
467
|
+
const record = body as Record<string, unknown>
|
|
468
|
+
if (Array.isArray(record.m)) {
|
|
469
|
+
return record.m as Array<Record<string, unknown>>
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const chatInfo = record.chatInfo
|
|
473
|
+
if (!chatInfo || typeof chatInfo !== 'object') return []
|
|
474
|
+
|
|
475
|
+
const displayMembers = (chatInfo as Record<string, unknown>).displayMembers
|
|
476
|
+
return Array.isArray(displayMembers) ? (displayMembers as Array<Record<string, unknown>>) : []
|
|
477
|
+
}
|
|
478
|
+
|
|
431
479
|
function formatMessages(
|
|
432
480
|
logs: Array<Record<string, unknown>>,
|
|
433
481
|
count: number,
|
|
@@ -875,7 +923,17 @@ export class KakaoTalkClient {
|
|
|
875
923
|
const response = await session.getAllMembers(parsedChatId)
|
|
876
924
|
assertLocoOk(response, 'GETMEM')
|
|
877
925
|
const members = (response.body.members ?? []) as Array<Record<string, unknown>>
|
|
878
|
-
|
|
926
|
+
let fallbackMembers: Array<Record<string, unknown>> = []
|
|
927
|
+
try {
|
|
928
|
+
// Some KakaoTalk rooms return only a subset from GETMEM even though
|
|
929
|
+
// CHATONROOM carries the full active member list in `m`.
|
|
930
|
+
const chatInfo = await session.getChatInfo(parsedChatId)
|
|
931
|
+
fallbackMembers = extractChatInfoMembers(chatInfo.body)
|
|
932
|
+
} catch {
|
|
933
|
+
fallbackMembers = []
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
return mergeMemberRecords(members, fallbackMembers).map(formatMember)
|
|
879
937
|
} catch (error) {
|
|
880
938
|
throw wrapError(error, 'get_members_failed')
|
|
881
939
|
}
|