@nordbyte/nordrelay 0.7.0 → 0.8.1
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/.env.example +35 -0
- package/README.md +118 -49
- package/dist/activity-events.js +2 -2
- package/dist/adapter-conformance.js +61 -0
- package/dist/bot.js +18 -31
- package/dist/channel-adapter.js +33 -6
- package/dist/channel-command-catalog.js +6 -0
- package/dist/channel-command-core.js +60 -0
- package/dist/channel-command-service.js +20 -4
- package/dist/channel-mirror-registry.js +9 -2
- package/dist/channel-prompt-engine.js +177 -0
- package/dist/channel-turn-lifecycle.js +73 -0
- package/dist/config-metadata.js +67 -8
- package/dist/config.js +48 -1
- package/dist/context-key.js +32 -0
- package/dist/discord-bot.js +99 -327
- package/dist/index.js +9 -0
- package/dist/metrics.js +2 -0
- package/dist/peer-client.js +90 -2
- package/dist/peer-readiness.js +77 -0
- package/dist/peer-runtime-service.js +22 -0
- package/dist/peer-server.js +20 -4
- package/dist/peer-store.js +17 -2
- package/dist/relay-runtime-helpers.js +3 -1
- package/dist/relay-runtime.js +7 -0
- package/dist/settings-wizard-test.js +216 -0
- package/dist/slack-artifacts.js +165 -0
- package/dist/slack-bot.js +1461 -0
- package/dist/slack-channel-runtime.js +147 -0
- package/dist/slack-command-surface.js +46 -0
- package/dist/slack-diagnostics.js +116 -0
- package/dist/slack-rate-limit.js +139 -0
- package/dist/user-management-crypto.js +38 -0
- package/dist/user-management-normalize.js +188 -0
- package/dist/user-management-types.js +1 -0
- package/dist/user-management.js +193 -196
- package/dist/web-api-contract.js +8 -0
- package/dist/web-dashboard-access-routes.js +62 -0
- package/dist/web-dashboard-assets.js +1 -0
- package/dist/web-dashboard-pages.js +14 -4
- package/dist/web-dashboard-peer-routes.js +32 -11
- package/dist/web-dashboard.js +34 -0
- package/dist/web-state.js +2 -2
- package/dist/webui-assets/dashboard.css +193 -0
- package/dist/webui-assets/dashboard.js +546 -145
- package/package.json +3 -1
- package/plugins/nordrelay/scripts/nordrelay.mjs +105 -11
package/dist/config-metadata.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export const SECRET_KEYS = new Set([
|
|
2
2
|
"TELEGRAM_BOT_TOKEN",
|
|
3
3
|
"DISCORD_BOT_TOKEN",
|
|
4
|
+
"SLACK_BOT_TOKEN",
|
|
5
|
+
"SLACK_APP_TOKEN",
|
|
6
|
+
"SLACK_SIGNING_SECRET",
|
|
4
7
|
"CODEX_API_KEY",
|
|
5
8
|
"HERMES_API_KEY",
|
|
6
9
|
"OPENCLAW_GATEWAY_TOKEN",
|
|
@@ -24,15 +27,34 @@ const DISCORD_SETTING_HELP = {
|
|
|
24
27
|
DISCORD_QUIET_HOURS: "Use a local-time range like 22-7, off, or blank to inherit the channel-neutral quiet-hours setting.",
|
|
25
28
|
DISCORD_AUTO_SEND_ARTIFACTS: "Overrides automatic artifact upload behavior for Discord only. Leave blank to use NORDRELAY_AUTO_SEND_ARTIFACTS.",
|
|
26
29
|
};
|
|
30
|
+
const SLACK_SETTING_HELP = {
|
|
31
|
+
SLACK_ENABLED: "Create a Slack app, install it into the workspace, then set bot/app tokens before enabling Slack.",
|
|
32
|
+
SLACK_BOT_TOKEN: "Slack app OAuth & Permissions: copy the bot token that starts with xoxb-.",
|
|
33
|
+
SLACK_APP_TOKEN: "Slack app Basic Information: create an app-level token with connections:write. Required for Socket Mode.",
|
|
34
|
+
SLACK_SIGNING_SECRET: "Slack app Basic Information: copy Signing Secret. Required only when Socket Mode is disabled.",
|
|
35
|
+
SLACK_ALLOWED_TEAM_IDS: "Optional workspace allow-list. Copy Team IDs from Slack event payloads or app diagnostics.",
|
|
36
|
+
SLACK_ALLOWED_CHANNEL_IDS: "Optional channel allow-list before NordRelay user/group checks. Copy channel IDs from Slack channel details.",
|
|
37
|
+
SLACK_COMMAND: "Slash command configured in the Slack app. Defaults to /nordrelay.",
|
|
38
|
+
};
|
|
39
|
+
const TELEGRAM_SETTING_HELP = {
|
|
40
|
+
TELEGRAM_ENABLED: "Enable this only after the BotFather token is configured and NordRelay users/chats are allowed through the user management system.",
|
|
41
|
+
TELEGRAM_BOT_TOKEN: "Telegram BotFather: open @BotFather, create a bot with /newbot, then paste only the token value.",
|
|
42
|
+
TELEGRAM_TRANSPORT: "Use polling for the simplest setup. Use webhook only when this NordRelay instance is reachable from Telegram through public HTTPS.",
|
|
43
|
+
TELEGRAM_WEBHOOK_URL: "Public HTTPS base URL for Telegram webhook delivery, for example https://relay.example.com.",
|
|
44
|
+
TELEGRAM_WEBHOOK_HOST: "Local interface where NordRelay binds the webhook listener. Use 127.0.0.1 behind a reverse proxy or 0.0.0.0 only when the endpoint is protected.",
|
|
45
|
+
TELEGRAM_WEBHOOK_PORT: "Local port for the Telegram webhook listener.",
|
|
46
|
+
TELEGRAM_WEBHOOK_PATH: "Webhook request path registered with Telegram. It must start with /.",
|
|
47
|
+
TELEGRAM_WEBHOOK_SECRET: "Optional secret token Telegram sends in X-Telegram-Bot-Api-Secret-Token. Use a random value for webhook mode.",
|
|
48
|
+
};
|
|
27
49
|
export const SETTING_DEFINITIONS = [
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
50
|
+
telegramSetting("TELEGRAM_ENABLED", "Enable Telegram", "boolean", "Start the Telegram bot adapter.", true),
|
|
51
|
+
telegramSetting("TELEGRAM_BOT_TOKEN", "Telegram bot token", "secret", "BotFather token.", true),
|
|
52
|
+
telegramSetting("TELEGRAM_TRANSPORT", "Telegram transport", "string", "polling or webhook.", true, ["polling", "webhook"]),
|
|
53
|
+
telegramSetting("TELEGRAM_WEBHOOK_URL", "Webhook public URL", "string", "Public base URL for webhook mode.", true),
|
|
54
|
+
telegramSetting("TELEGRAM_WEBHOOK_HOST", "Webhook bind host", "string", "Local webhook bind host.", true),
|
|
55
|
+
telegramSetting("TELEGRAM_WEBHOOK_PORT", "Webhook bind port", "number", "Local webhook bind port.", true),
|
|
56
|
+
telegramSetting("TELEGRAM_WEBHOOK_PATH", "Webhook path", "string", "Webhook request path.", true),
|
|
57
|
+
telegramSetting("TELEGRAM_WEBHOOK_SECRET", "Webhook secret", "secret", "Optional Telegram webhook secret token.", true),
|
|
36
58
|
discordSetting("DISCORD_ENABLED", "Enable Discord", "boolean", "Start the Discord bot adapter.", true),
|
|
37
59
|
discordSetting("DISCORD_BOT_TOKEN", "Discord bot token", "secret", "Discord bot token.", true),
|
|
38
60
|
discordSetting("DISCORD_CLIENT_ID", "Discord client ID", "string", "Discord application/client id used for slash command registration.", true),
|
|
@@ -47,6 +69,21 @@ export const SETTING_DEFINITIONS = [
|
|
|
47
69
|
discordSetting("DISCORD_NOTIFY_MODE", "Discord notify override", "string", "Optional Discord override for completion notifications.", false, ["off", "minimal", "all"]),
|
|
48
70
|
discordSetting("DISCORD_QUIET_HOURS", "Discord quiet hours override", "string", "Optional Discord quiet hours override. Use HH-HH, off, or leave blank for default.", false),
|
|
49
71
|
discordSetting("DISCORD_AUTO_SEND_ARTIFACTS", "Discord auto-send artifacts override", "boolean", "Optional Discord override for automatic artifact summaries/uploads.", false),
|
|
72
|
+
slackSetting("SLACK_ENABLED", "Enable Slack", "boolean", "Start the Slack bot adapter.", true),
|
|
73
|
+
slackSetting("SLACK_BOT_TOKEN", "Slack bot token", "secret", "Slack bot token.", true),
|
|
74
|
+
slackSetting("SLACK_APP_TOKEN", "Slack app token", "secret", "Slack app-level token for Socket Mode.", true),
|
|
75
|
+
slackSetting("SLACK_SIGNING_SECRET", "Slack signing secret", "secret", "Slack signing secret for HTTP Events mode.", true),
|
|
76
|
+
slackSetting("SLACK_SOCKET_MODE", "Slack Socket Mode", "boolean", "Use Slack Socket Mode instead of an HTTP events receiver.", true),
|
|
77
|
+
slackSetting("SLACK_PORT", "Slack HTTP port", "number", "HTTP port used when Slack Socket Mode is disabled.", true),
|
|
78
|
+
slackSetting("SLACK_ALLOWED_TEAM_IDS", "Allowed Slack teams", "list", "Optional comma-separated Slack team/workspace allow-list.", true),
|
|
79
|
+
slackSetting("SLACK_ALLOWED_CHANNEL_IDS", "Allowed Slack channels", "list", "Optional comma-separated Slack channel allow-list before user/group checks.", true),
|
|
80
|
+
slackSetting("SLACK_MESSAGE_CONTENT_ENABLED", "Slack message content", "boolean", "Read regular Slack text messages as prompts.", true),
|
|
81
|
+
slackSetting("SLACK_COMMAND", "Slack Slash command", "string", "Slash command configured in Slack.", true),
|
|
82
|
+
slackSetting("SLACK_CLI_MIRROR_MODE", "Slack mirror override", "string", "Optional Slack override for CLI mirror mode. Uses the NordRelay default when unset.", false, ["off", "status", "final", "full"]),
|
|
83
|
+
slackSetting("SLACK_CLI_MIRROR_MIN_UPDATE_MS", "Slack mirror update override", "number", "Optional Slack override for mirrored edit interval.", true),
|
|
84
|
+
slackSetting("SLACK_NOTIFY_MODE", "Slack notify override", "string", "Optional Slack override for completion notifications.", false, ["off", "minimal", "all"]),
|
|
85
|
+
slackSetting("SLACK_QUIET_HOURS", "Slack quiet hours override", "string", "Optional Slack quiet hours override. Use HH-HH, off, or leave blank for default.", false),
|
|
86
|
+
slackSetting("SLACK_AUTO_SEND_ARTIFACTS", "Slack auto-send artifacts override", "boolean", "Optional Slack override for automatic artifact summaries/uploads.", false),
|
|
50
87
|
setting("NORDRELAY_CODEX_ENABLED", "Enable Codex", "Agents", "boolean", "Allow Codex sessions.", true),
|
|
51
88
|
setting("NORDRELAY_PI_ENABLED", "Enable Pi", "Agents", "boolean", "Allow Pi sessions.", true),
|
|
52
89
|
setting("NORDRELAY_HERMES_ENABLED", "Enable Hermes", "Agents", "boolean", "Allow Hermes sessions through the Hermes API Server.", true),
|
|
@@ -166,6 +203,21 @@ const EXAMPLE_VALUES = {
|
|
|
166
203
|
"DISCORD_NOTIFY_MODE": "",
|
|
167
204
|
"DISCORD_QUIET_HOURS": "",
|
|
168
205
|
"DISCORD_AUTO_SEND_ARTIFACTS": "",
|
|
206
|
+
"SLACK_ENABLED": "false",
|
|
207
|
+
"SLACK_BOT_TOKEN": "",
|
|
208
|
+
"SLACK_APP_TOKEN": "",
|
|
209
|
+
"SLACK_SIGNING_SECRET": "",
|
|
210
|
+
"SLACK_SOCKET_MODE": "true",
|
|
211
|
+
"SLACK_PORT": "3000",
|
|
212
|
+
"SLACK_ALLOWED_TEAM_IDS": "",
|
|
213
|
+
"SLACK_ALLOWED_CHANNEL_IDS": "",
|
|
214
|
+
"SLACK_MESSAGE_CONTENT_ENABLED": "true",
|
|
215
|
+
"SLACK_COMMAND": "/nordrelay",
|
|
216
|
+
"SLACK_CLI_MIRROR_MODE": "",
|
|
217
|
+
"SLACK_CLI_MIRROR_MIN_UPDATE_MS": "",
|
|
218
|
+
"SLACK_NOTIFY_MODE": "",
|
|
219
|
+
"SLACK_QUIET_HOURS": "",
|
|
220
|
+
"SLACK_AUTO_SEND_ARTIFACTS": "",
|
|
169
221
|
"NORDRELAY_CODEX_ENABLED": "true",
|
|
170
222
|
"NORDRELAY_PI_ENABLED": "false",
|
|
171
223
|
"NORDRELAY_HERMES_ENABLED": "false",
|
|
@@ -276,6 +328,7 @@ const EXAMPLE_VALUES = {
|
|
|
276
328
|
const GROUP_INTROS = {
|
|
277
329
|
Telegram: "Telegram bot and transport settings.",
|
|
278
330
|
Discord: "Discord bot settings. Discord is opt-in and uses the same NordRelay users, groups, and permissions as Telegram.",
|
|
331
|
+
Slack: "Slack bot settings. Slack is opt-in and uses the same NordRelay users, groups, and permissions as Telegram and Discord.",
|
|
279
332
|
Agents: "Agent access. Codex is enabled by default; Pi, Hermes, OpenClaw, and Claude Code are opt-in.",
|
|
280
333
|
Codex: "Codex defaults for newly created or reattached sessions.",
|
|
281
334
|
Pi: "Pi coding agent defaults.",
|
|
@@ -318,3 +371,9 @@ function setting(key, label, group, kind, description, restartRequired, options,
|
|
|
318
371
|
function discordSetting(key, label, kind, description, restartRequired, options) {
|
|
319
372
|
return setting(key, label, "Discord", kind, description, restartRequired, options, DISCORD_SETTING_HELP[key]);
|
|
320
373
|
}
|
|
374
|
+
function telegramSetting(key, label, kind, description, restartRequired, options) {
|
|
375
|
+
return setting(key, label, "Telegram", kind, description, restartRequired, options, TELEGRAM_SETTING_HELP[key]);
|
|
376
|
+
}
|
|
377
|
+
function slackSetting(key, label, kind, description, restartRequired, options) {
|
|
378
|
+
return setting(key, label, "Slack", kind, description, restartRequired, options, SLACK_SETTING_HELP[key]);
|
|
379
|
+
}
|
package/dist/config.js
CHANGED
|
@@ -39,6 +39,20 @@ export function loadConfig() {
|
|
|
39
39
|
const discordMirrorMinUpdateMs = parseNonNegativeIntegerEnv(optionalString(process.env.DISCORD_CLI_MIRROR_MIN_UPDATE_MS), mirrorMinUpdateMs, "DISCORD_CLI_MIRROR_MIN_UPDATE_MS");
|
|
40
40
|
const discordNotifyMode = parseNotifyMode(optionalString(process.env.DISCORD_NOTIFY_MODE), notifyMode);
|
|
41
41
|
const discordQuietHours = parseQuietHoursOverride(process.env.DISCORD_QUIET_HOURS, quietHours);
|
|
42
|
+
const requestedSlackEnabled = parseBooleanEnv(optionalString(process.env.SLACK_ENABLED), false);
|
|
43
|
+
const slackBotToken = optionalString(process.env.SLACK_BOT_TOKEN);
|
|
44
|
+
const slackAppToken = optionalString(process.env.SLACK_APP_TOKEN);
|
|
45
|
+
const slackSigningSecret = optionalString(process.env.SLACK_SIGNING_SECRET);
|
|
46
|
+
const slackSocketMode = parseBooleanEnv(optionalString(process.env.SLACK_SOCKET_MODE), true);
|
|
47
|
+
const slackPort = parsePositiveIntegerEnv(optionalString(process.env.SLACK_PORT), 3000, "SLACK_PORT");
|
|
48
|
+
const slackAllowedTeamIds = parseOptionalStringList(optionalString(process.env.SLACK_ALLOWED_TEAM_IDS));
|
|
49
|
+
const slackAllowedChannelIds = parseOptionalStringList(optionalString(process.env.SLACK_ALLOWED_CHANNEL_IDS));
|
|
50
|
+
const slackMessageContentEnabled = parseBooleanEnv(optionalString(process.env.SLACK_MESSAGE_CONTENT_ENABLED), true);
|
|
51
|
+
const slackCommand = parseSlackCommand(optionalString(process.env.SLACK_COMMAND));
|
|
52
|
+
const slackMirrorMode = parseMirrorMode(optionalString(process.env.SLACK_CLI_MIRROR_MODE), mirrorMode);
|
|
53
|
+
const slackMirrorMinUpdateMs = parseNonNegativeIntegerEnv(optionalString(process.env.SLACK_CLI_MIRROR_MIN_UPDATE_MS), mirrorMinUpdateMs, "SLACK_CLI_MIRROR_MIN_UPDATE_MS");
|
|
54
|
+
const slackNotifyMode = parseNotifyMode(optionalString(process.env.SLACK_NOTIFY_MODE), notifyMode);
|
|
55
|
+
const slackQuietHours = parseQuietHoursOverride(process.env.SLACK_QUIET_HOURS, quietHours);
|
|
42
56
|
const workspace = resolveWorkspace();
|
|
43
57
|
const workspaceAllowedRoots = parsePathList(optionalString(process.env.WORKSPACE_ALLOWED_ROOTS));
|
|
44
58
|
const workspaceWarnRoots = parsePathList(optionalString(process.env.WORKSPACE_WARN_ROOTS));
|
|
@@ -51,6 +65,7 @@ export function loadConfig() {
|
|
|
51
65
|
const artifactIgnoreGlobs = parseOptionalStringList(optionalString(process.env.ARTIFACT_IGNORE_GLOBS));
|
|
52
66
|
const telegramAutoSendArtifacts = parseBooleanEnv(optionalString(process.env.TELEGRAM_AUTO_SEND_ARTIFACTS), autoSendArtifacts);
|
|
53
67
|
const discordAutoSendArtifacts = parseBooleanEnv(optionalString(process.env.DISCORD_AUTO_SEND_ARTIFACTS), autoSendArtifacts);
|
|
68
|
+
const slackAutoSendArtifacts = parseBooleanEnv(optionalString(process.env.SLACK_AUTO_SEND_ARTIFACTS), autoSendArtifacts);
|
|
54
69
|
const codexEnabled = parseBooleanEnv(optionalString(process.env.NORDRELAY_CODEX_ENABLED), true);
|
|
55
70
|
const codexApiKey = optionalString(process.env.CODEX_API_KEY);
|
|
56
71
|
const codexModel = optionalString(process.env.CODEX_MODEL);
|
|
@@ -130,7 +145,20 @@ export function loadConfig() {
|
|
|
130
145
|
discordEnabled = false;
|
|
131
146
|
adapterWarnings.push("Discord disabled: DISCORD_ENABLED=true requires DISCORD_BOT_TOKEN.");
|
|
132
147
|
}
|
|
133
|
-
|
|
148
|
+
let slackEnabled = requestedSlackEnabled;
|
|
149
|
+
if (slackEnabled && !slackBotToken) {
|
|
150
|
+
slackEnabled = false;
|
|
151
|
+
adapterWarnings.push("Slack disabled: SLACK_ENABLED=true requires SLACK_BOT_TOKEN.");
|
|
152
|
+
}
|
|
153
|
+
if (slackEnabled && slackSocketMode && !slackAppToken) {
|
|
154
|
+
slackEnabled = false;
|
|
155
|
+
adapterWarnings.push("Slack disabled: SLACK_SOCKET_MODE=true requires SLACK_APP_TOKEN.");
|
|
156
|
+
}
|
|
157
|
+
if (slackEnabled && !slackSocketMode && !slackSigningSecret) {
|
|
158
|
+
slackEnabled = false;
|
|
159
|
+
adapterWarnings.push("Slack disabled: SLACK_SOCKET_MODE=false requires SLACK_SIGNING_SECRET.");
|
|
160
|
+
}
|
|
161
|
+
if (!telegramEnabled && !discordEnabled && !slackEnabled) {
|
|
134
162
|
const detail = adapterWarnings.length > 0 ? ` ${adapterWarnings.join(" ")}` : "";
|
|
135
163
|
throw new Error(`At least one usable chat adapter must be enabled.${detail}`);
|
|
136
164
|
}
|
|
@@ -170,6 +198,21 @@ export function loadConfig() {
|
|
|
170
198
|
discordNotifyMode,
|
|
171
199
|
discordQuietHours,
|
|
172
200
|
discordAutoSendArtifacts,
|
|
201
|
+
slackEnabled,
|
|
202
|
+
slackBotToken,
|
|
203
|
+
slackAppToken,
|
|
204
|
+
slackSigningSecret,
|
|
205
|
+
slackSocketMode,
|
|
206
|
+
slackPort,
|
|
207
|
+
slackAllowedTeamIds,
|
|
208
|
+
slackAllowedChannelIds,
|
|
209
|
+
slackMessageContentEnabled,
|
|
210
|
+
slackCommand,
|
|
211
|
+
slackMirrorMode,
|
|
212
|
+
slackMirrorMinUpdateMs,
|
|
213
|
+
slackNotifyMode,
|
|
214
|
+
slackQuietHours,
|
|
215
|
+
slackAutoSendArtifacts,
|
|
173
216
|
workspace,
|
|
174
217
|
workspaceAllowedRoots,
|
|
175
218
|
workspaceWarnRoots,
|
|
@@ -437,6 +480,10 @@ function parseDiscordCommandMode(raw) {
|
|
|
437
480
|
console.warn(`Invalid DISCORD_COMMAND_MODE value: "${raw}". Expected slash, message, or both. Falling back to both.`);
|
|
438
481
|
return "both";
|
|
439
482
|
}
|
|
483
|
+
function parseSlackCommand(raw) {
|
|
484
|
+
const normalized = raw?.trim() || "/nordrelay";
|
|
485
|
+
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
486
|
+
}
|
|
440
487
|
function parseWebhookPath(raw) {
|
|
441
488
|
if (!raw) {
|
|
442
489
|
return "/telegram/webhook";
|
package/dist/context-key.js
CHANGED
|
@@ -77,6 +77,28 @@ export function parseDiscordContextKey(key) {
|
|
|
77
77
|
threadId,
|
|
78
78
|
};
|
|
79
79
|
}
|
|
80
|
+
export function slackContextKey(input) {
|
|
81
|
+
const teamId = input.teamId || "team";
|
|
82
|
+
const thread = input.threadTs && input.threadTs !== input.channelId ? `:${input.threadTs}` : "";
|
|
83
|
+
return `slack:${teamId}:${input.channelId}${thread}`;
|
|
84
|
+
}
|
|
85
|
+
export function isSlackContextKey(key) {
|
|
86
|
+
return /^slack:[^:]+:[^:]+(?::[^:]+)?$/.test(key);
|
|
87
|
+
}
|
|
88
|
+
export function parseSlackContextKey(key) {
|
|
89
|
+
if (!isSlackContextKey(key)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const [, team, channelId, threadTs] = key.split(":");
|
|
93
|
+
if (!channelId) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
teamId: team === "team" ? undefined : team,
|
|
98
|
+
channelId,
|
|
99
|
+
threadTs,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
80
102
|
export function parseChannelContextKey(key) {
|
|
81
103
|
const rawKey = String(key);
|
|
82
104
|
if (isTelegramContextKey(rawKey)) {
|
|
@@ -98,6 +120,16 @@ export function parseChannelContextKey(key) {
|
|
|
98
120
|
guildId: discord.guildId,
|
|
99
121
|
};
|
|
100
122
|
}
|
|
123
|
+
const slack = parseSlackContextKey(rawKey);
|
|
124
|
+
if (slack) {
|
|
125
|
+
return {
|
|
126
|
+
channelId: "slack",
|
|
127
|
+
contextKey: rawKey,
|
|
128
|
+
chatId: slack.channelId,
|
|
129
|
+
topicId: slack.threadTs,
|
|
130
|
+
guildId: slack.teamId,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
101
133
|
if (rawKey.startsWith("web:")) {
|
|
102
134
|
return {
|
|
103
135
|
channelId: "web",
|