mirror of
https://github.com/SethCohen/github-releases-to-discord
synced 2026-09-07 20:03:23 +00:00
177 lines
5.3 KiB
JavaScript
177 lines
5.3 KiB
JavaScript
import core from '@actions/core';
|
|
import github from '@actions/github';
|
|
import fetch from 'node-fetch';
|
|
|
|
/**
|
|
* Stylizes a markdown body into an appropriate embed message style.
|
|
* Remove Carriage Return character to reduce size
|
|
* Remove HTML comments (commonly added by 'Generate release notes' button)
|
|
* Better URL linking for common Github links: PRs, Issues, Compare
|
|
* Redundant whitespace and newlines removed, keeping at max 2 to provide space between paragraphs
|
|
* Trim leading/trailing whitespace
|
|
* If reduce_headings:
|
|
* H3s converted to bold and underlined
|
|
* H2s converted to bold
|
|
* @param {string} description
|
|
*/
|
|
const formatDescription = (description) => {
|
|
let edit = description
|
|
.replace(/\r/g, '')
|
|
.replace(/<!--.*?-->/gs, '')
|
|
.replace(
|
|
new RegExp(
|
|
"https://github.com/(.+)/(.+)/(issues|pull|commit|compare)/(\\S+)",
|
|
"g"
|
|
),
|
|
(match, user, repo, type, id) => {
|
|
return `[${getTypePrefix(type) + id}](${match})`
|
|
}
|
|
)
|
|
.replace(/\n\s*\n/g, (ws) => {
|
|
const nlCount = (ws.match(/\n/g) || []).length
|
|
return nlCount >= 2 ? '\n\n' : '\n'
|
|
})
|
|
.trim()
|
|
|
|
if (core.getBooleanInput('reduce_headings')) {
|
|
edit = edit
|
|
.replace(/^###\s+(.+)$/gm, '**__$1__**')
|
|
.replace(/^##\s+(.+)$/gm, '**$1**')
|
|
}
|
|
|
|
return edit
|
|
}
|
|
|
|
/**
|
|
* Get a prefix to use for Github link display
|
|
* @param {'issues' | 'pull' | 'commit' | 'compare'} type
|
|
*/
|
|
function getTypePrefix (type) {
|
|
switch (type) {
|
|
case 'issues':
|
|
return 'Issue #'
|
|
case 'pull':
|
|
return 'PR #'
|
|
case 'commit':
|
|
return 'Commit #'
|
|
case 'compare':
|
|
return ''
|
|
default:
|
|
return '#'
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the max description length if set to a valid number,
|
|
* otherwise the default of 4096
|
|
*/
|
|
function getMaxDescription () {
|
|
try {
|
|
const max = core.getInput('max_description')
|
|
if (typeof max === 'string' && max.length) {
|
|
// 4096 is max for Embed Description
|
|
// https://discord.com/developers/docs/resources/channel#embed-object-embed-limits
|
|
return Math.min(parseInt(max, 10), 4096)
|
|
}
|
|
} catch (err) {
|
|
core.warning(`max_description not a valid number: ${err}`)
|
|
}
|
|
return 4096
|
|
}
|
|
|
|
/**
|
|
* Get the context of the action, returns a GitHub Release payload.
|
|
*/
|
|
function getContext () {
|
|
const payload = github.context.payload;
|
|
|
|
return {
|
|
body: payload.release.body,
|
|
name: payload.release.name,
|
|
html_url: payload.release.html_url
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} str
|
|
* @param {number} maxLength
|
|
* @param {string} [url]
|
|
* @param {boolean} [clipAtLine=false]
|
|
*/
|
|
function limit(str, maxLength, url, clipAtLine) {
|
|
clipAtLine ??= false
|
|
if (str.length <= maxLength)
|
|
return str
|
|
let replacement = clipAtLine ? '\n…' : '…'
|
|
if (url) {
|
|
replacement = `${clipAtLine ? '\n' : ''}([…](${url}))`
|
|
}
|
|
maxLength = maxLength - replacement.length
|
|
str = str.substring(0, maxLength)
|
|
|
|
const lastNewline = str.search(new RegExp(`[^${clipAtLine ? '\n' : '\s'}]*$`))
|
|
if (lastNewline > -1) {
|
|
str = str.substring(0, lastNewline)
|
|
}
|
|
|
|
return str + replacement
|
|
}
|
|
|
|
/**
|
|
* Handles the action.
|
|
* Get inputs, creates a stylized response webhook, and sends it to the channel.
|
|
*/
|
|
async function run () {
|
|
const webhookUrl = core.getInput('webhook_url');
|
|
const color = core.getInput('color');
|
|
const username = core.getInput('username');
|
|
const avatarUrl = core.getInput('avatar_url');
|
|
const content = core.getInput('content');
|
|
const footerTitle = core.getInput('footer_title');
|
|
const footerIconUrl = core.getInput('footer_icon_url');
|
|
const footerTimestamp = core.getInput('footer_timestamp');
|
|
|
|
if (!webhookUrl) return core.setFailed('webhook_url not set. Please set it.');
|
|
|
|
const {body, html_url, name} = getContext();
|
|
|
|
const description = formatDescription(body);
|
|
|
|
let embedMsg = {
|
|
title: limit(name, 256),
|
|
url: html_url,
|
|
color: color,
|
|
description: description,
|
|
footer: {}
|
|
}
|
|
|
|
if (footerTitle != '') embedMsg.footer.text = limit(footerTitle, 2048);
|
|
if (footerIconUrl != '') embedMsg.footer.icon_url = footerIconUrl;
|
|
if (footerTimestamp == 'true') embedMsg.timestamp = new Date().toISOString();
|
|
|
|
let embedSize = embedMsg.title.length + (embedMsg.footer?.text?.length ?? 0)
|
|
embedMsg.description = limit(embedMsg.description, Math.min(getMaxDescription(), 6000 - embedSize), embedMsg.url, true)
|
|
|
|
let requestBody = {
|
|
embeds: [embedMsg]
|
|
}
|
|
|
|
if (username != '') requestBody.username = username;
|
|
if (avatarUrl != '') requestBody.avatar_url = avatarUrl;
|
|
if (content != '') requestBody.content = content;
|
|
|
|
const url = `${webhookUrl}?wait=true`;
|
|
fetch(url, {
|
|
method: 'post',
|
|
body: JSON.stringify(requestBody),
|
|
headers: { 'Content-Type': 'application/json' }
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => core.info(JSON.stringify(data)))
|
|
.catch(err => core.info(err))
|
|
}
|
|
|
|
run()
|
|
.then(() => {core.info('Action completed successfully')})
|
|
.catch(err => {core.setFailed(err.message)}) |