-
-
Notifications
You must be signed in to change notification settings - Fork 76
Feat: Send group mentions in status messages #270
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
bd2020b
53a6f0e
83b6795
146c8e8
007eda1
bf741e6
1ca5b3e
e294808
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,6 +49,7 @@ | |
| import { proto, type Proto } from '@proto' | ||
| import { | ||
| normalizeEphemeralSettingSeconds, | ||
| STATUS_MENTION_DELAY, | ||
| WA_ADDRESSING_MODES, | ||
| WA_DEFAULTS, | ||
| WA_NACK_REASONS, | ||
|
|
@@ -91,7 +92,9 @@ | |
| buildButtonAddonNode, | ||
| buildDirectMessageFanoutNode, | ||
| buildGroupSenderKeyMessageNode, | ||
| buildGroupStatusMentionMessage, | ||
| buildStatusMentionMetaNode, | ||
| buildMetaNode | ||
| } from '@transport/node/builders/message' | ||
| import type { BinaryNode } from '@transport/types' | ||
| import { bytesToHex, TEXT_ENCODER } from '@util/bytes' | ||
|
|
@@ -760,6 +763,7 @@ | |
| public async publishStatusMessage(input: { | ||
| readonly message: Proto.IMessage | ||
| readonly recipients: readonly string[] | ||
| readonly mentionedGroupJids?: readonly string[] | ||
| readonly statusSetting?: WaStatusDistributionSetting | ||
| readonly options?: WaSendMessageOptions | ||
| }): Promise<WaMessagePublishResult> { | ||
|
|
@@ -783,8 +787,15 @@ | |
| if (!seen.has(meUserLid)) { | ||
| recipientsWithSelf.push(meUserLid) | ||
| } | ||
| const mentionedGroups: string[] = [] | ||
| const seenGroups = new Set<string>() | ||
| for (const jid of input.mentionedGroupJids ?? []) { | ||
| if (!isGroupJid(jid) || seenGroups.has(jid)) continue | ||
| seenGroups.add(jid) | ||
| mentionedGroups.push(jid) | ||
| } | ||
| const statusSetting = input.statusSetting ?? 'contacts' | ||
| return this.publishSenderKeyFanout({ | ||
| const result = await this.publishSenderKeyFanout({ | ||
| groupJid: WA_DEFAULTS.STATUS_BROADCAST_JID, | ||
| senderJid, | ||
| recipients: recipientsWithSelf, | ||
|
|
@@ -825,14 +836,50 @@ | |
| remoteJid: WA_DEFAULTS.STATUS_BROADCAST_JID, | ||
| context: 'status' | ||
| }) | ||
| const customNodes: BinaryNode[] = [buildMetaNode({ status_setting: statusSetting })] | ||
| const customNodes: BinaryNode[] = [ | ||
| mentionedGroups.length > 0 | ||
| ? buildStatusMentionMetaNode(mentionedGroups, statusSetting) | ||
| : buildMetaNode({ status_setting: statusSetting }) | ||
| ] | ||
| if (reportingArtifacts?.node) customNodes.push(reportingArtifacts.node) | ||
| return { | ||
| extraParticipants: ackHints, | ||
| customNodes | ||
| } | ||
| } | ||
| }) | ||
| if (mentionedGroups.length > 0) { | ||
| if (result.id) { | ||
| await this.notifyGroupStatusMentions(result.id, mentionedGroups) | ||
| } else { | ||
| this.deps.logger.warn('invalid id, skipping mention notifications', { | ||
| groups: mentionedGroups.length | ||
| }) | ||
| } | ||
| } | ||
| return result | ||
| } | ||
|
|
||
| private async notifyGroupStatusMentions( | ||
| statusId: string, | ||
| groupJids: readonly string[] | ||
| ): Promise<void> { | ||
| const message = buildGroupStatusMentionMessage(statusId) | ||
| for (let index = 0; index < groupJids.length; index += 1) { | ||
| await new Promise((r) => setTimeout(r, STATUS_MENTION_DELAY)) | ||
| try { | ||
| await this.sendMessage(groupJids[index], message, { | ||
| additionalAttributes: { type: 'text' }, | ||
| disableGroupEphemeralAutoInject: true | ||
| }) | ||
| } catch (error) { | ||
| this.deps.logger.warn('failed to notify group of status mention', { | ||
| groupJid: groupJids[index], | ||
| statusId, | ||
| message: toError(error).message | ||
| }) | ||
|
Comment on lines
+876
to
+880
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Aggregate group notification failures. Each failed group notification emits a separate warning. Collect failed group JIDs during the loop, then emit one warning with the count and a bounded sample after the batch completes. As per coding guidelines, aggregate per-target failures in batch operations instead of emitting one warning per device or JID. 🤖 Prompt for AI AgentsSource: Coding guidelines |
||
| } | ||
| } | ||
| } | ||
|
|
||
| public async publishBroadcastListMessage(input: { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,7 @@ export interface WaStatusCoordinatorOptions { | |
| readonly publishStatusMessage: (input: { | ||
| readonly message: Proto.IMessage | ||
| readonly recipients: readonly string[] | ||
| readonly mentionedGroupJids?: readonly string[] | ||
| readonly statusSetting?: WaStatusDistributionSetting | ||
| readonly options?: WaSendMessageOptions | ||
| }) => Promise<WaMessagePublishResult> | ||
|
|
@@ -26,6 +27,7 @@ export interface WaStatusCoordinatorOptions { | |
| export interface WaSendStatusInput { | ||
| readonly content: WaSendMessageContent | ||
| readonly recipients: readonly string[] | ||
| readonly mentionedGroupJids: readonly string[] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
set -euo pipefail
ast-grep outline src/client/coordinators/WaStatusCoordinator.ts --items all
rg -n -C 3 --glob '*.{ts,tsx}' '\bWaSendStatusInput\b|\bmentionedGroupJids\b' .Repository: vinikjkkj/zapo Length of output: 5791 🏁 Script executed: #!/bin/bash
set -euo pipefail
printf '%s\n' '--- coordinator source ---'
sed -n '1,90p' src/client/coordinators/WaStatusCoordinator.ts
printf '%s\n' '--- relevant diff ---'
git diff --unified=12 -- src/client/coordinators/WaStatusCoordinator.ts
printf '%s\n' '--- exported API callers ---'
rg -n -C 4 --glob '*.{ts,tsx,md}' 'send\(\{[^}]*content|WaSendStatusInput|statusCoordinator\.(send|sendStatus)|\.send\(\{' src README.md docs 2>/dev/null || trueRepository: vinikjkkj/zapo Length of output: 9175 Keep
🤖 Prompt for AI Agents |
||
| readonly statusSetting?: WaStatusDistributionSetting | ||
| readonly options?: WaSendMessageOptions | ||
| } | ||
|
|
@@ -71,6 +73,7 @@ export function createStatusCoordinator(options: WaStatusCoordinatorOptions): Wa | |
| message, | ||
| recipients: input.recipients, | ||
| statusSetting: input.statusSetting, | ||
| mentionedGroupJids: input.mentionedGroupJids, | ||
| options: input.options | ||
| }) | ||
| return built.upload ? { ...published, upload: built.upload } : published | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,3 +7,5 @@ export const WA_STATUS_DISTRIBUTION_SETTINGS = Object.freeze({ | |
|
|
||
| export type WaStatusDistributionSetting = | ||
| (typeof WA_STATUS_DISTRIBUTION_SETTINGS)[keyof typeof WA_STATUS_DISTRIBUTION_SETTINGS] | ||
|
|
||
| export const STATUS_MENTION_DELAY = 500 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Use the protocol constant convention.
As per coding guidelines, protocol constants must use 🤖 Prompt for AI AgentsSource: Coding guidelines There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3: Protocol constants in Prompt for AI agents |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,8 @@ | ||
| import type { WaButtonAddonKind } from '@message/encode/content' | ||
| import { WA_MESSAGE_TAGS, WA_MESSAGE_TYPES, WA_NODE_TAGS } from '@protocol/constants' | ||
| import type { BinaryNode } from '@transport/types' | ||
| import { WA_MESSAGE_TAGS, WA_MESSAGE_TYPES, WA_NODE_TAGS } from '@protocol/constants' | ||
| import { WA_DEFAULTS } from '@protocol/defaults' | ||
| import { proto, type Proto } from '@proto' | ||
|
Comment on lines
+3
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win Resolve the import-order lint failure. ESLint rejects Lines 3-5 because they follow the As per coding guidelines, 🧰 Tools🪛 ESLint[error] 3-3: (import/order) [error] 4-4: (import/order) [error] 5-5: (import/order) 🤖 Prompt for AI AgentsSources: Coding guidelines, Linters/SAST tools |
||
|
|
||
| interface EncryptedParticipant { | ||
| readonly jid: string | ||
|
|
@@ -301,3 +303,43 @@ | |
| content: undefined | ||
| } | ||
| } | ||
|
|
||
| export function buildStatusMentionMetaNode( | ||
| groupJids: readonly string[], | ||
| statusSetting: string | ||
| ): BinaryNode { | ||
| return { | ||
| tag: 'meta', | ||
| attrs: { | ||
| status_setting: statusSetting | ||
| }, | ||
| content: [ | ||
| { | ||
| tag: 'mentioned_users', | ||
| attrs: {}, | ||
| content: groupJids.map((jid) => ({ | ||
| tag: 'to', | ||
| attrs: { jid } | ||
| })) | ||
| } | ||
| ] | ||
| } | ||
| } | ||
|
|
||
| export function buildGroupStatusMentionMessage(statusId: string): Proto.IMessage { | ||
| return { | ||
| groupStatusMentionMessage: { | ||
| message: { | ||
| protocolMessage: { | ||
| key: { | ||
| remoteJid: WA_DEFAULTS.STATUS_BROADCAST_JID, | ||
| fromMe: true, | ||
| id: statusId, | ||
| participant: 'status_me' | ||
| }, | ||
| type: proto.Message.ProtocolMessage.Type.STATUS_MENTION_MESSAGE | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3: notifyGroupStatusMentions emits a per-group
logger.warninside the loop for each failed group, but this is a batch operation overgroupJids. Per the repo's logging convention (AGENTS.md), per-target failures in batch ops should be aggregated into a single warn with{ droppedCount, totalExpected, sample }. With many mentioned groups, this produces N warn lines and duplicates thestatusId/context on every line.Prompt for AI agents