agent-messenger 2.1.0 → 2.2.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/.env.template +35 -17
- package/README.md +7 -7
- package/bun.lock +6 -6
- package/dist/package.json +2 -2
- package/dist/src/platforms/channeltalk/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/commands/auth.js +35 -28
- package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
- package/dist/src/platforms/channeltalk/ensure-auth.js +6 -6
- package/dist/src/platforms/channeltalk/ensure-auth.js.map +1 -1
- package/dist/src/platforms/channeltalk/token-extractor.d.ts +23 -1
- package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/token-extractor.js +299 -29
- package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
- package/dist/src/platforms/discord/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/auth.js +57 -49
- package/dist/src/platforms/discord/commands/auth.js.map +1 -1
- package/dist/src/platforms/discord/ensure-auth.js +3 -3
- package/dist/src/platforms/discord/ensure-auth.js.map +1 -1
- package/dist/src/platforms/discord/token-extractor.d.ts +6 -1
- package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/discord/token-extractor.js +167 -14
- package/dist/src/platforms/discord/token-extractor.js.map +1 -1
- package/dist/src/platforms/instagram/client.d.ts +2 -0
- package/dist/src/platforms/instagram/client.d.ts.map +1 -1
- package/dist/src/platforms/instagram/client.js +2 -2
- package/dist/src/platforms/instagram/client.js.map +1 -1
- package/dist/src/platforms/instagram/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/instagram/commands/auth.js +107 -14
- package/dist/src/platforms/instagram/commands/auth.js.map +1 -1
- package/dist/src/platforms/instagram/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/instagram/ensure-auth.js +57 -11
- package/dist/src/platforms/instagram/ensure-auth.js.map +1 -1
- package/dist/src/platforms/instagram/index.d.ts +1 -0
- package/dist/src/platforms/instagram/index.d.ts.map +1 -1
- package/dist/src/platforms/instagram/index.js +1 -0
- package/dist/src/platforms/instagram/index.js.map +1 -1
- package/dist/src/platforms/instagram/token-extractor.d.ts +44 -0
- package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -0
- package/dist/src/platforms/instagram/token-extractor.js +407 -0
- package/dist/src/platforms/instagram/token-extractor.js.map +1 -0
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +2 -1
- package/dist/src/platforms/kakaotalk/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/auth.js +14 -13
- package/dist/src/platforms/kakaotalk/commands/auth.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.js +2 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.js.map +1 -1
- package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/line/commands/auth.js +6 -5
- package/dist/src/platforms/line/commands/auth.js.map +1 -1
- package/dist/src/platforms/slack/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/auth.js +11 -10
- package/dist/src/platforms/slack/commands/auth.js.map +1 -1
- package/dist/src/platforms/slack/token-extractor.d.ts +9 -0
- package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/slack/token-extractor.js +300 -23
- package/dist/src/platforms/slack/token-extractor.js.map +1 -1
- package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/auth.js +9 -8
- package/dist/src/platforms/teams/commands/auth.js.map +1 -1
- package/dist/src/platforms/teams/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/teams/ensure-auth.js +2 -1
- package/dist/src/platforms/teams/ensure-auth.js.map +1 -1
- package/dist/src/platforms/teams/token-extractor.d.ts +5 -0
- package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/teams/token-extractor.js +161 -29
- package/dist/src/platforms/teams/token-extractor.js.map +1 -1
- package/dist/src/platforms/telegram/client.d.ts.map +1 -1
- package/dist/src/platforms/telegram/client.js +25 -7
- package/dist/src/platforms/telegram/client.js.map +1 -1
- package/dist/src/platforms/telegram/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/telegram/commands/auth.js +6 -5
- package/dist/src/platforms/telegram/commands/auth.js.map +1 -1
- package/dist/src/platforms/webex/client.d.ts +10 -0
- package/dist/src/platforms/webex/client.d.ts.map +1 -1
- package/dist/src/platforms/webex/client.js +124 -0
- package/dist/src/platforms/webex/client.js.map +1 -1
- package/dist/src/platforms/webex/commands/auth.d.ts +4 -0
- package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/commands/auth.js +46 -4
- package/dist/src/platforms/webex/commands/auth.js.map +1 -1
- package/dist/src/platforms/webex/credential-manager.js +1 -1
- package/dist/src/platforms/webex/credential-manager.js.map +1 -1
- package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/webex/ensure-auth.js +21 -5
- package/dist/src/platforms/webex/ensure-auth.js.map +1 -1
- package/dist/src/platforms/webex/index.d.ts +2 -0
- package/dist/src/platforms/webex/index.d.ts.map +1 -1
- package/dist/src/platforms/webex/index.js +1 -0
- package/dist/src/platforms/webex/index.js.map +1 -1
- package/dist/src/platforms/webex/token-extractor.d.ts +28 -0
- package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -0
- package/dist/src/platforms/webex/token-extractor.js +344 -0
- package/dist/src/platforms/webex/token-extractor.js.map +1 -0
- package/dist/src/platforms/webex/types.d.ts +4 -1
- package/dist/src/platforms/webex/types.d.ts.map +1 -1
- package/dist/src/platforms/webex/types.js +2 -1
- package/dist/src/platforms/webex/types.js.map +1 -1
- package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/client.js +6 -2
- package/dist/src/platforms/whatsapp/client.js.map +1 -1
- package/dist/src/shared/utils/derived-key-cache.d.ts +1 -1
- package/dist/src/shared/utils/derived-key-cache.d.ts.map +1 -1
- package/dist/src/shared/utils/error-handler.d.ts +1 -1
- package/dist/src/shared/utils/error-handler.d.ts.map +1 -1
- package/dist/src/shared/utils/error-handler.js +3 -2
- package/dist/src/shared/utils/error-handler.js.map +1 -1
- package/dist/src/shared/utils/stderr.d.ts +5 -0
- package/dist/src/shared/utils/stderr.d.ts.map +1 -0
- package/dist/src/shared/utils/stderr.js +18 -0
- package/dist/src/shared/utils/stderr.js.map +1 -0
- package/docs/content/docs/cli/channeltalk.mdx +7 -7
- package/docs/content/docs/cli/discord.mdx +3 -3
- package/docs/content/docs/cli/instagram.mdx +28 -6
- package/docs/content/docs/cli/slack.mdx +2 -2
- package/docs/content/docs/cli/teams.mdx +6 -4
- package/docs/content/docs/cli/webex.mdx +30 -11
- package/e2e/README.md +132 -8
- package/e2e/channeltalk.e2e.test.ts +2 -7
- package/e2e/channeltalkbot.e2e.test.ts +2 -6
- package/e2e/config.ts +172 -10
- package/e2e/helpers.ts +7 -0
- package/e2e/instagram.e2e.test.ts +97 -0
- package/e2e/kakaotalk.e2e.test.ts +74 -0
- package/e2e/line.e2e.test.ts +92 -0
- package/e2e/teams.e2e.test.ts +46 -1
- package/e2e/telegram.e2e.test.ts +84 -0
- package/e2e/webex.e2e.test.ts +190 -0
- package/e2e/whatsapp.e2e.test.ts +90 -0
- package/e2e/whatsappbot.e2e.test.ts +78 -0
- package/package.json +2 -2
- package/skills/agent-channeltalk/SKILL.md +9 -9
- package/skills/agent-channeltalk/references/authentication.md +21 -18
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +5 -5
- package/skills/agent-discord/references/authentication.md +8 -8
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +51 -9
- package/skills/agent-instagram/references/authentication.md +35 -3
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +5 -5
- package/skills/agent-slack/references/authentication.md +8 -8
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +6 -6
- package/skills/agent-teams/references/authentication.md +8 -8
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +35 -15
- package/skills/agent-webex/references/authentication.md +62 -9
- package/skills/agent-webex/references/common-patterns.md +6 -3
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/channeltalk/commands/auth.test.ts +5 -5
- package/src/platforms/channeltalk/commands/auth.ts +38 -32
- package/src/platforms/channeltalk/ensure-auth.test.ts +6 -6
- package/src/platforms/channeltalk/ensure-auth.ts +6 -6
- package/src/platforms/channeltalk/token-extractor.test.ts +182 -15
- package/src/platforms/channeltalk/token-extractor.ts +344 -30
- package/src/platforms/discord/commands/auth.test.ts +3 -3
- package/src/platforms/discord/commands/auth.ts +58 -54
- package/src/platforms/discord/ensure-auth.test.ts +3 -3
- package/src/platforms/discord/ensure-auth.ts +3 -3
- package/src/platforms/discord/token-extractor.test.ts +199 -27
- package/src/platforms/discord/token-extractor.ts +190 -17
- package/src/platforms/instagram/client.ts +2 -2
- package/src/platforms/instagram/commands/auth.ts +133 -14
- package/src/platforms/instagram/ensure-auth.ts +63 -12
- package/src/platforms/instagram/index.ts +1 -0
- package/src/platforms/instagram/token-extractor.test.ts +424 -0
- package/src/platforms/instagram/token-extractor.ts +478 -0
- package/src/platforms/kakaotalk/client.ts +3 -1
- package/src/platforms/kakaotalk/commands/auth.ts +14 -13
- package/src/platforms/kakaotalk/protocol/connection.ts +3 -1
- package/src/platforms/line/commands/auth.ts +7 -6
- package/src/platforms/slack/cli.test.ts +6 -5
- package/src/platforms/slack/commands/auth.test.ts +11 -7
- package/src/platforms/slack/commands/auth.ts +11 -10
- package/src/platforms/slack/token-extractor.test.ts +98 -1
- package/src/platforms/slack/token-extractor.ts +338 -26
- package/src/platforms/teams/commands/auth.ts +9 -8
- package/src/platforms/teams/ensure-auth.ts +3 -1
- package/src/platforms/teams/token-extractor.test.ts +136 -17
- package/src/platforms/teams/token-extractor.ts +182 -31
- package/src/platforms/telegram/client.test.ts +134 -0
- package/src/platforms/telegram/client.ts +27 -6
- package/src/platforms/telegram/commands/auth.ts +6 -5
- package/src/platforms/webex/client.test.ts +314 -0
- package/src/platforms/webex/client.ts +158 -0
- package/src/platforms/webex/commands/auth.ts +67 -4
- package/src/platforms/webex/commands/member.test.ts +10 -1
- package/src/platforms/webex/commands/message.test.ts +9 -5
- package/src/platforms/webex/commands/snapshot.test.ts +13 -4
- package/src/platforms/webex/commands/space.test.ts +12 -2
- package/src/platforms/webex/credential-manager.ts +1 -1
- package/src/platforms/webex/ensure-auth.test.ts +4 -0
- package/src/platforms/webex/ensure-auth.ts +23 -4
- package/src/platforms/webex/index.ts +2 -0
- package/src/platforms/webex/token-extractor.test.ts +327 -0
- package/src/platforms/webex/token-extractor.ts +393 -0
- package/src/platforms/webex/types.ts +4 -2
- package/src/platforms/whatsapp/client.ts +11 -7
- package/src/shared/utils/derived-key-cache.ts +1 -1
- package/src/shared/utils/error-handler.ts +4 -2
- package/src/shared/utils/stderr.ts +22 -0
|
@@ -13,9 +13,9 @@ let credManagerClearTokenSpy: ReturnType<typeof spyOn>
|
|
|
13
13
|
|
|
14
14
|
beforeEach(() => {
|
|
15
15
|
// Spy on DiscordTokenExtractor.prototype.extract
|
|
16
|
-
extractorExtractSpy = spyOn(DiscordTokenExtractor.prototype, 'extract').mockResolvedValue({
|
|
16
|
+
extractorExtractSpy = spyOn(DiscordTokenExtractor.prototype, 'extract').mockResolvedValue([{
|
|
17
17
|
token: 'test-token-123',
|
|
18
|
-
})
|
|
18
|
+
}])
|
|
19
19
|
|
|
20
20
|
// Spy on DiscordClient.prototype methods
|
|
21
21
|
clientTestAuthSpy = spyOn(DiscordClient.prototype, 'testAuth').mockResolvedValue({
|
|
@@ -53,7 +53,7 @@ test('extract: calls DiscordTokenExtractor', async () => {
|
|
|
53
53
|
const extractor = new DiscordTokenExtractor()
|
|
54
54
|
const result = await extractor.extract()
|
|
55
55
|
expect(result).toBeDefined()
|
|
56
|
-
expect(result?.token).toBe('test-token-123')
|
|
56
|
+
expect(result[0]?.token).toBe('test-token-123')
|
|
57
57
|
})
|
|
58
58
|
|
|
59
59
|
test('extract: validates token with DiscordClient', async () => {
|
|
@@ -2,6 +2,7 @@ import { Command } from 'commander'
|
|
|
2
2
|
|
|
3
3
|
import { handleError } from '@/shared/utils/error-handler'
|
|
4
4
|
import { formatOutput } from '@/shared/utils/output'
|
|
5
|
+
import { debug } from '@/shared/utils/stderr'
|
|
5
6
|
|
|
6
7
|
import { DiscordClient } from '../client'
|
|
7
8
|
import { DiscordCredentialManager } from '../credential-manager'
|
|
@@ -29,12 +30,12 @@ export async function extractAction(options: { pretty?: boolean; debug?: boolean
|
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
if (options.debug) {
|
|
32
|
-
|
|
33
|
+
debug(`[debug] Extracting Discord token...`)
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
const extracted = await extractor.extract()
|
|
36
37
|
|
|
37
|
-
if (
|
|
38
|
+
if (extracted.length === 0) {
|
|
38
39
|
console.log(
|
|
39
40
|
formatOutput(
|
|
40
41
|
{
|
|
@@ -47,62 +48,58 @@ export async function extractAction(options: { pretty?: boolean; debug?: boolean
|
|
|
47
48
|
process.exit(1)
|
|
48
49
|
}
|
|
49
50
|
|
|
50
|
-
|
|
51
|
-
console.error(`[debug] Token extracted: ${extracted.token.substring(0, 20)}...`)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
try {
|
|
55
|
-
const client = await new DiscordClient().login({ token: extracted.token })
|
|
56
|
-
|
|
51
|
+
for (const { token } of extracted) {
|
|
57
52
|
if (options.debug) {
|
|
58
|
-
|
|
53
|
+
debug(`[debug] Token extracted: ${token.substring(0, 20)}...`)
|
|
59
54
|
}
|
|
60
55
|
|
|
61
|
-
|
|
56
|
+
try {
|
|
57
|
+
const client = await new DiscordClient().login({ token })
|
|
62
58
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
59
|
+
if (options.debug) {
|
|
60
|
+
debug(`[debug] Testing token validity...`)
|
|
61
|
+
}
|
|
67
62
|
|
|
68
|
-
|
|
63
|
+
const authInfo = await client.testAuth()
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
if (options.debug) {
|
|
66
|
+
debug(`[debug] ✓ Token valid for user: ${authInfo.username}`)
|
|
67
|
+
debug(`[debug] Discovering servers...`)
|
|
68
|
+
}
|
|
73
69
|
|
|
74
|
-
|
|
75
|
-
console.log(
|
|
76
|
-
formatOutput(
|
|
77
|
-
{
|
|
78
|
-
error: 'No servers found. Make sure you are a member of at least one Discord server.',
|
|
79
|
-
},
|
|
80
|
-
options.pretty,
|
|
81
|
-
),
|
|
82
|
-
)
|
|
83
|
-
process.exit(1)
|
|
84
|
-
}
|
|
70
|
+
const servers = await client.listServers()
|
|
85
71
|
|
|
86
|
-
|
|
87
|
-
|
|
72
|
+
if (options.debug) {
|
|
73
|
+
debug(`[debug] ✓ Found ${servers.length} server(s)`)
|
|
74
|
+
}
|
|
88
75
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
76
|
+
if (servers.length === 0) {
|
|
77
|
+
if (options.debug) {
|
|
78
|
+
debug(`[debug] No servers found for this token, trying next...`)
|
|
79
|
+
}
|
|
80
|
+
continue
|
|
93
81
|
}
|
|
94
|
-
}
|
|
95
82
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
current_server: servers[0].id,
|
|
99
|
-
servers: serverMap,
|
|
100
|
-
}
|
|
83
|
+
const credManager = new DiscordCredentialManager()
|
|
84
|
+
const serverMap: Record<string, { server_id: string; server_name: string }> = {}
|
|
101
85
|
|
|
102
|
-
|
|
86
|
+
for (const server of servers) {
|
|
87
|
+
serverMap[server.id] = {
|
|
88
|
+
server_id: server.id,
|
|
89
|
+
server_name: server.name,
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const config = {
|
|
94
|
+
token,
|
|
95
|
+
current_server: servers[0].id,
|
|
96
|
+
servers: serverMap,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
await credManager.save(config)
|
|
103
100
|
|
|
104
101
|
if (options.debug) {
|
|
105
|
-
|
|
102
|
+
debug(`[debug] ✓ Credentials saved`)
|
|
106
103
|
}
|
|
107
104
|
|
|
108
105
|
const output = {
|
|
@@ -111,18 +108,25 @@ export async function extractAction(options: { pretty?: boolean; debug?: boolean
|
|
|
111
108
|
}
|
|
112
109
|
|
|
113
110
|
console.log(formatOutput(output, options.pretty))
|
|
111
|
+
return
|
|
114
112
|
} catch (error) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
},
|
|
121
|
-
options.pretty,
|
|
122
|
-
),
|
|
123
|
-
)
|
|
124
|
-
process.exit(1)
|
|
113
|
+
if (options.debug) {
|
|
114
|
+
debug(`[debug] Token validation failed: ${(error as Error).message}, trying next...`)
|
|
115
|
+
}
|
|
116
|
+
continue
|
|
117
|
+
}
|
|
125
118
|
}
|
|
119
|
+
|
|
120
|
+
console.log(
|
|
121
|
+
formatOutput(
|
|
122
|
+
{
|
|
123
|
+
error: 'No usable Discord token found. Tokens may be expired or have no servers.',
|
|
124
|
+
hint: 'Make sure Discord is logged in and you are a member of at least one server.',
|
|
125
|
+
},
|
|
126
|
+
options.pretty,
|
|
127
|
+
),
|
|
128
|
+
)
|
|
129
|
+
process.exit(1)
|
|
126
130
|
} catch (error) {
|
|
127
131
|
handleError(error as Error)
|
|
128
132
|
}
|
|
@@ -14,9 +14,9 @@ let saveSpy: ReturnType<typeof spyOn>
|
|
|
14
14
|
beforeEach(() => {
|
|
15
15
|
getTokenSpy = spyOn(DiscordCredentialManager.prototype, 'getToken').mockResolvedValue(null)
|
|
16
16
|
|
|
17
|
-
extractSpy = spyOn(DiscordTokenExtractor.prototype, 'extract').mockResolvedValue({
|
|
17
|
+
extractSpy = spyOn(DiscordTokenExtractor.prototype, 'extract').mockResolvedValue([{
|
|
18
18
|
token: 'test-token-123',
|
|
19
|
-
})
|
|
19
|
+
}])
|
|
20
20
|
|
|
21
21
|
testAuthSpy = spyOn(DiscordClient.prototype, 'testAuth').mockResolvedValue({
|
|
22
22
|
id: 'user-123',
|
|
@@ -90,7 +90,7 @@ describe('ensureDiscordAuth', () => {
|
|
|
90
90
|
|
|
91
91
|
test('does not save when extraction returns null', async () => {
|
|
92
92
|
// given
|
|
93
|
-
extractSpy.mockResolvedValue(
|
|
93
|
+
extractSpy.mockResolvedValue([])
|
|
94
94
|
|
|
95
95
|
// when
|
|
96
96
|
await ensureDiscordAuth()
|
|
@@ -10,9 +10,9 @@ export async function ensureDiscordAuth(): Promise<void> {
|
|
|
10
10
|
|
|
11
11
|
const extractor = new DiscordTokenExtractor()
|
|
12
12
|
const extracted = await extractor.extract()
|
|
13
|
-
if (
|
|
13
|
+
if (extracted.length === 0) return
|
|
14
14
|
|
|
15
|
-
const client = await new DiscordClient().login({ token: extracted.token })
|
|
15
|
+
const client = await new DiscordClient().login({ token: extracted[0].token })
|
|
16
16
|
const authInfo = await client.testAuth()
|
|
17
17
|
if (!authInfo) return
|
|
18
18
|
|
|
@@ -23,7 +23,7 @@ export async function ensureDiscordAuth(): Promise<void> {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
await credManager.save({
|
|
26
|
-
token: extracted.token,
|
|
26
|
+
token: extracted[0].token,
|
|
27
27
|
current_server: servers[0]?.id ?? null,
|
|
28
28
|
servers: serverMap,
|
|
29
29
|
})
|
|
@@ -55,6 +55,38 @@ describe('DiscordTokenExtractor', () => {
|
|
|
55
55
|
})
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
+
describe('getBrowserLevelDBDirs', () => {
|
|
59
|
+
test('returns browser LevelDB paths on macOS', () => {
|
|
60
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin')
|
|
61
|
+
const dirs = darwinExtractor.getBrowserLevelDBDirs()
|
|
62
|
+
|
|
63
|
+
const chromeBase = join(homedir(), 'Library', 'Application Support', 'Google', 'Chrome')
|
|
64
|
+
expect(dirs).toContain(join(chromeBase, 'Default', 'Local Storage', 'leveldb'))
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
test('returns browser LevelDB paths on Linux', () => {
|
|
68
|
+
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
69
|
+
const dirs = linuxExtractor.getBrowserLevelDBDirs()
|
|
70
|
+
|
|
71
|
+
const chromeBase = join(homedir(), '.config', 'google-chrome')
|
|
72
|
+
expect(dirs).toContain(join(chromeBase, 'Default', 'Local Storage', 'leveldb'))
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('returns browser LevelDB paths on Windows', () => {
|
|
76
|
+
const winExtractor = new DiscordTokenExtractor('win32')
|
|
77
|
+
const dirs = winExtractor.getBrowserLevelDBDirs()
|
|
78
|
+
|
|
79
|
+
const localAppData = process.env.LOCALAPPDATA || join(homedir(), 'AppData', 'Local')
|
|
80
|
+
const chromeBase = join(localAppData, 'Google', 'Chrome', 'User Data')
|
|
81
|
+
expect(dirs).toContain(join(chromeBase, 'Default', 'Local Storage', 'leveldb'))
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
test('returns empty array for unsupported platform', () => {
|
|
85
|
+
const unsupportedExtractor = new DiscordTokenExtractor('freebsd' as NodeJS.Platform)
|
|
86
|
+
expect(unsupportedExtractor.getBrowserLevelDBDirs()).toEqual([])
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
58
90
|
describe('token patterns', () => {
|
|
59
91
|
test('validates standard token format (base64.base64.base64)', () => {
|
|
60
92
|
const validToken = 'XXXXXXXXXXXXXXXXXXXXXXXX.YYYYYY.ZZZZZZZZZZZZZZZZZZZZZZZZZ'
|
|
@@ -167,77 +199,217 @@ describe('DiscordTokenExtractor', () => {
|
|
|
167
199
|
})
|
|
168
200
|
|
|
169
201
|
describe('extract', () => {
|
|
170
|
-
test('returns
|
|
202
|
+
test('returns empty array when no Discord directories exist on linux', async () => {
|
|
171
203
|
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
172
|
-
const extractFromLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromLevelDB').mockResolvedValue(
|
|
204
|
+
const extractFromLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromLevelDB').mockResolvedValue([])
|
|
205
|
+
const extractFromBrowserLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromBrowserLevelDB').mockResolvedValue([])
|
|
173
206
|
|
|
174
207
|
const result = await linuxExtractor.extract()
|
|
175
|
-
expect(result).
|
|
208
|
+
expect(result).toEqual([])
|
|
176
209
|
|
|
177
210
|
extractFromLevelDBSpy.mockRestore()
|
|
211
|
+
extractFromBrowserLevelDBSpy.mockRestore()
|
|
178
212
|
})
|
|
179
213
|
|
|
180
214
|
test('extracts token from LevelDB when available', async () => {
|
|
181
215
|
const mockToken = 'XXXXXXXXXXXXXXXXXXXXXXXX.YYYYYY.ZZZZZZZZZZZZZZZZZZZZZZZZZ'
|
|
182
216
|
|
|
183
217
|
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
184
|
-
const extractFromLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromLevelDB').mockResolvedValue(
|
|
185
|
-
token: mockToken,
|
|
186
|
-
|
|
218
|
+
const extractFromLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromLevelDB').mockResolvedValue([
|
|
219
|
+
{ token: mockToken },
|
|
220
|
+
])
|
|
221
|
+
const extractFromBrowserLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromBrowserLevelDB').mockResolvedValue([])
|
|
187
222
|
|
|
188
223
|
const result = await linuxExtractor.extract()
|
|
189
224
|
|
|
190
|
-
expect(result).not.
|
|
191
|
-
expect(result?.token).toBe(mockToken)
|
|
225
|
+
expect(result).not.toEqual([])
|
|
226
|
+
expect(result[0]?.token).toBe(mockToken)
|
|
192
227
|
|
|
193
228
|
extractFromLevelDBSpy.mockRestore()
|
|
229
|
+
extractFromBrowserLevelDBSpy.mockRestore()
|
|
194
230
|
})
|
|
195
231
|
|
|
196
|
-
test('tries
|
|
232
|
+
test('tries browser LevelDB when desktop LevelDB extraction fails', async () => {
|
|
233
|
+
const mockToken = 'XXXXXXXXXXXXXXXXXXXXXXXX.YYYYYY.browser_token_1234567890123'
|
|
234
|
+
|
|
235
|
+
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
236
|
+
const extractFromLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromLevelDB').mockResolvedValue([])
|
|
237
|
+
const extractFromBrowserLevelDBSpy = spyOn(
|
|
238
|
+
linuxExtractor as any,
|
|
239
|
+
'extractFromBrowserLevelDB',
|
|
240
|
+
).mockResolvedValue([{ token: mockToken }])
|
|
241
|
+
|
|
242
|
+
const result = await linuxExtractor.extract()
|
|
243
|
+
|
|
244
|
+
expect(extractFromLevelDBSpy).toHaveBeenCalled()
|
|
245
|
+
expect(extractFromBrowserLevelDBSpy).toHaveBeenCalled()
|
|
246
|
+
expect(result[0]?.token).toBe(mockToken)
|
|
247
|
+
|
|
248
|
+
extractFromLevelDBSpy.mockRestore()
|
|
249
|
+
extractFromBrowserLevelDBSpy.mockRestore()
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
test('tries CDP on macOS when both LevelDB extractions fail', async () => {
|
|
197
253
|
const mockToken = 'XXXXXXXXXXXXXXXXXXXXXXXX.YYYYYY.cdp_token_12345678901234567'
|
|
198
254
|
|
|
199
255
|
const darwinExtractor = new DiscordTokenExtractor('darwin', 0)
|
|
200
|
-
const extractFromLevelDBSpy = spyOn(darwinExtractor as any, 'extractFromLevelDB').mockResolvedValue(
|
|
256
|
+
const extractFromLevelDBSpy = spyOn(darwinExtractor as any, 'extractFromLevelDB').mockResolvedValue([])
|
|
257
|
+
const extractFromBrowserLevelDBSpy = spyOn(
|
|
258
|
+
darwinExtractor as any,
|
|
259
|
+
'extractFromBrowserLevelDB',
|
|
260
|
+
).mockResolvedValue([])
|
|
201
261
|
const tryExtractViaCDPSpy = spyOn(darwinExtractor as any, 'tryExtractViaCDP').mockResolvedValue(mockToken)
|
|
202
262
|
|
|
203
263
|
const result = await darwinExtractor.extract()
|
|
204
264
|
|
|
205
|
-
expect(
|
|
206
|
-
expect(
|
|
265
|
+
expect(extractFromLevelDBSpy).toHaveBeenCalled()
|
|
266
|
+
expect(extractFromBrowserLevelDBSpy).toHaveBeenCalled()
|
|
267
|
+
expect(tryExtractViaCDPSpy).toHaveBeenCalled()
|
|
268
|
+
expect(result[0]?.token).toBe(mockToken)
|
|
207
269
|
|
|
208
270
|
extractFromLevelDBSpy.mockRestore()
|
|
271
|
+
extractFromBrowserLevelDBSpy.mockRestore()
|
|
209
272
|
tryExtractViaCDPSpy.mockRestore()
|
|
210
273
|
})
|
|
211
274
|
|
|
212
|
-
test('
|
|
275
|
+
test('browser LevelDB tried before CDP on macOS', async () => {
|
|
276
|
+
const callOrder: string[] = []
|
|
277
|
+
|
|
278
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0)
|
|
279
|
+
const extractFromLevelDBSpy = spyOn(darwinExtractor as any, 'extractFromLevelDB').mockImplementation(async () => {
|
|
280
|
+
callOrder.push('desktop')
|
|
281
|
+
return []
|
|
282
|
+
})
|
|
283
|
+
const extractFromBrowserLevelDBSpy = spyOn(
|
|
284
|
+
darwinExtractor as any,
|
|
285
|
+
'extractFromBrowserLevelDB',
|
|
286
|
+
).mockImplementation(async () => {
|
|
287
|
+
callOrder.push('browser')
|
|
288
|
+
return []
|
|
289
|
+
})
|
|
290
|
+
const tryExtractViaCDPSpy = spyOn(darwinExtractor as any, 'tryExtractViaCDP').mockImplementation(async () => {
|
|
291
|
+
callOrder.push('cdp')
|
|
292
|
+
return null
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
await darwinExtractor.extract()
|
|
296
|
+
|
|
297
|
+
expect(callOrder).toEqual(['desktop', 'browser', 'cdp'])
|
|
298
|
+
|
|
299
|
+
extractFromLevelDBSpy.mockRestore()
|
|
300
|
+
extractFromBrowserLevelDBSpy.mockRestore()
|
|
301
|
+
tryExtractViaCDPSpy.mockRestore()
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
test('returns all valid tokens found across variants', async () => {
|
|
213
305
|
const mockToken = 'XXXXXXXXXXXXXXXXXXXXXXXX.YYYYYY.first_token_found_1234567'
|
|
214
306
|
|
|
215
307
|
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
216
|
-
const extractFromLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromLevelDB').mockResolvedValue(
|
|
217
|
-
token: mockToken,
|
|
218
|
-
|
|
308
|
+
const extractFromLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromLevelDB').mockResolvedValue([
|
|
309
|
+
{ token: mockToken },
|
|
310
|
+
])
|
|
311
|
+
const extractFromBrowserLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromBrowserLevelDB').mockResolvedValue([])
|
|
312
|
+
|
|
313
|
+
const result = await linuxExtractor.extract()
|
|
314
|
+
|
|
315
|
+
expect(result).not.toEqual([])
|
|
316
|
+
expect(typeof result[0]?.token).toBe('string')
|
|
317
|
+
|
|
318
|
+
extractFromLevelDBSpy.mockRestore()
|
|
319
|
+
extractFromBrowserLevelDBSpy.mockRestore()
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
test('deduplicates the same token found in desktop and browser sources', async () => {
|
|
323
|
+
const mockToken = 'XXXXXXXXXXXXXXXXXXXXXXXX.YYYYYY.ZZZZZZZZZZZZZZZZZZZZZZZZZ'
|
|
324
|
+
|
|
325
|
+
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
326
|
+
const extractFromLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromLevelDB').mockResolvedValue([
|
|
327
|
+
{ token: mockToken },
|
|
328
|
+
])
|
|
329
|
+
const extractFromBrowserLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromBrowserLevelDB').mockResolvedValue([
|
|
330
|
+
{ token: mockToken },
|
|
331
|
+
])
|
|
332
|
+
|
|
333
|
+
const result = await linuxExtractor.extract()
|
|
334
|
+
expect(result).toHaveLength(1)
|
|
335
|
+
expect(result[0]?.token).toBe(mockToken)
|
|
336
|
+
|
|
337
|
+
extractFromLevelDBSpy.mockRestore()
|
|
338
|
+
extractFromBrowserLevelDBSpy.mockRestore()
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
test('collects multiple distinct tokens from browser profiles', async () => {
|
|
342
|
+
const token1 = 'XXXXXXXXXXXXXXXXXXXXXXXX.YYYYYY.browser_token_1234567890123'
|
|
343
|
+
const token2 = 'YYYYYYYYYYYYYYYYYYYYYYYY.ZZZZZZ.browser_token_2345678901234'
|
|
344
|
+
|
|
345
|
+
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
346
|
+
const extractFromLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromLevelDB').mockResolvedValue([])
|
|
347
|
+
const extractFromBrowserLevelDBSpy = spyOn(linuxExtractor as any, 'extractFromBrowserLevelDB').mockResolvedValue([
|
|
348
|
+
{ token: token1 },
|
|
349
|
+
{ token: token2 },
|
|
350
|
+
])
|
|
219
351
|
|
|
220
352
|
const result = await linuxExtractor.extract()
|
|
353
|
+
expect(result).toHaveLength(2)
|
|
354
|
+
expect(result.map((r) => r.token)).toContain(token1)
|
|
355
|
+
expect(result.map((r) => r.token)).toContain(token2)
|
|
356
|
+
|
|
357
|
+
extractFromLevelDBSpy.mockRestore()
|
|
358
|
+
extractFromBrowserLevelDBSpy.mockRestore()
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
test('does not call CDP when desktop LevelDB extraction returns results', async () => {
|
|
362
|
+
const mockToken = 'XXXXXXXXXXXXXXXXXXXXXXXX.YYYYYY.ZZZZZZZZZZZZZZZZZZZZZZZZZ'
|
|
221
363
|
|
|
222
|
-
|
|
223
|
-
|
|
364
|
+
const darwinExtractor = new DiscordTokenExtractor('darwin', 0)
|
|
365
|
+
const extractFromLevelDBSpy = spyOn(darwinExtractor as any, 'extractFromLevelDB').mockResolvedValue([
|
|
366
|
+
{ token: mockToken },
|
|
367
|
+
])
|
|
368
|
+
const extractFromBrowserLevelDBSpy = spyOn(darwinExtractor as any, 'extractFromBrowserLevelDB').mockResolvedValue([])
|
|
369
|
+
const tryExtractViaCDPSpy = spyOn(darwinExtractor as any, 'tryExtractViaCDP').mockResolvedValue(null)
|
|
370
|
+
|
|
371
|
+
await darwinExtractor.extract()
|
|
372
|
+
expect(tryExtractViaCDPSpy).not.toHaveBeenCalled()
|
|
224
373
|
|
|
225
374
|
extractFromLevelDBSpy.mockRestore()
|
|
375
|
+
extractFromBrowserLevelDBSpy.mockRestore()
|
|
376
|
+
tryExtractViaCDPSpy.mockRestore()
|
|
226
377
|
})
|
|
227
378
|
})
|
|
228
379
|
|
|
229
380
|
describe('getKeychainVariants', () => {
|
|
230
|
-
test('
|
|
381
|
+
test('includes Discord-specific keychain variants', () => {
|
|
231
382
|
const macExtractor = new DiscordTokenExtractor('darwin')
|
|
383
|
+
const variants = macExtractor.getKeychainVariants()
|
|
384
|
+
|
|
385
|
+
expect(variants).toContainEqual({ service: 'discord Safe Storage', account: 'discord Key' })
|
|
386
|
+
expect(variants).toContainEqual({ service: 'discordcanary Safe Storage', account: 'discordcanary Key' })
|
|
387
|
+
expect(variants).toContainEqual({ service: 'discordptb Safe Storage', account: 'discordptb Key' })
|
|
388
|
+
expect(variants).toContainEqual({ service: 'Discord Safe Storage', account: 'Discord' })
|
|
389
|
+
expect(variants).toContainEqual({ service: 'Discord Canary Safe Storage', account: 'Discord Canary' })
|
|
390
|
+
expect(variants).toContainEqual({ service: 'Discord PTB Safe Storage', account: 'Discord PTB' })
|
|
391
|
+
})
|
|
232
392
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
393
|
+
test('includes browser keychain variants appended after Discord entries', () => {
|
|
394
|
+
const macExtractor = new DiscordTokenExtractor('darwin')
|
|
395
|
+
const variants = macExtractor.getKeychainVariants()
|
|
396
|
+
|
|
397
|
+
expect(variants).toContainEqual({ service: 'Chrome Safe Storage', account: 'Chrome' })
|
|
398
|
+
expect(variants).toContainEqual({ service: 'Chrome Canary Safe Storage', account: 'Chrome Canary' })
|
|
399
|
+
expect(variants).toContainEqual({ service: 'Microsoft Edge Safe Storage', account: 'Microsoft Edge' })
|
|
400
|
+
expect(variants).toContainEqual({ service: 'Arc Safe Storage', account: 'Arc' })
|
|
401
|
+
expect(variants).toContainEqual({ service: 'Brave Safe Storage', account: 'Brave' })
|
|
402
|
+
expect(variants).toContainEqual({ service: 'Vivaldi Safe Storage', account: 'Vivaldi' })
|
|
403
|
+
expect(variants).toContainEqual({ service: 'Chromium Safe Storage', account: 'Chromium' })
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
test('Discord entries come before browser entries', () => {
|
|
407
|
+
const macExtractor = new DiscordTokenExtractor('darwin')
|
|
408
|
+
const variants = macExtractor.getKeychainVariants()
|
|
409
|
+
|
|
410
|
+
const discordIdx = variants.findIndex((v) => v.service === 'discord Safe Storage')
|
|
411
|
+
const chromeIdx = variants.findIndex((v) => v.service === 'Chrome Safe Storage')
|
|
412
|
+
expect(discordIdx).toBeLessThan(chromeIdx)
|
|
241
413
|
})
|
|
242
414
|
})
|
|
243
415
|
|