@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.
@@ -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.11",
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: truncate4(quotedMessage?.pushName, 255),
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 findOrCreateParticipant(chatId, platformUserId, defaults2 = {}) {
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
- if (eventType?.length) {
370560
- const exact = eventType.filter((t) => !t.endsWith("*"));
370561
- const prefixes = eventType.filter((t) => t.endsWith("*")).map((t) => t.slice(0, -1));
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({ name: input.name, eventType: input.eventType, filters: input.filters ?? null, cursor }).onConflictDoNothing({ target: durableConsumers.name }).returning();
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.endsWith("*") ? like(omniEvents.eventType, `${escapeLikePattern(consumer.eventType.slice(0, -1))}%`) : eq(omniEvents.eventType, 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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@automagik/omni",
3
- "version": "2.260910.11",
3
+ "version": "2.260910.13",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/automagik-dev/omni.git",