From 1e37124f164063b29ee284ad478985751a8b552c Mon Sep 17 00:00:00 2001 From: Cyprian Gburek Date: Sat, 3 Oct 2026 13:33:02 +0200 Subject: [PATCH] chore: Relay Discord bez tokenu GitHub --- CONTRIBUTING.md | 2 +- discord-relay/README.md | 18 +++-------- discord-relay/src/format.js | 18 ++++------- discord-relay/src/index.js | 60 +++--------------------------------- discord-relay/wrangler.jsonc | 3 +- 5 files changed, 17 insertions(+), 84 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e5f86d6..7c10d9c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -83,7 +83,7 @@ Konfiguracja: - Lista obserwowanych workflow jest w `discord-notify.yml` każdego repozytorium (`workflows:`). Nowy workflow trzeba tam dopisać po nazwie (pole `name:`), na `main` i `develop`. `workflow_run` działa tylko z kopii na `main`. - Webhook Discorda to sekret repozytorium `DISCORD_WEBHOOK_URL` w każdym z czterech repozytoriów. Sekret organizacji nie wystarczy: na darmowym planie nie widzą go repozytoria prywatne. -- Worker: `discord-relay/wrangler.jsonc` (projekt, pola, przekazywanie issue i PR), sekrety `DISCORD_WEBHOOK_URL`, `GITHUB_WEBHOOK_SECRET` i opcjonalny `GITHUB_TOKEN`. Webhook organizacji wskazuje na `https://tuttitrip-hooks.gburek.app/github`. +- Worker: `discord-relay/wrangler.jsonc` (projekt, pola, przekazywanie issue i PR), sekrety `DISCORD_WEBHOOK_URL` i `GITHUB_WEBHOOK_SECRET` (bez tokenu GitHub, więc wiadomość o elemencie projektu ma link zamiast tytułu). Webhook organizacji wskazuje na `https://tuttitrip-hooks.gburek.app/github`. Zmiana (rotacja) webhooka Discorda: utwórz nowy webhook w ustawieniach kanału i usuń stary, potem z pliku z nowym URL-em: diff --git a/discord-relay/README.md b/discord-relay/README.md index fcd0fe3..20118a6 100644 --- a/discord-relay/README.md +++ b/discord-relay/README.md @@ -35,20 +35,12 @@ treści `{"posted": true, "discord": 204}` albo powód pominięcia, więc w | --- | --- | | `DISCORD_WEBHOOK_URL` | URL webhooka kanału Discord (ten sam co w sekretach repozytoriów) | | `GITHUB_WEBHOOK_SECRET` | losowy sekret, ten sam w ustawieniach webhooka organizacji | -| `GITHUB_TOKEN` (opcjonalny) | fine-grained PAT tylko do odczytu, opis niżej | -Bez `GITHUB_TOKEN` wiadomość ma typ elementu i link do niego w projekcie, ale -bez tytułu. Z tokenem Worker pobiera z GraphQL tytuł, numer, repozytorium i -bieżący Status. Token: GitHub, Settings, Developer settings, Fine-grained -tokens, Generate new token. Resource owner `HackYeah-TuttiTripTeam`, -Repository access "All repositories", uprawnienia repozytorium Issues: Read, -Pull requests: Read (Metadata: Read dochodzi samo), uprawnienia organizacji -Projects: Read. Potem: - -```bash -cd discord-relay -npx wrangler@4.147.0 secret put GITHUB_TOKEN # wklej token w prompt -``` +Worker nie używa tokenu GitHub. Zdarzenie elementu projektu nie zawiera +tytułu, więc wiadomość podaje typ (Issue, Pull request, Szkic), kto zmienił, +starą i nową wartość pola oraz link do elementu w projekcie. Nowe issue trafiają +do projektu przez formularze zgłoszeń (`projects:`) i wbudowany auto-add, nie +przez ten Worker. ## Wdrożenie diff --git a/discord-relay/src/format.js b/discord-relay/src/format.js index 3322742..58f3c8e 100644 --- a/discord-relay/src/format.js +++ b/discord-relay/src/format.js @@ -67,10 +67,10 @@ function message(embed) { } /** - * projects_v2_item -> Discord message, or { skip: reason }. - * `info` (optional, from GraphQL): { title, url, number, repo, state, status }. + * projects_v2_item -> Discord message, or { skip: reason }. The payload has no + * item title (that would need a GitHub token), so the message links to the item. */ -export function projectItemMessage(payload, env, info = {}) { +export function projectItemMessage(payload, env) { const item = payload.projects_v2_item const action = payload.action const projectUrl = env.PROJECT_URL @@ -123,21 +123,15 @@ export function projectItemMessage(payload, env, info = {}) { return { skip: `action ${action} not posted` } // reordered } - const title = info.title - ? plain(info.number ? `#${info.number} ${info.title}` : info.title) - : `${kind} (element ${item.id})` const fields = [ { name: 'Typ', value: kind, inline: true }, - info.repo && { name: 'Repozytorium', value: esc(info.repo, 100), inline: true }, { name: 'Kto', value: userLink(payload.sender), inline: true }, - info.status && - !description.includes('**Status**') && { name: 'Status', value: esc(info.status, 100), inline: true }, - ].filter(Boolean) + ] return message({ author: { name: 'Projekt TuttiTrip', url: projectUrl }, - title, - url: action === 'deleted' ? info.url || projectUrl : info.url || paneUrl, + title: `${kind} w projekcie (element ${item.id})`, + url: action === 'deleted' ? projectUrl : paneUrl, description: cut(description, 4000), color, fields, diff --git a/discord-relay/src/index.js b/discord-relay/src/index.js index 366f48b..68003ee 100644 --- a/discord-relay/src/index.js +++ b/discord-relay/src/index.js @@ -4,8 +4,9 @@ // closed / merged issues and PRs. Workflow runs are not handled here (GitHub // Actions posts them itself, see .github/workflows/discord-notify.yml). // -// Secrets: DISCORD_WEBHOOK_URL, GITHUB_WEBHOOK_SECRET, optional GITHUB_TOKEN -// (fine-grained PAT, read-only: titles and current Status of project items). +// Secrets: DISCORD_WEBHOOK_URL, GITHUB_WEBHOOK_SECRET. No GitHub token: messages +// use only what the webhook payload carries (a project item event has no title, +// so the message links to the item in the project). import { issueMessage, projectItemMessage, @@ -40,59 +41,6 @@ export async function verifySignature(secret, body, header) { return crypto.subtle.verify('HMAC', key, signature, body) } -const ITEM_QUERY = `query($ids: [ID!]!) { - nodes(ids: $ids) { - __typename - ... on ProjectV2Item { - fieldValueByName(name: "Status") { ... on ProjectV2ItemFieldSingleSelectValue { name } } - } - ... on DraftIssue { title } - ... on Issue { title number url repository { name } } - ... on PullRequest { title number url repository { name } } - } -}` - -/** Title, link and current Status of a project item. Empty without a token or on any error. */ -async function itemInfo(env, item) { - if (!env.GITHUB_TOKEN) return {} - try { - const res = await fetch('https://api.github.com/graphql', { - method: 'POST', - headers: { - authorization: `Bearer ${env.GITHUB_TOKEN}`, - 'content-type': 'application/json', - 'user-agent': 'tuttitrip-discord-relay', - }, - body: JSON.stringify({ - query: ITEM_QUERY, - variables: { ids: [item.node_id, item.content_node_id].filter(Boolean) }, - }), - signal: AbortSignal.timeout(5000), - }) - if (!res.ok) { - console.log(JSON.stringify({ graphql: res.status })) - return {} - } - // Deleted items/drafts come back as null nodes with NOT_FOUND errors. - const nodes = (await res.json()).data?.nodes ?? [] - const info = {} - for (const node of nodes) { - if (!node) continue - if (node.__typename === 'ProjectV2Item') info.status = node.fieldValueByName?.name - else { - info.title = node.title - info.number = node.number - info.url = node.url - info.repo = node.repository?.name - } - } - return info - } catch (e) { - console.log(JSON.stringify({ graphql: e.name })) - return {} - } -} - async function postToDiscord(env, message) { const post = () => fetch(env.DISCORD_WEBHOOK_URL, { @@ -118,7 +66,7 @@ async function buildMessage(event, payload, env) { const item = payload.projects_v2_item if (!anyProject && item.project_node_id !== env.PROJECT_NODE_ID) return { skip: 'other project' } if (payload.action === 'reordered') return { skip: 'reordered' } - return projectItemMessage(payload, env, await itemInfo(env, item)) + return projectItemMessage(payload, env) } case 'projects_v2': if (!anyProject && payload.projects_v2?.node_id !== env.PROJECT_NODE_ID) return { skip: 'other project' } diff --git a/discord-relay/wrangler.jsonc b/discord-relay/wrangler.jsonc index 30f8e30..ae37d98 100644 --- a/discord-relay/wrangler.jsonc +++ b/discord-relay/wrangler.jsonc @@ -24,6 +24,5 @@ // Also post opened/closed/merged issues and PRs (needs those events on the org webhook). "FORWARD_ISSUES_PRS": "true" } - // Secrets (wrangler secret put): DISCORD_WEBHOOK_URL, GITHUB_WEBHOOK_SECRET, - // optional GITHUB_TOKEN (fine-grained PAT, read-only, for item titles). + // Secrets (wrangler secret put): DISCORD_WEBHOOK_URL, GITHUB_WEBHOOK_SECRET. }