agent-messenger 2.25.0 → 2.26.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/README.md +1 -1
- package/dist/package.json +1 -1
- package/dist/src/platforms/discord/commands/auth.d.ts +4 -0
- package/dist/src/platforms/discord/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/auth.js +61 -1
- package/dist/src/platforms/discord/commands/auth.js.map +1 -1
- package/dist/src/platforms/discord/index.d.ts +2 -0
- package/dist/src/platforms/discord/index.d.ts.map +1 -1
- package/dist/src/platforms/discord/index.js +1 -0
- package/dist/src/platforms/discord/index.js.map +1 -1
- package/dist/src/platforms/discord/remote-auth.d.ts +42 -0
- package/dist/src/platforms/discord/remote-auth.d.ts.map +1 -0
- package/dist/src/platforms/discord/remote-auth.js +208 -0
- package/dist/src/platforms/discord/remote-auth.js.map +1 -0
- package/docs/content/docs/sdk/discord.mdx +22 -0
- package/package.json +1 -1
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +9 -2
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +1 -1
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-telegrambot/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-webexbot/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/discord/commands/auth.ts +81 -1
- package/src/platforms/discord/index.ts +2 -0
- package/src/platforms/discord/remote-auth.test.ts +261 -0
- package/src/platforms/discord/remote-auth.ts +317 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-messenger",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "Messaging platform interaction skills for AI agents. Interact with Slack, Discord, Microsoft Teams, Webex, Telegram, Telegram Bot, WhatsApp, LINE, Instagram, KakaoTalk, and Channel Talk - send messages, read channels, manage reactions, upload files, and more through simple CLI interfaces.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "agent-messenger",
|
package/README.md
CHANGED
|
@@ -73,7 +73,7 @@ This installs:
|
|
|
73
73
|
|
|
74
74
|
- `agent-slack` — Slack CLI (user token, zero-config, or QR code sign-in)
|
|
75
75
|
- `agent-slackbot` — Slack Bot CLI (bot token, for server-side/CI/CD)
|
|
76
|
-
- `agent-discord` — Discord CLI
|
|
76
|
+
- `agent-discord` — Discord CLI (token extraction, or QR code sign-in via the mobile app)
|
|
77
77
|
- `agent-discordbot` — Discord Bot CLI (bot token, for server-side/CI/CD)
|
|
78
78
|
- `agent-teams` — Microsoft Teams CLI
|
|
79
79
|
- `agent-webex` — Cisco Webex CLI (browser token extraction with e2e encryption + OAuth Device Grant, zero-config)
|
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-messenger",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "Multi-platform messaging CLI for AI agents (Slack, Discord, Teams, Webex, Telegram, Telegram Bot, WhatsApp, LINE, Instagram, KakaoTalk, Channel Talk)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -4,6 +4,10 @@ export declare function extractAction(options: {
|
|
|
4
4
|
debug?: boolean;
|
|
5
5
|
browserProfile?: string[];
|
|
6
6
|
}): Promise<void>;
|
|
7
|
+
export declare function qrAction(options: {
|
|
8
|
+
pretty?: boolean;
|
|
9
|
+
debug?: boolean;
|
|
10
|
+
}): Promise<void>;
|
|
7
11
|
export declare function logoutAction(options: {
|
|
8
12
|
pretty?: boolean;
|
|
9
13
|
}): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/discord/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../../src/platforms/discord/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAanC,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,cAAc,CAAC,EAAE,MAAM,EAAE,CAAA;CAC1B,GAAG,OAAO,CAAC,IAAI,CAAC,CA0HhB;AAwBD,wBAAsB,QAAQ,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA+C5F;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB/E;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgC/E;AAED,wBAAgB,6BAA6B,IAAI,MAAM,CAEtD;AAED,eAAO,MAAM,WAAW,SAiCrB,CAAA"}
|
|
@@ -2,9 +2,11 @@ import { Command } from 'commander';
|
|
|
2
2
|
import { collectBrowserProfileOption } from '../../../shared/chromium/index.js';
|
|
3
3
|
import { handleError } from '../../../shared/utils/error-handler.js';
|
|
4
4
|
import { formatOutput } from '../../../shared/utils/output.js';
|
|
5
|
-
import {
|
|
5
|
+
import { displayQR } from '../../../shared/utils/qr.js';
|
|
6
|
+
import { debug, info } from '../../../shared/utils/stderr.js';
|
|
6
7
|
import { DiscordClient } from '../client.js';
|
|
7
8
|
import { DiscordCredentialManager } from '../credential-manager.js';
|
|
9
|
+
import { loginWithRemoteAuth } from '../remote-auth.js';
|
|
8
10
|
import { DiscordTokenExtractor } from '../token-extractor.js';
|
|
9
11
|
export async function extractAction(options) {
|
|
10
12
|
try {
|
|
@@ -101,6 +103,59 @@ export async function extractAction(options) {
|
|
|
101
103
|
handleError(error);
|
|
102
104
|
}
|
|
103
105
|
}
|
|
106
|
+
async function saveTokenWithServers(token, credManager) {
|
|
107
|
+
const client = await new DiscordClient().login({ token });
|
|
108
|
+
const authInfo = await client.testAuth();
|
|
109
|
+
const servers = await client.listServers();
|
|
110
|
+
if (servers.length === 0)
|
|
111
|
+
return null;
|
|
112
|
+
const serverMap = {};
|
|
113
|
+
for (const server of servers) {
|
|
114
|
+
serverMap[server.id] = { server_id: server.id, server_name: server.name };
|
|
115
|
+
}
|
|
116
|
+
await credManager.save({ token, current_server: servers[0].id, servers: serverMap });
|
|
117
|
+
return {
|
|
118
|
+
username: authInfo.username,
|
|
119
|
+
servers: servers.map((s) => ({ id: s.id, name: s.name })),
|
|
120
|
+
current: servers[0].id,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
export async function qrAction(options) {
|
|
124
|
+
try {
|
|
125
|
+
const interactive = Boolean(process.stdout.isTTY);
|
|
126
|
+
const debugLog = options.debug ? (msg) => debug(`[debug] ${msg}`) : undefined;
|
|
127
|
+
const session = await loginWithRemoteAuth({
|
|
128
|
+
debug: debugLog,
|
|
129
|
+
onQrUrl: async (url) => {
|
|
130
|
+
await displayQR(url, {
|
|
131
|
+
platform: 'Discord',
|
|
132
|
+
brandColor: '#5865F2',
|
|
133
|
+
scanInstruction: 'Scan with the Discord mobile app (Settings → Scan QR Code)',
|
|
134
|
+
interactive,
|
|
135
|
+
formatOutput,
|
|
136
|
+
pretty: options.pretty,
|
|
137
|
+
});
|
|
138
|
+
},
|
|
139
|
+
onPendingLogin: (user) => {
|
|
140
|
+
if (interactive)
|
|
141
|
+
info(`\nQR scanned by ${user.username}. Confirm the login on your phone...\n`);
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
const credManager = new DiscordCredentialManager();
|
|
145
|
+
const saved = await saveTokenWithServers(session.token, credManager);
|
|
146
|
+
if (!saved) {
|
|
147
|
+
console.log(formatOutput({
|
|
148
|
+
error: 'Logged in, but this account is not a member of any server.',
|
|
149
|
+
hint: 'Join at least one server, then run this command again.',
|
|
150
|
+
}, options.pretty));
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
console.log(formatOutput({ user: saved.username, servers: saved.servers.map((s) => `${s.id}/${s.name}`), current: saved.current }, options.pretty));
|
|
154
|
+
}
|
|
155
|
+
catch (error) {
|
|
156
|
+
handleError(error);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
104
159
|
export async function logoutAction(options) {
|
|
105
160
|
try {
|
|
106
161
|
const credManager = new DiscordCredentialManager();
|
|
@@ -157,6 +212,11 @@ export const authCommand = new Command('auth')
|
|
|
157
212
|
.option('--debug', 'Show debug output for troubleshooting')
|
|
158
213
|
.option('--browser-profile <path>', 'Additional Chromium profile/user-data directory to scan (repeatable, comma-separated supported)', collectBrowserProfileOption, [])
|
|
159
214
|
.action(extractAction))
|
|
215
|
+
.addCommand(new Command('qr')
|
|
216
|
+
.description('Sign in by scanning a QR code with the Discord mobile app')
|
|
217
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
218
|
+
.option('--debug', 'Show debug output for troubleshooting')
|
|
219
|
+
.action(qrAction))
|
|
160
220
|
.addCommand(new Command('logout')
|
|
161
221
|
.description('Logout from Discord')
|
|
162
222
|
.option('--pretty', 'Pretty print JSON output')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../../src/platforms/discord/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAA;
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../../../../src/platforms/discord/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAA;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAC7C,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAEnD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAA;AACpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAE1D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,OAInC;IACC,IAAI,CAAC;QACH,MAAM,SAAS,GAAG,IAAI,qBAAqB,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,OAAO,CAAC,cAAc,CAAC,CAAA;QAE/G,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACf,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAA;YACvD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACf,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAA;YACvE,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAA;YAC3E,OAAO,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAA;YACnD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACf,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;YAC9B,OAAO,CAAC,GAAG,CAAC,iEAAiE,CAAC,CAAA;YAC9E,OAAO,CAAC,GAAG,CAAC,4DAA4D,CAAC,CAAA;YACzE,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAA;YAC/E,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACf,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAA;YACvE,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;QACjB,CAAC;QAED,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAClB,KAAK,CAAC,qCAAqC,CAAC,CAAA;QAC9C,CAAC;QAED,MAAM,SAAS,GAAG,MAAM,SAAS,CAAC,OAAO,EAAE,CAAA;QAE3C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CACT,YAAY,CACV;gBACE,KAAK,EAAE,6BAA6B,EAAE;gBACtC,IAAI,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,iCAAiC;aACpE,EACD,OAAO,CAAC,MAAM,CACf,CACF,CAAA;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QAED,KAAK,MAAM,EAAE,KAAK,EAAE,IAAI,SAAS,EAAE,CAAC;YAClC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;gBAClB,KAAK,CAAC,4BAA4B,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAA;YAChE,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,aAAa,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;gBAEzD,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;oBAClB,KAAK,CAAC,mCAAmC,CAAC,CAAA;gBAC5C,CAAC;gBAED,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAA;gBAExC,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;oBAClB,KAAK,CAAC,mCAAmC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAA;oBAC7D,KAAK,CAAC,gCAAgC,CAAC,CAAA;gBACzC,CAAC;gBAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAA;gBAE1C,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;oBAClB,KAAK,CAAC,mBAAmB,OAAO,CAAC,MAAM,YAAY,CAAC,CAAA;gBACtD,CAAC;gBAED,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACzB,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;wBAClB,KAAK,CAAC,yDAAyD,CAAC,CAAA;oBAClE,CAAC;oBACD,SAAQ;gBACV,CAAC;gBAED,MAAM,WAAW,GAAG,IAAI,wBAAwB,EAAE,CAAA;gBAClD,MAAM,SAAS,GAA+D,EAAE,CAAA;gBAEhF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;oBAC7B,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG;wBACrB,SAAS,EAAE,MAAM,CAAC,EAAE;wBACpB,WAAW,EAAE,MAAM,CAAC,IAAI;qBACzB,CAAA;gBACH,CAAC;gBAED,MAAM,MAAM,GAAG;oBACb,KAAK;oBACL,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;oBAC7B,OAAO,EAAE,SAAS;iBACnB,CAAA;gBAED,MAAM,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBAE9B,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;oBAClB,KAAK,CAAC,6BAA6B,CAAC,CAAA;gBACtC,CAAC;gBAED,MAAM,MAAM,GAAG;oBACb,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;oBAChD,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;iBACvB,CAAA;gBAED,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;gBACjD,OAAM;YACR,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;oBAClB,KAAK,CAAC,oCAAqC,KAAe,CAAC,OAAO,kBAAkB,CAAC,CAAA;gBACvF,CAAC;gBACD,SAAQ;YACV,CAAC;QACH,CAAC;QAED,OAAO,CAAC,GAAG,CACT,YAAY,CACV;YACE,KAAK,EAAE,0EAA0E;YACjF,IAAI,EAAE,sIAAsI;SAC7I,EACD,OAAO,CAAC,MAAM,CACf,CACF,CAAA;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACjB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,KAAc,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAED,KAAK,UAAU,oBAAoB,CACjC,KAAa,EACb,WAAqC;IAErC,MAAM,MAAM,GAAG,MAAM,IAAI,aAAa,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;IACzD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAA;IACxC,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,EAAE,CAAA;IAC1C,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAA;IAErC,MAAM,SAAS,GAA+D,EAAE,CAAA;IAChF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,MAAM,CAAC,IAAI,EAAE,CAAA;IAC3E,CAAC;IAED,MAAM,WAAW,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAA;IACpF,OAAO;QACL,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACzD,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE;KACvB,CAAA;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,OAA8C;IAC3E,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QACjD,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;QAErF,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC;YACxC,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;gBACrB,MAAM,SAAS,CAAC,GAAG,EAAE;oBACnB,QAAQ,EAAE,SAAS;oBACnB,UAAU,EAAE,SAAS;oBACrB,eAAe,EAAE,4DAA4D;oBAC7E,WAAW;oBACX,YAAY;oBACZ,MAAM,EAAE,OAAO,CAAC,MAAM;iBACvB,CAAC,CAAA;YACJ,CAAC;YACD,cAAc,EAAE,CAAC,IAAI,EAAE,EAAE;gBACvB,IAAI,WAAW;oBAAE,IAAI,CAAC,mBAAmB,IAAI,CAAC,QAAQ,wCAAwC,CAAC,CAAA;YACjG,CAAC;SACF,CAAC,CAAA;QAEF,MAAM,WAAW,GAAG,IAAI,wBAAwB,EAAE,CAAA;QAClD,MAAM,KAAK,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,KAAK,EAAE,WAAW,CAAC,CAAA;QAEpE,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,GAAG,CACT,YAAY,CACV;gBACE,KAAK,EAAE,4DAA4D;gBACnE,IAAI,EAAE,wDAAwD;aAC/D,EACD,OAAO,CAAC,MAAM,CACf,CACF,CAAA;YACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QAED,OAAO,CAAC,GAAG,CACT,YAAY,CACV,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,EACxG,OAAO,CAAC,MAAM,CACf,CACF,CAAA;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,KAAc,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAA6B;IAC9D,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,wBAAwB,EAAE,CAAA;QAClD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,CAAA;QAEvC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,8CAA8C,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;YACpG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QAED,MAAM,WAAW,CAAC,UAAU,EAAE,CAAA;QAE9B,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IAClF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,KAAc,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,OAA6B;IAC9D,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,wBAAwB,EAAE,CAAA;QAClD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,CAAA;QAEvC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YAClB,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,8CAA8C,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;YACpG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QAED,IAAI,QAAQ,GAA4C,IAAI,CAAA;QAC5D,IAAI,KAAK,GAAG,KAAK,CAAA;QAEjB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,aAAa,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAA;YACvE,QAAQ,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAA;YAClC,KAAK,GAAG,IAAI,CAAA;QACd,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,GAAG,KAAK,CAAA;QACf,CAAC;QAED,MAAM,MAAM,GAAG;YACb,aAAa,EAAE,KAAK;YACpB,IAAI,EAAE,QAAQ,EAAE,QAAQ;YACxB,cAAc,EAAE,MAAM,CAAC,cAAc;YACrC,aAAa,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM;SAClD,CAAA;QAED,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,KAAc,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,6BAA6B;IAC3C,OAAO,4GAA4G,CAAA;AACrH,CAAC;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC3C,WAAW,CAAC,yBAAyB,CAAC;KACtC,UAAU,CACT,IAAI,OAAO,CAAC,SAAS,CAAC;KACnB,WAAW,CAAC,wEAAwE,CAAC;KACrF,MAAM,CAAC,UAAU,EAAE,0BAA0B,CAAC;KAC9C,MAAM,CAAC,SAAS,EAAE,uCAAuC,CAAC;KAC1D,MAAM,CACL,0BAA0B,EAC1B,iGAAiG,EACjG,2BAA2B,EAC3B,EAAE,CACH;KACA,MAAM,CAAC,aAAa,CAAC,CACzB;KACA,UAAU,CACT,IAAI,OAAO,CAAC,IAAI,CAAC;KACd,WAAW,CAAC,2DAA2D,CAAC;KACxE,MAAM,CAAC,UAAU,EAAE,0BAA0B,CAAC;KAC9C,MAAM,CAAC,SAAS,EAAE,uCAAuC,CAAC;KAC1D,MAAM,CAAC,QAAQ,CAAC,CACpB;KACA,UAAU,CACT,IAAI,OAAO,CAAC,QAAQ,CAAC;KAClB,WAAW,CAAC,qBAAqB,CAAC;KAClC,MAAM,CAAC,UAAU,EAAE,0BAA0B,CAAC;KAC9C,MAAM,CAAC,YAAY,CAAC,CACxB;KACA,UAAU,CACT,IAAI,OAAO,CAAC,QAAQ,CAAC;KAClB,WAAW,CAAC,4BAA4B,CAAC;KACzC,MAAM,CAAC,UAAU,EAAE,0BAA0B,CAAC;KAC9C,MAAM,CAAC,YAAY,CAAC,CACxB,CAAA"}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { DiscordClient, DiscordError } from './client.js';
|
|
2
2
|
export { DiscordCredentialManager } from './credential-manager.js';
|
|
3
3
|
export { DiscordListener } from './listener.js';
|
|
4
|
+
export { loginWithRemoteAuth } from './remote-auth.js';
|
|
5
|
+
export type { RemoteAuthOptions, RemoteAuthSession } from './remote-auth.js';
|
|
4
6
|
export type { DiscordChannel, DiscordConfig, DiscordCredentials, DiscordDMChannel, DiscordFile, DiscordGatewayChannelEvent, DiscordGatewayEvent, DiscordGatewayGenericEvent, DiscordGatewayMemberEvent, DiscordGatewayMessageCreateEvent, DiscordGatewayMessageDeleteEvent, DiscordGatewayMessageUpdateEvent, DiscordGatewayPresenceEvent, DiscordGatewayReactionEvent, DiscordGatewayTypingEvent, DiscordGuild, DiscordGuildMember, DiscordListenerEventMap, DiscordMention, DiscordMessage, DiscordReaction, DiscordRelationship, DiscordSearchOptions, DiscordSearchResponse, DiscordSearchResult, DiscordUser, DiscordUserNote, DiscordUserProfile, } from './types.js';
|
|
5
7
|
export { DiscordChannelSchema, DiscordConfigSchema, DiscordCredentialsSchema, DiscordDMChannelSchema, DiscordFileSchema, DiscordGatewayOpcode, DiscordGuildSchema, DiscordIntent, DiscordMentionSchema, DiscordMessageSchema, DiscordReactionSchema, DiscordRelationshipSchema, DiscordSearchResponseSchema, DiscordSearchResultSchema, DiscordUserSchema, } from './types.js';
|
|
6
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/platforms/discord/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,YAAY,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACX,0BAA0B,EAC1B,mBAAmB,EACnB,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,gCAAgC,EAChC,gCAAgC,EAChC,2BAA2B,EAC3B,2BAA2B,EAC3B,yBAAyB,EACzB,YAAY,EACZ,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,WAAW,EACX,eAAe,EACf,kBAAkB,GACnB,MAAM,SAAS,CAAA;AAChB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,SAAS,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/platforms/discord/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACzE,YAAY,EACV,cAAc,EACd,aAAa,EACb,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACX,0BAA0B,EAC1B,mBAAmB,EACnB,0BAA0B,EAC1B,yBAAyB,EACzB,gCAAgC,EAChC,gCAAgC,EAChC,gCAAgC,EAChC,2BAA2B,EAC3B,2BAA2B,EAC3B,yBAAyB,EACzB,YAAY,EACZ,kBAAkB,EAClB,uBAAuB,EACvB,cAAc,EACd,cAAc,EACd,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,WAAW,EACX,eAAe,EACf,kBAAkB,GACnB,MAAM,SAAS,CAAA;AAChB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,SAAS,CAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { DiscordClient, DiscordError } from './client.js';
|
|
2
2
|
export { DiscordCredentialManager } from './credential-manager.js';
|
|
3
3
|
export { DiscordListener } from './listener.js';
|
|
4
|
+
export { loginWithRemoteAuth } from './remote-auth.js';
|
|
4
5
|
export { DiscordChannelSchema, DiscordConfigSchema, DiscordCredentialsSchema, DiscordDMChannelSchema, DiscordFileSchema, DiscordGatewayOpcode, DiscordGuildSchema, DiscordIntent, DiscordMentionSchema, DiscordMessageSchema, DiscordReactionSchema, DiscordRelationshipSchema, DiscordSearchResponseSchema, DiscordSearchResultSchema, DiscordUserSchema, } from './types.js';
|
|
5
6
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/platforms/discord/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/platforms/discord/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACtD,OAAO,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAA;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AAgCnD,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,wBAAwB,EACxB,sBAAsB,EACtB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,EAClB,aAAa,EACb,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,yBAAyB,EACzB,2BAA2B,EAC3B,yBAAyB,EACzB,iBAAiB,GAClB,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { createPrivateKey, createPublicKey, generateKeyPairSync } from 'node:crypto';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
interface RemoteAuthUser {
|
|
4
|
+
id: string;
|
|
5
|
+
username: string;
|
|
6
|
+
discriminator: string;
|
|
7
|
+
avatar: string | null;
|
|
8
|
+
}
|
|
9
|
+
export interface RemoteAuthSession {
|
|
10
|
+
token: string;
|
|
11
|
+
user: RemoteAuthUser | null;
|
|
12
|
+
}
|
|
13
|
+
export interface RemoteAuthOptions {
|
|
14
|
+
onQrUrl?: (url: string) => void | Promise<void>;
|
|
15
|
+
onPendingLogin?: (user: RemoteAuthUser) => void;
|
|
16
|
+
debug?: (message: string) => void;
|
|
17
|
+
fetchImpl?: typeof fetch;
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
createWebSocket?: (url: string) => WebSocket;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Authenticate a Discord user account via the Remote Auth ("scan QR with the mobile app") flow.
|
|
23
|
+
*
|
|
24
|
+
* The desktop side (this function) opens a WebSocket to Discord's remote-auth gateway, performs an
|
|
25
|
+
* RSA-OAEP key exchange, and renders a QR code. Once the user scans it with an authenticated Discord
|
|
26
|
+
* mobile app and confirms, Discord returns an encrypted ticket which is exchanged for the user token.
|
|
27
|
+
*/
|
|
28
|
+
export declare function loginWithRemoteAuth(options?: RemoteAuthOptions): Promise<RemoteAuthSession>;
|
|
29
|
+
declare function encodePublicKey(publicKey: ReturnType<typeof generateKeyPairSync>['publicKey']): string;
|
|
30
|
+
declare function computeNonceProof(privateKey: ReturnType<typeof generateKeyPairSync>['privateKey'], encryptedNonce: string): string;
|
|
31
|
+
declare function parseUserPayload(payload: string): RemoteAuthUser;
|
|
32
|
+
declare function base64UrlNoPadding(buffer: Buffer): string;
|
|
33
|
+
export declare const __test: {
|
|
34
|
+
encodePublicKey: typeof encodePublicKey;
|
|
35
|
+
computeNonceProof: typeof computeNonceProof;
|
|
36
|
+
parseUserPayload: typeof parseUserPayload;
|
|
37
|
+
base64UrlNoPadding: typeof base64UrlNoPadding;
|
|
38
|
+
createPublicKey: typeof createPublicKey;
|
|
39
|
+
createPrivateKey: typeof createPrivateKey;
|
|
40
|
+
};
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=remote-auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-auth.d.ts","sourceRoot":"","sources":["../../../../src/platforms/discord/remote-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,gBAAgB,EAChB,eAAe,EACf,mBAAmB,EAEpB,MAAM,aAAa,CAAA;AAEpB,OAAO,SAAS,MAAM,IAAI,CAAA;AAW1B,UAAU,cAAc;IACtB,EAAE,EAAE,MAAM,CAAA;IACV,QAAQ,EAAE,MAAM,CAAA;IAChB,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,cAAc,GAAG,IAAI,CAAA;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC/C,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAA;IAC/C,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,SAAS,CAAA;CAC7C;AAsCD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,iBAAsB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAsJ/F;AAED,iBAAS,eAAe,CAAC,SAAS,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,WAAW,CAAC,GAAG,MAAM,CAG/F;AAgBD,iBAAS,iBAAiB,CACxB,UAAU,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,YAAY,CAAC,EAChE,cAAc,EAAE,MAAM,GACrB,MAAM,CAGR;AAED,iBAAS,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAGzD;AAuCD,iBAAS,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,eAAO,MAAM,MAAM;;;;;;;CAOlB,CAAA"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { constants as cryptoConstants, createHash, createPrivateKey, createPublicKey, generateKeyPairSync, privateDecrypt, } from 'node:crypto';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
import { DiscordError } from './client.js';
|
|
4
|
+
import { getDiscordHeaders } from './super-properties.js';
|
|
5
|
+
const REMOTE_AUTH_GATEWAY_URL = 'wss://remote-auth-gateway.discord.gg/?v=2';
|
|
6
|
+
const REMOTE_AUTH_ORIGIN = 'https://discord.com';
|
|
7
|
+
const REMOTE_AUTH_LOGIN_URL = 'https://discord.com/api/v9/users/@me/remote-auth/login';
|
|
8
|
+
const QR_URL_PREFIX = 'https://discord.com/ra/';
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 150_000;
|
|
10
|
+
/**
|
|
11
|
+
* Authenticate a Discord user account via the Remote Auth ("scan QR with the mobile app") flow.
|
|
12
|
+
*
|
|
13
|
+
* The desktop side (this function) opens a WebSocket to Discord's remote-auth gateway, performs an
|
|
14
|
+
* RSA-OAEP key exchange, and renders a QR code. Once the user scans it with an authenticated Discord
|
|
15
|
+
* mobile app and confirms, Discord returns an encrypted ticket which is exchanged for the user token.
|
|
16
|
+
*/
|
|
17
|
+
export function loginWithRemoteAuth(options = {}) {
|
|
18
|
+
const debug = options.debug;
|
|
19
|
+
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
20
|
+
const { publicKey, privateKey } = generateKeyPairSync('rsa', {
|
|
21
|
+
modulusLength: 2048,
|
|
22
|
+
publicExponent: 0x10001,
|
|
23
|
+
});
|
|
24
|
+
const ws = options.createWebSocket
|
|
25
|
+
? options.createWebSocket(REMOTE_AUTH_GATEWAY_URL)
|
|
26
|
+
: new WebSocket(REMOTE_AUTH_GATEWAY_URL, { headers: { Origin: REMOTE_AUTH_ORIGIN } });
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
let settled = false;
|
|
29
|
+
let heartbeatTimer = null;
|
|
30
|
+
let pendingUser = null;
|
|
31
|
+
let exchangeStarted = false;
|
|
32
|
+
const cleanup = () => {
|
|
33
|
+
if (heartbeatTimer) {
|
|
34
|
+
clearInterval(heartbeatTimer);
|
|
35
|
+
heartbeatTimer = null;
|
|
36
|
+
}
|
|
37
|
+
clearTimeout(timeoutTimer);
|
|
38
|
+
try {
|
|
39
|
+
ws.close();
|
|
40
|
+
}
|
|
41
|
+
catch { }
|
|
42
|
+
};
|
|
43
|
+
const fail = (error) => {
|
|
44
|
+
if (settled)
|
|
45
|
+
return;
|
|
46
|
+
settled = true;
|
|
47
|
+
cleanup();
|
|
48
|
+
reject(error);
|
|
49
|
+
};
|
|
50
|
+
const succeed = (session) => {
|
|
51
|
+
if (settled)
|
|
52
|
+
return;
|
|
53
|
+
settled = true;
|
|
54
|
+
cleanup();
|
|
55
|
+
resolve(session);
|
|
56
|
+
};
|
|
57
|
+
const timeoutTimer = setTimeout(() => {
|
|
58
|
+
fail(new DiscordError('Remote auth timed out before the QR code was scanned.', 'remote_auth_timeout'));
|
|
59
|
+
}, timeoutMs);
|
|
60
|
+
timeoutTimer.unref?.();
|
|
61
|
+
const send = (payload) => {
|
|
62
|
+
try {
|
|
63
|
+
ws.send(JSON.stringify(payload));
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
fail(error instanceof Error ? error : new Error(String(error)));
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
ws.on('open', () => {
|
|
70
|
+
debug?.('Connected to remote auth gateway');
|
|
71
|
+
});
|
|
72
|
+
ws.on('message', (raw) => {
|
|
73
|
+
let message;
|
|
74
|
+
try {
|
|
75
|
+
message = JSON.parse(raw.toString());
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
handleMessage(message);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
fail(error instanceof Error ? error : new Error(String(error)));
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
const handleMessage = (message) => {
|
|
88
|
+
switch (message.op) {
|
|
89
|
+
case 'hello': {
|
|
90
|
+
const interval = message.heartbeat_interval;
|
|
91
|
+
heartbeatTimer = setInterval(() => send({ op: 'heartbeat' }), interval);
|
|
92
|
+
heartbeatTimer.unref?.();
|
|
93
|
+
send({ op: 'init', encoded_public_key: encodePublicKey(publicKey) });
|
|
94
|
+
debug?.('Sent init with public key');
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
case 'nonce_proof': {
|
|
98
|
+
const encrypted = message.encrypted_nonce;
|
|
99
|
+
const proof = computeNonceProof(privateKey, encrypted);
|
|
100
|
+
send({ op: 'nonce_proof', proof });
|
|
101
|
+
debug?.('Sent nonce proof');
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
case 'pending_remote_init': {
|
|
105
|
+
const { fingerprint } = message;
|
|
106
|
+
const qrUrl = `${QR_URL_PREFIX}${fingerprint}`;
|
|
107
|
+
debug?.('Received fingerprint; QR ready');
|
|
108
|
+
Promise.resolve(options.onQrUrl?.(qrUrl)).catch((error) => {
|
|
109
|
+
fail(error instanceof Error ? error : new Error(String(error)));
|
|
110
|
+
});
|
|
111
|
+
break;
|
|
112
|
+
}
|
|
113
|
+
case 'pending_ticket': {
|
|
114
|
+
const payload = decryptPayload(privateKey, message.encrypted_user_payload);
|
|
115
|
+
pendingUser = parseUserPayload(payload.toString('utf8'));
|
|
116
|
+
options.onPendingLogin?.(pendingUser);
|
|
117
|
+
debug?.(`QR scanned by ${pendingUser.username}`);
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
case 'pending_login': {
|
|
121
|
+
if (exchangeStarted)
|
|
122
|
+
break;
|
|
123
|
+
exchangeStarted = true;
|
|
124
|
+
const { ticket } = message;
|
|
125
|
+
debug?.('Exchanging ticket for token');
|
|
126
|
+
exchangeTicket(ticket, privateKey, options.fetchImpl ?? fetch)
|
|
127
|
+
.then((token) => succeed({ token, user: pendingUser }))
|
|
128
|
+
.catch(fail);
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
case 'cancel': {
|
|
132
|
+
fail(new DiscordError('Remote auth was cancelled from the mobile device.', 'remote_auth_cancelled'));
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
ws.on('close', (code) => {
|
|
138
|
+
if (settled)
|
|
139
|
+
return;
|
|
140
|
+
// The gateway closes the socket right after pending_login; the in-flight
|
|
141
|
+
// ticket exchange settles the result, so this close must not preempt it.
|
|
142
|
+
if (exchangeStarted)
|
|
143
|
+
return;
|
|
144
|
+
// 4003 is the gateway's timeout close code.
|
|
145
|
+
if (code === 4003) {
|
|
146
|
+
fail(new DiscordError('Remote auth session expired. Generate a new QR code and try again.', 'remote_auth_timeout'));
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
fail(new DiscordError(`Remote auth connection closed before completion (code ${code}).`, 'remote_auth_closed'));
|
|
150
|
+
});
|
|
151
|
+
ws.on('error', (error) => {
|
|
152
|
+
fail(error instanceof Error ? error : new Error(String(error)));
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function encodePublicKey(publicKey) {
|
|
157
|
+
const spki = publicKey.export({ type: 'spki', format: 'der' });
|
|
158
|
+
return spki.toString('base64');
|
|
159
|
+
}
|
|
160
|
+
function decryptPayload(privateKey, encryptedBase64) {
|
|
161
|
+
return privateDecrypt({
|
|
162
|
+
key: privateKey,
|
|
163
|
+
padding: cryptoConstants.RSA_PKCS1_OAEP_PADDING,
|
|
164
|
+
oaepHash: 'sha256',
|
|
165
|
+
}, Buffer.from(encryptedBase64, 'base64'));
|
|
166
|
+
}
|
|
167
|
+
function computeNonceProof(privateKey, encryptedNonce) {
|
|
168
|
+
const nonce = decryptPayload(privateKey, encryptedNonce);
|
|
169
|
+
return base64UrlNoPadding(createHash('sha256').update(nonce).digest());
|
|
170
|
+
}
|
|
171
|
+
function parseUserPayload(payload) {
|
|
172
|
+
const [id = '', discriminator = '0', avatar = '', username = ''] = payload.split(':');
|
|
173
|
+
return { id, username, discriminator, avatar: avatar || null };
|
|
174
|
+
}
|
|
175
|
+
async function exchangeTicket(ticket, privateKey, fetchImpl) {
|
|
176
|
+
const { Authorization: _omit, ...headers } = getDiscordHeaders('');
|
|
177
|
+
const response = await fetchImpl(REMOTE_AUTH_LOGIN_URL, {
|
|
178
|
+
method: 'POST',
|
|
179
|
+
headers: { ...headers, 'Content-Type': 'application/json' },
|
|
180
|
+
body: JSON.stringify({ ticket }),
|
|
181
|
+
});
|
|
182
|
+
if (response.status === 400) {
|
|
183
|
+
const body = (await response.json().catch(() => ({})));
|
|
184
|
+
if (body.captcha_sitekey) {
|
|
185
|
+
throw new DiscordError('Discord requires a captcha to complete this login. Captcha solving is not supported; use `auth extract` instead.', 'remote_auth_captcha');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (!response.ok) {
|
|
189
|
+
throw new DiscordError(`Failed to exchange remote auth ticket (HTTP ${response.status}).`, 'remote_auth_login_failed');
|
|
190
|
+
}
|
|
191
|
+
const body = (await response.json());
|
|
192
|
+
if (!body.encrypted_token) {
|
|
193
|
+
throw new DiscordError('Remote auth response did not include a token.', 'remote_auth_login_failed');
|
|
194
|
+
}
|
|
195
|
+
return decryptPayload(privateKey, body.encrypted_token).toString('utf8');
|
|
196
|
+
}
|
|
197
|
+
function base64UrlNoPadding(buffer) {
|
|
198
|
+
return buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
199
|
+
}
|
|
200
|
+
export const __test = {
|
|
201
|
+
encodePublicKey,
|
|
202
|
+
computeNonceProof,
|
|
203
|
+
parseUserPayload,
|
|
204
|
+
base64UrlNoPadding,
|
|
205
|
+
createPublicKey,
|
|
206
|
+
createPrivateKey,
|
|
207
|
+
};
|
|
208
|
+
//# sourceMappingURL=remote-auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-auth.js","sourceRoot":"","sources":["../../../../src/platforms/discord/remote-auth.ts"],"names":[],"mappings":"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"}
|
|
@@ -206,6 +206,28 @@ await client.archiveThread(threadId, false) // unarchive
|
|
|
206
206
|
// → DiscordChannel
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
+
## QR Code Login
|
|
210
|
+
|
|
211
|
+
Sign in by scanning a QR code with the Discord mobile app — no desktop app or browser token extraction required. `loginWithRemoteAuth` implements Discord's Remote Auth protocol: it opens a WebSocket to Discord's remote-auth gateway, performs an RSA key exchange, and hands you a QR URL to display. Once the user scans it with an authenticated Discord mobile app and confirms, you receive the user token.
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { DiscordClient, loginWithRemoteAuth } from 'agent-messenger/discord'
|
|
215
|
+
|
|
216
|
+
const session = await loginWithRemoteAuth({
|
|
217
|
+
onQrUrl: (url) => {
|
|
218
|
+
// Render this URL as a QR code (e.g. with the `qrcode` package)
|
|
219
|
+
console.log('Scan this with the Discord mobile app:', url)
|
|
220
|
+
},
|
|
221
|
+
onPendingLogin: (user) => {
|
|
222
|
+
console.log(`Scanned by ${user.username}. Confirm on your phone...`)
|
|
223
|
+
},
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
const client = await new DiscordClient().login({ token: session.token })
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
`session` is `{ token, user: RemoteAuthUser | null }`, where `RemoteAuthUser` is `{ id, username, discriminator, avatar }`. `user` is populated from the scan confirmation, but can be `null` if the gateway skips the user payload — if you need the identity, guard for `null` and call `await client.testAuth()` after login. Discord may require an hCaptcha on the final token exchange; when it does, the call rejects with a `DiscordError` of code `remote_auth_captcha` — fall back to `auth extract` in that case. The flow requires a phone with the Discord app, so it cannot run headlessly.
|
|
230
|
+
|
|
209
231
|
## Real-Time Events
|
|
210
232
|
|
|
211
233
|
`DiscordListener` connects to Discord's Gateway WebSocket for instant event streaming.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-messenger",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.26.0",
|
|
4
4
|
"description": "Multi-platform messaging CLI for AI agents (Slack, Discord, Teams, Webex, Telegram, Telegram Bot, WhatsApp, LINE, Instagram, KakaoTalk, Channel Talk)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-channeltalk
|
|
3
3
|
description: Interact with Channel Talk using extracted desktop app or browser credentials - read chats, send messages, search messages, manage groups
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.26.0
|
|
5
5
|
allowed-tools: Bash(agent-channeltalk:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-discord
|
|
3
3
|
description: Interact with Discord servers - send messages, read channels, manage reactions
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.26.0
|
|
5
5
|
allowed-tools: Bash(agent-discord:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -37,7 +37,10 @@ Credentials are extracted automatically from the Discord desktop app (or Chromiu
|
|
|
37
37
|
|
|
38
38
|
On macOS, the system may prompt for your Keychain password the first time (required to decrypt Discord's stored token). This is a one-time prompt.
|
|
39
39
|
|
|
40
|
-
**
|
|
40
|
+
**Obtaining tokens** — two options:
|
|
41
|
+
|
|
42
|
+
- **`agent-discord auth extract`** (default): extracts from the Discord desktop app first, falling back to Chromium browsers if the app isn't installed. Best for automated/headless use.
|
|
43
|
+
- **`agent-discord auth qr`**: signs in by scanning a QR code with the Discord mobile app (Settings → Scan QR Code). Use this when there is no desktop app or browser session to extract from. Requires a phone, so it cannot run headlessly.
|
|
41
44
|
|
|
42
45
|
### Multi-Server Support
|
|
43
46
|
|
|
@@ -142,6 +145,10 @@ agent-discord auth extract --browser-profile "$HOME/work-profile,$HOME/personal-
|
|
|
142
145
|
|
|
143
146
|
# --browser-profile accepts repeatable or comma-separated Chromium profile/user-data dirs
|
|
144
147
|
|
|
148
|
+
# Sign in by scanning a QR code with the Discord mobile app (Settings -> Scan QR Code)
|
|
149
|
+
agent-discord auth qr
|
|
150
|
+
agent-discord auth qr --debug
|
|
151
|
+
|
|
145
152
|
# Check auth status
|
|
146
153
|
agent-discord auth status
|
|
147
154
|
|