mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-05 13:50:22 +00:00
fix(telegram): clear offsets on token change
This commit is contained in:
@@ -45,6 +45,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
- Telegram: route non-abort slash commands on the normal chat/topic sequential lane while keeping true abort requests (`/stop`, `stop`) on the control lane, preventing command/reply race conditions from control-lane bypass. (#17899) Thanks @obviyus.
|
- Telegram: route non-abort slash commands on the normal chat/topic sequential lane while keeping true abort requests (`/stop`, `stop`) on the control lane, preventing command/reply race conditions from control-lane bypass. (#17899) Thanks @obviyus.
|
||||||
- Telegram: ignore `<media:...>` placeholder lines when extracting `MEDIA:` tool-result paths, preventing false local-file reads and dropped replies. (#18510) Thanks @yinghaosang.
|
- Telegram: ignore `<media:...>` placeholder lines when extracting `MEDIA:` tool-result paths, preventing false local-file reads and dropped replies. (#18510) Thanks @yinghaosang.
|
||||||
- Telegram: skip retries when inbound media `getFile` fails with Telegram's 20MB limit and continue processing message text, avoiding dropped messages for oversized attachments. (#18531) Thanks @brandonwise.
|
- Telegram: skip retries when inbound media `getFile` fails with Telegram's 20MB limit and continue processing message text, avoiding dropped messages for oversized attachments. (#18531) Thanks @brandonwise.
|
||||||
|
- Telegram: clear stored polling offsets when bot tokens change or accounts are deleted, preventing stale offsets after token rotations. (#18233)
|
||||||
- Auto-reply/TTS: keep tool-result media delivery enabled in group chats and native command sessions (while still suppressing tool summary text) so `NO_REPLY` follow-ups do not drop successful TTS audio. (#17991) Thanks @zerone0x.
|
- Auto-reply/TTS: keep tool-result media delivery enabled in group chats and native command sessions (while still suppressing tool summary text) so `NO_REPLY` follow-ups do not drop successful TTS audio. (#17991) Thanks @zerone0x.
|
||||||
- Agents/Tools: deliver tool-result media even when verbose tool output is off so media attachments are not dropped. (#16679)
|
- Agents/Tools: deliver tool-result media even when verbose tool output is off so media attachments are not dropped. (#16679)
|
||||||
- Discord: optimize reaction notification handling to skip unnecessary message fetches in `off`/`all`/`allowlist` modes, streamline reaction routing, and improve reaction emoji formatting. (#18248) Thanks @thewilloftheshadow and @victorGPT.
|
- Discord: optimize reaction notification handling to skip unnecessary message fetches in `off`/`all`/`allowlist` modes, streamline reaction routing, and improve reaction emoji formatting. (#18248) Thanks @thewilloftheshadow and @victorGPT.
|
||||||
|
|||||||
84
src/commands/channels.add.test.ts
Normal file
84
src/commands/channels.add.test.ts
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
import { setDefaultChannelPluginRegistryForTests } from "./channel-test-helpers.js";
|
||||||
|
import { baseConfigSnapshot, createTestRuntime } from "./test-runtime-config-helpers.js";
|
||||||
|
|
||||||
|
const configMocks = vi.hoisted(() => ({
|
||||||
|
readConfigFileSnapshot: vi.fn(),
|
||||||
|
writeConfigFile: vi.fn().mockResolvedValue(undefined),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const offsetMocks = vi.hoisted(() => ({
|
||||||
|
deleteTelegramUpdateOffset: vi.fn().mockResolvedValue(undefined),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("../config/config.js", async (importOriginal) => {
|
||||||
|
const actual = await importOriginal<typeof import("../config/config.js")>();
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
readConfigFileSnapshot: configMocks.readConfigFileSnapshot,
|
||||||
|
writeConfigFile: configMocks.writeConfigFile,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mock("../telegram/update-offset-store.js", async (importOriginal) => {
|
||||||
|
const actual = await importOriginal<typeof import("../telegram/update-offset-store.js")>();
|
||||||
|
return {
|
||||||
|
...actual,
|
||||||
|
deleteTelegramUpdateOffset: offsetMocks.deleteTelegramUpdateOffset,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
import { channelsAddCommand } from "./channels.js";
|
||||||
|
|
||||||
|
const runtime = createTestRuntime();
|
||||||
|
|
||||||
|
describe("channelsAddCommand", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
configMocks.readConfigFileSnapshot.mockReset();
|
||||||
|
configMocks.writeConfigFile.mockClear();
|
||||||
|
offsetMocks.deleteTelegramUpdateOffset.mockClear();
|
||||||
|
runtime.log.mockClear();
|
||||||
|
runtime.error.mockClear();
|
||||||
|
runtime.exit.mockClear();
|
||||||
|
setDefaultChannelPluginRegistryForTests();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("clears telegram update offsets when the token changes", async () => {
|
||||||
|
configMocks.readConfigFileSnapshot.mockResolvedValue({
|
||||||
|
...baseConfigSnapshot,
|
||||||
|
config: {
|
||||||
|
channels: {
|
||||||
|
telegram: { botToken: "old-token", enabled: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await channelsAddCommand(
|
||||||
|
{ channel: "telegram", account: "default", token: "new-token" },
|
||||||
|
runtime,
|
||||||
|
{ hasFlags: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(offsetMocks.deleteTelegramUpdateOffset).toHaveBeenCalledTimes(1);
|
||||||
|
expect(offsetMocks.deleteTelegramUpdateOffset).toHaveBeenCalledWith({ accountId: "default" });
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does not clear telegram update offsets when the token is unchanged", async () => {
|
||||||
|
configMocks.readConfigFileSnapshot.mockResolvedValue({
|
||||||
|
...baseConfigSnapshot,
|
||||||
|
config: {
|
||||||
|
channels: {
|
||||||
|
telegram: { botToken: "same-token", enabled: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await channelsAddCommand(
|
||||||
|
{ channel: "telegram", account: "default", token: "same-token" },
|
||||||
|
runtime,
|
||||||
|
{ hasFlags: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(offsetMocks.deleteTelegramUpdateOffset).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
|
import type { ChannelId, ChannelSetupInput } from "../../channels/plugins/types.js";
|
||||||
|
import type { ChannelChoice } from "../onboard-types.js";
|
||||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||||
import { listChannelPluginCatalogEntries } from "../../channels/plugins/catalog.js";
|
import { listChannelPluginCatalogEntries } from "../../channels/plugins/catalog.js";
|
||||||
import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
|
import { getChannelPlugin, normalizeChannelId } from "../../channels/plugins/index.js";
|
||||||
import type { ChannelId, ChannelSetupInput } from "../../channels/plugins/types.js";
|
|
||||||
import { writeConfigFile, type OpenClawConfig } from "../../config/config.js";
|
import { writeConfigFile, type OpenClawConfig } from "../../config/config.js";
|
||||||
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "../../routing/session-key.js";
|
||||||
import { defaultRuntime, type RuntimeEnv } from "../../runtime.js";
|
import { defaultRuntime, type RuntimeEnv } from "../../runtime.js";
|
||||||
|
import { resolveTelegramAccount } from "../../telegram/accounts.js";
|
||||||
|
import { deleteTelegramUpdateOffset } from "../../telegram/update-offset-store.js";
|
||||||
import { createClackPrompter } from "../../wizard/clack-prompter.js";
|
import { createClackPrompter } from "../../wizard/clack-prompter.js";
|
||||||
import { setupChannels } from "../onboard-channels.js";
|
import { setupChannels } from "../onboard-channels.js";
|
||||||
import type { ChannelChoice } from "../onboard-types.js";
|
|
||||||
import {
|
import {
|
||||||
ensureOnboardingPluginInstalled,
|
ensureOnboardingPluginInstalled,
|
||||||
reloadOnboardingPluginRegistry,
|
reloadOnboardingPluginRegistry,
|
||||||
@@ -209,6 +211,11 @@ export async function channelsAddCommand(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const previousTelegramToken =
|
||||||
|
channel === "telegram"
|
||||||
|
? resolveTelegramAccount({ cfg: nextConfig, accountId }).token.trim()
|
||||||
|
: "";
|
||||||
|
|
||||||
nextConfig = applyChannelAccountConfig({
|
nextConfig = applyChannelAccountConfig({
|
||||||
cfg: nextConfig,
|
cfg: nextConfig,
|
||||||
channel,
|
channel,
|
||||||
@@ -216,6 +223,14 @@ export async function channelsAddCommand(
|
|||||||
input,
|
input,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (channel === "telegram") {
|
||||||
|
const nextTelegramToken = resolveTelegramAccount({ cfg: nextConfig, accountId }).token.trim();
|
||||||
|
if (previousTelegramToken !== nextTelegramToken) {
|
||||||
|
// Clear stale polling offsets after Telegram token rotation.
|
||||||
|
await deleteTelegramUpdateOffset({ accountId });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
await writeConfigFile(nextConfig);
|
await writeConfigFile(nextConfig);
|
||||||
runtime.log(`Added ${channelLabel(channel)} account "${accountId}".`);
|
runtime.log(`Added ${channelLabel(channel)} account "${accountId}".`);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user