Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More description formatting and 2 new options #23

Merged
merged 10 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
8 changes: 8 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
125 changes: 101 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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]
}
Expand Down