@automagik/omni 2.260910.11 → 2.260910.13
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/0066_consumer_exclude_types.sql +16 -0
- package/db/drizzle/meta/_journal.json +7 -0
- package/dist/commands/events.d.ts +8 -0
- package/dist/commands/events.d.ts.map +1 -1
- package/dist/index.js +16 -5
- package/dist/sdk/client.d.ts +2 -0
- package/dist/sdk/client.d.ts.map +1 -1
- package/dist/sdk/types.generated.d.ts +15 -0
- package/dist/sdk/types.generated.d.ts.map +1 -1
- package/dist/server/index.js +87 -16
- package/package.json +1 -1
package/dist/server/index.js
CHANGED
|
@@ -174442,6 +174442,7 @@ var init_schema2 = __esm(() => {
|
|
|
174442
174442
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
174443
174443
|
name: varchar("name", { length: 100 }).notNull(),
|
|
174444
174444
|
eventType: varchar("event_type", { length: 255 }).notNull(),
|
|
174445
|
+
excludeTypes: jsonb("exclude_types").$type(),
|
|
174445
174446
|
filters: jsonb("filters").$type(),
|
|
174446
174447
|
cursor: bigint2("cursor", { mode: "number" }).notNull().default(0),
|
|
174447
174448
|
createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
|
|
@@ -251644,7 +251645,7 @@ var init_sentry_scrub = __esm(() => {
|
|
|
251644
251645
|
var require_package7 = __commonJS((exports, module) => {
|
|
251645
251646
|
module.exports = {
|
|
251646
251647
|
name: "@omni/api",
|
|
251647
|
-
version: "2.260910.
|
|
251648
|
+
version: "2.260910.13",
|
|
251648
251649
|
private: true,
|
|
251649
251650
|
type: "module",
|
|
251650
251651
|
exports: {
|
|
@@ -363796,6 +363797,19 @@ async function postProcessChat(services, chat2, _chatCreated, chatExternalId, ch
|
|
|
363796
363797
|
function resolveSenderDisplayName(senderName, rawPayload, participantResult) {
|
|
363797
363798
|
return truncate4(senderName ?? rawPayload?.pushName, 255) || participantResult?.participant.displayName || undefined;
|
|
363798
363799
|
}
|
|
363800
|
+
async function resolveQuotedSenderName(services, chatId, quotedMessage) {
|
|
363801
|
+
const pushName = truncate4(quotedMessage?.pushName, 255);
|
|
363802
|
+
if (pushName)
|
|
363803
|
+
return pushName;
|
|
363804
|
+
const participant = quotedMessage?.participant;
|
|
363805
|
+
if (!participant)
|
|
363806
|
+
return;
|
|
363807
|
+
const platformUserId = participant.split("@")[0]?.split(":")[0];
|
|
363808
|
+
if (!platformUserId)
|
|
363809
|
+
return;
|
|
363810
|
+
const existing = await services.chats.findParticipant(chatId, platformUserId);
|
|
363811
|
+
return truncate4(existing?.displayName ?? undefined, 255);
|
|
363812
|
+
}
|
|
363799
363813
|
async function maybeFindOrCreateParticipant(services, chatId, from, rawPayload, personId, platformIdentityId, senderName) {
|
|
363800
363814
|
if (!from)
|
|
363801
363815
|
return;
|
|
@@ -363955,7 +363969,7 @@ async function handleMessageReceived(services, payload, metadata, eventTimestamp
|
|
|
363955
363969
|
mediaLocalPath: rawPayload?.mediaLocalPath,
|
|
363956
363970
|
replyToExternalId: truncate4(payload.replyToId, 255),
|
|
363957
363971
|
quotedText: quotedMessage?.conversation,
|
|
363958
|
-
quotedSenderName:
|
|
363972
|
+
quotedSenderName: await resolveQuotedSenderName(services, chat2.id, quotedMessage),
|
|
363959
363973
|
threadExternalId: truncate4(payload.threadId, 255),
|
|
363960
363974
|
isThreadBroadcast: rawPayload?.isThreadBroadcast === true,
|
|
363961
363975
|
isForwarded: !!(rawPayload?.isForwarded || rawPayload?.forwardingScore),
|
|
@@ -370061,8 +370075,12 @@ class ChatService {
|
|
|
370061
370075
|
}).where(eq(chatParticipants.id, existing.id)).returning();
|
|
370062
370076
|
return updated ?? existing;
|
|
370063
370077
|
}
|
|
370064
|
-
async
|
|
370078
|
+
async findParticipant(chatId, platformUserId) {
|
|
370065
370079
|
const [existing] = await this.db.select().from(chatParticipants).where(and2(eq(chatParticipants.chatId, chatId), eq(chatParticipants.platformUserId, platformUserId))).limit(1);
|
|
370080
|
+
return existing;
|
|
370081
|
+
}
|
|
370082
|
+
async findOrCreateParticipant(chatId, platformUserId, defaults2 = {}) {
|
|
370083
|
+
const existing = await this.findParticipant(chatId, platformUserId);
|
|
370066
370084
|
if (existing) {
|
|
370067
370085
|
const hasNewIdentity = defaults2.personId || defaults2.platformIdentityId;
|
|
370068
370086
|
const missingIdentity = !existing.personId || !existing.platformIdentityId;
|
|
@@ -370514,6 +370532,20 @@ var init_dead_letters = __esm(() => {
|
|
|
370514
370532
|
function escapeLikePattern(value) {
|
|
370515
370533
|
return value.replace(/([\\%_])/g, "\\$1");
|
|
370516
370534
|
}
|
|
370535
|
+
function eventTypeGlobClause(globs) {
|
|
370536
|
+
const exact = globs.filter((t) => !t.endsWith("*"));
|
|
370537
|
+
const prefixes = globs.filter((t) => t.endsWith("*")).map((t) => t.slice(0, -1));
|
|
370538
|
+
const clauses = [
|
|
370539
|
+
...exact.length ? [inArray(omniEvents.eventType, exact)] : [],
|
|
370540
|
+
...prefixes.map((p2) => like(omniEvents.eventType, `${escapeLikePattern(p2)}%`))
|
|
370541
|
+
];
|
|
370542
|
+
return clauses.length === 1 ? clauses[0] : or2(...clauses);
|
|
370543
|
+
}
|
|
370544
|
+
function eventTypeFilterClause(include, exclude) {
|
|
370545
|
+
const inc = include?.length ? eventTypeGlobClause(include) : undefined;
|
|
370546
|
+
const exc = exclude?.length ? eventTypeGlobClause(exclude) : undefined;
|
|
370547
|
+
return and2(inc, exc ? not2(exc) : undefined);
|
|
370548
|
+
}
|
|
370517
370549
|
function consumerTypeMatches(filter, eventType) {
|
|
370518
370550
|
return filter.endsWith("*") ? eventType.startsWith(filter.slice(0, -1)) : filter === eventType;
|
|
370519
370551
|
}
|
|
@@ -370533,6 +370565,7 @@ class EventService {
|
|
|
370533
370565
|
personId,
|
|
370534
370566
|
chatId,
|
|
370535
370567
|
eventType,
|
|
370568
|
+
excludeEventType,
|
|
370536
370569
|
contentType,
|
|
370537
370570
|
direction,
|
|
370538
370571
|
since,
|
|
@@ -370556,17 +370589,9 @@ class EventService {
|
|
|
370556
370589
|
if (chatId) {
|
|
370557
370590
|
conditions3.push(eq(omniEvents.chatUuid, chatId));
|
|
370558
370591
|
}
|
|
370559
|
-
|
|
370560
|
-
|
|
370561
|
-
|
|
370562
|
-
const typeClauses = [
|
|
370563
|
-
...exact.length ? [inArray(omniEvents.eventType, exact)] : [],
|
|
370564
|
-
...prefixes.map((p2) => like(omniEvents.eventType, `${escapeLikePattern(p2)}%`))
|
|
370565
|
-
];
|
|
370566
|
-
const combined = typeClauses.length === 1 ? typeClauses[0] : or2(...typeClauses);
|
|
370567
|
-
if (combined)
|
|
370568
|
-
conditions3.push(combined);
|
|
370569
|
-
}
|
|
370592
|
+
const typeClause = eventTypeFilterClause(eventType, excludeEventType);
|
|
370593
|
+
if (typeClause)
|
|
370594
|
+
conditions3.push(typeClause);
|
|
370570
370595
|
if (contentType?.length) {
|
|
370571
370596
|
conditions3.push(inArray(omniEvents.contentType, contentType));
|
|
370572
370597
|
}
|
|
@@ -370830,7 +370855,13 @@ class EventConsumerService {
|
|
|
370830
370855
|
async create(input) {
|
|
370831
370856
|
const head = await this.head();
|
|
370832
370857
|
const cursor = resolveInitialCursor(input.startFrom ?? "now", head);
|
|
370833
|
-
const [created] = await this.db.insert(durableConsumers).values({
|
|
370858
|
+
const [created] = await this.db.insert(durableConsumers).values({
|
|
370859
|
+
name: input.name,
|
|
370860
|
+
eventType: input.eventType,
|
|
370861
|
+
excludeTypes: input.excludeTypes?.length ? input.excludeTypes : null,
|
|
370862
|
+
filters: input.filters ?? null,
|
|
370863
|
+
cursor
|
|
370864
|
+
}).onConflictDoNothing({ target: durableConsumers.name }).returning();
|
|
370834
370865
|
if (!created) {
|
|
370835
370866
|
throw new ConflictError("DurableConsumer", `a consumer named '${input.name}' already exists`, {
|
|
370836
370867
|
name: input.name
|
|
@@ -370890,7 +370921,7 @@ class EventConsumerService {
|
|
|
370890
370921
|
return { ...updated, head, lag: computeLag(head, updated.cursor) };
|
|
370891
370922
|
}
|
|
370892
370923
|
async scanPage(consumer, limit2) {
|
|
370893
|
-
const typeClause = consumer.eventType
|
|
370924
|
+
const typeClause = eventTypeFilterClause([consumer.eventType], consumer.excludeTypes ?? undefined);
|
|
370894
370925
|
return this.db.select().from(omniEvents).where(and2(gt(omniEvents.journalSeq, consumer.cursor), typeClause)).orderBy(asc(omniEvents.journalSeq)).limit(limit2);
|
|
370895
370926
|
}
|
|
370896
370927
|
}
|
|
@@ -379973,6 +380004,7 @@ var init_event_consumers2 = __esm(() => {
|
|
|
379973
380004
|
id: exports_external.string().uuid().openapi({ description: "Consumer UUID" }),
|
|
379974
380005
|
name: exports_external.string().openapi({ description: "Unique consumer name (the follow/ack handle)" }),
|
|
379975
380006
|
eventType: exports_external.string().openapi({ description: "Type filter: exact event type or trailing-* prefix glob" }),
|
|
380007
|
+
excludeTypes: exports_external.array(exports_external.string()).nullable().openapi({ description: "Type globs dropped from the stream (exclusion wins over eventType), or null" }),
|
|
379976
380008
|
filters: exports_external.array(ConsumerConditionSchema).nullable().openapi({ description: "Payload conditions (AND), or null" }),
|
|
379977
380009
|
cursor: exports_external.number().int().openapi({ description: "Last acked journal_seq; delivery resumes strictly after it" }),
|
|
379978
380010
|
head: exports_external.number().int().openapi({ description: "Highest journal_seq currently in the journal" }),
|
|
@@ -379983,6 +380015,7 @@ var init_event_consumers2 = __esm(() => {
|
|
|
379983
380015
|
CreateConsumerSchema = exports_external.object({
|
|
379984
380016
|
name: exports_external.string().min(1).max(100).regex(CONSUMER_NAME_PATTERN, "Consumer name must be lowercase tokens: a-z 0-9 . _ - (starting alphanumeric)").openapi({ description: "Unique consumer name (e.g. deploy-tracker)" }),
|
|
379985
380017
|
eventType: exports_external.string().min(1).max(255).openapi({ description: "Type filter: exact event type, or trailing-* prefix glob (e.g. custom.github.*)" }),
|
|
380018
|
+
excludeTypes: exports_external.array(exports_external.string().min(1).max(255)).max(20).optional().openapi({ description: "Type globs to drop (same syntax as eventType); exclusion wins over inclusion" }),
|
|
379986
380019
|
filters: exports_external.array(ConsumerConditionSchema).max(20).optional().openapi({ description: "Payload conditions (AND) \u2014 same matcher as events wait --filter" }),
|
|
379987
380020
|
startFrom: exports_external.enum(["now", "beginning"]).optional().openapi({ description: "Initial cursor: 'now' = journal head (default), 'beginning' = full replay" })
|
|
379988
380021
|
});
|
|
@@ -380299,6 +380332,7 @@ function registerEventSchemas(registry4) {
|
|
|
380299
380332
|
personId: exports_external.string().uuid().optional().openapi({ description: "Filter by person" }),
|
|
380300
380333
|
chatId: exports_external.string().uuid().optional().openapi({ description: "Filter by chat UUID" }),
|
|
380301
380334
|
eventType: exports_external.string().optional().openapi({ description: "Event types (comma-separated)" }),
|
|
380335
|
+
excludeEventType: exports_external.string().optional().openapi({ description: "Event type globs to drop (comma-separated); exclusion wins over eventType" }),
|
|
380302
380336
|
contentType: exports_external.string().optional().openapi({ description: "Content types (comma-separated)" }),
|
|
380303
380337
|
direction: exports_external.enum(["inbound", "outbound"]).optional().openapi({ description: "Direction" }),
|
|
380304
380338
|
since: exports_external.string().datetime().optional().openapi({ description: "Start date" }),
|
|
@@ -386871,6 +386905,7 @@ var init_events4 = __esm(() => {
|
|
|
386871
386905
|
personId: exports_external.string().uuid().optional(),
|
|
386872
386906
|
chatId: exports_external.string().uuid().optional(),
|
|
386873
386907
|
eventType: exports_external.string().optional().transform((v2) => v2?.split(",")),
|
|
386908
|
+
excludeEventType: exports_external.string().optional().transform((v2) => v2?.split(",")),
|
|
386874
386909
|
contentType: exports_external.string().optional().transform((v2) => v2?.split(",")),
|
|
386875
386910
|
direction: exports_external.enum(["inbound", "outbound"]).optional(),
|
|
386876
386911
|
since: optionalDateParam("since"),
|
|
@@ -398025,6 +398060,16 @@ async function resolveChatLink(db2, instanceId, chatId) {
|
|
|
398025
398060
|
return NO_CHAT_LINK;
|
|
398026
398061
|
}
|
|
398027
398062
|
}
|
|
398063
|
+
async function resolveReplyToEventId(db2, instanceId, replyToExternalId) {
|
|
398064
|
+
if (!instanceId || !replyToExternalId)
|
|
398065
|
+
return null;
|
|
398066
|
+
try {
|
|
398067
|
+
const [row] = await db2.select({ id: omniEvents.id }).from(omniEvents).where(and2(eq(omniEvents.instanceId, instanceId), eq(omniEvents.externalId, replyToExternalId))).orderBy(omniEvents.receivedAt).limit(1);
|
|
398068
|
+
return row?.id ?? null;
|
|
398069
|
+
} catch {
|
|
398070
|
+
return null;
|
|
398071
|
+
}
|
|
398072
|
+
}
|
|
398028
398073
|
async function resolvePersonId2(db2, candidate) {
|
|
398029
398074
|
if (typeof candidate !== "string" || !isValidUuid(candidate))
|
|
398030
398075
|
return null;
|
|
@@ -398059,6 +398104,7 @@ async function setupEventPersistence(eventBus, db2) {
|
|
|
398059
398104
|
await runConsumerInTenantContext(db2, event, async () => {
|
|
398060
398105
|
const sdb = scopedHandle(db2);
|
|
398061
398106
|
const chatLink = await resolveChatLink(sdb, metadata.instanceId, payload.chatId);
|
|
398107
|
+
const replyToEventId = await resolveReplyToEventId(sdb, metadata.instanceId, payload.replyToId);
|
|
398062
398108
|
const columns2 = {
|
|
398063
398109
|
externalId: payload.externalId,
|
|
398064
398110
|
channel: mapChannelType(metadata.channelType),
|
|
@@ -398073,6 +398119,7 @@ async function setupEventPersistence(eventBus, db2) {
|
|
|
398073
398119
|
mediaMimeType: payload.content.mimeType,
|
|
398074
398120
|
chatId: payload.chatId,
|
|
398075
398121
|
replyToExternalId: payload.replyToId,
|
|
398122
|
+
replyToEventId,
|
|
398076
398123
|
status: "received",
|
|
398077
398124
|
receivedAt: new Date(event.timestamp),
|
|
398078
398125
|
rawPayload: payload.rawPayload ? deepSanitize(payload.rawPayload) : undefined,
|
|
@@ -398109,6 +398156,7 @@ async function setupEventPersistence(eventBus, db2) {
|
|
|
398109
398156
|
await runConsumerInTenantContext(db2, event, async () => {
|
|
398110
398157
|
const sdb = scopedHandle(db2);
|
|
398111
398158
|
const chatLink = await resolveChatLink(sdb, metadata.instanceId, payload.chatId);
|
|
398159
|
+
const replyToEventId = await resolveReplyToEventId(sdb, metadata.instanceId, payload.replyToId);
|
|
398112
398160
|
const newEvent = {
|
|
398113
398161
|
...eventIdInsert(event.id),
|
|
398114
398162
|
externalId: payload.externalId,
|
|
@@ -398124,6 +398172,7 @@ async function setupEventPersistence(eventBus, db2) {
|
|
|
398124
398172
|
mediaMimeType: payload.content.mimeType,
|
|
398125
398173
|
chatId: payload.chatId,
|
|
398126
398174
|
replyToExternalId: payload.replyToId,
|
|
398175
|
+
replyToEventId,
|
|
398127
398176
|
status: "completed",
|
|
398128
398177
|
receivedAt: new Date(event.timestamp),
|
|
398129
398178
|
processedAt: new Date,
|
|
@@ -528573,6 +528622,19 @@ function getReplyToId2(msg) {
|
|
|
528573
528622
|
const contextInfo = getMessageContextInfo(msg);
|
|
528574
528623
|
return contextInfo?.stanzaId || undefined;
|
|
528575
528624
|
}
|
|
528625
|
+
function extractQuotedContext(msg) {
|
|
528626
|
+
const contextInfo = getMessageContextInfo(msg);
|
|
528627
|
+
if (!contextInfo?.quotedMessage)
|
|
528628
|
+
return;
|
|
528629
|
+
const content = extractContent3({ key: msg.key, message: contextInfo.quotedMessage });
|
|
528630
|
+
const type = content?.type ?? "unknown";
|
|
528631
|
+
return {
|
|
528632
|
+
stanzaId: contextInfo.stanzaId || undefined,
|
|
528633
|
+
participant: contextInfo.participant || undefined,
|
|
528634
|
+
type,
|
|
528635
|
+
conversation: content?.text || content?.caption || `[${type}]`
|
|
528636
|
+
};
|
|
528637
|
+
}
|
|
528576
528638
|
function isFromMe(msg) {
|
|
528577
528639
|
return msg.key.fromMe === true;
|
|
528578
528640
|
}
|
|
@@ -531673,6 +531735,7 @@ class WhatsAppPlugin extends BaseChannelPlugin {
|
|
|
531673
531735
|
}
|
|
531674
531736
|
} catch {}
|
|
531675
531737
|
}
|
|
531738
|
+
await this.enrichPayloadWithQuotedMessage(extendedPayload, instanceId, chatId, rawMessage);
|
|
531676
531739
|
if (content.poll)
|
|
531677
531740
|
extendedPayload.poll = content.poll;
|
|
531678
531741
|
if (content.pollVotes)
|
|
@@ -532205,6 +532268,7 @@ class WhatsAppPlugin extends BaseChannelPlugin {
|
|
|
532205
532268
|
if (content.localPath)
|
|
532206
532269
|
rawPayload.mediaLocalPath = content.localPath;
|
|
532207
532270
|
this.enrichPayloadWithChatName(rawPayload, instanceId, chatId);
|
|
532271
|
+
await this.enrichPayloadWithQuotedMessage(rawPayload, instanceId, chatId, msg);
|
|
532208
532272
|
const historySenderName = msg.pushName ?? undefined;
|
|
532209
532273
|
await this.emitMessageReceived({
|
|
532210
532274
|
instanceId,
|
|
@@ -532224,6 +532288,13 @@ class WhatsAppPlugin extends BaseChannelPlugin {
|
|
|
532224
532288
|
});
|
|
532225
532289
|
}
|
|
532226
532290
|
}
|
|
532291
|
+
async enrichPayloadWithQuotedMessage(rawPayload, instanceId, chatId, msg) {
|
|
532292
|
+
const quoted = extractQuotedContext(msg);
|
|
532293
|
+
if (!quoted)
|
|
532294
|
+
return;
|
|
532295
|
+
const pushName = quoted.participant ? (await this.getContactInfo(instanceId, quoted.participant, chatId))?.name : undefined;
|
|
532296
|
+
rawPayload.quotedMessage = { ...quoted, pushName };
|
|
532297
|
+
}
|
|
532227
532298
|
enrichPayloadWithChatName(payload, instanceId, chatId) {
|
|
532228
532299
|
if (chatId.includes("@g.us")) {
|
|
532229
532300
|
const group = this.groupsCache.get(instanceId)?.get(chatId);
|