refactor: drop autoReply, add topic requireMention
Co-authored-by: kitze <kristijan.mkd@gmail.com>
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
- Model config schema changes (auth profiles + model lists); doctor auto-migrates and the gateway rewrites legacy configs on startup.
|
- Model config schema changes (auth profiles + model lists); doctor auto-migrates and the gateway rewrites legacy configs on startup.
|
||||||
- Commands: gate all slash commands to authorized senders; add `/compact` to manually compact session context.
|
- Commands: gate all slash commands to authorized senders; add `/compact` to manually compact session context.
|
||||||
- Groups: `whatsapp.groups`, `telegram.groups`, and `imessage.groups` now act as allowlists when set. Add `"*"` to keep allow-all behavior.
|
- Groups: `whatsapp.groups`, `telegram.groups`, and `imessage.groups` now act as allowlists when set. Add `"*"` to keep allow-all behavior.
|
||||||
|
- Auto-reply: removed `autoReply` from Discord/Slack/Telegram channel configs; use `requireMention` instead (Telegram topics now support `requireMention` overrides).
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
- Pairing: generate DM pairing codes with CSPRNG, expire pending codes after 1 hour, and avoid re-sending codes for already pending requests.
|
- Pairing: generate DM pairing codes with CSPRNG, expire pending codes after 1 hour, and avoid re-sending codes for already pending requests.
|
||||||
|
|||||||
@@ -468,12 +468,16 @@ Set `telegram.enabled: false` to disable automatic startup.
|
|||||||
dmPolicy: "pairing", // pairing | allowlist | open | disabled
|
dmPolicy: "pairing", // pairing | allowlist | open | disabled
|
||||||
allowFrom: ["tg:123456789"], // optional; "open" requires ["*"]
|
allowFrom: ["tg:123456789"], // optional; "open" requires ["*"]
|
||||||
groups: {
|
groups: {
|
||||||
"*": { requireMention: true, autoReply: false },
|
"*": { requireMention: true },
|
||||||
"-1001234567890": {
|
"-1001234567890": {
|
||||||
allowFrom: ["@admin"],
|
allowFrom: ["@admin"],
|
||||||
systemPrompt: "Keep answers brief.",
|
systemPrompt: "Keep answers brief.",
|
||||||
topics: {
|
topics: {
|
||||||
"99": { skills: ["search"], systemPrompt: "Stay on topic." }
|
"99": {
|
||||||
|
requireMention: false,
|
||||||
|
skills: ["search"],
|
||||||
|
systemPrompt: "Stay on topic."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -580,7 +584,7 @@ Slack runs in Socket Mode and requires both a bot token and app token:
|
|||||||
C123: { allow: true, requireMention: true },
|
C123: { allow: true, requireMention: true },
|
||||||
"#general": {
|
"#general": {
|
||||||
allow: true,
|
allow: true,
|
||||||
autoReply: false,
|
requireMention: true,
|
||||||
users: ["U123"],
|
users: ["U123"],
|
||||||
skills: ["docs"],
|
skills: ["docs"],
|
||||||
systemPrompt: "Short answers only."
|
systemPrompt: "Short answers only."
|
||||||
|
|||||||
@@ -202,8 +202,7 @@ Notes:
|
|||||||
requireMention: true,
|
requireMention: true,
|
||||||
users: ["987654321098765432"],
|
users: ["987654321098765432"],
|
||||||
skills: ["search", "docs"],
|
skills: ["search", "docs"],
|
||||||
systemPrompt: "Keep answers short.",
|
systemPrompt: "Keep answers short."
|
||||||
autoReply: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -227,7 +226,6 @@ Ack reactions are controlled globally via `messages.ackReaction` +
|
|||||||
- `guilds.<id>.users`: optional per-guild user allowlist (ids or names).
|
- `guilds.<id>.users`: optional per-guild user allowlist (ids or names).
|
||||||
- `guilds.<id>.channels.<channel>.allow`: allow/deny the channel when `groupPolicy="allowlist"`.
|
- `guilds.<id>.channels.<channel>.allow`: allow/deny the channel when `groupPolicy="allowlist"`.
|
||||||
- `guilds.<id>.channels.<channel>.requireMention`: mention gating for the channel.
|
- `guilds.<id>.channels.<channel>.requireMention`: mention gating for the channel.
|
||||||
- `guilds.<id>.channels.<channel>.autoReply`: if `true`, reply to all messages (overrides `requireMention`).
|
|
||||||
- `guilds.<id>.channels.<channel>.users`: optional per-channel user allowlist.
|
- `guilds.<id>.channels.<channel>.users`: optional per-channel user allowlist.
|
||||||
- `guilds.<id>.channels.<channel>.skills`: skill filter (omit = all skills, empty = none).
|
- `guilds.<id>.channels.<channel>.skills`: skill filter (omit = all skills, empty = none).
|
||||||
- `guilds.<id>.channels.<channel>.systemPrompt`: extra system prompt for the channel (combined with channel topic).
|
- `guilds.<id>.channels.<channel>.systemPrompt`: extra system prompt for the channel (combined with channel topic).
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ Slack uses Socket Mode only (no HTTP webhook server). Provide both tokens:
|
|||||||
"C123": { "allow": true, "requireMention": true },
|
"C123": { "allow": true, "requireMention": true },
|
||||||
"#general": {
|
"#general": {
|
||||||
"allow": true,
|
"allow": true,
|
||||||
"autoReply": false,
|
"requireMention": true,
|
||||||
"users": ["U123"],
|
"users": ["U123"],
|
||||||
"skills": ["search", "docs"],
|
"skills": ["search", "docs"],
|
||||||
"systemPrompt": "Keep answers short."
|
"systemPrompt": "Keep answers short."
|
||||||
@@ -212,7 +212,6 @@ Ack reactions are controlled globally via `messages.ackReaction` +
|
|||||||
Channel options (`slack.channels.<id>` or `slack.channels.<name>`):
|
Channel options (`slack.channels.<id>` or `slack.channels.<name>`):
|
||||||
- `allow`: allow/deny the channel when `groupPolicy="allowlist"`.
|
- `allow`: allow/deny the channel when `groupPolicy="allowlist"`.
|
||||||
- `requireMention`: mention gating for the channel.
|
- `requireMention`: mention gating for the channel.
|
||||||
- `autoReply`: if `true`, reply to every message (overrides `requireMention`).
|
|
||||||
- `users`: optional per-channel user allowlist.
|
- `users`: optional per-channel user allowlist.
|
||||||
- `skills`: skill filter (omit = all skills, empty = none).
|
- `skills`: skill filter (omit = all skills, empty = none).
|
||||||
- `systemPrompt`: extra system prompt for the channel (combined with topic/purpose).
|
- `systemPrompt`: extra system prompt for the channel (combined with topic/purpose).
|
||||||
|
|||||||
@@ -117,12 +117,12 @@ Provider options:
|
|||||||
- `telegram.groupAllowFrom`: group sender allowlist (ids/usernames).
|
- `telegram.groupAllowFrom`: group sender allowlist (ids/usernames).
|
||||||
- `telegram.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
|
- `telegram.groups`: per-group defaults + allowlist (use `"*"` for global defaults).
|
||||||
- `telegram.groups.<id>.requireMention`: mention gating default.
|
- `telegram.groups.<id>.requireMention`: mention gating default.
|
||||||
- `telegram.groups.<id>.autoReply`: reply to every message (overrides `requireMention`).
|
|
||||||
- `telegram.groups.<id>.skills`: skill filter (omit = all skills, empty = none).
|
- `telegram.groups.<id>.skills`: skill filter (omit = all skills, empty = none).
|
||||||
- `telegram.groups.<id>.allowFrom`: per-group sender allowlist override.
|
- `telegram.groups.<id>.allowFrom`: per-group sender allowlist override.
|
||||||
- `telegram.groups.<id>.systemPrompt`: extra system prompt for the group.
|
- `telegram.groups.<id>.systemPrompt`: extra system prompt for the group.
|
||||||
- `telegram.groups.<id>.enabled`: disable the group when `false`.
|
- `telegram.groups.<id>.enabled`: disable the group when `false`.
|
||||||
- `telegram.groups.<id>.topics.<threadId>.*`: per-topic overrides (same fields as group).
|
- `telegram.groups.<id>.topics.<threadId>.*`: per-topic overrides (same fields as group).
|
||||||
|
- `telegram.groups.<id>.topics.<threadId>.requireMention`: per-topic mention gating override.
|
||||||
- `telegram.replyToMode`: `off | first | all`.
|
- `telegram.replyToMode`: `off | first | all`.
|
||||||
- `telegram.textChunkLimit`: outbound chunk size (chars).
|
- `telegram.textChunkLimit`: outbound chunk size (chars).
|
||||||
- `telegram.streamMode`: `off | partial | block` (draft streaming).
|
- `telegram.streamMode`: `off | partial | block` (draft streaming).
|
||||||
|
|||||||
@@ -44,11 +44,7 @@ function parseTelegramGroupId(value?: string | null) {
|
|||||||
return { chatId: raw, topicId: undefined };
|
return { chatId: raw, topicId: undefined };
|
||||||
}
|
}
|
||||||
|
|
||||||
function hasOwn(obj: unknown, key: string): boolean {
|
function resolveTelegramRequireMention(params: {
|
||||||
return Boolean(obj && typeof obj === "object" && Object.hasOwn(obj, key));
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveTelegramAutoReply(params: {
|
|
||||||
cfg: ClawdbotConfig;
|
cfg: ClawdbotConfig;
|
||||||
chatId?: string;
|
chatId?: string;
|
||||||
topicId?: string;
|
topicId?: string;
|
||||||
@@ -61,17 +57,17 @@ function resolveTelegramAutoReply(params: {
|
|||||||
topicId && groupConfig?.topics ? groupConfig.topics[topicId] : undefined;
|
topicId && groupConfig?.topics ? groupConfig.topics[topicId] : undefined;
|
||||||
const defaultTopicConfig =
|
const defaultTopicConfig =
|
||||||
topicId && groupDefault?.topics ? groupDefault.topics[topicId] : undefined;
|
topicId && groupDefault?.topics ? groupDefault.topics[topicId] : undefined;
|
||||||
if (hasOwn(topicConfig, "autoReply")) {
|
if (typeof topicConfig?.requireMention === "boolean") {
|
||||||
return (topicConfig as { autoReply?: boolean }).autoReply;
|
return topicConfig.requireMention;
|
||||||
}
|
}
|
||||||
if (hasOwn(defaultTopicConfig, "autoReply")) {
|
if (typeof defaultTopicConfig?.requireMention === "boolean") {
|
||||||
return (defaultTopicConfig as { autoReply?: boolean }).autoReply;
|
return defaultTopicConfig.requireMention;
|
||||||
}
|
}
|
||||||
if (hasOwn(groupConfig, "autoReply")) {
|
if (typeof groupConfig?.requireMention === "boolean") {
|
||||||
return (groupConfig as { autoReply?: boolean }).autoReply;
|
return groupConfig.requireMention;
|
||||||
}
|
}
|
||||||
if (hasOwn(groupDefault, "autoReply")) {
|
if (typeof groupDefault?.requireMention === "boolean") {
|
||||||
return (groupDefault as { autoReply?: boolean }).autoReply;
|
return groupDefault.requireMention;
|
||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -107,8 +103,12 @@ export function resolveGroupRequireMention(params: {
|
|||||||
const groupSpace = ctx.GroupSpace?.trim();
|
const groupSpace = ctx.GroupSpace?.trim();
|
||||||
if (provider === "telegram") {
|
if (provider === "telegram") {
|
||||||
const { chatId, topicId } = parseTelegramGroupId(groupId);
|
const { chatId, topicId } = parseTelegramGroupId(groupId);
|
||||||
const autoReply = resolveTelegramAutoReply({ cfg, chatId, topicId });
|
const requireMention = resolveTelegramRequireMention({
|
||||||
if (typeof autoReply === "boolean") return !autoReply;
|
cfg,
|
||||||
|
chatId,
|
||||||
|
topicId,
|
||||||
|
});
|
||||||
|
if (typeof requireMention === "boolean") return requireMention;
|
||||||
return resolveProviderGroupRequireMention({
|
return resolveProviderGroupRequireMention({
|
||||||
cfg,
|
cfg,
|
||||||
provider,
|
provider,
|
||||||
@@ -138,9 +138,6 @@ export function resolveGroupRequireMention(params: {
|
|||||||
(groupRoom
|
(groupRoom
|
||||||
? channelEntries[normalizeDiscordSlug(groupRoom)]
|
? channelEntries[normalizeDiscordSlug(groupRoom)]
|
||||||
: undefined);
|
: undefined);
|
||||||
if (entry && typeof entry.autoReply === "boolean") {
|
|
||||||
return !entry.autoReply;
|
|
||||||
}
|
|
||||||
if (entry && typeof entry.requireMention === "boolean") {
|
if (entry && typeof entry.requireMention === "boolean") {
|
||||||
return entry.requireMention;
|
return entry.requireMention;
|
||||||
}
|
}
|
||||||
@@ -163,7 +160,7 @@ export function resolveGroupRequireMention(params: {
|
|||||||
channelName ?? "",
|
channelName ?? "",
|
||||||
normalizedName,
|
normalizedName,
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
let matched: { requireMention?: boolean; autoReply?: boolean } | undefined;
|
let matched: { requireMention?: boolean } | undefined;
|
||||||
for (const candidate of candidates) {
|
for (const candidate of candidates) {
|
||||||
if (candidate && channels[candidate]) {
|
if (candidate && channels[candidate]) {
|
||||||
matched = channels[candidate];
|
matched = channels[candidate];
|
||||||
@@ -172,9 +169,6 @@ export function resolveGroupRequireMention(params: {
|
|||||||
}
|
}
|
||||||
const fallback = channels["*"];
|
const fallback = channels["*"];
|
||||||
const resolved = matched ?? fallback;
|
const resolved = matched ?? fallback;
|
||||||
if (typeof resolved?.autoReply === "boolean") {
|
|
||||||
return !resolved.autoReply;
|
|
||||||
}
|
|
||||||
if (typeof resolved?.requireMention === "boolean") {
|
if (typeof resolved?.requireMention === "boolean") {
|
||||||
return resolved.requireMention;
|
return resolved.requireMention;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,12 +237,11 @@ export type TelegramActionConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TelegramTopicConfig = {
|
export type TelegramTopicConfig = {
|
||||||
|
requireMention?: boolean;
|
||||||
/** If specified, only load these skills for this topic. Omit = all skills; empty = no skills. */
|
/** If specified, only load these skills for this topic. Omit = all skills; empty = no skills. */
|
||||||
skills?: string[];
|
skills?: string[];
|
||||||
/** If false, disable the bot for this topic. */
|
/** If false, disable the bot for this topic. */
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
/** If true, reply to every message (no mention required). */
|
|
||||||
autoReply?: boolean;
|
|
||||||
/** Optional allowlist for topic senders (ids or usernames). */
|
/** Optional allowlist for topic senders (ids or usernames). */
|
||||||
allowFrom?: Array<string | number>;
|
allowFrom?: Array<string | number>;
|
||||||
/** Optional system prompt snippet for this topic. */
|
/** Optional system prompt snippet for this topic. */
|
||||||
@@ -257,8 +256,6 @@ export type TelegramGroupConfig = {
|
|||||||
topics?: Record<string, TelegramTopicConfig>;
|
topics?: Record<string, TelegramTopicConfig>;
|
||||||
/** If false, disable the bot for this group (and its topics). */
|
/** If false, disable the bot for this group (and its topics). */
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
/** If true, reply to every message (no mention required). */
|
|
||||||
autoReply?: boolean;
|
|
||||||
/** Optional allowlist for group senders (ids or usernames). */
|
/** Optional allowlist for group senders (ids or usernames). */
|
||||||
allowFrom?: Array<string | number>;
|
allowFrom?: Array<string | number>;
|
||||||
/** Optional system prompt snippet for this group. */
|
/** Optional system prompt snippet for this group. */
|
||||||
@@ -325,8 +322,6 @@ export type DiscordGuildChannelConfig = {
|
|||||||
skills?: string[];
|
skills?: string[];
|
||||||
/** If false, disable the bot for this channel. */
|
/** If false, disable the bot for this channel. */
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
/** If true, reply to every message (no mention required). */
|
|
||||||
autoReply?: boolean;
|
|
||||||
/** Optional allowlist for channel senders (ids or names). */
|
/** Optional allowlist for channel senders (ids or names). */
|
||||||
users?: Array<string | number>;
|
users?: Array<string | number>;
|
||||||
/** Optional system prompt snippet for this channel. */
|
/** Optional system prompt snippet for this channel. */
|
||||||
@@ -412,8 +407,6 @@ export type SlackChannelConfig = {
|
|||||||
allow?: boolean;
|
allow?: boolean;
|
||||||
/** Require mentioning the bot to trigger replies. */
|
/** Require mentioning the bot to trigger replies. */
|
||||||
requireMention?: boolean;
|
requireMention?: boolean;
|
||||||
/** Reply to all messages without needing a mention. */
|
|
||||||
autoReply?: boolean;
|
|
||||||
/** Allowlist of users that can invoke the bot in this channel. */
|
/** Allowlist of users that can invoke the bot in this channel. */
|
||||||
users?: Array<string | number>;
|
users?: Array<string | number>;
|
||||||
/** Optional skill filter for this channel. */
|
/** Optional skill filter for this channel. */
|
||||||
|
|||||||
@@ -787,7 +787,6 @@ export const ClawdbotSchema = z.object({
|
|||||||
requireMention: z.boolean().optional(),
|
requireMention: z.boolean().optional(),
|
||||||
skills: z.array(z.string()).optional(),
|
skills: z.array(z.string()).optional(),
|
||||||
enabled: z.boolean().optional(),
|
enabled: z.boolean().optional(),
|
||||||
autoReply: z.boolean().optional(),
|
|
||||||
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
allowFrom: z.array(z.union([z.string(), z.number()])).optional(),
|
||||||
systemPrompt: z.string().optional(),
|
systemPrompt: z.string().optional(),
|
||||||
topics: z
|
topics: z
|
||||||
@@ -795,9 +794,9 @@ export const ClawdbotSchema = z.object({
|
|||||||
z.string(),
|
z.string(),
|
||||||
z
|
z
|
||||||
.object({
|
.object({
|
||||||
|
requireMention: z.boolean().optional(),
|
||||||
skills: z.array(z.string()).optional(),
|
skills: z.array(z.string()).optional(),
|
||||||
enabled: z.boolean().optional(),
|
enabled: z.boolean().optional(),
|
||||||
autoReply: z.boolean().optional(),
|
|
||||||
allowFrom: z
|
allowFrom: z
|
||||||
.array(z.union([z.string(), z.number()]))
|
.array(z.union([z.string(), z.number()]))
|
||||||
.optional(),
|
.optional(),
|
||||||
@@ -913,7 +912,6 @@ export const ClawdbotSchema = z.object({
|
|||||||
requireMention: z.boolean().optional(),
|
requireMention: z.boolean().optional(),
|
||||||
skills: z.array(z.string()).optional(),
|
skills: z.array(z.string()).optional(),
|
||||||
enabled: z.boolean().optional(),
|
enabled: z.boolean().optional(),
|
||||||
autoReply: z.boolean().optional(),
|
|
||||||
users: z
|
users: z
|
||||||
.array(z.union([z.string(), z.number()]))
|
.array(z.union([z.string(), z.number()]))
|
||||||
.optional(),
|
.optional(),
|
||||||
@@ -990,7 +988,6 @@ export const ClawdbotSchema = z.object({
|
|||||||
enabled: z.boolean().optional(),
|
enabled: z.boolean().optional(),
|
||||||
allow: z.boolean().optional(),
|
allow: z.boolean().optional(),
|
||||||
requireMention: z.boolean().optional(),
|
requireMention: z.boolean().optional(),
|
||||||
autoReply: z.boolean().optional(),
|
|
||||||
users: z.array(z.union([z.string(), z.number()])).optional(),
|
users: z.array(z.union([z.string(), z.number()])).optional(),
|
||||||
skills: z.array(z.string()).optional(),
|
skills: z.array(z.string()).optional(),
|
||||||
systemPrompt: z.string().optional(),
|
systemPrompt: z.string().optional(),
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ describe("discord guild/channel resolution", () => {
|
|||||||
requireMention: true,
|
requireMention: true,
|
||||||
skills: ["search"],
|
skills: ["search"],
|
||||||
enabled: false,
|
enabled: false,
|
||||||
autoReply: true,
|
|
||||||
users: ["123"],
|
users: ["123"],
|
||||||
systemPrompt: "Use short answers.",
|
systemPrompt: "Use short answers.",
|
||||||
},
|
},
|
||||||
@@ -126,7 +125,6 @@ describe("discord guild/channel resolution", () => {
|
|||||||
expect(help?.requireMention).toBe(true);
|
expect(help?.requireMention).toBe(true);
|
||||||
expect(help?.skills).toEqual(["search"]);
|
expect(help?.skills).toEqual(["search"]);
|
||||||
expect(help?.enabled).toBe(false);
|
expect(help?.enabled).toBe(false);
|
||||||
expect(help?.autoReply).toBe(true);
|
|
||||||
expect(help?.users).toEqual(["123"]);
|
expect(help?.users).toEqual(["123"]);
|
||||||
expect(help?.systemPrompt).toBe("Use short answers.");
|
expect(help?.systemPrompt).toBe("Use short answers.");
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -101,7 +101,6 @@ export type DiscordGuildEntryResolved = {
|
|||||||
requireMention?: boolean;
|
requireMention?: boolean;
|
||||||
skills?: string[];
|
skills?: string[];
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
autoReply?: boolean;
|
|
||||||
users?: Array<string | number>;
|
users?: Array<string | number>;
|
||||||
systemPrompt?: string;
|
systemPrompt?: string;
|
||||||
}
|
}
|
||||||
@@ -113,7 +112,6 @@ export type DiscordChannelConfigResolved = {
|
|||||||
requireMention?: boolean;
|
requireMention?: boolean;
|
||||||
skills?: string[];
|
skills?: string[];
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
autoReply?: boolean;
|
|
||||||
users?: Array<string | number>;
|
users?: Array<string | number>;
|
||||||
systemPrompt?: string;
|
systemPrompt?: string;
|
||||||
};
|
};
|
||||||
@@ -601,14 +599,8 @@ export function createDiscordMessageHandler(params: {
|
|||||||
guildHistories.set(message.channelId, history);
|
guildHistories.set(message.channelId, history);
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseRequireMention =
|
|
||||||
channelConfig?.requireMention ?? guildInfo?.requireMention ?? true;
|
|
||||||
const shouldRequireMention =
|
const shouldRequireMention =
|
||||||
channelConfig?.autoReply === true
|
channelConfig?.requireMention ?? guildInfo?.requireMention ?? true;
|
||||||
? false
|
|
||||||
: channelConfig?.autoReply === false
|
|
||||||
? true
|
|
||||||
: baseRequireMention;
|
|
||||||
const hasAnyMention = Boolean(
|
const hasAnyMention = Boolean(
|
||||||
!isDirectMessage &&
|
!isDirectMessage &&
|
||||||
(message.mentionedEveryone ||
|
(message.mentionedEveryone ||
|
||||||
@@ -1810,7 +1802,6 @@ export function resolveDiscordChannelConfig(params: {
|
|||||||
requireMention: byId.requireMention,
|
requireMention: byId.requireMention,
|
||||||
skills: byId.skills,
|
skills: byId.skills,
|
||||||
enabled: byId.enabled,
|
enabled: byId.enabled,
|
||||||
autoReply: byId.autoReply,
|
|
||||||
users: byId.users,
|
users: byId.users,
|
||||||
systemPrompt: byId.systemPrompt,
|
systemPrompt: byId.systemPrompt,
|
||||||
};
|
};
|
||||||
@@ -1821,7 +1812,6 @@ export function resolveDiscordChannelConfig(params: {
|
|||||||
requireMention: entry.requireMention,
|
requireMention: entry.requireMention,
|
||||||
skills: entry.skills,
|
skills: entry.skills,
|
||||||
enabled: entry.enabled,
|
enabled: entry.enabled,
|
||||||
autoReply: entry.autoReply,
|
|
||||||
users: entry.users,
|
users: entry.users,
|
||||||
systemPrompt: entry.systemPrompt,
|
systemPrompt: entry.systemPrompt,
|
||||||
};
|
};
|
||||||
@@ -1833,7 +1823,6 @@ export function resolveDiscordChannelConfig(params: {
|
|||||||
requireMention: entry.requireMention,
|
requireMention: entry.requireMention,
|
||||||
skills: entry.skills,
|
skills: entry.skills,
|
||||||
enabled: entry.enabled,
|
enabled: entry.enabled,
|
||||||
autoReply: entry.autoReply,
|
|
||||||
users: entry.users,
|
users: entry.users,
|
||||||
systemPrompt: entry.systemPrompt,
|
systemPrompt: entry.systemPrompt,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -159,7 +159,6 @@ type SlackThreadBroadcastEvent = {
|
|||||||
type SlackChannelConfigResolved = {
|
type SlackChannelConfigResolved = {
|
||||||
allowed: boolean;
|
allowed: boolean;
|
||||||
requireMention: boolean;
|
requireMention: boolean;
|
||||||
autoReply?: boolean;
|
|
||||||
users?: Array<string | number>;
|
users?: Array<string | number>;
|
||||||
skills?: string[];
|
skills?: string[];
|
||||||
systemPrompt?: string;
|
systemPrompt?: string;
|
||||||
@@ -284,7 +283,6 @@ function resolveSlackChannelConfig(params: {
|
|||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
allow?: boolean;
|
allow?: boolean;
|
||||||
requireMention?: boolean;
|
requireMention?: boolean;
|
||||||
autoReply?: boolean;
|
|
||||||
users?: Array<string | number>;
|
users?: Array<string | number>;
|
||||||
skills?: string[];
|
skills?: string[];
|
||||||
systemPrompt?: string;
|
systemPrompt?: string;
|
||||||
@@ -308,7 +306,6 @@ function resolveSlackChannelConfig(params: {
|
|||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
allow?: boolean;
|
allow?: boolean;
|
||||||
requireMention?: boolean;
|
requireMention?: boolean;
|
||||||
autoReply?: boolean;
|
|
||||||
users?: Array<string | number>;
|
users?: Array<string | number>;
|
||||||
skills?: string[];
|
skills?: string[];
|
||||||
systemPrompt?: string;
|
systemPrompt?: string;
|
||||||
@@ -341,14 +338,13 @@ function resolveSlackChannelConfig(params: {
|
|||||||
const requireMention =
|
const requireMention =
|
||||||
firstDefined(resolved.requireMention, fallback?.requireMention, true) ??
|
firstDefined(resolved.requireMention, fallback?.requireMention, true) ??
|
||||||
true;
|
true;
|
||||||
const autoReply = firstDefined(resolved.autoReply, fallback?.autoReply);
|
|
||||||
const users = firstDefined(resolved.users, fallback?.users);
|
const users = firstDefined(resolved.users, fallback?.users);
|
||||||
const skills = firstDefined(resolved.skills, fallback?.skills);
|
const skills = firstDefined(resolved.skills, fallback?.skills);
|
||||||
const systemPrompt = firstDefined(
|
const systemPrompt = firstDefined(
|
||||||
resolved.systemPrompt,
|
resolved.systemPrompt,
|
||||||
fallback?.systemPrompt,
|
fallback?.systemPrompt,
|
||||||
);
|
);
|
||||||
return { allowed, requireMention, autoReply, users, skills, systemPrompt };
|
return { allowed, requireMention, users, skills, systemPrompt };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function resolveSlackMedia(params: {
|
async function resolveSlackMedia(params: {
|
||||||
@@ -810,11 +806,7 @@ export async function monitorSlackProvider(opts: MonitorSlackOpts = {}) {
|
|||||||
surface: "slack",
|
surface: "slack",
|
||||||
});
|
});
|
||||||
const shouldRequireMention = isRoom
|
const shouldRequireMention = isRoom
|
||||||
? channelConfig?.autoReply === true
|
? (channelConfig?.requireMention ?? true)
|
||||||
? false
|
|
||||||
: channelConfig?.autoReply === false
|
|
||||||
? true
|
|
||||||
: (channelConfig?.requireMention ?? true)
|
|
||||||
: false;
|
: false;
|
||||||
const shouldBypassMention =
|
const shouldBypassMention =
|
||||||
allowTextCommands &&
|
allowTextCommands &&
|
||||||
|
|||||||
@@ -704,6 +704,50 @@ describe("createTelegramBot", () => {
|
|||||||
expect(replySpy).toHaveBeenCalledTimes(1);
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("allows per-topic requireMention override", async () => {
|
||||||
|
onSpy.mockReset();
|
||||||
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<
|
||||||
|
typeof vi.fn
|
||||||
|
>;
|
||||||
|
replySpy.mockReset();
|
||||||
|
loadConfig.mockReturnValue({
|
||||||
|
telegram: {
|
||||||
|
groups: {
|
||||||
|
"*": { requireMention: true },
|
||||||
|
"-1001234567890": {
|
||||||
|
requireMention: true,
|
||||||
|
topics: {
|
||||||
|
"99": { requireMention: false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
createTelegramBot({ token: "tok" });
|
||||||
|
const handler = onSpy.mock.calls[0][1] as (
|
||||||
|
ctx: Record<string, unknown>,
|
||||||
|
) => Promise<void>;
|
||||||
|
|
||||||
|
await handler({
|
||||||
|
message: {
|
||||||
|
chat: {
|
||||||
|
id: -1001234567890,
|
||||||
|
type: "supergroup",
|
||||||
|
title: "Forum Group",
|
||||||
|
is_forum: true,
|
||||||
|
},
|
||||||
|
text: "hello",
|
||||||
|
date: 1736380800,
|
||||||
|
message_thread_id: 99,
|
||||||
|
},
|
||||||
|
me: { username: "clawdbot_bot" },
|
||||||
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(replySpy).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
it("honors groups default when no explicit group override exists", async () => {
|
it("honors groups default when no explicit group override exists", async () => {
|
||||||
onSpy.mockReset();
|
onSpy.mockReset();
|
||||||
const replySpy = replyModule.__replySpy as unknown as ReturnType<
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<
|
||||||
|
|||||||
@@ -381,16 +381,11 @@ export function createTelegramBot(opts: TelegramBotOptions) {
|
|||||||
(ent) => ent.type === "mention",
|
(ent) => ent.type === "mention",
|
||||||
);
|
);
|
||||||
const baseRequireMention = resolveGroupRequireMention(chatId);
|
const baseRequireMention = resolveGroupRequireMention(chatId);
|
||||||
const autoReplySetting = firstDefined(
|
const requireMention = firstDefined(
|
||||||
topicConfig?.autoReply,
|
topicConfig?.requireMention,
|
||||||
groupConfig?.autoReply,
|
groupConfig?.requireMention,
|
||||||
|
baseRequireMention,
|
||||||
);
|
);
|
||||||
const requireMention =
|
|
||||||
autoReplySetting === true
|
|
||||||
? false
|
|
||||||
: autoReplySetting === false
|
|
||||||
? true
|
|
||||||
: baseRequireMention;
|
|
||||||
const shouldBypassMention =
|
const shouldBypassMention =
|
||||||
isGroup &&
|
isGroup &&
|
||||||
requireMention &&
|
requireMention &&
|
||||||
|
|||||||
Reference in New Issue
Block a user