@stage-labs/metro 0.1.0-beta.201 → 0.1.0-beta.202
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/claude.js +4 -15
- package/dist/cli.js +5 -3
- package/dist/hold.js +1 -2
- package/dist/runtime-install.js +4 -5
- package/dist/runtime.js +1 -4
- package/dist/serve.js +4 -5
- package/dist/service.js +82 -21
- package/dist/update.js +1 -1
- package/package.json +2 -2
- package/runtime/marketplace/plugin/.claude-plugin/plugin.json +1 -1
- package/runtime/node_modules/@metro-labs/core/src/events.ts +4 -2
- package/runtime/node_modules/@metro-labs/core/src/lines.ts +12 -25
- package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +45 -22
- package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +0 -1
- package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +8 -80
- package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +3 -5
- package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +4 -9
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +9 -39
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +9 -28
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +0 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +8 -116
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +11 -35
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +7 -9
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +3 -29
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +5 -8
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +5 -8
- package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +3 -6
- package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +25 -3
- package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +2 -3
- package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +6 -56
- package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +7 -6
- package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +7 -7
- package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +4 -4
- package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +2 -4
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +19 -22
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +1 -2
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +6 -8
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +3 -6
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +2 -3
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +28 -1
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +4 -4
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +3 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +6 -6
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +7 -7
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +3 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +10 -31
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +1 -2
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +5 -6
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
- package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +1 -4
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +3 -8
- package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +6 -7
- package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +5 -44
- package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +4 -4
- package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +6 -7
- package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +12 -13
- package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +5 -22
- package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +1 -19
- package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +7 -35
- package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +5 -10
- package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +1 -1
- package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +1 -2
- package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +1 -1
- package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +11 -12
- package/runtime/node_modules/@metro-labs/http/src/api-http.ts +2 -5
- package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +0 -1
- package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +1 -15
- package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +0 -6
- package/runtime/node_modules/@metro-labs/outlook/src/format.ts +0 -1
- package/runtime/node_modules/@metro-labs/outlook/src/state.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +4 -22
- package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +2 -2
- package/runtime/node_modules/@metro-labs/telegram/src/format.ts +1 -3
- package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +1 -2
- package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +4 -37
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +7 -23
- package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +1 -4
- package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +4 -9
- package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +3 -3
- package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +0 -11
- package/runtime/node_modules/@metro-labs/threema/src/actions.ts +2 -12
- package/runtime/node_modules/@metro-labs/threema/src/files.ts +4 -15
- package/runtime/node_modules/@metro-labs/threema/src/format.ts +2 -7
- package/runtime/node_modules/@metro-labs/whatsapp/package.json +1 -2
- package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +5 -26
- package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +5 -6
- package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +1 -3
- package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +1 -12
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +14 -5
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +31 -78
- package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +3 -4
- package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +1 -6
- package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +2 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +0 -2
- package/runtime/runtime.json +1 -1
- package/runtime/node_modules/@metro-labs/core/src/identity.ts +0 -7
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/needs.ts +0 -2
- package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +0 -12
- package/runtime/node_modules/@metro-labs/daemon/src/channels/react-note.ts +0 -13
- package/runtime/node_modules/@metro-labs/daemon/src/channels/reply-id.ts +0 -1
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +0 -29
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/api.ts +0 -43
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/move.ts +0 -165
- package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +0 -1
- package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +0 -22
- package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -1
- package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +0 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
- package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -1
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from '@metro-labs/core/stations/station-runtime';
|
|
9
9
|
import { accountFor, accounts, targetOf } from './accounts.js';
|
|
10
10
|
import type { UserClient } from './client.js';
|
|
11
|
-
import {
|
|
11
|
+
import { messagingAliases } from '@metro-labs/core/stations/messaging-normalize';
|
|
12
12
|
import {
|
|
13
13
|
sendAttachments,
|
|
14
14
|
type CanonicalAttachment,
|
|
@@ -251,6 +251,6 @@ export function makeHandleCall(
|
|
|
251
251
|
set_profile: makeSetProfile(clientFor),
|
|
252
252
|
profile: makeReadProfile(clientFor),
|
|
253
253
|
},
|
|
254
|
-
normalize:
|
|
254
|
+
normalize: messagingAliases(),
|
|
255
255
|
});
|
|
256
256
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Chat, Message, Peer, User } from '@mtcute/bun';
|
|
2
2
|
import { lineOf } from './accounts.js';
|
|
3
|
-
import { mintId } from '
|
|
3
|
+
import { mintId } from '@metro-labs/core/stations/station-runtime';
|
|
4
4
|
import { isDownloadable, pendingDescriptorOf } from './attachments.js';
|
|
5
5
|
|
|
6
6
|
const isUser = (peer: Peer): peer is User => peer.type === 'user';
|
|
@@ -72,7 +72,6 @@ export function envelope(
|
|
|
72
72
|
? [pendingDescriptorOf(media)]
|
|
73
73
|
: undefined;
|
|
74
74
|
return {
|
|
75
|
-
kind: 'inbound',
|
|
76
75
|
id: mintId(),
|
|
77
76
|
ts: m.date.toISOString(),
|
|
78
77
|
station: 'telegram',
|
|
@@ -108,7 +107,6 @@ export interface ReactionInput {
|
|
|
108
107
|
|
|
109
108
|
export function reactionEnvelope(r: ReactionInput): Record<string, unknown> {
|
|
110
109
|
return {
|
|
111
|
-
kind: 'react',
|
|
112
110
|
id: mintId(),
|
|
113
111
|
ts: r.date.toISOString(),
|
|
114
112
|
station: 'telegram',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Message } from '@mtcute/bun';
|
|
2
2
|
import { errMsg, log } from '@metro-labs/core/log';
|
|
3
|
-
import { emit } from '
|
|
3
|
+
import { emit } from '@metro-labs/core/stations/station-runtime';
|
|
4
4
|
import { reportAttachment } from '@metro-labs/core/stations/train-events';
|
|
5
5
|
import { envelope, isOwnEcho } from './format.js';
|
|
6
6
|
import { downloadMedia, isDownloadable } from './attachments.js';
|
|
@@ -6,12 +6,11 @@ import type { UserClient } from './client.js';
|
|
|
6
6
|
export interface CanonicalAttachment {
|
|
7
7
|
kind?: string;
|
|
8
8
|
path?: string;
|
|
9
|
-
url?: string;
|
|
10
9
|
mime?: string;
|
|
11
10
|
name?: string;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
const srcOf = (att: CanonicalAttachment): string => att.path ??
|
|
13
|
+
const srcOf = (att: CanonicalAttachment): string => att.path ?? '';
|
|
15
14
|
|
|
16
15
|
function isImage(att: CanonicalAttachment): boolean {
|
|
17
16
|
if (att.kind === 'image') return true;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { tl } from '@mtcute/bun';
|
|
2
2
|
import { getBarePeerId, getMarkedPeerId } from '@mtcute/bun';
|
|
3
3
|
import { errMsg } from '@metro-labs/core/log';
|
|
4
|
-
import { emit } from '
|
|
4
|
+
import { emit } from '@metro-labs/core/stations/station-runtime';
|
|
5
5
|
import { reactionEnvelope } from './format.js';
|
|
6
6
|
import type { UserClient } from './client.js';
|
|
7
7
|
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import { homedir } from 'node:os';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
1
|
import {
|
|
4
2
|
makeAccountStore,
|
|
5
3
|
resolveAccountId,
|
|
6
4
|
} from '@metro-labs/core/stations/account-store';
|
|
7
5
|
import { Line } from '@metro-labs/core/lines';
|
|
8
6
|
|
|
9
|
-
const ACCOUNTS_FILE =
|
|
10
|
-
process.env.TELEGRAM_BOT_ACCOUNTS_FILE ??
|
|
11
|
-
join(homedir(), '.metro', 'telegram-bot-accounts.json');
|
|
12
|
-
|
|
13
7
|
export interface AccountConfig {
|
|
14
8
|
id: string;
|
|
15
9
|
token: string;
|
|
@@ -17,20 +11,15 @@ export interface AccountConfig {
|
|
|
17
11
|
|
|
18
12
|
export const { loadAccounts } = makeAccountStore<AccountConfig>({
|
|
19
13
|
prefix: 'telegram-bot',
|
|
20
|
-
file: ACCOUNTS_FILE,
|
|
21
14
|
validate(raw, die) {
|
|
22
|
-
const seenId = new Set<string>();
|
|
23
15
|
const seenTok = new Set<string>();
|
|
24
16
|
for (const a of raw) {
|
|
25
|
-
if (!a.id) die('account missing id');
|
|
26
17
|
if (!a.token || typeof a.token !== 'string')
|
|
27
18
|
die(`account '${a.id}' missing token`);
|
|
28
|
-
if (seenId.has(a.id)) die(`duplicate account id '${a.id}'`);
|
|
29
19
|
if (seenTok.has(a.token))
|
|
30
20
|
die(
|
|
31
21
|
`account '${a.id}' reuses a token used by another account (409 on getUpdates)`,
|
|
32
22
|
);
|
|
33
|
-
seenId.add(a.id);
|
|
34
23
|
seenTok.add(a.token);
|
|
35
24
|
}
|
|
36
25
|
},
|
|
@@ -49,37 +38,15 @@ export async function tg<T>(
|
|
|
49
38
|
accountId: string,
|
|
50
39
|
method: string,
|
|
51
40
|
body: unknown,
|
|
52
|
-
timeoutMs = 30_000,
|
|
53
|
-
): Promise<T> {
|
|
54
|
-
const acct = accounts.get(accountId);
|
|
55
|
-
if (!acct) throw new Error(`unknown account '${accountId}'`);
|
|
56
|
-
const res = await fetch(`${acct.api}/${method}`, {
|
|
57
|
-
method: 'POST',
|
|
58
|
-
headers: { 'Content-Type': 'application/json' },
|
|
59
|
-
body: JSON.stringify(body),
|
|
60
|
-
signal: AbortSignal.timeout(timeoutMs),
|
|
61
|
-
});
|
|
62
|
-
const json = (await res.json()) as {
|
|
63
|
-
ok: boolean;
|
|
64
|
-
description?: string;
|
|
65
|
-
result?: T;
|
|
66
|
-
};
|
|
67
|
-
if (!json.ok)
|
|
68
|
-
throw new Error(`telegram-bot ${method}: ${json.description ?? 'unknown'}`);
|
|
69
|
-
return json.result as T;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export async function tgForm<T>(
|
|
73
|
-
accountId: string,
|
|
74
|
-
method: string,
|
|
75
|
-
form: FormData,
|
|
76
|
-
timeoutMs = 60_000,
|
|
41
|
+
timeoutMs = body instanceof FormData ? 60_000 : 30_000,
|
|
77
42
|
): Promise<T> {
|
|
78
43
|
const acct = accounts.get(accountId);
|
|
79
44
|
if (!acct) throw new Error(`unknown account '${accountId}'`);
|
|
45
|
+
const form = body instanceof FormData;
|
|
80
46
|
const res = await fetch(`${acct.api}/${method}`, {
|
|
81
47
|
method: 'POST',
|
|
82
|
-
|
|
48
|
+
...(form ? {} : { headers: { 'Content-Type': 'application/json' } }),
|
|
49
|
+
body: form ? body : JSON.stringify(body),
|
|
83
50
|
signal: AbortSignal.timeout(timeoutMs),
|
|
84
51
|
});
|
|
85
52
|
const json = (await res.json()) as {
|
|
@@ -1,17 +1,12 @@
|
|
|
1
1
|
import { accounts, tg, targetOf } from './accounts.js';
|
|
2
|
-
import { respond } from './wire.js';
|
|
3
2
|
import { errMsg } from '@metro-labs/core/log';
|
|
4
|
-
import {
|
|
3
|
+
import { messagingAliases } from '@metro-labs/core/stations/messaging-normalize';
|
|
5
4
|
import {
|
|
6
5
|
adminMemberList,
|
|
7
6
|
inaccessibleMemberList,
|
|
8
7
|
type TgChatMember,
|
|
9
8
|
} from './members.js';
|
|
10
|
-
import {
|
|
11
|
-
makeStation,
|
|
12
|
-
type CallMsg,
|
|
13
|
-
type StationHandler,
|
|
14
|
-
} from '@metro-labs/core/stations/station-runtime';
|
|
9
|
+
import { makeStation, respond, type CallMsg, type StationHandler } from '@metro-labs/core/stations/station-runtime';
|
|
15
10
|
import { mediaKindOf } from './attachments.js';
|
|
16
11
|
import {
|
|
17
12
|
emitOutbound,
|
|
@@ -65,13 +60,10 @@ interface SendArgs {
|
|
|
65
60
|
line: string;
|
|
66
61
|
text: string;
|
|
67
62
|
replyTo?: string;
|
|
68
|
-
parseMode?: string;
|
|
69
|
-
buttons?: { text: string; url: string }[][];
|
|
70
63
|
account?: string;
|
|
71
64
|
attachments?: {
|
|
72
65
|
kind?: string;
|
|
73
66
|
path?: string;
|
|
74
|
-
url?: string;
|
|
75
67
|
mime?: string;
|
|
76
68
|
name?: string;
|
|
77
69
|
}[];
|
|
@@ -80,11 +72,8 @@ interface SendArgs {
|
|
|
80
72
|
|
|
81
73
|
type WireAttachment = NonNullable<SendArgs['attachments']>[number];
|
|
82
74
|
|
|
83
|
-
const attachmentSrc = (att: WireAttachment): string | undefined =>
|
|
84
|
-
att.path ?? att.url;
|
|
85
|
-
|
|
86
75
|
const attachmentKind = (att: WireAttachment): string =>
|
|
87
|
-
mediaKindOf(att.kind, att.mime,
|
|
76
|
+
mediaKindOf(att.kind, att.mime, att.path ?? att.name);
|
|
88
77
|
|
|
89
78
|
async function sendOneAttachment(
|
|
90
79
|
a: SendArgs,
|
|
@@ -96,10 +85,9 @@ async function sendOneAttachment(
|
|
|
96
85
|
if (!mf) throw new Error(`telegram-bot cannot send a '${kind}' attachment`);
|
|
97
86
|
const sent = await sendMedia(mf.method, mf.field, {
|
|
98
87
|
line: a.line,
|
|
99
|
-
path:
|
|
88
|
+
path: att.path,
|
|
100
89
|
caption,
|
|
101
90
|
replyTo: a.replyTo,
|
|
102
|
-
parseMode: a.parseMode,
|
|
103
91
|
account: a.account,
|
|
104
92
|
name: att.name,
|
|
105
93
|
});
|
|
@@ -144,13 +132,11 @@ async function send(id: string, args: Record<string, unknown>): Promise<void> {
|
|
|
144
132
|
await sendAttachments(id, a);
|
|
145
133
|
return;
|
|
146
134
|
}
|
|
147
|
-
const { line, text, replyTo,
|
|
135
|
+
const { line, text, replyTo, account } = a;
|
|
148
136
|
const { accountId, chatId, topicId } = targetOf(line, account);
|
|
149
137
|
const body: Record<string, unknown> = { chat_id: chatId, text };
|
|
150
138
|
if (topicId !== undefined) body.message_thread_id = topicId;
|
|
151
139
|
if (replyTo) body.reply_parameters = { message_id: Number(replyTo) };
|
|
152
|
-
if (parseMode) body.parse_mode = parseMode;
|
|
153
|
-
if (buttons) body.reply_markup = { inline_keyboard: buttons };
|
|
154
140
|
const sent = await tg<{ message_id: number }>(accountId, 'sendMessage', body);
|
|
155
141
|
finishSend(id, accountId, line, String(sent.message_id), text, replyTo);
|
|
156
142
|
}
|
|
@@ -172,11 +158,10 @@ async function react(id: string, args: Record<string, unknown>): Promise<void> {
|
|
|
172
158
|
}
|
|
173
159
|
|
|
174
160
|
async function edit(id: string, args: Record<string, unknown>): Promise<void> {
|
|
175
|
-
const { line, messageId, text,
|
|
161
|
+
const { line, messageId, text, account } = args as {
|
|
176
162
|
line: string;
|
|
177
163
|
messageId: string;
|
|
178
164
|
text: string;
|
|
179
|
-
parseMode?: string;
|
|
180
165
|
account?: string;
|
|
181
166
|
};
|
|
182
167
|
const { accountId, chatId } = targetOf(line, account);
|
|
@@ -184,7 +169,6 @@ async function edit(id: string, args: Record<string, unknown>): Promise<void> {
|
|
|
184
169
|
chat_id: chatId,
|
|
185
170
|
message_id: Number(messageId),
|
|
186
171
|
text,
|
|
187
|
-
parse_mode: parseMode,
|
|
188
172
|
});
|
|
189
173
|
respond(id, { result: { ok: true, account: accountId } });
|
|
190
174
|
}
|
|
@@ -242,5 +226,5 @@ const HANDLERS: Record<string, StationHandler> = {
|
|
|
242
226
|
|
|
243
227
|
export const handleCall = makeStation({
|
|
244
228
|
handlers: HANDLERS,
|
|
245
|
-
normalize:
|
|
229
|
+
normalize: messagingAliases(),
|
|
246
230
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { accounts, lineOf } from './accounts.js';
|
|
2
2
|
import { reportAttachment } from '@metro-labs/core/stations/train-events';
|
|
3
|
-
import { mintId } from '
|
|
3
|
+
import { mintId } from '@metro-labs/core/stations/station-runtime';
|
|
4
4
|
import { mediaRefOf, saveTelegramMedia } from './attachments.js';
|
|
5
5
|
import type { TgMsg, TgReaction, TgReactionCount, TgUser } from './types.js';
|
|
6
6
|
|
|
@@ -80,7 +80,6 @@ export const fullName = (u: TgUser | undefined): string | undefined => {
|
|
|
80
80
|
export function envelope(accountId: string, m: TgMsg): Record<string, unknown> {
|
|
81
81
|
const { line } = lineForMsg(accountId, m);
|
|
82
82
|
return {
|
|
83
|
-
kind: 'inbound',
|
|
84
83
|
id: mintId(),
|
|
85
84
|
ts: new Date(m.date * 1000).toISOString(),
|
|
86
85
|
station: 'telegram-bot',
|
|
@@ -111,7 +110,6 @@ export function reactionEnvelope(
|
|
|
111
110
|
const added = newEmojis.filter((e) => !oldEmojis.includes(e));
|
|
112
111
|
if (!added.length) return null;
|
|
113
112
|
return {
|
|
114
|
-
kind: 'react',
|
|
115
113
|
id: mintId(),
|
|
116
114
|
ts: new Date(r.date * 1000).toISOString(),
|
|
117
115
|
station: 'telegram-bot',
|
|
@@ -141,7 +139,6 @@ export function reactionCountEnvelope(
|
|
|
141
139
|
const emoji = top.type.emoji ?? '';
|
|
142
140
|
if (!emoji) return null;
|
|
143
141
|
return {
|
|
144
|
-
kind: 'react',
|
|
145
142
|
id: mintId(),
|
|
146
143
|
ts: new Date(rc.date * 1000).toISOString(),
|
|
147
144
|
station: 'telegram-bot',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { tg, targetOf } from './accounts.js';
|
|
2
2
|
import { selfUri } from '@metro-labs/core/stations/train-events';
|
|
3
|
-
import { emit, mintId, respond } from '
|
|
3
|
+
import { emit, mintId, respond } from '@metro-labs/core/stations/station-runtime';
|
|
4
4
|
import { appendFile } from '@metro-labs/core/stations/attachments';
|
|
5
5
|
|
|
6
6
|
export function emitOutbound(
|
|
@@ -11,18 +11,16 @@ export function emitOutbound(
|
|
|
11
11
|
replyTo?: string,
|
|
12
12
|
): void {
|
|
13
13
|
emit({
|
|
14
|
-
kind: 'outbound',
|
|
15
14
|
id: mintId(),
|
|
16
15
|
ts: new Date().toISOString(),
|
|
17
16
|
station: 'telegram-bot',
|
|
18
17
|
line,
|
|
19
|
-
from: selfUri(
|
|
18
|
+
from: selfUri(),
|
|
20
19
|
to: line,
|
|
21
20
|
message_id: messageId,
|
|
22
21
|
text,
|
|
23
22
|
reply_to: replyTo,
|
|
24
23
|
...(replyTo ? { event: { type: 'reply', replyTo } } : {}),
|
|
25
|
-
account: accountId,
|
|
26
24
|
payload: { account: accountId },
|
|
27
25
|
});
|
|
28
26
|
}
|
|
@@ -47,7 +45,6 @@ interface MediaArgs {
|
|
|
47
45
|
path: string;
|
|
48
46
|
caption?: string;
|
|
49
47
|
replyTo?: string;
|
|
50
|
-
parseMode?: string;
|
|
51
48
|
account?: string;
|
|
52
49
|
name?: string;
|
|
53
50
|
}
|
|
@@ -62,7 +59,6 @@ export async function sendMedia(
|
|
|
62
59
|
path,
|
|
63
60
|
caption,
|
|
64
61
|
replyTo,
|
|
65
|
-
parseMode,
|
|
66
62
|
account,
|
|
67
63
|
name: fileName,
|
|
68
64
|
} = args as unknown as MediaArgs;
|
|
@@ -71,7 +67,6 @@ export async function sendMedia(
|
|
|
71
67
|
form.append('chat_id', String(chatId));
|
|
72
68
|
if (topicId !== undefined) form.append('message_thread_id', String(topicId));
|
|
73
69
|
if (caption) form.append('caption', caption);
|
|
74
|
-
if (parseMode) form.append('parse_mode', parseMode);
|
|
75
70
|
if (replyTo)
|
|
76
71
|
form.append(
|
|
77
72
|
'reply_parameters',
|
|
@@ -79,7 +74,7 @@ export async function sendMedia(
|
|
|
79
74
|
);
|
|
80
75
|
const name = fileName ?? path.split('/').pop() ?? fieldName;
|
|
81
76
|
await appendFile(form, fieldName, path, name);
|
|
82
|
-
const r = await
|
|
77
|
+
const r = await tg<{ message_id: number }>(accountId, method, form);
|
|
83
78
|
return { accountId, message_id: r.message_id };
|
|
84
79
|
}
|
|
85
80
|
|
|
@@ -9,15 +9,15 @@ import {
|
|
|
9
9
|
} from '@metro-labs/core/stations/profile';
|
|
10
10
|
import { makeProfileCache, nonEmpty, type SenderProfile } from '@metro-labs/core/stations/sender-profile';
|
|
11
11
|
import { TrainError } from '@metro-labs/core/train-error';
|
|
12
|
-
import { accountFor, tg
|
|
13
|
-
import { respond } from '
|
|
12
|
+
import { accountFor, tg } from './accounts.js';
|
|
13
|
+
import { respond } from '@metro-labs/core/stations/station-runtime';
|
|
14
14
|
|
|
15
15
|
async function setPhoto(accountId: string, avatar: ProfileAvatar): Promise<void> {
|
|
16
16
|
assertImage(avatar);
|
|
17
17
|
const form = new FormData();
|
|
18
18
|
form.append('photo', JSON.stringify({ type: 'static', photo: 'attach://avatar' }));
|
|
19
19
|
await appendFile(form, 'avatar', avatar.path, avatar.name);
|
|
20
|
-
await
|
|
20
|
+
await tg(accountId, 'setMyProfilePhoto', form);
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export async function applyProfile(accountId: string, change: ProfileChange): Promise<void> {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { homedir } from 'node:os';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
1
|
import {
|
|
4
2
|
makeAccountStore,
|
|
5
3
|
resolveAccountId,
|
|
@@ -12,10 +10,6 @@ import { isGatewayId, normalizeThreemaId, parsePrivateKey } from './ids.js';
|
|
|
12
10
|
import { GroupStore, parseGroupKey } from './groups.js';
|
|
13
11
|
import type { GroupRef } from './messages.js';
|
|
14
12
|
|
|
15
|
-
const ACCOUNTS_FILE =
|
|
16
|
-
process.env.THREEMA_ACCOUNTS_FILE ??
|
|
17
|
-
join(homedir(), '.metro', 'threema-accounts.json');
|
|
18
|
-
|
|
19
13
|
export interface AccountConfig {
|
|
20
14
|
id: string;
|
|
21
15
|
gatewayId: string;
|
|
@@ -26,7 +20,6 @@ export interface AccountConfig {
|
|
|
26
20
|
}
|
|
27
21
|
|
|
28
22
|
function checkAccount(a: AccountConfig, die: Die): void {
|
|
29
|
-
if (!a.id) die('account missing id');
|
|
30
23
|
if (typeof a.gatewayId !== 'string' || !isGatewayId(a.gatewayId))
|
|
31
24
|
die(`account '${a.id}' has no Gateway ID`);
|
|
32
25
|
if (typeof a.secret !== 'string' || a.secret === '')
|
|
@@ -37,16 +30,12 @@ function checkAccount(a: AccountConfig, die: Die): void {
|
|
|
37
30
|
|
|
38
31
|
export const { loadAccounts } = makeAccountStore<AccountConfig>({
|
|
39
32
|
prefix: 'threema',
|
|
40
|
-
file: ACCOUNTS_FILE,
|
|
41
33
|
validate(raw, die) {
|
|
42
|
-
const seenId = new Set<string>();
|
|
43
34
|
const seenGateway = new Set<string>();
|
|
44
35
|
for (const a of raw) {
|
|
45
36
|
checkAccount(a, die);
|
|
46
|
-
if (seenId.has(a.id)) die(`duplicate account id '${a.id}'`);
|
|
47
37
|
if (seenGateway.has(a.gatewayId))
|
|
48
38
|
die(`account '${a.id}' reuses the Gateway ID of another account`);
|
|
49
|
-
seenId.add(a.id);
|
|
50
39
|
seenGateway.add(a.gatewayId);
|
|
51
40
|
}
|
|
52
41
|
},
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
respond,
|
|
5
5
|
type CallMsg,
|
|
6
6
|
} from '@metro-labs/core/stations/station-runtime';
|
|
7
|
-
import
|
|
7
|
+
import { messagingAliases } from '@metro-labs/core/stations/messaging-normalize';
|
|
8
8
|
import { accountFor, accounts, publicKeyFor, targetOf, type Account } from './accounts.js';
|
|
9
9
|
import { hexToBytes, macMatches, open } from './crypto.js';
|
|
10
10
|
import { groupLineOf, type InboundMeta } from './format.js';
|
|
@@ -118,17 +118,7 @@ async function callback(id: string, args: Args): Promise<void> {
|
|
|
118
118
|
respond(id, { result: { ok: true, kind: deliver(acct, meta, decode(plain)) } });
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
export function normalizeThreema(action: string, env: Args): Normalized {
|
|
122
|
-
if (action === 'reply')
|
|
123
|
-
return {
|
|
124
|
-
action: 'send',
|
|
125
|
-
args: { line: env.line, text: env.text, replyTo: env.replyTo, account: env.account },
|
|
126
|
-
};
|
|
127
|
-
if (action === 'unreact') return { action: 'react', args: { ...env, action: 'removed' } };
|
|
128
|
-
return { action, args: env };
|
|
129
|
-
}
|
|
130
|
-
|
|
131
121
|
export const handleCall = makeStation({
|
|
132
122
|
handlers: { accounts: listAccounts, send, react, callback, listMembers },
|
|
133
|
-
normalize:
|
|
123
|
+
normalize: messagingAliases({ action: 'removed' }),
|
|
134
124
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import {
|
|
3
|
-
import { attachmentFailedEvent, attachmentSavedEvent, emitInbound } from '@metro-labs/core/stations/train-events';
|
|
2
|
+
import { emitInbound, reportAttachment, type SavedFile } from '@metro-labs/core/stations/train-events';
|
|
4
3
|
import { assertAttachmentSize, isImageMime, kindOf, saveBufferToCache } from '@metro-labs/core/stations/attachments';
|
|
5
4
|
import { TrainError } from '@metro-labs/core/train-error';
|
|
6
5
|
import type { Account } from './accounts.js';
|
|
@@ -30,12 +29,11 @@ export const encodeFileFor = (group: GroupRef | null, file: FileData): Uint8Arra
|
|
|
30
29
|
|
|
31
30
|
export const fileLabel = (file: OutgoingFile): string => kindOf(file.mime, file.path);
|
|
32
31
|
|
|
33
|
-
async function fetchFile(acct: Account, m: InboundMeta, file: FileData): Promise<
|
|
32
|
+
async function fetchFile(acct: Account, m: InboundMeta, file: FileData): Promise<SavedFile> {
|
|
34
33
|
const sealed = await downloadBlob(acct.cfg, file.blobId);
|
|
35
34
|
const plain = openBlob(sealed, hexToBytes(file.key, 'blob key'));
|
|
36
35
|
if (plain === null) throw new TrainError('threema_bad_blob', 'the file did not decrypt with the key in the message', { retryable: false });
|
|
37
|
-
|
|
38
|
-
return { path: saved.path, bytes: saved.bytes };
|
|
36
|
+
return saveBufferToCache(plain, m.messageId, 0, { mime: file.mime, name: file.name });
|
|
39
37
|
}
|
|
40
38
|
|
|
41
39
|
export function deliverFile(acct: Account, m: InboundMeta, file: FileData, room: Room, sentByUs: (id: string) => boolean): void {
|
|
@@ -44,15 +42,6 @@ export function deliverFile(acct: Account, m: InboundMeta, file: FileData, room:
|
|
|
44
42
|
const forId = String(env.id);
|
|
45
43
|
const payload = { ...(env.payload as Record<string, unknown>), attachments: [{ kind: kindOf(file.mime, file.name), name: file.name, mime: file.mime, ...(file.size === null ? {} : { size: file.size }) }] };
|
|
46
44
|
emitInbound(acct.cfg.id, { ...env, payload });
|
|
47
|
-
const at = { station: 'threema', account: acct.cfg.id, line, forId, index: 0 };
|
|
48
45
|
const kind = kindOf(file.mime, file.name);
|
|
49
|
-
fetchFile(acct, m, file)
|
|
50
|
-
.then(({ path, bytes }) => {
|
|
51
|
-
emit(attachmentSavedEvent({ ...at, saved: { path, mime: file.mime, name: file.name }, extra: { kind, size: bytes } }));
|
|
52
|
-
})
|
|
53
|
-
.catch((err: unknown) => {
|
|
54
|
-
const reason = err instanceof Error ? err.message : String(err);
|
|
55
|
-
process.stderr.write(`threema[${acct.cfg.id}]: could not fetch ${file.name} from ${m.from}: ${reason}\n`);
|
|
56
|
-
emit(attachmentFailedEvent({ ...at, reason, extra: { kind } }));
|
|
57
|
-
});
|
|
46
|
+
reportAttachment(fetchFile(acct, m, file), { station: 'threema', account: acct.cfg.id, line, forId, index: 0 }, { saved: { kind }, failed: { kind } });
|
|
58
47
|
}
|
|
@@ -53,7 +53,6 @@ function base(
|
|
|
53
53
|
): Record<string, unknown> {
|
|
54
54
|
const group = room.group;
|
|
55
55
|
return {
|
|
56
|
-
kind: 'inbound',
|
|
57
56
|
id: mintId(),
|
|
58
57
|
ts: tsOf(m.date),
|
|
59
58
|
station: 'threema',
|
|
@@ -127,18 +126,16 @@ export function emitOutbound(
|
|
|
127
126
|
replyTo?: string,
|
|
128
127
|
): void {
|
|
129
128
|
emit({
|
|
130
|
-
kind: 'outbound',
|
|
131
129
|
id: mintId(),
|
|
132
130
|
ts: new Date().toISOString(),
|
|
133
131
|
station: 'threema',
|
|
134
132
|
line,
|
|
135
|
-
from: selfUri(
|
|
133
|
+
from: selfUri(),
|
|
136
134
|
to: line,
|
|
137
135
|
message_id: messageId,
|
|
138
136
|
text,
|
|
139
137
|
reply_to: replyTo,
|
|
140
138
|
...(replyTo ? { event: { type: 'reply', replyTo } } : {}),
|
|
141
|
-
account: accountId,
|
|
142
139
|
payload: { account: accountId },
|
|
143
140
|
});
|
|
144
141
|
}
|
|
@@ -152,18 +149,16 @@ export function emitOutboundReaction(
|
|
|
152
149
|
removed: boolean,
|
|
153
150
|
): void {
|
|
154
151
|
emit({
|
|
155
|
-
kind: 'outbound',
|
|
156
152
|
id: mintId(),
|
|
157
153
|
ts: new Date().toISOString(),
|
|
158
154
|
station: 'threema',
|
|
159
155
|
line,
|
|
160
|
-
from: selfUri(
|
|
156
|
+
from: selfUri(),
|
|
161
157
|
to: line,
|
|
162
158
|
message_id: messageId,
|
|
163
159
|
text: `[react ${emoji}${removed ? ' (removed)' : ''}]`,
|
|
164
160
|
emoji,
|
|
165
161
|
event: { type: 'react', emoji, targetId, ...(removed ? { removed: true } : {}) },
|
|
166
|
-
account: accountId,
|
|
167
162
|
payload: { account: accountId },
|
|
168
163
|
});
|
|
169
164
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { homedir } from 'node:os';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
1
|
import {
|
|
4
2
|
makeAccountStore,
|
|
5
3
|
resolveAccountId,
|
|
@@ -7,30 +5,17 @@ import {
|
|
|
7
5
|
} from '@metro-labs/core/stations/account-store';
|
|
8
6
|
import type { WhatsAppAccount } from './types.js';
|
|
9
7
|
|
|
10
|
-
const ACCOUNTS_FILE =
|
|
11
|
-
process.env.WHATSAPP_ACCOUNTS_FILE ??
|
|
12
|
-
join(homedir(), '.metro', 'whatsapp-accounts.json');
|
|
13
|
-
|
|
14
8
|
const isJid = (s: string): boolean => s.includes('@');
|
|
15
9
|
|
|
16
|
-
function validateAccount(
|
|
17
|
-
a: WhatsAppAccount,
|
|
18
|
-
seen: Set<string>,
|
|
19
|
-
die: Die,
|
|
20
|
-
): void {
|
|
21
|
-
if (!a.id) die('account missing id');
|
|
10
|
+
function validateAccount(a: WhatsAppAccount, die: Die): void {
|
|
22
11
|
if (!a.phone || typeof a.phone !== 'string')
|
|
23
12
|
die(`account '${a.id}' missing phone`);
|
|
24
|
-
if (seen.has(a.id)) die(`duplicate account id '${a.id}'`);
|
|
25
|
-
seen.add(a.id);
|
|
26
13
|
}
|
|
27
14
|
|
|
28
15
|
export const { loadAccounts } = makeAccountStore<WhatsAppAccount>({
|
|
29
16
|
prefix: 'whatsapp',
|
|
30
|
-
file: ACCOUNTS_FILE,
|
|
31
17
|
validate(raw, die) {
|
|
32
|
-
const
|
|
33
|
-
for (const a of raw) validateAccount(a, seen, die);
|
|
18
|
+
for (const a of raw) validateAccount(a, die);
|
|
34
19
|
},
|
|
35
20
|
});
|
|
36
21
|
|
|
@@ -49,18 +34,12 @@ interface Target {
|
|
|
49
34
|
jid: string;
|
|
50
35
|
}
|
|
51
36
|
|
|
52
|
-
function splitScoped(path: string[]): { accountId: string; jid?: string } {
|
|
53
|
-
const first = path[0];
|
|
54
|
-
if (path.length >= 2 && first !== undefined && !isJid(first))
|
|
55
|
-
return { accountId: first, jid: path[1] };
|
|
56
|
-
return { accountId: 'default', jid: first };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
37
|
export function targetOf(line: string): Target | undefined {
|
|
60
38
|
const prefix = 'metro://whatsapp/';
|
|
61
39
|
if (!line.startsWith(prefix)) return undefined;
|
|
62
40
|
const path = line.slice(prefix.length).split('/').filter(Boolean);
|
|
63
|
-
const
|
|
64
|
-
if (
|
|
41
|
+
const [accountId, jid] = path;
|
|
42
|
+
if (path.length !== 2 || accountId === undefined || jid === undefined) return undefined;
|
|
43
|
+
if (!isJid(jid)) return undefined;
|
|
65
44
|
return { accountId, jid };
|
|
66
45
|
}
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
type CallMsg,
|
|
8
8
|
type StationHandler,
|
|
9
9
|
} from '@metro-labs/core/stations/station-runtime';
|
|
10
|
+
import { messagingAliases } from '@metro-labs/core/stations/messaging-normalize';
|
|
10
11
|
import { accountFor, accounts, targetOf } from './accounts.js';
|
|
11
|
-
import { normalizeWhatsApp } from './normalize.js';
|
|
12
12
|
import { phoneNumberOf, senderLookup } from './resolve.js';
|
|
13
13
|
import { assertImage, fieldsOf, parseProfileChange, type ProfileApplied } from '@metro-labs/core/stations/profile';
|
|
14
14
|
import type { WAClient } from './client.js';
|
|
@@ -50,8 +50,7 @@ async function guard<T>(run: () => Promise<T>): Promise<T> {
|
|
|
50
50
|
|
|
51
51
|
interface WireAttachment {
|
|
52
52
|
kind?: string;
|
|
53
|
-
path
|
|
54
|
-
url?: string;
|
|
53
|
+
path: string;
|
|
55
54
|
mime?: string;
|
|
56
55
|
name?: string;
|
|
57
56
|
}
|
|
@@ -59,7 +58,7 @@ interface WireAttachment {
|
|
|
59
58
|
function attachmentsOf(args: Args): WireAttachment[] {
|
|
60
59
|
const raw = args.attachments;
|
|
61
60
|
if (!Array.isArray(raw)) return [];
|
|
62
|
-
return (raw as WireAttachment[]).filter((a) => Boolean(a.path
|
|
61
|
+
return (raw as Partial<WireAttachment>[]).filter((a): a is WireAttachment => Boolean(a.path));
|
|
63
62
|
}
|
|
64
63
|
|
|
65
64
|
async function sendMediaSet(
|
|
@@ -74,7 +73,7 @@ async function sendMediaSet(
|
|
|
74
73
|
for (let i = 0; i < atts.length; i++) {
|
|
75
74
|
const att = atts[i];
|
|
76
75
|
if (!att) continue;
|
|
77
|
-
const path = att.path
|
|
76
|
+
const path = att.path;
|
|
78
77
|
const kind = att.kind ?? kindOf(att.mime ?? '', path);
|
|
79
78
|
messageId = await guard(() =>
|
|
80
79
|
client.sendMedia(
|
|
@@ -208,6 +207,6 @@ export function makeHandleCall(
|
|
|
208
207
|
set_profile: makeSetProfile(clientFor),
|
|
209
208
|
profile: makeProfile(clientFor),
|
|
210
209
|
},
|
|
211
|
-
normalize:
|
|
210
|
+
normalize: messagingAliases(),
|
|
212
211
|
});
|
|
213
212
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { lineOf } from './accounts.js';
|
|
2
|
-
import { mintId } from '
|
|
2
|
+
import { mintId } from '@metro-labs/core/stations/station-runtime';
|
|
3
3
|
import type { WAMediaRef } from './media.js';
|
|
4
4
|
|
|
5
5
|
export interface InboundMessage {
|
|
@@ -40,7 +40,6 @@ function attachmentView(ref: WAMediaRef): Record<string, unknown> {
|
|
|
40
40
|
|
|
41
41
|
export function envelope(m: InboundMessage): Record<string, unknown> {
|
|
42
42
|
return {
|
|
43
|
-
kind: 'inbound',
|
|
44
43
|
id: mintId(),
|
|
45
44
|
ts: m.date.toISOString(),
|
|
46
45
|
station: 'whatsapp',
|
|
@@ -63,7 +62,6 @@ export function envelope(m: InboundMessage): Record<string, unknown> {
|
|
|
63
62
|
|
|
64
63
|
export function reactionEnvelope(r: ReactionInput): Record<string, unknown> {
|
|
65
64
|
return {
|
|
66
|
-
kind: 'react',
|
|
67
65
|
id: mintId(),
|
|
68
66
|
ts: r.date.toISOString(),
|
|
69
67
|
station: 'whatsapp',
|