@femtomc/mu-server 26.2.30 → 26.2.31
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/control_plane.d.ts +2 -0
- package/dist/control_plane.js +57 -1
- package/package.json +5 -5
package/dist/control_plane.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export declare const ENV_VARS: {
|
|
|
8
8
|
};
|
|
9
9
|
readonly telegram: {
|
|
10
10
|
readonly webhookSecret: "MU_TELEGRAM_WEBHOOK_SECRET";
|
|
11
|
+
readonly botToken: "MU_TELEGRAM_BOT_TOKEN";
|
|
11
12
|
readonly botUsername: "MU_TELEGRAM_BOT_USERNAME";
|
|
12
13
|
readonly tenantId: "MU_TELEGRAM_TENANT_ID";
|
|
13
14
|
};
|
|
@@ -30,6 +31,7 @@ type DetectedAdapter = {
|
|
|
30
31
|
} | {
|
|
31
32
|
name: "telegram";
|
|
32
33
|
webhookSecret: string;
|
|
34
|
+
botToken: string | null;
|
|
33
35
|
botUsername: string | null;
|
|
34
36
|
tenantId: string | null;
|
|
35
37
|
};
|
package/dist/control_plane.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { ControlPlaneRuntime, ControlPlaneCommandPipeline, ControlPlaneOutbox, SlackControlPlaneAdapter, DiscordControlPlaneAdapter, TelegramControlPlaneAdapter, getControlPlanePaths, } from "@femtomc/mu-control-plane";
|
|
1
|
+
import { ControlPlaneRuntime, ControlPlaneCommandPipeline, ControlPlaneOutbox, ControlPlaneOutboxDispatcher, SlackControlPlaneAdapter, DiscordControlPlaneAdapter, TelegramControlPlaneAdapter, getControlPlanePaths, } from "@femtomc/mu-control-plane";
|
|
2
2
|
export const ENV_VARS = {
|
|
3
3
|
slack: { signingSecret: "MU_SLACK_SIGNING_SECRET" },
|
|
4
4
|
discord: { signingSecret: "MU_DISCORD_SIGNING_SECRET" },
|
|
5
5
|
telegram: {
|
|
6
6
|
webhookSecret: "MU_TELEGRAM_WEBHOOK_SECRET",
|
|
7
|
+
botToken: "MU_TELEGRAM_BOT_TOKEN",
|
|
7
8
|
botUsername: "MU_TELEGRAM_BOT_USERNAME",
|
|
8
9
|
tenantId: "MU_TELEGRAM_TENANT_ID",
|
|
9
10
|
},
|
|
@@ -28,6 +29,7 @@ export function detectAdapters(env) {
|
|
|
28
29
|
adapters.push({
|
|
29
30
|
name: "telegram",
|
|
30
31
|
webhookSecret: telegramSecret,
|
|
32
|
+
botToken: env[ENV_VARS.telegram.botToken] ?? null,
|
|
31
33
|
botUsername: env[ENV_VARS.telegram.botUsername] ?? null,
|
|
32
34
|
tenantId: env[ENV_VARS.telegram.tenantId] ?? null,
|
|
33
35
|
});
|
|
@@ -47,6 +49,8 @@ export async function bootstrapControlPlane(opts) {
|
|
|
47
49
|
await pipeline.start();
|
|
48
50
|
const outbox = new ControlPlaneOutbox(paths.outboxPath);
|
|
49
51
|
await outbox.load();
|
|
52
|
+
// Collect bot tokens for delivery.
|
|
53
|
+
const telegramBotTokens = new Map();
|
|
50
54
|
const adapterMap = new Map();
|
|
51
55
|
for (const d of detected) {
|
|
52
56
|
const route = ROUTE_MAP[d.name];
|
|
@@ -74,10 +78,61 @@ export async function bootstrapControlPlane(opts) {
|
|
|
74
78
|
botUsername: d.botUsername ?? undefined,
|
|
75
79
|
tenantId: d.tenantId ?? undefined,
|
|
76
80
|
});
|
|
81
|
+
if (d.botToken) {
|
|
82
|
+
telegramBotTokens.set(d.tenantId ?? "default", d.botToken);
|
|
83
|
+
}
|
|
77
84
|
break;
|
|
78
85
|
}
|
|
79
86
|
adapterMap.set(route, { adapter, info: { name: d.name, route } });
|
|
80
87
|
}
|
|
88
|
+
// Build delivery handler that routes by channel.
|
|
89
|
+
const deliver = async (record) => {
|
|
90
|
+
const { envelope } = record;
|
|
91
|
+
if (envelope.channel === "telegram") {
|
|
92
|
+
const tenantId = envelope.channel_tenant_id ?? "default";
|
|
93
|
+
const botToken = telegramBotTokens.get(tenantId) ?? telegramBotTokens.values().next().value;
|
|
94
|
+
if (!botToken) {
|
|
95
|
+
return { kind: "retry", error: "MU_TELEGRAM_BOT_TOKEN not configured" };
|
|
96
|
+
}
|
|
97
|
+
const res = await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`, {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: { "Content-Type": "application/json" },
|
|
100
|
+
body: JSON.stringify({
|
|
101
|
+
chat_id: envelope.channel_conversation_id,
|
|
102
|
+
text: envelope.body,
|
|
103
|
+
}),
|
|
104
|
+
});
|
|
105
|
+
if (res.ok) {
|
|
106
|
+
return { kind: "delivered" };
|
|
107
|
+
}
|
|
108
|
+
if (res.status === 429 || res.status >= 500) {
|
|
109
|
+
const retryAfter = res.headers.get("retry-after");
|
|
110
|
+
const retryDelayMs = retryAfter ? Number.parseInt(retryAfter, 10) * 1000 : undefined;
|
|
111
|
+
return {
|
|
112
|
+
kind: "retry",
|
|
113
|
+
error: `telegram sendMessage ${res.status}: ${await res.text().catch(() => "")}`,
|
|
114
|
+
retryDelayMs: retryDelayMs && Number.isFinite(retryDelayMs) ? retryDelayMs : undefined,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
// Permanent error — let it dead-letter after max attempts.
|
|
118
|
+
return {
|
|
119
|
+
kind: "retry",
|
|
120
|
+
error: `telegram sendMessage ${res.status}: ${await res.text().catch(() => "")}`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// Other channels: treat as delivered (no-op for now).
|
|
124
|
+
return undefined;
|
|
125
|
+
};
|
|
126
|
+
const dispatcher = new ControlPlaneOutboxDispatcher({ outbox, deliver });
|
|
127
|
+
// Drain loop: check for pending outbox records every 2 seconds.
|
|
128
|
+
const drainInterval = setInterval(async () => {
|
|
129
|
+
try {
|
|
130
|
+
await dispatcher.drainDue();
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
// Swallow errors — the dispatcher already handles retries internally.
|
|
134
|
+
}
|
|
135
|
+
}, 2_000);
|
|
81
136
|
return {
|
|
82
137
|
activeAdapters: [...adapterMap.values()].map((v) => v.info),
|
|
83
138
|
async handleWebhook(path, req) {
|
|
@@ -88,6 +143,7 @@ export async function bootstrapControlPlane(opts) {
|
|
|
88
143
|
return result.response;
|
|
89
144
|
},
|
|
90
145
|
async stop() {
|
|
146
|
+
clearInterval(drainInterval);
|
|
91
147
|
await pipeline.stop();
|
|
92
148
|
},
|
|
93
149
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@femtomc/mu-server",
|
|
3
|
-
"version": "26.2.
|
|
3
|
+
"version": "26.2.31",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -23,9 +23,9 @@
|
|
|
23
23
|
"start": "bun run dist/cli.js"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@femtomc/mu-core": "26.2.
|
|
27
|
-
"@femtomc/mu-issue": "26.2.
|
|
28
|
-
"@femtomc/mu-forum": "26.2.
|
|
29
|
-
"@femtomc/mu-control-plane": "26.2.
|
|
26
|
+
"@femtomc/mu-core": "26.2.31",
|
|
27
|
+
"@femtomc/mu-issue": "26.2.31",
|
|
28
|
+
"@femtomc/mu-forum": "26.2.31",
|
|
29
|
+
"@femtomc/mu-control-plane": "26.2.31"
|
|
30
30
|
}
|
|
31
31
|
}
|