This commit is contained in:
2026-05-14 21:09:18 +01:00
parent 3c08023cb9
commit 7026eacb12
2 changed files with 99 additions and 18 deletions
+2 -2
View File
@@ -101,8 +101,8 @@ loads the updated listener:
pm2 reload tssbot-webhook --update-env
```
The webhook listener reads `.env` on startup. To send a Discord notification
whenever the listener starts or restarts, set:
The webhook listener reads `.env` on startup. To send Discord notifications for
listener restarts and GitHub push deploy start/success/failure events, set:
```sh
DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/...
+97 -16
View File
@@ -52,6 +52,28 @@ function json(res, status, body) {
res.end(JSON.stringify(body))
}
function safeJsonParse(rawBody) {
try {
return JSON.parse(rawBody.toString('utf8'))
} catch {
return null
}
}
function shortSha(value) {
return value ? String(value).slice(0, 7) : 'unknown'
}
function branchName(ref) {
return String(ref || '').replace(/^refs\/heads\//, '') || 'unknown'
}
function truncate(value, maxLength = 900) {
const text = String(value || '')
if (text.length <= maxLength) return text
return `${text.slice(0, maxLength - 3)}...`
}
function verifySignature(rawBody, signature) {
if (!SECRET) return true
if (!signature || !signature.startsWith('sha256=')) return false
@@ -169,24 +191,77 @@ function postDiscordWebhook(payload) {
function notifyDiscordRestart() {
const startedAt = new Date()
postDiscordWebhook({
username: 'tssbot webhook',
embeds: [
{
title: 'Webhook listener restarted',
color: 0x00f2ff,
fields: [
{ name: 'Host', value: os.hostname(), inline: true },
{ name: 'Port', value: String(PORT), inline: true },
{ name: 'Restart targets', value: RESTART_TARGETS.join(', ') || 'none', inline: false },
],
timestamp: startedAt.toISOString(),
},
sendDiscordEmbed({
title: 'Webhook listener restarted',
color: 0x00f2ff,
fields: [
{ name: 'Host', value: os.hostname(), inline: true },
{ name: 'Port', value: String(PORT), inline: true },
{ name: 'Restart targets', value: RESTART_TARGETS.join(', ') || 'none', inline: false },
],
timestamp: startedAt.toISOString(),
})
}
async function deploy() {
function sendDiscordEmbed(embed) {
return postDiscordWebhook({
username: 'tssbot webhook',
embeds: [embed],
})
}
function deployFields(push) {
const headCommit = push?.head_commit
const fields = [
{ name: 'Host', value: os.hostname(), inline: true },
{ name: 'Branch', value: branchName(push?.ref), inline: true },
{ name: 'Commit', value: shortSha(push?.after || headCommit?.id), inline: true },
{ name: 'Restart targets', value: RESTART_TARGETS.join(', ') || 'none', inline: false },
]
if (push?.sender?.login) {
fields.push({ name: 'Sender', value: push.sender.login, inline: true })
}
if (headCommit?.message) {
fields.push({ name: 'Message', value: truncate(headCommit.message), inline: false })
}
return fields
}
async function notifyDeployStarted(push) {
await sendDiscordEmbed({
title: 'GitHub push deploy started',
color: 0xf4ee3e,
fields: deployFields(push),
timestamp: new Date().toISOString(),
})
}
async function notifyDeployCompleted(push) {
await sendDiscordEmbed({
title: 'GitHub push deploy completed',
color: 0x00f2ff,
fields: deployFields(push),
timestamp: new Date().toISOString(),
})
}
async function notifyDeployFailed(push, error) {
await sendDiscordEmbed({
title: 'GitHub push deploy failed',
color: 0xe82517,
fields: [
...deployFields(push),
{ name: 'Error', value: truncate(error?.message || error), inline: false },
],
timestamp: new Date().toISOString(),
})
}
async function deploy(push) {
await notifyDeployStarted(push)
await run('git', ['pull', '--ff-only'])
await ensureBuildDependencies()
await run('npm', ['run', 'build'])
@@ -194,6 +269,8 @@ async function deploy() {
for (const target of RESTART_TARGETS) {
await run('pm2', ['reload', target, '--update-env'])
}
await notifyDeployCompleted(push)
}
http
@@ -212,6 +289,7 @@ http
req.on('data', (chunk) => chunks.push(chunk))
req.on('end', () => {
const rawBody = Buffer.concat(chunks)
const push = safeJsonParse(rawBody)
if (!verifySignature(rawBody, req.headers['x-hub-signature-256'])) {
json(res, 401, { error: 'Invalid signature' })
@@ -231,9 +309,12 @@ http
deploying = true
json(res, 202, { accepted: true, restart_targets: RESTART_TARGETS })
deploy()
deploy(push)
.then(() => console.log('GitHub push deploy completed'))
.catch((error) => console.error('GitHub push deploy failed:', error))
.catch((error) => {
console.error('GitHub push deploy failed:', error)
notifyDeployFailed(push, error)
})
.finally(() => {
deploying = false
})