fix(render): link title head to the event object URL

This commit is contained in:
RhenCloud 2026-08-15 18:25:23 +08:00
parent 2f8a87048e
commit 7ac7bb7958
No known key found for this signature in database
GPG key ID: A574A617378C4E0B
4 changed files with 13 additions and 31 deletions

View file

@ -6,7 +6,6 @@ import {
MAX_FIELD_VALUE,
MAX_FOOTER,
MAX_TITLE,
repoUrlFromMessage,
splitMessageTitle,
} from "../../formatters/helpers";
@ -27,10 +26,9 @@ export function renderNeutralMessage(message: NeutralMessage): FormattedMessage
: undefined;
// Discord embed titles can only be linked as a whole, so only the repo head
// goes into the title (linked to the repository); the `: subject` text is
// rendered as the first line of the description, unlinked.
// goes into the title (linked to the event's object URL); the `: subject`
// text is rendered as the first line of the description, unlinked.
const { head, subject } = splitMessageTitle(message.title);
const repoUrl = repoUrlFromMessage(message.url);
const rawDescription = subject
? message.description
? `${subject}\n${message.description}`
@ -48,7 +46,7 @@ export function renderNeutralMessage(message: NeutralMessage): FormattedMessage
embeds: [
{
title: cap(head, MAX_TITLE),
url: subject ? repoUrl : message.url,
url: message.url,
color: message.color,
description: cap(rawDescription, MAX_DESCRIPTION) || undefined,
author: message.author

View file

@ -1,5 +1,5 @@
import type { NeutralMessage } from "../../types";
import { repoUrlFromMessage, splitMessageTitle } from "../../formatters/helpers";
import { splitMessageTitle } from "../../formatters/helpers";
/** Telegram caps a single message at 4096 characters (HTML entities included). */
const MAX_TEXT = 4096;
@ -64,11 +64,11 @@ export function renderNeutralMessage(message: NeutralMessage): string {
const parts: string[] = [];
// HTML allows partial links, so keep the `{repo}{#number}: {subject}` line
// intact with only the repo head linked (the subject stays plain text).
// intact with only the repo head linked to the event's object URL (the
// subject stays plain text).
const { head, subject } = splitMessageTitle(message.title);
const repoUrl = repoUrlFromMessage(message.url);
const title = subject
? `<b>${repoUrl ? `<a href="${esc(repoUrl)}">${mdToHtml(head)}</a>` : mdToHtml(head)}: ${mdToHtml(subject)}</b>`
? `<b>${message.url ? `<a href="${esc(message.url)}">${mdToHtml(head)}</a>` : mdToHtml(head)}: ${mdToHtml(subject)}</b>`
: message.url
? `<b><a href="${esc(message.url)}">${mdToHtml(message.title)}</a></b>`
: `<b>${mdToHtml(message.title)}</b>`;

View file

@ -31,23 +31,6 @@ export function splitMessageTitle(title: string): TitleParts {
return { head: title.slice(0, idx), subject: title.slice(idx + 2) };
}
/**
* Repository URL derived from an event URL (origin + owner + repo). Returns
* undefined when there is no URL to derive from callers then render the
* title without a link.
*/
export function repoUrlFromMessage(url: string | undefined): string | undefined {
if (!url) return undefined;
try {
const parsed = new URL(url);
const segments = parsed.pathname.split("/").filter(Boolean);
if (segments.length >= 2) return `${parsed.origin}/${segments[0]}/${segments[1]}`;
} catch {
// not a parseable URL — no link
}
return undefined;
}
export function buildMessage(
partial: Omit<Partial<NeutralMessage>, "title"> & { title: string },
t: T,