mirror of
https://github.com/SethCohen/github-releases-to-discord
synced 2026-09-07 20:03:23 +00:00
feat: added additional description formatting (#23)
remove: removed HTML comments added by the 'Generate release notes' button feat: description trimming style: reduce consecutive whitespace/newlines into a minimum of 2 to allow separation in paragraphs style: parse common Github URLs to more appropriate display feat: added max_description option feat: added reduce_headings option
This commit is contained in:
@@ -7,16 +7,18 @@ A GitHub action that parses a GitHub release and posts it to a Discord channel a
|
||||
|
||||
## Configuration
|
||||
|
||||
| Variable | Required | Default | Description |
|
||||
|-----------------|----------|----------------------------------------------------------------------------------------------------------------|--------------------------------------------|
|
||||
| webhook_url | ✔ | | Discord's webhook url. Use GH repo secrets.|
|
||||
| color | ❌ | "2105893" | Decimal color value for embed. |
|
||||
| username | ❌ | | String username for webhook. |
|
||||
| avatar_url | ❌ | | String url to webhook avatar picture. |
|
||||
| content | ❌ | | String content for webhook. |
|
||||
| footer_title | ❌ | | String title for the webhook footer. |
|
||||
| footer_icon_url | ❌ | | String url for the webhook footer picture. |
|
||||
| footer_timestamp| ❌ | | Boolean to enable footer timestamp. |
|
||||
| Variable | Required | Default | Description |
|
||||
|-----------------|----------|-------------------------------------------------------------------------------------------------------|-------------------------------------------------|
|
||||
| webhook_url | ✔ | | Discord's webhook url. Use GH repo secrets. |
|
||||
| color | ❌ | "2105893" | Decimal color value for embed. |
|
||||
| username | ❌ | | String username for webhook. |
|
||||
| avatar_url | ❌ | | String url to webhook avatar picture. |
|
||||
| content | ❌ | | String content for webhook. |
|
||||
| footer_title | ❌ | | String title for the webhook footer. |
|
||||
| footer_icon_url | ❌ | | String url for the webhook footer picture. |
|
||||
| footer_timestamp| ❌ | | Boolean to enable footer timestamp. |
|
||||
| max_description | ❌ | "4096" | Max length for the description. |
|
||||
| reduce_headings | ❌ | false | Converts H3 to bold, h2 to bold & underline. |
|
||||
|
||||
## Example Usage
|
||||
|
||||
|
||||
@@ -27,6 +27,14 @@ inputs:
|
||||
footer_timestamp:
|
||||
description: Timestamp for the footer.
|
||||
required: false
|
||||
max_description:
|
||||
description: Max length for the description.
|
||||
required: false
|
||||
default: '4096'
|
||||
reduce_headings:
|
||||
description: Converts H3 to bold, h2 to bold & underline.
|
||||
required: false
|
||||
default: 'false'
|
||||
runs:
|
||||
using: 'node16'
|
||||
main: 'index.js'
|
||||
|
||||
@@ -4,45 +4,119 @@ import fetch from 'node-fetch';
|
||||
|
||||
/**
|
||||
* Stylizes a markdown body into an appropriate embed message style.
|
||||
* H3s converted to bold and underlined.
|
||||
* H2s converted to bold.
|
||||
* Redundant whitespace and newlines removed.
|
||||
* @param description
|
||||
* @returns {*}
|
||||
* 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) => {
|
||||
return description
|
||||
.replace(/### (.*?)\n/g,function (substring) {
|
||||
const newString = substring.slice(4).replace(/(\r\n|\n|\r)/gm, "")
|
||||
return `**__${newString}__**`
|
||||
let edit = description
|
||||
.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'
|
||||
})
|
||||
.replace(/## (.*?)\n/g,function (substring) {
|
||||
const newString = substring.slice(3).replace(/(\r\n|\n|\r)/gm, "")
|
||||
return `**${newString}**`
|
||||
})
|
||||
.replace(/\n\s*\n/g, '\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.
|
||||
* @returns {Promise<{html_url, body: (*|string), name: string}>}
|
||||
*/
|
||||
async function getContext () {
|
||||
function getContext () {
|
||||
const payload = github.context.payload;
|
||||
|
||||
return {
|
||||
body: payload.release.body.length < 1500
|
||||
? payload.release.body
|
||||
: payload.release.body.substring(0, 1500) + ` ([...](${payload.release.html_url}))`,
|
||||
name: payload.release.name,
|
||||
body: payload.release.body,
|
||||
name: payload.release.name,
|
||||
html_url: payload.release.html_url
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {number} maxLength
|
||||
* @param {string=} url
|
||||
*/
|
||||
function limit(str, maxLength, url) {
|
||||
if (str.length <= maxLength)
|
||||
return str
|
||||
let replacement = '…'
|
||||
if (url) {
|
||||
replacement = `([${replacement}](${url}))`
|
||||
}
|
||||
maxLength = maxLength - replacement.length
|
||||
str = str.substring(0, maxLength)
|
||||
|
||||
const lastWhitespace = str.search(/[^\s]*$/)
|
||||
if (lastWhitespace > -1) {
|
||||
str = str.substring(0, lastWhitespace)
|
||||
}
|
||||
|
||||
return str + replacement
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the action.
|
||||
* Get inputs, creates a stylized response webhook, and sends it to the channel.
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async function run () {
|
||||
const webhookUrl = core.getInput('webhook_url');
|
||||
@@ -56,22 +130,25 @@ async function run () {
|
||||
|
||||
if (!webhookUrl) return core.setFailed('webhook_url not set. Please set it.');
|
||||
|
||||
const {body, html_url, name} = await getContext();
|
||||
const {body, html_url, name} = getContext();
|
||||
|
||||
const description = formatDescription(body);
|
||||
|
||||
let embedMsg = {
|
||||
title: name,
|
||||
title: limit(name, 256),
|
||||
url: html_url,
|
||||
color: color,
|
||||
description: description,
|
||||
footer: {}
|
||||
}
|
||||
|
||||
if (footerTitle != '') embedMsg.footer.text = footerTitle;
|
||||
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)
|
||||
|
||||
let requestBody = {
|
||||
embeds: [embedMsg]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user