fix: preserve Telegram compatibility and stabilize Office

This commit is contained in:
xmanrui 2026-08-21 01:37:21 +08:00
parent 7f114e1c36
commit 24e690498b
23 changed files with 1679 additions and 314 deletions

View file

@ -40,7 +40,7 @@ __export(index_exports, {
name: () => name
});
module.exports = __toCommonJS(index_exports);
var React17 = __toESM(require("react"), 1);
var React18 = __toESM(require("react"), 1);
// plugin-src/client/channel-logos.js
var React = __toESM(require("react"), 1);
@ -671,6 +671,28 @@ var EN = Object.freeze({
"\u63A5\u5165 Telegram \u673A\u5668\u4EBA": "Connect a Telegram bot",
"\u5148\u901A\u8FC7 @BotFather \u83B7\u53D6 Bot Token\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002": "Get a Bot Token from @BotFather, then connect it here.",
"\u586B\u5199 @BotFather \u751F\u6210\u7684 Bot Token": "Enter the Bot Token from @BotFather",
"\u8BBF\u95EE\u6A21\u5F0F": "Access mode",
"\u8BBF\u95EE\u8BBE\u7F6E": "Access settings",
"Telegram \u8BBF\u95EE\u6A21\u5F0F": "Telegram access mode",
"\u7FA4\u804A\u5168\u90E8\u5FFD\u7565\uFF0C\u79C1\u804A\u4EC5\u5141\u8BB8\u767D\u540D\u5355\u7528\u6237\u3002": "All group messages are ignored; only allowlisted users may send DMs.",
"\u4FDD\u6301\u539F\u6709\u884C\u4E3A\uFF1A\u79C1\u804A\u76F4\u63A5\u54CD\u5E94\uFF0C\u7FA4\u804A\u5728\u88AB\u63D0\u53CA\u6216\u56DE\u590D\u65F6\u54CD\u5E94\u3002": "Keep the original behavior: respond to DMs and to group mentions or replies.",
"\u5B89\u5168\u6A21\u5F0F": "Safe mode",
"\u517C\u5BB9\u6A21\u5F0F": "Compatible mode",
"\u5DF2\u751F\u6548\uFF1A\u5B89\u5168\u6A21\u5F0F": "Active: Safe mode",
"\u5DF2\u751F\u6548\uFF1A\u517C\u5BB9\u6A21\u5F0F": "Active: Compatible mode",
"\u6A21\u5F0F": "Mode",
"\u517C\u5BB9\u6A21\u5F0F\uFF08\u9ED8\u8BA4\uFF09": "Compatible mode (default)",
"\u5B89\u5168\u6A21\u5F0F\uFF08\u79C1\u804A\u767D\u540D\u5355\uFF09": "Safe mode (private-chat allowlist)",
"\u5141\u8BB8\u79C1\u804A\u7684 Telegram User ID": "Telegram User IDs allowed to send DMs",
"\u6BCF\u884C\u4E00\u4E2A\u6570\u5B57 User ID": "One numeric User ID per line",
"\u767D\u540D\u5355\u4EC5\u5C5E\u4E8E\u5F53\u524D\u673A\u5668\u4EBA\u3002": "This allowlist belongs only to the current bot.",
"\u517C\u5BB9\u6A21\u5F0F\u4E0B\u6682\u4E0D\u4F7F\u7528\u767D\u540D\u5355\uFF0C\u5207\u6362\u6A21\u5F0F\u65F6\u4F1A\u4FDD\u7559\u3002": "Compatible mode does not enforce the allowlist; it is retained when modes change.",
"\u767D\u540D\u5355\u4E3A\u7A7A\uFF1B\u4FDD\u5B58\u540E\u8BE5\u673A\u5668\u4EBA\u4F1A\u62D2\u7EDD\u6240\u6709\u5165\u7AD9\u6D88\u606F\u3002": "The allowlist is empty; this bot will reject all inbound messages after saving.",
"\u6B63\u5728\u4FDD\u5B58\u2026": "Saving\u2026",
"\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E": "Save access settings",
"User ID \u5FC5\u987B\u662F 1\u201316 \u4F4D\u6B63\u6574\u6570\uFF0C\u6BCF\u884C\u4E00\u4E2A\u3002": "Each User ID must be a 1\u201316 digit positive integer on its own line.",
"Telegram \u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002": "Telegram access settings are currently unavailable.",
"Telegram \u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002": "Could not save Telegram access settings.",
"\u63A5\u5165 Discord \u673A\u5668\u4EBA": "Connect a Discord bot",
"\u5148\u5728 Developer Portal \u521B\u5EFA Bot \u5E76\u9080\u8BF7\u5230\u670D\u52A1\u5668\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002": "Create a bot in the Developer Portal and invite it to your server, then connect it here.",
"\u586B\u5199 Discord Developer Portal \u7684 Bot Token": "Enter the Bot Token from the Discord Developer Portal",
@ -2464,7 +2486,9 @@ var TOKEN_BOT_ENDPOINTS = Object.freeze({
deleteBot: "bot.delete",
setWorkspace: "bot.workspace.set"
});
function createTokenChannelApi(channel4, connectionSummary) {
function createTokenChannelApi(channel4, connectionSummary, {
normalizeBotExtension = () => ({})
} = {}) {
const unwrapRpcResult10 = (result) => {
if (!isRecord2(result) || typeof result.ok !== "boolean") {
throw new Error(`${channel4} \u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94`);
@ -2480,6 +2504,7 @@ function createTokenChannelApi(channel4, connectionSummary) {
if (!isRecord2(value) || !id(value.botId)) return void 0;
const connected = value.connected === true;
const state = ACCOUNT_STATES2.has(value.state) ? value.state : "offline";
const extension = normalizeBotExtension(value);
return {
botId: id(value.botId),
connected,
@ -2500,7 +2525,8 @@ function createTokenChannelApi(channel4, connectionSummary) {
error: isRecord2(value.error) ? {
code: text(value.error.code, `${channel4.toUpperCase()}_ACCOUNT_ERROR`, 80),
message: text(value.error.message, `${channel4}\u8FDE\u63A5\u5C1A\u672A\u5C31\u7EEA`)
} : null
} : null,
...isRecord2(extension) ? extension : {}
};
};
const normalizeSnapshot9 = (value) => {
@ -2580,9 +2606,11 @@ function createTokenChannelSettings(definition) {
credentialOpenLabel = "\u624B\u52A8\u63A5\u5165",
credentialCloseLabel = "\u6536\u8D77\u51ED\u636E",
credentialNoun = "Bot Token",
emptyActionLabel = "\u586B\u5199 Bot Token"
emptyActionLabel = "\u586B\u5199 Bot Token",
AccountSettings = null,
accountSettingsEndpoint = null
} = definition;
function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onAccountSettingsSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
const state = busy === "reconnect" ? "connecting" : account.state;
const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
const stateLabel2 = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
@ -2640,6 +2668,11 @@ function createTokenChannelSettings(definition) {
disabled: Boolean(busy),
onSave: onWorkspaceSave
}),
AccountSettings ? h2(AccountSettings, {
account,
busy: Boolean(busy),
onSave: onAccountSettingsSave
}) : null,
h2(
"div",
{ className: "ddt-accountFooter dim-cardFooter" },
@ -2824,6 +2857,12 @@ function createTokenChannelSettings(definition) {
endpoints.setWorkspace,
{ botId: account.botId, workspace }
),
onAccountSettingsSave: AccountSettings && accountSettingsEndpoint ? (payload) => botAction(
account,
"settings",
accountSettingsEndpoint,
{ botId: account.botId, ...payload }
) : void 0,
onRequestRemove: () => setRemoveTarget(account.botId),
onCancelRemove: () => setRemoveTarget(null),
onConfirmRemove: async () => {
@ -6117,18 +6156,46 @@ var SlackAccountCard = channel2.AccountCard;
// plugin-src/client/channels/telegram/api.js
var TELEGRAM_RPC_CHANNEL = "/telegram";
var TELEGRAM_ENDPOINTS = TOKEN_BOT_ENDPOINTS;
var api3 = createTokenChannelApi("Telegram", " Bot API \u957F\u8F6E\u8BE2");
var TELEGRAM_ENDPOINTS = Object.freeze({
...TOKEN_BOT_ENDPOINTS,
setAccessPolicy: "bot.access-policy.set"
});
var api3 = createTokenChannelApi("Telegram", " Bot API \u957F\u8F6E\u8BE2", {
normalizeBotExtension: (value) => {
const source = value?.accessPolicy;
const accessMode = source?.accessMode === "private-allowlist" ? "private-allowlist" : "compatible";
const allowedUsers = Array.isArray(source?.allowedUsers) ? [...new Set(source.allowedUsers.filter((entry) => typeof entry === "string" && /^[1-9]\d{0,15}$/.test(entry)))] : [];
return { accessPolicy: { accessMode, allowedUsers } };
}
});
var unwrapRpcResult6 = api3.unwrapRpcResult;
var normalizeSnapshot5 = api3.normalizeSnapshot;
var presentError6 = api3.presentError;
// plugin-src/client/channels/telegram/index.js
var React14 = __toESM(require("react"), 1);
// plugin-src/client/channels/telegram/styles.js
var TELEGRAM_STYLE_ID = "xmanrui-dsh-im-telegram-settings";
var CSS7 = String.raw`
.dtg-page { --ddt-accent: #229ed9; --ddt-accent-deep: #1687bd; --ddt-accent-wash: #eaf7fd; }
.dtg-avatar { color: #fff; background: #229ed9; }
.dtg-avatar svg { display: block; }
.dtg-access { display: grid; gap: 10px; padding: 12px; border: 1px solid var(--dsw-alias-border-l2, #dfe1e5); border-radius: 10px; background: var(--dsw-alias-bg-layer-2, #f7f8fa); }
.dtg-accessHeading { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.dtg-accessHeading strong { font-size: 13px; }
.dtg-accessHeading p { margin: 3px 0 0; color: var(--dsw-alias-label-secondary, #646a73); font-size: 12px; line-height: 1.5; }
.dtg-accessBadge { flex: none; padding: 3px 8px; border-radius: 999px; color: #1687bd; background: #eaf7fd; font-size: 11px; font-weight: 700; }
.dtg-accessBadge[data-mode="private-allowlist"] { color: #a15c00; background: #fff3d6; }
.dtg-accessField { display: grid; gap: 5px; color: var(--dsw-alias-label-primary, #1f2329); font-size: 12px; font-weight: 600; }
.dtg-accessField select, .dtg-accessField textarea { width: 100%; box-sizing: border-box; border: 1px solid var(--dsw-alias-border-l1, #c9cdd4); border-radius: 7px; color: inherit; background: var(--dsw-alias-bg-layer-1, #fff); font: inherit; font-weight: 400; }
.dtg-accessField select { height: 34px; padding: 0 9px; }
.dtg-accessField textarea { min-height: 68px; padding: 8px 9px; resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
.dtg-accessField small { color: var(--dsw-alias-label-secondary, #646a73); font-weight: 400; }
.dtg-accessWarning, .dtg-accessError { margin: 0; font-size: 12px; line-height: 1.5; }
.dtg-accessWarning { color: #a15c00; }
.dtg-accessError { color: var(--dsw-alias-state-error-primary, #d83931); }
.dtg-accessActions { display: flex; justify-content: flex-end; }
`;
function installTelegramStyles() {
if (typeof document === "undefined") return () => {
@ -6145,6 +6212,116 @@ function installTelegramStyles() {
}
// plugin-src/client/channels/telegram/index.js
function policyFor(account) {
return {
accessMode: account?.accessPolicy?.accessMode === "private-allowlist" ? "private-allowlist" : "compatible",
allowedUsers: Array.isArray(account?.accessPolicy?.allowedUsers) ? account.accessPolicy.allowedUsers : []
};
}
function allowedUsersFromText(value) {
const entries = value.split(/\r?\n/).map((entry) => entry.trim()).filter(Boolean);
if (entries.some((entry) => !/^[1-9]\d{0,15}$/.test(entry))) {
throw new TypeError("User ID \u5FC5\u987B\u662F 1\u201316 \u4F4D\u6B63\u6574\u6570\uFF0C\u6BCF\u884C\u4E00\u4E2A\u3002");
}
return [...new Set(entries)];
}
function TelegramAccessSettings({ account, busy = false, onSave }) {
const policy = policyFor(account);
const sourceUsers = policy.allowedUsers.join("\n");
const [accessMode, setAccessMode] = React14.useState(policy.accessMode);
const [allowedUsers, setAllowedUsers] = React14.useState(sourceUsers);
const [error, setError] = React14.useState(null);
React14.useEffect(() => {
setAccessMode(policy.accessMode);
setAllowedUsers(sourceUsers);
setError(null);
}, [policy.accessMode, sourceUsers]);
const save = async (event) => {
event.preventDefault();
setError(null);
try {
const normalized = allowedUsersFromText(allowedUsers);
if (typeof onSave !== "function") throw new Error("Telegram \u8BBF\u95EE\u8BBE\u7F6E\u6682\u4E0D\u53EF\u7528\u3002");
await onSave({ accessMode, allowedUsers: normalized });
} catch (caught) {
setError(caught?.message ?? "Telegram \u8BBF\u95EE\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
}
};
const privateAllowlist = accessMode === "private-allowlist";
const savedPrivateAllowlist = policy.accessMode === "private-allowlist";
const emptyAllowlist = privateAllowlist && allowedUsers.trim() === "";
return h2(
"form",
{ className: "dtg-access", onSubmit: save },
h2(
"div",
{ className: "dtg-accessHeading" },
h2(
"div",
null,
h2("strong", null, "\u8BBF\u95EE\u8BBE\u7F6E"),
h2("p", null, savedPrivateAllowlist ? "\u7FA4\u804A\u5168\u90E8\u5FFD\u7565\uFF0C\u79C1\u804A\u4EC5\u5141\u8BB8\u767D\u540D\u5355\u7528\u6237\u3002" : "\u4FDD\u6301\u539F\u6709\u884C\u4E3A\uFF1A\u79C1\u804A\u76F4\u63A5\u54CD\u5E94\uFF0C\u7FA4\u804A\u5728\u88AB\u63D0\u53CA\u6216\u56DE\u590D\u65F6\u54CD\u5E94\u3002")
),
h2(
"span",
{ className: "dtg-accessBadge", "data-mode": policy.accessMode },
savedPrivateAllowlist ? "\u5DF2\u751F\u6548\uFF1A\u5B89\u5168\u6A21\u5F0F" : "\u5DF2\u751F\u6548\uFF1A\u517C\u5BB9\u6A21\u5F0F"
)
),
h2(
"label",
{ className: "dtg-accessField" },
h2("span", null, "\u6A21\u5F0F"),
h2(
"select",
{
value: accessMode,
disabled: busy,
"aria-label": "Telegram \u8BBF\u95EE\u6A21\u5F0F",
onChange: (event) => {
setAccessMode(event.target.value);
setError(null);
}
},
h2("option", { value: "compatible" }, "\u517C\u5BB9\u6A21\u5F0F\uFF08\u9ED8\u8BA4\uFF09"),
h2("option", { value: "private-allowlist" }, "\u5B89\u5168\u6A21\u5F0F\uFF08\u79C1\u804A\u767D\u540D\u5355\uFF09")
)
),
h2(
"label",
{ className: "dtg-accessField" },
h2("span", null, "\u5141\u8BB8\u79C1\u804A\u7684 Telegram User ID"),
h2("textarea", {
value: allowedUsers,
disabled: busy,
rows: 3,
placeholder: "\u6BCF\u884C\u4E00\u4E2A\u6570\u5B57 User ID",
"aria-label": "\u5141\u8BB8\u79C1\u804A\u7684 Telegram User ID",
onChange: (event) => {
setAllowedUsers(event.target.value);
setError(null);
}
}),
h2("small", null, privateAllowlist ? "\u767D\u540D\u5355\u4EC5\u5C5E\u4E8E\u5F53\u524D\u673A\u5668\u4EBA\u3002" : "\u517C\u5BB9\u6A21\u5F0F\u4E0B\u6682\u4E0D\u4F7F\u7528\u767D\u540D\u5355\uFF0C\u5207\u6362\u6A21\u5F0F\u65F6\u4F1A\u4FDD\u7559\u3002")
),
emptyAllowlist ? h2(
"p",
{ className: "dtg-accessWarning", role: "status" },
"\u767D\u540D\u5355\u4E3A\u7A7A\uFF1B\u4FDD\u5B58\u540E\u8BE5\u673A\u5668\u4EBA\u4F1A\u62D2\u7EDD\u6240\u6709\u5165\u7AD9\u6D88\u606F\u3002"
) : null,
error ? h2("p", { className: "dtg-accessError", role: "alert" }, error) : null,
h2(
"div",
{ className: "dtg-accessActions" },
h2("button", {
type: "submit",
className: "ddt-button",
"data-kind": "secondary",
disabled: busy
}, busy ? "\u6B63\u5728\u4FDD\u5B58\u2026" : "\u4FDD\u5B58\u8BBF\u95EE\u8BBE\u7F6E")
)
);
}
var channel3 = createTokenChannelSettings({
channel: "Telegram",
endpoints: TELEGRAM_ENDPOINTS,
@ -6157,7 +6334,9 @@ var channel3 = createTokenChannelSettings({
tokenPlaceholder: "\u586B\u5199 @BotFather \u751F\u6210\u7684 Bot Token",
emptyTitle: "\u63A5\u5165 Telegram \u673A\u5668\u4EBA",
emptyDescription: "\u5148\u901A\u8FC7 @BotFather \u83B7\u53D6 Bot Token\uFF0C\u518D\u5728\u8FD9\u91CC\u5B8C\u6210\u63A5\u5165\u3002",
platformLabel: "Telegram"
platformLabel: "Telegram",
AccountSettings: TelegramAccessSettings,
accountSettingsEndpoint: TELEGRAM_ENDPOINTS.setAccessPolicy
});
var TelegramSettingsTab = channel3.SettingsTab;
var TelegramAccountCard = channel3.AccountCard;
@ -6280,7 +6459,7 @@ function formatRemaining4(milliseconds) {
}
// plugin-src/client/channels/wecom/index.js
var React14 = __toESM(require("react"), 1);
var React15 = __toESM(require("react"), 1);
// plugin-src/client/channels/wecom/styles.js
var WECOM_STYLE_ID = "xmanrui-dsh-im-wecom-settings";
@ -6305,7 +6484,7 @@ function installWecomStyles() {
// plugin-src/client/channels/wecom/index.js
var ACTIVE_STATES2 = /* @__PURE__ */ new Set(["pending", "refreshing", "connecting"]);
var Button10 = React14.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
var Button10 = React15.forwardRef(function Button11({ children, kind = "secondary", className = "", ...props }, ref) {
return h2("button", {
...props,
ref,
@ -6590,21 +6769,21 @@ function AccountCard3({
);
}
function WecomSettingsTab({ rpcCall }) {
const [model, setModel] = React14.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null });
const [provision, setProvision] = React14.useState(null);
const [busy, setBusy] = React14.useState(false);
const [busyByBot, setBusyByBot] = React14.useState({});
const [feedbackByBot, setFeedbackByBot] = React14.useState({});
const [removeTarget, setRemoveTarget] = React14.useState(null);
const [credentialOpen, setCredentialOpen] = React14.useState(false);
const [credentialError, setCredentialError] = React14.useState(null);
const [notice, setNotice] = React14.useState("");
const [now, setNow] = React14.useState(Date.now());
const mounted = React14.useRef(true);
const [model, setModel] = React15.useState({ phase: "loading", bots: [], totals: { configured: 0, connected: 0 }, error: null });
const [provision, setProvision] = React15.useState(null);
const [busy, setBusy] = React15.useState(false);
const [busyByBot, setBusyByBot] = React15.useState({});
const [feedbackByBot, setFeedbackByBot] = React15.useState({});
const [removeTarget, setRemoveTarget] = React15.useState(null);
const [credentialOpen, setCredentialOpen] = React15.useState(false);
const [credentialError, setCredentialError] = React15.useState(null);
const [notice, setNotice] = React15.useState("");
const [now, setNow] = React15.useState(Date.now());
const mounted = React15.useRef(true);
const workspaceFence = useWorkspaceSnapshotFence();
const addButtonRef = React14.useRef(null);
const noticeFrameRef = React14.useRef(null);
const announce = React14.useCallback((message) => {
const addButtonRef = React15.useRef(null);
const noticeFrameRef = React15.useRef(null);
const announce = React15.useCallback((message) => {
if (!mounted.current) return;
if (noticeFrameRef.current !== null) {
window.cancelAnimationFrame(noticeFrameRef.current);
@ -6618,7 +6797,7 @@ function WecomSettingsTab({ rpcCall }) {
});
}
}, []);
React14.useEffect(() => {
React15.useEffect(() => {
const disposeDingtalk = installDingtalkStyles();
const disposeWecom = installWecomStyles();
mounted.current = true;
@ -6632,11 +6811,11 @@ function WecomSettingsTab({ rpcCall }) {
disposeDingtalk();
};
}, []);
const invoke = React14.useCallback(async (endpoint, payload = {}, signal) => {
const invoke = React15.useCallback(async (endpoint, payload = {}, signal) => {
if (typeof rpcCall !== "function") throw new TypeError("\u4F01\u4E1A\u5FAE\u4FE1\u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
return unwrapRpcResult7(await rpcCall(endpoint, payload, signal));
}, [rpcCall]);
const loadStatus = React14.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
const loadStatus = React15.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
const workspaceVersion = workspaceFence.beginStatus();
if (workspaceVersion === null) return void 0;
if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@ -6656,12 +6835,12 @@ function WecomSettingsTab({ rpcCall }) {
return void 0;
}
}, [invoke, workspaceFence]);
React14.useEffect(() => {
React15.useEffect(() => {
const controller = new AbortController();
void loadStatus({ signal: controller.signal, restore: true });
return () => controller.abort();
}, [loadStatus]);
React14.useEffect(() => {
React15.useEffect(() => {
if (model.phase !== "ready") return void 0;
const controller = new AbortController();
const timer = window.setInterval(() => void loadStatus({ signal: controller.signal, silent: true }), 15e3);
@ -6670,12 +6849,12 @@ function WecomSettingsTab({ rpcCall }) {
window.clearInterval(timer);
};
}, [loadStatus, model.phase]);
React14.useEffect(() => {
React15.useEffect(() => {
if (!provision || !ACTIVE_STATES2.has(provision.status)) return void 0;
const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
return () => window.clearInterval(timer);
}, [provision?.attemptId, provision?.status]);
const startProvisioning = React14.useCallback(async (replace = false) => {
const startProvisioning = React15.useCallback(async (replace = false) => {
setCredentialOpen(false);
setCredentialError(null);
setBusy(true);
@ -6693,7 +6872,7 @@ function WecomSettingsTab({ rpcCall }) {
if (mounted.current) setBusy(false);
}
}, [invoke, provision?.attemptId]);
const bindCredentials = React14.useCallback(async ({ identity, secret }) => {
const bindCredentials = React15.useCallback(async ({ identity, secret }) => {
const snapshotVersion = workspaceFence.beginMutation();
setBusy(true);
setCredentialError(null);
@ -6715,7 +6894,7 @@ function WecomSettingsTab({ rpcCall }) {
if (mounted.current) setBusy(false);
}
}, [invoke, loadStatus, workspaceFence]);
const closeProvision = React14.useCallback(async () => {
const closeProvision = React15.useCallback(async () => {
setBusy(true);
try {
if (provision?.attemptId && ACTIVE_STATES2.has(provision.status)) {
@ -6726,7 +6905,7 @@ function WecomSettingsTab({ rpcCall }) {
if (mounted.current) setBusy(false);
}
}, [invoke, provision?.attemptId, provision?.status]);
React14.useEffect(() => {
React15.useEffect(() => {
const attemptId = provision?.attemptId;
if (!attemptId || !ACTIVE_STATES2.has(provision.status)) return void 0;
const controller = new AbortController();
@ -6756,7 +6935,7 @@ function WecomSettingsTab({ rpcCall }) {
window.clearTimeout(timer);
};
}, [invoke, loadStatus, provision?.attemptId, provision?.pollIntervalMs, provision?.status]);
const botAction = React14.useCallback(async (account, operation, endpoint, payload) => {
const botAction = React15.useCallback(async (account, operation, endpoint, payload) => {
const snapshotVersion = workspaceFence.beginMutation();
setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
try {
@ -6775,7 +6954,7 @@ function WecomSettingsTab({ rpcCall }) {
});
}
}, [invoke, loadStatus, workspaceFence]);
const reconnect = React14.useCallback(async (account) => {
const reconnect = React15.useCallback(async (account) => {
setFeedbackByBot((current) => {
const next = { ...current };
delete next[account.botId];
@ -6888,7 +7067,7 @@ function WecomSettingsTab({ rpcCall }) {
}),
h2("div", { className: "ddt-visuallyHidden", role: "status", "aria-live": "polite" }, notice),
model.phase === "loading" ? h2(LoadingView4) : model.phase === "error" ? h2("div", { className: "ddt-card dim-surfaceCard" }, h2("div", { className: "ddt-inlineError dim-inlineError" }, h2("h3", null, "\u65E0\u6CD5\u8BFB\u53D6\u4F01\u4E1A\u5FAE\u4FE1\u673A\u5668\u4EBA\u72B6\u6001"), h2("p", null, model.error?.message), h2(Button10, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
React14.Fragment,
React15.Fragment,
null,
credentialView,
provisionView,
@ -6899,7 +7078,7 @@ function WecomSettingsTab({ rpcCall }) {
}
// plugin-src/client/channels/weixin/index.js
var React15 = __toESM(require("react"), 1);
var React16 = __toESM(require("react"), 1);
// plugin-src/client/channels/weixin/api.js
var WEIXIN_RPC_CHANNEL = "/weixin";
@ -7172,7 +7351,7 @@ function installWeixinStyles() {
}
// plugin-src/client/channels/weixin/index.js
var Button12 = React15.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
var Button12 = React16.forwardRef(function Button13({ children, kind = "secondary", className = "", ...props }, ref) {
return h2("button", {
...props,
ref,
@ -7245,14 +7424,14 @@ function EmptyView5({ onStart, busy }) {
);
}
function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
const [imageFailed, setImageFailed] = React15.useState(false);
const [imageFailed, setImageFailed] = React16.useState(false);
const source = safeQrSource5(provision.qrCodeDataUrl);
const href = safeVerificationUrl(provision.verificationUrl);
const remaining = Math.max(0, provision.expiresAt - now);
const expired = remaining === 0 || provision.status === "expired";
const duration = Math.max(1, provision.durationMs ?? 5 * 6e4);
const progress = Math.round(Math.min(1, remaining / duration) * 100);
React15.useEffect(() => setImageFailed(false), [source]);
React16.useEffect(() => setImageFailed(false), [source]);
return h2(
"div",
{ className: "dxw-card dim-surfaceCard" },
@ -7319,9 +7498,9 @@ function QrPanel4({ provision, now, busy, onRefresh, onCancel }) {
);
}
function VerificationPanel({ provision, busy, onSubmit, onCancel }) {
const [code, setCode] = React15.useState("");
const [code, setCode] = React16.useState("");
const valid = /^\d{4,8}$/.test(code);
React15.useEffect(() => setCode(""), [provision.attemptId]);
React16.useEffect(() => setCode(""), [provision.attemptId]);
return h2(
"div",
{ className: "dxw-card dim-surfaceCard" },
@ -7541,40 +7720,40 @@ function mergeWeixinProvisioningSnapshot(current, incoming, { restoreProvisionin
};
}
function WeixinSettingsTab({ rpcCall }) {
const [model, setModel] = React15.useState({
const [model, setModel] = React16.useState({
phase: "loading",
bots: [],
totals: EMPTY_TOTALS3,
revision: 0,
error: null
});
const [provision, setProvision] = React15.useState(null);
const [busy, setBusy] = React15.useState(false);
const [busyByBot, setBusyByBot] = React15.useState({});
const [feedbackByBot, setFeedbackByBot] = React15.useState({});
const [removeTarget, setRemoveTarget] = React15.useState(null);
const [notice, setNotice] = React15.useState("");
const [now, setNow] = React15.useState(() => Date.now());
const addButtonRef = React15.useRef(null);
const mountedRef = React15.useRef(true);
const [provision, setProvision] = React16.useState(null);
const [busy, setBusy] = React16.useState(false);
const [busyByBot, setBusyByBot] = React16.useState({});
const [feedbackByBot, setFeedbackByBot] = React16.useState({});
const [removeTarget, setRemoveTarget] = React16.useState(null);
const [notice, setNotice] = React16.useState("");
const [now, setNow] = React16.useState(() => Date.now());
const addButtonRef = React16.useRef(null);
const mountedRef = React16.useRef(true);
const workspaceFence = useWorkspaceSnapshotFence();
const scheduleAnimationFrame = useAnimationFrameScheduler();
React15.useEffect(() => {
React16.useEffect(() => {
mountedRef.current = true;
return () => {
mountedRef.current = false;
};
}, []);
const announce = React15.useCallback((value) => {
const announce = React16.useCallback((value) => {
setNotice("");
scheduleAnimationFrame(() => {
if (value) setNotice(value);
}, "announcement");
}, [scheduleAnimationFrame]);
const invoke = React15.useCallback(async (endpoint, payload = {}, signal) => {
const invoke = React16.useCallback(async (endpoint, payload = {}, signal) => {
return unwrapRpcResult8(await rpcCall(endpoint, payload, signal));
}, [rpcCall]);
const loadStatus = React15.useCallback(async ({
const loadStatus = React16.useCallback(async ({
signal,
silent = false,
restoreProvisioning = false
@ -7610,12 +7789,12 @@ function WeixinSettingsTab({ rpcCall }) {
return void 0;
}
}, [invoke, workspaceFence]);
React15.useEffect(() => {
React16.useEffect(() => {
const controller = new AbortController();
void loadStatus({ signal: controller.signal, restoreProvisioning: true });
return () => controller.abort();
}, [loadStatus]);
React15.useEffect(() => {
React16.useEffect(() => {
if (model.phase !== "ready") return void 0;
const controller = new AbortController();
let running = false;
@ -7634,12 +7813,12 @@ function WeixinSettingsTab({ rpcCall }) {
window.clearInterval(timer);
};
}, [loadStatus, model.phase]);
React15.useEffect(() => {
React16.useEffect(() => {
if (!provision || !["pending", "scanned"].includes(provision.status)) return void 0;
const timer = window.setInterval(() => setNow(Date.now()), 1e3);
return () => window.clearInterval(timer);
}, [provision?.attemptId, provision?.status]);
const startProvisioning = React15.useCallback(async ({ replace = false } = {}) => {
const startProvisioning = React16.useCallback(async ({ replace = false } = {}) => {
setBusy(true);
try {
if (replace && provision?.attemptId) {
@ -7660,7 +7839,7 @@ function WeixinSettingsTab({ rpcCall }) {
setBusy(false);
}
}, [announce, invoke, provision?.attemptId]);
const cancelProvisioning = React15.useCallback(async () => {
const cancelProvisioning = React16.useCallback(async () => {
setBusy(true);
try {
if (provision?.attemptId && !["failed", "expired", "cancelled"].includes(provision.status)) {
@ -7675,7 +7854,7 @@ function WeixinSettingsTab({ rpcCall }) {
setBusy(false);
}
}, [announce, invoke, provision?.attemptId, provision?.status, scheduleAnimationFrame]);
const submitVerification = React15.useCallback(async (verifyCode) => {
const submitVerification = React16.useCallback(async (verifyCode) => {
if (!provision?.attemptId) return;
setBusy(true);
try {
@ -7691,7 +7870,7 @@ function WeixinSettingsTab({ rpcCall }) {
setBusy(false);
}
}, [announce, invoke, provision?.attemptId]);
React15.useEffect(() => {
React16.useEffect(() => {
const attemptId = provision?.attemptId;
if (!attemptId || !["pending", "scanned", "connecting"].includes(provision.status)) return void 0;
const controller = new AbortController();
@ -7739,7 +7918,7 @@ function WeixinSettingsTab({ rpcCall }) {
controller.abort();
};
}, [announce, invoke, loadStatus, provision?.attemptId, provision?.status, provision?.pollIntervalMs]);
const setBotBusy = React15.useCallback((botId, value) => {
const setBotBusy = React16.useCallback((botId, value) => {
setBusyByBot((current) => {
const next = { ...current };
if (value) next[botId] = value;
@ -7747,7 +7926,7 @@ function WeixinSettingsTab({ rpcCall }) {
return next;
});
}, []);
const reconnect = React15.useCallback(async (account) => {
const reconnect = React16.useCallback(async (account) => {
const snapshotVersion = workspaceFence.beginMutation();
setBotBusy(account.botId, "reconnect");
setFeedbackByBot((current) => {
@ -7792,7 +7971,7 @@ function WeixinSettingsTab({ rpcCall }) {
setBotBusy(account.botId, null);
}
}, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
const saveWorkspace = React15.useCallback(async (account, workspace) => {
const saveWorkspace = React16.useCallback(async (account, workspace) => {
const workspaceVersion = workspaceFence.beginMutation();
setBotBusy(account.botId, "workspace");
try {
@ -7815,7 +7994,7 @@ function WeixinSettingsTab({ rpcCall }) {
if (mountedRef.current) setBotBusy(account.botId, null);
}
}, [invoke, loadStatus, setBotBusy, workspaceFence]);
const remove = React15.useCallback(async (account) => {
const remove = React16.useCallback(async (account) => {
const snapshotVersion = workspaceFence.beginMutation();
setBotBusy(account.botId, "delete");
try {
@ -7891,7 +8070,7 @@ function WeixinSettingsTab({ rpcCall }) {
h2(Button12, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
)
) : h2(
React15.Fragment,
React16.Fragment,
null,
provisionView,
model.bots.length === 0 && !provision ? h2(EmptyView5, { onStart: () => void startProvisioning(), busy }) : null,
@ -8023,7 +8202,7 @@ function formatRemaining6(milliseconds) {
}
// plugin-src/client/channels/whatsapp/index.js
var React16 = __toESM(require("react"), 1);
var React17 = __toESM(require("react"), 1);
// plugin-src/client/channels/whatsapp/styles.js
var WHATSAPP_STYLE_ID = "xmanrui-dsh-im-whatsapp-settings";
@ -8048,7 +8227,7 @@ function installWhatsappStyles() {
// plugin-src/client/channels/whatsapp/index.js
var ACTIVE_STATES3 = /* @__PURE__ */ new Set(["pending", "connecting"]);
var Button14 = React16.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
var Button14 = React17.forwardRef(function Button15({ children, kind = "secondary", className = "", ...props }, ref) {
return h2("button", {
...props,
ref,
@ -8363,22 +8542,22 @@ function WhatsappAccountCard({
);
}
function WhatsappSettingsTab({ rpcCall }) {
const [model, setModel] = React16.useState({
const [model, setModel] = React17.useState({
phase: "loading",
bots: [],
totals: { configured: 0, connected: 0 },
error: null
});
const [provision, setProvision] = React16.useState(null);
const [busy, setBusy] = React16.useState(false);
const [busyByBot, setBusyByBot] = React16.useState({});
const [testNoticeByBot, setTestNoticeByBot] = React16.useState({});
const [removeTarget, setRemoveTarget] = React16.useState(null);
const [now, setNow] = React16.useState(Date.now());
const mounted = React16.useRef(true);
const [provision, setProvision] = React17.useState(null);
const [busy, setBusy] = React17.useState(false);
const [busyByBot, setBusyByBot] = React17.useState({});
const [testNoticeByBot, setTestNoticeByBot] = React17.useState({});
const [removeTarget, setRemoveTarget] = React17.useState(null);
const [now, setNow] = React17.useState(Date.now());
const mounted = React17.useRef(true);
const workspaceFence = useWorkspaceSnapshotFence();
const addButtonRef = React16.useRef(null);
React16.useEffect(() => {
const addButtonRef = React17.useRef(null);
React17.useEffect(() => {
const disposeDingtalk = installDingtalkStyles();
const disposeWhatsapp = installWhatsappStyles();
mounted.current = true;
@ -8388,11 +8567,11 @@ function WhatsappSettingsTab({ rpcCall }) {
disposeDingtalk();
};
}, []);
const invoke = React16.useCallback(async (endpoint, payload = {}, signal) => {
const invoke = React17.useCallback(async (endpoint, payload = {}, signal) => {
if (typeof rpcCall !== "function") throw new TypeError("WhatsApp \u8BBE\u7F6E\u9875\u7F3A\u5C11 RPC \u8FDE\u63A5");
return unwrapRpcResult9(await rpcCall(endpoint, payload, signal));
}, [rpcCall]);
const loadStatus = React16.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
const loadStatus = React17.useCallback(async ({ signal, silent = false, restore = false } = {}) => {
const workspaceVersion = workspaceFence.beginStatus();
if (workspaceVersion === null) return void 0;
if (!silent && mounted.current) setModel((current) => ({ ...current, phase: "loading", error: null }));
@ -8416,12 +8595,12 @@ function WhatsappSettingsTab({ rpcCall }) {
return void 0;
}
}, [invoke, workspaceFence]);
React16.useEffect(() => {
React17.useEffect(() => {
const controller = new AbortController();
void loadStatus({ signal: controller.signal, restore: true });
return () => controller.abort();
}, [loadStatus]);
React16.useEffect(() => {
React17.useEffect(() => {
if (model.phase !== "ready") return void 0;
const controller = new AbortController();
const timer = window.setInterval(
@ -8433,12 +8612,12 @@ function WhatsappSettingsTab({ rpcCall }) {
window.clearInterval(timer);
};
}, [loadStatus, model.phase]);
React16.useEffect(() => {
React17.useEffect(() => {
if (!provision || !ACTIVE_STATES3.has(provision.status)) return void 0;
const timer = window.setInterval(() => mounted.current && setNow(Date.now()), 1e3);
return () => window.clearInterval(timer);
}, [provision?.attemptId, provision?.status]);
const startProvisioning = React16.useCallback(async (replace = false) => {
const startProvisioning = React17.useCallback(async (replace = false) => {
setBusy(true);
try {
if (replace && provision?.attemptId) {
@ -8456,7 +8635,7 @@ function WhatsappSettingsTab({ rpcCall }) {
if (mounted.current) setBusy(false);
}
}, [invoke, provision?.attemptId]);
const closeProvision = React16.useCallback(async () => {
const closeProvision = React17.useCallback(async () => {
setBusy(true);
try {
if (provision?.attemptId && ACTIVE_STATES3.has(provision.status)) {
@ -8467,7 +8646,7 @@ function WhatsappSettingsTab({ rpcCall }) {
if (mounted.current) setBusy(false);
}
}, [invoke, provision?.attemptId, provision?.status]);
React16.useEffect(() => {
React17.useEffect(() => {
const attemptId = provision?.attemptId;
if (!attemptId || !ACTIVE_STATES3.has(provision.status)) return void 0;
const controller = new AbortController();
@ -8508,7 +8687,7 @@ function WhatsappSettingsTab({ rpcCall }) {
if (timer) window.clearTimeout(timer);
};
}, [invoke, loadStatus, provision?.attemptId, provision?.status]);
const botAction = React16.useCallback(async (account, operation, endpoint, payload) => {
const botAction = React17.useCallback(async (account, operation, endpoint, payload) => {
const snapshotVersion = workspaceFence.beginMutation();
setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
if (operation === "reconnect") {
@ -8607,7 +8786,7 @@ function WhatsappSettingsTab({ rpcCall }) {
h2(Button14, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6")
)
) : h2(
React16.Fragment,
React17.Fragment,
null,
provision?.status === "pending" ? h2(QrPanel5, {
provision,
@ -8999,8 +9178,8 @@ function IMSettingsTab({
officeRpcCall,
workspaceDirectoryPicker
}) {
const [selected, setSelected] = React17.useState("weixin");
const githubTooltipId = React17.useId();
const [selected, setSelected] = React18.useState("weixin");
const githubTooltipId = React18.useId();
const active = CHANNELS.find((channel4) => channel4.id === selected) ?? CHANNELS[0];
return h2(
WorkspaceDirectoryPickerContext.Provider,