agent-messenger 2.12.1 → 2.13.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/dist/package.json +1 -1
- package/dist/src/platforms/kakaotalk/chat-classifier.d.ts +18 -0
- package/dist/src/platforms/kakaotalk/chat-classifier.d.ts.map +1 -0
- package/dist/src/platforms/kakaotalk/chat-classifier.js +29 -0
- package/dist/src/platforms/kakaotalk/chat-classifier.js.map +1 -0
- package/dist/src/platforms/kakaotalk/cli.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/cli.js +2 -1
- package/dist/src/platforms/kakaotalk/cli.js.map +1 -1
- package/dist/src/platforms/kakaotalk/client.d.ts +35 -1
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +318 -15
- 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 +2 -1
- package/dist/src/platforms/kakaotalk/commands/chat.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/index.d.ts +1 -0
- package/dist/src/platforms/kakaotalk/commands/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/index.js +1 -0
- package/dist/src/platforms/kakaotalk/commands/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/member.d.ts +3 -0
- package/dist/src/platforms/kakaotalk/commands/member.d.ts.map +1 -0
- package/dist/src/platforms/kakaotalk/commands/member.js +22 -0
- package/dist/src/platforms/kakaotalk/commands/member.js.map +1 -0
- package/dist/src/platforms/kakaotalk/index.d.ts +4 -2
- package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/index.js +2 -1
- package/dist/src/platforms/kakaotalk/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/listener.d.ts +4 -7
- package/dist/src/platforms/kakaotalk/listener.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/listener.js +48 -74
- package/dist/src/platforms/kakaotalk/listener.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.d.ts +28 -0
- package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js +44 -0
- package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
- package/dist/src/platforms/kakaotalk/types.d.ts +37 -0
- package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/types.js +17 -0
- package/dist/src/platforms/kakaotalk/types.js.map +1 -1
- package/dist/src/platforms/slackbot/client.d.ts +5 -0
- package/dist/src/platforms/slackbot/client.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/client.js +5 -0
- package/dist/src/platforms/slackbot/client.js.map +1 -1
- package/dist/src/tui/adapters/kakaotalk-adapter.js +3 -3
- package/dist/src/tui/adapters/kakaotalk-adapter.js.map +1 -1
- package/docs/content/docs/cli/kakaotalk.mdx +26 -1
- package/docs/content/docs/sdk/kakaotalk.mdx +45 -13
- package/docs/content/docs/sdk/slackbot.mdx +11 -0
- package/package.json +1 -1
- package/scripts/kakao-loco-capture.ts +466 -0
- 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 +30 -3
- package/skills/agent-kakaotalk/references/common-patterns.md +49 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -2
- 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/chat-classifier.test.ts +33 -0
- package/src/platforms/kakaotalk/chat-classifier.ts +31 -0
- package/src/platforms/kakaotalk/cli.ts +2 -1
- package/src/platforms/kakaotalk/client-listener-integration.test.ts +411 -0
- package/src/platforms/kakaotalk/client.test.ts +785 -1
- package/src/platforms/kakaotalk/client.ts +369 -18
- package/src/platforms/kakaotalk/commands/chat.ts +3 -1
- package/src/platforms/kakaotalk/commands/index.ts +1 -0
- package/src/platforms/kakaotalk/commands/member.test.ts +102 -0
- package/src/platforms/kakaotalk/commands/member.ts +32 -0
- package/src/platforms/kakaotalk/index.test.ts +5 -0
- package/src/platforms/kakaotalk/index.ts +4 -0
- package/src/platforms/kakaotalk/listener.test.ts +184 -149
- package/src/platforms/kakaotalk/listener.ts +51 -82
- package/src/platforms/kakaotalk/protocol/session.ts +44 -0
- package/src/platforms/kakaotalk/types.ts +39 -0
- package/src/platforms/slackbot/client.test.ts +67 -0
- package/src/platforms/slackbot/client.ts +17 -1
- package/src/tui/adapters/kakaotalk-adapter.ts +3 -3
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from 'events'
|
|
2
2
|
|
|
3
|
-
import type { KakaoTalkClient } from './client'
|
|
4
|
-
import { LocoSession } from './protocol/session'
|
|
3
|
+
import type { KakaoSessionEvent, KakaoTalkClient } from './client'
|
|
5
4
|
import type { LocoPacket } from './protocol/types'
|
|
6
5
|
import type {
|
|
7
6
|
KakaoTalkListenerEventMap,
|
|
@@ -11,9 +10,6 @@ import type {
|
|
|
11
10
|
KakaoTalkPushReadEvent,
|
|
12
11
|
} from './types'
|
|
13
12
|
|
|
14
|
-
const RECONNECT_BASE_DELAY = 1_000
|
|
15
|
-
const RECONNECT_MAX_DELAY = 30_000
|
|
16
|
-
|
|
17
13
|
type EventKey = keyof KakaoTalkListenerEventMap
|
|
18
14
|
|
|
19
15
|
function longToString(v: unknown): string {
|
|
@@ -27,11 +23,9 @@ function longToString(v: unknown): string {
|
|
|
27
23
|
export class KakaoTalkListener {
|
|
28
24
|
private client: KakaoTalkClient
|
|
29
25
|
private running = false
|
|
30
|
-
private session: LocoSession | null = null
|
|
31
26
|
private emitter = new EventEmitter()
|
|
32
|
-
private
|
|
33
|
-
private
|
|
34
|
-
private userId: string | null = null
|
|
27
|
+
private unsubscribePush: (() => void) | null = null
|
|
28
|
+
private unsubscribeSession: (() => void) | null = null
|
|
35
29
|
|
|
36
30
|
constructor(client: KakaoTalkClient) {
|
|
37
31
|
this.client = client
|
|
@@ -40,17 +34,33 @@ export class KakaoTalkListener {
|
|
|
40
34
|
async start(): Promise<void> {
|
|
41
35
|
if (this.running) return
|
|
42
36
|
this.running = true
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
|
|
38
|
+
this.unsubscribePush = this.client.onPush((packet) => this.handlePush(packet))
|
|
39
|
+
this.unsubscribeSession = this.client.onSessionEvent((event) => this.handleSessionEvent(event))
|
|
40
|
+
|
|
41
|
+
const alreadyConnected = this.client.isConnected()
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
await this.client.acquireSession()
|
|
45
|
+
if (!this.running) return
|
|
46
|
+
if (alreadyConnected) {
|
|
47
|
+
const { userId } = this.client.getCredentials()
|
|
48
|
+
this.emitter.emit('connected', { userId })
|
|
49
|
+
}
|
|
50
|
+
} catch (error) {
|
|
51
|
+
this.emitter.emit('error', error instanceof Error ? error : new Error(String(error)))
|
|
52
|
+
this.running = false
|
|
53
|
+
this.teardown()
|
|
54
|
+
}
|
|
45
55
|
}
|
|
46
56
|
|
|
47
57
|
stop(): void {
|
|
48
|
-
this.running
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
this.session.close()
|
|
52
|
-
this.session = null
|
|
58
|
+
if (!this.running) {
|
|
59
|
+
this.teardown()
|
|
60
|
+
return
|
|
53
61
|
}
|
|
62
|
+
this.running = false
|
|
63
|
+
this.teardown()
|
|
54
64
|
}
|
|
55
65
|
|
|
56
66
|
on<K extends EventKey>(event: K, listener: (...args: KakaoTalkListenerEventMap[K]) => void): this {
|
|
@@ -68,41 +78,28 @@ export class KakaoTalkListener {
|
|
|
68
78
|
return this
|
|
69
79
|
}
|
|
70
80
|
|
|
71
|
-
private
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
this.userId = userId
|
|
79
|
-
const session = new LocoSession()
|
|
80
|
-
|
|
81
|
-
session.onPush((packet) => this.handlePush(packet))
|
|
82
|
-
session.onClose(() => {
|
|
83
|
-
if (this.session !== session) return
|
|
84
|
-
this.session = null
|
|
85
|
-
if (this.running) {
|
|
86
|
-
this.emitter.emit('disconnected')
|
|
87
|
-
this.scheduleReconnect()
|
|
88
|
-
}
|
|
89
|
-
})
|
|
90
|
-
|
|
91
|
-
await session.login(oauthToken, userId, deviceUuid, undefined, deviceType)
|
|
81
|
+
private teardown(): void {
|
|
82
|
+
this.unsubscribePush?.()
|
|
83
|
+
this.unsubscribePush = null
|
|
84
|
+
this.unsubscribeSession?.()
|
|
85
|
+
this.unsubscribeSession = null
|
|
86
|
+
}
|
|
92
87
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return
|
|
96
|
-
}
|
|
88
|
+
private handleSessionEvent(event: KakaoSessionEvent): void {
|
|
89
|
+
if (!this.running) return
|
|
97
90
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
91
|
+
switch (event.type) {
|
|
92
|
+
case 'connected':
|
|
93
|
+
this.emitter.emit('connected', { userId: event.userId })
|
|
94
|
+
break
|
|
95
|
+
case 'disconnected':
|
|
96
|
+
this.emitter.emit('disconnected')
|
|
97
|
+
break
|
|
98
|
+
case 'kicked':
|
|
99
|
+
this.emitter.emit('error', new Error(event.reason))
|
|
100
|
+
this.running = false
|
|
101
|
+
this.teardown()
|
|
102
|
+
break
|
|
106
103
|
}
|
|
107
104
|
}
|
|
108
105
|
|
|
@@ -112,11 +109,14 @@ export class KakaoTalkListener {
|
|
|
112
109
|
switch (method) {
|
|
113
110
|
case 'MSG': {
|
|
114
111
|
const chatLog = body.chatLog as Record<string, unknown>
|
|
112
|
+
const chatId = longToString(body.chatId)
|
|
113
|
+
const authorId = chatLog.authorId as number
|
|
115
114
|
const event: KakaoTalkPushMessageEvent = {
|
|
116
115
|
type: 'MSG',
|
|
117
|
-
chat_id:
|
|
116
|
+
chat_id: chatId,
|
|
118
117
|
log_id: longToString(chatLog.logId),
|
|
119
|
-
author_id:
|
|
118
|
+
author_id: authorId,
|
|
119
|
+
author_name: this.client.lookupAuthorName?.(chatId, authorId) ?? null,
|
|
120
120
|
message: chatLog.message as string,
|
|
121
121
|
message_type: chatLog.type as number,
|
|
122
122
|
sent_at: chatLog.sendAt as number,
|
|
@@ -162,23 +162,6 @@ export class KakaoTalkListener {
|
|
|
162
162
|
break
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
case 'CHANGESVR': {
|
|
166
|
-
this.reconnectAttempts = 0
|
|
167
|
-
const prev = this.session
|
|
168
|
-
this.session = null
|
|
169
|
-
prev?.close()
|
|
170
|
-
this.connect()
|
|
171
|
-
break
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
case 'KICKOUT': {
|
|
175
|
-
this.emitter.emit('error', new Error('Session kicked — another device logged in'))
|
|
176
|
-
this.running = false
|
|
177
|
-
this.session?.close()
|
|
178
|
-
this.session = null
|
|
179
|
-
break
|
|
180
|
-
}
|
|
181
|
-
|
|
182
165
|
default: {
|
|
183
166
|
const event: KakaoTalkPushGenericEvent = { type: method, ...body }
|
|
184
167
|
this.emitter.emit('kakaotalk_event', event)
|
|
@@ -186,18 +169,4 @@ export class KakaoTalkListener {
|
|
|
186
169
|
}
|
|
187
170
|
}
|
|
188
171
|
}
|
|
189
|
-
|
|
190
|
-
private scheduleReconnect(): void {
|
|
191
|
-
this.clearTimers()
|
|
192
|
-
const delay = Math.min(RECONNECT_BASE_DELAY * 2 ** this.reconnectAttempts, RECONNECT_MAX_DELAY)
|
|
193
|
-
this.reconnectAttempts++
|
|
194
|
-
this.reconnectTimer = setTimeout(() => this.connect(), delay)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
private clearTimers(): void {
|
|
198
|
-
if (this.reconnectTimer) {
|
|
199
|
-
clearTimeout(this.reconnectTimer)
|
|
200
|
-
this.reconnectTimer = null
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
172
|
}
|
|
@@ -149,6 +149,50 @@ export class LocoSession {
|
|
|
149
149
|
return this.connection.sendPacket('CHATONROOM', { chatId })
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
/**
|
|
153
|
+
* Fetch detailed channel info (CHATINFO). Unlike CHATONROOM, this returns
|
|
154
|
+
* a `chatInfo` sub-document containing `chatMetas` (room title, notice, etc.)
|
|
155
|
+
* and `displayMembers` (user_id ↔ nickname pairs). Used to resolve the
|
|
156
|
+
* canonical user-set room title.
|
|
157
|
+
*/
|
|
158
|
+
async getChannelInfo(chatId: Long): Promise<LocoPacket> {
|
|
159
|
+
if (!this.connection) throw new Error('Not connected')
|
|
160
|
+
return this.connection.sendPacket('CHATINFO', { chatId })
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Fetch open-link info (INFOLINK) for one or more openchat links. The
|
|
165
|
+
* response body has shape `{ ols: OpenLinkStruct[] }` where each struct's
|
|
166
|
+
* `ln` field carries the open-chat link name — the canonical fallback when
|
|
167
|
+
* an open chat has no user-set TITLE meta.
|
|
168
|
+
*/
|
|
169
|
+
async getOpenLinkInfo(linkIds: Long[]): Promise<LocoPacket> {
|
|
170
|
+
if (!this.connection) throw new Error('Not connected')
|
|
171
|
+
return this.connection.sendPacket('INFOLINK', { lis: linkIds })
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Fetch the full member list for a chat (GETMEM). Response body has shape
|
|
176
|
+
* `{ members: NormalMemberStruct[] | OpenMemberStruct[], token: number }`.
|
|
177
|
+
* Use this to resolve nicknames for users not present in the chat list's
|
|
178
|
+
* "display members" cache — necessary for groups with more than ~5 members.
|
|
179
|
+
*/
|
|
180
|
+
async getAllMembers(chatId: Long): Promise<LocoPacket> {
|
|
181
|
+
if (!this.connection) throw new Error('Not connected')
|
|
182
|
+
return this.connection.sendPacket('GETMEM', { chatId })
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Fetch info for a specific subset of members in a chat (MEMBER). Response
|
|
187
|
+
* body has shape `{ chatId, members: NormalMemberStruct[] | OpenMemberStruct[] }`.
|
|
188
|
+
* Useful when you already have user IDs (e.g. from a CHATONROOM `mi` array
|
|
189
|
+
* for >100-member rooms) and only need to resolve a few of them.
|
|
190
|
+
*/
|
|
191
|
+
async getMembersByIds(chatId: Long, memberIds: Long[]): Promise<LocoPacket> {
|
|
192
|
+
if (!this.connection) throw new Error('Not connected')
|
|
193
|
+
return this.connection.sendPacket('MEMBER', { chatId, memberIds })
|
|
194
|
+
}
|
|
195
|
+
|
|
152
196
|
async getChatList(lastTokenId?: Long, lastChatId?: Long): Promise<LocoPacket> {
|
|
153
197
|
if (!this.connection) throw new Error('Not connected')
|
|
154
198
|
return this.connection.sendPacket('LCHATLIST', {
|
|
@@ -73,10 +73,12 @@ export interface KakaoChat {
|
|
|
73
73
|
chat_id: string
|
|
74
74
|
type: number
|
|
75
75
|
display_name: string | null
|
|
76
|
+
title: string | null
|
|
76
77
|
active_members: number
|
|
77
78
|
unread_count: number
|
|
78
79
|
last_message: {
|
|
79
80
|
author_id: number
|
|
81
|
+
author_name: string | null
|
|
80
82
|
message: string
|
|
81
83
|
sent_at: number
|
|
82
84
|
} | null
|
|
@@ -86,10 +88,28 @@ export interface KakaoMessage {
|
|
|
86
88
|
log_id: string
|
|
87
89
|
type: number
|
|
88
90
|
author_id: number
|
|
91
|
+
author_name: string | null
|
|
89
92
|
message: string
|
|
90
93
|
sent_at: number
|
|
91
94
|
}
|
|
92
95
|
|
|
96
|
+
export interface KakaoMember {
|
|
97
|
+
user_id: string
|
|
98
|
+
nickname: string
|
|
99
|
+
profile_image_url: string | null
|
|
100
|
+
full_profile_image_url: string | null
|
|
101
|
+
original_profile_image_url: string | null
|
|
102
|
+
status_message: string | null
|
|
103
|
+
country_iso: string | null
|
|
104
|
+
/** KakaoTalk UserType: 100=FRIEND, 1000=OPEN_PROFILE, etc. `null` when the server omits the field. */
|
|
105
|
+
user_type: number | null
|
|
106
|
+
/** Open-chat-only fields below; `null` for normal chats. */
|
|
107
|
+
open_token: number | null
|
|
108
|
+
open_profile_link_id: string | null
|
|
109
|
+
/** OpenChannelUserPerm bitfield: 1=OWNER, 2=NONE, 4=MANAGER, 8=BOT. Forward-compatible with future values. */
|
|
110
|
+
open_permission: number | null
|
|
111
|
+
}
|
|
112
|
+
|
|
93
113
|
export interface KakaoSendResult {
|
|
94
114
|
success: boolean
|
|
95
115
|
status_code: number
|
|
@@ -102,11 +122,13 @@ export const KakaoChatSchema = z.object({
|
|
|
102
122
|
chat_id: z.string(),
|
|
103
123
|
type: z.number(),
|
|
104
124
|
display_name: z.string().nullable(),
|
|
125
|
+
title: z.string().nullable(),
|
|
105
126
|
active_members: z.number(),
|
|
106
127
|
unread_count: z.number(),
|
|
107
128
|
last_message: z
|
|
108
129
|
.object({
|
|
109
130
|
author_id: z.number(),
|
|
131
|
+
author_name: z.string().nullable(),
|
|
110
132
|
message: z.string(),
|
|
111
133
|
sent_at: z.number(),
|
|
112
134
|
})
|
|
@@ -117,10 +139,25 @@ export const KakaoMessageSchema = z.object({
|
|
|
117
139
|
log_id: z.string(),
|
|
118
140
|
type: z.number(),
|
|
119
141
|
author_id: z.number(),
|
|
142
|
+
author_name: z.string().nullable(),
|
|
120
143
|
message: z.string(),
|
|
121
144
|
sent_at: z.number(),
|
|
122
145
|
})
|
|
123
146
|
|
|
147
|
+
export const KakaoMemberSchema = z.object({
|
|
148
|
+
user_id: z.string(),
|
|
149
|
+
nickname: z.string(),
|
|
150
|
+
profile_image_url: z.string().nullable(),
|
|
151
|
+
full_profile_image_url: z.string().nullable(),
|
|
152
|
+
original_profile_image_url: z.string().nullable(),
|
|
153
|
+
status_message: z.string().nullable(),
|
|
154
|
+
country_iso: z.string().nullable(),
|
|
155
|
+
user_type: z.number().nullable(),
|
|
156
|
+
open_token: z.number().nullable(),
|
|
157
|
+
open_profile_link_id: z.string().nullable(),
|
|
158
|
+
open_permission: z.number().nullable(),
|
|
159
|
+
})
|
|
160
|
+
|
|
124
161
|
export const KakaoSendResultSchema = z.object({
|
|
125
162
|
success: z.boolean(),
|
|
126
163
|
status_code: z.number(),
|
|
@@ -183,6 +220,7 @@ export interface KakaoTalkPushMessageEvent {
|
|
|
183
220
|
chat_id: string
|
|
184
221
|
log_id: string
|
|
185
222
|
author_id: number
|
|
223
|
+
author_name: string | null
|
|
186
224
|
message: string
|
|
187
225
|
message_type: number
|
|
188
226
|
sent_at: number
|
|
@@ -228,6 +266,7 @@ export const KakaoTalkPushMessageEventSchema = z.object({
|
|
|
228
266
|
chat_id: z.string(),
|
|
229
267
|
log_id: z.string(),
|
|
230
268
|
author_id: z.number(),
|
|
269
|
+
author_name: z.string().nullable(),
|
|
231
270
|
message: z.string(),
|
|
232
271
|
message_type: z.number(),
|
|
233
272
|
sent_at: z.number(),
|
|
@@ -243,6 +243,73 @@ describe('SlackBotClient', () => {
|
|
|
243
243
|
expect(result.ts).toBe('1234567890.123456')
|
|
244
244
|
expect(result.text).toBe('Hello')
|
|
245
245
|
})
|
|
246
|
+
|
|
247
|
+
it('does not pass blocks/attachments when omitted', async () => {
|
|
248
|
+
// given
|
|
249
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
250
|
+
|
|
251
|
+
// when
|
|
252
|
+
await client.postMessage('C123', 'Hello')
|
|
253
|
+
|
|
254
|
+
// then
|
|
255
|
+
expect(mockChat.postMessage).toHaveBeenCalledWith({
|
|
256
|
+
channel: 'C123',
|
|
257
|
+
text: 'Hello',
|
|
258
|
+
thread_ts: undefined,
|
|
259
|
+
blocks: undefined,
|
|
260
|
+
attachments: undefined,
|
|
261
|
+
unfurl_links: undefined,
|
|
262
|
+
unfurl_media: undefined,
|
|
263
|
+
mrkdwn: undefined,
|
|
264
|
+
})
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
it('forwards blocks to chat.postMessage', async () => {
|
|
268
|
+
// given
|
|
269
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
270
|
+
const blocks = [
|
|
271
|
+
{
|
|
272
|
+
type: 'section',
|
|
273
|
+
text: { type: 'mrkdwn', text: '*hello*' },
|
|
274
|
+
},
|
|
275
|
+
]
|
|
276
|
+
|
|
277
|
+
// when
|
|
278
|
+
await client.postMessage('C123', 'Hello', { blocks })
|
|
279
|
+
|
|
280
|
+
// then
|
|
281
|
+
expect(mockChat.postMessage).toHaveBeenCalledWith(
|
|
282
|
+
expect.objectContaining({ channel: 'C123', text: 'Hello', blocks }),
|
|
283
|
+
)
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
it('forwards thread_ts, attachments, and unfurl/mrkdwn flags', async () => {
|
|
287
|
+
// given
|
|
288
|
+
const client = await new SlackBotClient().login({ token: 'xoxb-test-token' })
|
|
289
|
+
const attachments = [{ text: 'attached' }]
|
|
290
|
+
|
|
291
|
+
// when
|
|
292
|
+
await client.postMessage('C123', 'Hello', {
|
|
293
|
+
thread_ts: '1234567890.000001',
|
|
294
|
+
attachments,
|
|
295
|
+
unfurl_links: false,
|
|
296
|
+
unfurl_media: false,
|
|
297
|
+
mrkdwn: true,
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
// then
|
|
301
|
+
expect(mockChat.postMessage).toHaveBeenCalledWith(
|
|
302
|
+
expect.objectContaining({
|
|
303
|
+
channel: 'C123',
|
|
304
|
+
text: 'Hello',
|
|
305
|
+
thread_ts: '1234567890.000001',
|
|
306
|
+
attachments,
|
|
307
|
+
unfurl_links: false,
|
|
308
|
+
unfurl_media: false,
|
|
309
|
+
mrkdwn: true,
|
|
310
|
+
}),
|
|
311
|
+
)
|
|
312
|
+
})
|
|
246
313
|
})
|
|
247
314
|
|
|
248
315
|
describe('getConversationHistory', () => {
|
|
@@ -103,12 +103,28 @@ export class SlackBotClient {
|
|
|
103
103
|
})
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
-
async postMessage(
|
|
106
|
+
async postMessage(
|
|
107
|
+
channel: string,
|
|
108
|
+
text: string,
|
|
109
|
+
options?: {
|
|
110
|
+
thread_ts?: string
|
|
111
|
+
blocks?: unknown[]
|
|
112
|
+
attachments?: unknown[]
|
|
113
|
+
unfurl_links?: boolean
|
|
114
|
+
unfurl_media?: boolean
|
|
115
|
+
mrkdwn?: boolean
|
|
116
|
+
},
|
|
117
|
+
): Promise<SlackMessage> {
|
|
107
118
|
return this.withRetry(async () => {
|
|
108
119
|
const response = await this.ensureAuth().chat.postMessage({
|
|
109
120
|
channel,
|
|
110
121
|
text,
|
|
111
122
|
thread_ts: options?.thread_ts,
|
|
123
|
+
blocks: options?.blocks as any,
|
|
124
|
+
attachments: options?.attachments as any,
|
|
125
|
+
unfurl_links: options?.unfurl_links,
|
|
126
|
+
unfurl_media: options?.unfurl_media,
|
|
127
|
+
mrkdwn: options?.mrkdwn,
|
|
112
128
|
})
|
|
113
129
|
this.checkResponse(response)
|
|
114
130
|
|
|
@@ -26,10 +26,10 @@ export class KakaoTalkAdapter implements PlatformAdapter {
|
|
|
26
26
|
|
|
27
27
|
async getChannels(): Promise<UnifiedChannel[]> {
|
|
28
28
|
const client = this.ensureClient()
|
|
29
|
-
const chats = await client.getChats()
|
|
29
|
+
const chats = await client.getChats({ resolveTitles: true })
|
|
30
30
|
return chats.map((chat) => ({
|
|
31
31
|
id: chat.chat_id,
|
|
32
|
-
name: chat.display_name || `Chat ${chat.chat_id}`,
|
|
32
|
+
name: chat.title || chat.display_name || `Chat ${chat.chat_id}`,
|
|
33
33
|
}))
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -39,7 +39,7 @@ export class KakaoTalkAdapter implements PlatformAdapter {
|
|
|
39
39
|
return messages.map((msg) => ({
|
|
40
40
|
id: msg.log_id,
|
|
41
41
|
channelId,
|
|
42
|
-
author: String(msg.author_id),
|
|
42
|
+
author: msg.author_name || String(msg.author_id),
|
|
43
43
|
content: msg.message ?? '',
|
|
44
44
|
timestamp: String(msg.sent_at),
|
|
45
45
|
}))
|