responses-proxy 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -0
- package/cli.js +118 -0
- package/dist/anthropic-messages.js +383 -0
- package/dist/anthropic-messages.test.js +209 -0
- package/dist/audit-log.js +138 -0
- package/dist/audit-log.test.js +480 -0
- package/dist/billing-expiration.js +70 -0
- package/dist/billing-expiration.test.js +114 -0
- package/dist/billing.js +716 -0
- package/dist/billing.test.js +228 -0
- package/dist/chatgpt-oauth-store.js +240 -0
- package/dist/chatgpt-oauth-store.test.js +88 -0
- package/dist/chatgpt-oauth.js +118 -0
- package/dist/chatgpt-oauth.test.js +63 -0
- package/dist/chatgpt-provider-auth.js +60 -0
- package/dist/chatgpt-provider-auth.test.js +101 -0
- package/dist/client/app-icon.svg +17 -0
- package/dist/client/assets/index-C7Vvhst8.js +14 -0
- package/dist/client/assets/index-DpqgYK3L.css +1 -0
- package/dist/client/favicon.svg +17 -0
- package/dist/client/index.html +31 -0
- package/dist/client-config-apply.js +345 -0
- package/dist/client-config-apply.test.js +185 -0
- package/dist/client-token-limits.js +111 -0
- package/dist/client-token-limits.test.js +129 -0
- package/dist/codex-config.js +47 -0
- package/dist/codex-setup.js +87 -0
- package/dist/codex-setup.test.js +30 -0
- package/dist/config.js +314 -0
- package/dist/cost-analytics.js +31 -0
- package/dist/cost-analytics.test.js +38 -0
- package/dist/customer-key-access.js +126 -0
- package/dist/customer-key-access.test.js +178 -0
- package/dist/customer-keys.js +209 -0
- package/dist/customer-keys.test.js +68 -0
- package/dist/customer-usage.js +18 -0
- package/dist/customer-usage.test.js +55 -0
- package/dist/dashboard-auth.js +318 -0
- package/dist/dashboard-auth.test.js +133 -0
- package/dist/dashboard-serving.test.js +235 -0
- package/dist/error-response.js +174 -0
- package/dist/error-response.test.js +88 -0
- package/dist/forward.js +357 -0
- package/dist/health-websocket-manager.js +174 -0
- package/dist/http-rate-limit.js +36 -0
- package/dist/http-rate-limit.test.js +62 -0
- package/dist/kiro-auth.js +136 -0
- package/dist/kiro-auth.test.js +234 -0
- package/dist/kiro-codewhisperer.js +646 -0
- package/dist/kiro-codewhisperer.test.js +219 -0
- package/dist/kiro-device-login.js +338 -0
- package/dist/kiro-eventstream.js +219 -0
- package/dist/kiro-eventstream.test.js +79 -0
- package/dist/kiro-forward.js +401 -0
- package/dist/kiro-import-cli.js +69 -0
- package/dist/kiro-import.js +94 -0
- package/dist/kiro-import.test.js +125 -0
- package/dist/kiro-token-store.js +196 -0
- package/dist/kiro-token-store.test.js +207 -0
- package/dist/krouter-usage.js +243 -0
- package/dist/model-combo-repository.js +147 -0
- package/dist/model-routing.js +69 -0
- package/dist/model-routing.test.js +41 -0
- package/dist/normalize-request.js +531 -0
- package/dist/normalize-request.test.js +277 -0
- package/dist/omv-public-firewall.test.js +11 -0
- package/dist/package.json +17 -0
- package/dist/prompt-cache-state.js +146 -0
- package/dist/prompt-cache-state.test.js +71 -0
- package/dist/prompt-cache.js +229 -0
- package/dist/provider-health-service.js +404 -0
- package/dist/provider-request-parameters.js +107 -0
- package/dist/provider-request-parameters.test.js +26 -0
- package/dist/provider-routing.js +114 -0
- package/dist/provider-routing.test.js +64 -0
- package/dist/provider-usage.js +314 -0
- package/dist/request-timeout-policy.js +61 -0
- package/dist/request-timeout-policy.test.js +40 -0
- package/dist/response-cache.js +69 -0
- package/dist/response-cache.test.js +28 -0
- package/dist/routing-combo-repository.js +300 -0
- package/dist/routing-engine.js +377 -0
- package/dist/routing-integration.js +155 -0
- package/dist/routing-simulation-engine.js +326 -0
- package/dist/rtk-layer.js +483 -0
- package/dist/rtk-layer.test.js +198 -0
- package/dist/runtime-provider-repository.js +1742 -0
- package/dist/runtime-provider-repository.test.js +1177 -0
- package/dist/schema.js +118 -0
- package/dist/schema.test.js +16 -0
- package/dist/sepay-webhook.js +87 -0
- package/dist/sepay-webhook.test.js +142 -0
- package/dist/server-body-limit.test.js +35 -0
- package/dist/server-client-token-limits.test.js +161 -0
- package/dist/server-codex-config-setup.test.js +76 -0
- package/dist/server-http-rate-limit.test.js +80 -0
- package/dist/server-response-cache.test.js +105 -0
- package/dist/server-routes-alias.test.js +39 -0
- package/dist/server-sepay-webhook-security.test.js +59 -0
- package/dist/server.js +5906 -0
- package/dist/session-log.js +178 -0
- package/dist/tailnet-funnel-script.test.js +33 -0
- package/dist/telegram-bot/actions.js +118 -0
- package/dist/telegram-bot/admin-actions.js +103 -0
- package/dist/telegram-bot/auth.js +46 -0
- package/dist/telegram-bot/auth.test.js +1 -0
- package/dist/telegram-bot/bot-identity-repository.js +189 -0
- package/dist/telegram-bot/bot-identity-repository.test.js +78 -0
- package/dist/telegram-bot/callbacks.js +30 -0
- package/dist/telegram-bot/codex-config-delivery.js +38 -0
- package/dist/telegram-bot/codex-config-delivery.test.js +75 -0
- package/dist/telegram-bot/commands/accounts.js +140 -0
- package/dist/telegram-bot/commands/apikey.js +737 -0
- package/dist/telegram-bot/commands/apply.js +265 -0
- package/dist/telegram-bot/commands/clients.js +13 -0
- package/dist/telegram-bot/commands/customer-billing.test.js +271 -0
- package/dist/telegram-bot/commands/grant.js +138 -0
- package/dist/telegram-bot/commands/grant.test.js +217 -0
- package/dist/telegram-bot/commands/help.js +52 -0
- package/dist/telegram-bot/commands/me.js +53 -0
- package/dist/telegram-bot/commands/models.js +6 -0
- package/dist/telegram-bot/commands/oauth.js +64 -0
- package/dist/telegram-bot/commands/plans.js +96 -0
- package/dist/telegram-bot/commands/providers.js +27 -0
- package/dist/telegram-bot/commands/quota.js +10 -0
- package/dist/telegram-bot/commands/renew-user.js +139 -0
- package/dist/telegram-bot/commands/renew-user.test.js +184 -0
- package/dist/telegram-bot/commands/renew.js +1369 -0
- package/dist/telegram-bot/commands/renew.test.js +1633 -0
- package/dist/telegram-bot/commands/start.js +212 -0
- package/dist/telegram-bot/commands/start.test.js +280 -0
- package/dist/telegram-bot/commands/status.js +6 -0
- package/dist/telegram-bot/commands/tailscale.js +15 -0
- package/dist/telegram-bot/commands/tailscale.test.js +76 -0
- package/dist/telegram-bot/commands/test.js +51 -0
- package/dist/telegram-bot/commands/test.test.js +14 -0
- package/dist/telegram-bot/commands/usage.js +10 -0
- package/dist/telegram-bot/config.js +98 -0
- package/dist/telegram-bot/config.test.js +42 -0
- package/dist/telegram-bot/customer-actions.js +160 -0
- package/dist/telegram-bot/customer-api-keys.js +68 -0
- package/dist/telegram-bot/customer-billing.js +72 -0
- package/dist/telegram-bot/customer-workspace-repository.js +134 -0
- package/dist/telegram-bot/customer-workspace-repository.test.js +47 -0
- package/dist/telegram-bot/dashboard-login.js +39 -0
- package/dist/telegram-bot/format.js +140 -0
- package/dist/telegram-bot/grants.js +370 -0
- package/dist/telegram-bot/grants.test.js +290 -0
- package/dist/telegram-bot/index.js +85 -0
- package/dist/telegram-bot/message-cleanup.js +55 -0
- package/dist/telegram-bot/message-cleanup.test.js +77 -0
- package/dist/telegram-bot/message-format.js +45 -0
- package/dist/telegram-bot/message-format.test.js +10 -0
- package/dist/telegram-bot/proxy-client.js +174 -0
- package/dist/telegram-bot/rate-limit.js +95 -0
- package/dist/telegram-bot/rate-limit.test.js +58 -0
- package/dist/telegram-bot/sessions.js +171 -0
- package/dist/telegram-bot/sessions.test.js +107 -0
- package/dist/telegram-bot/telegram-adapter.js +126 -0
- package/dist/telegram-bot/worker.js +63 -0
- package/package.json +39 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import test from "node:test";
|
|
6
|
+
import BetterSqlite3 from "better-sqlite3";
|
|
7
|
+
import { BotIdentityRepository } from "./bot-identity-repository.js";
|
|
8
|
+
function withRepository(fn) {
|
|
9
|
+
const dir = mkdtempSync(path.join(os.tmpdir(), "telegram-identity-"));
|
|
10
|
+
try {
|
|
11
|
+
const dbFile = path.join(dir, "bot.sqlite");
|
|
12
|
+
fn(BotIdentityRepository.create(dbFile), dbFile);
|
|
13
|
+
}
|
|
14
|
+
finally {
|
|
15
|
+
rmSync(dir, { recursive: true, force: true });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
test("BotIdentityRepository creates and updates Telegram users", () => {
|
|
19
|
+
withRepository((repo) => {
|
|
20
|
+
const first = repo.upsertUser({
|
|
21
|
+
telegramUserId: "1283361952",
|
|
22
|
+
username: "atger",
|
|
23
|
+
firstName: "Atger",
|
|
24
|
+
languageCode: "en",
|
|
25
|
+
now: new Date("2026-04-27T00:00:00.000Z"),
|
|
26
|
+
});
|
|
27
|
+
assert.equal(first.role, "customer");
|
|
28
|
+
assert.equal(first.status, "active");
|
|
29
|
+
assert.equal(first.username, "atger");
|
|
30
|
+
assert.equal(first.createdAt, "2026-04-27T00:00:00.000Z");
|
|
31
|
+
repo.setUserStatus("1283361952", "blocked", new Date("2026-04-27T00:05:00.000Z"));
|
|
32
|
+
const updated = repo.upsertUser({
|
|
33
|
+
telegramUserId: "1283361952",
|
|
34
|
+
username: "new_name",
|
|
35
|
+
firstName: "New",
|
|
36
|
+
languageCode: "vi",
|
|
37
|
+
now: new Date("2026-04-27T00:10:00.000Z"),
|
|
38
|
+
});
|
|
39
|
+
assert.equal(updated.username, "new_name");
|
|
40
|
+
assert.equal(updated.firstName, "New");
|
|
41
|
+
assert.equal(updated.languageCode, "vi");
|
|
42
|
+
assert.equal(updated.status, "blocked");
|
|
43
|
+
assert.equal(updated.createdAt, "2026-04-27T00:00:00.000Z");
|
|
44
|
+
assert.equal(updated.lastSeenAt, "2026-04-27T00:10:00.000Z");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
test("BotIdentityRepository creates chats and memberships", () => {
|
|
48
|
+
withRepository((repo, dbFile) => {
|
|
49
|
+
repo.upsertUser({ telegramUserId: "1" });
|
|
50
|
+
const chat = repo.upsertChat({
|
|
51
|
+
telegramChatId: "-100",
|
|
52
|
+
chatType: "supergroup",
|
|
53
|
+
title: "Customers",
|
|
54
|
+
now: new Date("2026-04-27T01:00:00.000Z"),
|
|
55
|
+
});
|
|
56
|
+
repo.upsertMembership({
|
|
57
|
+
telegramUserId: "1",
|
|
58
|
+
telegramChatId: "-100",
|
|
59
|
+
role: "member",
|
|
60
|
+
now: new Date("2026-04-27T01:00:01.000Z"),
|
|
61
|
+
});
|
|
62
|
+
assert.equal(chat.telegramChatId, "-100");
|
|
63
|
+
assert.equal(chat.chatType, "supergroup");
|
|
64
|
+
assert.equal(chat.title, "Customers");
|
|
65
|
+
const db = new BetterSqlite3(dbFile);
|
|
66
|
+
const row = db
|
|
67
|
+
.prepare(`SELECT telegram_user_id, telegram_chat_id, role
|
|
68
|
+
FROM telegram_chat_memberships
|
|
69
|
+
WHERE telegram_user_id = ? AND telegram_chat_id = ?`)
|
|
70
|
+
.get("1", "-100");
|
|
71
|
+
db.close();
|
|
72
|
+
assert.deepEqual(row, {
|
|
73
|
+
telegram_user_id: "1",
|
|
74
|
+
telegram_chat_id: "-100",
|
|
75
|
+
role: "member",
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export async function answerCallbackQuerySafely(ctx, payload) {
|
|
2
|
+
try {
|
|
3
|
+
await ctx.answerCallbackQuery(payload);
|
|
4
|
+
}
|
|
5
|
+
catch {
|
|
6
|
+
// Telegram callback queries can expire before the bot answers; the action itself can still continue.
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export async function replyOrEditMessage(ctx, text, options) {
|
|
10
|
+
const callbackMessage = ctx.callbackQuery && "message" in ctx.callbackQuery ? ctx.callbackQuery.message : undefined;
|
|
11
|
+
if (callbackMessage) {
|
|
12
|
+
try {
|
|
13
|
+
await ctx.editMessageText(text, options);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
18
|
+
const normalized = message.toLowerCase();
|
|
19
|
+
if (normalized.includes("message is not modified")) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (normalized.includes("message to edit not found")
|
|
23
|
+
|| normalized.includes("message can't be edited")
|
|
24
|
+
|| normalized.includes("message is too old")) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
await ctx.reply(text, options);
|
|
30
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { InputFile } from "grammy";
|
|
2
|
+
import { buildCodexConfigFiles, buildCodexSetupCurlCommand, } from "../codex-setup.js";
|
|
3
|
+
export { buildCodexConfigFiles } from "../codex-setup.js";
|
|
4
|
+
export async function sendCodexConfigFiles(ctx, telegramUserId, files) {
|
|
5
|
+
const chatId = Number(telegramUserId);
|
|
6
|
+
await ctx.api.sendDocument(chatId, new InputFile(Buffer.from(files.configToml, "utf8"), "config.toml"), { caption: "Codex config.toml" });
|
|
7
|
+
await ctx.api.sendDocument(chatId, new InputFile(Buffer.from(files.authJson, "utf8"), "auth.json"), { caption: "Codex auth.json" });
|
|
8
|
+
}
|
|
9
|
+
export async function sendCustomerCodexSetup(ctx, input) {
|
|
10
|
+
try {
|
|
11
|
+
await ctx.api.sendMessage(Number(input.telegramUserId), [
|
|
12
|
+
input.title,
|
|
13
|
+
...input.details,
|
|
14
|
+
"",
|
|
15
|
+
"Run this on machine that should hold Codex config:",
|
|
16
|
+
buildCodexSetupCurlCommand({
|
|
17
|
+
publicResponsesBaseUrl: input.baseUrl,
|
|
18
|
+
apiKey: input.apiKey,
|
|
19
|
+
}),
|
|
20
|
+
"",
|
|
21
|
+
"It patches ~/.codex/config.toml and ~/.codex/auth.json in place.",
|
|
22
|
+
"If you want manual paste instead, files are attached below.",
|
|
23
|
+
"Paste both files into your Codex config folder:",
|
|
24
|
+
"Mac: ~/.codex/",
|
|
25
|
+
"Windows: %USERPROFILE%\\.codex\\",
|
|
26
|
+
"If that folder does not exist, create it first.",
|
|
27
|
+
].join("\n"));
|
|
28
|
+
await sendCodexConfigFiles(ctx, input.telegramUserId, buildCodexConfigFiles({
|
|
29
|
+
baseUrl: input.baseUrl,
|
|
30
|
+
apiKey: input.apiKey,
|
|
31
|
+
model: input.model,
|
|
32
|
+
}));
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { buildCodexConfigFiles, sendCustomerCodexSetup } from "./codex-config-delivery.js";
|
|
4
|
+
test("buildCodexConfigFiles creates plug-and-play Codex config files", () => {
|
|
5
|
+
const files = buildCodexConfigFiles({
|
|
6
|
+
baseUrl: "https://proxy.example.com/v1",
|
|
7
|
+
apiKey: "sk-customer-secret",
|
|
8
|
+
model: "gpt-5.5",
|
|
9
|
+
});
|
|
10
|
+
assert.match(files.configToml, /model = "gpt-5\.5"/);
|
|
11
|
+
assert.match(files.configToml, /model_provider = "resproxy"/);
|
|
12
|
+
assert.match(files.configToml, /base_url = "https:\/\/proxy\.example\.com\/v1"/);
|
|
13
|
+
assert.match(files.configToml, /api_key = "sk-customer-secret"/);
|
|
14
|
+
assert.match(files.configToml, /wire_api = "responses"/);
|
|
15
|
+
assert.deepEqual(JSON.parse(files.authJson), {
|
|
16
|
+
auth_mode: "apikey",
|
|
17
|
+
OPENAI_API_KEY: "sk-customer-secret",
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
test("sendCustomerCodexSetup includes Mac and Windows paste locations", async () => {
|
|
21
|
+
const sentMessages = [];
|
|
22
|
+
const sentDocuments = [];
|
|
23
|
+
const ctx = {
|
|
24
|
+
api: {
|
|
25
|
+
async sendMessage(_chatId, text) {
|
|
26
|
+
sentMessages.push(text);
|
|
27
|
+
return {};
|
|
28
|
+
},
|
|
29
|
+
async sendDocument(_chatId, document) {
|
|
30
|
+
sentDocuments.push({
|
|
31
|
+
filename: document.filename,
|
|
32
|
+
content: document.fileData ? Buffer.from(document.fileData).toString("utf8") : "",
|
|
33
|
+
});
|
|
34
|
+
return {};
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
const ok = await sendCustomerCodexSetup(ctx, {
|
|
39
|
+
telegramUserId: "42",
|
|
40
|
+
baseUrl: "https://proxy.example.com/v1",
|
|
41
|
+
apiKey: "sk-customer-secret",
|
|
42
|
+
title: "Your access is active",
|
|
43
|
+
details: ["• Plan ID: basic"],
|
|
44
|
+
});
|
|
45
|
+
assert.equal(ok, true);
|
|
46
|
+
assert.equal(sentMessages[0]?.includes("Mac: ~/.codex/"), true);
|
|
47
|
+
assert.equal(sentMessages[0]?.includes("Windows: %USERPROFILE%\\.codex\\"), true);
|
|
48
|
+
assert.deepEqual(sentDocuments.map((document) => document.filename), ["config.toml", "auth.json"]);
|
|
49
|
+
assert.match(sentDocuments[0]?.content ?? "", /base_url = "https:\/\/proxy\.example\.com\/v1"/);
|
|
50
|
+
});
|
|
51
|
+
test("sendCustomerCodexSetup includes curl patch command for customer machine", async () => {
|
|
52
|
+
const sentMessages = [];
|
|
53
|
+
const ctx = {
|
|
54
|
+
api: {
|
|
55
|
+
async sendMessage(_chatId, text) {
|
|
56
|
+
sentMessages.push(text);
|
|
57
|
+
return {};
|
|
58
|
+
},
|
|
59
|
+
async sendDocument() {
|
|
60
|
+
return {};
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
const ok = await sendCustomerCodexSetup(ctx, {
|
|
65
|
+
telegramUserId: "42",
|
|
66
|
+
baseUrl: "https://proxy.example.com/v1",
|
|
67
|
+
apiKey: "sk-customer-secret",
|
|
68
|
+
title: "Your access is active",
|
|
69
|
+
details: [],
|
|
70
|
+
});
|
|
71
|
+
assert.equal(ok, true);
|
|
72
|
+
assert.equal(sentMessages[0]?.includes("curl -fsSL"), true);
|
|
73
|
+
assert.equal(sentMessages[0]?.includes("https://proxy.example.com/api/customer/codex/setup.sh"), true);
|
|
74
|
+
assert.equal(sentMessages[0]?.includes("Authorization: Bearer sk-customer-secret"), true);
|
|
75
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { InlineKeyboard } from "grammy";
|
|
2
|
+
import { renderAdminScreen } from "../admin-actions.js";
|
|
3
|
+
import { isAdmin } from "../auth.js";
|
|
4
|
+
import { answerCallbackQuerySafely } from "../callbacks.js";
|
|
5
|
+
import { formatOauthStatus } from "../format.js";
|
|
6
|
+
import { getProxyErrorMessage } from "../actions.js";
|
|
7
|
+
function buildAccountsKeyboard(payload, stateStore, admin) {
|
|
8
|
+
const keyboard = new InlineKeyboard();
|
|
9
|
+
for (const account of payload?.accounts ?? []) {
|
|
10
|
+
const accountActionToken = stateStore.issueCallbackToken({
|
|
11
|
+
kind: "account_action",
|
|
12
|
+
action: "refresh",
|
|
13
|
+
accountId: account.id,
|
|
14
|
+
});
|
|
15
|
+
const label = (account.email || account.accountId || account.id).slice(0, 18);
|
|
16
|
+
keyboard.text(`↻ ${label}`, `v1:acct:refresh:${accountActionToken}`);
|
|
17
|
+
if (account.disabled) {
|
|
18
|
+
keyboard.text("🟢 Enable", `v1:acct:enable:${accountActionToken}`);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
keyboard.text("🟡 Disable", `v1:acct:disable:${accountActionToken}`);
|
|
22
|
+
}
|
|
23
|
+
if (admin) {
|
|
24
|
+
keyboard.text("🔴 Delete", `v1:acct:delete-confirm:${accountActionToken}`);
|
|
25
|
+
}
|
|
26
|
+
keyboard.row();
|
|
27
|
+
}
|
|
28
|
+
return (payload?.accounts?.length ?? 0) > 0 ? keyboard : undefined;
|
|
29
|
+
}
|
|
30
|
+
export function registerAccountsCommand(bot, deps, stateStore) {
|
|
31
|
+
bot.command("accounts", async (ctx) => {
|
|
32
|
+
await renderAccountsScreen(ctx, deps, stateStore);
|
|
33
|
+
});
|
|
34
|
+
bot.callbackQuery("v1:acct:list", async (ctx) => {
|
|
35
|
+
await answerCallbackQuerySafely(ctx, { text: "Loaded" });
|
|
36
|
+
await renderAccountsScreen(ctx, deps, stateStore);
|
|
37
|
+
});
|
|
38
|
+
bot.callbackQuery(/^v1:acct:(refresh|disable|enable):([A-Za-z0-9_-]+)$/, async (ctx) => {
|
|
39
|
+
const action = ctx.match[1];
|
|
40
|
+
const token = ctx.match[2];
|
|
41
|
+
try {
|
|
42
|
+
const callbackState = stateStore.readCallbackToken(token);
|
|
43
|
+
const accountId = callbackState?.kind === "account_action" ? callbackState.accountId : undefined;
|
|
44
|
+
if (!accountId) {
|
|
45
|
+
await answerCallbackQuerySafely(ctx, { text: "Action expired. Refresh /accounts.", show_alert: true });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (action === "refresh") {
|
|
49
|
+
await deps.proxyClient.refreshAccount(accountId);
|
|
50
|
+
}
|
|
51
|
+
else if (action === "disable") {
|
|
52
|
+
await deps.proxyClient.disableAccount(accountId);
|
|
53
|
+
}
|
|
54
|
+
else if (action === "enable") {
|
|
55
|
+
await deps.proxyClient.enableAccount(accountId);
|
|
56
|
+
}
|
|
57
|
+
await answerCallbackQuerySafely(ctx, { text: `${action} ok` });
|
|
58
|
+
await renderAccountsScreen(ctx, deps, stateStore);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
await answerCallbackQuerySafely(ctx);
|
|
62
|
+
await renderAdminScreen(ctx, {
|
|
63
|
+
text: getProxyErrorMessage(error),
|
|
64
|
+
loop: "accounts",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
bot.callbackQuery(/^v1:acct:delete-confirm:([A-Za-z0-9_-]+)$/, async (ctx) => {
|
|
69
|
+
if (!isAdmin(ctx, deps.config)) {
|
|
70
|
+
await answerCallbackQuerySafely(ctx, { text: "Admin only", show_alert: true });
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const token = ctx.match[1];
|
|
74
|
+
const callbackState = stateStore.readCallbackToken(token);
|
|
75
|
+
const accountId = callbackState?.kind === "account_action" ? callbackState.accountId : undefined;
|
|
76
|
+
if (!accountId) {
|
|
77
|
+
await answerCallbackQuerySafely(ctx, { text: "Action expired. Refresh /accounts.", show_alert: true });
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const deleteToken = stateStore.issueCallbackToken({
|
|
81
|
+
kind: "account_action",
|
|
82
|
+
action: "delete",
|
|
83
|
+
accountId,
|
|
84
|
+
});
|
|
85
|
+
await answerCallbackQuerySafely(ctx);
|
|
86
|
+
await renderAdminScreen(ctx, {
|
|
87
|
+
text: "Confirm account deletion?",
|
|
88
|
+
loop: "accounts",
|
|
89
|
+
primaryKeyboard: new InlineKeyboard()
|
|
90
|
+
.text("Delete account", `v1:acct:delete:${deleteToken}`)
|
|
91
|
+
.text("Cancel", "v1:acct:delete-cancel"),
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
bot.callbackQuery("v1:acct:delete-cancel", async (ctx) => {
|
|
95
|
+
await answerCallbackQuerySafely(ctx, { text: "Cancelled" });
|
|
96
|
+
await renderAccountsScreen(ctx, deps, stateStore);
|
|
97
|
+
});
|
|
98
|
+
bot.callbackQuery(/^v1:acct:delete:([A-Za-z0-9_-]+)$/, async (ctx) => {
|
|
99
|
+
if (!isAdmin(ctx, deps.config)) {
|
|
100
|
+
await answerCallbackQuerySafely(ctx, { text: "Admin only", show_alert: true });
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
const token = ctx.match[1];
|
|
105
|
+
const callbackState = stateStore.readCallbackToken(token);
|
|
106
|
+
const accountId = callbackState?.kind === "account_action" ? callbackState.accountId : undefined;
|
|
107
|
+
if (!accountId) {
|
|
108
|
+
await answerCallbackQuerySafely(ctx, { text: "Action expired. Refresh /accounts.", show_alert: true });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
stateStore.clearCallbackToken(token);
|
|
112
|
+
await deps.proxyClient.deleteAccount(accountId);
|
|
113
|
+
await answerCallbackQuerySafely(ctx, { text: "delete ok" });
|
|
114
|
+
await renderAccountsScreen(ctx, deps, stateStore);
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
await answerCallbackQuerySafely(ctx);
|
|
118
|
+
await renderAdminScreen(ctx, {
|
|
119
|
+
text: getProxyErrorMessage(error),
|
|
120
|
+
loop: "accounts",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async function renderAccountsScreen(ctx, deps, stateStore) {
|
|
126
|
+
try {
|
|
127
|
+
const payload = await deps.proxyClient.getOauthStatus();
|
|
128
|
+
await renderAdminScreen(ctx, {
|
|
129
|
+
text: formatOauthStatus(payload),
|
|
130
|
+
loop: "accounts",
|
|
131
|
+
primaryKeyboard: buildAccountsKeyboard(payload, stateStore, isAdmin(ctx, deps.config)),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
await renderAdminScreen(ctx, {
|
|
136
|
+
text: getProxyErrorMessage(error),
|
|
137
|
+
loop: "accounts",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|