This commit is contained in:
parent
34802ac712
commit
fc87bc74f8
7 changed files with 40 additions and 12 deletions
|
|
@ -18,6 +18,7 @@ PORT=3000
|
|||
DATA_DIR=/app/data
|
||||
JOJO_WEBHOOK_SECRET=...
|
||||
GITHUB_WEBHOOK_SECRET=...
|
||||
DISCORD_OUTPUT_ENABLED=false
|
||||
DISCORD_WEBHOOK_URL=
|
||||
DISCORD_NOTIFY_EVENTS=push,pull_request,release
|
||||
FEED_SOURCES_PATH=./feed-sources.json
|
||||
|
|
@ -25,9 +26,9 @@ PATCHBAY_FLOW_DISPATCH_URL=
|
|||
PATCHBAY_FLOW_DISPATCH_SECRET=
|
||||
```
|
||||
|
||||
Discord notifications are optional. When `DISCORD_WEBHOOK_URL` is unset, the
|
||||
service skips Discord output. `DISCORD_NOTIFY_EVENTS` is a comma-separated
|
||||
allow list and defaults to `push,pull_request,release`.
|
||||
Discord notifications are off by default. Set `DISCORD_OUTPUT_ENABLED=true`
|
||||
and `DISCORD_WEBHOOK_URL` to send Discord output. `DISCORD_NOTIFY_EVENTS` is a
|
||||
comma-separated allow list and defaults to `push,pull_request,release`.
|
||||
|
||||
## Development
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ type DiscordPayload = {
|
|||
type FetchLike = (url: string, init: RequestInit) => Promise<Response>;
|
||||
|
||||
export type DiscordConfig = {
|
||||
enabled: boolean;
|
||||
webhookUrl?: string;
|
||||
notifyEvents: Set<string>;
|
||||
};
|
||||
|
|
@ -38,7 +39,13 @@ export type DiscordNotification = {
|
|||
|
||||
const defaultNotifyEvents = ["push", "pull_request", "release"];
|
||||
|
||||
function parseEnabled(value?: string): boolean {
|
||||
const normalized = value?.trim().toLowerCase();
|
||||
return normalized === "true" || normalized === "1" || normalized === "yes" || normalized === "on";
|
||||
}
|
||||
|
||||
export function parseDiscordConfig(input: {
|
||||
enabled?: string;
|
||||
webhookUrl?: string;
|
||||
notifyEvents?: string;
|
||||
}): DiscordConfig {
|
||||
|
|
@ -50,6 +57,7 @@ export function parseDiscordConfig(input: {
|
|||
);
|
||||
|
||||
return {
|
||||
enabled: parseEnabled(input.enabled),
|
||||
webhookUrl: input.webhookUrl?.trim() || undefined,
|
||||
notifyEvents,
|
||||
};
|
||||
|
|
@ -188,7 +196,7 @@ export async function notifyDiscord(
|
|||
fetchImpl: FetchLike = fetch,
|
||||
): Promise<void> {
|
||||
const eventName = notification.signal?.event ?? notification.event?.event;
|
||||
if (!config.webhookUrl || !eventName || !config.notifyEvents.has(eventName)) {
|
||||
if (!config.enabled || !config.webhookUrl || !eventName || !config.notifyEvents.has(eventName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ export async function pollFeedSource(input: {
|
|||
flowDispatches += 1;
|
||||
}
|
||||
}
|
||||
await notifyDiscord(input.discord ?? { notifyEvents: new Set() }, { signal, job });
|
||||
await notifyDiscord(input.discord ?? { enabled: false, notifyEvents: new Set() }, { signal, job });
|
||||
signals.push(signal);
|
||||
console.log(JSON.stringify({
|
||||
type: "feed.accepted",
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@ if (import.meta.main) {
|
|||
dataDir: process.env.DATA_DIR ?? "./data",
|
||||
adminToken: process.env.PATCHBAY_ADMIN_TOKEN,
|
||||
discord: parseDiscordConfig({
|
||||
enabled: process.env.DISCORD_OUTPUT_ENABLED,
|
||||
webhookUrl: process.env.DISCORD_WEBHOOK_URL,
|
||||
notifyEvents: process.env.DISCORD_NOTIFY_EVENTS,
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -55,12 +55,20 @@ const feedSignal: FeedSignal = {
|
|||
describe("discord notifications", () => {
|
||||
test("parses default notify events", () => {
|
||||
const config = parseDiscordConfig({});
|
||||
expect(config.enabled).toBe(false);
|
||||
expect(config.notifyEvents.has("push")).toBe(true);
|
||||
expect(config.notifyEvents.has("pull_request")).toBe(true);
|
||||
expect(config.notifyEvents.has("release")).toBe(true);
|
||||
expect(config.notifyEvents.has("ping")).toBe(false);
|
||||
});
|
||||
|
||||
test("parses explicit enable flag", () => {
|
||||
expect(parseDiscordConfig({ enabled: "true" }).enabled).toBe(true);
|
||||
expect(parseDiscordConfig({ enabled: "1" }).enabled).toBe(true);
|
||||
expect(parseDiscordConfig({ enabled: "yes" }).enabled).toBe(true);
|
||||
expect(parseDiscordConfig({ enabled: "false" }).enabled).toBe(false);
|
||||
});
|
||||
|
||||
test("builds readable push embeds", () => {
|
||||
const payload = buildDiscordPayload({
|
||||
event: pushEvent,
|
||||
|
|
@ -92,7 +100,16 @@ describe("discord notifications", () => {
|
|||
|
||||
test("does nothing without a webhook URL", async () => {
|
||||
let calls = 0;
|
||||
await notifyDiscord(parseDiscordConfig({}), { event: pushEvent }, async () => {
|
||||
await notifyDiscord(parseDiscordConfig({ enabled: "true" }), { event: pushEvent }, async () => {
|
||||
calls += 1;
|
||||
return new Response(null, { status: 204 });
|
||||
});
|
||||
expect(calls).toBe(0);
|
||||
});
|
||||
|
||||
test("does nothing when Discord output is disabled", async () => {
|
||||
let calls = 0;
|
||||
await notifyDiscord(parseDiscordConfig({ webhookUrl: "https://discord.example/webhook", notifyEvents: "push" }), { event: pushEvent }, async () => {
|
||||
calls += 1;
|
||||
return new Response(null, { status: 204 });
|
||||
});
|
||||
|
|
@ -101,7 +118,7 @@ describe("discord notifications", () => {
|
|||
|
||||
test("skips unconfigured events", async () => {
|
||||
let calls = 0;
|
||||
await notifyDiscord(parseDiscordConfig({ webhookUrl: "https://discord.example/webhook", notifyEvents: "release" }), { event: pushEvent }, async () => {
|
||||
await notifyDiscord(parseDiscordConfig({ enabled: "true", webhookUrl: "https://discord.example/webhook", notifyEvents: "release" }), { event: pushEvent }, async () => {
|
||||
calls += 1;
|
||||
return new Response(null, { status: 204 });
|
||||
});
|
||||
|
|
@ -110,7 +127,7 @@ describe("discord notifications", () => {
|
|||
|
||||
test("posts configured events", async () => {
|
||||
let body = "";
|
||||
await notifyDiscord(parseDiscordConfig({ webhookUrl: "https://discord.example/webhook", notifyEvents: "push" }), { event: pushEvent }, async (_url, init) => {
|
||||
await notifyDiscord(parseDiscordConfig({ enabled: "true", webhookUrl: "https://discord.example/webhook", notifyEvents: "push" }), { event: pushEvent }, async (_url, init) => {
|
||||
body = String(init?.body);
|
||||
return new Response(null, { status: 204 });
|
||||
});
|
||||
|
|
@ -119,7 +136,7 @@ describe("discord notifications", () => {
|
|||
});
|
||||
|
||||
test("throws on Discord failure", async () => {
|
||||
await expect(notifyDiscord(parseDiscordConfig({ webhookUrl: "https://discord.example/webhook" }), { event: pushEvent }, async () => {
|
||||
await expect(notifyDiscord(parseDiscordConfig({ enabled: "true", webhookUrl: "https://discord.example/webhook" }), { event: pushEvent }, async () => {
|
||||
return new Response("bad", { status: 500 });
|
||||
})).rejects.toThrow("Discord webhook returned 500");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ describe("feed watcher", () => {
|
|||
const sourcesPath = join(dataDir, "sources.json");
|
||||
await writeFile(sourcesPath, JSON.stringify({ sources: [source] }), "utf8");
|
||||
|
||||
await pollFeedsOnce({ dataDir, sourcesPath, discord: { webhookUrl: "https://discord.example/webhook", notifyEvents: new Set(["push"]) } }, async () => {
|
||||
await pollFeedsOnce({ dataDir, sourcesPath, discord: { enabled: true, webhookUrl: "https://discord.example/webhook", notifyEvents: new Set(["push"]) } }, async () => {
|
||||
return new Response(atom, { status: 200 });
|
||||
});
|
||||
|
||||
|
|
@ -128,7 +128,7 @@ describe("feed watcher", () => {
|
|||
}), "utf8");
|
||||
|
||||
let feedCalls = 0;
|
||||
await pollFeedsOnce({ dataDir, sourcesPath, discord: { notifyEvents: new Set(["release"]) } }, async () => {
|
||||
await pollFeedsOnce({ dataDir, sourcesPath, discord: { enabled: false, notifyEvents: new Set(["release"]) } }, async () => {
|
||||
feedCalls += 1;
|
||||
return new Response(rss, { status: 200 });
|
||||
});
|
||||
|
|
@ -170,7 +170,7 @@ describe("feed watcher", () => {
|
|||
await pollFeedsOnce({
|
||||
dataDir,
|
||||
sourcesPath,
|
||||
discord: { notifyEvents: new Set(["release"]) },
|
||||
discord: { enabled: false, notifyEvents: new Set(["release"]) },
|
||||
flowDispatch: {
|
||||
env: {
|
||||
FLOW_URL: "https://flow.example/events",
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ describe("server", () => {
|
|||
jojoSecret: "jojo",
|
||||
dataDir,
|
||||
discord: {
|
||||
enabled: true,
|
||||
webhookUrl: "https://discord.example/webhook",
|
||||
notifyEvents: new Set(["push"]),
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue