fix: remove ack reactions after reply (#633) (thanks @levifig)
This commit is contained in:
@@ -27,6 +27,7 @@
|
|||||||
- Providers: add Microsoft Teams provider with polling, attachments, and CLI send support. (#404) — thanks @onutc
|
- Providers: add Microsoft Teams provider with polling, attachments, and CLI send support. (#404) — thanks @onutc
|
||||||
- Slack: honor reply tags + replyToMode while keeping threaded replies in-thread. (#574) — thanks @bolismauro
|
- Slack: honor reply tags + replyToMode while keeping threaded replies in-thread. (#574) — thanks @bolismauro
|
||||||
- Slack: configurable reply threading (`slack.replyToMode`) + proper mrkdwn formatting for outbound messages. (#464) — thanks @austinm911
|
- Slack: configurable reply threading (`slack.replyToMode`) + proper mrkdwn formatting for outbound messages. (#464) — thanks @austinm911
|
||||||
|
- Providers: remove ack reactions after reply on Discord/Slack/Telegram. (#633) — thanks @levifig
|
||||||
- Discord: avoid category parent overrides for channel allowlists and refactor thread context helpers. (#588) — thanks @steipete
|
- Discord: avoid category parent overrides for channel allowlists and refactor thread context helpers. (#588) — thanks @steipete
|
||||||
- Discord: fix forum thread starters and cache channel lookups for thread context. (#585) — thanks @thewilloftheshadow
|
- Discord: fix forum thread starters and cache channel lookups for thread context. (#585) — thanks @thewilloftheshadow
|
||||||
- Discord: log gateway disconnect/reconnect events at info and add verbose gateway metrics. (#595) — thanks @steipete
|
- Discord: log gateway disconnect/reconnect events at info and add verbose gateway metrics. (#595) — thanks @steipete
|
||||||
|
|||||||
@@ -948,7 +948,8 @@ See [Messages](/concepts/messages) for queueing, sessions, and streaming context
|
|||||||
messages: {
|
messages: {
|
||||||
responsePrefix: "🦞", // or "auto"
|
responsePrefix: "🦞", // or "auto"
|
||||||
ackReaction: "👀",
|
ackReaction: "👀",
|
||||||
ackReactionScope: "group-mentions"
|
ackReactionScope: "group-mentions",
|
||||||
|
removeAckAfterReply: false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -975,6 +976,9 @@ active agent’s `identity.emoji` when set, otherwise `"👀"`. Set it to `""` t
|
|||||||
- `direct`: direct messages only
|
- `direct`: direct messages only
|
||||||
- `all`: all messages
|
- `all`: all messages
|
||||||
|
|
||||||
|
`removeAckAfterReply` removes the bot’s ack reaction after a reply is sent
|
||||||
|
(Slack/Discord/Telegram only). Default: `false`.
|
||||||
|
|
||||||
### `talk`
|
### `talk`
|
||||||
|
|
||||||
Defaults for Talk mode (macOS/iOS/Android). Voice IDs fall back to `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID` when unset.
|
Defaults for Talk mode (macOS/iOS/Android). Voice IDs fall back to `ELEVENLABS_VOICE_ID` or `SAG_VOICE_ID` when unset.
|
||||||
|
|||||||
@@ -225,7 +225,8 @@ Outbound Discord API calls retry on rate limits (429) using Discord `retry_after
|
|||||||
```
|
```
|
||||||
|
|
||||||
Ack reactions are controlled globally via `messages.ackReaction` +
|
Ack reactions are controlled globally via `messages.ackReaction` +
|
||||||
`messages.ackReactionScope`.
|
`messages.ackReactionScope`. Use `messages.removeAckAfterReply` to clear the
|
||||||
|
ack reaction after the bot replies.
|
||||||
|
|
||||||
- `dm.enabled`: set `false` to ignore all DMs (default `true`).
|
- `dm.enabled`: set `false` to ignore all DMs (default `true`).
|
||||||
- `dm.policy`: DM access control (`pairing` recommended). `"open"` requires `dm.allowFrom=["*"]`.
|
- `dm.policy`: DM access control (`pairing` recommended). `"open"` requires `dm.allowFrom=["*"]`.
|
||||||
|
|||||||
@@ -192,7 +192,8 @@ Tokens can also be supplied via env vars:
|
|||||||
- `SLACK_APP_TOKEN`
|
- `SLACK_APP_TOKEN`
|
||||||
|
|
||||||
Ack reactions are controlled globally via `messages.ackReaction` +
|
Ack reactions are controlled globally via `messages.ackReaction` +
|
||||||
`messages.ackReactionScope`.
|
`messages.ackReactionScope`. Use `messages.removeAckAfterReply` to clear the
|
||||||
|
ack reaction after the bot replies.
|
||||||
|
|
||||||
## Limits
|
## Limits
|
||||||
- Outbound text is chunked to `slack.textChunkLimit` (default 4000).
|
- Outbound text is chunked to `slack.textChunkLimit` (default 4000).
|
||||||
|
|||||||
@@ -287,4 +287,4 @@ Related global options:
|
|||||||
- `agents.list[].groupChat.mentionPatterns` (mention gating patterns).
|
- `agents.list[].groupChat.mentionPatterns` (mention gating patterns).
|
||||||
- `messages.groupChat.mentionPatterns` (global fallback).
|
- `messages.groupChat.mentionPatterns` (global fallback).
|
||||||
- `commands.native`, `commands.text`, `commands.useAccessGroups` (command behavior).
|
- `commands.native`, `commands.text`, `commands.useAccessGroups` (command behavior).
|
||||||
- `messages.responsePrefix`, `messages.ackReaction`, `messages.ackReactionScope`.
|
- `messages.responsePrefix`, `messages.ackReaction`, `messages.ackReactionScope`, `messages.removeAckAfterReply`.
|
||||||
|
|||||||
@@ -977,17 +977,19 @@ export function createDiscordMessageHandler(params: {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let didAddAckReaction = false;
|
const ackReactionPromise = shouldAckReaction()
|
||||||
if (shouldAckReaction()) {
|
? reactMessageDiscord(message.channelId, message.id, ackReaction, {
|
||||||
reactMessageDiscord(message.channelId, message.id, ackReaction, {
|
rest: client.rest,
|
||||||
rest: client.rest,
|
}).then(
|
||||||
}).catch((err) => {
|
() => true,
|
||||||
logVerbose(
|
(err) => {
|
||||||
`discord react failed for channel ${message.channelId}: ${String(err)}`,
|
logVerbose(
|
||||||
);
|
`discord react failed for channel ${message.channelId}: ${String(err)}`,
|
||||||
});
|
);
|
||||||
didAddAckReaction = true;
|
return false;
|
||||||
}
|
},
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
const fromLabel = isDirectMessage
|
const fromLabel = isDirectMessage
|
||||||
? buildDirectLabel(author)
|
? buildDirectLabel(author)
|
||||||
@@ -1208,13 +1210,22 @@ export function createDiscordMessageHandler(params: {
|
|||||||
`discord: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${replyTarget}`,
|
`discord: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${replyTarget}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (removeAckAfterReply && didAddAckReaction && ackReaction) {
|
if (removeAckAfterReply && ackReactionPromise && ackReaction) {
|
||||||
removeReactionDiscord(message.channelId, message.id, ackReaction, {
|
const ackReactionValue = ackReaction;
|
||||||
rest: client.rest,
|
void ackReactionPromise.then((didAck) => {
|
||||||
}).catch((err) => {
|
if (!didAck) return;
|
||||||
logVerbose(
|
removeReactionDiscord(
|
||||||
`discord: failed to remove ack reaction from ${message.channelId}/${message.id}: ${String(err)}`,
|
message.channelId,
|
||||||
);
|
message.id,
|
||||||
|
ackReactionValue,
|
||||||
|
{
|
||||||
|
rest: client.rest,
|
||||||
|
},
|
||||||
|
).catch((err) => {
|
||||||
|
logVerbose(
|
||||||
|
`discord: failed to remove ack reaction from ${message.channelId}/${message.id}: ${String(err)}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -913,6 +913,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
const rawBody = (message.text ?? "").trim() || media?.placeholder || "";
|
const rawBody = (message.text ?? "").trim() || media?.placeholder || "";
|
||||||
if (!rawBody) return;
|
if (!rawBody) return;
|
||||||
const ackReaction = resolveAckReaction(cfg, route.agentId);
|
const ackReaction = resolveAckReaction(cfg, route.agentId);
|
||||||
|
const ackReactionValue = ackReaction ?? "";
|
||||||
const removeAckAfterReply = cfg.messages?.removeAckAfterReply ?? false;
|
const removeAckAfterReply = cfg.messages?.removeAckAfterReply ?? false;
|
||||||
const shouldAckReaction = () => {
|
const shouldAckReaction = () => {
|
||||||
if (!ackReaction) return false;
|
if (!ackReaction) return false;
|
||||||
@@ -928,18 +929,27 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let didAddAckReaction = false;
|
const ackReactionMessageTs = message.ts;
|
||||||
if (shouldAckReaction() && message.ts) {
|
const ackReactionPromise =
|
||||||
reactSlackMessage(message.channel, message.ts, ackReaction, {
|
shouldAckReaction() && ackReactionMessageTs && ackReactionValue
|
||||||
token: botToken,
|
? reactSlackMessage(
|
||||||
client: app.client,
|
message.channel,
|
||||||
}).catch((err) => {
|
ackReactionMessageTs,
|
||||||
logVerbose(
|
ackReactionValue,
|
||||||
`slack react failed for channel ${message.channel}: ${String(err)}`,
|
{
|
||||||
);
|
token: botToken,
|
||||||
});
|
client: app.client,
|
||||||
didAddAckReaction = true;
|
},
|
||||||
}
|
).then(
|
||||||
|
() => true,
|
||||||
|
(err) => {
|
||||||
|
logVerbose(
|
||||||
|
`slack react failed for channel ${message.channel}: ${String(err)}`,
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
const roomLabel = channelName ? `#${channelName}` : `#${message.channel}`;
|
const roomLabel = channelName ? `#${channelName}` : `#${message.channel}`;
|
||||||
|
|
||||||
@@ -1160,14 +1170,18 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
`slack: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${replyTarget}`,
|
`slack: delivered ${finalCount} reply${finalCount === 1 ? "" : "ies"} to ${replyTarget}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (removeAckAfterReply && didAddAckReaction && ackReaction && message.ts) {
|
if (removeAckAfterReply && ackReactionPromise && ackReactionMessageTs) {
|
||||||
removeSlackReaction(message.channel, message.ts, ackReaction, {
|
const messageTs = ackReactionMessageTs;
|
||||||
token: botToken,
|
void ackReactionPromise.then((didAck) => {
|
||||||
client: app.client,
|
if (!didAck) return;
|
||||||
}).catch((err) => {
|
removeSlackReaction(message.channel, messageTs, ackReactionValue, {
|
||||||
logVerbose(
|
token: botToken,
|
||||||
`slack: failed to remove ack reaction from ${message.channel}/${message.ts}: ${String(err)}`,
|
client: app.client,
|
||||||
);
|
}).catch((err) => {
|
||||||
|
logVerbose(
|
||||||
|
`slack: failed to remove ack reaction from ${message.channel}/${message.ts}: ${String(err)}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -591,28 +591,31 @@ export function createTelegramBot(opts: TelegramBotOptions) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let didAddAckReaction = false;
|
const api = bot.api as unknown as {
|
||||||
if (shouldAckReaction() && msg.message_id) {
|
setMessageReaction?: (
|
||||||
const api = bot.api as unknown as {
|
chatId: number | string,
|
||||||
setMessageReaction?: (
|
messageId: number,
|
||||||
chatId: number | string,
|
reactions: Array<{ type: "emoji"; emoji: string }>,
|
||||||
messageId: number,
|
) => Promise<void>;
|
||||||
reactions: Array<{ type: "emoji"; emoji: string }>,
|
};
|
||||||
) => Promise<void>;
|
const reactionApi =
|
||||||
};
|
typeof api.setMessageReaction === "function"
|
||||||
if (typeof api.setMessageReaction === "function") {
|
? api.setMessageReaction.bind(api)
|
||||||
api
|
: null;
|
||||||
.setMessageReaction(chatId, msg.message_id, [
|
const ackReactionPromise =
|
||||||
|
shouldAckReaction() && msg.message_id && reactionApi
|
||||||
|
? reactionApi(chatId, msg.message_id, [
|
||||||
{ type: "emoji", emoji: ackReaction },
|
{ type: "emoji", emoji: ackReaction },
|
||||||
])
|
]).then(
|
||||||
.catch((err) => {
|
() => true,
|
||||||
logVerbose(
|
(err) => {
|
||||||
`telegram react failed for chat ${chatId}: ${String(err)}`,
|
logVerbose(
|
||||||
);
|
`telegram react failed for chat ${chatId}: ${String(err)}`,
|
||||||
});
|
);
|
||||||
didAddAckReaction = true;
|
return false;
|
||||||
}
|
},
|
||||||
}
|
)
|
||||||
|
: null;
|
||||||
|
|
||||||
let placeholder = "";
|
let placeholder = "";
|
||||||
if (msg.photo) placeholder = "<media:image>";
|
if (msg.photo) placeholder = "<media:image>";
|
||||||
@@ -857,21 +860,20 @@ export function createTelegramBot(opts: TelegramBotOptions) {
|
|||||||
markDispatchIdle();
|
markDispatchIdle();
|
||||||
draftStream?.stop();
|
draftStream?.stop();
|
||||||
if (!queuedFinal) return;
|
if (!queuedFinal) return;
|
||||||
if (removeAckAfterReply && didAddAckReaction && msg.message_id) {
|
if (
|
||||||
const api = bot.api as unknown as {
|
removeAckAfterReply &&
|
||||||
setMessageReaction?: (
|
ackReactionPromise &&
|
||||||
chatId: number | string,
|
msg.message_id &&
|
||||||
messageId: number,
|
reactionApi
|
||||||
reactions: Array<{ type: "emoji"; emoji: string }>,
|
) {
|
||||||
) => Promise<void>;
|
void ackReactionPromise.then((didAck) => {
|
||||||
};
|
if (!didAck) return;
|
||||||
if (typeof api.setMessageReaction === "function") {
|
reactionApi(chatId, msg.message_id, []).catch((err) => {
|
||||||
api.setMessageReaction(chatId, msg.message_id, []).catch((err) => {
|
|
||||||
logVerbose(
|
logVerbose(
|
||||||
`telegram: failed to remove ack reaction from ${chatId}/${msg.message_id}: ${String(err)}`,
|
`telegram: failed to remove ack reaction from ${chatId}/${msg.message_id}: ${String(err)}`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user