mirror of
https://github.com/hansjone/dsh-im-ops.git
synced 2026-10-11 06:33:17 +08:00
feat: send test messages during bot connection checks
This commit is contained in:
parent
94da24538f
commit
fb61c4235f
78 changed files with 3460 additions and 210 deletions
408
lib/client.js
408
lib/client.js
|
|
@ -246,6 +246,13 @@ function normalizeError(value, fallbackCode, fallbackMessage) {
|
|||
message: sanitizeMessage(value.message, fallbackMessage)
|
||||
};
|
||||
}
|
||||
function normalizeTestMessage(value) {
|
||||
if (!isRecord(value)) return null;
|
||||
if (value.sent === true) return { sent: true };
|
||||
if (value.sent !== false) return null;
|
||||
const code = value.code === "test-target-unavailable" ? "test-target-unavailable" : "test-message-failed";
|
||||
return { sent: false, code };
|
||||
}
|
||||
function unwrapRpcResult(result) {
|
||||
if (!isRecord(result) || typeof result.ok !== "boolean") {
|
||||
throw new Error("\u9489\u9489\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
|
||||
|
|
@ -340,9 +347,17 @@ function normalizeSnapshot(value) {
|
|||
configured: bots.length,
|
||||
connected: bots.filter((bot) => bot.connected).length
|
||||
},
|
||||
provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null
|
||||
provisioning: source.provisioning ? normalizeProvisioning(source.provisioning) : null,
|
||||
testMessage: normalizeTestMessage(source.testMessage)
|
||||
};
|
||||
}
|
||||
function connectionTestFeedback(result) {
|
||||
if (result?.sent === true) return "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002";
|
||||
if (result?.code === "test-target-unavailable") {
|
||||
return "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u8BF7\u5148\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u9001 /status\uFF0C\u518D\u91CD\u65B0\u68C0\u67E5\u3002";
|
||||
}
|
||||
return result ? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
|
||||
}
|
||||
function presentError(error) {
|
||||
return {
|
||||
code: safeErrorCode(error?.code, "DINGTALK_ERROR"),
|
||||
|
|
@ -436,6 +451,23 @@ var EN = Object.freeze({
|
|||
"\u521A\u521A": "Just now",
|
||||
"\u68C0\u67E5\u8FDE\u63A5": "Check connection",
|
||||
"\u68C0\u67E5\u4E2D\u2026": "Checking\u2026",
|
||||
"\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002": "Connection check failed. Try again later.",
|
||||
"\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u5BF9\u5E94\u673A\u5668\u4EBA\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002": "Test message sent. Check the matching bot conversation.",
|
||||
"\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u8BF7\u5148\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u9001 /status\uFF0C\u518D\u91CD\u65B0\u68C0\u67E5\u3002": "Connection check completed. Send /status to the bot in a direct message, then check again.",
|
||||
"\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002": "Connection check completed, but the test message could not be sent.",
|
||||
"\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u98DE\u4E66\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002": "Test message sent. Check the Feishu conversation.",
|
||||
"\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684\u6D4B\u8BD5\u6D88\u606F\u63A5\u6536\u4EBA\u3002": "Connection check completed, but no test-message recipient is available.",
|
||||
"\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230 WhatsApp \u81EA\u804A\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002": "Test message sent. Check the WhatsApp self-chat.",
|
||||
"\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684 WhatsApp \u81EA\u804A\u76EE\u6807\u3002": "Connection check completed, but no WhatsApp self-chat target is available.",
|
||||
"\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002": "DingTalk connection check completed and the test message was sent.",
|
||||
"\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u8BF7\u5148\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u9001 /status\uFF0C\u518D\u91CD\u65B0\u68C0\u67E5\u3002": "DingTalk connection check completed. Send /status to the bot in a direct message, then check again.",
|
||||
"\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002": "DingTalk connection check completed, but the test message could not be sent.",
|
||||
"\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002": "WeChat connection check completed and the test message was sent.",
|
||||
"\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u8BF7\u5148\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u9001 /status\uFF0C\u518D\u91CD\u65B0\u68C0\u67E5\u3002": "WeChat connection check completed. Send /status to the bot in a direct message, then check again.",
|
||||
"\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002": "WeChat connection check completed, but the test message could not be sent.",
|
||||
"\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002": "WeCom connection check completed and the test message was sent.",
|
||||
"\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u8BF7\u5148\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u9001 /status\uFF0C\u518D\u91CD\u65B0\u68C0\u67E5\u3002": "WeCom connection check completed. Send /status to the bot in a direct message, then check again.",
|
||||
"\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002": "WeCom connection check completed, but the test message could not be sent.",
|
||||
"\u91CD\u8BD5\u8FDE\u63A5": "Reconnect",
|
||||
"\u91CD\u8BD5\u4E2D\u2026": "Retrying\u2026",
|
||||
"\u79FB\u9664\u63A5\u5165": "Remove connection",
|
||||
|
|
@ -1735,6 +1767,7 @@ function RemoveConfirmation({ account, busy, onConfirm, onCancel }) {
|
|||
function AccountCard({
|
||||
account,
|
||||
busy,
|
||||
feedback,
|
||||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
|
|
@ -1798,6 +1831,10 @@ function AccountCard({
|
|||
"div",
|
||||
{ className: "ddt-accountFooter dim-cardFooter" },
|
||||
summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
|
||||
feedback ? h2("div", {
|
||||
className: "ddt-summary dim-cardSummary",
|
||||
role: "status"
|
||||
}, feedback) : null,
|
||||
h2(
|
||||
"div",
|
||||
{ className: "ddt-actions dim-cardActions" },
|
||||
|
|
@ -1837,6 +1874,7 @@ function AccountList(props) {
|
|||
h2(AccountCard, {
|
||||
account,
|
||||
busy: props.busyByBot[account.botId],
|
||||
feedback: props.feedbackByBot[account.botId]?.message,
|
||||
removing: props.removeTarget === account.botId,
|
||||
onReconnect: () => props.onReconnect(account),
|
||||
onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
|
||||
|
|
@ -1859,6 +1897,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|||
const [provision, setProvision] = React7.useState(null);
|
||||
const [busy, setBusy] = React7.useState(false);
|
||||
const [busyByBot, setBusyByBot] = React7.useState({});
|
||||
const [feedbackByBot, setFeedbackByBot] = React7.useState({});
|
||||
const [removeTarget, setRemoveTarget] = React7.useState(null);
|
||||
const [credentialOpen, setCredentialOpen] = React7.useState(false);
|
||||
const [credentialError, setCredentialError] = React7.useState(null);
|
||||
|
|
@ -1900,6 +1939,21 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|||
});
|
||||
}
|
||||
}, []);
|
||||
const discardStaleFeedback = React7.useCallback((snapshot) => {
|
||||
const botsById = new Map(snapshot.bots.map((bot) => [bot.botId, bot]));
|
||||
setFeedbackByBot((current) => {
|
||||
let changed = false;
|
||||
const next = { ...current };
|
||||
for (const [botId, feedback] of Object.entries(next)) {
|
||||
const bot = botsById.get(botId);
|
||||
if (!bot || feedback.clearWhenDisconnected && (!bot.connected || bot.error)) {
|
||||
delete next[botId];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
return changed ? next : current;
|
||||
});
|
||||
}, []);
|
||||
const focusAddButton = React7.useCallback(() => {
|
||||
if (!mountedRef.current) return;
|
||||
if (focusFrameRef.current !== null) window.cancelAnimationFrame(focusFrameRef.current);
|
||||
|
|
@ -1936,6 +1990,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|||
revision: snapshot.revision,
|
||||
error: null
|
||||
});
|
||||
discardStaleFeedback(snapshot);
|
||||
if (restoreProvisioning && snapshot.provisioning) {
|
||||
setProvision((current) => !current || current.attemptId === snapshot.provisioning.attemptId ? {
|
||||
...current,
|
||||
|
|
@ -1953,7 +2008,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|||
}));
|
||||
return void 0;
|
||||
}
|
||||
}, [invoke, workspaceFence]);
|
||||
}, [discardStaleFeedback, invoke, workspaceFence]);
|
||||
React7.useEffect(() => {
|
||||
const controller = new AbortController();
|
||||
void loadStatus({ signal: controller.signal, restoreProvisioning: true });
|
||||
|
|
@ -2042,6 +2097,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|||
revision: snapshot.revision,
|
||||
error: null
|
||||
});
|
||||
discardStaleFeedback(snapshot);
|
||||
}
|
||||
setCredentialOpen(false);
|
||||
announce("\u9489\u9489\u673A\u5668\u4EBA\u51ED\u636E\u5DF2\u7ED1\u5B9A\u3002");
|
||||
|
|
@ -2052,7 +2108,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|||
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
||||
if (mountedRef.current) setBusy(false);
|
||||
}
|
||||
}, [announce, invoke, loadStatus, workspaceFence]);
|
||||
}, [announce, discardStaleFeedback, invoke, loadStatus, workspaceFence]);
|
||||
const cancelProvisioning = React7.useCallback(async () => {
|
||||
if (!mountedRef.current) return;
|
||||
setBusy(true);
|
||||
|
|
@ -2143,6 +2199,13 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|||
if (!mountedRef.current) return void 0;
|
||||
const snapshotVersion = workspaceFence.beginMutation();
|
||||
setBotBusy(account.botId, operation);
|
||||
if (operation === "reconnect") {
|
||||
setFeedbackByBot((current) => {
|
||||
const next = { ...current };
|
||||
delete next[account.botId];
|
||||
return next;
|
||||
});
|
||||
}
|
||||
try {
|
||||
const snapshot = normalizeSnapshot(await invoke(endpoint, payload));
|
||||
if (!mountedRef.current) return void 0;
|
||||
|
|
@ -2154,25 +2217,47 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|||
revision: snapshot.revision,
|
||||
error: null
|
||||
});
|
||||
discardStaleFeedback(snapshot);
|
||||
}
|
||||
announce(typeof success === "function" ? success(snapshot) : success);
|
||||
const successMessage = typeof success === "function" ? success(snapshot) : success;
|
||||
if (operation === "reconnect") {
|
||||
setFeedbackByBot((current) => ({
|
||||
...current,
|
||||
[account.botId]: {
|
||||
message: successMessage,
|
||||
clearWhenDisconnected: snapshot.testMessage?.sent === true
|
||||
}
|
||||
}));
|
||||
}
|
||||
announce(successMessage);
|
||||
return snapshot;
|
||||
} catch (error) {
|
||||
if (!mountedRef.current) return void 0;
|
||||
announce(`\u64CD\u4F5C\u5931\u8D25\uFF1A${presentError(error).message}`);
|
||||
const failureMessage = operation === "reconnect" ? "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002" : `\u64CD\u4F5C\u5931\u8D25\uFF1A${presentError(error).message}`;
|
||||
if (operation === "reconnect") {
|
||||
setFeedbackByBot((current) => ({
|
||||
...current,
|
||||
[account.botId]: { message: failureMessage, clearWhenDisconnected: false }
|
||||
}));
|
||||
}
|
||||
announce(failureMessage);
|
||||
return void 0;
|
||||
} finally {
|
||||
const shouldRefresh = workspaceFence.endMutation();
|
||||
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true, restoreProvisioning: false });
|
||||
if (mountedRef.current) setBotBusy(account.botId, null);
|
||||
}
|
||||
}, [announce, invoke, loadStatus, setBotBusy, workspaceFence]);
|
||||
}, [announce, discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
|
||||
const reconnect = React7.useCallback((account) => runBotAction({
|
||||
account,
|
||||
operation: "reconnect",
|
||||
endpoint: DINGTALK_ENDPOINTS.reconnectBot,
|
||||
payload: { botId: account.botId },
|
||||
success: (snapshot) => snapshot?.bots.find((bot) => bot.botId === account.botId)?.connected ? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002" : "\u9489\u9489\u4ECD\u672A\u8FDE\u63A5\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002"
|
||||
payload: { botId: account.botId, sendTest: true },
|
||||
success: (snapshot) => {
|
||||
const refreshed = snapshot?.bots.find((bot) => bot.botId === account.botId);
|
||||
if (!refreshed?.connected) return "\u9489\u9489\u4ECD\u672A\u8FDE\u63A5\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002";
|
||||
return connectionTestFeedback(snapshot.testMessage) ?? "\u9489\u9489\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002";
|
||||
}
|
||||
}), [runBotAction]);
|
||||
const saveWorkspace = React7.useCallback(async (account, workspace) => {
|
||||
const workspaceVersion = workspaceFence.beginMutation();
|
||||
|
|
@ -2190,13 +2275,14 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|||
revision: snapshot.revision,
|
||||
error: null
|
||||
});
|
||||
discardStaleFeedback(snapshot);
|
||||
}
|
||||
} finally {
|
||||
const shouldRefresh = workspaceFence.endMutation();
|
||||
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
||||
if (mountedRef.current) setBotBusy(account.botId, null);
|
||||
}
|
||||
}, [invoke, loadStatus, setBotBusy, workspaceFence]);
|
||||
}, [discardStaleFeedback, invoke, loadStatus, setBotBusy, workspaceFence]);
|
||||
const remove = React7.useCallback(async (account) => {
|
||||
const snapshot = await runBotAction({
|
||||
account,
|
||||
|
|
@ -2287,6 +2373,7 @@ function DingtalkSettingsTab({ rpcCall }) {
|
|||
model.bots.length > 0 ? h2(AccountList, {
|
||||
bots: model.bots,
|
||||
busyByBot,
|
||||
feedbackByBot,
|
||||
removeTarget,
|
||||
onReconnect: (account) => void reconnect(account),
|
||||
onWorkspaceSave: saveWorkspace,
|
||||
|
|
@ -2411,6 +2498,13 @@ function checkedTime2(value) {
|
|||
return "\u521A\u521A";
|
||||
}
|
||||
}
|
||||
function connectionTestNotice(value) {
|
||||
if (value?.testMessage?.sent === true) return "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u5BF9\u5E94\u673A\u5668\u4EBA\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002";
|
||||
if (value?.testMessage?.code === "test-target-unavailable") {
|
||||
return "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u8BF7\u5148\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u9001 /status\uFF0C\u518D\u91CD\u65B0\u68C0\u67E5\u3002";
|
||||
}
|
||||
return value?.testMessage ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
|
||||
}
|
||||
function createTokenChannelSettings(definition) {
|
||||
const {
|
||||
channel: channel4,
|
||||
|
|
@ -2433,7 +2527,7 @@ function createTokenChannelSettings(definition) {
|
|||
credentialNoun = "Bot Token",
|
||||
emptyActionLabel = "\u586B\u5199 Bot Token"
|
||||
} = definition;
|
||||
function AccountCard5({ account, busy, removing, onReconnect, onWorkspaceSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
|
||||
function AccountCard5({ account, busy, testNotice, removing, onReconnect, onWorkspaceSave, onRequestRemove, onConfirmRemove, onCancelRemove }) {
|
||||
const state = busy === "reconnect" ? "connecting" : account.state;
|
||||
const tone = account.connected ? "success" : state === "error" ? "error" : "warning";
|
||||
const stateLabel = account.connected ? "\u8FD0\u884C\u6B63\u5E38" : state === "connecting" ? "\u6B63\u5728\u8FDE\u63A5" : "\u8FDE\u63A5\u672A\u5C31\u7EEA";
|
||||
|
|
@ -2495,6 +2589,7 @@ function createTokenChannelSettings(definition) {
|
|||
"div",
|
||||
{ className: "ddt-accountFooter dim-cardFooter" },
|
||||
summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
|
||||
testNotice ? h2("div", { className: "ddt-summary dim-cardSummary", role: "status" }, testNotice) : null,
|
||||
h2(
|
||||
"div",
|
||||
{ className: "ddt-actions dim-cardActions" },
|
||||
|
|
@ -2541,6 +2636,7 @@ function createTokenChannelSettings(definition) {
|
|||
const [credentialError, setCredentialError] = React8.useState(null);
|
||||
const [busy, setBusy] = React8.useState(false);
|
||||
const [busyByBot, setBusyByBot] = React8.useState({});
|
||||
const [testNoticeByBot, setTestNoticeByBot] = React8.useState({});
|
||||
const [removeTarget, setRemoveTarget] = React8.useState(null);
|
||||
const mounted = React8.useRef(true);
|
||||
const workspaceFence = useWorkspaceSnapshotFence();
|
||||
|
|
@ -2619,10 +2715,25 @@ function createTokenChannelSettings(definition) {
|
|||
const snapshotVersion = workspaceFence.beginMutation();
|
||||
setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
|
||||
try {
|
||||
const snapshot = api4.normalizeSnapshot(await invoke(endpoint, payload));
|
||||
const value = await invoke(endpoint, payload);
|
||||
const snapshot = api4.normalizeSnapshot(value);
|
||||
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
}
|
||||
if (mounted.current && operation === "reconnect") {
|
||||
setTestNoticeByBot((current) => ({
|
||||
...current,
|
||||
[account.botId]: connectionTestNotice(value)
|
||||
}));
|
||||
}
|
||||
} catch (error) {
|
||||
if (operation !== "reconnect") throw error;
|
||||
if (mounted.current) {
|
||||
setTestNoticeByBot((current) => ({
|
||||
...current,
|
||||
[account.botId]: "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"
|
||||
}));
|
||||
}
|
||||
} finally {
|
||||
const shouldRefresh = workspaceFence.endMutation();
|
||||
if (shouldRefresh && mounted.current) void loadStatus({ silent: true });
|
||||
|
|
@ -2644,12 +2755,13 @@ function createTokenChannelSettings(definition) {
|
|||
h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard5, {
|
||||
account,
|
||||
busy: busyByBot[account.botId],
|
||||
testNotice: testNoticeByBot[account.botId],
|
||||
removing: removeTarget === account.botId,
|
||||
onReconnect: () => void botAction(
|
||||
account,
|
||||
"reconnect",
|
||||
endpoints.reconnectBot,
|
||||
{ botId: account.botId }
|
||||
{ botId: account.botId, sendTest: true }
|
||||
),
|
||||
onWorkspaceSave: (workspace) => botAction(
|
||||
account,
|
||||
|
|
@ -3946,6 +4058,15 @@ function formatCheckedTime(timestamp7) {
|
|||
return "\u521A\u521A";
|
||||
}
|
||||
}
|
||||
function connectionTestNotice2(value) {
|
||||
if (value?.testMessage?.sent === true) {
|
||||
return "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u98DE\u4E66\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002";
|
||||
}
|
||||
if (value?.testMessage?.code === "test-target-unavailable") {
|
||||
return "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684\u6D4B\u8BD5\u6D88\u606F\u63A5\u6536\u4EBA\u3002";
|
||||
}
|
||||
return value?.testMessage ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
|
||||
}
|
||||
function RemoveConfirmation2({ bot, busy, onConfirm, onCancel }) {
|
||||
const cancelRef = React10.useRef(null);
|
||||
const idPart = bot.botId.replace(/[^a-zA-Z0-9_-]/g, "-");
|
||||
|
|
@ -3988,6 +4109,7 @@ function BotCard({
|
|||
connection,
|
||||
busy,
|
||||
actionError,
|
||||
testNotice,
|
||||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
|
|
@ -4068,6 +4190,10 @@ function BotCard({
|
|||
{ className: "bxf-healthSummary dim-cardSummary", "data-error": actionError || connection.error ? "true" : void 0 },
|
||||
summary
|
||||
) : null,
|
||||
testNotice ? h2("div", {
|
||||
className: "bxf-healthSummary dim-cardSummary",
|
||||
role: "status"
|
||||
}, testNotice) : null,
|
||||
h2(
|
||||
"div",
|
||||
{ className: "bxf-actions bxf-botActions dim-cardActions" },
|
||||
|
|
@ -4116,6 +4242,7 @@ function BotList(props) {
|
|||
connection: bot,
|
||||
busy: props.busyByBot[bot.botId],
|
||||
actionError: props.errorsByBot[bot.botId],
|
||||
testNotice: props.testNoticesByBot[bot.botId],
|
||||
removing: props.removeTargetId === bot.botId,
|
||||
onReconnect: () => props.onReconnect(bot),
|
||||
onWorkspaceSave: (workspace) => props.onWorkspaceSave(bot, workspace),
|
||||
|
|
@ -4195,6 +4322,7 @@ function FeishuSettingsTab({ rpcCall }) {
|
|||
const [credentialError, setCredentialError] = React10.useState(null);
|
||||
const [busyByBot, setBusyByBot] = React10.useState({});
|
||||
const [errorsByBot, setErrorsByBot] = React10.useState({});
|
||||
const [testNoticesByBot, setTestNoticesByBot] = React10.useState({});
|
||||
const [removeTargetId, setRemoveTargetId] = React10.useState(null);
|
||||
const [announcement, setAnnouncement] = React10.useState("");
|
||||
const [now, setNow] = React10.useState(() => Date.now());
|
||||
|
|
@ -4460,8 +4588,14 @@ function FeishuSettingsTab({ rpcCall }) {
|
|||
const snapshotVersion = workspaceFence.beginMutation();
|
||||
setBotBusy(botId, "reconnect");
|
||||
setBotError(botId, null);
|
||||
setTestNoticesByBot((current) => {
|
||||
const next = { ...current };
|
||||
delete next[botId];
|
||||
return next;
|
||||
});
|
||||
try {
|
||||
const snapshot = normalizeBotsSnapshot(await invoke(FEISHU_ENDPOINTS.reconnectBot, { botId }));
|
||||
const value = await invoke(FEISHU_ENDPOINTS.reconnectBot, { botId, sendTest: true });
|
||||
const snapshot = normalizeBotsSnapshot(value);
|
||||
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
mergeSnapshot(snapshot);
|
||||
}
|
||||
|
|
@ -4473,10 +4607,16 @@ function FeishuSettingsTab({ rpcCall }) {
|
|||
error.code = refreshed?.error?.code ?? "FEISHU_BOT_OFFLINE";
|
||||
throw error;
|
||||
}
|
||||
announce(connection.connected ? `${bot.name}\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002` : `${bot.name}\u5DF2\u91CD\u65B0\u8FDE\u63A5\u3002`);
|
||||
const testNotice = connectionTestNotice2(value);
|
||||
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setTestNoticesByBot((current) => ({ ...current, [botId]: testNotice }));
|
||||
}
|
||||
announce(testNotice ?? (connection.connected ? `${bot.name}\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002` : `${bot.name}\u5DF2\u91CD\u65B0\u8FDE\u63A5\u3002`));
|
||||
} catch (error) {
|
||||
setBotError(botId, error);
|
||||
announce(`${bot.name}\u64CD\u4F5C\u5931\u8D25\uFF0C\u8BF7\u67E5\u770B\u673A\u5668\u4EBA\u72B6\u6001\u3002`);
|
||||
const failure = new Error("\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002");
|
||||
failure.code = error?.code;
|
||||
setBotError(botId, failure);
|
||||
announce(failure.message);
|
||||
} finally {
|
||||
const shouldRefresh = workspaceFence.endMutation();
|
||||
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
||||
|
|
@ -4625,6 +4765,7 @@ function FeishuSettingsTab({ rpcCall }) {
|
|||
bots: model.bots,
|
||||
busyByBot,
|
||||
errorsByBot,
|
||||
testNoticesByBot,
|
||||
removeTargetId,
|
||||
onReconnect: (bot) => void reconnectOneBot(bot),
|
||||
onWorkspaceSave: saveWorkspace,
|
||||
|
|
@ -4652,6 +4793,7 @@ var QQ_ENDPOINTS = Object.freeze({
|
|||
});
|
||||
var PROVISION_STATES2 = /* @__PURE__ */ new Set(["starting", "pending", "refreshing", "connecting", "connected", "failed", "cancelled"]);
|
||||
var ACCOUNT_STATES3 = /* @__PURE__ */ new Set(["connected", "connecting", "offline", "error"]);
|
||||
var TEST_MESSAGE_CODES = /* @__PURE__ */ new Set(["test-target-unavailable", "test-message-failed"]);
|
||||
var QR_DATA_URL2 = /^data:image\/(?:png|webp);base64,[a-z\d+/]+={0,2}$/i;
|
||||
function isRecord4(value) {
|
||||
return value !== null && typeof value === "object" && !Array.isArray(value);
|
||||
|
|
@ -4725,17 +4867,35 @@ function normalizeBot3(value) {
|
|||
} : null
|
||||
};
|
||||
}
|
||||
function normalizeTestMessage2(value) {
|
||||
if (!isRecord4(value) || typeof value.sent !== "boolean") return void 0;
|
||||
if (value.sent) return { sent: true };
|
||||
const code = text2(value.code, "test-message-failed", 80);
|
||||
return {
|
||||
sent: false,
|
||||
code: TEST_MESSAGE_CODES.has(code) ? code : "test-message-failed"
|
||||
};
|
||||
}
|
||||
function normalizeSnapshot3(value) {
|
||||
const source = isRecord4(value?.snapshot) ? value.snapshot : value;
|
||||
if (!isRecord4(source) || !Array.isArray(source.bots)) throw new Error("QQ \u670D\u52A1\u6CA1\u6709\u8FD4\u56DE\u6709\u6548\u7684\u673A\u5668\u4EBA\u5217\u8868");
|
||||
const bots = source.bots.map(normalizeBot3).filter(Boolean);
|
||||
const testMessage = normalizeTestMessage2(source.testMessage);
|
||||
return {
|
||||
revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
|
||||
bots,
|
||||
totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
|
||||
provisioning: source.provisioning ? normalizeProvisioning3(source.provisioning) : null
|
||||
provisioning: source.provisioning ? normalizeProvisioning3(source.provisioning) : null,
|
||||
...testMessage ? { testMessage } : {}
|
||||
};
|
||||
}
|
||||
function connectionTestFeedback2(result) {
|
||||
if (result?.sent === true) return "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230\u5BF9\u5E94\u673A\u5668\u4EBA\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002";
|
||||
if (result?.code === "test-target-unavailable") {
|
||||
return "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u8BF7\u5148\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u9001 /status\uFF0C\u518D\u91CD\u65B0\u68C0\u67E5\u3002";
|
||||
}
|
||||
return result ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
|
||||
}
|
||||
function presentError4(error) {
|
||||
return {
|
||||
code: text2(error?.code, "QQ_ERROR", 80),
|
||||
|
|
@ -4983,6 +5143,7 @@ function RemoveConfirmation3({ account, busy, onConfirm, onCancel }) {
|
|||
function AccountCard2({
|
||||
account,
|
||||
busy,
|
||||
feedback,
|
||||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
|
|
@ -5035,6 +5196,11 @@ function AccountCard2({
|
|||
"div",
|
||||
{ className: "ddt-accountFooter dim-cardFooter" },
|
||||
summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
|
||||
feedback ? h2("div", {
|
||||
className: "ddt-summary dim-cardSummary",
|
||||
role: "status",
|
||||
"aria-live": "polite"
|
||||
}, feedback) : null,
|
||||
h2(
|
||||
"div",
|
||||
{ className: "ddt-actions dim-cardActions" },
|
||||
|
|
@ -5056,6 +5222,7 @@ function QqSettingsTab({ rpcCall }) {
|
|||
const [provision, setProvision] = React11.useState(null);
|
||||
const [busy, setBusy] = React11.useState(false);
|
||||
const [busyByBot, setBusyByBot] = React11.useState({});
|
||||
const [feedbackByBot, setFeedbackByBot] = React11.useState({});
|
||||
const [removeTarget, setRemoveTarget] = React11.useState(null);
|
||||
const [credentialOpen, setCredentialOpen] = React11.useState(false);
|
||||
const [credentialError, setCredentialError] = React11.useState(null);
|
||||
|
|
@ -5216,6 +5383,34 @@ function QqSettingsTab({ rpcCall }) {
|
|||
});
|
||||
}
|
||||
}, [invoke, loadStatus, workspaceFence]);
|
||||
const reconnect = React11.useCallback(async (account) => {
|
||||
setFeedbackByBot((current) => {
|
||||
const next = { ...current };
|
||||
delete next[account.botId];
|
||||
return next;
|
||||
});
|
||||
try {
|
||||
const snapshot = await botAction(
|
||||
account,
|
||||
"reconnect",
|
||||
QQ_ENDPOINTS.reconnectBot,
|
||||
{ botId: account.botId, sendTest: true }
|
||||
);
|
||||
if (mounted.current) {
|
||||
setFeedbackByBot((current) => ({
|
||||
...current,
|
||||
[account.botId]: connectionTestFeedback2(snapshot?.testMessage)
|
||||
}));
|
||||
}
|
||||
} catch {
|
||||
if (mounted.current) {
|
||||
setFeedbackByBot((current) => ({
|
||||
...current,
|
||||
[account.botId]: "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"
|
||||
}));
|
||||
}
|
||||
}
|
||||
}, [botAction]);
|
||||
let provisionView = null;
|
||||
if (provision?.status === "starting") provisionView = h2("div", { className: "ddt-card ddt-loading dim-surfaceCard" }, h2("div", { className: "ddt-spinner" }), "\u6B63\u5728\u7533\u8BF7 QQ \u4E8C\u7EF4\u7801\u2026");
|
||||
else if (["pending", "refreshing"].includes(provision?.status)) provisionView = h2(QrPanel2, {
|
||||
|
|
@ -5242,8 +5437,9 @@ function QqSettingsTab({ rpcCall }) {
|
|||
h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard2, {
|
||||
account,
|
||||
busy: busyByBot[account.botId],
|
||||
feedback: feedbackByBot[account.botId],
|
||||
removing: removeTarget === account.botId,
|
||||
onReconnect: () => void botAction(account, "reconnect", QQ_ENDPOINTS.reconnectBot, { botId: account.botId }),
|
||||
onReconnect: () => void reconnect(account),
|
||||
onWorkspaceSave: (workspace) => botAction(
|
||||
account,
|
||||
"workspace",
|
||||
|
|
@ -5597,6 +5793,13 @@ function timestamp4(value) {
|
|||
const parsed = typeof value === "string" ? Date.parse(value) : Number.NaN;
|
||||
return Number.isNaN(parsed) ? void 0 : parsed;
|
||||
}
|
||||
function normalizeTestMessage3(value) {
|
||||
if (!isRecord5(value)) return null;
|
||||
if (value.sent === true) return { sent: true };
|
||||
if (value.sent !== false) return null;
|
||||
const code = value.code === "test-target-unavailable" ? "test-target-unavailable" : "test-message-failed";
|
||||
return { sent: false, code };
|
||||
}
|
||||
function unwrapRpcResult7(result) {
|
||||
if (!isRecord5(result) || typeof result.ok !== "boolean") throw new Error("\u4F01\u4E1A\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
|
||||
if (!result.ok) {
|
||||
|
|
@ -5662,7 +5865,8 @@ function normalizeSnapshot6(value) {
|
|||
revision: Number.isSafeInteger(source.revision) ? source.revision : 0,
|
||||
bots,
|
||||
totals: { configured: bots.length, connected: bots.filter((bot) => bot.connected).length },
|
||||
provisioning: source.provisioning ? normalizeProvisioning4(source.provisioning) : null
|
||||
provisioning: source.provisioning ? normalizeProvisioning4(source.provisioning) : null,
|
||||
testMessage: normalizeTestMessage3(source.testMessage)
|
||||
};
|
||||
}
|
||||
function presentError7(error) {
|
||||
|
|
@ -5912,6 +6116,7 @@ function RemoveConfirmation4({ account, busy, onConfirm, onCancel }) {
|
|||
function AccountCard3({
|
||||
account,
|
||||
busy,
|
||||
feedback,
|
||||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
|
|
@ -5964,6 +6169,11 @@ function AccountCard3({
|
|||
"div",
|
||||
{ className: "ddt-accountFooter dim-cardFooter" },
|
||||
summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
|
||||
feedback ? h2("div", {
|
||||
className: "ddt-summary dim-cardSummary",
|
||||
role: "status",
|
||||
"aria-live": "polite"
|
||||
}, feedback) : null,
|
||||
h2(
|
||||
"div",
|
||||
{ className: "ddt-actions dim-cardActions" },
|
||||
|
|
@ -5985,19 +6195,40 @@ function WecomSettingsTab({ rpcCall }) {
|
|||
const [provision, setProvision] = React13.useState(null);
|
||||
const [busy, setBusy] = React13.useState(false);
|
||||
const [busyByBot, setBusyByBot] = React13.useState({});
|
||||
const [feedbackByBot, setFeedbackByBot] = React13.useState({});
|
||||
const [removeTarget, setRemoveTarget] = React13.useState(null);
|
||||
const [credentialOpen, setCredentialOpen] = React13.useState(false);
|
||||
const [credentialError, setCredentialError] = React13.useState(null);
|
||||
const [notice, setNotice] = React13.useState("");
|
||||
const [now, setNow] = React13.useState(Date.now());
|
||||
const mounted = React13.useRef(true);
|
||||
const workspaceFence = useWorkspaceSnapshotFence();
|
||||
const addButtonRef = React13.useRef(null);
|
||||
const noticeFrameRef = React13.useRef(null);
|
||||
const announce = React13.useCallback((message) => {
|
||||
if (!mounted.current) return;
|
||||
if (noticeFrameRef.current !== null) {
|
||||
window.cancelAnimationFrame(noticeFrameRef.current);
|
||||
noticeFrameRef.current = null;
|
||||
}
|
||||
setNotice("");
|
||||
if (message) {
|
||||
noticeFrameRef.current = window.requestAnimationFrame(() => {
|
||||
noticeFrameRef.current = null;
|
||||
if (mounted.current) setNotice(message);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
React13.useEffect(() => {
|
||||
const disposeDingtalk = installDingtalkStyles();
|
||||
const disposeWecom = installWecomStyles();
|
||||
mounted.current = true;
|
||||
return () => {
|
||||
mounted.current = false;
|
||||
if (noticeFrameRef.current !== null) {
|
||||
window.cancelAnimationFrame(noticeFrameRef.current);
|
||||
noticeFrameRef.current = null;
|
||||
}
|
||||
disposeWecom();
|
||||
disposeDingtalk();
|
||||
};
|
||||
|
|
@ -6145,6 +6376,45 @@ function WecomSettingsTab({ rpcCall }) {
|
|||
});
|
||||
}
|
||||
}, [invoke, loadStatus, workspaceFence]);
|
||||
const reconnect = React13.useCallback(async (account) => {
|
||||
setFeedbackByBot((current) => {
|
||||
const next = { ...current };
|
||||
delete next[account.botId];
|
||||
return next;
|
||||
});
|
||||
try {
|
||||
const snapshot = await botAction(
|
||||
account,
|
||||
"reconnect",
|
||||
WECOM_ENDPOINTS.reconnectBot,
|
||||
{ botId: account.botId, sendTest: true }
|
||||
);
|
||||
if (!snapshot) return;
|
||||
const refreshed = snapshot.bots.find((bot) => bot.botId === account.botId);
|
||||
let feedback;
|
||||
if (!refreshed?.connected) {
|
||||
feedback = "\u4F01\u4E1A\u5FAE\u4FE1\u4ECD\u672A\u8FDE\u63A5\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002";
|
||||
} else if (snapshot.testMessage?.sent) {
|
||||
feedback = "\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002";
|
||||
} else if (snapshot.testMessage?.code === "test-target-unavailable") {
|
||||
feedback = "\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u8BF7\u5148\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u9001 /status\uFF0C\u518D\u91CD\u65B0\u68C0\u67E5\u3002";
|
||||
} else if (snapshot.testMessage) {
|
||||
feedback = "\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002";
|
||||
} else {
|
||||
feedback = "\u4F01\u4E1A\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002";
|
||||
}
|
||||
if (mounted.current) {
|
||||
setFeedbackByBot((current) => ({ ...current, [account.botId]: feedback }));
|
||||
}
|
||||
announce(feedback);
|
||||
} catch {
|
||||
const feedback = "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
||||
if (mounted.current) {
|
||||
setFeedbackByBot((current) => ({ ...current, [account.botId]: feedback }));
|
||||
}
|
||||
announce(feedback);
|
||||
}
|
||||
}, [announce, botAction]);
|
||||
let provisionView = null;
|
||||
if (provision?.status === "starting") provisionView = h2("div", { className: "ddt-card ddt-loading dim-surfaceCard" }, h2("div", { className: "ddt-spinner" }), "\u6B63\u5728\u7533\u8BF7\u4F01\u4E1A\u5FAE\u4FE1\u4E8C\u7EF4\u7801\u2026");
|
||||
else if (["pending", "refreshing"].includes(provision?.status)) provisionView = h2(QrPanel3, {
|
||||
|
|
@ -6171,8 +6441,9 @@ function WecomSettingsTab({ rpcCall }) {
|
|||
h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(AccountCard3, {
|
||||
account,
|
||||
busy: busyByBot[account.botId],
|
||||
feedback: feedbackByBot[account.botId],
|
||||
removing: removeTarget === account.botId,
|
||||
onReconnect: () => void botAction(account, "reconnect", WECOM_ENDPOINTS.reconnectBot, { botId: account.botId }),
|
||||
onReconnect: () => void reconnect(account),
|
||||
onWorkspaceSave: (workspace) => botAction(
|
||||
account,
|
||||
"workspace",
|
||||
|
|
@ -6216,6 +6487,7 @@ function WecomSettingsTab({ rpcCall }) {
|
|||
credentialOpen,
|
||||
addButtonRef
|
||||
}),
|
||||
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(Button9, { onClick: () => void loadStatus() }, "\u91CD\u65B0\u8BFB\u53D6"))) : h2(
|
||||
React13.Fragment,
|
||||
null,
|
||||
|
|
@ -6263,6 +6535,13 @@ function string(value, fallback = "") {
|
|||
function timestamp5(value) {
|
||||
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
||||
}
|
||||
function normalizeTestMessage4(value) {
|
||||
if (!isRecord6(value)) return null;
|
||||
if (value.sent === true) return { sent: true };
|
||||
if (value.sent !== false) return null;
|
||||
const code = value.code === "test-target-unavailable" ? "test-target-unavailable" : "test-message-failed";
|
||||
return { sent: false, code };
|
||||
}
|
||||
function unwrapRpcResult8(result) {
|
||||
if (!isRecord6(result) || typeof result.ok !== "boolean") {
|
||||
throw new Error("\u5FAE\u4FE1\u670D\u52A1\u8FD4\u56DE\u4E86\u65E0\u6CD5\u8BC6\u522B\u7684\u54CD\u5E94");
|
||||
|
|
@ -6356,7 +6635,8 @@ function normalizeSnapshot7(value) {
|
|||
configured: bots.length,
|
||||
connected: bots.filter((bot) => bot.connected).length
|
||||
},
|
||||
provisioning: value.provisioning ? normalizeProvisioning5(value.provisioning) : null
|
||||
provisioning: value.provisioning ? normalizeProvisioning5(value.provisioning) : null,
|
||||
testMessage: normalizeTestMessage4(value.testMessage)
|
||||
};
|
||||
}
|
||||
function presentError8(error) {
|
||||
|
|
@ -6736,6 +7016,7 @@ function checkedTime5(timestamp7) {
|
|||
function AccountCard4({
|
||||
account,
|
||||
busy,
|
||||
feedback,
|
||||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
|
|
@ -6793,6 +7074,11 @@ function AccountCard4({
|
|||
"div",
|
||||
{ className: "dxw-accountFooter dim-cardFooter" },
|
||||
summary ? h2("div", { className: "dxw-summary dim-cardSummary" }, summary) : null,
|
||||
feedback ? h2("div", {
|
||||
className: "dxw-summary dim-cardSummary",
|
||||
role: "status",
|
||||
"aria-live": "polite"
|
||||
}, feedback) : null,
|
||||
h2(
|
||||
"div",
|
||||
{ className: "dxw-actions dim-cardActions" },
|
||||
|
|
@ -6834,6 +7120,7 @@ function AccountList2(props) {
|
|||
h2(AccountCard4, {
|
||||
account,
|
||||
busy: props.busyByBot[account.botId],
|
||||
feedback: props.feedbackByBot[account.botId],
|
||||
removing: props.removeTarget === account.botId,
|
||||
onReconnect: () => props.onReconnect(account),
|
||||
onWorkspaceSave: (workspace) => props.onWorkspaceSave(account, workspace),
|
||||
|
|
@ -6865,6 +7152,7 @@ function WeixinSettingsTab({ rpcCall }) {
|
|||
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 [notice, setNotice] = React14.useState("");
|
||||
const [now, setNow] = React14.useState(() => Date.now());
|
||||
|
|
@ -7063,15 +7351,42 @@ function WeixinSettingsTab({ rpcCall }) {
|
|||
const reconnect = React14.useCallback(async (account) => {
|
||||
const snapshotVersion = workspaceFence.beginMutation();
|
||||
setBotBusy(account.botId, "reconnect");
|
||||
setFeedbackByBot((current) => {
|
||||
const next = { ...current };
|
||||
delete next[account.botId];
|
||||
return next;
|
||||
});
|
||||
try {
|
||||
const snapshot = normalizeSnapshot7(await invoke(WEIXIN_ENDPOINTS.reconnectBot, { botId: account.botId }));
|
||||
const snapshot = normalizeSnapshot7(await invoke(
|
||||
WEIXIN_ENDPOINTS.reconnectBot,
|
||||
{ botId: account.botId, sendTest: true }
|
||||
));
|
||||
if (mountedRef.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel((current) => ({ ...current, bots: snapshot.bots, totals: snapshot.totals, revision: snapshot.revision }));
|
||||
}
|
||||
const refreshed = snapshot.bots.find((bot) => bot.botId === account.botId);
|
||||
announce(refreshed?.connected ? "\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002" : "\u5FAE\u4FE1\u4ECD\u672A\u8FDE\u63A5\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002");
|
||||
} catch (error) {
|
||||
announce(`\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF1A${presentError8(error).message}`);
|
||||
let feedback;
|
||||
if (!refreshed?.connected) {
|
||||
feedback = "\u5FAE\u4FE1\u4ECD\u672A\u8FDE\u63A5\uFF0C\u63D2\u4EF6\u4F1A\u7EE7\u7EED\u81EA\u52A8\u91CD\u8BD5\u3002";
|
||||
} else if (snapshot.testMessage?.sent) {
|
||||
feedback = "\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\u3002";
|
||||
} else if (snapshot.testMessage?.code === "test-target-unavailable") {
|
||||
feedback = "\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002\u8BF7\u5148\u79C1\u804A\u673A\u5668\u4EBA\u53D1\u9001 /status\uFF0C\u518D\u91CD\u65B0\u68C0\u67E5\u3002";
|
||||
} else if (snapshot.testMessage) {
|
||||
feedback = "\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002";
|
||||
} else {
|
||||
feedback = "\u5FAE\u4FE1\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\u3002";
|
||||
}
|
||||
if (mountedRef.current) {
|
||||
setFeedbackByBot((current) => ({ ...current, [account.botId]: feedback }));
|
||||
}
|
||||
announce(feedback);
|
||||
} catch {
|
||||
const feedback = "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002";
|
||||
if (mountedRef.current) {
|
||||
setFeedbackByBot((current) => ({ ...current, [account.botId]: feedback }));
|
||||
}
|
||||
announce(feedback);
|
||||
} finally {
|
||||
const shouldRefresh = workspaceFence.endMutation();
|
||||
if (shouldRefresh && mountedRef.current) void loadStatus({ silent: true });
|
||||
|
|
@ -7184,6 +7499,7 @@ function WeixinSettingsTab({ rpcCall }) {
|
|||
model.bots.length > 0 ? h2(AccountList2, {
|
||||
bots: model.bots,
|
||||
busyByBot,
|
||||
feedbackByBot,
|
||||
removeTarget,
|
||||
onReconnect: (account) => void reconnect(account),
|
||||
onWorkspaceSave: saveWorkspace,
|
||||
|
|
@ -7354,6 +7670,15 @@ function checkedTime6(value) {
|
|||
return "\u521A\u521A";
|
||||
}
|
||||
}
|
||||
function connectionTestNotice3(value) {
|
||||
if (value?.testMessage?.sent === true) {
|
||||
return "\u6D4B\u8BD5\u6D88\u606F\u5DF2\u53D1\u9001\uFF0C\u8BF7\u5230 WhatsApp \u81EA\u804A\u4F1A\u8BDD\u4E2D\u786E\u8BA4\u3002";
|
||||
}
|
||||
if (value?.testMessage?.code === "test-target-unavailable") {
|
||||
return "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u5F53\u524D\u6CA1\u6709\u53EF\u7528\u7684 WhatsApp \u81EA\u804A\u76EE\u6807\u3002";
|
||||
}
|
||||
return value?.testMessage ? "\u8FDE\u63A5\u68C0\u67E5\u5B8C\u6210\uFF0C\u4F46\u6D4B\u8BD5\u6D88\u606F\u53D1\u9001\u5931\u8D25\u3002" : null;
|
||||
}
|
||||
function Heading6({ totals, busy, onAdd, addButtonRef }) {
|
||||
return h2(
|
||||
"div",
|
||||
|
|
@ -7542,6 +7867,7 @@ function RemoveConfirmation5({ account, busy, onConfirm, onCancel }) {
|
|||
function WhatsappAccountCard({
|
||||
account,
|
||||
busy,
|
||||
testNotice,
|
||||
removing,
|
||||
onReconnect,
|
||||
onWorkspaceSave,
|
||||
|
|
@ -7608,6 +7934,10 @@ function WhatsappAccountCard({
|
|||
"div",
|
||||
{ className: "ddt-accountFooter dim-cardFooter" },
|
||||
summary ? h2("div", { className: "ddt-summary dim-cardSummary" }, summary) : null,
|
||||
testNotice ? h2("div", {
|
||||
className: "ddt-summary dim-cardSummary",
|
||||
role: "status"
|
||||
}, testNotice) : null,
|
||||
h2(
|
||||
"div",
|
||||
{ className: "ddt-actions dim-cardActions" },
|
||||
|
|
@ -7643,6 +7973,7 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|||
const [provision, setProvision] = React15.useState(null);
|
||||
const [busy, setBusy] = React15.useState(false);
|
||||
const [busyByBot, setBusyByBot] = React15.useState({});
|
||||
const [testNoticeByBot, setTestNoticeByBot] = React15.useState({});
|
||||
const [removeTarget, setRemoveTarget] = React15.useState(null);
|
||||
const [now, setNow] = React15.useState(Date.now());
|
||||
const mounted = React15.useRef(true);
|
||||
|
|
@ -7781,10 +8112,32 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|||
const botAction = React15.useCallback(async (account, operation, endpoint, payload) => {
|
||||
const snapshotVersion = workspaceFence.beginMutation();
|
||||
setBusyByBot((current) => ({ ...current, [account.botId]: operation }));
|
||||
if (operation === "reconnect") {
|
||||
setTestNoticeByBot((current) => {
|
||||
const next = { ...current };
|
||||
delete next[account.botId];
|
||||
return next;
|
||||
});
|
||||
}
|
||||
try {
|
||||
const snapshot = normalizeSnapshot8(await invoke(endpoint, payload));
|
||||
const value = await invoke(endpoint, payload);
|
||||
const snapshot = normalizeSnapshot8(value);
|
||||
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setModel({ phase: "ready", bots: snapshot.bots, totals: snapshot.totals, error: null });
|
||||
if (operation === "reconnect") {
|
||||
setTestNoticeByBot((current) => ({
|
||||
...current,
|
||||
[account.botId]: connectionTestNotice3(value)
|
||||
}));
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (operation !== "reconnect") throw error;
|
||||
if (mounted.current && workspaceFence.canCommitMutation(snapshotVersion)) {
|
||||
setTestNoticeByBot((current) => ({
|
||||
...current,
|
||||
[account.botId]: "\u8FDE\u63A5\u68C0\u67E5\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\u3002"
|
||||
}));
|
||||
}
|
||||
} finally {
|
||||
const shouldRefresh = workspaceFence.endMutation();
|
||||
|
|
@ -7807,12 +8160,13 @@ function WhatsappSettingsTab({ rpcCall }) {
|
|||
h2("ul", { className: "ddt-list dim-botList" }, model.bots.map((account) => h2("li", { key: account.botId }, h2(WhatsappAccountCard, {
|
||||
account,
|
||||
busy: busyByBot[account.botId],
|
||||
testNotice: testNoticeByBot[account.botId],
|
||||
removing: removeTarget === account.botId,
|
||||
onReconnect: () => void botAction(
|
||||
account,
|
||||
"reconnect",
|
||||
WHATSAPP_ENDPOINTS.reconnectBot,
|
||||
{ botId: account.botId }
|
||||
{ botId: account.botId, sendTest: true }
|
||||
),
|
||||
onWorkspaceSave: (workspace) => botAction(
|
||||
account,
|
||||
|
|
|
|||
255
lib/index.js
255
lib/index.js
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue