@automagik/omni 2.260804.3 → 2.260830.2
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/db/drizzle/0048_message_threads.sql +45 -0
- package/db/drizzle/0049_scheduled_messages.sql +73 -0
- package/db/drizzle/0050_slack_user_token.sql +30 -0
- package/db/drizzle/0051_message_pin_star.sql +27 -0
- package/db/drizzle/meta/_journal.json +28 -0
- package/dist/commands/_setup-helpers.d.ts +50 -0
- package/dist/commands/_setup-helpers.d.ts.map +1 -0
- package/dist/commands/connect.d.ts.map +1 -1
- package/dist/commands/events.d.ts.map +1 -1
- package/dist/commands/instances.d.ts +3 -0
- package/dist/commands/instances.d.ts.map +1 -1
- package/dist/commands/schedule.d.ts +24 -0
- package/dist/commands/schedule.d.ts.map +1 -0
- package/dist/commands/setup.d.ts +21 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/slack.d.ts +12 -0
- package/dist/commands/slack.d.ts.map +1 -0
- package/dist/index.js +531 -23
- package/dist/sdk/client.d.ts +68 -0
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/index.d.ts +1 -1
- package/dist/sdk/index.d.ts.map +1 -1
- package/dist/sdk/index.js +87 -0
- package/dist/server/index.js +1817 -230
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -24040,6 +24040,8 @@ var init_instance = __esm(() => {
|
|
|
24040
24040
|
sessionIdPrefix: exports_external.string().max(50).nullable(),
|
|
24041
24041
|
discordBotToken: exports_external.string().nullable(),
|
|
24042
24042
|
slackBotToken: exports_external.string().nullable(),
|
|
24043
|
+
slackUserToken: exports_external.string().nullable(),
|
|
24044
|
+
slackAuthMode: exports_external.string().nullable(),
|
|
24043
24045
|
slackAppToken: exports_external.string().nullable(),
|
|
24044
24046
|
slackSigningSecret: exports_external.string().nullable(),
|
|
24045
24047
|
telegramBotToken: exports_external.string().nullable(),
|
|
@@ -138240,11 +138242,11 @@ var require_applyToDefaults = __commonJS((exports, module) => {
|
|
|
138240
138242
|
return Merge(copy, source, { nullOverride, mergeArrays: false });
|
|
138241
138243
|
};
|
|
138242
138244
|
internals.reachCopy = function(dst, src, path) {
|
|
138243
|
-
for (const
|
|
138244
|
-
if (!(
|
|
138245
|
+
for (const segment2 of path) {
|
|
138246
|
+
if (!(segment2 in src)) {
|
|
138245
138247
|
return;
|
|
138246
138248
|
}
|
|
138247
|
-
const val = src[
|
|
138249
|
+
const val = src[segment2];
|
|
138248
138250
|
if (typeof val !== "object" || val === null) {
|
|
138249
138251
|
return;
|
|
138250
138252
|
}
|
|
@@ -138253,11 +138255,11 @@ var require_applyToDefaults = __commonJS((exports, module) => {
|
|
|
138253
138255
|
const value = src;
|
|
138254
138256
|
let ref = dst;
|
|
138255
138257
|
for (let i = 0;i < path.length - 1; ++i) {
|
|
138256
|
-
const
|
|
138257
|
-
if (typeof ref[
|
|
138258
|
-
ref[
|
|
138258
|
+
const segment2 = path[i];
|
|
138259
|
+
if (typeof ref[segment2] !== "object") {
|
|
138260
|
+
ref[segment2] = {};
|
|
138259
138261
|
}
|
|
138260
|
-
ref = ref[
|
|
138262
|
+
ref = ref[segment2];
|
|
138261
138263
|
}
|
|
138262
138264
|
ref[path[path.length - 1]] = value;
|
|
138263
138265
|
};
|
|
@@ -156556,11 +156558,11 @@ var require_applyToDefaults2 = __commonJS((exports, module) => {
|
|
|
156556
156558
|
return Merge(copy, source, { nullOverride, mergeArrays: false });
|
|
156557
156559
|
};
|
|
156558
156560
|
internals.reachCopy = function(dst, src, path) {
|
|
156559
|
-
for (const
|
|
156560
|
-
if (!(
|
|
156561
|
+
for (const segment2 of path) {
|
|
156562
|
+
if (!(segment2 in src)) {
|
|
156561
156563
|
return;
|
|
156562
156564
|
}
|
|
156563
|
-
const val = src[
|
|
156565
|
+
const val = src[segment2];
|
|
156564
156566
|
if (typeof val !== "object" || val === null) {
|
|
156565
156567
|
return;
|
|
156566
156568
|
}
|
|
@@ -156569,11 +156571,11 @@ var require_applyToDefaults2 = __commonJS((exports, module) => {
|
|
|
156569
156571
|
const value = src;
|
|
156570
156572
|
let ref = dst;
|
|
156571
156573
|
for (let i = 0;i < path.length - 1; ++i) {
|
|
156572
|
-
const
|
|
156573
|
-
if (typeof ref[
|
|
156574
|
-
ref[
|
|
156574
|
+
const segment2 = path[i];
|
|
156575
|
+
if (typeof ref[segment2] !== "object") {
|
|
156576
|
+
ref[segment2] = {};
|
|
156575
156577
|
}
|
|
156576
|
-
ref = ref[
|
|
156578
|
+
ref = ref[segment2];
|
|
156577
156579
|
}
|
|
156578
156580
|
ref[path[path.length - 1]] = value;
|
|
156579
156581
|
};
|
|
@@ -164339,6 +164341,9 @@ __export(exports_schema, {
|
|
|
164339
164341
|
settingValueTypes: () => settingValueTypes,
|
|
164340
164342
|
settingChangeHistoryRelations: () => settingChangeHistoryRelations,
|
|
164341
164343
|
settingChangeHistory: () => settingChangeHistory,
|
|
164344
|
+
scheduledMessages: () => scheduledMessages,
|
|
164345
|
+
scheduledMessageStatuses: () => scheduledMessageStatuses,
|
|
164346
|
+
scheduledMessageDeliveryModes: () => scheduledMessageDeliveryModes,
|
|
164342
164347
|
ruleTypes: () => ruleTypes,
|
|
164343
164348
|
replyFilterMode: () => replyFilterMode,
|
|
164344
164349
|
providerSchemas: () => providerSchemas,
|
|
@@ -164439,7 +164444,7 @@ __export(exports_schema, {
|
|
|
164439
164444
|
accessRules: () => accessRules,
|
|
164440
164445
|
accessModes: () => accessModes
|
|
164441
164446
|
});
|
|
164442
|
-
var channelTypes, agentTypes, agentSystems, agentEntityTypes, debounceMode, splitDelayMode, supersedeMode, replyFilterMode, agentSessionStrategies, ruleTypes, accessModes, settingValueTypes, apiKeyStatuses, apiKeyProfiles, eventTypes, contentTypes, chatTypes, messageSources, messageTypes, messageStatuses, deliveryStatuses, jobStatuses, providerSchemas, agentProviders, agents, agentRoutes, agentSessions, apiKeys, apiKeyAuditLogs, apiKeysRelations, apiKeyAuditLogsRelations, instances, whatsappTemplates, whatsappFlowKeys, persons, platformIdentities, conversations, chats, chatParticipants, omniGroups, messages2, omniEvents, handoffLogs, closeContactOutcomes, closeContactLogs, accessRules, globalSettings, settingChangeHistory, batchJobs, syncJobTypes, syncJobs, mediaContent, chatIdMappings, pluginStorage, agentProvidersRelations, agentsRelations, instancesRelations, syncJobsRelations, personsRelations, platformIdentitiesRelations, conversationsRelations, chatsRelations, chatParticipantsRelations, messagesRelations, omniEventsRelations, accessRulesRelations, globalSettingsRelations, settingChangeHistoryRelations, batchJobsRelations, mediaContentRelations, chatIdMappingsRelations, deadLetterStatuses, deadLetterEvents, payloadStorageConfig, payloadStages, eventPayloads, webhookSources, conditionOperators, actionTypes, automationDebounceModes, automations2, automationLogStatuses, automationLogs, consumerOffsets, automationsRelations, automationLogsRelations, triggerLogs, triggerLogsRelations, agentRoutesRelations, agentTaskStatuses, agentTasks, agentTasksRelations, turnStatuses, turnActions, turns, turnsRelations, followUpDisarmReasons, chatFollowUpState, chatFollowUpStateRelations, processedEvents, genieHosts, tenantStatuses, principalTypes, principalStatuses, tenantRoles, membershipStatuses, credentialClasses, authCredentialStatuses, platformApiKeyStatuses, tenants, principals, tenantMemberships, tenantRolePolicies, platformApiKeys, tenantKeyLineage, authCredentials, tenantAuditLogs, platformAuditLogs, platformProviderCatalog, tenantProviderConfig, platformSettings, tenantSettings, platformSettingChangeHistory, tenantSettingChangeHistory, platformPluginStorage, tenantPluginStorage, platformPayloadStorageConfig, tenantPayloadStorageOverrides, tenantMigrationLedger, tenantMigrationLedgerHistory;
|
|
164447
|
+
var channelTypes, agentTypes, agentSystems, agentEntityTypes, debounceMode, splitDelayMode, supersedeMode, replyFilterMode, agentSessionStrategies, ruleTypes, accessModes, settingValueTypes, apiKeyStatuses, apiKeyProfiles, eventTypes, contentTypes, chatTypes, messageSources, messageTypes, messageStatuses, deliveryStatuses, scheduledMessageDeliveryModes, scheduledMessageStatuses, jobStatuses, providerSchemas, agentProviders, agents, agentRoutes, agentSessions, apiKeys, apiKeyAuditLogs, apiKeysRelations, apiKeyAuditLogsRelations, instances, whatsappTemplates, whatsappFlowKeys, scheduledMessages, persons, platformIdentities, conversations, chats, chatParticipants, omniGroups, messages2, omniEvents, handoffLogs, closeContactOutcomes, closeContactLogs, accessRules, globalSettings, settingChangeHistory, batchJobs, syncJobTypes, syncJobs, mediaContent, chatIdMappings, pluginStorage, agentProvidersRelations, agentsRelations, instancesRelations, syncJobsRelations, personsRelations, platformIdentitiesRelations, conversationsRelations, chatsRelations, chatParticipantsRelations, messagesRelations, omniEventsRelations, accessRulesRelations, globalSettingsRelations, settingChangeHistoryRelations, batchJobsRelations, mediaContentRelations, chatIdMappingsRelations, deadLetterStatuses, deadLetterEvents, payloadStorageConfig, payloadStages, eventPayloads, webhookSources, conditionOperators, actionTypes, automationDebounceModes, automations2, automationLogStatuses, automationLogs, consumerOffsets, automationsRelations, automationLogsRelations, triggerLogs, triggerLogsRelations, agentRoutesRelations, agentTaskStatuses, agentTasks, agentTasksRelations, turnStatuses, turnActions, turns, turnsRelations, followUpDisarmReasons, chatFollowUpState, chatFollowUpStateRelations, processedEvents, genieHosts, tenantStatuses, principalTypes, principalStatuses, tenantRoles, membershipStatuses, credentialClasses, authCredentialStatuses, platformApiKeyStatuses, tenants, principals, tenantMemberships, tenantRolePolicies, platformApiKeys, tenantKeyLineage, authCredentials, tenantAuditLogs, platformAuditLogs, platformProviderCatalog, tenantProviderConfig, platformSettings, tenantSettings, platformSettingChangeHistory, tenantSettingChangeHistory, platformPluginStorage, tenantPluginStorage, platformPayloadStorageConfig, tenantPayloadStorageOverrides, tenantMigrationLedger, tenantMigrationLedgerHistory;
|
|
164443
164448
|
var init_schema2 = __esm(() => {
|
|
164444
164449
|
init_events();
|
|
164445
164450
|
init_types5();
|
|
@@ -164514,6 +164519,8 @@ var init_schema2 = __esm(() => {
|
|
|
164514
164519
|
];
|
|
164515
164520
|
messageStatuses = ["active", "edited", "deleted", "expired"];
|
|
164516
164521
|
deliveryStatuses = ["pending", "sent", "delivered", "read", "failed"];
|
|
164522
|
+
scheduledMessageDeliveryModes = ["platform", "local"];
|
|
164523
|
+
scheduledMessageStatuses = ["pending", "sending", "sent", "canceled", "failed"];
|
|
164517
164524
|
jobStatuses = ["pending", "running", "completed", "failed", "cancelled"];
|
|
164518
164525
|
providerSchemas = [
|
|
164519
164526
|
"agno",
|
|
@@ -164716,6 +164723,8 @@ var init_schema2 = __esm(() => {
|
|
|
164716
164723
|
guildConfigOverrides: jsonb("guild_config_overrides").$type(),
|
|
164717
164724
|
discordPresence: jsonb("discord_presence").$type(),
|
|
164718
164725
|
slackBotToken: text("slack_bot_token"),
|
|
164726
|
+
slackUserToken: text("slack_user_token"),
|
|
164727
|
+
slackAuthMode: varchar("slack_auth_mode", { length: 10 }),
|
|
164719
164728
|
slackAppToken: text("slack_app_token"),
|
|
164720
164729
|
slackSigningSecret: text("slack_signing_secret"),
|
|
164721
164730
|
telegramBotToken: text("telegram_bot_token"),
|
|
@@ -164860,6 +164869,31 @@ var init_schema2 = __esm(() => {
|
|
|
164860
164869
|
}, (t) => ({
|
|
164861
164870
|
instanceUnique: uniqueIndex("idx_wa_flow_keys_instance").on(t.instanceId)
|
|
164862
164871
|
}));
|
|
164872
|
+
scheduledMessages = pgTable("scheduled_messages", {
|
|
164873
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
164874
|
+
instanceId: uuid("instance_id").notNull().references(() => instances.id, { onDelete: "cascade" }),
|
|
164875
|
+
chatExternalId: varchar("chat_external_id", { length: 255 }).notNull(),
|
|
164876
|
+
threadExternalId: varchar("thread_external_id", { length: 255 }),
|
|
164877
|
+
isThreadBroadcast: boolean("is_thread_broadcast").notNull().default(false),
|
|
164878
|
+
content: jsonb("content").$type().notNull(),
|
|
164879
|
+
sendAt: timestamp("send_at", { withTimezone: true }).notNull(),
|
|
164880
|
+
deliveryMode: varchar("delivery_mode", { length: 20 }).notNull().$type(),
|
|
164881
|
+
status: varchar("status", { length: 20 }).notNull().default("pending").$type(),
|
|
164882
|
+
externalScheduledId: varchar("external_scheduled_id", { length: 255 }),
|
|
164883
|
+
sentExternalId: varchar("sent_external_id", { length: 255 }),
|
|
164884
|
+
sentAt: timestamp("sent_at", { withTimezone: true }),
|
|
164885
|
+
canceledAt: timestamp("canceled_at", { withTimezone: true }),
|
|
164886
|
+
failedAt: timestamp("failed_at", { withTimezone: true }),
|
|
164887
|
+
lastError: text("last_error"),
|
|
164888
|
+
attemptCount: integer("attempt_count").notNull().default(0),
|
|
164889
|
+
createdByAgentId: uuid("created_by_agent_id"),
|
|
164890
|
+
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
164891
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
164892
|
+
}, (t) => ({
|
|
164893
|
+
dueIdx: index("scheduled_messages_due_idx").on(t.status, t.sendAt),
|
|
164894
|
+
instanceIdx: index("scheduled_messages_instance_idx").on(t.instanceId, t.status),
|
|
164895
|
+
chatIdx: index("scheduled_messages_chat_idx").on(t.instanceId, t.chatExternalId)
|
|
164896
|
+
}));
|
|
164863
164897
|
persons = pgTable("persons", {
|
|
164864
164898
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
164865
164899
|
displayName: varchar("display_name", { length: 255 }),
|
|
@@ -165044,6 +165078,15 @@ var init_schema2 = __esm(() => {
|
|
|
165044
165078
|
replyToExternalId: varchar("reply_to_external_id", { length: 255 }),
|
|
165045
165079
|
quotedText: text("quoted_text"),
|
|
165046
165080
|
quotedSenderName: varchar("quoted_sender_name", { length: 255 }),
|
|
165081
|
+
threadExternalId: varchar("thread_external_id", { length: 255 }),
|
|
165082
|
+
threadRootMessageId: uuid("thread_root_message_id"),
|
|
165083
|
+
isThreadBroadcast: boolean("is_thread_broadcast").notNull().default(false),
|
|
165084
|
+
replyCount: integer("reply_count").notNull().default(0),
|
|
165085
|
+
latestReplyAt: timestamp("latest_reply_at", { withTimezone: true }),
|
|
165086
|
+
permalink: text("permalink"),
|
|
165087
|
+
pinnedAt: timestamp("pinned_at", { withTimezone: true }),
|
|
165088
|
+
pinnedBy: varchar("pinned_by", { length: 255 }),
|
|
165089
|
+
starredAt: timestamp("starred_at", { withTimezone: true }),
|
|
165047
165090
|
forwardedFromMessageId: uuid("forwarded_from_message_id"),
|
|
165048
165091
|
forwardedFromExternalId: varchar("forwarded_from_external_id", { length: 255 }),
|
|
165049
165092
|
forwardCount: integer("forward_count").notNull().default(0),
|
|
@@ -165081,6 +165124,8 @@ var init_schema2 = __esm(() => {
|
|
|
165081
165124
|
platformTimestampIdx: index("messages_platform_timestamp_idx").on(table3.platformTimestamp),
|
|
165082
165125
|
replyToIdx: index("messages_reply_to_idx").on(table3.replyToMessageId),
|
|
165083
165126
|
replyToExternalIdx: index("messages_reply_to_external_idx").on(table3.chatId, table3.replyToExternalId, table3.isFromMe),
|
|
165127
|
+
threadExternalIdx: index("messages_thread_external_idx").on(table3.chatId, table3.threadExternalId, table3.platformTimestamp),
|
|
165128
|
+
threadRootIdx: index("messages_thread_root_idx").on(table3.threadRootMessageId),
|
|
165084
165129
|
hasMediaIdx: index("messages_has_media_idx").on(table3.hasMedia),
|
|
165085
165130
|
originalEventIdx: index("messages_original_event_idx").on(table3.originalEventId)
|
|
165086
165131
|
}));
|
|
@@ -169982,6 +170027,9 @@ __export(exports_src3, {
|
|
|
169982
170027
|
settingChangeHistoryRelations: () => settingChangeHistoryRelations,
|
|
169983
170028
|
settingChangeHistory: () => settingChangeHistory,
|
|
169984
170029
|
scrubDdlCredential: () => scrubDdlCredential,
|
|
170030
|
+
scheduledMessages: () => scheduledMessages,
|
|
170031
|
+
scheduledMessageStatuses: () => scheduledMessageStatuses,
|
|
170032
|
+
scheduledMessageDeliveryModes: () => scheduledMessageDeliveryModes,
|
|
169985
170033
|
ruleTypes: () => ruleTypes,
|
|
169986
170034
|
roleAttributeViolations: () => roleAttributeViolations,
|
|
169987
170035
|
revertTenantRlsEnforcement: () => revertTenantRlsEnforcement,
|
|
@@ -171390,6 +171438,7 @@ var init_plugin = __esm(() => {
|
|
|
171390
171438
|
type: content.type,
|
|
171391
171439
|
text: content.text ?? content.caption,
|
|
171392
171440
|
mediaUrl: content.mediaUrl,
|
|
171441
|
+
mediaId: content.mediaId,
|
|
171393
171442
|
mimeType: content.mimeType,
|
|
171394
171443
|
isVoiceNote: content.isVoiceNote
|
|
171395
171444
|
},
|
|
@@ -242836,7 +242885,7 @@ var init_sentry_scrub = __esm(() => {
|
|
|
242836
242885
|
var require_package7 = __commonJS((exports, module) => {
|
|
242837
242886
|
module.exports = {
|
|
242838
242887
|
name: "@omni/api",
|
|
242839
|
-
version: "2.
|
|
242888
|
+
version: "2.260830.2",
|
|
242840
242889
|
type: "module",
|
|
242841
242890
|
exports: {
|
|
242842
242891
|
".": {
|
|
@@ -278805,7 +278854,7 @@ var require_channelz = __commonJS((exports) => {
|
|
|
278805
278854
|
return (0, net_1.isIPv6)(ipAddress) && ipAddress.toLowerCase().startsWith("::ffff:") && (0, net_1.isIPv4)(ipAddress.substring(7));
|
|
278806
278855
|
}
|
|
278807
278856
|
function ipv4AddressStringToBuffer(ipAddress) {
|
|
278808
|
-
return Buffer.from(Uint8Array.from(ipAddress.split(".").map((
|
|
278857
|
+
return Buffer.from(Uint8Array.from(ipAddress.split(".").map((segment2) => Number.parseInt(segment2))));
|
|
278809
278858
|
}
|
|
278810
278859
|
function ipAddressStringToBuffer(ipAddress) {
|
|
278811
278860
|
if ((0, net_1.isIPv4)(ipAddress)) {
|
|
@@ -289942,21 +289991,21 @@ var splitPath2 = (path2) => {
|
|
|
289942
289991
|
const segments = path2.split("/");
|
|
289943
289992
|
const results = [];
|
|
289944
289993
|
let basePath = "";
|
|
289945
|
-
segments.forEach((
|
|
289946
|
-
if (
|
|
289947
|
-
basePath += "/" +
|
|
289948
|
-
} else if (/\:/.test(
|
|
289949
|
-
if (/\?/.test(
|
|
289994
|
+
segments.forEach((segment2) => {
|
|
289995
|
+
if (segment2 !== "" && !/\:/.test(segment2)) {
|
|
289996
|
+
basePath += "/" + segment2;
|
|
289997
|
+
} else if (/\:/.test(segment2)) {
|
|
289998
|
+
if (/\?/.test(segment2)) {
|
|
289950
289999
|
if (results.length === 0 && basePath === "") {
|
|
289951
290000
|
results.push("/");
|
|
289952
290001
|
} else {
|
|
289953
290002
|
results.push(basePath);
|
|
289954
290003
|
}
|
|
289955
|
-
const optionalSegment =
|
|
290004
|
+
const optionalSegment = segment2.replace("?", "");
|
|
289956
290005
|
basePath += "/" + optionalSegment;
|
|
289957
290006
|
results.push(basePath);
|
|
289958
290007
|
} else {
|
|
289959
|
-
basePath += "/" +
|
|
290008
|
+
basePath += "/" + segment2;
|
|
289960
290009
|
}
|
|
289961
290010
|
}
|
|
289962
290011
|
});
|
|
@@ -291488,11 +291537,11 @@ var defaultJoin = (...paths) => {
|
|
|
291488
291537
|
result = result.replace(/(?<=\/)\/+/g, "");
|
|
291489
291538
|
const segments = result.split("/");
|
|
291490
291539
|
const resolved = [];
|
|
291491
|
-
for (const
|
|
291492
|
-
if (
|
|
291540
|
+
for (const segment2 of segments) {
|
|
291541
|
+
if (segment2 === ".." && resolved.length > 0 && resolved.at(-1) !== "..") {
|
|
291493
291542
|
resolved.pop();
|
|
291494
|
-
} else if (
|
|
291495
|
-
resolved.push(
|
|
291543
|
+
} else if (segment2 !== ".") {
|
|
291544
|
+
resolved.push(segment2);
|
|
291496
291545
|
}
|
|
291497
291546
|
}
|
|
291498
291547
|
return resolved.join("/") || ".";
|
|
@@ -293281,9 +293330,13 @@ var init_profiles = __esm(() => {
|
|
|
293281
293330
|
});
|
|
293282
293331
|
|
|
293283
293332
|
// ../api/src/middleware/output-redactor.ts
|
|
293333
|
+
import { createHash as createHash12 } from "crypto";
|
|
293284
293334
|
function escapeRegex(literal) {
|
|
293285
293335
|
return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
293286
293336
|
}
|
|
293337
|
+
function hashPattern(source) {
|
|
293338
|
+
return createHash12("sha256").update(source).digest("hex").slice(0, 12);
|
|
293339
|
+
}
|
|
293287
293340
|
function compilePatterns(literals) {
|
|
293288
293341
|
const out = [];
|
|
293289
293342
|
for (const literal of literals) {
|
|
@@ -293453,7 +293506,7 @@ async function emitRedactionEvents(c, apiKey, presetKey, hits) {
|
|
|
293453
293506
|
keyId: apiKey.id,
|
|
293454
293507
|
profile: apiKey.profile ?? null,
|
|
293455
293508
|
presetKey,
|
|
293456
|
-
|
|
293509
|
+
patternHash: hashPattern(hit.pattern),
|
|
293457
293510
|
field: hit.field,
|
|
293458
293511
|
count: hit.count,
|
|
293459
293512
|
route: c.req.path,
|
|
@@ -293462,7 +293515,7 @@ async function emitRedactionEvents(c, apiKey, presetKey, hits) {
|
|
|
293462
293515
|
} catch (err2) {
|
|
293463
293516
|
log60.warn("output-redactor: failed to publish secret.redacted", {
|
|
293464
293517
|
keyId: apiKey.id,
|
|
293465
|
-
|
|
293518
|
+
patternHash: hashPattern(hit.pattern),
|
|
293466
293519
|
error: String(err2)
|
|
293467
293520
|
});
|
|
293468
293521
|
}
|
|
@@ -293653,6 +293706,9 @@ var init_scopes = __esm(() => {
|
|
|
293653
293706
|
"GET /instances/:id/status": "instances:read",
|
|
293654
293707
|
"GET /instances/:id/qr": "instances:read",
|
|
293655
293708
|
"POST /instances/:id/pair": "instances:write",
|
|
293709
|
+
"GET /instances/:id/passkey": "instances:read",
|
|
293710
|
+
"POST /instances/:id/passkey/response": "instances:write",
|
|
293711
|
+
"POST /instances/:id/passkey/confirm": "instances:write",
|
|
293656
293712
|
"POST /instances/:id/connect": "instances:write",
|
|
293657
293713
|
"POST /instances/:id/disconnect": "instances:write",
|
|
293658
293714
|
"POST /instances/:id/restart": "instances:write",
|
|
@@ -294377,17 +294433,18 @@ var init_route_ownership = __esm(() => {
|
|
|
294377
294433
|
"DELETE /api/v2/follow-up/chats/:id",
|
|
294378
294434
|
"DELETE /api/v2/follow-up/instances/:id",
|
|
294379
294435
|
"DELETE /api/v2/instances/:id",
|
|
294380
|
-
"DELETE /api/v2/instances/:id/whatsapp-flows/:flowId",
|
|
294381
294436
|
"DELETE /api/v2/instances/:id/block",
|
|
294382
294437
|
"DELETE /api/v2/instances/:id/guilds/:guildId/config",
|
|
294383
294438
|
"DELETE /api/v2/instances/:id/profile/picture",
|
|
294384
294439
|
"DELETE /api/v2/instances/:id/whatsapp-business/connection",
|
|
294440
|
+
"DELETE /api/v2/instances/:id/whatsapp-flows/:flowId",
|
|
294385
294441
|
"DELETE /api/v2/instances/:id/whatsapp-templates/:templateId",
|
|
294386
294442
|
"DELETE /api/v2/instances/:instanceId/routes/:id",
|
|
294387
294443
|
"DELETE /api/v2/messages/:id",
|
|
294388
294444
|
"DELETE /api/v2/messages/:id/reactions",
|
|
294389
294445
|
"DELETE /api/v2/messages/:id/star",
|
|
294390
294446
|
"DELETE /api/v2/providers/:id",
|
|
294447
|
+
"DELETE /api/v2/scheduled-messages/:id",
|
|
294391
294448
|
"DELETE /api/v2/settings/:key",
|
|
294392
294449
|
"DELETE /api/v2/webhook-sources/:id",
|
|
294393
294450
|
"GET /api/v2",
|
|
@@ -294456,6 +294513,7 @@ var init_route_ownership = __esm(() => {
|
|
|
294456
294513
|
"GET /api/v2/instances/:id/guilds/:guildId/audit",
|
|
294457
294514
|
"GET /api/v2/instances/:id/guilds/:guildId/config",
|
|
294458
294515
|
"GET /api/v2/instances/:id/pairing-requests",
|
|
294516
|
+
"GET /api/v2/instances/:id/passkey",
|
|
294459
294517
|
"GET /api/v2/instances/:id/privacy",
|
|
294460
294518
|
"GET /api/v2/instances/:id/qr",
|
|
294461
294519
|
"GET /api/v2/instances/:id/status",
|
|
@@ -294480,6 +294538,7 @@ var init_route_ownership = __esm(() => {
|
|
|
294480
294538
|
"GET /api/v2/media/:instanceId/*",
|
|
294481
294539
|
"GET /api/v2/messages",
|
|
294482
294540
|
"GET /api/v2/messages/:id",
|
|
294541
|
+
"GET /api/v2/messages/:id/permalink",
|
|
294483
294542
|
"GET /api/v2/messages/by-external",
|
|
294484
294543
|
"GET /api/v2/messages/tts/voices",
|
|
294485
294544
|
"GET /api/v2/payload-config",
|
|
@@ -294495,9 +294554,12 @@ var init_route_ownership = __esm(() => {
|
|
|
294495
294554
|
"GET /api/v2/providers/:id/teams",
|
|
294496
294555
|
"GET /api/v2/providers/:id/workflows",
|
|
294497
294556
|
"GET /api/v2/routes/metrics",
|
|
294557
|
+
"GET /api/v2/scheduled-messages",
|
|
294558
|
+
"GET /api/v2/scheduled-messages/:id",
|
|
294498
294559
|
"GET /api/v2/settings",
|
|
294499
294560
|
"GET /api/v2/settings/:key",
|
|
294500
294561
|
"GET /api/v2/settings/:key/history",
|
|
294562
|
+
"GET /api/v2/slack/search",
|
|
294501
294563
|
"GET /api/v2/turns",
|
|
294502
294564
|
"GET /api/v2/turns/:id",
|
|
294503
294565
|
"GET /api/v2/turns/stats",
|
|
@@ -294589,6 +294651,8 @@ var init_route_ownership = __esm(() => {
|
|
|
294589
294651
|
"POST /api/v2/instances/:id/groups/join",
|
|
294590
294652
|
"POST /api/v2/instances/:id/logout",
|
|
294591
294653
|
"POST /api/v2/instances/:id/pair",
|
|
294654
|
+
"POST /api/v2/instances/:id/passkey/confirm",
|
|
294655
|
+
"POST /api/v2/instances/:id/passkey/response",
|
|
294592
294656
|
"POST /api/v2/instances/:id/pairing-requests/:requestId/action",
|
|
294593
294657
|
"POST /api/v2/instances/:id/replay",
|
|
294594
294658
|
"POST /api/v2/instances/:id/restart",
|
|
@@ -294643,6 +294707,8 @@ var init_route_ownership = __esm(() => {
|
|
|
294643
294707
|
"POST /api/v2/persons/unlink",
|
|
294644
294708
|
"POST /api/v2/providers",
|
|
294645
294709
|
"POST /api/v2/providers/:id/health",
|
|
294710
|
+
"POST /api/v2/scheduled-messages",
|
|
294711
|
+
"POST /api/v2/slack/dm/open",
|
|
294646
294712
|
"POST /api/v2/turns/:id/close",
|
|
294647
294713
|
"POST /api/v2/turns/close",
|
|
294648
294714
|
"POST /api/v2/turns/close-all",
|
|
@@ -294661,9 +294727,9 @@ var init_route_ownership = __esm(() => {
|
|
|
294661
294727
|
"PUT /api/v2/instances/:id/presence",
|
|
294662
294728
|
"PUT /api/v2/instances/:id/profile/name",
|
|
294663
294729
|
"PUT /api/v2/instances/:id/profile/picture",
|
|
294730
|
+
"PUT /api/v2/instances/:id/profile/status",
|
|
294664
294731
|
"PUT /api/v2/instances/:id/whatsapp-business/profile",
|
|
294665
294732
|
"PUT /api/v2/instances/:id/whatsapp-flows/:flowId",
|
|
294666
|
-
"PUT /api/v2/instances/:id/profile/status",
|
|
294667
294733
|
"PUT /api/v2/payload-config/:eventType",
|
|
294668
294734
|
"PUT /api/v2/settings/:key"
|
|
294669
294735
|
];
|
|
@@ -316521,10 +316587,10 @@ var import_p_retry, import_google_auth_library, _defaultBaseGeminiUrl = undefine
|
|
|
316521
316587
|
invalidSegments.sort((a, b3) => a.start - b3.start);
|
|
316522
316588
|
if (invalidSegments.length > 0) {
|
|
316523
316589
|
let lastEnd = 0;
|
|
316524
|
-
const underline = invalidSegments.reduce((acc,
|
|
316525
|
-
const spaces = " ".repeat(
|
|
316526
|
-
const arrows = "^".repeat(
|
|
316527
|
-
lastEnd =
|
|
316590
|
+
const underline = invalidSegments.reduce((acc, segment2) => {
|
|
316591
|
+
const spaces = " ".repeat(segment2.start - lastEnd);
|
|
316592
|
+
const arrows = "^".repeat(segment2.length);
|
|
316593
|
+
lastEnd = segment2.start + segment2.length;
|
|
316528
316594
|
return acc + spaces + arrows;
|
|
316529
316595
|
}, "");
|
|
316530
316596
|
throw new GeminiNextGenAPIClientError(`Path parameters result in path with invalid segments:
|
|
@@ -322500,10 +322566,10 @@ function extractSegments(buffer3) {
|
|
|
322500
322566
|
const splitIndex = remaining.indexOf(`
|
|
322501
322567
|
|
|
322502
322568
|
`);
|
|
322503
|
-
const
|
|
322569
|
+
const segment2 = remaining.slice(0, splitIndex).trim();
|
|
322504
322570
|
remaining = remaining.slice(splitIndex + 2);
|
|
322505
|
-
if (
|
|
322506
|
-
segments.push(
|
|
322571
|
+
if (segment2)
|
|
322572
|
+
segments.push(segment2);
|
|
322507
322573
|
}
|
|
322508
322574
|
return [segments, remaining];
|
|
322509
322575
|
}
|
|
@@ -322518,8 +322584,8 @@ async function* processStreamChunks(streamGenerator, enableSplit) {
|
|
|
322518
322584
|
`)) {
|
|
322519
322585
|
const [segments, remaining] = extractSegments(buffer3);
|
|
322520
322586
|
buffer3 = remaining;
|
|
322521
|
-
for (const
|
|
322522
|
-
yield
|
|
322587
|
+
for (const segment2 of segments) {
|
|
322588
|
+
yield segment2;
|
|
322523
322589
|
}
|
|
322524
322590
|
}
|
|
322525
322591
|
if (chunk2.isComplete && buffer3.trim()) {
|
|
@@ -327231,12 +327297,12 @@ var is_array, hex_table, limit = 1024, encode3 = (str, _defaultEncoder, charset,
|
|
|
327231
327297
|
}
|
|
327232
327298
|
let out = "";
|
|
327233
327299
|
for (let j2 = 0;j2 < string.length; j2 += limit) {
|
|
327234
|
-
const
|
|
327300
|
+
const segment2 = string.length >= limit ? string.slice(j2, j2 + limit) : string;
|
|
327235
327301
|
const arr = [];
|
|
327236
|
-
for (let i = 0;i <
|
|
327237
|
-
let c =
|
|
327302
|
+
for (let i = 0;i < segment2.length; ++i) {
|
|
327303
|
+
let c = segment2.charCodeAt(i);
|
|
327238
327304
|
if (c === 45 || c === 46 || c === 95 || c === 126 || c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || format === RFC1738 && (c === 40 || c === 41)) {
|
|
327239
|
-
arr[arr.length] =
|
|
327305
|
+
arr[arr.length] = segment2.charAt(i);
|
|
327240
327306
|
continue;
|
|
327241
327307
|
}
|
|
327242
327308
|
if (c < 128) {
|
|
@@ -327252,7 +327318,7 @@ var is_array, hex_table, limit = 1024, encode3 = (str, _defaultEncoder, charset,
|
|
|
327252
327318
|
continue;
|
|
327253
327319
|
}
|
|
327254
327320
|
i += 1;
|
|
327255
|
-
c = 65536 + ((c & 1023) << 10 |
|
|
327321
|
+
c = 65536 + ((c & 1023) << 10 | segment2.charCodeAt(i) & 1023);
|
|
327256
327322
|
arr[arr.length] = hex_table[240 | c >> 18] + hex_table[128 | c >> 12 & 63] + hex_table[128 | c >> 6 & 63] + hex_table[128 | c & 63];
|
|
327257
327323
|
}
|
|
327258
327324
|
out += arr.join("");
|
|
@@ -346313,6 +346379,7 @@ var init_instances = __esm(() => {
|
|
|
346313
346379
|
SEALED_CREDENTIAL_COLUMNS = [
|
|
346314
346380
|
"discordBotToken",
|
|
346315
346381
|
"slackBotToken",
|
|
346382
|
+
"slackUserToken",
|
|
346316
346383
|
"slackAppToken",
|
|
346317
346384
|
"slackSigningSecret",
|
|
346318
346385
|
"telegramBotToken",
|
|
@@ -346468,7 +346535,7 @@ class MessageService {
|
|
|
346468
346535
|
return result;
|
|
346469
346536
|
}
|
|
346470
346537
|
async hasBotRepliedInThread(chatId, threadExternalId) {
|
|
346471
|
-
const [result] = await this.db.select({ id: messages2.id }).from(messages2).where(and2(eq(messages2.chatId, chatId), eq(messages2.isFromMe, true), or2(eq(messages2.replyToExternalId, threadExternalId), eq(messages2.externalId, threadExternalId)))).limit(1);
|
|
346538
|
+
const [result] = await this.db.select({ id: messages2.id }).from(messages2).where(and2(eq(messages2.chatId, chatId), eq(messages2.isFromMe, true), or2(eq(messages2.threadExternalId, threadExternalId), eq(messages2.replyToExternalId, threadExternalId), eq(messages2.externalId, threadExternalId)))).limit(1);
|
|
346472
346539
|
return !!result;
|
|
346473
346540
|
}
|
|
346474
346541
|
async getByExternalId(chatId, externalId) {
|
|
@@ -346557,6 +346624,8 @@ class MessageService {
|
|
|
346557
346624
|
quotedSenderName: options.quotedSenderName,
|
|
346558
346625
|
isForwarded: options.isForwarded ?? false,
|
|
346559
346626
|
forwardedFromExternalId: options.forwardedFromExternalId,
|
|
346627
|
+
threadExternalId: options.threadExternalId,
|
|
346628
|
+
isThreadBroadcast: options.isThreadBroadcast ?? false,
|
|
346560
346629
|
rawPayload: options.rawPayload,
|
|
346561
346630
|
originalEventId: options.originalEventId,
|
|
346562
346631
|
latestEventId: options.originalEventId
|
|
@@ -346760,6 +346829,19 @@ class MessageService {
|
|
|
346760
346829
|
const [replyToMessage] = await this.db.select({ id: messages2.id }).from(messages2).where(and2(eq(messages2.chatId, chatId), eq(messages2.externalId, replyToExternalId))).limit(1);
|
|
346761
346830
|
return replyToMessage?.id ?? null;
|
|
346762
346831
|
}
|
|
346832
|
+
async setStarred(chatId, externalId, starred) {
|
|
346833
|
+
await this.db.update(messages2).set({ starredAt: starred ? new Date : null, updatedAt: new Date }).where(and2(eq(messages2.chatId, chatId), eq(messages2.externalId, externalId)));
|
|
346834
|
+
}
|
|
346835
|
+
async setPinned(chatId, externalId, pinned, pinnedBy) {
|
|
346836
|
+
await this.db.update(messages2).set({
|
|
346837
|
+
pinnedAt: pinned ? new Date : null,
|
|
346838
|
+
pinnedBy: pinned ? pinnedBy ?? null : null,
|
|
346839
|
+
updatedAt: new Date
|
|
346840
|
+
}).where(and2(eq(messages2.chatId, chatId), eq(messages2.externalId, externalId)));
|
|
346841
|
+
}
|
|
346842
|
+
async setPermalink(chatId, externalId, permalink) {
|
|
346843
|
+
await this.db.update(messages2).set({ permalink, updatedAt: new Date }).where(and2(eq(messages2.chatId, chatId), eq(messages2.externalId, externalId)));
|
|
346844
|
+
}
|
|
346763
346845
|
async updateReplyToReference(id, replyToMessageId) {
|
|
346764
346846
|
await this.db.update(messages2).set({ replyToMessageId, updatedAt: new Date }).where(eq(messages2.id, id));
|
|
346765
346847
|
}
|
|
@@ -346941,7 +347023,92 @@ var init_payload_store2 = __esm(() => {
|
|
|
346941
347023
|
log97 = createLogger("payload-store");
|
|
346942
347024
|
});
|
|
346943
347025
|
|
|
347026
|
+
// ../api/src/utils/phone.ts
|
|
347027
|
+
function isValidE164Phone(phone) {
|
|
347028
|
+
const bare = phone.replace(/^\+/, "");
|
|
347029
|
+
if (!/^\d+$/.test(bare))
|
|
347030
|
+
return false;
|
|
347031
|
+
return bare.length >= 7 && bare.length <= 15;
|
|
347032
|
+
}
|
|
347033
|
+
function isLidFormat(platformUserId) {
|
|
347034
|
+
const bare = platformUserId.split("@")[0] || platformUserId;
|
|
347035
|
+
return /^\d{14,}$/.test(bare);
|
|
347036
|
+
}
|
|
347037
|
+
function validateContactPhone(phone, platformUserId) {
|
|
347038
|
+
if (!phone)
|
|
347039
|
+
return;
|
|
347040
|
+
if (!isValidE164Phone(phone))
|
|
347041
|
+
return;
|
|
347042
|
+
const barePhone = phone.replace(/^\+/, "");
|
|
347043
|
+
const barePuid = platformUserId.split("@")[0] || platformUserId;
|
|
347044
|
+
if (isLidFormat(platformUserId) && barePhone === barePuid)
|
|
347045
|
+
return;
|
|
347046
|
+
return phone;
|
|
347047
|
+
}
|
|
347048
|
+
|
|
347049
|
+
// ../api/src/utils/canonical-handle.ts
|
|
347050
|
+
function isWhatsAppFamily(channel5) {
|
|
347051
|
+
return WHATSAPP_FAMILY_CHANNELS.has(channel5);
|
|
347052
|
+
}
|
|
347053
|
+
function isPersonlessChannel(channel5) {
|
|
347054
|
+
return PERSONLESS_CHANNELS.has(channel5);
|
|
347055
|
+
}
|
|
347056
|
+
function stripProviderPrefix(raw2) {
|
|
347057
|
+
return raw2.replace(/^whatsapp:/i, "").trim();
|
|
347058
|
+
}
|
|
347059
|
+
function stripDeviceSuffix2(local) {
|
|
347060
|
+
const colon = local.indexOf(":");
|
|
347061
|
+
return colon === -1 ? local : local.slice(0, colon);
|
|
347062
|
+
}
|
|
347063
|
+
function canonicalizeHandle(channel5, rawUserId) {
|
|
347064
|
+
if (!rawUserId)
|
|
347065
|
+
return { platformUserId: rawUserId };
|
|
347066
|
+
if (!isWhatsAppFamily(channel5))
|
|
347067
|
+
return { platformUserId: rawUserId };
|
|
347068
|
+
const stripped = stripProviderPrefix(rawUserId);
|
|
347069
|
+
const atIndex = stripped.indexOf("@");
|
|
347070
|
+
const suffix = atIndex === -1 ? "" : stripped.slice(atIndex);
|
|
347071
|
+
const localRaw = atIndex === -1 ? stripped : stripped.slice(0, atIndex);
|
|
347072
|
+
if (suffix === "@lid") {
|
|
347073
|
+
return { platformUserId: `${stripDeviceSuffix2(localRaw)}@lid` };
|
|
347074
|
+
}
|
|
347075
|
+
if (suffix === "@g.us" || suffix === "@broadcast" || suffix === "@newsletter") {
|
|
347076
|
+
return { platformUserId: stripped };
|
|
347077
|
+
}
|
|
347078
|
+
const digits = stripDeviceSuffix2(localRaw).replace(/^\+/, "");
|
|
347079
|
+
if (isValidE164Phone(digits)) {
|
|
347080
|
+
return { platformUserId: `${digits}${WA_PHONE_SUFFIX}`, phone: `+${digits}` };
|
|
347081
|
+
}
|
|
347082
|
+
return { platformUserId: rawUserId };
|
|
347083
|
+
}
|
|
347084
|
+
var WHATSAPP_FAMILY_CHANNELS, PERSONLESS_CHANNELS, WA_PHONE_SUFFIX = "@s.whatsapp.net";
|
|
347085
|
+
var init_canonical_handle = __esm(() => {
|
|
347086
|
+
WHATSAPP_FAMILY_CHANNELS = new Set([
|
|
347087
|
+
"whatsapp-baileys",
|
|
347088
|
+
"whatsapp-business",
|
|
347089
|
+
"twilio-whatsapp",
|
|
347090
|
+
"gupshup",
|
|
347091
|
+
"hermes"
|
|
347092
|
+
]);
|
|
347093
|
+
PERSONLESS_CHANNELS = new Set(["internal", "a2a"]);
|
|
347094
|
+
});
|
|
347095
|
+
|
|
346944
347096
|
// ../api/src/services/persons.ts
|
|
347097
|
+
function canonicalizeIdentityInput(rawData, rawLinkOptions) {
|
|
347098
|
+
const data = {
|
|
347099
|
+
...rawData,
|
|
347100
|
+
platformUserId: canonicalizeHandle(rawData.channel, rawData.platformUserId).platformUserId
|
|
347101
|
+
};
|
|
347102
|
+
if (rawLinkOptions?.matchByPlatformUserId === undefined) {
|
|
347103
|
+
return { data, linkOptions: rawLinkOptions };
|
|
347104
|
+
}
|
|
347105
|
+
const linkOptions = {
|
|
347106
|
+
...rawLinkOptions,
|
|
347107
|
+
matchByPlatformUserId: canonicalizeHandle(rawData.channel, rawLinkOptions.matchByPlatformUserId).platformUserId
|
|
347108
|
+
};
|
|
347109
|
+
return { data, linkOptions };
|
|
347110
|
+
}
|
|
347111
|
+
|
|
346945
347112
|
class PersonService {
|
|
346946
347113
|
pool;
|
|
346947
347114
|
eventBus;
|
|
@@ -347034,8 +347201,8 @@ class PersonService {
|
|
|
347034
347201
|
`);
|
|
347035
347202
|
return result;
|
|
347036
347203
|
}
|
|
347037
|
-
async getById(id) {
|
|
347038
|
-
const [result] = await
|
|
347204
|
+
async getById(id, handle = this.db) {
|
|
347205
|
+
const [result] = await handle.select().from(persons).where(eq(persons.id, id)).limit(1);
|
|
347039
347206
|
if (!result) {
|
|
347040
347207
|
throw new NotFoundError("Person", id);
|
|
347041
347208
|
}
|
|
@@ -347101,8 +347268,8 @@ class PersonService {
|
|
|
347101
347268
|
const [mapping] = await this.db.select({ phoneId: chatIdMappings.phoneId }).from(chatIdMappings).where(and2(eq(chatIdMappings.instanceId, instanceId), eq(chatIdMappings.lidId, lidId))).limit(1);
|
|
347102
347269
|
return mapping?.phoneId ?? null;
|
|
347103
347270
|
}
|
|
347104
|
-
async findPersonByPhone(phone) {
|
|
347105
|
-
const [existing] = await
|
|
347271
|
+
async findPersonByPhone(phone, handle = this.db) {
|
|
347272
|
+
const [existing] = await handle.select().from(persons).where(eq(persons.primaryPhone, phone)).limit(1);
|
|
347106
347273
|
return existing ? { personId: existing.id, wasLinked: true } : null;
|
|
347107
347274
|
}
|
|
347108
347275
|
async findPersonToLink(linkOptions) {
|
|
@@ -347135,9 +347302,9 @@ class PersonService {
|
|
|
347135
347302
|
return { wasLinked: false };
|
|
347136
347303
|
return this.createPersonWithConflictHandling(linkOptions);
|
|
347137
347304
|
}
|
|
347138
|
-
async createPersonWithConflictHandling(linkOptions) {
|
|
347305
|
+
async createPersonWithConflictHandling(linkOptions, handle = this.db) {
|
|
347139
347306
|
try {
|
|
347140
|
-
const [newPerson] = await
|
|
347307
|
+
const [newPerson] = await handle.insert(persons).values({
|
|
347141
347308
|
displayName: linkOptions.displayName,
|
|
347142
347309
|
primaryPhone: linkOptions.matchByPhone,
|
|
347143
347310
|
primaryEmail: linkOptions.matchByEmail
|
|
@@ -347145,17 +347312,17 @@ class PersonService {
|
|
|
347145
347312
|
if (newPerson)
|
|
347146
347313
|
return { personId: newPerson.id, wasLinked: false };
|
|
347147
347314
|
if (linkOptions.matchByPhone) {
|
|
347148
|
-
return await this.findPersonByPhone(linkOptions.matchByPhone) ?? { wasLinked: false };
|
|
347315
|
+
return await this.findPersonByPhone(linkOptions.matchByPhone, handle) ?? { wasLinked: false };
|
|
347149
347316
|
}
|
|
347150
347317
|
} catch {
|
|
347151
347318
|
if (linkOptions.matchByPhone) {
|
|
347152
|
-
return await this.findPersonByPhone(linkOptions.matchByPhone) ?? { wasLinked: false };
|
|
347319
|
+
return await this.findPersonByPhone(linkOptions.matchByPhone, handle) ?? { wasLinked: false };
|
|
347153
347320
|
}
|
|
347154
347321
|
}
|
|
347155
347322
|
return { wasLinked: false };
|
|
347156
347323
|
}
|
|
347157
|
-
async updateExistingIdentity(existing, data) {
|
|
347158
|
-
const [updated] = await
|
|
347324
|
+
async updateExistingIdentity(existing, data, handle = this.db) {
|
|
347325
|
+
const [updated] = await handle.update(platformIdentities).set({
|
|
347159
347326
|
platformUsername: data.platformUsername ?? existing.platformUsername,
|
|
347160
347327
|
profilePicUrl: data.profilePicUrl ?? existing.profilePicUrl,
|
|
347161
347328
|
profileData: data.profileData ?? existing.profileData,
|
|
@@ -347167,23 +347334,24 @@ class PersonService {
|
|
|
347167
347334
|
}
|
|
347168
347335
|
let person2 = null;
|
|
347169
347336
|
if (updated.personId) {
|
|
347170
|
-
person2 = await this.getById(updated.personId);
|
|
347337
|
+
person2 = await this.getById(updated.personId, handle);
|
|
347171
347338
|
}
|
|
347172
347339
|
return { identity: updated, person: person2, isNew: false, wasLinked: false };
|
|
347173
347340
|
}
|
|
347174
|
-
async findOrCreateIdentity(
|
|
347175
|
-
const instanceId =
|
|
347341
|
+
async findOrCreateIdentity(rawData, rawLinkOptions) {
|
|
347342
|
+
const instanceId = rawData.instanceId;
|
|
347176
347343
|
if (!instanceId) {
|
|
347177
347344
|
throw new Error("instanceId is required");
|
|
347178
347345
|
}
|
|
347346
|
+
const { data, linkOptions } = canonicalizeIdentityInput(rawData, rawLinkOptions);
|
|
347179
347347
|
const existing = await this.db.select().from(platformIdentities).where(and2(eq(platformIdentities.channel, data.channel), eq(platformIdentities.instanceId, instanceId), eq(platformIdentities.platformUserId, data.platformUserId))).limit(1);
|
|
347180
347348
|
if (existing[0]) {
|
|
347181
347349
|
return this.updateExistingIdentity(existing[0], data);
|
|
347182
347350
|
}
|
|
347183
347351
|
let personId = data.personId;
|
|
347184
347352
|
let wasLinked = false;
|
|
347353
|
+
let resolvedLinkOptions = linkOptions;
|
|
347185
347354
|
if (!personId && linkOptions) {
|
|
347186
|
-
let resolvedLinkOptions = linkOptions;
|
|
347187
347355
|
if (!linkOptions.matchByPhone && data.platformUserId.endsWith("@lid") && instanceId) {
|
|
347188
347356
|
const phoneFromLid = await this.resolvePhoneFromLid(instanceId, data.platformUserId);
|
|
347189
347357
|
if (phoneFromLid) {
|
|
@@ -347191,31 +347359,63 @@ class PersonService {
|
|
|
347191
347359
|
resolvedLinkOptions = { ...linkOptions, matchByPhone: phoneNumber };
|
|
347192
347360
|
}
|
|
347193
347361
|
}
|
|
347194
|
-
const linkResult = await this.findPersonToLink({ ...resolvedLinkOptions, instanceId });
|
|
347362
|
+
const linkResult = await this.findPersonToLink({ ...resolvedLinkOptions, instanceId, createPerson: false });
|
|
347195
347363
|
personId = linkResult.personId;
|
|
347196
347364
|
wasLinked = linkResult.wasLinked;
|
|
347197
347365
|
}
|
|
347198
347366
|
const matchType = linkOptions?.matchByPhone ? "phone" : linkOptions?.matchByEmail ? "email" : "platform_id";
|
|
347199
347367
|
const linkedBy = wasLinked ? `${matchType}_match` : personId ? "initial" : undefined;
|
|
347200
347368
|
const linkReason = wasLinked ? `Matched by ${matchType}` : undefined;
|
|
347201
|
-
const
|
|
347202
|
-
|
|
347369
|
+
const createPersonOptions = !personId && resolvedLinkOptions?.createPerson ? {
|
|
347370
|
+
matchByPhone: resolvedLinkOptions.matchByPhone,
|
|
347371
|
+
matchByEmail: resolvedLinkOptions.matchByEmail,
|
|
347372
|
+
displayName: resolvedLinkOptions.displayName
|
|
347373
|
+
} : undefined;
|
|
347374
|
+
return this.secureIdentity({
|
|
347375
|
+
data,
|
|
347203
347376
|
personId,
|
|
347377
|
+
wasLinked,
|
|
347204
347378
|
linkedBy,
|
|
347379
|
+
linkReason,
|
|
347205
347380
|
confidence: wasLinked ? 90 : 100,
|
|
347206
|
-
|
|
347207
|
-
})
|
|
347208
|
-
|
|
347209
|
-
|
|
347210
|
-
}
|
|
347211
|
-
|
|
347212
|
-
|
|
347213
|
-
|
|
347214
|
-
|
|
347215
|
-
|
|
347381
|
+
createPersonOptions
|
|
347382
|
+
});
|
|
347383
|
+
}
|
|
347384
|
+
async secureIdentity(params) {
|
|
347385
|
+
const { data, personId, wasLinked, linkedBy, linkReason, confidence, createPersonOptions } = params;
|
|
347386
|
+
const { instanceId, channel: channel5, platformUserId } = data;
|
|
347387
|
+
return this.db.transaction(async (tx) => {
|
|
347388
|
+
const txDb = tx;
|
|
347389
|
+
const [inserted] = await txDb.insert(platformIdentities).values({
|
|
347390
|
+
...data,
|
|
347391
|
+
personId,
|
|
347392
|
+
linkedBy,
|
|
347393
|
+
confidence,
|
|
347394
|
+
linkReason
|
|
347395
|
+
}).onConflictDoNothing().returning();
|
|
347396
|
+
if (!inserted) {
|
|
347397
|
+
const [existing] = await txDb.select().from(platformIdentities).where(and2(eq(platformIdentities.channel, channel5), eq(platformIdentities.instanceId, instanceId ?? ""), eq(platformIdentities.platformUserId, platformUserId))).limit(1);
|
|
347398
|
+
if (!existing) {
|
|
347399
|
+
throw new Error("Failed to resolve identity after conflict");
|
|
347400
|
+
}
|
|
347401
|
+
return this.updateExistingIdentity(existing, data, txDb);
|
|
347402
|
+
}
|
|
347403
|
+
if (inserted.personId || !createPersonOptions) {
|
|
347404
|
+
const person3 = inserted.personId ? await this.getById(inserted.personId, txDb) : null;
|
|
347405
|
+
return { identity: inserted, person: person3, isNew: true, wasLinked };
|
|
347406
|
+
}
|
|
347407
|
+
const created = await this.createPersonWithConflictHandling(createPersonOptions, txDb);
|
|
347408
|
+
if (!created.personId) {
|
|
347409
|
+
return { identity: inserted, person: null, isNew: true, wasLinked };
|
|
347410
|
+
}
|
|
347411
|
+
const [linked] = await txDb.update(platformIdentities).set({ personId: created.personId, linkedBy: "initial", confidence: 100, updatedAt: new Date }).where(eq(platformIdentities.id, inserted.id)).returning();
|
|
347412
|
+
const person2 = await this.getById(created.personId, txDb);
|
|
347413
|
+
return { identity: linked ?? inserted, person: person2, isNew: true, wasLinked: created.wasLinked };
|
|
347414
|
+
});
|
|
347216
347415
|
}
|
|
347217
347416
|
async getIdentityByPlatformId(channel5, instanceId, platformUserId) {
|
|
347218
|
-
const
|
|
347417
|
+
const canonicalUserId = canonicalizeHandle(channel5, platformUserId).platformUserId;
|
|
347418
|
+
const [identity] = await this.db.select().from(platformIdentities).where(and2(eq(platformIdentities.channel, channel5), eq(platformIdentities.instanceId, instanceId), eq(platformIdentities.platformUserId, canonicalUserId))).limit(1);
|
|
347219
347419
|
return identity || null;
|
|
347220
347420
|
}
|
|
347221
347421
|
async listIdentitiesByInstance(instanceId, options = {}) {
|
|
@@ -347392,6 +347592,7 @@ var init_persons = __esm(() => {
|
|
|
347392
347592
|
init_src5();
|
|
347393
347593
|
init_drizzle_orm();
|
|
347394
347594
|
init_tenant_scope();
|
|
347595
|
+
init_canonical_handle();
|
|
347395
347596
|
});
|
|
347396
347597
|
|
|
347397
347598
|
// ../api/src/lib/agent-key-name.ts
|
|
@@ -348363,16 +348564,6 @@ function buildSentChatPreview(payload) {
|
|
|
348363
348564
|
const badge = payload.content.type !== "text" ? MEDIA_BADGES2[payload.content.type] ?? `[${payload.content.type}]` : "";
|
|
348364
348565
|
return badge ? text3 ? `${badge} ${text3}` : badge : text3;
|
|
348365
348566
|
}
|
|
348366
|
-
function extractPhoneFromSender(senderId, channel5) {
|
|
348367
|
-
if (!channel5.startsWith("whatsapp"))
|
|
348368
|
-
return;
|
|
348369
|
-
const bare = senderId.split("@")[0] || senderId;
|
|
348370
|
-
if (!/^\d+$/.test(bare))
|
|
348371
|
-
return;
|
|
348372
|
-
if (bare.length < 7 || bare.length > 15)
|
|
348373
|
-
return;
|
|
348374
|
-
return `+${bare}`;
|
|
348375
|
-
}
|
|
348376
348567
|
async function processSenderIdentity(services, payload, metadata, channel5, trustedTenantId) {
|
|
348377
348568
|
if (metadata.platformIdentityId) {
|
|
348378
348569
|
return { personId: metadata.personId, platformIdentityId: metadata.platformIdentityId };
|
|
@@ -348380,11 +348571,15 @@ async function processSenderIdentity(services, payload, metadata, channel5, trus
|
|
|
348380
348571
|
if (!payload.from) {
|
|
348381
348572
|
return { personId: metadata.personId, platformIdentityId: undefined };
|
|
348382
348573
|
}
|
|
348574
|
+
if (isPersonlessChannel(channel5)) {
|
|
348575
|
+
return { personId: metadata.personId, platformIdentityId: metadata.platformIdentityId };
|
|
348576
|
+
}
|
|
348383
348577
|
const displayName = truncate4(payload.senderName ?? payload.rawPayload?.pushName, 255);
|
|
348384
|
-
const
|
|
348578
|
+
const canonical = canonicalizeHandle(channel5, payload.from);
|
|
348579
|
+
const platformUserId = truncate4(canonical.platformUserId, 255) ?? canonical.platformUserId;
|
|
348385
348580
|
const isLidAddressed = payload.rawPayload?.addressingMode === "lid" || payload.rawPayload?.senderIsLid === true;
|
|
348386
348581
|
const resolvedPhone = isLidAddressed ? payload.rawPayload?.resolvedSenderPhone : undefined;
|
|
348387
|
-
const phoneNumber = isLidAddressed ? resolvedPhone ? `+${resolvedPhone}` : undefined :
|
|
348582
|
+
const phoneNumber = isLidAddressed ? resolvedPhone ? `+${resolvedPhone}` : undefined : canonical.phone;
|
|
348388
348583
|
const { identity, person: person2, isNew } = await services.persons.findOrCreateIdentity({ channel: channel5, instanceId: metadata.instanceId, platformUserId, platformUsername: displayName }, {
|
|
348389
348584
|
createPerson: true,
|
|
348390
348585
|
displayName,
|
|
@@ -348559,6 +348754,22 @@ async function resolveOrCreateChat(services, instanceId, chatExternalId, chatTyp
|
|
|
348559
348754
|
canonicalId
|
|
348560
348755
|
});
|
|
348561
348756
|
}
|
|
348757
|
+
async function linkReplyTarget(services, chatId, messageId, replyToExternalId) {
|
|
348758
|
+
if (!replyToExternalId)
|
|
348759
|
+
return;
|
|
348760
|
+
try {
|
|
348761
|
+
const replyToMessageId = await services.messages.resolveReplyToMessage(chatId, replyToExternalId);
|
|
348762
|
+
if (replyToMessageId) {
|
|
348763
|
+
await services.messages.updateReplyToReference(messageId, replyToMessageId);
|
|
348764
|
+
}
|
|
348765
|
+
} catch (error3) {
|
|
348766
|
+
log103.debug("Could not resolve reply target", {
|
|
348767
|
+
chatId,
|
|
348768
|
+
replyToExternalId,
|
|
348769
|
+
error: error3 instanceof Error ? error3.message : String(error3)
|
|
348770
|
+
});
|
|
348771
|
+
}
|
|
348772
|
+
}
|
|
348562
348773
|
async function handleMessageReceived(services, payload, metadata, eventTimestamp, trustedTenantId, identity) {
|
|
348563
348774
|
const channel5 = metadata.channelType ?? "whatsapp";
|
|
348564
348775
|
const isHistorySync = metadata.ingestMode === "history-sync";
|
|
@@ -348602,12 +348813,15 @@ async function handleMessageReceived(services, payload, metadata, eventTimestamp
|
|
|
348602
348813
|
replyToExternalId: truncate4(payload.replyToId, 255),
|
|
348603
348814
|
quotedText: quotedMessage?.conversation,
|
|
348604
348815
|
quotedSenderName: truncate4(quotedMessage?.pushName, 255),
|
|
348816
|
+
threadExternalId: truncate4(payload.threadId, 255),
|
|
348817
|
+
isThreadBroadcast: rawPayload?.isThreadBroadcast === true,
|
|
348605
348818
|
isForwarded: !!(rawPayload?.isForwarded || rawPayload?.forwardingScore),
|
|
348606
348819
|
rawPayload
|
|
348607
348820
|
});
|
|
348608
348821
|
await maybeRecordMessageEdit(services, created, rawPayload, message2.id, sanitizeText(payload.content.text) ?? undefined, platformTimestamp ?? new Date(eventTimestamp), payload.from);
|
|
348609
348822
|
if (created) {
|
|
348610
348823
|
log103.debug("Created message", { externalId: payload.externalId, chatId: chat2.id });
|
|
348824
|
+
await linkReplyTarget(services, chat2.id, message2.id, payload.replyToId);
|
|
348611
348825
|
}
|
|
348612
348826
|
await maybeRecordParticipantActivity(services, chat2.id, payload.from);
|
|
348613
348827
|
maybeUpdateRecency(services, metadata.instanceId, chat2.id, rawPayload, payload, platformTimestamp ?? new Date(eventTimestamp), trustedTenantId);
|
|
@@ -348705,7 +348919,8 @@ async function setupMessagePersistence(eventBus, services) {
|
|
|
348705
348919
|
mediaLocalPath: sentContent.mediaLocalPath,
|
|
348706
348920
|
mediaMetadata: sentContent.mediaMetadata,
|
|
348707
348921
|
rawPayload: sentContent.rawPayload,
|
|
348708
|
-
replyToExternalId: truncate4(payload.replyToId, 255)
|
|
348922
|
+
replyToExternalId: truncate4(payload.replyToId, 255),
|
|
348923
|
+
threadExternalId: truncate4(payload.threadId, 255)
|
|
348709
348924
|
});
|
|
348710
348925
|
return { chat: chat3, message: message3, created: created2 };
|
|
348711
348926
|
});
|
|
@@ -348903,6 +349118,7 @@ var init_message_persistence = __esm(() => {
|
|
|
348903
349118
|
init_esm5();
|
|
348904
349119
|
init_sentry_scrub();
|
|
348905
349120
|
init_worker_tenant_context();
|
|
349121
|
+
init_canonical_handle();
|
|
348906
349122
|
init_loader2();
|
|
348907
349123
|
log103 = createLogger("message-persistence");
|
|
348908
349124
|
CONTENT_TYPE_MAP = {
|
|
@@ -349038,12 +349254,12 @@ var init_session_storage = __esm(() => {
|
|
|
349038
349254
|
});
|
|
349039
349255
|
|
|
349040
349256
|
// ../api/src/plugins/agent-dispatcher.ts
|
|
349041
|
-
import { createHash as
|
|
349257
|
+
import { createHash as createHash13 } from "crypto";
|
|
349042
349258
|
import { unlink, writeFile as writeFile5 } from "fs/promises";
|
|
349043
349259
|
import { tmpdir as tmpdir12 } from "os";
|
|
349044
349260
|
import { join as join22, resolve as resolve3 } from "path";
|
|
349045
349261
|
function sha256Digest(value) {
|
|
349046
|
-
return `sha256:${
|
|
349262
|
+
return `sha256:${createHash13("sha256").update(value).digest("hex")}`;
|
|
349047
349263
|
}
|
|
349048
349264
|
function redactLifecycleText(value) {
|
|
349049
349265
|
return value.replace(/[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}/gi, "[EMAIL]").replace(/\b\+?\d[\d\s().-]{5,}\d\b/g, "[PHONE]").replace(/\b\d{6,}\b/g, "[NUMBER]").replace(/\b[^\s@]+@(s\.whatsapp\.net|g\.us|lid|newsletter)\b/gi, "[JID]");
|
|
@@ -365905,6 +366121,10 @@ function sanitizeInstance(instance4) {
|
|
|
365905
366121
|
function applySlackProfileMetadata(opts, metadata) {
|
|
365906
366122
|
if (!metadata)
|
|
365907
366123
|
return;
|
|
366124
|
+
if (metadata.mode)
|
|
366125
|
+
opts.mode = metadata.mode;
|
|
366126
|
+
if (metadata.httpPort)
|
|
366127
|
+
opts.httpPort = metadata.httpPort;
|
|
365908
366128
|
if (metadata.replyToMode)
|
|
365909
366129
|
opts.replyToMode = metadata.replyToMode;
|
|
365910
366130
|
if (metadata.streamMode)
|
|
@@ -365918,6 +366138,12 @@ function applySlackConnectOptions(opts, instance4, overrides) {
|
|
|
365918
366138
|
const botToken = overrides?.slackBotToken ?? instance4.slackBotToken ?? opts.token;
|
|
365919
366139
|
if (botToken)
|
|
365920
366140
|
opts.botToken = botToken;
|
|
366141
|
+
const userToken = overrides?.slackUserToken ?? instance4.slackUserToken;
|
|
366142
|
+
if (userToken)
|
|
366143
|
+
opts.userToken = userToken;
|
|
366144
|
+
const authMode = overrides?.slackAuthMode ?? instance4.slackAuthMode;
|
|
366145
|
+
if (authMode)
|
|
366146
|
+
opts.authMode = authMode;
|
|
365921
366147
|
const appToken = overrides?.slackAppToken ?? instance4.slackAppToken;
|
|
365922
366148
|
if (appToken)
|
|
365923
366149
|
opts.appToken = appToken;
|
|
@@ -365942,8 +366168,13 @@ function applyTelegramConnectionOptions(options, input) {
|
|
|
365942
366168
|
options.telegramReactionLevel = input.telegramReactionLevel;
|
|
365943
366169
|
}
|
|
365944
366170
|
function applySlackConnectionOptions(options, input) {
|
|
366171
|
+
applySlackProfileMetadata(options, input.profileMetadata);
|
|
365945
366172
|
if (input.token)
|
|
365946
366173
|
options.botToken = input.token;
|
|
366174
|
+
if (input.slackUserToken)
|
|
366175
|
+
options.userToken = input.slackUserToken;
|
|
366176
|
+
if (input.slackAuthMode)
|
|
366177
|
+
options.authMode = input.slackAuthMode;
|
|
365947
366178
|
if (input.slackAppToken)
|
|
365948
366179
|
options.appToken = input.slackAppToken;
|
|
365949
366180
|
if (input.slackSigningSecret)
|
|
@@ -366054,6 +366285,10 @@ function buildTokenPersistUpdates(channel5, body) {
|
|
|
366054
366285
|
if (channel5 === "slack") {
|
|
366055
366286
|
if (body.slackBotToken)
|
|
366056
366287
|
updates.slackBotToken = body.slackBotToken;
|
|
366288
|
+
if (body.slackUserToken)
|
|
366289
|
+
updates.slackUserToken = body.slackUserToken;
|
|
366290
|
+
if (body.slackAuthMode)
|
|
366291
|
+
updates.slackAuthMode = body.slackAuthMode;
|
|
366057
366292
|
if (body.slackAppToken)
|
|
366058
366293
|
updates.slackAppToken = body.slackAppToken;
|
|
366059
366294
|
if (body.slackSigningSecret)
|
|
@@ -366113,6 +366348,9 @@ async function handleAgentKeyProvisioning(services, instanceId, newAgentId, oldA
|
|
|
366113
366348
|
}
|
|
366114
366349
|
}
|
|
366115
366350
|
}
|
|
366351
|
+
function supportsPasskey(plugin10) {
|
|
366352
|
+
return "getPasskeyState" in plugin10 && "submitPasskeyResponse" in plugin10 && "confirmPasskey" in plugin10;
|
|
366353
|
+
}
|
|
366116
366354
|
function buildConnectConnectionOptions(instance4, body, forceNewQr) {
|
|
366117
366355
|
const connectToken = body.token ?? persistedTokenForChannel(instance4);
|
|
366118
366356
|
return buildInstanceConnectionOptions({
|
|
@@ -366120,8 +366358,11 @@ function buildConnectConnectionOptions(instance4, body, forceNewQr) {
|
|
|
366120
366358
|
forceNewQr,
|
|
366121
366359
|
token: connectToken,
|
|
366122
366360
|
telegramReactionLevel: instance4.telegramReactionLevel,
|
|
366361
|
+
slackUserToken: body.slackUserToken ?? instance4.slackUserToken,
|
|
366362
|
+
slackAuthMode: body.slackAuthMode ?? instance4.slackAuthMode,
|
|
366123
366363
|
slackAppToken: body.slackAppToken ?? instance4.slackAppToken,
|
|
366124
366364
|
slackSigningSecret: body.slackSigningSecret ?? instance4.slackSigningSecret,
|
|
366365
|
+
profileMetadata: instance4.profileMetadata,
|
|
366125
366366
|
whatsapp: body.whatsapp,
|
|
366126
366367
|
gupshupCallbackUrl: instance4.gupshupCallbackUrl,
|
|
366127
366368
|
gupshupAuthToken: instance4.gupshupAuthToken,
|
|
@@ -366279,7 +366520,7 @@ function getCacheKey(instanceId, guildId) {
|
|
|
366279
366520
|
function invalidateGuildCache(instanceId, guildId) {
|
|
366280
366521
|
guildConfigCache.delete(getCacheKey(instanceId, guildId));
|
|
366281
366522
|
}
|
|
366282
|
-
var log115, instancesRoutes, instanceAccess2, listQuerySchema12, agentReplyFilterSchema, createInstanceSchema, updateInstanceSchema, DEFAULT_AGENT_REPLY_FILTER, SENSITIVE_INSTANCE_FIELDS, pairingCodeSchema, connectInstanceSchema, syncRequestSchema, listContactsQuerySchema, listGroupsQuerySchema, checkNumberSchema, updateBioSchema, blockContactSchema, groupParticipantActionSchema, groupSettingSchema, groupParticipantsSchema, groupParticipantsPatchSchema, updateGroupSubjectSchema, updateGroupDescriptionSchema, updateGroupSettingsSchema, patchGroupSchema, resyncSchema, replaySchema, pairingActionSchema, guildConfigOverrideSchema, guildConfigCache, GUILD_CONFIG_TTL, presenceSchema;
|
|
366523
|
+
var log115, instancesRoutes, instanceAccess2, listQuerySchema12, agentReplyFilterSchema, createInstanceSchema, updateInstanceSchema, DEFAULT_AGENT_REPLY_FILTER, SENSITIVE_INSTANCE_FIELDS, pairingCodeSchema, passkeyBase64UrlSchema, passkeyCredentialSchema, connectInstanceSchema, syncRequestSchema, listContactsQuerySchema, listGroupsQuerySchema, checkNumberSchema, updateBioSchema, blockContactSchema, groupParticipantActionSchema, groupSettingSchema, groupParticipantsSchema, groupParticipantsPatchSchema, updateGroupSubjectSchema, updateGroupDescriptionSchema, updateGroupSettingsSchema, patchGroupSchema, resyncSchema, replaySchema, pairingActionSchema, guildConfigOverrideSchema, guildConfigCache, GUILD_CONFIG_TTL, presenceSchema;
|
|
366283
366524
|
var init_instances3 = __esm(() => {
|
|
366284
366525
|
init_dist6();
|
|
366285
366526
|
init_src();
|
|
@@ -366346,6 +366587,8 @@ var init_instances3 = __esm(() => {
|
|
|
366346
366587
|
agentGatePrompt: exports_external.string().nullable().default(null).describe("Custom prompt for response gate (null = use default)"),
|
|
366347
366588
|
telegramBotToken: exports_external.string().optional().nullable().describe("Telegram bot token (persisted for reconnection)"),
|
|
366348
366589
|
discordBotToken: exports_external.string().optional().nullable().describe("Discord bot token (persisted for reconnection)"),
|
|
366590
|
+
slackUserToken: exports_external.string().optional().nullable().describe("Slack user token xoxp (required when slackAuthMode is user)"),
|
|
366591
|
+
slackAuthMode: exports_external.enum(["bot", "user"]).optional().nullable().describe("Slack identity for outbound actions"),
|
|
366349
366592
|
slackBotToken: exports_external.string().optional().nullable().describe("Slack bot token (persisted for reconnection)"),
|
|
366350
366593
|
slackAppToken: exports_external.string().optional().nullable().describe("Slack app token (persisted for reconnection)"),
|
|
366351
366594
|
slackSigningSecret: exports_external.string().optional().nullable().describe("Slack signing secret (persisted for reconnection)"),
|
|
@@ -366377,6 +366620,7 @@ var init_instances3 = __esm(() => {
|
|
|
366377
366620
|
allowFirstParty: exports_external.boolean().default(false).describe(`When true, this instance processes (does not drop) inbound messages whose sender matches another active instance owner. Lets an "assistant" instance reply to messages from the operator's own personal number. Default: false (loop-protection drop).`)
|
|
366378
366621
|
});
|
|
366379
366622
|
updateInstanceSchema = createInstanceSchema.partial().extend({
|
|
366623
|
+
profileMetadata: exports_external.record(exports_external.unknown()).nullable().optional(),
|
|
366380
366624
|
agentId: exports_external.string().uuid().nullable().optional(),
|
|
366381
366625
|
agentErrorMessages: exports_external.array(exports_external.string()).nullable().optional(),
|
|
366382
366626
|
agentReplyFilter: agentReplyFilterSchema.nullable().optional(),
|
|
@@ -366384,6 +366628,8 @@ var init_instances3 = __esm(() => {
|
|
|
366384
366628
|
telegramBotToken: exports_external.string().nullable().optional(),
|
|
366385
366629
|
discordBotToken: exports_external.string().nullable().optional(),
|
|
366386
366630
|
slackBotToken: exports_external.string().nullable().optional(),
|
|
366631
|
+
slackUserToken: exports_external.string().nullable().optional(),
|
|
366632
|
+
slackAuthMode: exports_external.enum(["bot", "user"]).nullable().optional(),
|
|
366387
366633
|
slackAppToken: exports_external.string().nullable().optional(),
|
|
366388
366634
|
gupshupCallbackUrl: exports_external.string().nullable().optional(),
|
|
366389
366635
|
gupshupAuthToken: exports_external.string().nullable().optional(),
|
|
@@ -366418,6 +366664,7 @@ var init_instances3 = __esm(() => {
|
|
|
366418
366664
|
"telegramBotToken",
|
|
366419
366665
|
"discordBotToken",
|
|
366420
366666
|
"slackBotToken",
|
|
366667
|
+
"slackUserToken",
|
|
366421
366668
|
"slackAppToken",
|
|
366422
366669
|
"slackSigningSecret",
|
|
366423
366670
|
"gupshupAuthToken",
|
|
@@ -366509,8 +366756,11 @@ var init_instances3 = __esm(() => {
|
|
|
366509
366756
|
forceNewQr: true,
|
|
366510
366757
|
token: connectToken,
|
|
366511
366758
|
telegramReactionLevel: instance4.telegramReactionLevel,
|
|
366759
|
+
slackUserToken: instance4.slackUserToken,
|
|
366760
|
+
slackAuthMode: instance4.slackAuthMode,
|
|
366512
366761
|
slackAppToken: instance4.slackAppToken,
|
|
366513
366762
|
slackSigningSecret: instance4.slackSigningSecret,
|
|
366763
|
+
profileMetadata: instance4.profileMetadata,
|
|
366514
366764
|
gupshupCallbackUrl: instance4.gupshupCallbackUrl,
|
|
366515
366765
|
gupshupAuthToken: instance4.gupshupAuthToken,
|
|
366516
366766
|
gupshupEventId: instance4.gupshupEventId,
|
|
@@ -366656,6 +366906,18 @@ var init_instances3 = __esm(() => {
|
|
|
366656
366906
|
pairingCodeSchema = exports_external.object({
|
|
366657
366907
|
phoneNumber: exports_external.string().min(10).max(20).describe("Phone number in international format (e.g., +5511999999999)")
|
|
366658
366908
|
});
|
|
366909
|
+
passkeyBase64UrlSchema = exports_external.string().min(1).regex(/^[A-Za-z0-9_-]+$/);
|
|
366910
|
+
passkeyCredentialSchema = exports_external.object({
|
|
366911
|
+
id: passkeyBase64UrlSchema,
|
|
366912
|
+
rawId: passkeyBase64UrlSchema,
|
|
366913
|
+
type: exports_external.literal("public-key"),
|
|
366914
|
+
response: exports_external.object({
|
|
366915
|
+
clientDataJSON: passkeyBase64UrlSchema,
|
|
366916
|
+
authenticatorData: passkeyBase64UrlSchema,
|
|
366917
|
+
signature: passkeyBase64UrlSchema,
|
|
366918
|
+
userHandle: passkeyBase64UrlSchema.nullable()
|
|
366919
|
+
})
|
|
366920
|
+
});
|
|
366659
366921
|
instancesRoutes.post("/:id/pair", instanceAccess2, zValidator("json", pairingCodeSchema), async (c) => {
|
|
366660
366922
|
const id = c.req.param("id");
|
|
366661
366923
|
const { phoneNumber } = c.req.valid("json");
|
|
@@ -366691,9 +366953,59 @@ var init_instances3 = __esm(() => {
|
|
|
366691
366953
|
return c.json({ error: { code: "PAIRING_FAILED", message: message2 } }, 500);
|
|
366692
366954
|
}
|
|
366693
366955
|
});
|
|
366956
|
+
instancesRoutes.get("/:id/passkey", instanceAccess2, async (c) => {
|
|
366957
|
+
const id = c.req.param("id");
|
|
366958
|
+
const services = c.get("services");
|
|
366959
|
+
const channelRegistry2 = c.get("channelRegistry");
|
|
366960
|
+
const instance4 = await services.instances.getById(id);
|
|
366961
|
+
if (!instance4.channel.startsWith("whatsapp")) {
|
|
366962
|
+
return c.json({ error: { code: "INVALID_OPERATION", message: "Passkey is only available for WhatsApp." } }, 400);
|
|
366963
|
+
}
|
|
366964
|
+
const plugin10 = channelRegistry2?.get(instance4.channel);
|
|
366965
|
+
if (!plugin10 || !supportsPasskey(plugin10)) {
|
|
366966
|
+
return c.json({ error: { code: "NOT_SUPPORTED", message: "This WhatsApp connector does not support passkey." } }, 400);
|
|
366967
|
+
}
|
|
366968
|
+
return c.json({ data: plugin10.getPasskeyState(id) });
|
|
366969
|
+
});
|
|
366970
|
+
instancesRoutes.post("/:id/passkey/response", instanceAccess2, zValidator("json", passkeyCredentialSchema), async (c) => {
|
|
366971
|
+
const id = c.req.param("id");
|
|
366972
|
+
const services = c.get("services");
|
|
366973
|
+
const channelRegistry2 = c.get("channelRegistry");
|
|
366974
|
+
const instance4 = await services.instances.getById(id);
|
|
366975
|
+
const plugin10 = channelRegistry2?.get(instance4.channel);
|
|
366976
|
+
if (!instance4.channel.startsWith("whatsapp") || !plugin10 || !supportsPasskey(plugin10)) {
|
|
366977
|
+
return c.json({ error: { code: "NOT_SUPPORTED", message: "Passkey is not available for this instance." } }, 400);
|
|
366978
|
+
}
|
|
366979
|
+
try {
|
|
366980
|
+
await plugin10.submitPasskeyResponse(id, c.req.valid("json"));
|
|
366981
|
+
return c.json({ data: { status: "submitted" } });
|
|
366982
|
+
} catch (error3) {
|
|
366983
|
+
const message2 = error3 instanceof Error ? error3.message : "Unable to submit passkey response.";
|
|
366984
|
+
return c.json({ error: { code: "PASSKEY_FAILED", message: message2 } }, 500);
|
|
366985
|
+
}
|
|
366986
|
+
});
|
|
366987
|
+
instancesRoutes.post("/:id/passkey/confirm", instanceAccess2, async (c) => {
|
|
366988
|
+
const id = c.req.param("id");
|
|
366989
|
+
const services = c.get("services");
|
|
366990
|
+
const channelRegistry2 = c.get("channelRegistry");
|
|
366991
|
+
const instance4 = await services.instances.getById(id);
|
|
366992
|
+
const plugin10 = channelRegistry2?.get(instance4.channel);
|
|
366993
|
+
if (!instance4.channel.startsWith("whatsapp") || !plugin10 || !supportsPasskey(plugin10)) {
|
|
366994
|
+
return c.json({ error: { code: "NOT_SUPPORTED", message: "Passkey is not available for this instance." } }, 400);
|
|
366995
|
+
}
|
|
366996
|
+
try {
|
|
366997
|
+
await plugin10.confirmPasskey(id);
|
|
366998
|
+
return c.json({ data: { status: "confirmed" } });
|
|
366999
|
+
} catch (error3) {
|
|
367000
|
+
const message2 = error3 instanceof Error ? error3.message : "Unable to confirm passkey pairing.";
|
|
367001
|
+
return c.json({ error: { code: "PASSKEY_FAILED", message: message2 } }, 500);
|
|
367002
|
+
}
|
|
367003
|
+
});
|
|
366694
367004
|
connectInstanceSchema = exports_external.object({
|
|
366695
367005
|
token: exports_external.string().optional().describe("Bot token for Discord/Telegram instances"),
|
|
366696
367006
|
slackBotToken: exports_external.string().optional().describe("Slack bot token (xoxb-...)"),
|
|
367007
|
+
slackUserToken: exports_external.string().optional().describe("Slack user token (xoxp-...), for authMode user"),
|
|
367008
|
+
slackAuthMode: exports_external.enum(["bot", "user"]).optional().describe("Act as the bot (default) or as the authorizing user"),
|
|
366697
367009
|
slackAppToken: exports_external.string().optional().describe("Slack app-level token (xapp-...)"),
|
|
366698
367010
|
slackSigningSecret: exports_external.string().optional().describe("Slack signing secret"),
|
|
366699
367011
|
forceNewQr: exports_external.boolean().optional().describe("Force new QR code for WhatsApp (re-authentication)"),
|
|
@@ -366787,11 +367099,6 @@ var init_instances3 = __esm(() => {
|
|
|
366787
367099
|
restartOptions.token = restartToken;
|
|
366788
367100
|
if (instance4.channel === "telegram") {
|
|
366789
367101
|
restartOptions.telegramReactionLevel = instance4.telegramReactionLevel;
|
|
366790
|
-
} else if (instance4.channel === "slack") {
|
|
366791
|
-
if (restartToken)
|
|
366792
|
-
restartOptions.botToken = restartToken;
|
|
366793
|
-
if (instance4.slackAppToken)
|
|
366794
|
-
restartOptions.appToken = instance4.slackAppToken;
|
|
366795
367102
|
}
|
|
366796
367103
|
if (instance4.channel === "slack") {
|
|
366797
367104
|
applySlackConnectOptions(restartOptions, instance4);
|
|
@@ -369496,8 +369803,8 @@ var init_media2 = __esm(() => {
|
|
|
369496
369803
|
return c.json({ error: { code: "INVALID_PATH", message: "No path specified" } }, 400);
|
|
369497
369804
|
}
|
|
369498
369805
|
const pathSegments = path3.split("/");
|
|
369499
|
-
for (const
|
|
369500
|
-
if (!isValidPathComponent(
|
|
369806
|
+
for (const segment2 of pathSegments) {
|
|
369807
|
+
if (!isValidPathComponent(segment2)) {
|
|
369501
369808
|
return c.json({ error: { code: "INVALID_PATH", message: "Invalid path" } }, 400);
|
|
369502
369809
|
}
|
|
369503
369810
|
}
|
|
@@ -369685,6 +369992,13 @@ async function resolveRecipient(to, channelType, services) {
|
|
|
369685
369992
|
recoverable: false
|
|
369686
369993
|
});
|
|
369687
369994
|
}
|
|
369995
|
+
async function persistStarState(services, instanceId, channelExternalId, messageExternalId, starred) {
|
|
369996
|
+
try {
|
|
369997
|
+
const chat2 = await services.chats.getByExternalId(instanceId, channelExternalId);
|
|
369998
|
+
if (chat2)
|
|
369999
|
+
await services.messages.setStarred(chat2.id, messageExternalId, starred);
|
|
370000
|
+
} catch {}
|
|
370001
|
+
}
|
|
369688
370002
|
async function getPluginForInstance2(services, channelRegistry2, instanceId, requiredCapability) {
|
|
369689
370003
|
const instance4 = await services.instances.getById(instanceId);
|
|
369690
370004
|
if (!channelRegistry2) {
|
|
@@ -369850,7 +370164,7 @@ async function computeCloseContactTerminalState(db2, chatUuid, outcome, auditRow
|
|
|
369850
370164
|
return { terminal: false, escalated: false, closeUntil };
|
|
369851
370165
|
}
|
|
369852
370166
|
function hasPresenceStatusSender(plugin10) {
|
|
369853
|
-
return typeof plugin10 === "object" && plugin10 !== null &&
|
|
370167
|
+
return typeof plugin10 === "object" && plugin10 !== null && typeof plugin10.sendPresenceStatus === "function";
|
|
369854
370168
|
}
|
|
369855
370169
|
async function verifyMessageInstanceOwnership(services, message2, instanceId) {
|
|
369856
370170
|
if (!message2.chatId)
|
|
@@ -369873,7 +370187,7 @@ async function resolveChannelMessageId(services, messageId, instanceId) {
|
|
|
369873
370187
|
log117.debug("Resolved internal UUID to external ID", { messageId, externalId: message2.externalId });
|
|
369874
370188
|
return message2.externalId;
|
|
369875
370189
|
}
|
|
369876
|
-
var log117, mediaDownloadLog, messagesRoutes, MIME_BY_EXTENSION, DEFAULT_MIME_BY_MEDIA_TYPE, UUID_REGEX2, MessageSourceSchema, MessageTypeSchema, MessageStatusSchema, DeliveryStatusSchema, listQuerySchema14, createMessageSchema, updateMessageSchema, recordEditSchema, addReactionSchema, removeReactionSchema, updateDeliveryStatusSchema, MentionSchema, sendTextSchema, sendMediaSchema, sendReactionSchema, sendStickerSchema, sendContactSchema, sendLocationSchema, sendHandoffSchema, sendCloseContactSchema, messageRefSchema, _mediaStorageForDownload = null, sendTtsSchema, forwardMessageSchema, sendPresenceSchema, markMessageReadSchema, markBatchReadSchema, sendPollSchema, sendEmbedSchema, editMessageChannelSchema, deleteMessageChannelSchema, starMessageSchema;
|
|
370190
|
+
var log117, mediaDownloadLog, messagesRoutes, MIME_BY_EXTENSION, DEFAULT_MIME_BY_MEDIA_TYPE, UUID_REGEX2, permalinkQuerySchema, MessageSourceSchema, MessageTypeSchema, MessageStatusSchema, DeliveryStatusSchema, listQuerySchema14, createMessageSchema, updateMessageSchema, recordEditSchema, addReactionSchema, removeReactionSchema, updateDeliveryStatusSchema, MentionSchema, sendTextSchema, sendMediaSchema, sendReactionSchema, sendStickerSchema, sendContactSchema, sendLocationSchema, sendHandoffSchema, sendCloseContactSchema, messageRefSchema, _mediaStorageForDownload = null, sendTtsSchema, forwardMessageSchema, sendPresenceSchema, markMessageReadSchema, markBatchReadSchema, sendPollSchema, sendEmbedSchema, editMessageChannelSchema, deleteMessageChannelSchema, starMessageSchema;
|
|
369877
370191
|
var init_messages5 = __esm(() => {
|
|
369878
370192
|
init_dist6();
|
|
369879
370193
|
init_src2();
|
|
@@ -369916,6 +370230,42 @@ var init_messages5 = __esm(() => {
|
|
|
369916
370230
|
document: "application/octet-stream"
|
|
369917
370231
|
};
|
|
369918
370232
|
UUID_REGEX2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
370233
|
+
permalinkQuerySchema = exports_external.object({
|
|
370234
|
+
instanceId: exports_external.string().uuid(),
|
|
370235
|
+
channelId: exports_external.string().min(1).describe("Platform chat/channel id containing the message")
|
|
370236
|
+
});
|
|
370237
|
+
messagesRoutes.get("/:id/permalink", zValidator("query", permalinkQuerySchema), async (c) => {
|
|
370238
|
+
const messageId = c.req.param("id");
|
|
370239
|
+
const { instanceId, channelId } = c.req.valid("query");
|
|
370240
|
+
checkInstanceAccess2(c.get("apiKey"), instanceId);
|
|
370241
|
+
const services = c.get("services");
|
|
370242
|
+
const chat2 = await services.chats.getByExternalId(instanceId, channelId);
|
|
370243
|
+
const stored = chat2 ? await services.messages.getByExternalId(chat2.id, messageId) : null;
|
|
370244
|
+
if (stored?.permalink) {
|
|
370245
|
+
return c.json({ data: { messageId, permalink: stored.permalink, cached: true } });
|
|
370246
|
+
}
|
|
370247
|
+
const { instance: instance4, plugin: plugin10 } = await getPluginForInstance2(services, c.get("channelRegistry"), instanceId);
|
|
370248
|
+
if (typeof plugin10.getPermalink !== "function") {
|
|
370249
|
+
throw new OmniError({
|
|
370250
|
+
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
370251
|
+
message: `Channel ${instance4.channel} cannot resolve permalinks`,
|
|
370252
|
+
context: { channelType: instance4.channel },
|
|
370253
|
+
recoverable: false
|
|
370254
|
+
});
|
|
370255
|
+
}
|
|
370256
|
+
const permalink = await plugin10.getPermalink(instanceId, channelId, messageId);
|
|
370257
|
+
if (!permalink) {
|
|
370258
|
+
throw new OmniError({
|
|
370259
|
+
code: ERROR_CODES.NOT_FOUND,
|
|
370260
|
+
message: `No permalink available for message ${messageId}`,
|
|
370261
|
+
recoverable: false
|
|
370262
|
+
});
|
|
370263
|
+
}
|
|
370264
|
+
if (chat2) {
|
|
370265
|
+
await services.messages.setPermalink(chat2.id, messageId, permalink).catch(() => {});
|
|
370266
|
+
}
|
|
370267
|
+
return c.json({ data: { messageId, permalink, cached: false } });
|
|
370268
|
+
});
|
|
369919
370269
|
MessageSourceSchema = exports_external.enum(["realtime", "sync", "api", "import"]);
|
|
369920
370270
|
MessageTypeSchema = exports_external.enum([
|
|
369921
370271
|
"text",
|
|
@@ -370928,7 +371278,7 @@ var init_messages5 = __esm(() => {
|
|
|
370928
371278
|
stability: data.stability,
|
|
370929
371279
|
similarityBoost: data.similarityBoost
|
|
370930
371280
|
});
|
|
370931
|
-
if (
|
|
371281
|
+
if (typeof plugin10.sendTyping === "function") {
|
|
370932
371282
|
const presenceDuration = data.presenceDelay ?? Math.min(ttsResult.durationMs, 15000);
|
|
370933
371283
|
try {
|
|
370934
371284
|
await plugin10.sendTyping(data.instanceId, resolvedTo, presenceDuration);
|
|
@@ -371105,7 +371455,7 @@ var init_messages5 = __esm(() => {
|
|
|
371105
371455
|
status,
|
|
371106
371456
|
loadingMessages
|
|
371107
371457
|
}) : await (async () => {
|
|
371108
|
-
if (
|
|
371458
|
+
if (typeof plugin10.sendTyping !== "function") {
|
|
371109
371459
|
throw new OmniError({
|
|
371110
371460
|
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
371111
371461
|
message: `Channel ${instance4.channel} plugin does not implement sendTyping`,
|
|
@@ -371157,7 +371507,7 @@ var init_messages5 = __esm(() => {
|
|
|
371157
371507
|
});
|
|
371158
371508
|
}
|
|
371159
371509
|
const { instance: instance4, plugin: plugin10 } = await getPluginForInstance2(services, channelRegistry2, instanceId, "canReceiveReadReceipts");
|
|
371160
|
-
if (
|
|
371510
|
+
if (typeof plugin10.markAsRead !== "function") {
|
|
371161
371511
|
throw new OmniError({
|
|
371162
371512
|
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
371163
371513
|
message: `Channel ${instance4.channel} plugin does not implement markAsRead`,
|
|
@@ -371183,7 +371533,7 @@ var init_messages5 = __esm(() => {
|
|
|
371183
371533
|
const channelRegistry2 = c.get("channelRegistry");
|
|
371184
371534
|
checkInstanceAccess2(c.get("apiKey"), instanceId);
|
|
371185
371535
|
const { instance: instance4, plugin: plugin10 } = await getPluginForInstance2(services, channelRegistry2, instanceId, "canReceiveReadReceipts");
|
|
371186
|
-
if (
|
|
371536
|
+
if (typeof plugin10.markAsRead !== "function") {
|
|
371187
371537
|
throw new OmniError({
|
|
371188
371538
|
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
371189
371539
|
message: `Channel ${instance4.channel} plugin does not implement markAsRead`,
|
|
@@ -371419,7 +371769,7 @@ var init_messages5 = __esm(() => {
|
|
|
371419
371769
|
recoverable: false
|
|
371420
371770
|
});
|
|
371421
371771
|
}
|
|
371422
|
-
if (
|
|
371772
|
+
if (typeof plugin10.editMessage !== "function") {
|
|
371423
371773
|
throw new OmniError({
|
|
371424
371774
|
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
371425
371775
|
message: `Channel ${instance4.channel} plugin does not implement editMessage`,
|
|
@@ -371475,7 +371825,7 @@ var init_messages5 = __esm(() => {
|
|
|
371475
371825
|
recoverable: false
|
|
371476
371826
|
});
|
|
371477
371827
|
}
|
|
371478
|
-
if (
|
|
371828
|
+
if (typeof plugin10.deleteMessage !== "function") {
|
|
371479
371829
|
throw new OmniError({
|
|
371480
371830
|
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
371481
371831
|
message: `Channel ${instance4.channel} plugin does not implement deleteMessage`,
|
|
@@ -371518,7 +371868,7 @@ var init_messages5 = __esm(() => {
|
|
|
371518
371868
|
recoverable: false
|
|
371519
371869
|
});
|
|
371520
371870
|
}
|
|
371521
|
-
if (
|
|
371871
|
+
if (typeof plugin10.starMessage !== "function") {
|
|
371522
371872
|
throw new OmniError({
|
|
371523
371873
|
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
371524
371874
|
message: `Channel ${instance4.channel} does not support starring messages`,
|
|
@@ -371527,6 +371877,7 @@ var init_messages5 = __esm(() => {
|
|
|
371527
371877
|
});
|
|
371528
371878
|
}
|
|
371529
371879
|
await plugin10.starMessage(instanceId, channelId, messageId, true, fromMe);
|
|
371880
|
+
await persistStarState(services, instanceId, channelId, messageId, true);
|
|
371530
371881
|
return c.json({
|
|
371531
371882
|
success: true,
|
|
371532
371883
|
data: { messageId, starred: true }
|
|
@@ -371555,7 +371906,7 @@ var init_messages5 = __esm(() => {
|
|
|
371555
371906
|
recoverable: false
|
|
371556
371907
|
});
|
|
371557
371908
|
}
|
|
371558
|
-
if (
|
|
371909
|
+
if (typeof plugin10.starMessage !== "function") {
|
|
371559
371910
|
throw new OmniError({
|
|
371560
371911
|
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
371561
371912
|
message: `Channel ${instance4.channel} does not support starring messages`,
|
|
@@ -371564,6 +371915,7 @@ var init_messages5 = __esm(() => {
|
|
|
371564
371915
|
});
|
|
371565
371916
|
}
|
|
371566
371917
|
await plugin10.starMessage(instanceId, channelId, messageId, false, fromMe);
|
|
371918
|
+
await persistStarState(services, instanceId, channelId, messageId, false);
|
|
371567
371919
|
return c.json({
|
|
371568
371920
|
success: true,
|
|
371569
371921
|
data: { messageId, starred: false }
|
|
@@ -372029,15 +372381,345 @@ var init_providers4 = __esm(() => {
|
|
|
372029
372381
|
});
|
|
372030
372382
|
});
|
|
372031
372383
|
|
|
372384
|
+
// ../api/src/services/scheduled-messages.ts
|
|
372385
|
+
function accumulate(total, pass) {
|
|
372386
|
+
total.scanned += pass.scanned;
|
|
372387
|
+
total.sent += pass.sent;
|
|
372388
|
+
total.failed += pass.failed;
|
|
372389
|
+
}
|
|
372390
|
+
function parseOutgoingContent(raw2, scheduledMessageId) {
|
|
372391
|
+
if (!raw2 || typeof raw2 !== "object" || Array.isArray(raw2)) {
|
|
372392
|
+
throw callerError(`Scheduled message ${scheduledMessageId} has non-object content`);
|
|
372393
|
+
}
|
|
372394
|
+
const type = raw2.type;
|
|
372395
|
+
if (typeof type !== "string" || type.length === 0) {
|
|
372396
|
+
throw callerError(`Scheduled message ${scheduledMessageId} has content without a 'type' discriminant`);
|
|
372397
|
+
}
|
|
372398
|
+
return raw2;
|
|
372399
|
+
}
|
|
372400
|
+
function callerError(message2) {
|
|
372401
|
+
return new OmniError({ code: ERROR_CODES.VALIDATION, message: message2, recoverable: false });
|
|
372402
|
+
}
|
|
372403
|
+
|
|
372404
|
+
class ScheduledMessageService {
|
|
372405
|
+
db;
|
|
372406
|
+
resolvePlugin;
|
|
372407
|
+
logger;
|
|
372408
|
+
authPlaneDb;
|
|
372409
|
+
warnedMissingAuthPlane = false;
|
|
372410
|
+
constructor(db2, resolvePlugin, logger7) {
|
|
372411
|
+
this.db = db2;
|
|
372412
|
+
this.resolvePlugin = resolvePlugin;
|
|
372413
|
+
this.logger = logger7 ?? createLogger("services:scheduled-messages");
|
|
372414
|
+
}
|
|
372415
|
+
async schedule(input) {
|
|
372416
|
+
if (input.sendAt.getTime() <= Date.now()) {
|
|
372417
|
+
throw callerError(`sendAt must be in the future (got ${input.sendAt.toISOString()})`);
|
|
372418
|
+
}
|
|
372419
|
+
parseOutgoingContent(input.content, "(new)");
|
|
372420
|
+
const plugin10 = await this.resolvePlugin(input.instanceId);
|
|
372421
|
+
if (!plugin10) {
|
|
372422
|
+
throw new Error(`No channel plugin for instance ${input.instanceId}`);
|
|
372423
|
+
}
|
|
372424
|
+
const native = plugin10.capabilities.canScheduleMessage === true && typeof plugin10.scheduleMessage === "function";
|
|
372425
|
+
const deliveryMode = native ? "platform" : "local";
|
|
372426
|
+
const maxAhead = plugin10.capabilities.maxScheduleAheadMs;
|
|
372427
|
+
if (native && typeof maxAhead === "number" && input.sendAt.getTime() - Date.now() > maxAhead) {
|
|
372428
|
+
throw callerError(`sendAt exceeds what ${plugin10.id} accepts natively (${Math.round(maxAhead / 86400000)} days ahead).`);
|
|
372429
|
+
}
|
|
372430
|
+
let externalScheduledId;
|
|
372431
|
+
if (native) {
|
|
372432
|
+
externalScheduledId = await plugin10.scheduleMessage?.(input.instanceId, this.toOutgoingMessage(input), input.sendAt);
|
|
372433
|
+
}
|
|
372434
|
+
try {
|
|
372435
|
+
const [row] = await this.db.insert(scheduledMessages).values({
|
|
372436
|
+
instanceId: input.instanceId,
|
|
372437
|
+
chatExternalId: input.chatExternalId,
|
|
372438
|
+
threadExternalId: input.threadExternalId,
|
|
372439
|
+
isThreadBroadcast: input.isThreadBroadcast ?? false,
|
|
372440
|
+
content: input.content,
|
|
372441
|
+
sendAt: input.sendAt,
|
|
372442
|
+
deliveryMode,
|
|
372443
|
+
status: "pending",
|
|
372444
|
+
externalScheduledId,
|
|
372445
|
+
createdByAgentId: input.createdByAgentId
|
|
372446
|
+
}).returning();
|
|
372447
|
+
if (!row)
|
|
372448
|
+
throw new Error("Failed to persist scheduled message");
|
|
372449
|
+
return row;
|
|
372450
|
+
} catch (error3) {
|
|
372451
|
+
if (native && externalScheduledId) {
|
|
372452
|
+
try {
|
|
372453
|
+
await plugin10.cancelScheduledMessage?.(input.instanceId, input.chatExternalId, externalScheduledId);
|
|
372454
|
+
} catch (cancelError) {
|
|
372455
|
+
this.logger.warn("Failed to roll back native schedule after a row-insert failure", {
|
|
372456
|
+
instanceId: input.instanceId,
|
|
372457
|
+
externalScheduledId,
|
|
372458
|
+
error: cancelError instanceof Error ? cancelError.message : String(cancelError)
|
|
372459
|
+
});
|
|
372460
|
+
}
|
|
372461
|
+
}
|
|
372462
|
+
throw error3;
|
|
372463
|
+
}
|
|
372464
|
+
}
|
|
372465
|
+
async cancel(id) {
|
|
372466
|
+
const row = await this.getById(id);
|
|
372467
|
+
if (!row)
|
|
372468
|
+
return null;
|
|
372469
|
+
if (row.status !== "pending")
|
|
372470
|
+
return row;
|
|
372471
|
+
if (row.deliveryMode === "platform" && row.externalScheduledId) {
|
|
372472
|
+
const plugin10 = await this.resolvePlugin(row.instanceId);
|
|
372473
|
+
try {
|
|
372474
|
+
await plugin10?.cancelScheduledMessage?.(row.instanceId, row.chatExternalId, row.externalScheduledId);
|
|
372475
|
+
} catch (error3) {
|
|
372476
|
+
this.logger.warn("Platform refused the cancel; marking canceled locally anyway", {
|
|
372477
|
+
scheduledMessageId: id,
|
|
372478
|
+
error: error3 instanceof Error ? error3.message : String(error3)
|
|
372479
|
+
});
|
|
372480
|
+
}
|
|
372481
|
+
}
|
|
372482
|
+
const [updated] = await this.db.update(scheduledMessages).set({ status: "canceled", canceledAt: new Date, updatedAt: new Date }).where(and2(eq(scheduledMessages.id, id), eq(scheduledMessages.status, "pending"))).returning();
|
|
372483
|
+
return updated ?? row;
|
|
372484
|
+
}
|
|
372485
|
+
async getById(id) {
|
|
372486
|
+
const [row] = await this.db.select().from(scheduledMessages).where(eq(scheduledMessages.id, id)).limit(1);
|
|
372487
|
+
return row ?? null;
|
|
372488
|
+
}
|
|
372489
|
+
async listPending(instanceId, limit2 = 100) {
|
|
372490
|
+
return this.db.select().from(scheduledMessages).where(and2(eq(scheduledMessages.instanceId, instanceId), eq(scheduledMessages.status, "pending"))).orderBy(asc(scheduledMessages.sendAt)).limit(limit2);
|
|
372491
|
+
}
|
|
372492
|
+
setAuthPlane(db2) {
|
|
372493
|
+
this.authPlaneDb = db2;
|
|
372494
|
+
}
|
|
372495
|
+
async sweep() {
|
|
372496
|
+
if (!isMultitenancyEnabled()) {
|
|
372497
|
+
return this.sweepWorld({ kind: "all" });
|
|
372498
|
+
}
|
|
372499
|
+
const totals = { scanned: 0, sent: 0, failed: 0 };
|
|
372500
|
+
if (this.authPlaneDb) {
|
|
372501
|
+
for (const tenantId of await enumerateActiveWorkTenants(this.authPlaneDb)) {
|
|
372502
|
+
try {
|
|
372503
|
+
accumulate(totals, await this.sweepWorld({ kind: "tenant", tenantId }));
|
|
372504
|
+
} catch (error3) {
|
|
372505
|
+
this.logger.warn("scheduled-message sweeper: tenant pass failed", {
|
|
372506
|
+
tenantId,
|
|
372507
|
+
error: error3 instanceof Error ? error3.message : String(error3)
|
|
372508
|
+
});
|
|
372509
|
+
}
|
|
372510
|
+
}
|
|
372511
|
+
} else if (!this.warnedMissingAuthPlane) {
|
|
372512
|
+
this.warnedMissingAuthPlane = true;
|
|
372513
|
+
this.logger.warn("scheduled-message sweeper: multitenancy is enabled but no auth-plane connection was injected \u2014 tenant rows will not be swept");
|
|
372514
|
+
}
|
|
372515
|
+
if (resolveEnforcementMode(process.env) !== "enforced") {
|
|
372516
|
+
accumulate(totals, await this.sweepWorld({ kind: "legacy-rows" }));
|
|
372517
|
+
}
|
|
372518
|
+
return totals;
|
|
372519
|
+
}
|
|
372520
|
+
worldPredicate(world) {
|
|
372521
|
+
if (world.kind === "tenant") {
|
|
372522
|
+
return [
|
|
372523
|
+
sql`EXISTS (SELECT 1 FROM ${instances} WHERE ${instances.id} = ${scheduledMessages.instanceId} AND ${instances.tenantId} = ${world.tenantId})`
|
|
372524
|
+
];
|
|
372525
|
+
}
|
|
372526
|
+
if (world.kind === "legacy-rows") {
|
|
372527
|
+
return [
|
|
372528
|
+
sql`EXISTS (SELECT 1 FROM ${instances} WHERE ${instances.id} = ${scheduledMessages.instanceId} AND ${instances.tenantId} IS NULL)`
|
|
372529
|
+
];
|
|
372530
|
+
}
|
|
372531
|
+
return [];
|
|
372532
|
+
}
|
|
372533
|
+
async sweepWorld(world) {
|
|
372534
|
+
const stats = { scanned: 0, sent: 0, failed: 0 };
|
|
372535
|
+
const now = new Date;
|
|
372536
|
+
const claimCutoff = new Date(now.getTime() - CLAIM_LEASE_MS);
|
|
372537
|
+
const due = await this.db.transaction(async (tx) => {
|
|
372538
|
+
const locked = await tx.select({ id: scheduledMessages.id }).from(scheduledMessages).where(and2(eq(scheduledMessages.deliveryMode, "local"), lte(scheduledMessages.sendAt, now), or2(eq(scheduledMessages.status, "pending"), and2(eq(scheduledMessages.status, "sending"), lte(scheduledMessages.updatedAt, claimCutoff))), ...this.worldPredicate(world))).orderBy(asc(scheduledMessages.sendAt)).limit(MAX_PER_TICK).for("update", { skipLocked: true });
|
|
372539
|
+
if (locked.length === 0)
|
|
372540
|
+
return [];
|
|
372541
|
+
return tx.update(scheduledMessages).set({ status: "sending", updatedAt: now }).where(inArray(scheduledMessages.id, locked.map((r) => r.id))).returning();
|
|
372542
|
+
});
|
|
372543
|
+
stats.scanned = due.length;
|
|
372544
|
+
for (const row of due) {
|
|
372545
|
+
try {
|
|
372546
|
+
const plugin10 = await this.resolvePlugin(row.instanceId);
|
|
372547
|
+
if (!plugin10)
|
|
372548
|
+
throw new Error(`No channel plugin for instance ${row.instanceId}`);
|
|
372549
|
+
const result = await plugin10.sendMessage(row.instanceId, {
|
|
372550
|
+
to: row.chatExternalId,
|
|
372551
|
+
threadId: row.threadExternalId ?? undefined,
|
|
372552
|
+
content: parseOutgoingContent(row.content, row.id),
|
|
372553
|
+
metadata: { isThreadBroadcast: row.isThreadBroadcast }
|
|
372554
|
+
});
|
|
372555
|
+
if (!result.success) {
|
|
372556
|
+
throw new Error(result.error ?? "sendMessage reported failure without an error");
|
|
372557
|
+
}
|
|
372558
|
+
await this.db.update(scheduledMessages).set({
|
|
372559
|
+
status: "sent",
|
|
372560
|
+
sentAt: new Date,
|
|
372561
|
+
sentExternalId: result.messageId,
|
|
372562
|
+
attemptCount: row.attemptCount + 1,
|
|
372563
|
+
updatedAt: new Date
|
|
372564
|
+
}).where(and2(eq(scheduledMessages.id, row.id), eq(scheduledMessages.status, "sending")));
|
|
372565
|
+
stats.sent++;
|
|
372566
|
+
} catch (error3) {
|
|
372567
|
+
const message2 = error3 instanceof Error ? error3.message : String(error3);
|
|
372568
|
+
const attempts = row.attemptCount + 1;
|
|
372569
|
+
const exhausted = attempts >= MAX_ATTEMPTS;
|
|
372570
|
+
await this.db.update(scheduledMessages).set({
|
|
372571
|
+
status: exhausted ? "failed" : "pending",
|
|
372572
|
+
failedAt: exhausted ? new Date : null,
|
|
372573
|
+
lastError: message2,
|
|
372574
|
+
attemptCount: attempts,
|
|
372575
|
+
updatedAt: new Date
|
|
372576
|
+
}).where(and2(eq(scheduledMessages.id, row.id), eq(scheduledMessages.status, "sending")));
|
|
372577
|
+
stats.failed++;
|
|
372578
|
+
this.logger.warn("Scheduled message delivery failed", {
|
|
372579
|
+
scheduledMessageId: row.id,
|
|
372580
|
+
attempts,
|
|
372581
|
+
exhausted,
|
|
372582
|
+
error: message2
|
|
372583
|
+
});
|
|
372584
|
+
}
|
|
372585
|
+
}
|
|
372586
|
+
return stats;
|
|
372587
|
+
}
|
|
372588
|
+
async pruneTerminal(olderThan) {
|
|
372589
|
+
const result = await this.db.delete(scheduledMessages).where(and2(sql`${scheduledMessages.status} IN ('sent', 'canceled', 'failed')`, lte(scheduledMessages.updatedAt, olderThan))).returning({ id: scheduledMessages.id });
|
|
372590
|
+
return result.length;
|
|
372591
|
+
}
|
|
372592
|
+
toOutgoingMessage(input) {
|
|
372593
|
+
return {
|
|
372594
|
+
to: input.chatExternalId,
|
|
372595
|
+
threadId: input.threadExternalId,
|
|
372596
|
+
content: parseOutgoingContent(input.content, "(new)"),
|
|
372597
|
+
metadata: { isThreadBroadcast: input.isThreadBroadcast ?? false }
|
|
372598
|
+
};
|
|
372599
|
+
}
|
|
372600
|
+
}
|
|
372601
|
+
function createPluginResolver(db2, getPlugin2) {
|
|
372602
|
+
return async (instanceId) => {
|
|
372603
|
+
const [row] = await db2.select({ channel: instances.channel }).from(instances).where(eq(instances.id, instanceId)).limit(1);
|
|
372604
|
+
if (!row)
|
|
372605
|
+
return null;
|
|
372606
|
+
return getPlugin2(row.channel) ?? null;
|
|
372607
|
+
};
|
|
372608
|
+
}
|
|
372609
|
+
var MAX_PER_TICK = 50, MAX_ATTEMPTS = 3, CLAIM_LEASE_MS;
|
|
372610
|
+
var init_scheduled_messages = __esm(() => {
|
|
372611
|
+
init_src();
|
|
372612
|
+
init_src5();
|
|
372613
|
+
init_drizzle_orm();
|
|
372614
|
+
init_periodic_tenant_work();
|
|
372615
|
+
CLAIM_LEASE_MS = 5 * 60000;
|
|
372616
|
+
});
|
|
372617
|
+
|
|
372618
|
+
// ../api/src/routes/v2/scheduled-messages.ts
|
|
372619
|
+
function getService(c) {
|
|
372620
|
+
const db2 = c.get("db");
|
|
372621
|
+
const registry5 = c.get("channelRegistry");
|
|
372622
|
+
if (!registry5) {
|
|
372623
|
+
throw new OmniError({
|
|
372624
|
+
code: ERROR_CODES.CHANNEL_NOT_CONNECTED,
|
|
372625
|
+
message: "Channel registry not available",
|
|
372626
|
+
recoverable: false
|
|
372627
|
+
});
|
|
372628
|
+
}
|
|
372629
|
+
return new ScheduledMessageService(db2, createPluginResolver(db2, (channel5) => registry5.get(channel5)));
|
|
372630
|
+
}
|
|
372631
|
+
var scheduledMessagesRoutes, scheduleSchema, listQuerySchema17;
|
|
372632
|
+
var init_scheduled_messages2 = __esm(() => {
|
|
372633
|
+
init_dist6();
|
|
372634
|
+
init_src();
|
|
372635
|
+
init_dist2();
|
|
372636
|
+
init_zod();
|
|
372637
|
+
init_scheduled_messages();
|
|
372638
|
+
scheduledMessagesRoutes = new Hono2;
|
|
372639
|
+
scheduleSchema = exports_external.object({
|
|
372640
|
+
instanceId: exports_external.string().uuid(),
|
|
372641
|
+
chatId: exports_external.string().min(1).describe("Platform chat/channel id (e.g. Slack C\u2026/D\u2026)"),
|
|
372642
|
+
content: exports_external.record(exports_external.unknown()).describe("OutgoingContent, e.g. { type: 'text', text: 'oi' }"),
|
|
372643
|
+
sendAt: exports_external.string().datetime().describe("ISO-8601 delivery time (UTC-aware)"),
|
|
372644
|
+
threadId: exports_external.string().optional().describe("Post into this thread (Slack thread_ts)"),
|
|
372645
|
+
isThreadBroadcast: exports_external.boolean().optional().describe("Also surface it in the channel (Slack reply_broadcast)")
|
|
372646
|
+
});
|
|
372647
|
+
listQuerySchema17 = exports_external.object({
|
|
372648
|
+
instanceId: exports_external.string().uuid(),
|
|
372649
|
+
limit: exports_external.coerce.number().int().min(1).max(500).default(100)
|
|
372650
|
+
});
|
|
372651
|
+
scheduledMessagesRoutes.post("/", zValidator("json", scheduleSchema), async (c) => {
|
|
372652
|
+
const body = c.req.valid("json");
|
|
372653
|
+
const sendAt = new Date(body.sendAt);
|
|
372654
|
+
try {
|
|
372655
|
+
const row = await getService(c).schedule({
|
|
372656
|
+
instanceId: body.instanceId,
|
|
372657
|
+
chatExternalId: body.chatId,
|
|
372658
|
+
content: body.content,
|
|
372659
|
+
sendAt,
|
|
372660
|
+
threadExternalId: body.threadId,
|
|
372661
|
+
isThreadBroadcast: body.isThreadBroadcast
|
|
372662
|
+
});
|
|
372663
|
+
return c.json({ success: true, data: row }, 201);
|
|
372664
|
+
} catch (error3) {
|
|
372665
|
+
if (error3 instanceof OmniError)
|
|
372666
|
+
throw error3;
|
|
372667
|
+
throw new OmniError({
|
|
372668
|
+
code: ERROR_CODES.UNKNOWN,
|
|
372669
|
+
message: error3 instanceof Error ? error3.message : String(error3),
|
|
372670
|
+
recoverable: false,
|
|
372671
|
+
cause: error3 instanceof Error ? error3 : undefined
|
|
372672
|
+
});
|
|
372673
|
+
}
|
|
372674
|
+
});
|
|
372675
|
+
scheduledMessagesRoutes.get("/", zValidator("query", listQuerySchema17), async (c) => {
|
|
372676
|
+
const { instanceId, limit: limit2 } = c.req.valid("query");
|
|
372677
|
+
const rows = await getService(c).listPending(instanceId, limit2);
|
|
372678
|
+
return c.json({
|
|
372679
|
+
data: rows,
|
|
372680
|
+
meta: {
|
|
372681
|
+
count: rows.length,
|
|
372682
|
+
scope: "scheduled-via-omni"
|
|
372683
|
+
}
|
|
372684
|
+
});
|
|
372685
|
+
});
|
|
372686
|
+
scheduledMessagesRoutes.get("/:id", async (c) => {
|
|
372687
|
+
const row = await getService(c).getById(c.req.param("id"));
|
|
372688
|
+
if (!row) {
|
|
372689
|
+
throw new OmniError({
|
|
372690
|
+
code: ERROR_CODES.NOT_FOUND,
|
|
372691
|
+
message: `Scheduled message ${c.req.param("id")} not found`,
|
|
372692
|
+
recoverable: false
|
|
372693
|
+
});
|
|
372694
|
+
}
|
|
372695
|
+
return c.json({ data: row });
|
|
372696
|
+
});
|
|
372697
|
+
scheduledMessagesRoutes.delete("/:id", async (c) => {
|
|
372698
|
+
const id = c.req.param("id");
|
|
372699
|
+
const row = await getService(c).cancel(id);
|
|
372700
|
+
if (!row) {
|
|
372701
|
+
throw new OmniError({
|
|
372702
|
+
code: ERROR_CODES.NOT_FOUND,
|
|
372703
|
+
message: `Scheduled message ${id} not found`,
|
|
372704
|
+
recoverable: false
|
|
372705
|
+
});
|
|
372706
|
+
}
|
|
372707
|
+
return c.json({
|
|
372708
|
+
success: true,
|
|
372709
|
+
data: { id: row.id, status: row.status, canceledAt: row.canceledAt }
|
|
372710
|
+
});
|
|
372711
|
+
});
|
|
372712
|
+
});
|
|
372713
|
+
|
|
372032
372714
|
// ../api/src/routes/v2/settings.ts
|
|
372033
|
-
var settingsRoutes,
|
|
372715
|
+
var settingsRoutes, listQuerySchema18, setSettingSchema, bulkUpdateSchema, historyQuerySchema;
|
|
372034
372716
|
var init_settings3 = __esm(() => {
|
|
372035
372717
|
init_dist6();
|
|
372036
372718
|
init_dist2();
|
|
372037
372719
|
init_zod();
|
|
372038
372720
|
init_date_query();
|
|
372039
372721
|
settingsRoutes = new Hono2;
|
|
372040
|
-
|
|
372722
|
+
listQuerySchema18 = exports_external.object({
|
|
372041
372723
|
category: exports_external.string().optional()
|
|
372042
372724
|
});
|
|
372043
372725
|
setSettingSchema = exports_external.object({
|
|
@@ -372052,7 +372734,7 @@ var init_settings3 = __esm(() => {
|
|
|
372052
372734
|
limit: exports_external.coerce.number().int().min(1).max(100).default(50),
|
|
372053
372735
|
since: optionalDateParam("since")
|
|
372054
372736
|
});
|
|
372055
|
-
settingsRoutes.get("/", zValidator("query",
|
|
372737
|
+
settingsRoutes.get("/", zValidator("query", listQuerySchema18), async (c) => {
|
|
372056
372738
|
const { category } = c.req.valid("query");
|
|
372057
372739
|
const services = c.get("services");
|
|
372058
372740
|
const settings = await services.settings.list(category);
|
|
@@ -372127,6 +372809,116 @@ var init_settings3 = __esm(() => {
|
|
|
372127
372809
|
});
|
|
372128
372810
|
});
|
|
372129
372811
|
|
|
372812
|
+
// ../api/src/routes/v2/slack.ts
|
|
372813
|
+
function checkInstanceAccess3(apiKey, instanceId) {
|
|
372814
|
+
if (apiKey && !ApiKeyService.instanceAllowed(apiKey.instanceIds, instanceId)) {
|
|
372815
|
+
throw new OmniError({
|
|
372816
|
+
code: ERROR_CODES.FORBIDDEN,
|
|
372817
|
+
message: "API key does not have access to this instance",
|
|
372818
|
+
context: { instanceId },
|
|
372819
|
+
recoverable: false
|
|
372820
|
+
});
|
|
372821
|
+
}
|
|
372822
|
+
}
|
|
372823
|
+
async function getSlackPlugin(c, instanceId) {
|
|
372824
|
+
checkInstanceAccess3(c.get("apiKey"), instanceId);
|
|
372825
|
+
const services = c.get("services");
|
|
372826
|
+
const registry5 = c.get("channelRegistry");
|
|
372827
|
+
const instance4 = await services.instances.getById(instanceId);
|
|
372828
|
+
if (instance4.channel !== "slack") {
|
|
372829
|
+
throw new OmniError({
|
|
372830
|
+
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
372831
|
+
message: `Instance ${instanceId} is a ${instance4.channel} instance; these endpoints are Slack-only`,
|
|
372832
|
+
context: { channelType: instance4.channel },
|
|
372833
|
+
recoverable: false
|
|
372834
|
+
});
|
|
372835
|
+
}
|
|
372836
|
+
if (!registry5) {
|
|
372837
|
+
throw new OmniError({
|
|
372838
|
+
code: ERROR_CODES.CHANNEL_NOT_CONNECTED,
|
|
372839
|
+
message: "Channel registry not available",
|
|
372840
|
+
recoverable: false
|
|
372841
|
+
});
|
|
372842
|
+
}
|
|
372843
|
+
const plugin10 = registry5.get("slack");
|
|
372844
|
+
if (!plugin10) {
|
|
372845
|
+
throw new OmniError({
|
|
372846
|
+
code: ERROR_CODES.CHANNEL_NOT_CONNECTED,
|
|
372847
|
+
message: "Slack plugin not registered",
|
|
372848
|
+
recoverable: false
|
|
372849
|
+
});
|
|
372850
|
+
}
|
|
372851
|
+
return plugin10;
|
|
372852
|
+
}
|
|
372853
|
+
var slackRoutes, openDmSchema, searchSchema;
|
|
372854
|
+
var init_slack = __esm(() => {
|
|
372855
|
+
init_dist6();
|
|
372856
|
+
init_src();
|
|
372857
|
+
init_dist2();
|
|
372858
|
+
init_zod();
|
|
372859
|
+
init_api_keys();
|
|
372860
|
+
slackRoutes = new Hono2;
|
|
372861
|
+
openDmSchema = exports_external.object({
|
|
372862
|
+
instanceId: exports_external.string().uuid(),
|
|
372863
|
+
userId: exports_external.string().min(1).describe("Slack user id (U\u2026) to open a DM with")
|
|
372864
|
+
});
|
|
372865
|
+
searchSchema = exports_external.object({
|
|
372866
|
+
instanceId: exports_external.string().uuid(),
|
|
372867
|
+
query: exports_external.string().min(1),
|
|
372868
|
+
count: exports_external.coerce.number().int().min(1).max(100).default(20),
|
|
372869
|
+
page: exports_external.coerce.number().int().min(1).default(1)
|
|
372870
|
+
});
|
|
372871
|
+
slackRoutes.post("/dm/open", zValidator("json", openDmSchema), async (c) => {
|
|
372872
|
+
const { instanceId, userId } = c.req.valid("json");
|
|
372873
|
+
const plugin10 = await getSlackPlugin(c, instanceId);
|
|
372874
|
+
if (typeof plugin10.openDirectMessage !== "function") {
|
|
372875
|
+
throw new OmniError({
|
|
372876
|
+
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
372877
|
+
message: "Slack plugin does not implement openDirectMessage",
|
|
372878
|
+
recoverable: false
|
|
372879
|
+
});
|
|
372880
|
+
}
|
|
372881
|
+
try {
|
|
372882
|
+
const channelId = await plugin10.openDirectMessage(instanceId, userId);
|
|
372883
|
+
return c.json({ success: true, data: { userId, channelId } });
|
|
372884
|
+
} catch (error3) {
|
|
372885
|
+
throw new OmniError({
|
|
372886
|
+
code: ERROR_CODES.CHANNEL_SEND_FAILED,
|
|
372887
|
+
message: error3 instanceof Error ? error3.message : String(error3),
|
|
372888
|
+
recoverable: true
|
|
372889
|
+
});
|
|
372890
|
+
}
|
|
372891
|
+
});
|
|
372892
|
+
slackRoutes.get("/search", zValidator("query", searchSchema), async (c) => {
|
|
372893
|
+
const { instanceId, query, count: count3, page } = c.req.valid("query");
|
|
372894
|
+
const plugin10 = await getSlackPlugin(c, instanceId);
|
|
372895
|
+
if (typeof plugin10.searchMessages !== "function") {
|
|
372896
|
+
throw new OmniError({
|
|
372897
|
+
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
372898
|
+
message: "Slack plugin does not implement searchMessages",
|
|
372899
|
+
recoverable: false
|
|
372900
|
+
});
|
|
372901
|
+
}
|
|
372902
|
+
try {
|
|
372903
|
+
const matches = await plugin10.searchMessages(instanceId, query, { count: count3, page });
|
|
372904
|
+
return c.json({
|
|
372905
|
+
data: matches,
|
|
372906
|
+
meta: {
|
|
372907
|
+
count: matches.length,
|
|
372908
|
+
page,
|
|
372909
|
+
scope: "authorizing-user"
|
|
372910
|
+
}
|
|
372911
|
+
});
|
|
372912
|
+
} catch (error3) {
|
|
372913
|
+
throw new OmniError({
|
|
372914
|
+
code: ERROR_CODES.CAPABILITY_NOT_SUPPORTED,
|
|
372915
|
+
message: error3 instanceof Error ? error3.message : String(error3),
|
|
372916
|
+
recoverable: false
|
|
372917
|
+
});
|
|
372918
|
+
}
|
|
372919
|
+
});
|
|
372920
|
+
});
|
|
372921
|
+
|
|
372130
372922
|
// ../api/src/routes/v2/templates.ts
|
|
372131
372923
|
function jsonError(message2, code = "BAD_REQUEST", _status = 400) {
|
|
372132
372924
|
return { error: { code, message: message2 } };
|
|
@@ -372151,7 +372943,7 @@ function makeClient(cfg) {
|
|
|
372151
372943
|
function variablesToBodyParameters(variables) {
|
|
372152
372944
|
return Object.entries(variables).map(([k2, v2]) => [Number.parseInt(k2, 10), v2]).filter(([n2]) => Number.isFinite(n2) && n2 >= 1).sort((a, b3) => a[0] - b3[0]).map(([, v2]) => v2);
|
|
372153
372945
|
}
|
|
372154
|
-
var log118, templatesRoutes, instanceAccess3, idParamSchema6, templateIdParamSchema, templateNameParamSchema,
|
|
372946
|
+
var log118, templatesRoutes, instanceAccess3, idParamSchema6, templateIdParamSchema, templateNameParamSchema, listQuerySchema19, createBodySchema, sendTestBodySchema, sendByNameBodySchema;
|
|
372155
372947
|
var init_templates3 = __esm(() => {
|
|
372156
372948
|
init_dist6();
|
|
372157
372949
|
init_src6();
|
|
@@ -372176,7 +372968,7 @@ var init_templates3 = __esm(() => {
|
|
|
372176
372968
|
id: exports_external.string().uuid(),
|
|
372177
372969
|
templateName: exports_external.string().min(1)
|
|
372178
372970
|
});
|
|
372179
|
-
|
|
372971
|
+
listQuerySchema19 = exports_external.object({
|
|
372180
372972
|
status: MetaTemplateStatusSchema.optional(),
|
|
372181
372973
|
category: MetaTemplateCategorySchema.optional(),
|
|
372182
372974
|
language: exports_external.string().min(2).optional(),
|
|
@@ -372204,7 +372996,7 @@ var init_templates3 = __esm(() => {
|
|
|
372204
372996
|
filename: exports_external.string().optional()
|
|
372205
372997
|
}).optional()
|
|
372206
372998
|
});
|
|
372207
|
-
templatesRoutes.get("/instances/:id/whatsapp-templates", instanceAccess3, zValidator("param", idParamSchema6), zValidator("query",
|
|
372999
|
+
templatesRoutes.get("/instances/:id/whatsapp-templates", instanceAccess3, zValidator("param", idParamSchema6), zValidator("query", listQuerySchema19), async (c) => {
|
|
372208
373000
|
const { id: instanceId } = c.req.valid("param");
|
|
372209
373001
|
const { status, category, language, sync } = c.req.valid("query");
|
|
372210
373002
|
const services = c.get("services");
|
|
@@ -372494,7 +373286,7 @@ async function resolveOpenTurn(services, keyId, turnId, headerInstanceId, header
|
|
|
372494
373286
|
}
|
|
372495
373287
|
return null;
|
|
372496
373288
|
}
|
|
372497
|
-
var turnsRoutes, closeTurnSchema,
|
|
373289
|
+
var turnsRoutes, closeTurnSchema, listQuerySchema20, adminForceCloseSchema, bulkCloseSchema;
|
|
372498
373290
|
var init_turns2 = __esm(() => {
|
|
372499
373291
|
init_dist6();
|
|
372500
373292
|
init_dist2();
|
|
@@ -372557,7 +373349,7 @@ var init_turns2 = __esm(() => {
|
|
|
372557
373349
|
}
|
|
372558
373350
|
});
|
|
372559
373351
|
});
|
|
372560
|
-
|
|
373352
|
+
listQuerySchema20 = exports_external.object({
|
|
372561
373353
|
status: exports_external.enum(["open", "done", "timeout"]).optional(),
|
|
372562
373354
|
instanceId: exports_external.string().uuid().optional(),
|
|
372563
373355
|
chatId: exports_external.string().optional(),
|
|
@@ -372572,7 +373364,7 @@ var init_turns2 = __esm(() => {
|
|
|
372572
373364
|
confirm: exports_external.boolean(),
|
|
372573
373365
|
reason: exports_external.string().optional()
|
|
372574
373366
|
});
|
|
372575
|
-
turnsRoutes.get("/", zValidator("query",
|
|
373367
|
+
turnsRoutes.get("/", zValidator("query", listQuerySchema20), async (c) => {
|
|
372576
373368
|
const denied = requireAdmin(c);
|
|
372577
373369
|
if (denied)
|
|
372578
373370
|
return denied;
|
|
@@ -372803,7 +373595,7 @@ var init_voice2 = __esm(() => {
|
|
|
372803
373595
|
});
|
|
372804
373596
|
|
|
372805
373597
|
// ../api/src/routes/v2/webhooks.ts
|
|
372806
|
-
var webhooksRoutes, createWebhookSourceSchema, updateWebhookSourceSchema,
|
|
373598
|
+
var webhooksRoutes, createWebhookSourceSchema, updateWebhookSourceSchema, listQuerySchema21, triggerEventSchema;
|
|
372807
373599
|
var init_webhooks3 = __esm(() => {
|
|
372808
373600
|
init_dist6();
|
|
372809
373601
|
init_dist2();
|
|
@@ -372817,10 +373609,10 @@ var init_webhooks3 = __esm(() => {
|
|
|
372817
373609
|
enabled: exports_external.boolean().default(true).describe("Whether source is enabled")
|
|
372818
373610
|
});
|
|
372819
373611
|
updateWebhookSourceSchema = createWebhookSourceSchema.partial();
|
|
372820
|
-
|
|
373612
|
+
listQuerySchema21 = exports_external.object({
|
|
372821
373613
|
enabled: exports_external.coerce.boolean().optional()
|
|
372822
373614
|
});
|
|
372823
|
-
webhooksRoutes.get("/webhook-sources", zValidator("query",
|
|
373615
|
+
webhooksRoutes.get("/webhook-sources", zValidator("query", listQuerySchema21), async (c) => {
|
|
372824
373616
|
const { enabled } = c.req.valid("query");
|
|
372825
373617
|
const services = c.get("services");
|
|
372826
373618
|
const sources = await services.webhooks.list({ enabled });
|
|
@@ -373685,7 +374477,9 @@ var init_v2 = __esm(() => {
|
|
|
373685
374477
|
init_persons3();
|
|
373686
374478
|
init_processed_events();
|
|
373687
374479
|
init_providers4();
|
|
374480
|
+
init_scheduled_messages2();
|
|
373688
374481
|
init_settings3();
|
|
374482
|
+
init_slack();
|
|
373689
374483
|
init_templates3();
|
|
373690
374484
|
init_trust();
|
|
373691
374485
|
init_turns2();
|
|
@@ -373702,6 +374496,8 @@ var init_v2 = __esm(() => {
|
|
|
373702
374496
|
v2Routes.route("/instances", instancesRoutes);
|
|
373703
374497
|
v2Routes.route("/logs", logsRoutes);
|
|
373704
374498
|
v2Routes.route("/messages", messagesRoutes);
|
|
374499
|
+
v2Routes.route("/scheduled-messages", scheduledMessagesRoutes);
|
|
374500
|
+
v2Routes.route("/slack", slackRoutes);
|
|
373705
374501
|
v2Routes.route("/events", eventsRoutes);
|
|
373706
374502
|
v2Routes.route("/journeys", journeysRoutes);
|
|
373707
374503
|
v2Routes.route("/persons", personsRoutes);
|
|
@@ -374818,6 +375614,27 @@ async function downloadMediaFromUrl(ctx, instanceId, messageId, mediaUrl, mimeTy
|
|
|
374818
375614
|
return null;
|
|
374819
375615
|
}
|
|
374820
375616
|
}
|
|
375617
|
+
async function downloadMediaViaPlugin(ctx, instanceId, messageId, channelType, mediaRef, fallbackMimeType, platformTimestamp, trustedTenantId) {
|
|
375618
|
+
const plugin10 = await (ctx.resolveChannelPlugin ?? getPlugin)(channelType);
|
|
375619
|
+
if (!plugin10?.downloadInboundMedia) {
|
|
375620
|
+
log122.warn("Channel plugin cannot materialize deferred media", { channelType, messageId });
|
|
375621
|
+
return null;
|
|
375622
|
+
}
|
|
375623
|
+
try {
|
|
375624
|
+
const { buffer: buffer3, mimeType } = pluginMediaSchema.parse(await plugin10.downloadInboundMedia(instanceId, mediaRef));
|
|
375625
|
+
const result = await ctx.mediaStorage.storeFromBuffer(instanceId, messageId, buffer3, mimeType || fallbackMimeType, platformTimestamp, trustedTenantId);
|
|
375626
|
+
await runMediaDb(ctx, trustedTenantId, () => ctx.mediaStorage.updateMessageLocalPath(messageId, result.localPath));
|
|
375627
|
+
log122.debug("Downloaded inbound media via plugin", { messageId, channelType, filePath: result.localPath });
|
|
375628
|
+
return result.localPath;
|
|
375629
|
+
} catch (error3) {
|
|
375630
|
+
log122.error("Failed to download inbound media via plugin", {
|
|
375631
|
+
error: String(error3),
|
|
375632
|
+
channelType,
|
|
375633
|
+
messageId
|
|
375634
|
+
});
|
|
375635
|
+
return null;
|
|
375636
|
+
}
|
|
375637
|
+
}
|
|
374821
375638
|
async function resolveMediaPath2(ctx, instanceId, chatId, externalId, content, mimeType, channelType, trustedTenantId) {
|
|
374822
375639
|
const maxWaitMs = 5000;
|
|
374823
375640
|
const pollMs = 250;
|
|
@@ -374847,6 +375664,11 @@ async function resolveMediaPath2(ctx, instanceId, chatId, externalId, content, m
|
|
|
374847
375664
|
if (!filePath)
|
|
374848
375665
|
return null;
|
|
374849
375666
|
}
|
|
375667
|
+
if (!filePath && content.mediaId && channelType) {
|
|
375668
|
+
filePath = await downloadMediaViaPlugin(ctx, instanceId, message2.id, channelType, content.mediaId, mimeType, message2.platformTimestamp ?? undefined, trustedTenantId);
|
|
375669
|
+
if (!filePath)
|
|
375670
|
+
return null;
|
|
375671
|
+
}
|
|
374850
375672
|
if (!filePath) {
|
|
374851
375673
|
log122.debug("No media file path available", { externalId });
|
|
374852
375674
|
return null;
|
|
@@ -375144,17 +375966,23 @@ async function setupMediaProcessor(eventBus, db2, services) {
|
|
|
375144
375966
|
});
|
|
375145
375967
|
log122.info("Media processor initialized");
|
|
375146
375968
|
}
|
|
375147
|
-
var log122, PROCESSABLE_MEDIA_TYPES;
|
|
375969
|
+
var log122, PROCESSABLE_MEDIA_TYPES, pluginMediaSchema;
|
|
375148
375970
|
var init_media_processor = __esm(() => {
|
|
375149
375971
|
init_src();
|
|
375150
375972
|
init_src5();
|
|
375151
375973
|
init_src8();
|
|
375152
375974
|
init_drizzle_orm();
|
|
375975
|
+
init_zod();
|
|
375153
375976
|
init_media_storage();
|
|
375154
375977
|
init_tenant_scope();
|
|
375155
375978
|
init_worker_tenant_context();
|
|
375979
|
+
init_loader2();
|
|
375156
375980
|
log122 = createLogger("media-processor");
|
|
375157
375981
|
PROCESSABLE_MEDIA_TYPES = new Set(["audio", "image", "document", "video"]);
|
|
375982
|
+
pluginMediaSchema = exports_external.object({
|
|
375983
|
+
buffer: exports_external.instanceof(Buffer),
|
|
375984
|
+
mimeType: exports_external.string().min(1).optional()
|
|
375985
|
+
});
|
|
375158
375986
|
});
|
|
375159
375987
|
|
|
375160
375988
|
// ../api/src/tenancy/inflight-revocation.ts
|
|
@@ -375207,29 +376035,6 @@ var init_inflight_revocation = __esm(() => {
|
|
|
375207
376035
|
};
|
|
375208
376036
|
});
|
|
375209
376037
|
|
|
375210
|
-
// ../api/src/utils/phone.ts
|
|
375211
|
-
function isValidE164Phone(phone) {
|
|
375212
|
-
const bare = phone.replace(/^\+/, "");
|
|
375213
|
-
if (!/^\d+$/.test(bare))
|
|
375214
|
-
return false;
|
|
375215
|
-
return bare.length >= 7 && bare.length <= 15;
|
|
375216
|
-
}
|
|
375217
|
-
function isLidFormat(platformUserId) {
|
|
375218
|
-
const bare = platformUserId.split("@")[0] || platformUserId;
|
|
375219
|
-
return /^\d{14,}$/.test(bare);
|
|
375220
|
-
}
|
|
375221
|
-
function validateContactPhone(phone, platformUserId) {
|
|
375222
|
-
if (!phone)
|
|
375223
|
-
return;
|
|
375224
|
-
if (!isValidE164Phone(phone))
|
|
375225
|
-
return;
|
|
375226
|
-
const barePhone = phone.replace(/^\+/, "");
|
|
375227
|
-
const barePuid = platformUserId.split("@")[0] || platformUserId;
|
|
375228
|
-
if (isLidFormat(platformUserId) && barePhone === barePuid)
|
|
375229
|
-
return;
|
|
375230
|
-
return phone;
|
|
375231
|
-
}
|
|
375232
|
-
|
|
375233
376038
|
// ../api/src/plugins/sync-worker.ts
|
|
375234
376039
|
class RateLimiter {
|
|
375235
376040
|
lastRequest = 0;
|
|
@@ -376801,6 +377606,11 @@ async function withCronMonitor(slug, cron2, checkinMargin, maxRuntime, handler)
|
|
|
376801
377606
|
await handler();
|
|
376802
377607
|
}
|
|
376803
377608
|
}
|
|
377609
|
+
function createScheduledMessageSweeper(services, channelRegistry2) {
|
|
377610
|
+
const sweeper2 = new ScheduledMessageService(services.db, createPluginResolver(services.db, (channel5) => channelRegistry2.get(channel5) ?? undefined));
|
|
377611
|
+
sweeper2.setAuthPlane(services.authPlane.db);
|
|
377612
|
+
return sweeper2;
|
|
377613
|
+
}
|
|
376804
377614
|
function setupScheduler(services, channelRegistry2) {
|
|
376805
377615
|
const scheduler = getScheduler();
|
|
376806
377616
|
scheduler.register({
|
|
@@ -376929,6 +377739,29 @@ function setupScheduler(services, channelRegistry2) {
|
|
|
376929
377739
|
});
|
|
376930
377740
|
}
|
|
376931
377741
|
});
|
|
377742
|
+
if (channelRegistry2) {
|
|
377743
|
+
const scheduledMessages2 = createScheduledMessageSweeper(services, channelRegistry2);
|
|
377744
|
+
scheduler.register({
|
|
377745
|
+
name: "scheduled-message-sweeper",
|
|
377746
|
+
cron: "*/15 * * * * *",
|
|
377747
|
+
runOnStart: false,
|
|
377748
|
+
handler: async () => {
|
|
377749
|
+
await withCronMonitor("scheduled-message-sweeper", "*/15 * * * * *", 1, 1, async () => {
|
|
377750
|
+
const startTime3 = Date.now();
|
|
377751
|
+
try {
|
|
377752
|
+
const stats = await scheduledMessages2.sweep();
|
|
377753
|
+
recordScheduledJob("scheduled-message-sweeper", "success", (Date.now() - startTime3) / 1000);
|
|
377754
|
+
if (stats.scanned > 0) {
|
|
377755
|
+
log127.debug("Scheduled-message sweep tick", { ...stats });
|
|
377756
|
+
}
|
|
377757
|
+
} catch (err2) {
|
|
377758
|
+
recordScheduledJob("scheduled-message-sweeper", "failure", (Date.now() - startTime3) / 1000);
|
|
377759
|
+
throw err2;
|
|
377760
|
+
}
|
|
377761
|
+
});
|
|
377762
|
+
}
|
|
377763
|
+
});
|
|
377764
|
+
}
|
|
376932
377765
|
scheduler.register({
|
|
376933
377766
|
name: "unread-count-refresh",
|
|
376934
377767
|
cron: CronExpressions.EVERY_HOUR,
|
|
@@ -376980,6 +377813,7 @@ var init_scheduler2 = __esm(() => {
|
|
|
376980
377813
|
init_src();
|
|
376981
377814
|
init_esm5();
|
|
376982
377815
|
init_sentry_scrub();
|
|
377816
|
+
init_scheduled_messages();
|
|
376983
377817
|
init_periodic_tenant_work();
|
|
376984
377818
|
log127 = createLogger("scheduler:setup");
|
|
376985
377819
|
});
|
|
@@ -383701,6 +384535,11 @@ var SLACK_CAPABILITIES = {
|
|
|
383701
384535
|
canDeleteMessage: true,
|
|
383702
384536
|
canReplyToMessage: true,
|
|
383703
384537
|
canForwardMessage: false,
|
|
384538
|
+
canScheduleMessage: true,
|
|
384539
|
+
maxScheduleAheadMs: 120 * 24 * 60 * 60 * 1000,
|
|
384540
|
+
canGetPermalink: true,
|
|
384541
|
+
canPinMessage: true,
|
|
384542
|
+
canSearchMessages: false,
|
|
383704
384543
|
canSendContact: false,
|
|
383705
384544
|
canSendLocation: false,
|
|
383706
384545
|
canSendSticker: false,
|
|
@@ -383754,7 +384593,17 @@ function resolveStreamThrottle(throttleMs) {
|
|
|
383754
384593
|
// ../channel-slack/src/connection/bolt-client.ts
|
|
383755
384594
|
init_types10();
|
|
383756
384595
|
var import_bolt = __toESM(require_dist16(), 1);
|
|
384596
|
+
var import_web_api = __toESM(require_dist13(), 1);
|
|
383757
384597
|
var HTTP_MAX_BODY_BYTES = 1024 * 1024;
|
|
384598
|
+
function buildActingClients(options, botClient) {
|
|
384599
|
+
if (options.authMode !== "user")
|
|
384600
|
+
return { actingClient: botClient };
|
|
384601
|
+
if (!options.userToken) {
|
|
384602
|
+
throw new SlackError(SlackErrorCode.CONNECTION_FAILED, "userToken is required when authMode is 'user'");
|
|
384603
|
+
}
|
|
384604
|
+
const userClient = new import_web_api.WebClient(options.userToken);
|
|
384605
|
+
return { actingClient: userClient, userClient };
|
|
384606
|
+
}
|
|
383758
384607
|
function createBoltApp(options, logger5) {
|
|
383759
384608
|
const mode = options.mode ?? "socket";
|
|
383760
384609
|
if (mode === "http") {
|
|
@@ -383788,6 +384637,7 @@ function createSocketBoltApp(options, logger5) {
|
|
|
383788
384637
|
return {
|
|
383789
384638
|
app,
|
|
383790
384639
|
client: app.client,
|
|
384640
|
+
...buildActingClients(options, app.client),
|
|
383791
384641
|
botToken: options.botToken,
|
|
383792
384642
|
mode: "socket"
|
|
383793
384643
|
};
|
|
@@ -383820,6 +384670,7 @@ function createHttpBoltApp(options, logger5) {
|
|
|
383820
384670
|
return {
|
|
383821
384671
|
app,
|
|
383822
384672
|
client: app.client,
|
|
384673
|
+
...buildActingClients(options, app.client),
|
|
383823
384674
|
botToken: options.botToken,
|
|
383824
384675
|
mode: "http",
|
|
383825
384676
|
httpPort: options.httpPort,
|
|
@@ -383895,11 +384746,22 @@ async function startBoltConnection(connection, logger5) {
|
|
|
383895
384746
|
teamId: connection.teamId,
|
|
383896
384747
|
teamName: connection.teamName
|
|
383897
384748
|
});
|
|
384749
|
+
if (connection.userClient) {
|
|
384750
|
+
const userAuth = await connection.userClient.auth.test();
|
|
384751
|
+
connection.actingUserId = userAuth.user_id ?? undefined;
|
|
384752
|
+
logger5.info("Acting user identity resolved", {
|
|
384753
|
+
actingUserId: connection.actingUserId,
|
|
384754
|
+
actingUser: userAuth.user
|
|
384755
|
+
});
|
|
384756
|
+
}
|
|
383898
384757
|
} catch (error) {
|
|
383899
384758
|
logger5.warn("Failed to resolve bot identity before start \u2014 self-message filtering may be unreliable", {
|
|
383900
384759
|
error: String(error)
|
|
383901
384760
|
});
|
|
383902
384761
|
}
|
|
384762
|
+
if (connection.userClient && !connection.actingUserId) {
|
|
384763
|
+
throw new SlackError(SlackErrorCode.CONNECTION_FAILED, "User mode requires a resolved acting user id, but it could not be determined from the user token. Refusing to start.");
|
|
384764
|
+
}
|
|
383903
384765
|
if (connection.mode === "http") {
|
|
383904
384766
|
const port = connection.httpPort ?? 3001;
|
|
383905
384767
|
try {
|
|
@@ -384228,12 +385090,13 @@ function extractMessageMeta(event) {
|
|
|
384228
385090
|
ts,
|
|
384229
385091
|
userId,
|
|
384230
385092
|
teamId,
|
|
384231
|
-
isDm: channelType === "im",
|
|
385093
|
+
isDm: channelType === "im" || channelType === "mpim",
|
|
385094
|
+
isMpim: channelType === "mpim",
|
|
384232
385095
|
isThreadReply: threadTs !== undefined && threadTs !== ts,
|
|
384233
385096
|
channelType
|
|
384234
385097
|
};
|
|
384235
385098
|
}
|
|
384236
|
-
function shouldSkipMessage(msg,
|
|
385099
|
+
function shouldSkipMessage(msg, selfUserIds) {
|
|
384237
385100
|
if (msg.subtype === "bot_message" || msg.bot_id)
|
|
384238
385101
|
return true;
|
|
384239
385102
|
if (msg.subtype === "message_changed" || msg.subtype === "message_deleted")
|
|
@@ -384241,7 +385104,7 @@ function shouldSkipMessage(msg, botUserId) {
|
|
|
384241
385104
|
const userId = msg.user;
|
|
384242
385105
|
if (!userId)
|
|
384243
385106
|
return true;
|
|
384244
|
-
if (
|
|
385107
|
+
if (selfUserIds.some((id) => id && id === userId))
|
|
384245
385108
|
return true;
|
|
384246
385109
|
return false;
|
|
384247
385110
|
}
|
|
@@ -384265,6 +385128,7 @@ function buildRawPayload(meta, msg, extra) {
|
|
|
384265
385128
|
channelType: meta.channelType,
|
|
384266
385129
|
teamId: meta.teamId,
|
|
384267
385130
|
isDm: meta.isDm,
|
|
385131
|
+
isMpim: meta.isMpim === true,
|
|
384268
385132
|
isThreadReply: meta.isThreadReply,
|
|
384269
385133
|
files: msg.files,
|
|
384270
385134
|
...extra
|
|
@@ -384358,11 +385222,12 @@ async function processMessage2(instanceId, msg, currentBotUserId, callbacks, log
|
|
|
384358
385222
|
}
|
|
384359
385223
|
await callbacks.onMessage(instanceId, meta.ts, meta.channelId, userId, content, replyToId, rawPayload, platformTimestamp, meta);
|
|
384360
385224
|
}
|
|
384361
|
-
function setupMessageHandlers2(app, instanceId, botUserId, callbacks, dmPolicyConfig, logger5, filterConfig, reliability) {
|
|
385225
|
+
function setupMessageHandlers2(app, instanceId, botUserId, callbacks, dmPolicyConfig, logger5, filterConfig, reliability, actingUserId) {
|
|
384362
385226
|
const resolveBotUserId = () => typeof botUserId === "function" ? botUserId() : botUserId;
|
|
385227
|
+
const resolveActingUserId = () => typeof actingUserId === "function" ? actingUserId() : actingUserId;
|
|
384363
385228
|
app.message(async ({ message: message2 }) => {
|
|
384364
385229
|
const msg = message2;
|
|
384365
|
-
if (shouldSkipMessage(msg, resolveBotUserId()))
|
|
385230
|
+
if (shouldSkipMessage(msg, [resolveBotUserId(), resolveActingUserId()]))
|
|
384366
385231
|
return;
|
|
384367
385232
|
const userId = msg.user;
|
|
384368
385233
|
const meta = extractMessageMeta(msg);
|
|
@@ -384514,16 +385379,52 @@ async function uploadFileFromUrl(client, options, logger5) {
|
|
|
384514
385379
|
|
|
384515
385380
|
// ../channel-slack/src/markdown.ts
|
|
384516
385381
|
var MAX_SLACK_MESSAGE_LENGTH = 4000;
|
|
385382
|
+
function escapeMrkdwn(text) {
|
|
385383
|
+
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
385384
|
+
}
|
|
385385
|
+
function segment(text) {
|
|
385386
|
+
const segments = [];
|
|
385387
|
+
const pattern = /(```[\s\S]*?```|`[^`\n]*`)/g;
|
|
385388
|
+
let lastIndex = 0;
|
|
385389
|
+
for (const match of text.matchAll(pattern)) {
|
|
385390
|
+
const start = match.index ?? 0;
|
|
385391
|
+
if (start > lastIndex) {
|
|
385392
|
+
segments.push({ text: text.slice(lastIndex, start), isCode: false });
|
|
385393
|
+
}
|
|
385394
|
+
segments.push({ text: match[0], isCode: true });
|
|
385395
|
+
lastIndex = start + match[0].length;
|
|
385396
|
+
}
|
|
385397
|
+
if (lastIndex < text.length) {
|
|
385398
|
+
segments.push({ text: text.slice(lastIndex), isCode: false });
|
|
385399
|
+
}
|
|
385400
|
+
return segments;
|
|
385401
|
+
}
|
|
385402
|
+
function unescapeUrl(url) {
|
|
385403
|
+
return url.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
385404
|
+
}
|
|
385405
|
+
function convertProse(text) {
|
|
385406
|
+
const BOLD = "\x00B\x00";
|
|
385407
|
+
let out = escapeMrkdwn(text);
|
|
385408
|
+
out = out.replace(/^>\s?/gm, "> ");
|
|
385409
|
+
out = out.replace(/^#{1,6}\s+(.+)$/gm, `${BOLD}$1${BOLD}`);
|
|
385410
|
+
out = out.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_m, alt, url) => `<${unescapeUrl(url)}|${alt}>`);
|
|
385411
|
+
out = out.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_m, label, url) => `<${unescapeUrl(url)}|${label}>`);
|
|
385412
|
+
out = out.replace(/^(-{3,}|\*{3,}|_{3,})$/gm, "\u2014\u2014\u2014");
|
|
385413
|
+
out = out.replace(/^(\s*)[-*+]\s+/gm, "$1\u2022 ");
|
|
385414
|
+
out = out.replace(/\*\*(.+?)\*\*/g, `${BOLD}$1${BOLD}`);
|
|
385415
|
+
out = out.replace(/(^|[\s(])\*(?!\s)([^*\n]+?)\*(?=[\s).,;:!?]|$)/g, "$1_$2_");
|
|
385416
|
+
out = out.split(BOLD).join("*");
|
|
385417
|
+
out = out.replace(/~~(.+?)~~/g, "~$1~");
|
|
385418
|
+
return out;
|
|
385419
|
+
}
|
|
384517
385420
|
function markdownToMrkdwn(markdown) {
|
|
384518
|
-
|
|
384519
|
-
|
|
384520
|
-
|
|
384521
|
-
|
|
384522
|
-
|
|
384523
|
-
|
|
384524
|
-
|
|
384525
|
-
text = text.replace(/^(-{3,}|\*{3,}|_{3,})$/gm, "\u2014\u2014\u2014");
|
|
384526
|
-
return text;
|
|
385421
|
+
return segment(markdown).map((s) => s.isCode ? s.text : convertProse(s.text)).join("");
|
|
385422
|
+
}
|
|
385423
|
+
function findOpenFence(text) {
|
|
385424
|
+
const fences = text.match(/```[^\n]*/g);
|
|
385425
|
+
if (!fences || fences.length % 2 === 0)
|
|
385426
|
+
return null;
|
|
385427
|
+
return fences[fences.length - 1]?.slice(3).trim() ?? "";
|
|
384527
385428
|
}
|
|
384528
385429
|
function chunkMessage2(text, maxLength = MAX_SLACK_MESSAGE_LENGTH) {
|
|
384529
385430
|
if (text.length <= maxLength) {
|
|
@@ -384531,21 +385432,37 @@ function chunkMessage2(text, maxLength = MAX_SLACK_MESSAGE_LENGTH) {
|
|
|
384531
385432
|
}
|
|
384532
385433
|
const chunks = [];
|
|
384533
385434
|
let remaining = text;
|
|
385435
|
+
let carryFence = null;
|
|
384534
385436
|
while (remaining.length > 0) {
|
|
384535
|
-
|
|
384536
|
-
|
|
385437
|
+
const prefix = carryFence === null ? "" : `\`\`\`${carryFence}
|
|
385438
|
+
`;
|
|
385439
|
+
if (prefix.length + remaining.length <= maxLength) {
|
|
385440
|
+
chunks.push(prefix + remaining);
|
|
384537
385441
|
break;
|
|
384538
385442
|
}
|
|
384539
|
-
|
|
384540
|
-
|
|
384541
|
-
|
|
384542
|
-
|
|
384543
|
-
|
|
384544
|
-
|
|
384545
|
-
|
|
384546
|
-
|
|
384547
|
-
|
|
384548
|
-
|
|
385443
|
+
const split = (budget) => {
|
|
385444
|
+
let at = remaining.lastIndexOf(`
|
|
385445
|
+
`, budget);
|
|
385446
|
+
if (at <= 0 || at < budget * 0.5)
|
|
385447
|
+
at = remaining.lastIndexOf(" ", budget);
|
|
385448
|
+
if (at <= 0 || at < budget * 0.5)
|
|
385449
|
+
at = budget;
|
|
385450
|
+
return at;
|
|
385451
|
+
};
|
|
385452
|
+
const plainBudget = Math.max(1, maxLength - prefix.length);
|
|
385453
|
+
let splitIndex = split(plainBudget);
|
|
385454
|
+
let head = remaining.slice(0, splitIndex);
|
|
385455
|
+
let openFence = findOpenFence(prefix + head);
|
|
385456
|
+
if (openFence !== null) {
|
|
385457
|
+
const fencedBudget = Math.max(1, maxLength - prefix.length - 4);
|
|
385458
|
+
splitIndex = split(fencedBudget);
|
|
385459
|
+
head = remaining.slice(0, splitIndex);
|
|
385460
|
+
openFence = findOpenFence(prefix + head);
|
|
385461
|
+
}
|
|
385462
|
+
chunks.push(openFence === null ? prefix + head : `${prefix + head}
|
|
385463
|
+
\`\`\``);
|
|
385464
|
+
carryFence = openFence;
|
|
385465
|
+
remaining = remaining.slice(splitIndex).trimStart();
|
|
384549
385466
|
}
|
|
384550
385467
|
return chunks;
|
|
384551
385468
|
}
|
|
@@ -384887,6 +385804,55 @@ async function sendTextMessage2(client, options, logger5) {
|
|
|
384887
385804
|
}
|
|
384888
385805
|
return lastTs;
|
|
384889
385806
|
}
|
|
385807
|
+
var MAX_SCHEDULE_AHEAD_MS = 120 * 24 * 60 * 60 * 1000;
|
|
385808
|
+
async function scheduleTextMessage(client, options, logger5) {
|
|
385809
|
+
const formattedText = options.formatMode === "passthrough" ? options.text : markdownToMrkdwn(options.text);
|
|
385810
|
+
if (formattedText.length > MAX_SLACK_MESSAGE_LENGTH) {
|
|
385811
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, `Scheduled message is ${formattedText.length} chars, over Slack's ${MAX_SLACK_MESSAGE_LENGTH} limit. Chunking is not applied when scheduling \u2014 each chunk would get its own handle and a later cancel could half-fire.`);
|
|
385812
|
+
}
|
|
385813
|
+
const leadMs = options.postAt.getTime() - Date.now();
|
|
385814
|
+
if (leadMs <= 0) {
|
|
385815
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, `postAt is in the past (${options.postAt.toISOString()})`);
|
|
385816
|
+
}
|
|
385817
|
+
if (leadMs > MAX_SCHEDULE_AHEAD_MS) {
|
|
385818
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, `postAt is ${Math.round(leadMs / 86400000)} days out; Slack accepts at most 120.`);
|
|
385819
|
+
}
|
|
385820
|
+
try {
|
|
385821
|
+
const result = await client.chat.scheduleMessage({
|
|
385822
|
+
channel: options.channelId,
|
|
385823
|
+
text: formattedText,
|
|
385824
|
+
post_at: Math.floor(options.postAt.getTime() / 1000),
|
|
385825
|
+
thread_ts: options.threadTs,
|
|
385826
|
+
reply_broadcast: options.replyBroadcast,
|
|
385827
|
+
username: options.username,
|
|
385828
|
+
icon_url: options.iconUrl,
|
|
385829
|
+
icon_emoji: options.iconEmoji
|
|
385830
|
+
});
|
|
385831
|
+
const scheduledId = result.scheduled_message_id;
|
|
385832
|
+
if (!scheduledId) {
|
|
385833
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, "chat.scheduleMessage returned no scheduled_message_id \u2014 cannot cancel later");
|
|
385834
|
+
}
|
|
385835
|
+
return scheduledId;
|
|
385836
|
+
} catch (error) {
|
|
385837
|
+
if (error instanceof SlackError)
|
|
385838
|
+
throw error;
|
|
385839
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
385840
|
+
logger5.error("Failed to schedule message", { error: message2, channelId: options.channelId });
|
|
385841
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, `Failed to schedule message: ${message2}`);
|
|
385842
|
+
}
|
|
385843
|
+
}
|
|
385844
|
+
async function cancelScheduledSlackMessage(client, channelId, scheduledMessageId, logger5) {
|
|
385845
|
+
try {
|
|
385846
|
+
await client.chat.deleteScheduledMessage({
|
|
385847
|
+
channel: channelId,
|
|
385848
|
+
scheduled_message_id: scheduledMessageId
|
|
385849
|
+
});
|
|
385850
|
+
} catch (error) {
|
|
385851
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
385852
|
+
logger5.error("Failed to cancel scheduled message", { error: message2, channelId, scheduledMessageId });
|
|
385853
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, `Failed to cancel scheduled message: ${message2}`);
|
|
385854
|
+
}
|
|
385855
|
+
}
|
|
384890
385856
|
async function editSlackMessage(client, channelId, ts, newText, formatMode, logger5) {
|
|
384891
385857
|
const formattedText = formatMode === "passthrough" ? newText : markdownToMrkdwn(newText);
|
|
384892
385858
|
try {
|
|
@@ -384919,19 +385885,27 @@ init_types10();
|
|
|
384919
385885
|
var downloadGuard4 = createDownloadGuard();
|
|
384920
385886
|
function resolveSlackTokens(slackConfig, rawOptions, rawCredentials) {
|
|
384921
385887
|
const botToken = slackConfig.botToken ?? rawOptions.token ?? rawCredentials.botToken ?? rawCredentials.token;
|
|
385888
|
+
const userToken = slackConfig.userToken ?? rawCredentials.userToken;
|
|
385889
|
+
const authMode = slackConfig.authMode ?? "bot";
|
|
384922
385890
|
const appToken = slackConfig.appToken ?? rawCredentials.appToken;
|
|
384923
385891
|
const signingSecret = slackConfig.signingSecret ?? rawCredentials.signingSecret;
|
|
384924
385892
|
const mode = slackConfig.mode ?? "socket";
|
|
384925
385893
|
if (!botToken) {
|
|
384926
385894
|
throw new SlackError(SlackErrorCode.INVALID_TOKEN, "botToken (xoxb-...) is required");
|
|
384927
385895
|
}
|
|
385896
|
+
if (authMode === "user" && !userToken) {
|
|
385897
|
+
throw new SlackError(SlackErrorCode.INVALID_TOKEN, "userToken (xoxp-...) is required when authMode is 'user' \u2014 without it every action would silently go out as the bot");
|
|
385898
|
+
}
|
|
385899
|
+
if (userToken && !userToken.startsWith("xoxp-")) {
|
|
385900
|
+
throw new SlackError(SlackErrorCode.INVALID_TOKEN, "userToken must be a user token (xoxp-...); got a token with a different prefix");
|
|
385901
|
+
}
|
|
384928
385902
|
if (mode === "socket" && !appToken) {
|
|
384929
385903
|
throw new SlackError(SlackErrorCode.INVALID_TOKEN, "appToken (xapp-...) is required for Socket Mode");
|
|
384930
385904
|
}
|
|
384931
385905
|
if (mode === "http" && !signingSecret) {
|
|
384932
385906
|
throw new SlackError(SlackErrorCode.INVALID_TOKEN, "signingSecret is required for HTTP mode");
|
|
384933
385907
|
}
|
|
384934
|
-
return { botToken, appToken, signingSecret, mode };
|
|
385908
|
+
return { botToken, userToken, authMode, appToken, signingSecret, mode };
|
|
384935
385909
|
}
|
|
384936
385910
|
|
|
384937
385911
|
class SlackPlugin extends BaseChannelPlugin {
|
|
@@ -385010,6 +385984,8 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385010
385984
|
}
|
|
385011
385985
|
connection = createBoltApp({
|
|
385012
385986
|
botToken: resolved.botToken,
|
|
385987
|
+
userToken: resolved.userToken,
|
|
385988
|
+
authMode: resolved.authMode,
|
|
385013
385989
|
appToken: resolved.appToken,
|
|
385014
385990
|
signingSecret: resolved.signingSecret,
|
|
385015
385991
|
retryConfig: slackConfig.retryConfig,
|
|
@@ -385138,7 +386114,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385138
386114
|
const streamMode = resolveStreamMode(slackConfig.streamMode);
|
|
385139
386115
|
const throttleMs = resolveStreamThrottle(slackConfig.streamThrottleMs);
|
|
385140
386116
|
const base2 = streamMode === "native" ? createNativeStreamSender({
|
|
385141
|
-
client: connection.
|
|
386117
|
+
client: connection.actingClient,
|
|
385142
386118
|
channelId: chatId,
|
|
385143
386119
|
threadTs,
|
|
385144
386120
|
throttleMs,
|
|
@@ -385148,7 +386124,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385148
386124
|
formatMode: options?.formatMode ?? "convert",
|
|
385149
386125
|
logger: this.logger
|
|
385150
386126
|
}) : createSlackStreamSender({
|
|
385151
|
-
client: connection.
|
|
386127
|
+
client: connection.actingClient,
|
|
385152
386128
|
channelId: chatId,
|
|
385153
386129
|
threadTs,
|
|
385154
386130
|
streamMode,
|
|
@@ -385195,12 +386171,12 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385195
386171
|
const status = shouldClear ? "" : options?.status ?? (type === "recording" ? "is recording..." : "is typing...");
|
|
385196
386172
|
const timerKey = this.presenceStatusTimerKey(instanceId, chatId, threadTs);
|
|
385197
386173
|
const delivered = status === "" ? await clearTypingStatus({
|
|
385198
|
-
client: connection.
|
|
386174
|
+
client: connection.actingClient,
|
|
385199
386175
|
channelId: chatId,
|
|
385200
386176
|
threadTs,
|
|
385201
386177
|
logger: this.logger
|
|
385202
386178
|
}) : await setSlackThreadStatus({
|
|
385203
|
-
client: connection.
|
|
386179
|
+
client: connection.actingClient,
|
|
385204
386180
|
channelId: chatId,
|
|
385205
386181
|
threadTs,
|
|
385206
386182
|
status,
|
|
@@ -385216,7 +386192,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385216
386192
|
return;
|
|
385217
386193
|
this.presenceStatusTimers.delete(timerKey);
|
|
385218
386194
|
clearTypingStatus({
|
|
385219
|
-
client: connection.
|
|
386195
|
+
client: connection.actingClient,
|
|
385220
386196
|
channelId: chatId,
|
|
385221
386197
|
threadTs,
|
|
385222
386198
|
logger: this.logger
|
|
@@ -385253,21 +386229,105 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385253
386229
|
}
|
|
385254
386230
|
async editMessage(instanceId, channelId, messageTs, newText) {
|
|
385255
386231
|
const connection = this.getConnection(instanceId);
|
|
385256
|
-
await editSlackMessage(connection.
|
|
386232
|
+
await editSlackMessage(connection.actingClient, channelId, messageTs, newText, "convert", this.logger);
|
|
385257
386233
|
}
|
|
385258
386234
|
async deleteMessage(instanceId, channelId, messageTs) {
|
|
385259
386235
|
const connection = this.getConnection(instanceId);
|
|
385260
|
-
await deleteSlackMessage(connection.
|
|
386236
|
+
await deleteSlackMessage(connection.actingClient, channelId, messageTs, this.logger);
|
|
386237
|
+
}
|
|
386238
|
+
async scheduleMessage(instanceId, message2, sendAt) {
|
|
386239
|
+
const connection = this.getConnection(instanceId);
|
|
386240
|
+
const config2 = this.slackConfigs.get(instanceId);
|
|
386241
|
+
if (message2.content.type !== "text" || !message2.content.text) {
|
|
386242
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, `Only text messages can be scheduled (got '${message2.content.type}') \u2014 chat.scheduleMessage carries no attachment.`);
|
|
386243
|
+
}
|
|
386244
|
+
const threadTs = this.resolveThreadTs(config2?.replyToMode ?? "all", message2.replyTo, message2.threadId);
|
|
386245
|
+
return scheduleTextMessage(connection.actingClient, {
|
|
386246
|
+
channelId: message2.to,
|
|
386247
|
+
text: message2.content.text,
|
|
386248
|
+
threadTs,
|
|
386249
|
+
replyBroadcast: message2.metadata?.isThreadBroadcast === true,
|
|
386250
|
+
username: config2?.defaultUsername,
|
|
386251
|
+
iconUrl: config2?.defaultIconUrl,
|
|
386252
|
+
iconEmoji: config2?.defaultIconEmoji,
|
|
386253
|
+
formatMode: message2.metadata?.messageFormatMode,
|
|
386254
|
+
postAt: sendAt
|
|
386255
|
+
}, this.logger);
|
|
386256
|
+
}
|
|
386257
|
+
async openDirectMessage(instanceId, userId) {
|
|
386258
|
+
const connection = this.getConnection(instanceId);
|
|
386259
|
+
try {
|
|
386260
|
+
const result = await connection.actingClient.conversations.open({ users: userId });
|
|
386261
|
+
const channelId = result.channel?.id;
|
|
386262
|
+
if (!channelId) {
|
|
386263
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, `conversations.open returned no channel for user ${userId}`);
|
|
386264
|
+
}
|
|
386265
|
+
return channelId;
|
|
386266
|
+
} catch (error) {
|
|
386267
|
+
if (error instanceof SlackError)
|
|
386268
|
+
throw error;
|
|
386269
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
386270
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, `Failed to open DM with ${userId}: ${message2}`);
|
|
386271
|
+
}
|
|
386272
|
+
}
|
|
386273
|
+
async searchMessages(instanceId, query, options = {}) {
|
|
386274
|
+
const connection = this.getConnection(instanceId);
|
|
386275
|
+
const config2 = this.slackConfigs.get(instanceId);
|
|
386276
|
+
if (config2?.authMode !== "user" || !connection.userClient) {
|
|
386277
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, "search.messages needs a user token (search:read); this instance runs in 'bot' auth mode");
|
|
386278
|
+
}
|
|
386279
|
+
try {
|
|
386280
|
+
const result = await connection.userClient.search.messages({
|
|
386281
|
+
query,
|
|
386282
|
+
count: options.count ?? 20,
|
|
386283
|
+
page: options.page ?? 1
|
|
386284
|
+
});
|
|
386285
|
+
const matches = result.messages?.matches ?? [];
|
|
386286
|
+
return matches.map((raw) => {
|
|
386287
|
+
const m2 = raw;
|
|
386288
|
+
return {
|
|
386289
|
+
channelId: m2.channel?.id,
|
|
386290
|
+
ts: m2.ts,
|
|
386291
|
+
text: m2.text,
|
|
386292
|
+
permalink: m2.permalink,
|
|
386293
|
+
username: m2.username
|
|
386294
|
+
};
|
|
386295
|
+
});
|
|
386296
|
+
} catch (error) {
|
|
386297
|
+
const message2 = error instanceof Error ? error.message : String(error);
|
|
386298
|
+
throw new SlackError(SlackErrorCode.SEND_FAILED, `Search failed: ${message2}`);
|
|
386299
|
+
}
|
|
386300
|
+
}
|
|
386301
|
+
async cancelScheduledMessage(instanceId, channelId, scheduledId) {
|
|
386302
|
+
const connection = this.getConnection(instanceId);
|
|
386303
|
+
await cancelScheduledSlackMessage(connection.actingClient, channelId, scheduledId, this.logger);
|
|
386304
|
+
}
|
|
386305
|
+
async getPermalink(instanceId, channelId, messageTs) {
|
|
386306
|
+
const connection = this.getConnection(instanceId);
|
|
386307
|
+
try {
|
|
386308
|
+
const result = await connection.actingClient.chat.getPermalink({
|
|
386309
|
+
channel: channelId,
|
|
386310
|
+
message_ts: messageTs
|
|
386311
|
+
});
|
|
386312
|
+
return result.permalink ?? null;
|
|
386313
|
+
} catch (error) {
|
|
386314
|
+
this.logger.warn("Failed to resolve permalink", {
|
|
386315
|
+
error: error instanceof Error ? error.message : String(error),
|
|
386316
|
+
channelId,
|
|
386317
|
+
messageTs
|
|
386318
|
+
});
|
|
386319
|
+
return null;
|
|
386320
|
+
}
|
|
385261
386321
|
}
|
|
385262
386322
|
async addReaction(instanceId, channelId, messageTs, emoji) {
|
|
385263
386323
|
const connection = this.getConnection(instanceId);
|
|
385264
386324
|
const { addReaction: addReaction3 } = await Promise.resolve().then(() => (init_tools(), exports_tools));
|
|
385265
|
-
await addReaction3(connection.
|
|
386325
|
+
await addReaction3(connection.actingClient, channelId, messageTs, emoji, this.logger);
|
|
385266
386326
|
}
|
|
385267
386327
|
async removeReaction(instanceId, channelId, messageTs, emoji) {
|
|
385268
386328
|
const connection = this.getConnection(instanceId);
|
|
385269
386329
|
const { removeReaction: removeReaction3 } = await Promise.resolve().then(() => (init_tools(), exports_tools));
|
|
385270
|
-
await removeReaction3(connection.
|
|
386330
|
+
await removeReaction3(connection.actingClient, channelId, messageTs, emoji, this.logger);
|
|
385271
386331
|
}
|
|
385272
386332
|
async getProfile(instanceId) {
|
|
385273
386333
|
const connection = this.getConnection(instanceId);
|
|
@@ -385286,7 +386346,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385286
386346
|
async fetchUserProfile(instanceId, userId) {
|
|
385287
386347
|
const connection = this.getConnection(instanceId);
|
|
385288
386348
|
try {
|
|
385289
|
-
const result = await connection.
|
|
386349
|
+
const result = await connection.actingClient.users.info({ user: userId });
|
|
385290
386350
|
const user = result.user;
|
|
385291
386351
|
if (!user)
|
|
385292
386352
|
return {};
|
|
@@ -385346,11 +386406,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385346
386406
|
if (!channelId || !threadTs)
|
|
385347
386407
|
return { totalFetched: 0, messages: [] };
|
|
385348
386408
|
const botUserId = connection.botUserId;
|
|
385349
|
-
const botToken =
|
|
385350
|
-
if (!botToken) {
|
|
385351
|
-
this.logger.warn("fetchHistory: no botToken for instance", { instanceId });
|
|
385352
|
-
return { totalFetched: 0, messages: [] };
|
|
385353
|
-
}
|
|
386409
|
+
const botToken = connection.botToken;
|
|
385354
386410
|
const limit = options.limit ?? 200;
|
|
385355
386411
|
const messages = await this.paginateThreadHistory(connection, channelId, threadTs, botUserId, botToken, limit);
|
|
385356
386412
|
return { totalFetched: messages.length, messages };
|
|
@@ -385359,7 +386415,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385359
386415
|
const messages = [];
|
|
385360
386416
|
let cursor;
|
|
385361
386417
|
do {
|
|
385362
|
-
const response = await connection.
|
|
386418
|
+
const response = await connection.actingClient.conversations.replies({
|
|
385363
386419
|
channel: channelId,
|
|
385364
386420
|
ts: threadTs,
|
|
385365
386421
|
limit: Math.min(200, maxMessages - messages.length),
|
|
@@ -385432,7 +386488,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385432
386488
|
const connection = this.getConnection(instanceId);
|
|
385433
386489
|
const slackName = SlackPlugin.EMOJI_TO_SLACK[emoji] ?? emoji.replace(/^:|:$/g, "");
|
|
385434
386490
|
try {
|
|
385435
|
-
await connection.
|
|
386491
|
+
await connection.actingClient.reactions.add({ channel: chatId, timestamp: messageId, name: slackName });
|
|
385436
386492
|
} catch (err2) {
|
|
385437
386493
|
this.logger.warn("react: failed to add reaction", { chatId, messageId, emoji, error: String(err2) });
|
|
385438
386494
|
}
|
|
@@ -385441,7 +386497,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385441
386497
|
const connection = this.getConnection(instanceId);
|
|
385442
386498
|
const slackName = SlackPlugin.EMOJI_TO_SLACK[emoji] ?? emoji.replace(/^:|:$/g, "");
|
|
385443
386499
|
try {
|
|
385444
|
-
await connection.
|
|
386500
|
+
await connection.actingClient.reactions.remove({ channel: chatId, timestamp: messageId, name: slackName });
|
|
385445
386501
|
} catch (err2) {
|
|
385446
386502
|
this.logger.warn("unreact: failed to remove reaction", { chatId, messageId, emoji, error: String(err2) });
|
|
385447
386503
|
}
|
|
@@ -385453,7 +386509,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385453
386509
|
const emojiName = typeof ackEmoji === "string" ? ackEmoji.replace(/^:|:$/g, "") : null;
|
|
385454
386510
|
if (!emojiName)
|
|
385455
386511
|
return;
|
|
385456
|
-
connection.
|
|
386512
|
+
connection.actingClient.reactions.add({ channel: channelId, timestamp: messageTs, name: emojiName }).catch((err2) => {
|
|
385457
386513
|
this.logger.warn("ack reaction: failed to add", { channelId, messageTs, emoji: emojiName, error: String(err2) });
|
|
385458
386514
|
});
|
|
385459
386515
|
if (config2.removeAckAfterReply !== false) {
|
|
@@ -385472,7 +386528,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385472
386528
|
if (!emojiName)
|
|
385473
386529
|
continue;
|
|
385474
386530
|
this.pendingAckReactions.delete(key);
|
|
385475
|
-
connection.
|
|
386531
|
+
connection.actingClient.reactions.remove({ channel: channelId, timestamp: ts, name: emojiName }).catch((err2) => {
|
|
385476
386532
|
this.logger.warn("ack reaction: failed to remove", { channelId, ts, emoji: emojiName, error: String(err2) });
|
|
385477
386533
|
});
|
|
385478
386534
|
break;
|
|
@@ -385486,7 +386542,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385486
386542
|
if (!resolvedThread)
|
|
385487
386543
|
return;
|
|
385488
386544
|
await clearTypingStatus({
|
|
385489
|
-
client: connection.
|
|
386545
|
+
client: connection.actingClient,
|
|
385490
386546
|
channelId,
|
|
385491
386547
|
threadTs: resolvedThread,
|
|
385492
386548
|
logger: this.logger
|
|
@@ -385532,14 +386588,16 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385532
386588
|
async buildEnrichedPayload(instanceId, from, rawPayload) {
|
|
385533
386589
|
const displayName = await this.resolveUserDisplayName(instanceId, from);
|
|
385534
386590
|
const isDm = rawPayload.isDm;
|
|
385535
|
-
const
|
|
386591
|
+
const isMpim = rawPayload.isMpim === true;
|
|
386592
|
+
const isOneToOne = isDm && !isMpim;
|
|
386593
|
+
const chatName = isOneToOne ? displayName : undefined;
|
|
385536
386594
|
return {
|
|
385537
386595
|
...rawPayload,
|
|
385538
386596
|
displayName,
|
|
385539
386597
|
senderName: displayName,
|
|
385540
386598
|
pushName: displayName,
|
|
385541
386599
|
chatName,
|
|
385542
|
-
isGroup: !
|
|
386600
|
+
isGroup: !isOneToOne
|
|
385543
386601
|
};
|
|
385544
386602
|
}
|
|
385545
386603
|
async dispatchMessageFromDebounce(instanceId, args) {
|
|
@@ -385570,7 +386628,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385570
386628
|
},
|
|
385571
386629
|
onDmRejected: async (_instId, channelId, _userId, message2) => {
|
|
385572
386630
|
try {
|
|
385573
|
-
await sendTextMessage2(connection.
|
|
386631
|
+
await sendTextMessage2(connection.actingClient, {
|
|
385574
386632
|
channelId,
|
|
385575
386633
|
text: message2,
|
|
385576
386634
|
formatMode: "passthrough"
|
|
@@ -385587,7 +386645,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385587
386645
|
channelAllowlist: config2.channelAllowlist,
|
|
385588
386646
|
channelBlocklist: config2.channelBlocklist,
|
|
385589
386647
|
channels: config2.channels
|
|
385590
|
-
}, reliability);
|
|
386648
|
+
}, reliability, () => connection.actingUserId);
|
|
385591
386649
|
setupReactionHandlers2(connection.app, instanceId, () => connection.botUserId, {
|
|
385592
386650
|
onReaction: async (instId, messageId, chatId, userId, emoji, action) => {
|
|
385593
386651
|
await this.handleReactionReceived(instId, messageId, chatId, userId, emoji, action);
|
|
@@ -385627,7 +386685,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385627
386685
|
const formatMode = message2.metadata?.messageFormatMode ?? "convert";
|
|
385628
386686
|
const replyToMode = config2.replyToMode ?? "all";
|
|
385629
386687
|
const threadTs = this.resolveThreadTs(replyToMode, message2.replyTo, message2.threadId);
|
|
385630
|
-
return sendTextMessage2(connection.
|
|
386688
|
+
return sendTextMessage2(connection.actingClient, {
|
|
385631
386689
|
channelId,
|
|
385632
386690
|
text: message2.content.text ?? "",
|
|
385633
386691
|
threadTs,
|
|
@@ -385645,7 +386703,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385645
386703
|
if (message2.metadata?.base64) {
|
|
385646
386704
|
const buffer2 = Buffer.from(message2.metadata.base64, "base64");
|
|
385647
386705
|
const filename = message2.content.filename || `file-${Date.now()}`;
|
|
385648
|
-
return uploadFile(connection.
|
|
386706
|
+
return uploadFile(connection.actingClient, {
|
|
385649
386707
|
channelId,
|
|
385650
386708
|
content: buffer2,
|
|
385651
386709
|
filename,
|
|
@@ -385656,7 +386714,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385656
386714
|
if (!message2.content.mediaUrl) {
|
|
385657
386715
|
throw new SlackError(SlackErrorCode.SEND_FAILED, "Media URL or base64 required");
|
|
385658
386716
|
}
|
|
385659
|
-
return uploadFileFromUrl(connection.
|
|
386717
|
+
return uploadFileFromUrl(connection.actingClient, {
|
|
385660
386718
|
channelId,
|
|
385661
386719
|
url: message2.content.mediaUrl,
|
|
385662
386720
|
filename: message2.content.filename || `file-${Date.now()}`,
|
|
@@ -385671,7 +386729,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385671
386729
|
throw new SlackError(SlackErrorCode.SEND_FAILED, "Reaction requires emoji and target message");
|
|
385672
386730
|
}
|
|
385673
386731
|
const { addReaction: addReaction3 } = await Promise.resolve().then(() => (init_tools(), exports_tools));
|
|
385674
|
-
await addReaction3(connection.
|
|
386732
|
+
await addReaction3(connection.actingClient, channelId, targetTs, emoji, this.logger);
|
|
385675
386733
|
return targetTs;
|
|
385676
386734
|
}
|
|
385677
386735
|
async handleInboundFiles(instanceId, externalId, chatId, from, content, replyToId, rawPayload, platformTimestamp) {
|
|
@@ -385697,6 +386755,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385697
386755
|
const timings = platformTimestamp ? this.captureInboundTimings(platformTimestamp) : undefined;
|
|
385698
386756
|
const senderName = typeof rawPayload.senderName === "string" ? rawPayload.senderName : typeof rawPayload.displayName === "string" ? rawPayload.displayName : undefined;
|
|
385699
386757
|
const chatName = typeof rawPayload.chatName === "string" ? rawPayload.chatName : undefined;
|
|
386758
|
+
const threadTs = typeof rawPayload.threadTs === "string" ? rawPayload.threadTs : undefined;
|
|
385700
386759
|
const correlationId = await this.emitMessageReceived({
|
|
385701
386760
|
instanceId,
|
|
385702
386761
|
externalId,
|
|
@@ -385705,7 +386764,7 @@ class SlackPlugin extends BaseChannelPlugin {
|
|
|
385705
386764
|
senderName,
|
|
385706
386765
|
chatName,
|
|
385707
386766
|
content,
|
|
385708
|
-
|
|
386767
|
+
threadId: threadTs,
|
|
385709
386768
|
rawPayload,
|
|
385710
386769
|
timings
|
|
385711
386770
|
});
|
|
@@ -387055,14 +388114,14 @@ var protectedSplitters = {
|
|
|
387055
388114
|
expandable_blockquote: trySplitBlockquote,
|
|
387056
388115
|
plain: () => null
|
|
387057
388116
|
};
|
|
387058
|
-
function splitLongProtectedSegment(
|
|
387059
|
-
if (
|
|
387060
|
-
return [
|
|
387061
|
-
const splitter = protectedSplitters[
|
|
387062
|
-
const result = splitter(
|
|
388117
|
+
function splitLongProtectedSegment(segment2, maxLength) {
|
|
388118
|
+
if (segment2.value.length <= maxLength)
|
|
388119
|
+
return [segment2.value];
|
|
388120
|
+
const splitter = protectedSplitters[segment2.type];
|
|
388121
|
+
const result = splitter(segment2.value, maxLength);
|
|
387063
388122
|
if (result)
|
|
387064
388123
|
return result;
|
|
387065
|
-
return splitPlainByBoundaries(
|
|
388124
|
+
return splitPlainByBoundaries(segment2.value, maxLength);
|
|
387066
388125
|
}
|
|
387067
388126
|
function findNextProtected(html, from) {
|
|
387068
388127
|
const patterns = [
|
|
@@ -387146,12 +388205,12 @@ class ChunkAccumulator {
|
|
|
387146
388205
|
return this.chunks.length > 0 ? this.chunks : [""];
|
|
387147
388206
|
}
|
|
387148
388207
|
}
|
|
387149
|
-
function processPlainSegment(acc,
|
|
387150
|
-
const plainParts = splitPlainHtmlPreservingTags(
|
|
388208
|
+
function processPlainSegment(acc, segment2, maxLength) {
|
|
388209
|
+
const plainParts = splitPlainHtmlPreservingTags(segment2.value, maxLength);
|
|
387151
388210
|
acc.appendParts(plainParts);
|
|
387152
388211
|
}
|
|
387153
|
-
function processProtectedSegment(acc,
|
|
387154
|
-
const protectedParts = splitLongProtectedSegment(
|
|
388212
|
+
function processProtectedSegment(acc, segment2, maxLength) {
|
|
388213
|
+
const protectedParts = splitLongProtectedSegment(segment2, maxLength);
|
|
387155
388214
|
for (const part of protectedParts) {
|
|
387156
388215
|
if (part.length > maxLength) {
|
|
387157
388216
|
const hardParts = splitPlainByBoundaries(part, maxLength);
|
|
@@ -387166,11 +388225,11 @@ function splitHtmlMessage(html, maxLength = 4096) {
|
|
|
387166
388225
|
return [html];
|
|
387167
388226
|
const segments = segmentHtml(html);
|
|
387168
388227
|
const acc = new ChunkAccumulator(maxLength);
|
|
387169
|
-
for (const
|
|
387170
|
-
if (
|
|
387171
|
-
processPlainSegment(acc,
|
|
388228
|
+
for (const segment2 of segments) {
|
|
388229
|
+
if (segment2.type === "plain") {
|
|
388230
|
+
processPlainSegment(acc, segment2, maxLength);
|
|
387172
388231
|
} else {
|
|
387173
|
-
processProtectedSegment(acc,
|
|
388232
|
+
processProtectedSegment(acc, segment2, maxLength);
|
|
387174
388233
|
}
|
|
387175
388234
|
}
|
|
387176
388235
|
return acc.result();
|
|
@@ -477591,6 +478650,7 @@ for (const [i, DOUBLE_BYTE_TOKEN] of DOUBLE_BYTE_TOKENS.entries()) {
|
|
|
477591
478650
|
|
|
477592
478651
|
// ../../node_modules/.bun/baileys@vendor+baileys-v7.0.0-rc10.tgz+89588f7e3fe8b2e6/node_modules/baileys/lib/WABinary/jid-utils.js
|
|
477593
478652
|
var S_WHATSAPP_NET = "@s.whatsapp.net";
|
|
478653
|
+
var SERVER_JID = "server@c.us";
|
|
477594
478654
|
var PSA_WID = "0@c.us";
|
|
477595
478655
|
var WAJIDDomains;
|
|
477596
478656
|
(function(WAJIDDomains2) {
|
|
@@ -490823,6 +491883,181 @@ function buildAckStanza(node, errorCode, meId) {
|
|
|
490823
491883
|
}
|
|
490824
491884
|
return stanza;
|
|
490825
491885
|
}
|
|
491886
|
+
// ../../node_modules/.bun/baileys@vendor+baileys-v7.0.0-rc10.tgz+89588f7e3fe8b2e6/node_modules/baileys/lib/Utils/passkey.js
|
|
491887
|
+
var BASE64URL_PATTERN = /^[A-Za-z0-9_-]+$/;
|
|
491888
|
+
var PASSKEY_HANDOFF_INFO = "shortcake-passkey-handoff-v1";
|
|
491889
|
+
var PAIRING_ENCRYPTION_INFO = "Pairing Information Encryption Key";
|
|
491890
|
+
var assertBase64Url = (value, field, nullable = false) => {
|
|
491891
|
+
if (nullable && value === null) {
|
|
491892
|
+
return null;
|
|
491893
|
+
}
|
|
491894
|
+
if (typeof value !== "string" || !value || !BASE64URL_PATTERN.test(value)) {
|
|
491895
|
+
throw new Error(`Invalid ${field} in passkey payload`);
|
|
491896
|
+
}
|
|
491897
|
+
return value;
|
|
491898
|
+
};
|
|
491899
|
+
var parseAllowedCredentials = (value) => {
|
|
491900
|
+
if (value === undefined) {
|
|
491901
|
+
return;
|
|
491902
|
+
}
|
|
491903
|
+
if (!Array.isArray(value)) {
|
|
491904
|
+
throw new Error("Invalid allowCredentials in passkey request");
|
|
491905
|
+
}
|
|
491906
|
+
return value.map((credential, index) => {
|
|
491907
|
+
if (!credential || typeof credential !== "object") {
|
|
491908
|
+
throw new Error(`Invalid allowCredentials[${index}] in passkey request`);
|
|
491909
|
+
}
|
|
491910
|
+
const candidate = credential;
|
|
491911
|
+
if (typeof candidate.type !== "string" || !candidate.type) {
|
|
491912
|
+
throw new Error(`Invalid allowCredentials[${index}].type in passkey request`);
|
|
491913
|
+
}
|
|
491914
|
+
if (candidate.transports !== undefined && (!Array.isArray(candidate.transports) || candidate.transports.some((item) => typeof item !== "string"))) {
|
|
491915
|
+
throw new Error(`Invalid allowCredentials[${index}].transports in passkey request`);
|
|
491916
|
+
}
|
|
491917
|
+
return {
|
|
491918
|
+
id: assertBase64Url(candidate.id, `allowCredentials[${index}].id`),
|
|
491919
|
+
type: candidate.type,
|
|
491920
|
+
...candidate.transports ? { transports: candidate.transports } : {}
|
|
491921
|
+
};
|
|
491922
|
+
});
|
|
491923
|
+
};
|
|
491924
|
+
var parsePasskeyRequestOptions = (content) => {
|
|
491925
|
+
let parsed;
|
|
491926
|
+
try {
|
|
491927
|
+
parsed = JSON.parse(content.toString("utf8"));
|
|
491928
|
+
} catch {
|
|
491929
|
+
throw new Error("WhatsApp sent malformed passkey request JSON");
|
|
491930
|
+
}
|
|
491931
|
+
if (!parsed || typeof parsed !== "object") {
|
|
491932
|
+
throw new Error("WhatsApp sent an invalid passkey request");
|
|
491933
|
+
}
|
|
491934
|
+
const value = parsed;
|
|
491935
|
+
if (typeof value.rpId !== "string" || value.rpId !== "whatsapp.com") {
|
|
491936
|
+
throw new Error("WhatsApp sent an unexpected passkey relying party");
|
|
491937
|
+
}
|
|
491938
|
+
if (value.timeout !== undefined && (typeof value.timeout !== "number" || value.timeout <= 0)) {
|
|
491939
|
+
throw new Error("WhatsApp sent an invalid passkey timeout");
|
|
491940
|
+
}
|
|
491941
|
+
if (value.userVerification !== undefined && typeof value.userVerification !== "string") {
|
|
491942
|
+
throw new Error("WhatsApp sent an invalid passkey user verification mode");
|
|
491943
|
+
}
|
|
491944
|
+
if (value.extensions !== undefined && (!value.extensions || typeof value.extensions !== "object")) {
|
|
491945
|
+
throw new Error("WhatsApp sent invalid passkey extensions");
|
|
491946
|
+
}
|
|
491947
|
+
return {
|
|
491948
|
+
challenge: assertBase64Url(value.challenge, "challenge"),
|
|
491949
|
+
rpId: value.rpId,
|
|
491950
|
+
...value.timeout === undefined ? {} : { timeout: value.timeout },
|
|
491951
|
+
...value.allowCredentials === undefined ? {} : { allowCredentials: parseAllowedCredentials(value.allowCredentials) },
|
|
491952
|
+
...value.userVerification === undefined ? {} : { userVerification: value.userVerification },
|
|
491953
|
+
...value.extensions === undefined ? {} : { extensions: value.extensions }
|
|
491954
|
+
};
|
|
491955
|
+
};
|
|
491956
|
+
var parsePasskeyCredentialResponse = (input) => {
|
|
491957
|
+
if (!input || typeof input !== "object") {
|
|
491958
|
+
throw new Error("Invalid passkey credential response");
|
|
491959
|
+
}
|
|
491960
|
+
const value = input;
|
|
491961
|
+
if (value.type !== "public-key") {
|
|
491962
|
+
throw new Error("Invalid passkey credential type");
|
|
491963
|
+
}
|
|
491964
|
+
if (!value.response || typeof value.response !== "object") {
|
|
491965
|
+
throw new Error("Invalid passkey authenticator response");
|
|
491966
|
+
}
|
|
491967
|
+
const response = value.response;
|
|
491968
|
+
return {
|
|
491969
|
+
id: assertBase64Url(value.id, "credential id"),
|
|
491970
|
+
rawId: assertBase64Url(value.rawId, "raw credential id"),
|
|
491971
|
+
type: value.type,
|
|
491972
|
+
response: {
|
|
491973
|
+
clientDataJSON: assertBase64Url(response.clientDataJSON, "clientDataJSON"),
|
|
491974
|
+
authenticatorData: assertBase64Url(response.authenticatorData, "authenticatorData"),
|
|
491975
|
+
signature: assertBase64Url(response.signature, "signature"),
|
|
491976
|
+
userHandle: assertBase64Url(response.userHandle, "userHandle", true)
|
|
491977
|
+
}
|
|
491978
|
+
};
|
|
491979
|
+
};
|
|
491980
|
+
var getPasskeyDeviceType = (browser) => {
|
|
491981
|
+
const browserName = browser[1].toLowerCase();
|
|
491982
|
+
if (browserName.includes("firefox"))
|
|
491983
|
+
return proto.DeviceProps.PlatformType.FIREFOX;
|
|
491984
|
+
if (browserName.includes("edge"))
|
|
491985
|
+
return proto.DeviceProps.PlatformType.EDGE;
|
|
491986
|
+
if (browserName.includes("opera"))
|
|
491987
|
+
return proto.DeviceProps.PlatformType.OPERA;
|
|
491988
|
+
if (browserName.includes("safari"))
|
|
491989
|
+
return proto.DeviceProps.PlatformType.SAFARI;
|
|
491990
|
+
return proto.DeviceProps.PlatformType.CHROME;
|
|
491991
|
+
};
|
|
491992
|
+
var derivePasskeyHandoffKey = (advSecretKey) => Buffer.from(hkdf(Buffer.from(advSecretKey, "base64"), 32, { info: PASSKEY_HANDOFF_INFO }));
|
|
491993
|
+
var buildPasskeyPrologue = ({ credential, pairingRef, browser, handoffKey, keyPair = Curve.generateKeyPair(), companionNonce }) => {
|
|
491994
|
+
if (companionNonce.length !== 32) {
|
|
491995
|
+
throw new Error("Passkey companion nonce must contain 32 bytes");
|
|
491996
|
+
}
|
|
491997
|
+
const deviceType = getPasskeyDeviceType(browser);
|
|
491998
|
+
const identity = Buffer.from(proto.CompanionEphemeralIdentity.encode({
|
|
491999
|
+
publicKey: keyPair.public,
|
|
492000
|
+
deviceType,
|
|
492001
|
+
ref: pairingRef
|
|
492002
|
+
}).finish());
|
|
492003
|
+
const commitment = sha256(Buffer.concat([identity, companionNonce]));
|
|
492004
|
+
const payload = Buffer.from(proto.ProloguePayload.encode({
|
|
492005
|
+
companionEphemeralIdentity: identity,
|
|
492006
|
+
commitment: { hash: commitment }
|
|
492007
|
+
}).finish());
|
|
492008
|
+
const content = [
|
|
492009
|
+
{ tag: "credential_id", attrs: {}, content: Buffer.from(credential.rawId, "base64url") },
|
|
492010
|
+
{ tag: "webauthn_assertion", attrs: {}, content: Buffer.from(JSON.stringify(credential), "utf8") },
|
|
492011
|
+
{ tag: "prologue_payload", attrs: {}, content: payload }
|
|
492012
|
+
];
|
|
492013
|
+
if (handoffKey) {
|
|
492014
|
+
content.push({ tag: "pairing_handoff_proof", attrs: {}, content: hmacSign(payload, handoffKey) });
|
|
492015
|
+
}
|
|
492016
|
+
return {
|
|
492017
|
+
content,
|
|
492018
|
+
payload,
|
|
492019
|
+
cache: { keyPair, companionNonce, pairingRef, deviceType },
|
|
492020
|
+
skipHandoffUX: Boolean(handoffKey)
|
|
492021
|
+
};
|
|
492022
|
+
};
|
|
492023
|
+
var continuePasskeyPairing = ({ cache: cache2, primaryIdentity }) => {
|
|
492024
|
+
const publicKey = primaryIdentity.publicKey ? Buffer.from(primaryIdentity.publicKey) : undefined;
|
|
492025
|
+
const nonce = primaryIdentity.nonce ? Buffer.from(primaryIdentity.nonce) : undefined;
|
|
492026
|
+
if (!publicKey || publicKey.length !== 32) {
|
|
492027
|
+
throw new Error("Invalid primary passkey public key");
|
|
492028
|
+
}
|
|
492029
|
+
if (!nonce || nonce.length !== 32) {
|
|
492030
|
+
throw new Error("Invalid primary passkey nonce");
|
|
492031
|
+
}
|
|
492032
|
+
const sharedSecret = Curve.sharedKey(cache2.keyPair.private, publicKey);
|
|
492033
|
+
const salt = `Companion Pairing ${cache2.deviceType} with ref ${cache2.pairingRef}`;
|
|
492034
|
+
const encryptionKey = Buffer.from(hkdf(sharedSecret, 32, { salt: Buffer.from(salt, "utf8"), info: PAIRING_ENCRYPTION_INFO }));
|
|
492035
|
+
const digest = sha256(Buffer.concat([cache2.companionNonce, publicKey]));
|
|
492036
|
+
const codeBytes = Buffer.alloc(5);
|
|
492037
|
+
for (let index = 0;index < codeBytes.length; index += 1) {
|
|
492038
|
+
codeBytes[index] = nonce[index] ^ digest[index];
|
|
492039
|
+
}
|
|
492040
|
+
const encodedCode = bytesToCrockford(codeBytes);
|
|
492041
|
+
return {
|
|
492042
|
+
cache: { ...cache2, encryptionKey },
|
|
492043
|
+
code: `${encodedCode.slice(0, 4)}-${encodedCode.slice(4)}`
|
|
492044
|
+
};
|
|
492045
|
+
};
|
|
492046
|
+
var buildEncryptedPasskeyPairingRequest = ({ cache: cache2, noisePublicKey, identityPublicKey, advSecretKey, iv }) => {
|
|
492047
|
+
if (!cache2.encryptionKey) {
|
|
492048
|
+
throw new Error("Passkey confirmation is not ready");
|
|
492049
|
+
}
|
|
492050
|
+
if (iv.length !== 12) {
|
|
492051
|
+
throw new Error("Passkey encryption IV must contain 12 bytes");
|
|
492052
|
+
}
|
|
492053
|
+
const request = Buffer.from(proto.PairingRequest.encode({
|
|
492054
|
+
companionPublicKey: noisePublicKey,
|
|
492055
|
+
companionIdentityKey: identityPublicKey,
|
|
492056
|
+
advSecret: Buffer.from(advSecretKey, "base64")
|
|
492057
|
+
}).finish());
|
|
492058
|
+
const encryptedPayload = aesEncryptGCM(request, cache2.encryptionKey, iv, Buffer.alloc(0));
|
|
492059
|
+
return Buffer.from(proto.EncryptedPairingRequest.encode({ encryptedPayload, iv }).finish());
|
|
492060
|
+
};
|
|
490826
492061
|
// ../../node_modules/.bun/baileys@vendor+baileys-v7.0.0-rc10.tgz+89588f7e3fe8b2e6/node_modules/baileys/lib/Signal/Group/sender-key-name.js
|
|
490827
492062
|
function isNull(str) {
|
|
490828
492063
|
return str === null || str === "";
|
|
@@ -490874,14 +492109,14 @@ class SenderKeyName2 {
|
|
|
490874
492109
|
}
|
|
490875
492110
|
|
|
490876
492111
|
// ../../node_modules/.bun/baileys@vendor+baileys-v7.0.0-rc10.tgz+89588f7e3fe8b2e6/node_modules/baileys/lib/Signal/Group/sender-chain-key.js
|
|
490877
|
-
var
|
|
492112
|
+
var import_crypto19 = __toESM(require_crypto(), 1);
|
|
490878
492113
|
|
|
490879
492114
|
// ../../node_modules/.bun/baileys@vendor+baileys-v7.0.0-rc10.tgz+89588f7e3fe8b2e6/node_modules/baileys/lib/Signal/Group/sender-message-key.js
|
|
490880
|
-
var
|
|
492115
|
+
var import_crypto18 = __toESM(require_crypto(), 1);
|
|
490881
492116
|
|
|
490882
492117
|
class SenderMessageKey {
|
|
490883
492118
|
constructor(iteration, seed) {
|
|
490884
|
-
const derivative =
|
|
492119
|
+
const derivative = import_crypto18.deriveSecrets(seed, Buffer.alloc(32), Buffer.from("WhisperGroup"));
|
|
490885
492120
|
const keys = new Uint8Array(32);
|
|
490886
492121
|
keys.set(new Uint8Array(derivative[0].slice(16)));
|
|
490887
492122
|
keys.set(new Uint8Array(derivative[1].slice(0, 16)), 16);
|
|
@@ -490925,7 +492160,7 @@ class SenderChainKey {
|
|
|
490925
492160
|
return this.chainKey;
|
|
490926
492161
|
}
|
|
490927
492162
|
getDerivative(seed, key) {
|
|
490928
|
-
return
|
|
492163
|
+
return import_crypto19.calculateMAC(key, seed);
|
|
490929
492164
|
}
|
|
490930
492165
|
}
|
|
490931
492166
|
|
|
@@ -491162,7 +492397,7 @@ class GroupSessionBuilder2 {
|
|
|
491162
492397
|
}
|
|
491163
492398
|
}
|
|
491164
492399
|
// ../../node_modules/.bun/baileys@vendor+baileys-v7.0.0-rc10.tgz+89588f7e3fe8b2e6/node_modules/baileys/lib/Signal/Group/group_cipher.js
|
|
491165
|
-
var
|
|
492400
|
+
var import_crypto20 = __toESM(require_crypto(), 1);
|
|
491166
492401
|
|
|
491167
492402
|
// ../../node_modules/.bun/baileys@vendor+baileys-v7.0.0-rc10.tgz+89588f7e3fe8b2e6/node_modules/baileys/lib/Signal/Group/sender-key-message.js
|
|
491168
492403
|
var import_curve2 = __toESM(require_curve2(), 1);
|
|
@@ -491287,14 +492522,14 @@ class GroupCipher2 {
|
|
|
491287
492522
|
}
|
|
491288
492523
|
async getPlainText(iv, key, ciphertext) {
|
|
491289
492524
|
try {
|
|
491290
|
-
return
|
|
492525
|
+
return import_crypto20.decrypt(key, ciphertext, iv);
|
|
491291
492526
|
} catch (e) {
|
|
491292
492527
|
throw new Error("InvalidMessageException");
|
|
491293
492528
|
}
|
|
491294
492529
|
}
|
|
491295
492530
|
async getCipherText(iv, key, plaintext) {
|
|
491296
492531
|
try {
|
|
491297
|
-
return
|
|
492532
|
+
return import_crypto20.encrypt(key, plaintext, iv);
|
|
491298
492533
|
} catch (e) {
|
|
491299
492534
|
throw new Error("InvalidMessageException");
|
|
491300
492535
|
}
|
|
@@ -493982,6 +495217,9 @@ var makeSocket = (config2) => {
|
|
|
493982
495217
|
let epoch = 1;
|
|
493983
495218
|
let keepAliveReq;
|
|
493984
495219
|
let qrTimer;
|
|
495220
|
+
let passkeyHandoff;
|
|
495221
|
+
let passkeyLinkingCache;
|
|
495222
|
+
let passkeySkipHandoffUX = false;
|
|
493985
495223
|
let closed = false;
|
|
493986
495224
|
const socketEndHandlers = [];
|
|
493987
495225
|
const onUnexpectedError = (err2, msg) => {
|
|
@@ -494337,6 +495575,156 @@ var makeSocket = (config2) => {
|
|
|
494337
495575
|
const ciphered = aesEncryptCTR(authState.creds.pairingEphemeralKeyPair.public, key, randomIv);
|
|
494338
495576
|
return Buffer.concat([salt, randomIv, ciphered]);
|
|
494339
495577
|
}
|
|
495578
|
+
const getPasskeyRequestOptions = async (notification) => {
|
|
495579
|
+
let optionsNode = notification ? getBinaryNodeChild(notification, "passkey_request_options") : undefined;
|
|
495580
|
+
if (!optionsNode) {
|
|
495581
|
+
const response = await query({
|
|
495582
|
+
tag: "iq",
|
|
495583
|
+
attrs: {
|
|
495584
|
+
to: S_WHATSAPP_NET,
|
|
495585
|
+
type: "get",
|
|
495586
|
+
xmlns: "md"
|
|
495587
|
+
},
|
|
495588
|
+
content: [{ tag: "passkey_request_options", attrs: {} }]
|
|
495589
|
+
});
|
|
495590
|
+
optionsNode = getBinaryNodeChild(response, "passkey_request_options");
|
|
495591
|
+
}
|
|
495592
|
+
if (!optionsNode?.content || Array.isArray(optionsNode.content)) {
|
|
495593
|
+
throw new Error("Passkey request did not contain public key options");
|
|
495594
|
+
}
|
|
495595
|
+
return parsePasskeyRequestOptions(Buffer.from(optionsNode.content));
|
|
495596
|
+
};
|
|
495597
|
+
const handlePasskeyRequest = async (notification) => {
|
|
495598
|
+
if (notification.attrs.from !== S_WHATSAPP_NET && notification.attrs.from !== SERVER_JID) {
|
|
495599
|
+
logger5.warn({ from: notification.attrs.from }, "ignored passkey request from unexpected sender");
|
|
495600
|
+
return;
|
|
495601
|
+
}
|
|
495602
|
+
try {
|
|
495603
|
+
const publicKey = await getPasskeyRequestOptions(notification);
|
|
495604
|
+
clearTimeout(qrTimer);
|
|
495605
|
+
passkeyHandoff = {
|
|
495606
|
+
key: derivePasskeyHandoffKey(creds.advSecretKey),
|
|
495607
|
+
createdAt: Date.now()
|
|
495608
|
+
};
|
|
495609
|
+
creds.advSecretKey = randomBytes9(32).toString("base64");
|
|
495610
|
+
ev.emit("creds.update", { advSecretKey: creds.advSecretKey });
|
|
495611
|
+
ev.emit("connection.update", {
|
|
495612
|
+
qr: undefined,
|
|
495613
|
+
passkey: { state: "request", publicKey }
|
|
495614
|
+
});
|
|
495615
|
+
} catch (error) {
|
|
495616
|
+
const message2 = error instanceof Error ? error.message : "Unable to process passkey request";
|
|
495617
|
+
logger5.warn({ err: error }, "failed to process passkey request");
|
|
495618
|
+
ev.emit("connection.update", {
|
|
495619
|
+
passkey: { state: "error", phase: "request", message: message2 }
|
|
495620
|
+
});
|
|
495621
|
+
}
|
|
495622
|
+
};
|
|
495623
|
+
const sendPasskeyResponse = async (input) => {
|
|
495624
|
+
const credential = parsePasskeyCredentialResponse(input);
|
|
495625
|
+
const refResponse = await query({
|
|
495626
|
+
tag: "iq",
|
|
495627
|
+
attrs: {
|
|
495628
|
+
to: S_WHATSAPP_NET,
|
|
495629
|
+
type: "get",
|
|
495630
|
+
xmlns: "md"
|
|
495631
|
+
},
|
|
495632
|
+
content: [{ tag: "ref", attrs: {} }]
|
|
495633
|
+
});
|
|
495634
|
+
const refNode = getBinaryNodeChild(refResponse, "ref");
|
|
495635
|
+
if (!refNode?.content || Array.isArray(refNode.content)) {
|
|
495636
|
+
throw new Error("WhatsApp did not provide a companion reference");
|
|
495637
|
+
}
|
|
495638
|
+
const handoffKey = passkeyHandoff && Date.now() - passkeyHandoff.createdAt < 5 * 60000 ? passkeyHandoff.key : undefined;
|
|
495639
|
+
const prologue = buildPasskeyPrologue({
|
|
495640
|
+
credential,
|
|
495641
|
+
pairingRef: Buffer.from(refNode.content).toString("utf8"),
|
|
495642
|
+
browser,
|
|
495643
|
+
handoffKey,
|
|
495644
|
+
companionNonce: randomBytes9(32)
|
|
495645
|
+
});
|
|
495646
|
+
passkeyLinkingCache = prologue.cache;
|
|
495647
|
+
passkeySkipHandoffUX = prologue.skipHandoffUX;
|
|
495648
|
+
await query({
|
|
495649
|
+
tag: "iq",
|
|
495650
|
+
attrs: {
|
|
495651
|
+
to: S_WHATSAPP_NET,
|
|
495652
|
+
type: "set",
|
|
495653
|
+
xmlns: "md"
|
|
495654
|
+
},
|
|
495655
|
+
content: [{ tag: "passkey_prologue", attrs: {}, content: prologue.content }]
|
|
495656
|
+
});
|
|
495657
|
+
passkeyHandoff = undefined;
|
|
495658
|
+
};
|
|
495659
|
+
const handlePasskeyContinuation = async (notification) => {
|
|
495660
|
+
if (notification.attrs.from !== S_WHATSAPP_NET && notification.attrs.from !== SERVER_JID) {
|
|
495661
|
+
logger5.warn({ from: notification.attrs.from }, "ignored passkey continuation from unexpected sender");
|
|
495662
|
+
return;
|
|
495663
|
+
}
|
|
495664
|
+
try {
|
|
495665
|
+
if (!passkeyLinkingCache) {
|
|
495666
|
+
throw new Error("Passkey continuation arrived without an active ceremony");
|
|
495667
|
+
}
|
|
495668
|
+
const identityNode = getBinaryNodeChild(notification, "primary_ephemeral_identity");
|
|
495669
|
+
if (!identityNode?.content || Array.isArray(identityNode.content)) {
|
|
495670
|
+
throw new Error("Passkey continuation did not contain a primary identity");
|
|
495671
|
+
}
|
|
495672
|
+
const primaryIdentity = proto.PrimaryEphemeralIdentity.decode(Buffer.from(identityNode.content));
|
|
495673
|
+
const continuation = continuePasskeyPairing({ cache: passkeyLinkingCache, primaryIdentity });
|
|
495674
|
+
passkeyLinkingCache = continuation.cache;
|
|
495675
|
+
await query({
|
|
495676
|
+
tag: "iq",
|
|
495677
|
+
attrs: {
|
|
495678
|
+
to: S_WHATSAPP_NET,
|
|
495679
|
+
type: "set",
|
|
495680
|
+
xmlns: "md"
|
|
495681
|
+
},
|
|
495682
|
+
content: [
|
|
495683
|
+
{
|
|
495684
|
+
tag: "companion_nonce",
|
|
495685
|
+
attrs: {},
|
|
495686
|
+
content: passkeyLinkingCache.companionNonce
|
|
495687
|
+
}
|
|
495688
|
+
]
|
|
495689
|
+
});
|
|
495690
|
+
ev.emit("connection.update", {
|
|
495691
|
+
passkey: {
|
|
495692
|
+
state: "confirmation",
|
|
495693
|
+
code: continuation.code,
|
|
495694
|
+
skipHandoffUX: passkeySkipHandoffUX
|
|
495695
|
+
}
|
|
495696
|
+
});
|
|
495697
|
+
} catch (error) {
|
|
495698
|
+
const message2 = error instanceof Error ? error.message : "Unable to continue passkey pairing";
|
|
495699
|
+
logger5.warn({ err: error }, "failed to process passkey continuation");
|
|
495700
|
+
ev.emit("connection.update", {
|
|
495701
|
+
passkey: { state: "error", phase: "continuation", message: message2 }
|
|
495702
|
+
});
|
|
495703
|
+
}
|
|
495704
|
+
};
|
|
495705
|
+
const sendPasskeyConfirmation = async () => {
|
|
495706
|
+
if (!passkeyLinkingCache) {
|
|
495707
|
+
throw new Error("No passkey confirmation is pending");
|
|
495708
|
+
}
|
|
495709
|
+
const encryptedRequest = buildEncryptedPasskeyPairingRequest({
|
|
495710
|
+
cache: passkeyLinkingCache,
|
|
495711
|
+
noisePublicKey: creds.noiseKey.public,
|
|
495712
|
+
identityPublicKey: creds.signedIdentityKey.public,
|
|
495713
|
+
advSecretKey: creds.advSecretKey,
|
|
495714
|
+
iv: randomBytes9(12)
|
|
495715
|
+
});
|
|
495716
|
+
await query({
|
|
495717
|
+
tag: "iq",
|
|
495718
|
+
attrs: {
|
|
495719
|
+
to: S_WHATSAPP_NET,
|
|
495720
|
+
type: "set",
|
|
495721
|
+
xmlns: "md"
|
|
495722
|
+
},
|
|
495723
|
+
content: [{ tag: "encrypted_pairing_request", attrs: {}, content: encryptedRequest }]
|
|
495724
|
+
});
|
|
495725
|
+
passkeyLinkingCache = undefined;
|
|
495726
|
+
passkeySkipHandoffUX = false;
|
|
495727
|
+
};
|
|
494340
495728
|
const sendWAMBuffer = (wamBuffer) => {
|
|
494341
495729
|
return query({
|
|
494342
495730
|
tag: "iq",
|
|
@@ -494366,6 +495754,12 @@ var makeSocket = (config2) => {
|
|
|
494366
495754
|
ws.on("error", mapWebSocketError(end));
|
|
494367
495755
|
ws.on("close", () => void end(new import_boom14.Boom("Connection Terminated", { statusCode: DisconnectReason.connectionClosed })));
|
|
494368
495756
|
ws.on("CB:xmlstreamend", () => void end(new import_boom14.Boom("Connection Terminated by Server", { statusCode: DisconnectReason.connectionClosed })));
|
|
495757
|
+
ws.on("CB:notification,type:passkey_prologue_request", (notification) => {
|
|
495758
|
+
handlePasskeyRequest(notification);
|
|
495759
|
+
});
|
|
495760
|
+
ws.on("CB:notification,type:crsc_continuation", (notification) => {
|
|
495761
|
+
handlePasskeyContinuation(notification);
|
|
495762
|
+
});
|
|
494369
495763
|
ws.on("CB:iq,type:set,pair-device", async (stanza) => {
|
|
494370
495764
|
const iq = {
|
|
494371
495765
|
tag: "iq",
|
|
@@ -494593,6 +495987,8 @@ var makeSocket = (config2) => {
|
|
|
494593
495987
|
digestKeyBundle,
|
|
494594
495988
|
rotateSignedPreKey,
|
|
494595
495989
|
requestPairingCode,
|
|
495990
|
+
sendPasskeyResponse,
|
|
495991
|
+
sendPasskeyConfirmation,
|
|
494596
495992
|
updateServerTimeOffset,
|
|
494597
495993
|
sendUnifiedSession,
|
|
494598
495994
|
wamBuffer: publicWAMBuffer,
|
|
@@ -500123,6 +501519,7 @@ var MAX_QR_ATTEMPTS = 3;
|
|
|
500123
501519
|
var qrCycleAttempts = new Map;
|
|
500124
501520
|
var MAX_QR_CYCLES = 2;
|
|
500125
501521
|
var activeQrCodes = new Map;
|
|
501522
|
+
var passkeyInstances = new Set;
|
|
500126
501523
|
var connectionTimeouts = new Map;
|
|
500127
501524
|
var CONNECTION_TIMEOUT_MS = 45000;
|
|
500128
501525
|
var authenticatedInstances = new Set;
|
|
@@ -500219,6 +501616,7 @@ async function handleConnectionClose(plugin7, instanceId, lastDisconnect, config
|
|
|
500219
501616
|
qrCodeAttempts.delete(instanceId);
|
|
500220
501617
|
qrCycleAttempts.delete(instanceId);
|
|
500221
501618
|
authenticatedInstances.delete(instanceId);
|
|
501619
|
+
passkeyInstances.delete(instanceId);
|
|
500222
501620
|
cancelPendingReconnect(instanceId);
|
|
500223
501621
|
await plugin7.handleDisconnected(instanceId, "Logged out from WhatsApp", false);
|
|
500224
501622
|
return;
|
|
@@ -500266,6 +501664,7 @@ async function handleConnectionOpen(plugin7, instanceId, sock) {
|
|
|
500266
501664
|
qrCodeAttempts.delete(instanceId);
|
|
500267
501665
|
qrCycleAttempts.delete(instanceId);
|
|
500268
501666
|
activeQrCodes.delete(instanceId);
|
|
501667
|
+
passkeyInstances.delete(instanceId);
|
|
500269
501668
|
clearConnectionTimeout(instanceId);
|
|
500270
501669
|
authenticatedInstances.add(instanceId);
|
|
500271
501670
|
log51.info("Connection opened", { instanceId });
|
|
@@ -500273,8 +501672,16 @@ async function handleConnectionOpen(plugin7, instanceId, sock) {
|
|
|
500273
501672
|
}
|
|
500274
501673
|
function setupConnectionHandlers2(sock, plugin7, instanceId, onReconnect, clearAuthAndReconnect, config2 = DEFAULT_RECONNECT_CONFIG2) {
|
|
500275
501674
|
sock.ev.on("connection.update", async (update) => {
|
|
500276
|
-
const { connection, lastDisconnect, qr } = update;
|
|
500277
|
-
if (
|
|
501675
|
+
const { connection, lastDisconnect, passkey: passkey2, qr } = update;
|
|
501676
|
+
if (passkey2) {
|
|
501677
|
+
passkeyInstances.add(instanceId);
|
|
501678
|
+
activeQrCodes.delete(instanceId);
|
|
501679
|
+
qrCodeAttempts.delete(instanceId);
|
|
501680
|
+
qrCycleAttempts.delete(instanceId);
|
|
501681
|
+
clearConnectionTimeout(instanceId);
|
|
501682
|
+
await plugin7.handlePasskeyUpdate(instanceId, passkey2);
|
|
501683
|
+
}
|
|
501684
|
+
if (qr && !passkey2 && !passkeyInstances.has(instanceId)) {
|
|
500278
501685
|
const shouldContinue = await handleQrCode(plugin7, instanceId, qr, clearAuthAndReconnect);
|
|
500279
501686
|
if (!shouldContinue) {
|
|
500280
501687
|
return;
|
|
@@ -500301,6 +501708,7 @@ function resetConnectionState2(instanceId) {
|
|
|
500301
501708
|
qrCodeAttempts.delete(instanceId);
|
|
500302
501709
|
qrCycleAttempts.delete(instanceId);
|
|
500303
501710
|
activeQrCodes.delete(instanceId);
|
|
501711
|
+
passkeyInstances.delete(instanceId);
|
|
500304
501712
|
authenticatedInstances.delete(instanceId);
|
|
500305
501713
|
clearConnectionTimeout(instanceId);
|
|
500306
501714
|
cancelPendingReconnect(instanceId);
|
|
@@ -500492,6 +501900,77 @@ function getMediaSize(msg) {
|
|
|
500492
501900
|
var log52 = createLogger("whatsapp:messages");
|
|
500493
501901
|
var fallbackDedupeCache3 = createInboundDedupeCache();
|
|
500494
501902
|
var downloadGuard6 = createDownloadGuard({ maxSizeBytes: getWhatsAppMediaDownloadMaxBytes() });
|
|
501903
|
+
function joinLines(...parts) {
|
|
501904
|
+
const text = parts.filter((p2) => typeof p2 === "string" && p2.trim().length > 0).join(`
|
|
501905
|
+
`);
|
|
501906
|
+
return text.length > 0 ? text : undefined;
|
|
501907
|
+
}
|
|
501908
|
+
function bracketButtons(labels) {
|
|
501909
|
+
const clean = labels.filter((l) => typeof l === "string" && l.trim().length > 0);
|
|
501910
|
+
return clean.length > 0 ? clean.map((l) => `[${l.trim()}]`).join(" ") : undefined;
|
|
501911
|
+
}
|
|
501912
|
+
function extractListMessageText(list) {
|
|
501913
|
+
const rows = (list.sections ?? []).flatMap((s) => (s.rows ?? []).map((r) => r.title ? `\u2022 ${r.title}` : ""));
|
|
501914
|
+
return joinLines(list.title, list.description, ...rows, list.footerText);
|
|
501915
|
+
}
|
|
501916
|
+
function extractButtonsMessageText(bm) {
|
|
501917
|
+
const labels = bracketButtons((bm.buttons ?? []).map((b2) => b2.buttonText?.displayText));
|
|
501918
|
+
return joinLines(bm.contentText ?? bm.text, bm.footerText, labels);
|
|
501919
|
+
}
|
|
501920
|
+
function extractInteractiveMessageText(im) {
|
|
501921
|
+
const labels = bracketButtons((im.nativeFlowMessage?.buttons ?? []).map((b2) => b2.name));
|
|
501922
|
+
return joinLines(im.header?.title, im.header?.subtitle, im.body?.text, im.footer?.text, labels);
|
|
501923
|
+
}
|
|
501924
|
+
function extractTemplateMessageText(tpl) {
|
|
501925
|
+
const hydrated = tpl.hydratedTemplate ?? tpl.hydratedFourRowTemplate;
|
|
501926
|
+
if (hydrated) {
|
|
501927
|
+
const labels = bracketButtons((hydrated.hydratedButtons ?? []).map((b2) => b2.quickReplyButton?.displayText ?? b2.urlButton?.displayText ?? b2.callButton?.displayText));
|
|
501928
|
+
return joinLines(hydrated.hydratedTitleText, hydrated.hydratedContentText, hydrated.hydratedFooterText, labels);
|
|
501929
|
+
}
|
|
501930
|
+
return tpl.interactiveMessageTemplate ? extractInteractiveMessageText(tpl.interactiveMessageTemplate) : undefined;
|
|
501931
|
+
}
|
|
501932
|
+
function extractInteractiveMediaHeader(container, caption) {
|
|
501933
|
+
if (container.imageMessage) {
|
|
501934
|
+
return {
|
|
501935
|
+
type: "image",
|
|
501936
|
+
caption: caption ?? container.imageMessage.caption ?? undefined,
|
|
501937
|
+
mimeType: container.imageMessage.mimetype ?? "image/jpeg",
|
|
501938
|
+
mediaUrl: container.imageMessage.url ?? undefined
|
|
501939
|
+
};
|
|
501940
|
+
}
|
|
501941
|
+
if (container.videoMessage) {
|
|
501942
|
+
return {
|
|
501943
|
+
type: "video",
|
|
501944
|
+
caption: caption ?? container.videoMessage.caption ?? undefined,
|
|
501945
|
+
mimeType: container.videoMessage.mimetype ?? "video/mp4",
|
|
501946
|
+
mediaUrl: container.videoMessage.url ?? undefined
|
|
501947
|
+
};
|
|
501948
|
+
}
|
|
501949
|
+
if (container.documentMessage) {
|
|
501950
|
+
return {
|
|
501951
|
+
type: "document",
|
|
501952
|
+
filename: container.documentMessage.fileName ?? undefined,
|
|
501953
|
+
caption: caption ?? container.documentMessage.caption ?? undefined,
|
|
501954
|
+
mimeType: container.documentMessage.mimetype ?? "application/octet-stream",
|
|
501955
|
+
mediaUrl: container.documentMessage.url ?? undefined
|
|
501956
|
+
};
|
|
501957
|
+
}
|
|
501958
|
+
return null;
|
|
501959
|
+
}
|
|
501960
|
+
function extractButtonsMessage(bm) {
|
|
501961
|
+
const text = extractButtonsMessageText(bm);
|
|
501962
|
+
return extractInteractiveMediaHeader(bm, text) ?? { type: "text", text };
|
|
501963
|
+
}
|
|
501964
|
+
function extractInteractiveMessage(im) {
|
|
501965
|
+
const text = extractInteractiveMessageText(im);
|
|
501966
|
+
return extractInteractiveMediaHeader(im.header ?? {}, text) ?? { type: "text", text };
|
|
501967
|
+
}
|
|
501968
|
+
function extractTemplateMessage(tpl) {
|
|
501969
|
+
const text = extractTemplateMessageText(tpl);
|
|
501970
|
+
const hydrated = tpl.hydratedTemplate ?? tpl.hydratedFourRowTemplate;
|
|
501971
|
+
const media = hydrated ? extractInteractiveMediaHeader(hydrated, text) : null;
|
|
501972
|
+
return media ?? { type: "text", text };
|
|
501973
|
+
}
|
|
500495
501974
|
var contentExtractors = [
|
|
500496
501975
|
{
|
|
500497
501976
|
check: (m2) => !!m2.conversation,
|
|
@@ -500695,6 +502174,25 @@ var contentExtractors = [
|
|
|
500695
502174
|
text: m2.buttonsResponseMessage?.selectedDisplayText ?? m2.buttonsResponseMessage?.selectedButtonId ?? undefined
|
|
500696
502175
|
})
|
|
500697
502176
|
},
|
|
502177
|
+
{
|
|
502178
|
+
check: (m2) => !!m2.listMessage,
|
|
502179
|
+
extract: (m2) => ({
|
|
502180
|
+
type: "text",
|
|
502181
|
+
text: m2.listMessage ? extractListMessageText(m2.listMessage) : undefined
|
|
502182
|
+
})
|
|
502183
|
+
},
|
|
502184
|
+
{
|
|
502185
|
+
check: (m2) => !!m2.buttonsMessage,
|
|
502186
|
+
extract: (m2) => m2.buttonsMessage ? extractButtonsMessage(m2.buttonsMessage) : null
|
|
502187
|
+
},
|
|
502188
|
+
{
|
|
502189
|
+
check: (m2) => !!m2.interactiveMessage,
|
|
502190
|
+
extract: (m2) => m2.interactiveMessage ? extractInteractiveMessage(m2.interactiveMessage) : null
|
|
502191
|
+
},
|
|
502192
|
+
{
|
|
502193
|
+
check: (m2) => !!m2.templateMessage,
|
|
502194
|
+
extract: (m2) => m2.templateMessage ? extractTemplateMessage(m2.templateMessage) : null
|
|
502195
|
+
},
|
|
500698
502196
|
{
|
|
500699
502197
|
check: (m2) => !!m2.senderKeyDistributionMessage,
|
|
500700
502198
|
extract: () => null
|
|
@@ -501499,9 +502997,9 @@ function splitWhatsAppMessage(text, maxLength = DEFAULT_MAX_LENGTH2) {
|
|
|
501499
502997
|
flushCurrent();
|
|
501500
502998
|
current = piece;
|
|
501501
502999
|
};
|
|
501502
|
-
for (const
|
|
501503
|
-
if (
|
|
501504
|
-
const paragraphUnits = splitByDelimiter(
|
|
503000
|
+
for (const segment2 of segments) {
|
|
503001
|
+
if (segment2.type === "text") {
|
|
503002
|
+
const paragraphUnits = splitByDelimiter(segment2.value, `
|
|
501505
503003
|
|
|
501506
503004
|
`);
|
|
501507
503005
|
const textPieces = packUnits(paragraphUnits, maxLength);
|
|
@@ -501510,12 +503008,12 @@ function splitWhatsAppMessage(text, maxLength = DEFAULT_MAX_LENGTH2) {
|
|
|
501510
503008
|
}
|
|
501511
503009
|
continue;
|
|
501512
503010
|
}
|
|
501513
|
-
if (
|
|
501514
|
-
appendPiece(
|
|
503011
|
+
if (segment2.value.length <= maxLength) {
|
|
503012
|
+
appendPiece(segment2.value);
|
|
501515
503013
|
continue;
|
|
501516
503014
|
}
|
|
501517
503015
|
flushCurrent();
|
|
501518
|
-
const splitCode = splitHugeCodeBlock(
|
|
503016
|
+
const splitCode = splitHugeCodeBlock(segment2.value, maxLength);
|
|
501519
503017
|
for (const piece of splitCode) {
|
|
501520
503018
|
appendPiece(piece);
|
|
501521
503019
|
}
|
|
@@ -502131,6 +503629,7 @@ class WhatsAppPlugin extends BaseChannelPlugin {
|
|
|
502131
503629
|
version = "1.0.0";
|
|
502132
503630
|
capabilities = WHATSAPP_CAPABILITIES;
|
|
502133
503631
|
sockets = new Map;
|
|
503632
|
+
passkeyStates = new Map;
|
|
502134
503633
|
pluginConfig = {};
|
|
502135
503634
|
historySyncCallbacks = new Map;
|
|
502136
503635
|
historyPushFetchCount = new Map;
|
|
@@ -502496,6 +503995,7 @@ class WhatsAppPlugin extends BaseChannelPlugin {
|
|
|
502496
503995
|
async onInitialize(_context) {}
|
|
502497
503996
|
async connect(instanceId, config2) {
|
|
502498
503997
|
if (config2.options?.forceNewQr === true) {
|
|
503998
|
+
this.passkeyStates.delete(instanceId);
|
|
502499
503999
|
const existingSocket = this.sockets.get(instanceId);
|
|
502500
504000
|
if (existingSocket) {
|
|
502501
504001
|
existingSocket.ev.removeAllListeners("connection.update");
|
|
@@ -502589,6 +504089,7 @@ class WhatsAppPlugin extends BaseChannelPlugin {
|
|
|
502589
504089
|
await this.emitInstanceDisconnected(instanceId, "User requested disconnect");
|
|
502590
504090
|
}
|
|
502591
504091
|
clearInstanceCaches(instanceId) {
|
|
504092
|
+
this.passkeyStates.delete(instanceId);
|
|
502592
504093
|
this.groupMetadataCache.delete(instanceId);
|
|
502593
504094
|
this.groupsCache.delete(instanceId);
|
|
502594
504095
|
this.contactsCache.delete(instanceId);
|
|
@@ -502632,6 +504133,91 @@ class WhatsAppPlugin extends BaseChannelPlugin {
|
|
|
502632
504133
|
throw new WhatsAppError(ErrorCode2.PAIRING_FAILED, `Failed to request pairing code: ${message2}`);
|
|
502633
504134
|
}
|
|
502634
504135
|
}
|
|
504136
|
+
getPasskeyState(instanceId) {
|
|
504137
|
+
return this.passkeyStates.get(instanceId) ?? null;
|
|
504138
|
+
}
|
|
504139
|
+
async submitPasskeyResponse(instanceId, credential) {
|
|
504140
|
+
const sock = this.sockets.get(instanceId);
|
|
504141
|
+
if (!sock) {
|
|
504142
|
+
throw new WhatsAppError(ErrorCode2.NOT_CONNECTED, `Instance ${instanceId} is not connected.`);
|
|
504143
|
+
}
|
|
504144
|
+
const current = this.passkeyStates.get(instanceId);
|
|
504145
|
+
if (current?.state !== "request") {
|
|
504146
|
+
throw new WhatsAppError(ErrorCode2.PAIRING_FAILED, "No passkey authentication is pending.");
|
|
504147
|
+
}
|
|
504148
|
+
this.passkeyStates.set(instanceId, { state: "confirming", requestedAt: current.requestedAt });
|
|
504149
|
+
try {
|
|
504150
|
+
await sock.sendPasskeyResponse(credential);
|
|
504151
|
+
} catch (error) {
|
|
504152
|
+
this.passkeyStates.set(instanceId, current);
|
|
504153
|
+
throw error;
|
|
504154
|
+
}
|
|
504155
|
+
}
|
|
504156
|
+
async confirmPasskey(instanceId) {
|
|
504157
|
+
const sock = this.sockets.get(instanceId);
|
|
504158
|
+
if (!sock) {
|
|
504159
|
+
throw new WhatsAppError(ErrorCode2.NOT_CONNECTED, `Instance ${instanceId} is not connected.`);
|
|
504160
|
+
}
|
|
504161
|
+
const current = this.passkeyStates.get(instanceId);
|
|
504162
|
+
if (current?.state !== "confirmation") {
|
|
504163
|
+
throw new WhatsAppError(ErrorCode2.PAIRING_FAILED, "No passkey confirmation is pending.");
|
|
504164
|
+
}
|
|
504165
|
+
this.passkeyStates.set(instanceId, { state: "confirming", requestedAt: current.requestedAt });
|
|
504166
|
+
try {
|
|
504167
|
+
await sock.sendPasskeyConfirmation();
|
|
504168
|
+
} catch (error) {
|
|
504169
|
+
this.passkeyStates.set(instanceId, current);
|
|
504170
|
+
throw error;
|
|
504171
|
+
}
|
|
504172
|
+
}
|
|
504173
|
+
async handlePasskeyUpdate(instanceId, update) {
|
|
504174
|
+
const requestedAt = new Date().toISOString();
|
|
504175
|
+
if (update.state === "request") {
|
|
504176
|
+
this.passkeyStates.set(instanceId, { state: "request", publicKey: update.publicKey, requestedAt });
|
|
504177
|
+
return;
|
|
504178
|
+
}
|
|
504179
|
+
if (update.state === "error") {
|
|
504180
|
+
this.passkeyStates.set(instanceId, {
|
|
504181
|
+
state: "error",
|
|
504182
|
+
phase: update.phase,
|
|
504183
|
+
message: update.message,
|
|
504184
|
+
requestedAt
|
|
504185
|
+
});
|
|
504186
|
+
return;
|
|
504187
|
+
}
|
|
504188
|
+
if (update.skipHandoffUX) {
|
|
504189
|
+
this.passkeyStates.set(instanceId, { state: "confirming", requestedAt });
|
|
504190
|
+
const sock = this.sockets.get(instanceId);
|
|
504191
|
+
if (!sock) {
|
|
504192
|
+
this.passkeyStates.set(instanceId, {
|
|
504193
|
+
state: "error",
|
|
504194
|
+
phase: "continuation",
|
|
504195
|
+
message: "WhatsApp connection ended before passkey confirmation.",
|
|
504196
|
+
requestedAt
|
|
504197
|
+
});
|
|
504198
|
+
return;
|
|
504199
|
+
}
|
|
504200
|
+
try {
|
|
504201
|
+
await sock.sendPasskeyConfirmation();
|
|
504202
|
+
} catch (error) {
|
|
504203
|
+
const message2 = error instanceof Error ? error.message : "Unable to confirm passkey pairing.";
|
|
504204
|
+
this.passkeyStates.set(instanceId, {
|
|
504205
|
+
state: "error",
|
|
504206
|
+
phase: "continuation",
|
|
504207
|
+
message: message2,
|
|
504208
|
+
requestedAt
|
|
504209
|
+
});
|
|
504210
|
+
this.logger.warn("Failed to auto-confirm passkey pairing", { instanceId, error: message2 });
|
|
504211
|
+
}
|
|
504212
|
+
return;
|
|
504213
|
+
}
|
|
504214
|
+
this.passkeyStates.set(instanceId, {
|
|
504215
|
+
state: "confirmation",
|
|
504216
|
+
code: update.code,
|
|
504217
|
+
requiresUserConfirmation: true,
|
|
504218
|
+
requestedAt
|
|
504219
|
+
});
|
|
504220
|
+
}
|
|
502635
504221
|
buildQuotedOptions(message2, jid) {
|
|
502636
504222
|
if (!message2.replyTo)
|
|
502637
504223
|
return;
|
|
@@ -503556,6 +505142,7 @@ class WhatsAppPlugin extends BaseChannelPlugin {
|
|
|
503556
505142
|
}
|
|
503557
505143
|
}
|
|
503558
505144
|
async handleConnected(instanceId, sock) {
|
|
505145
|
+
this.passkeyStates.delete(instanceId);
|
|
503559
505146
|
let profileName;
|
|
503560
505147
|
let profilePicUrl;
|
|
503561
505148
|
let ownerIdentifier;
|