fix: publish ntfy as JSON so Chinese titles honor titleTemplate

Non-ASCII rendered titles were silently replaced by a hardcoded ASCII
fallback (fetch headers are Latin-1 only), making titleTemplate appear
to not work. JSON publishing carries title/message as UTF-8 body.
This commit is contained in:
杨豪
2026-09-18 15:43:26 +08:00
parent a4efba3f1f
commit c16ae3a11d
2 changed files with 19 additions and 42 deletions
+1 -1
View File
@@ -41,4 +41,4 @@
本插件只有一个通道:`ntfy`。其他推送通道和对应实现已移除。
NTFY 元数据使用 `Title``Tags``X-Project``X-Session``X-Event``X-Task` headers。含中文的字段会写入 Base64 版本的 headers,完整中文内容始终保留在正文中
NTFY 采用 JSON 发布(POST 到 server 根路径):`title``message` 随 UTF-8 JSON body 传输,中文标题/正文不受 HTTP header 只允许 Latin-1 的限制;`tags`/`priority`/`click`/`icon`/`email` 同样走 JSON 字段
+18 -41
View File
@@ -25,14 +25,6 @@ function eventTagFor(event: NotifyPayload["event"]): string {
}
}
function isAscii(value: string): boolean {
return [...value].every((char) => char.charCodeAt(0) <= 255);
}
function base64(value: string): string {
return Buffer.from(value, "utf8").toString("base64url");
}
function addErrorDetail(result: HttpResult): HttpResult {
if (result.ok) return result;
let detail = result.error ?? `HTTP ${result.status}`;
@@ -66,44 +58,29 @@ export async function sendNtfy(
: `Bearer ${token}`;
}
const project = payload.vars.project ?? "";
const session = payload.vars.session ?? "";
const task = payload.vars.task ?? "";
const asciiSession = isAscii(session) ? session : "";
const asciiTitle = isAscii(payload.title)
? payload.title
: `[${project || "pi"}] pi ${payload.event}${asciiSession ? ` · ${asciiSession}` : ""}`;
// NTFY headers must be ASCII-safe under Node's fetch implementation.
headers.Title = asciiTitle;
headers["X-Event"] = payload.event;
if (isAscii(project)) headers["X-Project"] = project;
if (asciiSession) headers["X-Session"] = asciiSession;
if (isAscii(task) && task) headers["X-Task"] = task;
if (project) headers["X-Project-B64"] = base64(project);
if (session) headers["X-Session-B64"] = base64(session);
if (task) headers["X-Task-B64"] = base64(task);
if (channel.priority != null && channel.priority !== "") {
headers.Priority = String(channel.priority);
}
// Publish as JSON (POST to the server root): title/message travel in a UTF-8
// body, so Chinese titles work — plain-text publishing would force headers to
// be Latin-1 (fetch ByteString) and we had to mangle non-ASCII titles.
const configuredTags =
channel.tags == null
? ""
? []
: Array.isArray(channel.tags)
? channel.tags.join(",")
: String(channel.tags);
const tags = [configuredTags, eventTagFor(payload.event)]
.filter(Boolean)
.join(",");
if (tags) headers.Tags = tags;
if (channel.clickUrl) headers.Click = channel.clickUrl;
if (channel.icon) headers.Icon = channel.icon;
if (channel.email) headers.Email = channel.email;
? channel.tags
: [String(channel.tags)];
const body = JSON.stringify({
topic,
title: payload.title,
message: payload.text,
tags: [...configuredTags, eventTagFor(payload.event)],
...(channel.priority != null && channel.priority !== "" && { priority: channel.priority }),
...(channel.clickUrl && { click: channel.clickUrl }),
...(channel.icon && { icon: channel.icon }),
...(channel.email && { email: channel.email }),
});
const result = await httpRequest(
`${server}/${encodeURIComponent(topic)}`,
{ method: "POST", headers, body: payload.text },
server,
{ method: "POST", headers, body },
timeoutMs,
);
return addErrorDetail(result);