
























@@ -188,11 +188,15 @@ type StartChannelOptions = {
188188preserveManualStop?: boolean;
189189};
190190191+type StopChannelOptions = {
192+manual?: boolean;
193+};
194+191195export type ChannelManager = {
192196getRuntimeSnapshot: () => ChannelRuntimeSnapshot;
193197startChannels: () => Promise<void>;
194198startChannel: (channel: ChannelId, accountId?: string) => Promise<void>;
195-stopChannel: (channel: ChannelId, accountId?: string) => Promise<void>;
199+stopChannel: (channel: ChannelId, accountId?: string, opts?: StopChannelOptions) => Promise<void>;
196200markChannelLoggedOut: (channelId: ChannelId, cleared: boolean, accountId?: string) => void;
197201isManuallyStopped: (channelId: ChannelId, accountId: string) => boolean;
198202resetRestartAttempts: (channelId: ChannelId, accountId: string) => void;
@@ -216,6 +220,7 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
216220const restartAttempts = new Map<string, number>();
217221// Tracks accounts that were manually stopped so we don't auto-restart them.
218222const manuallyStopped = new Set<string>();
223+const recoveryStopTimedOut = new Set<string>();
219224220225const restartKey = (channelId: ChannelId, accountId: string) => `${channelId}:${accountId}`;
221226const ensureChannelLog = (channelId: ChannelId): SubsystemLogger => {
@@ -568,15 +573,24 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
568573restartPending: true,
569574reconnectAttempts: attempt,
570575});
576+const recoveryRestartSleepAbort = recoveryStopTimedOut.has(rKey)
577+ ? new AbortController()
578+ : undefined;
579+if (recoveryRestartSleepAbort) {
580+store.aborts.set(id, recoveryRestartSleepAbort);
581+}
571582try {
572-await sleepWithAbort(delayMs, abort.signal);
583+const restartSleepAbort = recoveryRestartSleepAbort?.signal ?? abort.signal;
584+await sleepWithAbort(delayMs, restartSleepAbort);
573585if (manuallyStopped.has(rKey)) {
586+recoveryStopTimedOut.delete(rKey);
574587return;
575588}
589+recoveryStopTimedOut.delete(rKey);
576590if (store.tasks.get(id) === trackedPromise) {
577591store.tasks.delete(id);
578592}
579-if (store.aborts.get(id) === abort) {
593+if (store.aborts.get(id) === (recoveryRestartSleepAbort ?? abort)) {
580594store.aborts.delete(id);
581595}
582596await startChannelInternal(channelId, id, {
@@ -585,6 +599,13 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
585599});
586600} catch {
587601// abort or startup failure — next crash will retry
602+} finally {
603+if (recoveryRestartSleepAbort) {
604+recoveryStopTimedOut.delete(rKey);
605+if (store.aborts.get(id) === recoveryRestartSleepAbort) {
606+store.aborts.delete(id);
607+}
608+}
588609}
589610})
590611.finally(() => {
@@ -630,7 +651,12 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
630651await startChannelInternal(channelId, accountId);
631652};
632653633-const stopChannel = async (channelId: ChannelId, accountId?: string) => {
654+const stopChannel = async (
655+channelId: ChannelId,
656+accountId?: string,
657+opts: StopChannelOptions = {},
658+) => {
659+const manual = opts.manual ?? true;
634660const plugin = getChannelPlugin(channelId);
635661const store = getStore(channelId);
636662// Fast path: nothing running and no explicit plugin shutdown hook to run.
@@ -656,7 +682,10 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
656682if (!abort && !task && !plugin?.gateway?.stopAccount) {
657683return;
658684}
659-manuallyStopped.add(restartKey(channelId, id));
685+const rKey = restartKey(channelId, id);
686+if (manual) {
687+manuallyStopped.add(rKey);
688+}
660689abort?.abort();
661690const log = ensureChannelLog(channelId);
662691const runtime = ensureChannelRuntime(channelId);
@@ -683,12 +712,16 @@ export function createChannelManager(opts: ChannelManagerOptions): ChannelManage
683712);
684713setRuntime(channelId, id, {
685714accountId: id,
686-running: true,
687-restartPending: false,
715+running: manual,
716+restartPending: !manual,
688717lastError: `channel stop timed out after ${CHANNEL_STOP_ABORT_TIMEOUT_MS}ms`,
689718});
719+if (!manual) {
720+recoveryStopTimedOut.add(rKey);
721+}
690722return;
691723}
724+recoveryStopTimedOut.delete(rKey);
692725store.aborts.delete(id);
693726store.tasks.delete(id);
694727setRuntime(channelId, id, {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。