agent-messenger 2.2.0 → 2.4.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/README.md +16 -16
- package/.claude-plugin/marketplace.json +29 -29
- package/.claude-plugin/plugin.json +5 -5
- package/CONTRIBUTING.md +1 -1
- package/README.md +9 -6
- package/bun.lock +89 -105
- package/bunfig.toml +3 -0
- package/dist/package.json +13 -3
- package/dist/src/platforms/discordbot/client.js +2 -2
- package/dist/src/platforms/discordbot/client.js.map +1 -1
- 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 +2 -1
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +52 -2
- package/dist/src/platforms/kakaotalk/client.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/profile.d.ts +3 -0
- package/dist/src/platforms/kakaotalk/commands/profile.d.ts.map +1 -0
- package/dist/src/platforms/kakaotalk/commands/profile.js +19 -0
- package/dist/src/platforms/kakaotalk/commands/profile.js.map +1 -0
- package/dist/src/platforms/kakaotalk/index.d.ts +2 -2
- package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/index.js +1 -1
- package/dist/src/platforms/kakaotalk/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js +2 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
- package/dist/src/platforms/kakaotalk/types.d.ts +16 -0
- package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/types.js +8 -0
- package/dist/src/platforms/kakaotalk/types.js.map +1 -1
- package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/line/commands/auth.js +32 -20
- package/dist/src/platforms/line/commands/auth.js.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.js +2 -0
- package/dist/src/platforms/teams/commands/reaction.js.map +1 -1
- package/dist/src/platforms/webex/client.d.ts +2 -0
- package/dist/src/platforms/webex/client.d.ts.map +1 -1
- package/dist/src/platforms/webex/client.js +66 -23
- package/dist/src/platforms/webex/client.js.map +1 -1
- package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/commands/auth.js +4 -0
- package/dist/src/platforms/webex/commands/auth.js.map +1 -1
- package/dist/src/platforms/webex/encryption.d.ts +10 -0
- package/dist/src/platforms/webex/encryption.d.ts.map +1 -0
- package/dist/src/platforms/webex/encryption.js +49 -0
- package/dist/src/platforms/webex/encryption.js.map +1 -0
- package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/ensure-auth.js +4 -0
- package/dist/src/platforms/webex/ensure-auth.js.map +1 -1
- package/dist/src/platforms/webex/token-extractor.d.ts +6 -5
- package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/webex/token-extractor.js +92 -43
- package/dist/src/platforms/webex/token-extractor.js.map +1 -1
- package/dist/src/platforms/webex/types.d.ts +4 -0
- package/dist/src/platforms/webex/types.d.ts.map +1 -1
- package/dist/src/platforms/webex/types.js +2 -0
- package/dist/src/platforms/webex/types.js.map +1 -1
- package/dist/src/platforms/wechatbot/cli.d.ts +5 -0
- package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/cli.js +18 -0
- package/dist/src/platforms/wechatbot/cli.js.map +1 -0
- package/dist/src/platforms/wechatbot/client.d.ts +36 -0
- package/dist/src/platforms/wechatbot/client.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/client.js +208 -0
- package/dist/src/platforms/wechatbot/client.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/auth.d.ts +28 -0
- package/dist/src/platforms/wechatbot/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/auth.js +164 -0
- package/dist/src/platforms/wechatbot/commands/auth.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/index.d.ts +5 -0
- package/dist/src/platforms/wechatbot/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/index.js +5 -0
- package/dist/src/platforms/wechatbot/commands/index.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/message.d.ts +18 -0
- package/dist/src/platforms/wechatbot/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/message.js +80 -0
- package/dist/src/platforms/wechatbot/commands/message.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/shared.d.ts +9 -0
- package/dist/src/platforms/wechatbot/commands/shared.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/shared.js +13 -0
- package/dist/src/platforms/wechatbot/commands/shared.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/template.d.ts +19 -0
- package/dist/src/platforms/wechatbot/commands/template.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/template.js +76 -0
- package/dist/src/platforms/wechatbot/commands/template.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/user.d.ts +20 -0
- package/dist/src/platforms/wechatbot/commands/user.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/user.js +53 -0
- package/dist/src/platforms/wechatbot/commands/user.js.map +1 -0
- package/dist/src/platforms/wechatbot/credential-manager.d.ts +17 -0
- package/dist/src/platforms/wechatbot/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/credential-manager.js +121 -0
- package/dist/src/platforms/wechatbot/credential-manager.js.map +1 -0
- package/dist/src/platforms/wechatbot/index.d.ts +5 -0
- package/dist/src/platforms/wechatbot/index.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/index.js +4 -0
- package/dist/src/platforms/wechatbot/index.js.map +1 -0
- package/dist/src/platforms/wechatbot/types.d.ts +94 -0
- package/dist/src/platforms/wechatbot/types.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/types.js +54 -0
- package/dist/src/platforms/wechatbot/types.js.map +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/client.js +27 -13
- package/dist/src/platforms/whatsapp/client.js.map +1 -1
- package/dist/src/platforms/whatsapp/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/commands/auth.js +21 -18
- package/dist/src/platforms/whatsapp/commands/auth.js.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.js +14 -8
- package/dist/src/platforms/whatsapp/credential-manager.js.map +1 -1
- package/docs/content/docs/agent-skills.mdx +4 -4
- package/docs/content/docs/cli/channeltalk.mdx +1 -1
- package/docs/content/docs/cli/channeltalkbot.mdx +1 -1
- package/docs/content/docs/cli/discord.mdx +1 -1
- package/docs/content/docs/cli/discordbot.mdx +1 -1
- package/docs/content/docs/cli/instagram.mdx +1 -1
- package/docs/content/docs/cli/kakaotalk.mdx +1 -1
- package/docs/content/docs/cli/line.mdx +1 -1
- package/docs/content/docs/cli/meta.json +1 -0
- package/docs/content/docs/cli/slack.mdx +1 -1
- package/docs/content/docs/cli/slackbot.mdx +1 -1
- package/docs/content/docs/cli/teams.mdx +1 -1
- package/docs/content/docs/cli/webex.mdx +5 -3
- package/docs/content/docs/cli/wechatbot.mdx +179 -0
- package/docs/content/docs/cli/whatsapp.mdx +1 -1
- package/docs/content/docs/cli/whatsappbot.mdx +1 -1
- package/docs/content/docs/sdk/meta.json +1 -1
- package/docs/content/docs/sdk/wechatbot.mdx +282 -0
- package/docs/content/docs/tui.mdx +1 -1
- package/docs/src/app/page.tsx +5 -5
- package/package.json +13 -3
- 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 +24 -1
- package/skills/agent-line/SKILL.md +7 -11
- package/skills/agent-line/references/authentication.md +13 -4
- 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-webex/SKILL.md +1 -1
- package/skills/agent-webex/references/authentication.md +4 -3
- package/skills/agent-webex/references/common-patterns.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +385 -0
- package/skills/agent-whatsapp/SKILL.md +12 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/discord/credential-manager.test.ts +18 -1
- package/src/platforms/discordbot/client.ts +2 -2
- package/src/platforms/instagram/commands/auth.test.ts +216 -0
- package/src/platforms/instagram/commands/chat.test.ts +127 -0
- package/src/platforms/instagram/commands/message.test.ts +178 -0
- package/src/platforms/kakaotalk/cli.ts +2 -1
- package/src/platforms/kakaotalk/client.test.ts +157 -0
- package/src/platforms/kakaotalk/client.ts +57 -3
- package/src/platforms/kakaotalk/commands/auth.test.ts +299 -0
- package/src/platforms/kakaotalk/commands/chat.test.ts +97 -0
- package/src/platforms/kakaotalk/commands/index.ts +1 -0
- package/src/platforms/kakaotalk/commands/message.test.ts +113 -0
- package/src/platforms/kakaotalk/commands/profile.test.ts +84 -0
- package/src/platforms/kakaotalk/commands/profile.ts +21 -0
- package/src/platforms/kakaotalk/index.test.ts +5 -0
- package/src/platforms/kakaotalk/index.ts +2 -0
- package/src/platforms/kakaotalk/protocol/session.ts +2 -0
- package/src/platforms/kakaotalk/types.ts +18 -0
- package/src/platforms/line/commands/auth.test.ts +141 -0
- package/src/platforms/line/commands/auth.ts +28 -19
- package/src/platforms/line/commands/chat.test.ts +110 -0
- package/src/platforms/line/commands/friend.test.ts +98 -0
- package/src/platforms/line/commands/message.test.ts +119 -0
- package/src/platforms/line/commands/profile.test.ts +85 -0
- package/src/platforms/slackbot/commands/channel.test.ts +139 -0
- package/src/platforms/slackbot/commands/message.test.ts +226 -0
- package/src/platforms/slackbot/commands/reaction.test.ts +90 -0
- package/src/platforms/slackbot/commands/user.test.ts +143 -0
- package/src/platforms/teams/commands/reaction.test.ts +45 -61
- package/src/platforms/teams/commands/reaction.ts +2 -0
- package/src/platforms/telegram/commands/chat.test.ts +125 -0
- package/src/platforms/telegram/commands/message.test.ts +92 -0
- package/src/platforms/webex/client.ts +98 -26
- package/src/platforms/webex/commands/auth.ts +4 -0
- package/src/platforms/webex/commands/member.test.ts +65 -58
- package/src/platforms/webex/commands/message.test.ts +78 -121
- package/src/platforms/webex/commands/snapshot.test.ts +59 -46
- package/src/platforms/webex/commands/space.test.ts +49 -48
- package/src/platforms/webex/encryption.ts +53 -0
- package/src/platforms/webex/ensure-auth.ts +4 -0
- package/src/platforms/webex/token-extractor.ts +107 -40
- package/src/platforms/webex/types.ts +4 -0
- package/src/platforms/webex/typings/node-jose.d.ts +27 -0
- package/src/platforms/wechatbot/cli.ts +24 -0
- package/src/platforms/wechatbot/client.test.ts +497 -0
- package/src/platforms/wechatbot/client.ts +268 -0
- package/src/platforms/wechatbot/commands/auth.test.ts +211 -0
- package/src/platforms/wechatbot/commands/auth.ts +203 -0
- package/src/platforms/wechatbot/commands/index.ts +4 -0
- package/src/platforms/wechatbot/commands/message.test.ts +155 -0
- package/src/platforms/wechatbot/commands/message.ts +104 -0
- package/src/platforms/wechatbot/commands/shared.ts +22 -0
- package/src/platforms/wechatbot/commands/template.test.ts +199 -0
- package/src/platforms/wechatbot/commands/template.ts +102 -0
- package/src/platforms/wechatbot/commands/user.test.ts +165 -0
- package/src/platforms/wechatbot/commands/user.ts +75 -0
- package/src/platforms/wechatbot/credential-manager.test.ts +255 -0
- package/src/platforms/wechatbot/credential-manager.ts +148 -0
- package/src/platforms/wechatbot/index.test.ts +49 -0
- package/src/platforms/wechatbot/index.ts +19 -0
- package/src/platforms/wechatbot/types.test.ts +223 -0
- package/src/platforms/wechatbot/types.ts +107 -0
- package/src/platforms/whatsapp/client.ts +24 -13
- package/src/platforms/whatsapp/commands/auth.test.ts +311 -0
- package/src/platforms/whatsapp/commands/auth.ts +21 -17
- package/src/platforms/whatsapp/commands/chat.test.ts +198 -0
- package/src/platforms/whatsapp/commands/message.test.ts +231 -0
- package/src/platforms/whatsapp/credential-manager.test.ts +20 -0
- package/src/platforms/whatsapp/credential-manager.ts +17 -8
- package/src/platforms/whatsappbot/commands/auth.test.ts +217 -0
- package/src/platforms/whatsappbot/commands/message.test.ts +198 -0
- package/src/platforms/whatsappbot/commands/template.test.ts +112 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
const originalConsoleLog = console.log
|
|
4
|
+
import type { Command } from 'commander'
|
|
5
|
+
|
|
6
|
+
const mockListChats = mock(() =>
|
|
7
|
+
Promise.resolve([
|
|
8
|
+
{ id: 'thread-1', title: 'Alice', last_message: 'Hi' },
|
|
9
|
+
{ id: 'thread-2', title: 'Bob', last_message: 'Hey' },
|
|
10
|
+
]),
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
const mockSearchChats = mock(() =>
|
|
14
|
+
Promise.resolve([{ id: 'thread-1', title: 'Alice', last_message: 'Hi' }]),
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
const mockClient = {
|
|
18
|
+
listChats: mockListChats,
|
|
19
|
+
searchChats: mockSearchChats,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
mock.module('./shared', () => ({
|
|
23
|
+
withInstagramClient: async (_options: unknown, fn: (client: typeof mockClient) => Promise<unknown>) => {
|
|
24
|
+
return fn(mockClient)
|
|
25
|
+
},
|
|
26
|
+
}))
|
|
27
|
+
|
|
28
|
+
import { chatCommand } from './chat'
|
|
29
|
+
|
|
30
|
+
function resetCommandState(cmd: Command): void {
|
|
31
|
+
for (const sub of cmd.commands) {
|
|
32
|
+
(sub as unknown as { _optionValues: Record<string, unknown>; _optionValueSources: Record<string, unknown> })._optionValues = {}
|
|
33
|
+
;(sub as unknown as { _optionValues: Record<string, unknown>; _optionValueSources: Record<string, unknown> })._optionValueSources = {}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
afterAll(() => {
|
|
38
|
+
mock.restore()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe('chat commands', () => {
|
|
42
|
+
let consoleLogSpy: ReturnType<typeof mock>
|
|
43
|
+
let processExitSpy: ReturnType<typeof spyOn>
|
|
44
|
+
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
resetCommandState(chatCommand)
|
|
47
|
+
|
|
48
|
+
mockListChats.mockReset()
|
|
49
|
+
mockSearchChats.mockReset()
|
|
50
|
+
|
|
51
|
+
mockListChats.mockImplementation(() =>
|
|
52
|
+
Promise.resolve([
|
|
53
|
+
{ id: 'thread-1', title: 'Alice', last_message: 'Hi' },
|
|
54
|
+
{ id: 'thread-2', title: 'Bob', last_message: 'Hey' },
|
|
55
|
+
]),
|
|
56
|
+
)
|
|
57
|
+
mockSearchChats.mockImplementation(() =>
|
|
58
|
+
Promise.resolve([{ id: 'thread-1', title: 'Alice', last_message: 'Hi' }]),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
consoleLogSpy = mock((..._args: unknown[]) => {}); console.log = consoleLogSpy
|
|
62
|
+
processExitSpy = spyOn(process, 'exit').mockImplementation(() => {
|
|
63
|
+
throw new Error('process.exit called')
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
console.log = originalConsoleLog
|
|
69
|
+
processExitSpy.mockRestore()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('list', () => {
|
|
73
|
+
test('lists DM conversations', async () => {
|
|
74
|
+
await expect(
|
|
75
|
+
chatCommand.parseAsync(['list'], { from: 'user' }),
|
|
76
|
+
).rejects.toThrow('process.exit called')
|
|
77
|
+
|
|
78
|
+
expect(processExitSpy).toHaveBeenCalledWith(0)
|
|
79
|
+
expect(mockListChats).toHaveBeenCalledWith(20)
|
|
80
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
81
|
+
expect(output).toHaveLength(2)
|
|
82
|
+
expect(output[0].id).toBe('thread-1')
|
|
83
|
+
expect(output[1].id).toBe('thread-2')
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
test('passes custom limit', async () => {
|
|
87
|
+
await expect(
|
|
88
|
+
chatCommand.parseAsync(['list', '--limit', '5'], { from: 'user' }),
|
|
89
|
+
).rejects.toThrow('process.exit called')
|
|
90
|
+
|
|
91
|
+
expect(mockListChats).toHaveBeenCalledWith(5)
|
|
92
|
+
})
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
describe('search', () => {
|
|
96
|
+
test('searches DM conversations by query', async () => {
|
|
97
|
+
await expect(
|
|
98
|
+
chatCommand.parseAsync(['search', 'Alice'], { from: 'user' }),
|
|
99
|
+
).rejects.toThrow('process.exit called')
|
|
100
|
+
|
|
101
|
+
expect(processExitSpy).toHaveBeenCalledWith(0)
|
|
102
|
+
expect(mockSearchChats).toHaveBeenCalledWith('Alice', 20)
|
|
103
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
104
|
+
expect(output).toHaveLength(1)
|
|
105
|
+
expect(output[0].id).toBe('thread-1')
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test('passes custom limit to search', async () => {
|
|
109
|
+
await expect(
|
|
110
|
+
chatCommand.parseAsync(['search', 'Alice', '--limit', '10'], { from: 'user' }),
|
|
111
|
+
).rejects.toThrow('process.exit called')
|
|
112
|
+
|
|
113
|
+
expect(mockSearchChats).toHaveBeenCalledWith('Alice', 10)
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('returns empty array when no results', async () => {
|
|
117
|
+
mockSearchChats.mockImplementation(() => Promise.resolve([]))
|
|
118
|
+
|
|
119
|
+
await expect(
|
|
120
|
+
chatCommand.parseAsync(['search', 'nobody'], { from: 'user' }),
|
|
121
|
+
).rejects.toThrow('process.exit called')
|
|
122
|
+
|
|
123
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
124
|
+
expect(output).toEqual([])
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
})
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { afterAll, afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
const originalConsoleLog = console.log
|
|
4
|
+
import type { Command } from 'commander'
|
|
5
|
+
|
|
6
|
+
const mockGetMessages = mock(() => Promise.resolve([{ id: 'msg-1', text: 'Hello' }]))
|
|
7
|
+
const mockSendMessage = mock(() => Promise.resolve({ id: 'msg-2', text: 'Sent' }))
|
|
8
|
+
const mockSendMessageToUser = mock(() => Promise.resolve({ id: 'msg-3', text: 'Sent to user' }))
|
|
9
|
+
const mockSearchMessages = mock(() => Promise.resolve([{ id: 'msg-4', text: 'Found' }]))
|
|
10
|
+
const mockSearchUsers = mock(() => Promise.resolve([{ pk: '999', username: 'targetuser' }]))
|
|
11
|
+
|
|
12
|
+
const mockClient = {
|
|
13
|
+
getMessages: mockGetMessages,
|
|
14
|
+
sendMessage: mockSendMessage,
|
|
15
|
+
sendMessageToUser: mockSendMessageToUser,
|
|
16
|
+
searchMessages: mockSearchMessages,
|
|
17
|
+
searchUsers: mockSearchUsers,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
mock.module('./shared', () => ({
|
|
21
|
+
withInstagramClient: async (_options: unknown, fn: (client: typeof mockClient) => Promise<unknown>) => {
|
|
22
|
+
return fn(mockClient)
|
|
23
|
+
},
|
|
24
|
+
}))
|
|
25
|
+
|
|
26
|
+
import { messageCommand } from './message'
|
|
27
|
+
|
|
28
|
+
function resetCommandState(cmd: Command): void {
|
|
29
|
+
for (const sub of cmd.commands) {
|
|
30
|
+
(sub as unknown as { _optionValues: Record<string, unknown>; _optionValueSources: Record<string, unknown> })._optionValues = {}
|
|
31
|
+
;(sub as unknown as { _optionValues: Record<string, unknown>; _optionValueSources: Record<string, unknown> })._optionValueSources = {}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
afterAll(() => {
|
|
36
|
+
mock.restore()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
describe('message commands', () => {
|
|
40
|
+
let consoleLogSpy: ReturnType<typeof mock>
|
|
41
|
+
let processExitSpy: ReturnType<typeof spyOn>
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
resetCommandState(messageCommand)
|
|
45
|
+
|
|
46
|
+
mockGetMessages.mockReset()
|
|
47
|
+
mockSendMessage.mockReset()
|
|
48
|
+
mockSendMessageToUser.mockReset()
|
|
49
|
+
mockSearchMessages.mockReset()
|
|
50
|
+
mockSearchUsers.mockReset()
|
|
51
|
+
|
|
52
|
+
mockGetMessages.mockImplementation(() => Promise.resolve([{ id: 'msg-1', text: 'Hello' }]))
|
|
53
|
+
mockSendMessage.mockImplementation(() => Promise.resolve({ id: 'msg-2', text: 'Sent' }))
|
|
54
|
+
mockSendMessageToUser.mockImplementation(() => Promise.resolve({ id: 'msg-3', text: 'Sent to user' }))
|
|
55
|
+
mockSearchMessages.mockImplementation(() => Promise.resolve([{ id: 'msg-4', text: 'Found' }]))
|
|
56
|
+
mockSearchUsers.mockImplementation(() => Promise.resolve([{ pk: '999', username: 'targetuser' }]))
|
|
57
|
+
|
|
58
|
+
consoleLogSpy = mock((..._args: unknown[]) => {}); console.log = consoleLogSpy
|
|
59
|
+
processExitSpy = spyOn(process, 'exit').mockImplementation(() => {
|
|
60
|
+
throw new Error('process.exit called')
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
console.log = originalConsoleLog
|
|
66
|
+
processExitSpy.mockRestore()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
describe('list', () => {
|
|
70
|
+
test('lists messages from a thread', async () => {
|
|
71
|
+
await expect(
|
|
72
|
+
messageCommand.parseAsync(['list', 'thread-123'], { from: 'user' }),
|
|
73
|
+
).rejects.toThrow('process.exit called')
|
|
74
|
+
|
|
75
|
+
expect(processExitSpy).toHaveBeenCalledWith(0)
|
|
76
|
+
expect(mockGetMessages).toHaveBeenCalledWith('thread-123', 25)
|
|
77
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
78
|
+
expect(output).toEqual([{ id: 'msg-1', text: 'Hello' }])
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('passes custom limit', async () => {
|
|
82
|
+
await expect(
|
|
83
|
+
messageCommand.parseAsync(['list', 'thread-123', '--limit', '10'], { from: 'user' }),
|
|
84
|
+
).rejects.toThrow('process.exit called')
|
|
85
|
+
|
|
86
|
+
expect(mockGetMessages).toHaveBeenCalledWith('thread-123', 10)
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
describe('send', () => {
|
|
91
|
+
test('sends a message to a thread', async () => {
|
|
92
|
+
await expect(
|
|
93
|
+
messageCommand.parseAsync(['send', 'thread-123', 'Hello world'], { from: 'user' }),
|
|
94
|
+
).rejects.toThrow('process.exit called')
|
|
95
|
+
|
|
96
|
+
expect(processExitSpy).toHaveBeenCalledWith(0)
|
|
97
|
+
expect(mockSendMessage).toHaveBeenCalledWith('thread-123', 'Hello world')
|
|
98
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
99
|
+
expect(output).toEqual({ id: 'msg-2', text: 'Sent' })
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
describe('send-to', () => {
|
|
104
|
+
test('sends a message to a user by username', async () => {
|
|
105
|
+
await expect(
|
|
106
|
+
messageCommand.parseAsync(['send-to', 'targetuser', 'Hi there'], { from: 'user' }),
|
|
107
|
+
).rejects.toThrow('process.exit called')
|
|
108
|
+
|
|
109
|
+
expect(processExitSpy).toHaveBeenCalledWith(0)
|
|
110
|
+
expect(mockSearchUsers).toHaveBeenCalledWith('targetuser')
|
|
111
|
+
expect(mockSendMessageToUser).toHaveBeenCalledWith('999', 'Hi there')
|
|
112
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
113
|
+
expect(output).toEqual({ id: 'msg-3', text: 'Sent to user' })
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('strips @ prefix from username', async () => {
|
|
117
|
+
await expect(
|
|
118
|
+
messageCommand.parseAsync(['send-to', '@targetuser', 'Hi there'], { from: 'user' }),
|
|
119
|
+
).rejects.toThrow('process.exit called')
|
|
120
|
+
|
|
121
|
+
expect(mockSearchUsers).toHaveBeenCalledWith('targetuser')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('handles user not found error', async () => {
|
|
125
|
+
mockSearchUsers.mockImplementation(() => Promise.resolve([]))
|
|
126
|
+
|
|
127
|
+
try {
|
|
128
|
+
await messageCommand.parseAsync(['send-to', 'unknownuser', 'Hi'], { from: 'user' })
|
|
129
|
+
} catch {
|
|
130
|
+
/* empty */
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
expect(mockSendMessageToUser).not.toHaveBeenCalled()
|
|
134
|
+
})
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
describe('search', () => {
|
|
138
|
+
test('searches messages by query', async () => {
|
|
139
|
+
await expect(
|
|
140
|
+
messageCommand.parseAsync(['search', 'hello'], { from: 'user' }),
|
|
141
|
+
).rejects.toThrow('process.exit called')
|
|
142
|
+
|
|
143
|
+
expect(processExitSpy).toHaveBeenCalledWith(0)
|
|
144
|
+
expect(mockSearchMessages).toHaveBeenCalledWith('hello', { threadId: undefined, limit: 20 })
|
|
145
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
146
|
+
expect(output).toEqual([{ id: 'msg-4', text: 'Found' }])
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test('passes thread option to search', async () => {
|
|
150
|
+
await expect(
|
|
151
|
+
messageCommand.parseAsync(['search', 'hello', '--thread', 'thread-456'], { from: 'user' }),
|
|
152
|
+
).rejects.toThrow('process.exit called')
|
|
153
|
+
|
|
154
|
+
expect(mockSearchMessages).toHaveBeenCalledWith('hello', { threadId: 'thread-456', limit: 20 })
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
test('passes limit option to search', async () => {
|
|
158
|
+
await expect(
|
|
159
|
+
messageCommand.parseAsync(['search', 'hello2', '--limit', '5'], { from: 'user' }),
|
|
160
|
+
).rejects.toThrow('process.exit called')
|
|
161
|
+
|
|
162
|
+
expect(mockSearchMessages).toHaveBeenCalledWith('hello2', { threadId: undefined, limit: 5 })
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
describe('search-users', () => {
|
|
167
|
+
test('searches users by query', async () => {
|
|
168
|
+
await expect(
|
|
169
|
+
messageCommand.parseAsync(['search-users', 'target'], { from: 'user' }),
|
|
170
|
+
).rejects.toThrow('process.exit called')
|
|
171
|
+
|
|
172
|
+
expect(processExitSpy).toHaveBeenCalledWith(0)
|
|
173
|
+
expect(mockSearchUsers).toHaveBeenCalledWith('target')
|
|
174
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0])
|
|
175
|
+
expect(output).toEqual([{ pk: '999', username: 'targetuser' }])
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
})
|
|
@@ -4,7 +4,7 @@ import type { Command as CommandType } from 'commander'
|
|
|
4
4
|
import { Command } from 'commander'
|
|
5
5
|
|
|
6
6
|
import pkg from '../../../package.json' with { type: 'json' }
|
|
7
|
-
import { authCommand, chatCommand, messageCommand } from './commands/index'
|
|
7
|
+
import { authCommand, chatCommand, messageCommand, profileCommand } from './commands/index'
|
|
8
8
|
import { ensureKakaoAuth } from './ensure-auth'
|
|
9
9
|
|
|
10
10
|
function isAuthCommand(command: CommandType): boolean {
|
|
@@ -31,6 +31,7 @@ program.hook('preAction', async (_thisCommand, actionCommand) => {
|
|
|
31
31
|
program.addCommand(authCommand)
|
|
32
32
|
program.addCommand(chatCommand)
|
|
33
33
|
program.addCommand(messageCommand)
|
|
34
|
+
program.addCommand(profileCommand)
|
|
34
35
|
|
|
35
36
|
program.parse(process.argv)
|
|
36
37
|
|
|
@@ -146,6 +146,68 @@ describe('KakaoTalkClient', () => {
|
|
|
146
146
|
client.close()
|
|
147
147
|
})
|
|
148
148
|
|
|
149
|
+
test('falls back to LCHATLIST when login snapshot is empty (new device)', async () => {
|
|
150
|
+
// given — LOGINLIST returns empty chatDatas with eof:true (new device scenario)
|
|
151
|
+
const emptyLoginResult = {
|
|
152
|
+
chatDatas: [],
|
|
153
|
+
lastTokenId: makeLong(0),
|
|
154
|
+
lastChatId: makeLong(0),
|
|
155
|
+
eof: true,
|
|
156
|
+
}
|
|
157
|
+
mockLogin.mockResolvedValue(emptyLoginResult)
|
|
158
|
+
|
|
159
|
+
mockGetChatList.mockResolvedValueOnce({
|
|
160
|
+
body: {
|
|
161
|
+
chatDatas: [
|
|
162
|
+
{
|
|
163
|
+
c: 100,
|
|
164
|
+
t: 1,
|
|
165
|
+
k: ['Alice', 'Bob'],
|
|
166
|
+
a: 2,
|
|
167
|
+
n: 3,
|
|
168
|
+
o: 1700000000,
|
|
169
|
+
l: { authorId: 1, message: 'hi', sendAt: 1700000000 },
|
|
170
|
+
ll: makeLong(999),
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
c: 200,
|
|
174
|
+
t: 2,
|
|
175
|
+
k: ['Charlie'],
|
|
176
|
+
a: 1,
|
|
177
|
+
n: 0,
|
|
178
|
+
o: 1699999000,
|
|
179
|
+
l: null,
|
|
180
|
+
ll: makeLong(500),
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
lastTokenId: makeLong(1),
|
|
184
|
+
lastChatId: makeLong(200),
|
|
185
|
+
eof: true,
|
|
186
|
+
},
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
// when — default chat list (no --all flag)
|
|
190
|
+
const client = await new KakaoTalkClient().login({ oauthToken: 'token', userId: 'user1', deviceUuid: 'device1' })
|
|
191
|
+
const chats = await client.getChats()
|
|
192
|
+
|
|
193
|
+
// then — fetched via LCHATLIST despite eof:true and no --all
|
|
194
|
+
expect(chats).toHaveLength(2)
|
|
195
|
+
expect(mockGetChatList).toHaveBeenCalledTimes(1)
|
|
196
|
+
expect(chats[0].display_name).toBe('Alice, Bob')
|
|
197
|
+
expect(chats[1].display_name).toBe('Charlie')
|
|
198
|
+
|
|
199
|
+
client.close()
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
test('does not call LCHATLIST when login snapshot has chats', async () => {
|
|
203
|
+
const client = await new KakaoTalkClient().login({ oauthToken: 'token', userId: 'user1', deviceUuid: 'device1' })
|
|
204
|
+
await client.getChats()
|
|
205
|
+
|
|
206
|
+
expect(mockGetChatList).not.toHaveBeenCalled()
|
|
207
|
+
|
|
208
|
+
client.close()
|
|
209
|
+
})
|
|
210
|
+
|
|
149
211
|
test('paginates when all=true and not eof', async () => {
|
|
150
212
|
const loginResult = {
|
|
151
213
|
...DEFAULT_LOGIN_RESULT,
|
|
@@ -384,6 +446,101 @@ describe('KakaoTalkClient', () => {
|
|
|
384
446
|
})
|
|
385
447
|
})
|
|
386
448
|
|
|
449
|
+
describe('getProfile', () => {
|
|
450
|
+
const mockFetch = mock(() => Promise.resolve(new Response()))
|
|
451
|
+
|
|
452
|
+
beforeEach(() => {
|
|
453
|
+
mockFetch.mockReset()
|
|
454
|
+
globalThis.fetch = mockFetch as unknown as typeof fetch
|
|
455
|
+
})
|
|
456
|
+
|
|
457
|
+
afterEach(() => {
|
|
458
|
+
mockFetch.mockReset()
|
|
459
|
+
})
|
|
460
|
+
|
|
461
|
+
function makeJsonResponse(data: unknown, status = 200): Response {
|
|
462
|
+
return new Response(JSON.stringify(data), {
|
|
463
|
+
status,
|
|
464
|
+
headers: { 'Content-Type': 'application/json' },
|
|
465
|
+
})
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
test('returns profile data on success', async () => {
|
|
469
|
+
mockFetch
|
|
470
|
+
.mockResolvedValueOnce(makeJsonResponse({
|
|
471
|
+
profile: {
|
|
472
|
+
nickName: 'Test User',
|
|
473
|
+
profileImageUrl: 'https://example.com/profile.jpg',
|
|
474
|
+
originalProfileImageUrl: 'https://example.com/original.jpg',
|
|
475
|
+
statusMessage: 'Hello world',
|
|
476
|
+
},
|
|
477
|
+
}))
|
|
478
|
+
.mockResolvedValueOnce(makeJsonResponse({ accountDisplayId: 'testuser123' }))
|
|
479
|
+
|
|
480
|
+
const client = await new KakaoTalkClient().login({ oauthToken: 'mytoken', userId: 'user42', deviceUuid: 'device1' })
|
|
481
|
+
const profile = await client.getProfile()
|
|
482
|
+
|
|
483
|
+
expect(profile.user_id).toBe('user42')
|
|
484
|
+
expect(profile.nickname).toBe('Test User')
|
|
485
|
+
expect(profile.profile_image_url).toBe('https://example.com/profile.jpg')
|
|
486
|
+
expect(profile.original_profile_image_url).toBe('https://example.com/original.jpg')
|
|
487
|
+
expect(profile.status_message).toBe('Hello world')
|
|
488
|
+
expect(profile.account_display_id).toBe('testuser123')
|
|
489
|
+
|
|
490
|
+
client.close()
|
|
491
|
+
})
|
|
492
|
+
|
|
493
|
+
test('throws not_authenticated when not logged in', async () => {
|
|
494
|
+
const client = new KakaoTalkClient()
|
|
495
|
+
try {
|
|
496
|
+
await client.getProfile()
|
|
497
|
+
expect.unreachable('should have thrown')
|
|
498
|
+
} catch (e) {
|
|
499
|
+
expect(e).toBeInstanceOf(KakaoTalkError)
|
|
500
|
+
expect((e as KakaoTalkError).code).toBe('not_authenticated')
|
|
501
|
+
}
|
|
502
|
+
})
|
|
503
|
+
|
|
504
|
+
test('throws profile_request_failed when profile HTTP request fails', async () => {
|
|
505
|
+
mockFetch
|
|
506
|
+
.mockResolvedValueOnce(makeJsonResponse({}, 401))
|
|
507
|
+
.mockResolvedValueOnce(makeJsonResponse({ accountDisplayId: null }))
|
|
508
|
+
|
|
509
|
+
const client = await new KakaoTalkClient().login({ oauthToken: 'mytoken', userId: 'user42', deviceUuid: 'device1' })
|
|
510
|
+
try {
|
|
511
|
+
await client.getProfile()
|
|
512
|
+
expect.unreachable('should have thrown')
|
|
513
|
+
} catch (e) {
|
|
514
|
+
expect(e).toBeInstanceOf(KakaoTalkError)
|
|
515
|
+
expect((e as KakaoTalkError).code).toBe('profile_request_failed')
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
client.close()
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
test('returns null account_display_id when more_settings request fails', async () => {
|
|
522
|
+
mockFetch
|
|
523
|
+
.mockResolvedValueOnce(makeJsonResponse({
|
|
524
|
+
profile: {
|
|
525
|
+
nickName: 'Test User',
|
|
526
|
+
profileImageUrl: null,
|
|
527
|
+
originalProfileImageUrl: null,
|
|
528
|
+
statusMessage: null,
|
|
529
|
+
},
|
|
530
|
+
}))
|
|
531
|
+
.mockResolvedValueOnce(makeJsonResponse({}, 500))
|
|
532
|
+
|
|
533
|
+
const client = await new KakaoTalkClient().login({ oauthToken: 'mytoken', userId: 'user42', deviceUuid: 'device1' })
|
|
534
|
+
const profile = await client.getProfile()
|
|
535
|
+
|
|
536
|
+
expect(profile.user_id).toBe('user42')
|
|
537
|
+
expect(profile.nickname).toBe('Test User')
|
|
538
|
+
expect(profile.account_display_id).toBeNull()
|
|
539
|
+
|
|
540
|
+
client.close()
|
|
541
|
+
})
|
|
542
|
+
})
|
|
543
|
+
|
|
387
544
|
describe('session lifecycle', () => {
|
|
388
545
|
test('lazy init: does not call login until first method call', async () => {
|
|
389
546
|
const client = await new KakaoTalkClient().login({ oauthToken: 'token', userId: 'user1', deviceUuid: 'device1' })
|
|
@@ -2,9 +2,10 @@ import { Long } from 'bson'
|
|
|
2
2
|
|
|
3
3
|
import { warn } from '@/shared/utils/stderr'
|
|
4
4
|
|
|
5
|
+
import { APP_VERSION, LANG, OS } from './protocol/config'
|
|
5
6
|
import { LocoSession } from './protocol/session'
|
|
6
7
|
import type { ChatListResponse, LoginListResponse } from './protocol/types'
|
|
7
|
-
import type { KakaoChat, KakaoMessage, KakaoSendResult } from './types'
|
|
8
|
+
import type { KakaoChat, KakaoMessage, KakaoProfile, KakaoSendResult } from './types'
|
|
8
9
|
|
|
9
10
|
export class KakaoTalkError extends Error {
|
|
10
11
|
code: string
|
|
@@ -206,11 +207,22 @@ export class KakaoTalkClient {
|
|
|
206
207
|
|
|
207
208
|
collectChats((loginResult.chatDatas ?? []) as ChatData[], allChats, seenChatIds)
|
|
208
209
|
|
|
209
|
-
|
|
210
|
+
// Paginate via LCHATLIST when explicitly requested (--all / --search) OR when
|
|
211
|
+
// the login snapshot is empty. New device registrations often return an empty
|
|
212
|
+
// chatDatas with eof=true because the server has no prior sync state for the
|
|
213
|
+
// device — LCHATLIST fetches the canonical chat list regardless of device history.
|
|
214
|
+
const snapshotEmpty = allChats.length === 0
|
|
215
|
+
if (options?.all || options?.search || snapshotEmpty) {
|
|
210
216
|
let cursor: ChatListResponse = loginResult
|
|
211
217
|
let pages = 0
|
|
212
218
|
|
|
213
|
-
while (
|
|
219
|
+
while (pages < MAX_PAGES) {
|
|
220
|
+
// Trust eof only when the snapshot had data. When the snapshot was empty
|
|
221
|
+
// (new device), ignore eof for the first iteration so we always attempt
|
|
222
|
+
// at least one LCHATLIST call.
|
|
223
|
+
if (cursor.eof && !snapshotEmpty) break
|
|
224
|
+
if (cursor.eof && snapshotEmpty && pages > 0) break
|
|
225
|
+
|
|
214
226
|
const lastTokenId = bsonToLong(cursor.lastTokenId)
|
|
215
227
|
const lastChatId = bsonToLong(cursor.lastChatId)
|
|
216
228
|
|
|
@@ -317,6 +329,48 @@ export class KakaoTalkClient {
|
|
|
317
329
|
})
|
|
318
330
|
}
|
|
319
331
|
|
|
332
|
+
async getProfile(): Promise<KakaoProfile> {
|
|
333
|
+
this.ensureAuth()
|
|
334
|
+
try {
|
|
335
|
+
const headers = {
|
|
336
|
+
Authorization: `${this.oauthToken}-${this.deviceUuid}`,
|
|
337
|
+
A: `${OS}/${APP_VERSION}/${LANG}`,
|
|
338
|
+
'User-Agent': `KT/${APP_VERSION} Md/macOS ${LANG}`,
|
|
339
|
+
Accept: '*/*',
|
|
340
|
+
'Accept-Language': LANG,
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const [profileRes, settingsRes] = await Promise.all([
|
|
344
|
+
fetch('https://katalk.kakao.com/mac/profile3/me.json', { headers }),
|
|
345
|
+
fetch('https://katalk.kakao.com/mac/account/more_settings.json?since=0&lang=ko', { headers }),
|
|
346
|
+
])
|
|
347
|
+
|
|
348
|
+
if (!profileRes.ok) {
|
|
349
|
+
throw new KakaoTalkError(`Profile request failed: ${profileRes.status}`, 'profile_request_failed')
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const profileData = await profileRes.json() as Record<string, unknown>
|
|
353
|
+
const profile = profileData.profile as Record<string, unknown> | undefined
|
|
354
|
+
|
|
355
|
+
let accountDisplayId: string | null = null
|
|
356
|
+
if (settingsRes.ok) {
|
|
357
|
+
const settingsData = await settingsRes.json() as Record<string, unknown>
|
|
358
|
+
accountDisplayId = (settingsData.accountDisplayId as string) || null
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return {
|
|
362
|
+
user_id: this.userId!,
|
|
363
|
+
nickname: (profile?.nickName as string) || '',
|
|
364
|
+
profile_image_url: (profile?.profileImageUrl as string) || null,
|
|
365
|
+
original_profile_image_url: (profile?.originalProfileImageUrl as string) || null,
|
|
366
|
+
status_message: (profile?.statusMessage as string) || null,
|
|
367
|
+
account_display_id: accountDisplayId,
|
|
368
|
+
}
|
|
369
|
+
} catch (error) {
|
|
370
|
+
throw wrapError(error, 'get_profile_failed')
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
320
374
|
close(): void {
|
|
321
375
|
this.closed = true
|
|
322
376
|
if (this.state) {
|