mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-23 00:21:28 +00:00
chore: auto-fix lint & formatting [skip ci]
This commit is contained in:
parent
4edd468ace
commit
2e4cb24c1d
17 changed files with 386 additions and 120 deletions
|
|
@ -3,9 +3,7 @@ import { sendMessage } from "../discord-rest";
|
|||
import { dispatchEvent, isGatewayEnabled } from "../discord";
|
||||
import type { Env, Route } from "../types";
|
||||
|
||||
function mockFetch(
|
||||
handler: (url: string, init?: RequestInit) => Response,
|
||||
): void {
|
||||
function mockFetch(handler: (url: string, init?: RequestInit) => Response): void {
|
||||
globalThis.fetch = (input: RequestInfo | URL, init?: RequestInit): Promise<Response> =>
|
||||
Promise.resolve(handler(String(input), init));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ function createMockKV(): KVNamespace {
|
|||
delete: async (key: string) => {
|
||||
store.delete(key);
|
||||
},
|
||||
list: async () => ({ keys: [...store.keys()].map((k) => ({ name: k })), list_complete: true, cacheStatus: null }),
|
||||
list: async () => ({
|
||||
keys: [...store.keys()].map((k) => ({ name: k })),
|
||||
list_complete: true,
|
||||
cacheStatus: null,
|
||||
}),
|
||||
} as unknown as KVNamespace;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,11 @@ function createMockKV(): KVNamespace {
|
|||
delete: async (key: string) => {
|
||||
store.delete(key);
|
||||
},
|
||||
list: async () => ({ keys: [...store.keys()].map((k) => ({ name: k })), list_complete: true, cacheStatus: null }),
|
||||
list: async () => ({
|
||||
keys: [...store.keys()].map((k) => ({ name: k })),
|
||||
list_complete: true,
|
||||
cacheStatus: null,
|
||||
}),
|
||||
} as unknown as KVNamespace;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -144,7 +144,16 @@ export function createActionRoutes(): Hono<{ Bindings: Env }> {
|
|||
if (!userId) return c.json({ error: "Invalid or expired token" }, 401);
|
||||
|
||||
const body = await readJson(c);
|
||||
const reactions = ["+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", "eyes"] as const;
|
||||
const reactions = [
|
||||
"+1",
|
||||
"-1",
|
||||
"laugh",
|
||||
"confused",
|
||||
"heart",
|
||||
"hooray",
|
||||
"rocket",
|
||||
"eyes",
|
||||
] as const;
|
||||
if (
|
||||
!body ||
|
||||
!isNonEmptyString(body.owner) ||
|
||||
|
|
@ -164,14 +173,7 @@ export function createActionRoutes(): Hono<{ Bindings: Env }> {
|
|||
repo: body.repo,
|
||||
issue_number: body.issueNumber,
|
||||
content: body.reaction as
|
||||
| "+1"
|
||||
| "-1"
|
||||
| "laugh"
|
||||
| "confused"
|
||||
| "heart"
|
||||
| "hooray"
|
||||
| "rocket"
|
||||
| "eyes",
|
||||
"+1" | "-1" | "laugh" | "confused" | "heart" | "hooray" | "rocket" | "eyes",
|
||||
});
|
||||
} catch (err) {
|
||||
log.error({ err }, "Failed to create reaction");
|
||||
|
|
|
|||
|
|
@ -7,13 +7,7 @@ import {
|
|||
clearAdminCookie,
|
||||
type AdminSession,
|
||||
} from "./admin-session";
|
||||
import {
|
||||
loadGroups,
|
||||
saveGroups,
|
||||
resolveScope,
|
||||
hasAnyAccess,
|
||||
type AccessScope,
|
||||
} from "./groups";
|
||||
import { loadGroups, saveGroups, resolveScope, hasAnyAccess, type AccessScope } from "./groups";
|
||||
import { getSendLog } from "./send-log";
|
||||
import { log } from "./log";
|
||||
|
||||
|
|
@ -40,9 +34,7 @@ function deepEqual(a: unknown, b: unknown): boolean {
|
|||
const ak = Object.keys(ao);
|
||||
const bk = Object.keys(bo);
|
||||
if (ak.length !== bk.length) return false;
|
||||
return ak.every(
|
||||
(k) => Object.prototype.hasOwnProperty.call(bo, k) && deepEqual(ao[k], bo[k]),
|
||||
);
|
||||
return ak.every((k) => Object.prototype.hasOwnProperty.call(bo, k) && deepEqual(ao[k], bo[k]));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -314,7 +306,10 @@ export function createAdminRoutes(): Hono<{ Bindings: Env }> {
|
|||
const groupId = c.req.param("groupId");
|
||||
const access = groupAccess(s, groupId);
|
||||
if (!access.ok) {
|
||||
return c.json({ error: access.status === 404 ? "Group not found" : "Forbidden" }, access.status);
|
||||
return c.json(
|
||||
{ error: access.status === 404 ? "Group not found" : "Forbidden" },
|
||||
access.status,
|
||||
);
|
||||
}
|
||||
const all = await loadRoutes(c.env.KV);
|
||||
return c.json({ group: access.group, routes: all.filter((r) => r.groupId === groupId) });
|
||||
|
|
@ -326,7 +321,10 @@ export function createAdminRoutes(): Hono<{ Bindings: Env }> {
|
|||
const groupId = c.req.param("groupId");
|
||||
const access = groupAccess(s, groupId);
|
||||
if (!access.ok) {
|
||||
return c.json({ error: access.status === 404 ? "Group not found" : "Forbidden" }, access.status);
|
||||
return c.json(
|
||||
{ error: access.status === 404 ? "Group not found" : "Forbidden" },
|
||||
access.status,
|
||||
);
|
||||
}
|
||||
|
||||
let body: unknown;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,11 @@ const APP_COMMANDS = [
|
|||
type: COMMAND_TYPE.CHAT_INPUT,
|
||||
description: "GitHub 集成",
|
||||
options: [
|
||||
{ type: OPTION_TYPE.SUB_COMMAND, name: "login", description: "绑定你的 GitHub 账号以用本人身份评论" },
|
||||
{
|
||||
type: OPTION_TYPE.SUB_COMMAND,
|
||||
name: "login",
|
||||
description: "绑定你的 GitHub 账号以用本人身份评论",
|
||||
},
|
||||
{ type: OPTION_TYPE.SUB_COMMAND, name: "logout", description: "解绑你的 GitHub 账号" },
|
||||
{
|
||||
type: OPTION_TYPE.SUB_COMMAND_GROUP,
|
||||
|
|
@ -62,7 +66,12 @@ const APP_COMMANDS = [
|
|||
name: "add",
|
||||
description: "在 issue/PR 下新增评论",
|
||||
options: [
|
||||
{ type: OPTION_TYPE.STRING, name: "link", description: "issue/PR 链接", required: true },
|
||||
{
|
||||
type: OPTION_TYPE.STRING,
|
||||
name: "link",
|
||||
description: "issue/PR 链接",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -70,7 +79,12 @@ const APP_COMMANDS = [
|
|||
name: "edit",
|
||||
description: "编辑一条评论",
|
||||
options: [
|
||||
{ type: OPTION_TYPE.STRING, name: "link", description: "评论链接(含 #issuecomment-)", required: true },
|
||||
{
|
||||
type: OPTION_TYPE.STRING,
|
||||
name: "link",
|
||||
description: "评论链接(含 #issuecomment-)",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -78,7 +92,12 @@ const APP_COMMANDS = [
|
|||
name: "del",
|
||||
description: "删除一条评论",
|
||||
options: [
|
||||
{ type: OPTION_TYPE.STRING, name: "link", description: "评论链接(含 #issuecomment-)", required: true },
|
||||
{
|
||||
type: OPTION_TYPE.STRING,
|
||||
name: "link",
|
||||
description: "评论链接(含 #issuecomment-)",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
@ -186,7 +205,10 @@ export class DiscordGateway {
|
|||
});
|
||||
|
||||
ws.addEventListener("error", (event) => {
|
||||
log.error({ err: String((event as ErrorEvent).message ?? event) }, "Gateway WebSocket error");
|
||||
log.error(
|
||||
{ err: String((event as ErrorEvent).message ?? event) },
|
||||
"Gateway WebSocket error",
|
||||
);
|
||||
});
|
||||
} catch (err) {
|
||||
this.connecting = false;
|
||||
|
|
@ -196,10 +218,7 @@ export class DiscordGateway {
|
|||
}
|
||||
|
||||
private scheduleReconnect(): void {
|
||||
const delay = Math.min(
|
||||
BASE_RECONNECT_DELAY * 2 ** this.reconnectAttempt,
|
||||
MAX_RECONNECT_DELAY,
|
||||
);
|
||||
const delay = Math.min(BASE_RECONNECT_DELAY * 2 ** this.reconnectAttempt, MAX_RECONNECT_DELAY);
|
||||
this.reconnectAttempt++;
|
||||
this.state.storage.setAlarm(Date.now() + delay);
|
||||
}
|
||||
|
|
@ -277,7 +296,10 @@ export class DiscordGateway {
|
|||
|
||||
private identify(): void {
|
||||
if (!this.socket || this.socket.readyState !== WebSocket.OPEN || !this.token) {
|
||||
log.warn({ hasSocket: !!this.socket, readyState: this.socket?.readyState ?? null }, "Cannot identify");
|
||||
log.warn(
|
||||
{ hasSocket: !!this.socket, readyState: this.socket?.readyState ?? null },
|
||||
"Cannot identify",
|
||||
);
|
||||
return;
|
||||
}
|
||||
log.info("Sending IDENTIFY");
|
||||
|
|
@ -374,7 +396,14 @@ export class DiscordGateway {
|
|||
custom_id?: string;
|
||||
message?: { id?: string };
|
||||
};
|
||||
return this.handleButton(id, token, userId, interaction.channel_id, data.message?.id, data.custom_id);
|
||||
return this.handleButton(
|
||||
id,
|
||||
token,
|
||||
userId,
|
||||
interaction.channel_id,
|
||||
data.message?.id,
|
||||
data.custom_id,
|
||||
);
|
||||
}
|
||||
|
||||
if (interaction.type === INTERACTION_TYPE.COMMAND) {
|
||||
|
|
@ -382,14 +411,29 @@ export class DiscordGateway {
|
|||
name?: string;
|
||||
type?: number;
|
||||
target_id?: string;
|
||||
options?: Array<{ name: string; options?: Array<{ name: string; value?: string; options?: Array<{ name: string; value?: string }> }> }>;
|
||||
resolved?: { messages?: Record<string, { embeds?: Array<{ url?: string }>; content?: string }> };
|
||||
options?: Array<{
|
||||
name: string;
|
||||
options?: Array<{
|
||||
name: string;
|
||||
value?: string;
|
||||
options?: Array<{ name: string; value?: string }>;
|
||||
}>;
|
||||
}>;
|
||||
resolved?: {
|
||||
messages?: Record<string, { embeds?: Array<{ url?: string }>; content?: string }>;
|
||||
};
|
||||
};
|
||||
|
||||
// Right-click (message context-menu) commands.
|
||||
if (data.type === COMMAND_TYPE.MESSAGE) {
|
||||
const op =
|
||||
data.name === MSG_CMD_ADD ? "add" : data.name === MSG_CMD_EDIT ? "edit" : data.name === MSG_CMD_DEL ? "del" : null;
|
||||
data.name === MSG_CMD_ADD
|
||||
? "add"
|
||||
: data.name === MSG_CMD_EDIT
|
||||
? "edit"
|
||||
: data.name === MSG_CMD_DEL
|
||||
? "del"
|
||||
: null;
|
||||
if (!op) return;
|
||||
const target = data.target_id ? data.resolved?.messages?.[data.target_id] : undefined;
|
||||
const source = target?.embeds?.[0]?.url ?? target?.content ?? "";
|
||||
|
|
@ -403,7 +447,14 @@ export class DiscordGateway {
|
|||
if (top?.name === "logout") return this.cmdLogout(id, token, userId);
|
||||
if (top?.name === "comment") {
|
||||
const sub = top.options?.[0];
|
||||
const op = sub?.name === "add" ? "add" : sub?.name === "edit" ? "edit" : sub?.name === "del" ? "del" : null;
|
||||
const op =
|
||||
sub?.name === "add"
|
||||
? "add"
|
||||
: sub?.name === "edit"
|
||||
? "edit"
|
||||
: sub?.name === "del"
|
||||
? "del"
|
||||
: null;
|
||||
if (!op) return;
|
||||
const link = sub?.options?.find((o) => o.name === "link")?.value ?? "";
|
||||
return this.commentOp(id, token, userId, op, link);
|
||||
|
|
@ -518,7 +569,8 @@ export class DiscordGateway {
|
|||
private async cmdLogin(id: string, token: string, userId: string | null): Promise<void> {
|
||||
if (!userId) return this.respond(id, token, "无法识别你的 Discord 账号。");
|
||||
const clientId = this.env.GITHUB_CLIENT_ID;
|
||||
if (!clientId) return this.respond(id, token, "服务器未配置 GitHub OAuth(GITHUB_CLIENT_ID)。");
|
||||
if (!clientId)
|
||||
return this.respond(id, token, "服务器未配置 GitHub OAuth(GITHUB_CLIENT_ID)。");
|
||||
|
||||
const state = crypto.randomUUID().replace(/-/g, "");
|
||||
await this.env.KV.put(
|
||||
|
|
@ -543,7 +595,8 @@ export class DiscordGateway {
|
|||
/** Map a GitHub op error code to a user-facing (Chinese) message. */
|
||||
private errText(err: unknown): string {
|
||||
const t = err instanceof Error ? err.message : String(err);
|
||||
if (t === "GITHUB_TOKEN_EXPIRED") return "GitHub 授权已过期或无效,请重新使用 `/gh login` 绑定。";
|
||||
if (t === "GITHUB_TOKEN_EXPIRED")
|
||||
return "GitHub 授权已过期或无效,请重新使用 `/gh login` 绑定。";
|
||||
if (t === "GITHUB_FORBIDDEN") return "GitHub 拒绝了此操作:你的账号没有权限修改/删除这条评论。";
|
||||
if (t === "GITHUB_NOT_FOUND") return "找不到目标(可能评论已被删除或仓库不可访问)。";
|
||||
return `操作失败:${t}`;
|
||||
|
|
@ -568,14 +621,28 @@ export class DiscordGateway {
|
|||
|
||||
if (op === "add") {
|
||||
const m = source.match(GITHUB_ISSUE_RE);
|
||||
if (!m) return this.respond(id, token, "找不到 issue / PR 链接(右键 issue/PR 通知,或用 link 传入链接)。");
|
||||
return this.openCommentModal(id, token, `${MODAL_ADD}${m[1]}|${m[2]}|${m[3]}`, `评论 ${m[1]}/${m[2]}#${m[3]}`);
|
||||
if (!m)
|
||||
return this.respond(
|
||||
id,
|
||||
token,
|
||||
"找不到 issue / PR 链接(右键 issue/PR 通知,或用 link 传入链接)。",
|
||||
);
|
||||
return this.openCommentModal(
|
||||
id,
|
||||
token,
|
||||
`${MODAL_ADD}${m[1]}|${m[2]}|${m[3]}`,
|
||||
`评论 ${m[1]}/${m[2]}#${m[3]}`,
|
||||
);
|
||||
}
|
||||
|
||||
// edit / del both need a specific comment id.
|
||||
const m = source.match(GITHUB_COMMENT_RE);
|
||||
if (!m) {
|
||||
return this.respond(id, token, "找不到评论链接(需含 `#issuecomment-...`,请右键某条评论通知,或粘贴评论链接)。");
|
||||
return this.respond(
|
||||
id,
|
||||
token,
|
||||
"找不到评论链接(需含 `#issuecomment-...`,请右键某条评论通知,或粘贴评论链接)。",
|
||||
);
|
||||
}
|
||||
const [, owner, repo, commentId] = m;
|
||||
|
||||
|
|
@ -591,7 +658,13 @@ export class DiscordGateway {
|
|||
// edit: fetch current body to prefill the modal.
|
||||
let prefill = "";
|
||||
try {
|
||||
const { body } = await getCommentAsUser(this.env.KV, githubUserId, owner!, repo!, Number(commentId));
|
||||
const { body } = await getCommentAsUser(
|
||||
this.env.KV,
|
||||
githubUserId,
|
||||
owner!,
|
||||
repo!,
|
||||
Number(commentId),
|
||||
);
|
||||
prefill = body;
|
||||
} catch (err) {
|
||||
return this.respond(id, token, this.errText(err));
|
||||
|
|
@ -662,7 +735,8 @@ export class DiscordGateway {
|
|||
// ghc|add|owner|repo|issueNumber
|
||||
if (customId.startsWith(MODAL_ADD)) {
|
||||
const [owner, repo, number] = customId.slice(MODAL_ADD.length).split("|");
|
||||
if (!owner || !repo || !number) return this.respond(id, token, "内部错误:无法解析目标 issue。");
|
||||
if (!owner || !repo || !number)
|
||||
return this.respond(id, token, "内部错误:无法解析目标 issue。");
|
||||
try {
|
||||
const { htmlUrl, login } = await commentAsUser(
|
||||
this.env.KV,
|
||||
|
|
@ -681,7 +755,8 @@ export class DiscordGateway {
|
|||
// ghc|edit|owner|repo|commentId
|
||||
if (customId.startsWith(MODAL_EDIT)) {
|
||||
const [owner, repo, commentId] = customId.slice(MODAL_EDIT.length).split("|");
|
||||
if (!owner || !repo || !commentId) return this.respond(id, token, "内部错误:无法解析目标评论。");
|
||||
if (!owner || !repo || !commentId)
|
||||
return this.respond(id, token, "内部错误:无法解析目标评论。");
|
||||
try {
|
||||
const { htmlUrl } = await editCommentAsUser(
|
||||
this.env.KV,
|
||||
|
|
|
|||
123
src/formatter.ts
123
src/formatter.ts
|
|
@ -67,7 +67,11 @@ const WORKFLOW_CONCLUSION_EMOJI: Record<string, string> = {
|
|||
|
||||
type T = (key: string, params?: Record<string, string | number>) => string;
|
||||
|
||||
export function formatEvent(route: Route, event: WebhookEvent, tr?: Translations): FormattedMessage {
|
||||
export function formatEvent(
|
||||
route: Route,
|
||||
event: WebhookEvent,
|
||||
tr?: Translations,
|
||||
): FormattedMessage {
|
||||
const { event: eventType, payload } = event;
|
||||
const repo = (payload.repository as { full_name?: string })?.full_name;
|
||||
const sender = (payload.sender as { login?: string })?.login;
|
||||
|
|
@ -163,7 +167,9 @@ function formatPush(
|
|||
descriptionParts.push(t("events.push.branch_created"));
|
||||
}
|
||||
|
||||
descriptionParts.push(t("events.push.commits_pushed", { count, s: count !== 1 ? "s" : "", ref: ref ?? "" }));
|
||||
descriptionParts.push(
|
||||
t("events.push.commits_pushed", { count, s: count !== 1 ? "s" : "", ref: ref ?? "" }),
|
||||
);
|
||||
|
||||
if (compareUrl) {
|
||||
descriptionParts.push(t("events.push.view_comparison", { url: compareUrl }));
|
||||
|
|
@ -219,7 +225,11 @@ function formatPush(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.push.title", { count, s: count !== 1 ? "s" : "", repo: repo ?? t("common.repository") }),
|
||||
title: t("events.push.title", {
|
||||
count,
|
||||
s: count !== 1 ? "s" : "",
|
||||
repo: repo ?? t("common.repository"),
|
||||
}),
|
||||
url: compareUrl,
|
||||
color: GITHUB_COLORS.push,
|
||||
description: descriptionParts.join("\n"),
|
||||
|
|
@ -331,7 +341,11 @@ function formatPullRequest(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.pr.title", { repo: repo ?? t("common.repository"), number: pr.number ?? "?", title: pr.title ?? t("common.untitled") }),
|
||||
title: t("events.pr.title", {
|
||||
repo: repo ?? t("common.repository"),
|
||||
number: pr.number ?? "?",
|
||||
title: pr.title ?? t("common.untitled"),
|
||||
}),
|
||||
url: pr.html_url,
|
||||
color: GITHUB_COLORS[colorKey],
|
||||
description: descriptionParts.join("\n"),
|
||||
|
|
@ -412,7 +426,11 @@ function formatIssues(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.issues.title", { repo: repo ?? t("common.repository"), number: issue.number ?? "?", title: issue.title ?? t("common.untitled") }),
|
||||
title: t("events.issues.title", {
|
||||
repo: repo ?? t("common.repository"),
|
||||
number: issue.number ?? "?",
|
||||
title: issue.title ?? t("common.untitled"),
|
||||
}),
|
||||
url: issue.html_url,
|
||||
color: GITHUB_COLORS[colorKey],
|
||||
description: descriptionParts.join("\n"),
|
||||
|
|
@ -449,7 +467,11 @@ function formatIssueComment(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.issue_comment.comment_on", { repo: repo ?? t("common.repository"), number: issue.number ?? "?", title: issue.title ?? t("common.untitled") }),
|
||||
title: t("events.issue_comment.comment_on", {
|
||||
repo: repo ?? t("common.repository"),
|
||||
number: issue.number ?? "?",
|
||||
title: issue.title ?? t("common.untitled"),
|
||||
}),
|
||||
url: comment.html_url ?? issue.html_url,
|
||||
color: GITHUB_COLORS.issue_comment,
|
||||
description: `${t("events.issue_comment.action_comment", { action: al })}\n\n> ${commentBody}${truncated ? "..." : ""}`,
|
||||
|
|
@ -563,7 +585,13 @@ function formatRelease(
|
|||
const emoji = action === "deleted" ? "🗑️" : isPrerelease ? "⚠️" : "🚀";
|
||||
|
||||
const descriptionParts: string[] = [];
|
||||
descriptionParts.push(t("events.release.action_release", { emoji, action: al, tag: release.tag_name ?? t("common.unknown") }));
|
||||
descriptionParts.push(
|
||||
t("events.release.action_release", {
|
||||
emoji,
|
||||
action: al,
|
||||
tag: release.tag_name ?? t("common.unknown"),
|
||||
}),
|
||||
);
|
||||
|
||||
if (release.body) {
|
||||
const truncated = release.body.slice(0, 300);
|
||||
|
|
@ -574,7 +602,10 @@ function formatRelease(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.release.title", { name: release.name ?? release.tag_name ?? "Release", repo: repo ?? t("common.repository") }),
|
||||
title: t("events.release.title", {
|
||||
name: release.name ?? release.tag_name ?? "Release",
|
||||
repo: repo ?? t("common.repository"),
|
||||
}),
|
||||
url: release.html_url,
|
||||
color: GITHUB_COLORS[colorKey],
|
||||
description: descriptionParts.join("\n"),
|
||||
|
|
@ -622,7 +653,12 @@ function formatCreate(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.create.title", { emoji, type: refType, ref, repo: repo ?? t("common.repository") }),
|
||||
title: t("events.create.title", {
|
||||
emoji,
|
||||
type: refType,
|
||||
ref,
|
||||
repo: repo ?? t("common.repository"),
|
||||
}),
|
||||
color: GITHUB_COLORS.create,
|
||||
fields,
|
||||
footer: { text: t("common.footer", { repo: repo ?? t("common.github") }) },
|
||||
|
|
@ -647,7 +683,12 @@ function formatDelete(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.delete.title", { emoji, type: refType, ref, repo: repo ?? t("common.repository") }),
|
||||
title: t("events.delete.title", {
|
||||
emoji,
|
||||
type: refType,
|
||||
ref,
|
||||
repo: repo ?? t("common.repository"),
|
||||
}),
|
||||
color: GITHUB_COLORS.delete,
|
||||
footer: { text: t("common.footer", { repo: repo ?? t("common.github") }) },
|
||||
timestamp: new Date().toISOString(),
|
||||
|
|
@ -693,7 +734,10 @@ function formatFork(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.fork.title", { repo: repo ?? t("common.repository"), forkee: forkee?.full_name ?? t("common.unknown") }),
|
||||
title: t("events.fork.title", {
|
||||
repo: repo ?? t("common.repository"),
|
||||
forkee: forkee?.full_name ?? t("common.unknown"),
|
||||
}),
|
||||
url: forkee?.html_url ?? repoUrl,
|
||||
color: GITHUB_COLORS.fork,
|
||||
footer: { text: t("common.footer", { repo: repo ?? t("common.github") }) },
|
||||
|
|
@ -797,7 +841,11 @@ function formatPullRequestReview(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.pr_review.title", { repo: repo ?? t("common.repository"), number: pr.number ?? "?", title: pr.title ?? t("common.untitled") }),
|
||||
title: t("events.pr_review.title", {
|
||||
repo: repo ?? t("common.repository"),
|
||||
number: pr.number ?? "?",
|
||||
title: pr.title ?? t("common.untitled"),
|
||||
}),
|
||||
url: review.html_url,
|
||||
color: GITHUB_COLORS[colorKey],
|
||||
description: descriptionParts.join("\n"),
|
||||
|
|
@ -833,7 +881,10 @@ function formatPullRequestReviewComment(
|
|||
const fields: Array<{ name: string; value: string; inline?: boolean }> = [];
|
||||
|
||||
if (comment.path) {
|
||||
const loc = comment.position != null ? t("events.pr_review_comment.line", { position: comment.position }) : "";
|
||||
const loc =
|
||||
comment.position != null
|
||||
? t("events.pr_review_comment.line", { position: comment.position })
|
||||
: "";
|
||||
fields.push({
|
||||
name: t("fields.file"),
|
||||
value: `\`${comment.path}\`${loc}`,
|
||||
|
|
@ -845,7 +896,10 @@ function formatPullRequestReviewComment(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.pr_review_comment.title", { repo: repo ?? t("common.repository"), number: pr.number ?? "?" }),
|
||||
title: t("events.pr_review_comment.title", {
|
||||
repo: repo ?? t("common.repository"),
|
||||
number: pr.number ?? "?",
|
||||
}),
|
||||
url: comment.html_url,
|
||||
color: GITHUB_COLORS.pull_request_review_commented,
|
||||
description: `${t("events.pr_review_comment.action_inline", { action: al })}\n\n> ${commentBody}${truncated ? "..." : ""}`,
|
||||
|
|
@ -889,7 +943,10 @@ function formatCommitComment(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.commit_comment.title", { sha: shortSha, repo: repo ?? t("common.repository") }),
|
||||
title: t("events.commit_comment.title", {
|
||||
sha: shortSha,
|
||||
repo: repo ?? t("common.repository"),
|
||||
}),
|
||||
url: comment.html_url,
|
||||
color: GITHUB_COLORS.commit_comment,
|
||||
description: `${t("events.commit_comment.action_comment", { action: al })}\n\n> ${commentBody}${truncated ? "..." : ""}`,
|
||||
|
|
@ -1062,7 +1119,11 @@ function formatLabel(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.label.title", { emoji, action: al, name: label.name ?? t("common.unknown") }),
|
||||
title: t("events.label.title", {
|
||||
emoji,
|
||||
action: al,
|
||||
name: label.name ?? t("common.unknown"),
|
||||
}),
|
||||
color: GITHUB_COLORS.label,
|
||||
fields: fields.length > 0 ? fields : undefined,
|
||||
footer: { text: t("common.footer", { repo: repo ?? t("common.github") }) },
|
||||
|
|
@ -1133,7 +1194,11 @@ function formatMilestone(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.milestone.title", { emoji: stateEmoji, action: al, title: milestone.title ?? t("common.unknown") }),
|
||||
title: t("events.milestone.title", {
|
||||
emoji: stateEmoji,
|
||||
action: al,
|
||||
title: milestone.title ?? t("common.unknown"),
|
||||
}),
|
||||
url: milestone.html_url,
|
||||
color:
|
||||
milestone.state === "closed"
|
||||
|
|
@ -1179,13 +1244,20 @@ function formatDiscussion(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.discussion.title", { emoji: stateEmoji, number: discussion.number ?? "?", title: discussion.title ?? t("common.untitled") }),
|
||||
title: t("events.discussion.title", {
|
||||
emoji: stateEmoji,
|
||||
number: discussion.number ?? "?",
|
||||
title: discussion.title ?? t("common.untitled"),
|
||||
}),
|
||||
url: discussion.html_url,
|
||||
color:
|
||||
action === "answered"
|
||||
? GITHUB_COLORS.discussion_answered
|
||||
: GITHUB_COLORS.discussion_created,
|
||||
description: t("events.discussion.action_discussion", { action: al, category: category ? ` in **${category}**` : "" }),
|
||||
description: t("events.discussion.action_discussion", {
|
||||
action: al,
|
||||
category: category ? ` in **${category}**` : "",
|
||||
}),
|
||||
footer: { text: t("common.footer", { repo: repo ?? t("common.github") }) },
|
||||
timestamp: new Date().toISOString(),
|
||||
},
|
||||
|
|
@ -1217,7 +1289,10 @@ function formatDiscussionComment(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.discussion_comment.comment_on", { number: discussion.number ?? "?", title: discussion.title ?? t("common.untitled") }),
|
||||
title: t("events.discussion_comment.comment_on", {
|
||||
number: discussion.number ?? "?",
|
||||
title: discussion.title ?? t("common.untitled"),
|
||||
}),
|
||||
url: comment.html_url,
|
||||
color: GITHUB_COLORS.discussion_comment,
|
||||
description: `${t("events.discussion_comment.action_comment", { action: al })}\n\n> ${commentBody}${truncated ? "..." : ""}`,
|
||||
|
|
@ -1243,7 +1318,8 @@ function formatRepository(
|
|||
|
||||
if (action === "renamed") {
|
||||
const changes = payload.changes as { name?: { from?: string } } | undefined;
|
||||
const newName = (payload.repository as { full_name?: string })?.full_name ?? t("common.unknown");
|
||||
const newName =
|
||||
(payload.repository as { full_name?: string })?.full_name ?? t("common.unknown");
|
||||
fields.push({
|
||||
name: t("fields.renamed"),
|
||||
value: changes?.name?.from ? `${changes.name.from} → ${newName}` : newName,
|
||||
|
|
@ -1492,7 +1568,10 @@ function formatGeneric(
|
|||
embeds: [
|
||||
{
|
||||
author,
|
||||
title: t("events.generic.title", { event: eventType, action: payload.action ? `: ${payload.action}` : "" }),
|
||||
title: t("events.generic.title", {
|
||||
event: eventType,
|
||||
action: payload.action ? `: ${payload.action}` : "",
|
||||
}),
|
||||
color: GITHUB_COLORS.default,
|
||||
footer: { text: t("common.footer", { repo: repo ?? t("common.github") }) },
|
||||
timestamp: new Date().toISOString(),
|
||||
|
|
|
|||
|
|
@ -53,6 +53,9 @@ export function t(
|
|||
translations?: Translations,
|
||||
): string {
|
||||
const dict = translations ?? en;
|
||||
const raw = getByPath(dict as Record<string, unknown>, key) ?? getByPath(en as Record<string, unknown>, key) ?? key;
|
||||
const raw =
|
||||
getByPath(dict as Record<string, unknown>, key) ??
|
||||
getByPath(en as Record<string, unknown>, key) ??
|
||||
key;
|
||||
return params ? interpolate(raw, params) : raw;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,7 @@ export async function recordSend(kv: KVNamespace, record: SendRecord): Promise<v
|
|||
export async function getSendLog(kv: KVNamespace, limit = 50): Promise<SendRecord[]> {
|
||||
try {
|
||||
const list = await kv.list({ prefix: KEY_PREFIX, limit: MAX_READ });
|
||||
const records = await Promise.all(
|
||||
list.keys.map((k) => kv.get<SendRecord>(k.name, "json")),
|
||||
);
|
||||
const records = await Promise.all(list.keys.map((k) => kv.get<SendRecord>(k.name, "json")));
|
||||
return records
|
||||
.filter((r): r is SendRecord => r != null)
|
||||
.sort((a, b) => b.ts - a.ts)
|
||||
|
|
|
|||
|
|
@ -87,9 +87,6 @@ export async function getDiscordLink(
|
|||
return await kv.get(`discord-link:${discordUserId}`, "text");
|
||||
}
|
||||
|
||||
export async function removeDiscordLink(
|
||||
kv: KVNamespace,
|
||||
discordUserId: string,
|
||||
): Promise<void> {
|
||||
export async function removeDiscordLink(kv: KVNamespace, discordUserId: string): Promise<void> {
|
||||
await kv.delete(`discord-link:${discordUserId}`);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue