omni-notify-mcp 1.3.21 → 1.3.23
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/ui/messaging/telegramError.js +6 -0
- package/dist/ui/server.js +45 -44
- package/package.json +1 -1
package/dist/ui/server.js
CHANGED
|
@@ -13,6 +13,7 @@ import nodemailer from "nodemailer";
|
|
|
13
13
|
import { PinpointSMSVoiceV2Client, SendTextMessageCommand, DescribePhoneNumbersCommand, DescribeVerifiedDestinationNumbersCommand } from "@aws-sdk/client-pinpoint-sms-voice-v2";
|
|
14
14
|
import { tmpdir } from "os";
|
|
15
15
|
import { sendWithRouting, idleReading, formatIdleReading, describePriorityRouting } from "./messaging/notificationEngine.js";
|
|
16
|
+
import { telegramSendErrorHint } from "./messaging/telegramError.js";
|
|
16
17
|
import { z } from "zod";
|
|
17
18
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
18
19
|
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
@@ -152,32 +153,37 @@ const LEGACY_SECRETS_PATH = join(homedir(), ".notify-mcp-secrets");
|
|
|
152
153
|
function e164(s) {
|
|
153
154
|
return String(s ?? "").replace(/[^\d+]/g, "");
|
|
154
155
|
}
|
|
156
|
+
// The ONE list of credential-shaped fields, read by both maskSecrets() (out)
|
|
157
|
+
// and mergePreservingSecrets() (back in). Before story #442 these were two
|
|
158
|
+
// hand-written lists of `if`/`guard` calls kept in sync by hand — that's
|
|
159
|
+
// exactly how ntfy.token got added to a channel (story #427's "brainless
|
|
160
|
+
// config" import) without ever being added to either list, and leaked in the
|
|
161
|
+
// clear over `GET /api/config` for as long as the channel existed. A field
|
|
162
|
+
// belongs here the moment its channel gets one that authenticates outbound
|
|
163
|
+
// (a token, password, or bearer webhook URL) — see
|
|
164
|
+
// tests/smoke.test.mjs's naming-convention test, which independently catches
|
|
165
|
+
// a field like this one being added to defaultConfig() but not to this list.
|
|
166
|
+
const SENSITIVE_FIELDS = [
|
|
167
|
+
["email", "pass"],
|
|
168
|
+
["email", "clientSecret"],
|
|
169
|
+
["email", "refreshToken"],
|
|
170
|
+
["email", "accessToken"],
|
|
171
|
+
["sms", "secretAccessKey"],
|
|
172
|
+
["telegram", "token"],
|
|
173
|
+
["ntfy", "token"],
|
|
174
|
+
["discord", "webhookUrl"],
|
|
175
|
+
["discord", "clientSecret"],
|
|
176
|
+
["slack", "webhookUrl"],
|
|
177
|
+
["slack", "botToken"],
|
|
178
|
+
["slack", "clientSecret"],
|
|
179
|
+
["teams", "webhookUrl"],
|
|
180
|
+
];
|
|
155
181
|
function maskSecrets(config) {
|
|
156
182
|
const c = JSON.parse(JSON.stringify(config));
|
|
157
|
-
|
|
158
|
-
c
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (c.email?.refreshToken)
|
|
162
|
-
c.email.refreshToken = MASKED;
|
|
163
|
-
if (c.email?.accessToken)
|
|
164
|
-
c.email.accessToken = MASKED;
|
|
165
|
-
if (c.sms?.secretAccessKey)
|
|
166
|
-
c.sms.secretAccessKey = MASKED;
|
|
167
|
-
if (c.telegram?.token)
|
|
168
|
-
c.telegram.token = MASKED;
|
|
169
|
-
if (c.discord?.webhookUrl)
|
|
170
|
-
c.discord.webhookUrl = MASKED;
|
|
171
|
-
if (c.discord?.clientSecret)
|
|
172
|
-
c.discord.clientSecret = MASKED;
|
|
173
|
-
if (c.slack?.webhookUrl)
|
|
174
|
-
c.slack.webhookUrl = MASKED;
|
|
175
|
-
if (c.slack?.botToken)
|
|
176
|
-
c.slack.botToken = MASKED;
|
|
177
|
-
if (c.slack?.clientSecret)
|
|
178
|
-
c.slack.clientSecret = MASKED;
|
|
179
|
-
if (c.teams?.webhookUrl)
|
|
180
|
-
c.teams.webhookUrl = MASKED;
|
|
183
|
+
for (const [sec, field] of SENSITIVE_FIELDS) {
|
|
184
|
+
if (c[sec]?.[field])
|
|
185
|
+
c[sec][field] = MASKED;
|
|
186
|
+
}
|
|
181
187
|
delete c.whatsapp;
|
|
182
188
|
return c;
|
|
183
189
|
}
|
|
@@ -192,24 +198,11 @@ function mergePreservingSecrets(existing, update) {
|
|
|
192
198
|
}
|
|
193
199
|
if (typeof update.muteAll === "boolean")
|
|
194
200
|
merged.muteAll = update.muteAll;
|
|
195
|
-
const
|
|
196
|
-
const [sec, field] = path;
|
|
201
|
+
for (const [sec, field] of SENSITIVE_FIELDS) {
|
|
197
202
|
if (update[sec]?.[field] === MASKED) {
|
|
198
203
|
merged[sec][field] = existing[sec]?.[field] ?? "";
|
|
199
204
|
}
|
|
200
|
-
}
|
|
201
|
-
guard(["email", "pass"]);
|
|
202
|
-
guard(["email", "clientSecret"]);
|
|
203
|
-
guard(["email", "refreshToken"]);
|
|
204
|
-
guard(["email", "accessToken"]);
|
|
205
|
-
guard(["sms", "secretAccessKey"]);
|
|
206
|
-
guard(["telegram", "token"]);
|
|
207
|
-
guard(["discord", "webhookUrl"]);
|
|
208
|
-
guard(["discord", "clientSecret"]);
|
|
209
|
-
guard(["slack", "webhookUrl"]);
|
|
210
|
-
guard(["slack", "botToken"]);
|
|
211
|
-
guard(["slack", "clientSecret"]);
|
|
212
|
-
guard(["teams", "webhookUrl"]);
|
|
205
|
+
}
|
|
213
206
|
return merged;
|
|
214
207
|
}
|
|
215
208
|
const app = express();
|
|
@@ -562,7 +555,7 @@ app.post("/api/test/telegram", async (_req, res) => {
|
|
|
562
555
|
if (r.ok)
|
|
563
556
|
sent++;
|
|
564
557
|
else
|
|
565
|
-
errors.push(`${chatId}: ${
|
|
558
|
+
errors.push(`${chatId}: ${await telegramSendErrorHint(r)}`);
|
|
566
559
|
}
|
|
567
560
|
catch (err) {
|
|
568
561
|
errors.push(`${chatId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -1518,10 +1511,11 @@ async function sendNotification(message, priority, client) {
|
|
|
1518
1511
|
headers: { "Content-Type": "application/json" },
|
|
1519
1512
|
body: JSON.stringify(body),
|
|
1520
1513
|
});
|
|
1521
|
-
if (r.ok)
|
|
1514
|
+
if (r.ok) {
|
|
1522
1515
|
sent++;
|
|
1523
|
-
|
|
1524
|
-
|
|
1516
|
+
continue;
|
|
1517
|
+
}
|
|
1518
|
+
errors.push(`${chatId}: ${await telegramSendErrorHint(r)}`);
|
|
1525
1519
|
}
|
|
1526
1520
|
if (sent === 0 && errors.length)
|
|
1527
1521
|
throw new Error(errors.join("; "));
|
|
@@ -2843,6 +2837,10 @@ function createMcpServer(clientId, sessionTag) {
|
|
|
2843
2837
|
? `\n\nReply with: @${sessionTag} <your answer>`
|
|
2844
2838
|
: `\n\nReply to this message with your answer.`;
|
|
2845
2839
|
for (const chatId of cfg.telegram.chatIds) {
|
|
2840
|
+
// A non-2xx Telegram response does NOT reject this fetch — only a
|
|
2841
|
+
// network-level failure does — so this used to silently do nothing
|
|
2842
|
+
// on e.g. a revoked token: no error, no log line, just a question
|
|
2843
|
+
// the user never saw and no clue why. Story #443: check r.ok too.
|
|
2846
2844
|
await fetch(`https://api.telegram.org/bot${cfg.telegram.token}/sendMessage`, {
|
|
2847
2845
|
method: "POST",
|
|
2848
2846
|
headers: { "Content-Type": "application/json" },
|
|
@@ -2850,7 +2848,10 @@ function createMcpServer(clientId, sessionTag) {
|
|
|
2850
2848
|
chat_id: chatId,
|
|
2851
2849
|
text: `${askPrefix} ${question}${replyHint}`,
|
|
2852
2850
|
}),
|
|
2853
|
-
})
|
|
2851
|
+
})
|
|
2852
|
+
.then(async (r) => { if (!r.ok)
|
|
2853
|
+
log("→", "ask:telegram", `ERROR: ${chatId}: ${await telegramSendErrorHint(r)}`, clientId); })
|
|
2854
|
+
.catch((err) => log("→", "ask:telegram", `ERROR: ${chatId}: ${err}`, clientId));
|
|
2854
2855
|
}
|
|
2855
2856
|
}
|
|
2856
2857
|
const email = cfg.email ?? {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omni-notify-mcp",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.23",
|
|
4
4
|
"description": "Reach your AI agents from anywhere — and let them reach you. Every notification channel is set up in one config UI with one-click server launch: desktop, Telegram, Slack, SMS, email, ntfy. Agents then notify you when long work finishes or a decision is needed, ask you a question and wait for your answer, and receive and reply to the messages you send them — with Do Not Disturb and idle detection deciding what actually gets through.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|