mirror of
https://github.com/wassname/pi-telegram.git
synced 2026-06-27 15:16:19 +08:00
bot token from env
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
- `Constraint-Driven Evolution`: Add structure when the bridge gains real operator or runtime constraints
|
||||
- `Single Source of Truth`: Keep durable rules in `AGENTS.md`, open work in `BACKLOG.md`, completed delivery in `CHANGELOG.md`, and deeper technical detail in `/docs`
|
||||
- `Boundary Clarity`: Separate Telegram transport concerns, pi integration concerns, rendering behavior, and release/documentation state
|
||||
- `Progressive Enhancement + Graceful Degradation`: Prefer behavior that upgrades automatically when richer runtime context exists, but always preserves a useful fallback path when it does not
|
||||
- `Runtime Safety`: Prefer queue and rendering behavior that fails predictably over clever behavior that can desynchronize the Telegram bridge from pi session state
|
||||
|
||||
## 1. Concept
|
||||
@@ -63,6 +64,7 @@
|
||||
|
||||
- Telegram API methods currently used include polling, message editing, draft streaming, callback queries, reactions, file download, and media upload endpoints
|
||||
- pi integration depends on lifecycle hooks such as `before_agent_start`, `agent_start`, `message_start`, `message_update`, and `agent_end`
|
||||
- `ctx.ui.input()` provides placeholder text rather than an editable prefilled value; when a real default must appear already filled in, prefer `ctx.ui.editor()`
|
||||
- Status/model/thinking controls are driven through Telegram inline keyboards and callback queries
|
||||
- Inbound files may become pi image inputs; outbound files must flow through `telegram_attach`
|
||||
|
||||
|
||||
@@ -9,3 +9,4 @@
|
||||
- `[Metadata]` Updated package repository metadata to point at the `llblab/pi-telegram` fork. Impact: published package links no longer send users to stale upstream coordinates.
|
||||
- `[Validation]` Added lightweight regression tests for Telegram Markdown rendering and queue/compaction dispatch guards. Impact: key renderer and queue invariants now have repeatable automated coverage.
|
||||
- `[Model Switching]` Enabled `/model` during an active Telegram-owned run by applying the new model and continuing on the new model automatically, delaying the abort until the current tool finishes when needed. Impact: Telegram can now approximate pi's manual stop-switch-continue workflow with fewer mid-tool aborts.
|
||||
- `[Setup]` `/telegram-setup` now shows the stored bot token first, otherwise prefills from common Telegram bot environment variables before falling back to the placeholder, using an actual prefilled editor when a real default exists. Impact: repeat setup respects local saved state while first-run and secret-managed setup stay fast.
|
||||
|
||||
@@ -61,6 +61,7 @@ Start pi, then run:
|
||||
```
|
||||
|
||||
Paste the bot token when prompted.
|
||||
If a bot token is already saved in `~/.pi/agent/telegram.json`, `/telegram-setup` shows that stored value by default. Otherwise it pre-fills from the first configured environment variable in `TELEGRAM_BOT_TOKEN`, `TELEGRAM_BOT_KEY`, `TELEGRAM_TOKEN`, or `TELEGRAM_KEY`.
|
||||
|
||||
The extension stores config in:
|
||||
|
||||
|
||||
@@ -247,6 +247,11 @@ interface TelegramInFlightModelSwitchState {
|
||||
hasAbortHandler: boolean;
|
||||
}
|
||||
|
||||
interface TelegramBotTokenPromptSpec {
|
||||
method: "input" | "editor";
|
||||
value: string;
|
||||
}
|
||||
|
||||
type TelegramReplyMarkup = {
|
||||
inline_keyboard: Array<Array<{ text: string; callback_data: string }>>;
|
||||
};
|
||||
@@ -269,6 +274,15 @@ const THINKING_LEVELS: readonly ThinkingLevel[] = [
|
||||
"high",
|
||||
"xhigh",
|
||||
];
|
||||
const TELEGRAM_BOT_TOKEN_INPUT_PLACEHOLDER = "123456:ABCDEF...";
|
||||
const TELEGRAM_BOT_TOKEN_ENV_VARS = [
|
||||
"TELEGRAM_BOT_TOKEN",
|
||||
"TELEGRAM_BOT_KEY",
|
||||
"TELEGRAM_TOKEN",
|
||||
"TELEGRAM_KEY",
|
||||
"BOT_TOKEN",
|
||||
"BOT_KEY",
|
||||
] as const;
|
||||
const SYSTEM_PROMPT_SUFFIX = `
|
||||
|
||||
Telegram bridge extension is active.
|
||||
@@ -1319,6 +1333,31 @@ function canDispatchTelegramTurnState(
|
||||
);
|
||||
}
|
||||
|
||||
function getTelegramBotTokenInputDefault(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
configToken?: string,
|
||||
): string {
|
||||
const trimmedConfigToken = configToken?.trim();
|
||||
if (trimmedConfigToken) return trimmedConfigToken;
|
||||
for (const key of TELEGRAM_BOT_TOKEN_ENV_VARS) {
|
||||
const value = env[key]?.trim();
|
||||
if (value) return value;
|
||||
}
|
||||
return TELEGRAM_BOT_TOKEN_INPUT_PLACEHOLDER;
|
||||
}
|
||||
|
||||
function getTelegramBotTokenPromptSpec(
|
||||
env: NodeJS.ProcessEnv = process.env,
|
||||
configToken?: string,
|
||||
): TelegramBotTokenPromptSpec {
|
||||
const value = getTelegramBotTokenInputDefault(env, configToken);
|
||||
return {
|
||||
method:
|
||||
value === TELEGRAM_BOT_TOKEN_INPUT_PLACEHOLDER ? "input" : "editor",
|
||||
value,
|
||||
};
|
||||
}
|
||||
|
||||
function canRestartTelegramTurnForModelSwitch(
|
||||
state: TelegramInFlightModelSwitchState,
|
||||
): boolean {
|
||||
@@ -1362,6 +1401,8 @@ export const __telegramTestUtils = {
|
||||
MAX_MESSAGE_LENGTH,
|
||||
renderTelegramMessage,
|
||||
canDispatchTelegramTurnState,
|
||||
getTelegramBotTokenInputDefault,
|
||||
getTelegramBotTokenPromptSpec,
|
||||
canRestartTelegramTurnForModelSwitch,
|
||||
buildTelegramModelSwitchContinuationText,
|
||||
};
|
||||
@@ -1915,10 +1956,16 @@ export default function (pi: ExtensionAPI) {
|
||||
if (!ctx.hasUI || setupInProgress) return;
|
||||
setupInProgress = true;
|
||||
try {
|
||||
const token = await ctx.ui.input(
|
||||
"Telegram bot token",
|
||||
"123456:ABCDEF...",
|
||||
const tokenPrompt = getTelegramBotTokenPromptSpec(
|
||||
process.env,
|
||||
config.botToken,
|
||||
);
|
||||
// Use the editor when a real default exists because ctx.ui.input only
|
||||
// exposes placeholder text, not an editable prefilled value.
|
||||
const token =
|
||||
tokenPrompt.method === "editor"
|
||||
? await ctx.ui.editor("Telegram bot token", tokenPrompt.value)
|
||||
: await ctx.ui.input("Telegram bot token", tokenPrompt.value);
|
||||
if (!token) return;
|
||||
|
||||
const nextConfig: TelegramConfig = { ...config, botToken: token.trim() };
|
||||
@@ -2440,7 +2487,11 @@ export default function (pi: ExtensionAPI) {
|
||||
);
|
||||
return true;
|
||||
}
|
||||
queueTelegramModelSwitchContinuation(activeTelegramTurn, selection, ctx);
|
||||
queueTelegramModelSwitchContinuation(
|
||||
activeTelegramTurn,
|
||||
selection,
|
||||
ctx,
|
||||
);
|
||||
currentAbort();
|
||||
await answerCallbackQuery(
|
||||
query.id,
|
||||
@@ -3495,7 +3546,10 @@ export default function (pi: ExtensionAPI) {
|
||||
|
||||
pi.on("tool_execution_end", async (_event, ctx) => {
|
||||
if (!activeTelegramTurn) return;
|
||||
activeTelegramToolExecutions = Math.max(0, activeTelegramToolExecutions - 1);
|
||||
activeTelegramToolExecutions = Math.max(
|
||||
0,
|
||||
activeTelegramToolExecutions - 1,
|
||||
);
|
||||
triggerPendingTelegramModelSwitchAbort(ctx);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { __telegramTestUtils } from "../index.ts";
|
||||
|
||||
test("Bot token input prefers stored config over env vars", () => {
|
||||
const value = __telegramTestUtils.getTelegramBotTokenInputDefault(
|
||||
{
|
||||
TELEGRAM_KEY: "key-last",
|
||||
TELEGRAM_TOKEN: "token-third",
|
||||
TELEGRAM_BOT_KEY: "key-second",
|
||||
TELEGRAM_BOT_TOKEN: "token-first",
|
||||
},
|
||||
"stored-token",
|
||||
);
|
||||
assert.equal(value, "stored-token");
|
||||
});
|
||||
|
||||
|
||||
test("Bot token input prefers the first configured Telegram env var when no config exists", () => {
|
||||
const value = __telegramTestUtils.getTelegramBotTokenInputDefault({
|
||||
TELEGRAM_KEY: "key-last",
|
||||
TELEGRAM_TOKEN: "token-third",
|
||||
TELEGRAM_BOT_KEY: "key-second",
|
||||
TELEGRAM_BOT_TOKEN: "token-first",
|
||||
});
|
||||
assert.equal(value, "token-first");
|
||||
});
|
||||
|
||||
test("Bot token prompt uses the editor when a real prefill exists", () => {
|
||||
const prompt = __telegramTestUtils.getTelegramBotTokenPromptSpec({
|
||||
TELEGRAM_BOT_TOKEN: "token-first",
|
||||
});
|
||||
assert.deepEqual(prompt, {
|
||||
method: "editor",
|
||||
value: "token-first",
|
||||
});
|
||||
});
|
||||
|
||||
test("Bot token prompt shows stored config before env values", () => {
|
||||
const prompt = __telegramTestUtils.getTelegramBotTokenPromptSpec(
|
||||
{
|
||||
TELEGRAM_BOT_TOKEN: "token-first",
|
||||
},
|
||||
"stored-token",
|
||||
);
|
||||
assert.deepEqual(prompt, {
|
||||
method: "editor",
|
||||
value: "stored-token",
|
||||
});
|
||||
});
|
||||
|
||||
test("Bot token input skips blank env vars and falls back to config", () => {
|
||||
const value = __telegramTestUtils.getTelegramBotTokenInputDefault(
|
||||
{
|
||||
TELEGRAM_BOT_TOKEN: " ",
|
||||
TELEGRAM_BOT_KEY: "",
|
||||
TELEGRAM_TOKEN: " ",
|
||||
},
|
||||
"stored-token",
|
||||
);
|
||||
assert.equal(value, "stored-token");
|
||||
});
|
||||
|
||||
test("Bot token input falls back to placeholder when no value exists", () => {
|
||||
const value = __telegramTestUtils.getTelegramBotTokenInputDefault({});
|
||||
assert.equal(value, "123456:ABCDEF...");
|
||||
});
|
||||
|
||||
test("Bot token prompt uses placeholder input when no prefill exists", () => {
|
||||
const prompt = __telegramTestUtils.getTelegramBotTokenPromptSpec({});
|
||||
assert.deepEqual(prompt, {
|
||||
method: "input",
|
||||
value: "123456:ABCDEF...",
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user