fix(channelbridge): IM 发送过滤 action/narration + <p>拆多气泡 + 0.5-2s 间隔
把回复转发给 IM 适配器(sendReply)时剥掉 <action>/<narration> 动作/内心独白(Web 端 才渲染灰斜体,IM 只能收纯文本),并按 <p> 段落拆成多条气泡分别发送;多气泡发送间隔 随机 0.5~2s(仅 IM,Web 前端不延迟)。新增 sanitize.go stripNarration/splitBubbles/ bubbleGap + 测试。
This commit is contained in:
1 parent
dcb0b33954
commit
5a0c1e2fd1
11 files changed
+790
-12
No files matched your search
@@ -0,0 +1,64 @@
|
||||
import type { AdminChannelRecord } from "../../../lib/types";
|
||||
import { PLATFORM_FIELDS } from "../channelFields";
|
||||
import type { ChannelConfigInitial } from "../channelConfigForm";
|
||||
|
||||
/** Platform part of an adapter name ("kook:conf0" → "kook"). */
|
||||
export function platformOf(name: string): string {
|
||||
return name.split(":")[0];
|
||||
}
|
||||
|
||||
/** Adapter (platform:account) name for one account config. The bridge registers a
|
||||
* channels.json account as "kook:conf0" (env single-account = bare platform), so a
|
||||
* config id of "default"/"" maps to the bare platform name. */
|
||||
export function adapterNameOf(platform: string, accountId?: string): string {
|
||||
const id = accountId?.trim();
|
||||
return id && id !== "default" ? `${platform}:${id}` : platform;
|
||||
}
|
||||
|
||||
/** Open-config target: the platform + the prefill bundle for its form. */
|
||||
export interface CfgTarget {
|
||||
platform: string;
|
||||
initial: ChannelConfigInitial;
|
||||
}
|
||||
|
||||
/** Build the config-form prefill from a platform name and the channel records.
|
||||
* Do NOT put the masked placeholder into `token`: it would be written back as
|
||||
* the real bot_token (overwriting the stored secret with a demo string). Keep
|
||||
* token empty (save omits it → keeps the existing secret) and show a hint. */
|
||||
export function buildCfgTarget(name: string, records: AdminChannelRecord[]): CfgTarget {
|
||||
const plat = platformOf(name);
|
||||
const acct = records.find((c) => platformOf(c.name) === plat)?.accounts?.[0];
|
||||
const acctCfg = acct?.config ?? {};
|
||||
const hasToken = typeof acctCfg.bot_token === "string" && acctCfg.bot_token && !acctCfg.bot_token.includes("(已配置");
|
||||
const platVals: Record<string, string> = {};
|
||||
for (const f of PLATFORM_FIELDS[plat] ?? []) {
|
||||
const v = acctCfg[f.key];
|
||||
platVals[f.key] = v == null ? "" : String(v);
|
||||
}
|
||||
return {
|
||||
platform: plat,
|
||||
initial: {
|
||||
accountId: acct?.id ?? "",
|
||||
tokenHint: hasToken ? "已配置(仅掩码显示),留空则不改动;如需更换请重新输入。" : "",
|
||||
baseURL: typeof acctCfg.base_url === "string" ? acctCfg.base_url : "",
|
||||
platVals,
|
||||
allowGroups: (acct?.allowGroups ?? []).join(","),
|
||||
denyGroups: (acct?.denyGroups ?? []).join(","),
|
||||
allowUsers: (acct?.allowUsers ?? []).join(","),
|
||||
denyUsers: acct?.denyUsers ? (Array.isArray(acct.denyUsers) ? acct.denyUsers.join(",") : "") : "",
|
||||
groupMsgPerMin: acctCfg.groupMsgPerMin == null ? "" : String(acctCfg.groupMsgPerMin),
|
||||
msgPerMin: acctCfg.msgPerMin == null ? "" : String(acctCfg.msgPerMin),
|
||||
maxMessages: acctCfg.maxMessages == null ? "" : String(acctCfg.maxMessages),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** The set of platforms that have at least one configured account. */
|
||||
export function configuredPlatforms(records: AdminChannelRecord[]): AdminChannelRecord[] {
|
||||
return records.filter((c) => (c.accounts?.length ?? 0) > 0);
|
||||
}
|
||||
|
||||
/** Total configured accounts across all platforms. */
|
||||
export function configuredAccountCount(records: AdminChannelRecord[]): number {
|
||||
return configuredPlatforms(records).reduce((n, c) => n + (c.accounts?.length ?? 0), 0);
|
||||
}
|
||||
Reference in new issue
Block a user