@stage-labs/metro 0.1.0-beta.20 → 0.1.0-beta.22
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/dist/cli.js +1 -1
- package/package.json +1 -1
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/package.json +1 -1
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/accounts.ts +10 -10
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/actions.ts +1 -1
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/attachments.ts +1 -1
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/format.ts +7 -7
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group-actions.ts +1 -1
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/index.ts +10 -10
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/station.ts +2 -2
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/voice-transcribe.ts +6 -6
- package/runtime/node_modules/@metro-labs/mcp/package.json +2 -2
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +14 -8
- package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +52 -21
- package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +5 -2
- package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +2 -2
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/accounts.ts +43 -13
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-schemas.ts +6 -6
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-interactive.ts +6 -6
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach.ts +6 -6
- package/runtime/node_modules/@metro-labs/mcp/src/stations/lines.ts +2 -2
- package/runtime/node_modules/@metro-labs/mcp/src/stations/registry.ts +4 -4
- package/runtime/node_modules/@metro-labs/telegram/package.json +5 -2
- package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +47 -86
- package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +210 -251
- package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +66 -107
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/client.ts +4 -4
- package/runtime/node_modules/@metro-labs/telegram/src/format.ts +110 -141
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/group.ts +1 -1
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/history.ts +1 -1
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/inbound.ts +6 -6
- package/runtime/node_modules/@metro-labs/telegram/src/index.ts +35 -92
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/login.ts +20 -20
- package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +65 -151
- package/runtime/node_modules/@metro-labs/telegram/src/members.ts +60 -32
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/reactions.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram/src/station.ts +8 -1
- package/runtime/node_modules/@metro-labs/telegram/src/types.ts +6 -47
- package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -3
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram-bot}/package.json +3 -6
- package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +172 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +48 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
- package/runtime/runtime.json +1 -1
- package/runtime/node_modules/@metro-labs/telegram-user/src/accounts.ts +0 -81
- package/runtime/node_modules/@metro-labs/telegram-user/src/actions.ts +0 -254
- package/runtime/node_modules/@metro-labs/telegram-user/src/attachments.ts +0 -87
- package/runtime/node_modules/@metro-labs/telegram-user/src/format.ts +0 -141
- package/runtime/node_modules/@metro-labs/telegram-user/src/index.ts +0 -46
- package/runtime/node_modules/@metro-labs/telegram-user/src/media-actions.ts +0 -80
- package/runtime/node_modules/@metro-labs/telegram-user/src/members.ts +0 -75
- package/runtime/node_modules/@metro-labs/telegram-user/src/station.ts +0 -24
- package/runtime/node_modules/@metro-labs/telegram-user/src/types.ts +0 -7
- package/runtime/node_modules/@metro-labs/telegram-user/src/wire.ts +0 -1
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/api-base.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/members.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/send-files.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/verify.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/voice-speak.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/voice.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/wire.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/normalize.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{telegram → telegram-bot}/src/verify.ts +0 -0
|
@@ -2,11 +2,11 @@ import { randomBytes } from 'node:crypto';
|
|
|
2
2
|
import {
|
|
3
3
|
DiscordTokenError,
|
|
4
4
|
verifyDiscordBotToken,
|
|
5
|
-
} from '@metro-labs/discord/verify';
|
|
5
|
+
} from '@metro-labs/discord-bot/verify';
|
|
6
6
|
import {
|
|
7
7
|
TelegramTokenError,
|
|
8
8
|
verifyTelegramBotToken,
|
|
9
|
-
} from '@metro-labs/telegram/verify';
|
|
9
|
+
} from '@metro-labs/telegram-bot/verify';
|
|
10
10
|
import { ApiError } from '../daemon/api-error.js';
|
|
11
11
|
import { publicBaseOrDefault } from '../daemon/attach-serve.js';
|
|
12
12
|
import {
|
|
@@ -18,8 +18,8 @@ import {
|
|
|
18
18
|
} from './attach-xmtp.js';
|
|
19
19
|
|
|
20
20
|
export const ATTACHABLE_STATIONS = [
|
|
21
|
-
'discord',
|
|
22
|
-
'telegram',
|
|
21
|
+
'discord-bot',
|
|
22
|
+
'telegram-bot',
|
|
23
23
|
'xmtp',
|
|
24
24
|
'webhook',
|
|
25
25
|
] as const;
|
|
@@ -175,8 +175,8 @@ export async function prepareAccount(
|
|
|
175
175
|
input: AttachInput,
|
|
176
176
|
verify: VerifyXmtpKey = verifyXmtpKeyOutOfProcess,
|
|
177
177
|
): Promise<PreparedAccount> {
|
|
178
|
-
if (input.station === 'discord') return prepareDiscord(input.token);
|
|
179
|
-
if (input.station === 'telegram') return prepareTelegram(input.token);
|
|
178
|
+
if (input.station === 'discord-bot') return prepareDiscord(input.token);
|
|
179
|
+
if (input.station === 'telegram-bot') return prepareTelegram(input.token);
|
|
180
180
|
if (input.station === 'webhook') return prepareWebhook();
|
|
181
181
|
return prepareXmtp(verify);
|
|
182
182
|
}
|
|
@@ -38,7 +38,7 @@ function parseTelegramLine(
|
|
|
38
38
|
line: Line | string,
|
|
39
39
|
): { accountId: string; chatId: number; topicId?: number } | null {
|
|
40
40
|
const p = Line.parse(line);
|
|
41
|
-
if (p?.station !== 'telegram') return null;
|
|
41
|
+
if (p?.station !== 'telegram-bot') return null;
|
|
42
42
|
const { accountId, rest } = splitTelegramAccount(p.path);
|
|
43
43
|
const [chatId, topicId] = rest;
|
|
44
44
|
if (rest.length < 1 || rest.length > 2 || chatId === undefined || !isSignedInt(chatId))
|
|
@@ -76,7 +76,7 @@ export const Line = {
|
|
|
76
76
|
parseXmtp: (line: Line | string) => parseAccountScoped(line, 'xmtp'),
|
|
77
77
|
|
|
78
78
|
parseDiscord: (line: Line | string) =>
|
|
79
|
-
parseAccountScoped(line, 'discord', isSnowflake),
|
|
79
|
+
parseAccountScoped(line, 'discord-bot', isSnowflake),
|
|
80
80
|
|
|
81
81
|
parseTelegram: (line: Line | string) => parseTelegramLine(line),
|
|
82
82
|
};
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import type { Station } from './types.js';
|
|
2
2
|
import { Line } from './lines.js';
|
|
3
3
|
import { xmtpStation } from '@metro-labs/xmtp';
|
|
4
|
+
import { telegramBotStation } from '@metro-labs/telegram-bot';
|
|
4
5
|
import { telegramStation } from '@metro-labs/telegram';
|
|
5
|
-
import {
|
|
6
|
-
import { discordStation } from '@metro-labs/discord';
|
|
6
|
+
import { discordBotStation } from '@metro-labs/discord-bot';
|
|
7
7
|
import { whatsappStation } from '@metro-labs/whatsapp';
|
|
8
8
|
import { webhookStation } from '@metro-labs/webhook';
|
|
9
9
|
|
|
10
10
|
export const STATIONS: readonly Station[] = [
|
|
11
11
|
xmtpStation,
|
|
12
|
+
telegramBotStation,
|
|
12
13
|
telegramStation,
|
|
13
|
-
|
|
14
|
-
discordStation,
|
|
14
|
+
discordBotStation,
|
|
15
15
|
whatsappStation,
|
|
16
16
|
webhookStation,
|
|
17
17
|
];
|
|
@@ -6,20 +6,23 @@
|
|
|
6
6
|
"exports": {
|
|
7
7
|
".": "./src/station.ts",
|
|
8
8
|
"./train": "./src/index.ts",
|
|
9
|
-
"./
|
|
9
|
+
"./login": "./src/login.ts"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc",
|
|
13
13
|
"test": "tsc --noEmit && bun test test/"
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@metro-labs/mcp": "workspace:*"
|
|
16
|
+
"@metro-labs/mcp": "workspace:*",
|
|
17
|
+
"@mtcute/bun": "0.30.1"
|
|
17
18
|
},
|
|
18
19
|
"devDependencies": {
|
|
19
20
|
"@stage-labs/config": "0.1.0-beta.2",
|
|
21
|
+
"@types/qrcode-terminal": "^0.12.2",
|
|
20
22
|
"eslint": "^10.3.0",
|
|
21
23
|
"knip": "^6.15.0",
|
|
22
24
|
"madge": "^8.0.0",
|
|
25
|
+
"qrcode-terminal": "^0.12.0",
|
|
23
26
|
"typescript": "^5"
|
|
24
27
|
}
|
|
25
28
|
}
|
|
@@ -3,102 +3,43 @@ import { join } from 'node:path';
|
|
|
3
3
|
import {
|
|
4
4
|
makeAccountStore,
|
|
5
5
|
resolveAccountId,
|
|
6
|
+
type Die,
|
|
6
7
|
} from '@metro-labs/mcp/stations/account-store';
|
|
7
|
-
import {
|
|
8
|
+
import type { UserAccount } from './types.js';
|
|
8
9
|
|
|
9
10
|
const ACCOUNTS_FILE =
|
|
10
11
|
process.env.TELEGRAM_ACCOUNTS_FILE ??
|
|
11
12
|
join(homedir(), '.metro', 'telegram-accounts.json');
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
const isSignedInt = (s: string): boolean => /^-?\d+$/.test(s);
|
|
15
|
+
const isTopic = (s: string): boolean => /^\d+$/.test(s);
|
|
16
|
+
|
|
17
|
+
function validateAccount(a: UserAccount, seen: Set<string>, die: Die): void {
|
|
18
|
+
if (!a.id) die('account missing id');
|
|
19
|
+
if (!a.session || typeof a.session !== 'string')
|
|
20
|
+
die(`account '${a.id}' missing session`);
|
|
21
|
+
if (!Number.isInteger(a.apiId) || (a.apiId ?? 0) <= 0)
|
|
22
|
+
die(`account '${a.id}' missing apiId`);
|
|
23
|
+
if (!a.apiHash || typeof a.apiHash !== 'string')
|
|
24
|
+
die(`account '${a.id}' missing apiHash`);
|
|
25
|
+
if (seen.has(a.id)) die(`duplicate account id '${a.id}'`);
|
|
26
|
+
seen.add(a.id);
|
|
17
27
|
}
|
|
18
28
|
|
|
19
|
-
export const { loadAccounts } = makeAccountStore<
|
|
29
|
+
export const { loadAccounts } = makeAccountStore<UserAccount>({
|
|
20
30
|
prefix: 'telegram',
|
|
21
31
|
file: ACCOUNTS_FILE,
|
|
22
32
|
allowlistEnv: ['TELEGRAM_ONLY_ACCOUNTS', 'TELEGRAM_ACCOUNTS'],
|
|
23
33
|
validate(raw, die) {
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
for (const a of raw) {
|
|
27
|
-
if (!a.id) die('account missing id');
|
|
28
|
-
if (!a.token || typeof a.token !== 'string')
|
|
29
|
-
die(`account '${a.id}' missing token`);
|
|
30
|
-
if (seenId.has(a.id)) die(`duplicate account id '${a.id}'`);
|
|
31
|
-
if (seenTok.has(a.token))
|
|
32
|
-
die(
|
|
33
|
-
`account '${a.id}' reuses a token used by another account (409 on getUpdates)`,
|
|
34
|
-
);
|
|
35
|
-
seenId.add(a.id);
|
|
36
|
-
seenTok.add(a.token);
|
|
37
|
-
}
|
|
34
|
+
const seen = new Set<string>();
|
|
35
|
+
for (const a of raw) validateAccount(a, seen, die);
|
|
38
36
|
},
|
|
39
37
|
});
|
|
40
38
|
|
|
41
|
-
export
|
|
42
|
-
cfg: AccountConfig;
|
|
43
|
-
api: string;
|
|
44
|
-
fileApi: string;
|
|
45
|
-
offset: number;
|
|
46
|
-
}
|
|
47
|
-
export const accounts = new Map<string, Account>();
|
|
48
|
-
|
|
49
|
-
export async function tg<T>(
|
|
50
|
-
accountId: string,
|
|
51
|
-
method: string,
|
|
52
|
-
body: unknown,
|
|
53
|
-
timeoutMs = 30_000,
|
|
54
|
-
): Promise<T> {
|
|
55
|
-
const acct = accounts.get(accountId);
|
|
56
|
-
if (!acct) throw new Error(`unknown account '${accountId}'`);
|
|
57
|
-
const res = await fetch(`${acct.api}/${method}`, {
|
|
58
|
-
method: 'POST',
|
|
59
|
-
headers: { 'Content-Type': 'application/json' },
|
|
60
|
-
body: JSON.stringify(body),
|
|
61
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
62
|
-
});
|
|
63
|
-
const json = (await res.json()) as {
|
|
64
|
-
ok: boolean;
|
|
65
|
-
description?: string;
|
|
66
|
-
result?: T;
|
|
67
|
-
};
|
|
68
|
-
if (!json.ok)
|
|
69
|
-
throw new Error(`telegram ${method}: ${json.description ?? 'unknown'}`);
|
|
70
|
-
return json.result as T;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export async function tgForm<T>(
|
|
74
|
-
accountId: string,
|
|
75
|
-
method: string,
|
|
76
|
-
form: FormData,
|
|
77
|
-
timeoutMs = 60_000,
|
|
78
|
-
): Promise<T> {
|
|
79
|
-
const acct = accounts.get(accountId);
|
|
80
|
-
if (!acct) throw new Error(`unknown account '${accountId}'`);
|
|
81
|
-
const res = await fetch(`${acct.api}/${method}`, {
|
|
82
|
-
method: 'POST',
|
|
83
|
-
body: form,
|
|
84
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
85
|
-
});
|
|
86
|
-
const json = (await res.json()) as {
|
|
87
|
-
ok: boolean;
|
|
88
|
-
description?: string;
|
|
89
|
-
result?: T;
|
|
90
|
-
};
|
|
91
|
-
if (!json.ok)
|
|
92
|
-
throw new Error(`telegram ${method}: ${json.description ?? 'unknown'}`);
|
|
93
|
-
return json.result as T;
|
|
94
|
-
}
|
|
39
|
+
export const accounts = new Map<string, UserAccount>();
|
|
95
40
|
|
|
96
41
|
export function accountFor(args: { account?: string; line?: string }): string {
|
|
97
|
-
return resolveAccountId(
|
|
98
|
-
accounts,
|
|
99
|
-
args,
|
|
100
|
-
(line) => Line.parseTelegram(line)?.accountId,
|
|
101
|
-
);
|
|
42
|
+
return resolveAccountId(accounts, args, (line) => targetOf(line)?.accountId);
|
|
102
43
|
}
|
|
103
44
|
|
|
104
45
|
export function lineOf(
|
|
@@ -110,11 +51,31 @@ export function lineOf(
|
|
|
110
51
|
return `metro://telegram/${accountId}/${tail}`;
|
|
111
52
|
}
|
|
112
53
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
54
|
+
interface Target {
|
|
55
|
+
accountId: string;
|
|
56
|
+
chatId: number;
|
|
57
|
+
topicId?: number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function splitScoped(path: string[]): { accountId: string; rest: string[] } {
|
|
61
|
+
const first = path[0];
|
|
62
|
+
if (path.length >= 2 && first !== undefined && !isSignedInt(first))
|
|
63
|
+
return { accountId: first, rest: path.slice(1) };
|
|
64
|
+
return { accountId: 'default', rest: path };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function targetOf(line: string): Target | undefined {
|
|
68
|
+
const prefix = 'metro://telegram/';
|
|
69
|
+
if (!line.startsWith(prefix)) return undefined;
|
|
70
|
+
const path = line.slice(prefix.length).split('/').filter(Boolean);
|
|
71
|
+
const { accountId, rest } = splitScoped(path);
|
|
72
|
+
const [chatId, topicId] = rest;
|
|
73
|
+
if (rest.length < 1 || rest.length > 2 || chatId === undefined) return undefined;
|
|
74
|
+
if (!isSignedInt(chatId)) return undefined;
|
|
75
|
+
if (topicId !== undefined && !isTopic(topicId)) return undefined;
|
|
76
|
+
return {
|
|
77
|
+
accountId,
|
|
78
|
+
chatId: Number(chatId),
|
|
79
|
+
...(topicId !== undefined ? { topicId: Number(topicId) } : {}),
|
|
80
|
+
};
|
|
120
81
|
}
|