feat: update checker

This commit is contained in:
Isaac 2023-03-06 22:09:05 +00:00
parent 229cfe11e5
commit 6133a3d59f
No known key found for this signature in database
GPG Key ID: 0DE40AE37BBA5C33
8 changed files with 171 additions and 15 deletions

View File

@ -37,7 +37,7 @@
},
"homepage": "https://discordtickets.app",
"engines": {
"node": ">=18.0"
"node": ">=18"
},
"dependencies": {
"@discord-tickets/settings": "^1.4.2",
@ -50,6 +50,7 @@
"@fastify/jwt": "^5.0.1",
"@fastify/oauth2": "^5.1.0",
"@prisma/client": "^4.10.1",
"boxen": "^7.0.2",
"cryptr": "^6.1.0",
"discord.js": "^14.7.1",
"dotenv": "^16.0.3",

View File

@ -14,6 +14,7 @@ specifiers:
'@fastify/oauth2': ^5.1.0
'@prisma/client': ^4.10.1
all-contributors-cli: ^6.24.0
boxen: ^7.0.2
bufferutil: ^4.0.7
conventional-changelog-cli: ^2.2.2
cryptr: ^6.1.0
@ -55,6 +56,7 @@ dependencies:
'@fastify/jwt': 5.0.1
'@fastify/oauth2': 5.1.0
'@prisma/client': 4.10.1_prisma@4.10.1
boxen: 7.0.2
cryptr: 6.1.0
discord.js: 14.7.1_3cxu5zja4e2r5wmvge7mdcljwq
dotenv: 16.0.3
@ -800,6 +802,12 @@ packages:
- encoding
dev: true
/ansi-align/3.0.1:
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
dependencies:
string-width: 4.2.3
dev: false
/ansi-escapes/4.3.2:
resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
engines: {node: '>=8'}
@ -809,12 +817,10 @@ packages:
/ansi-regex/5.0.1:
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
engines: {node: '>=8'}
dev: true
/ansi-regex/6.0.1:
resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
engines: {node: '>=12'}
dev: true
/ansi-styles/3.2.1:
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
@ -833,7 +839,6 @@ packages:
/ansi-styles/6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
dev: true
/anymatch/3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
@ -944,6 +949,20 @@ packages:
- supports-color
dev: false
/boxen/7.0.2:
resolution: {integrity: sha512-1Z4UJabXUP1/R9rLpoU3O2lEMnG3pPLAs/ZD2lF3t2q7qD5lM8rqbtnvtvm4N0wEyNlE+9yZVTVAGmd1V5jabg==}
engines: {node: '>=14.16'}
dependencies:
ansi-align: 3.0.1
camelcase: 7.0.1
chalk: 5.2.0
cli-boxes: 3.0.0
string-width: 5.1.2
type-fest: 2.19.0
widest-line: 4.0.1
wrap-ansi: 8.1.0
dev: false
/brace-expansion/1.1.11:
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
dependencies:
@ -1010,6 +1029,11 @@ packages:
engines: {node: '>=6'}
dev: true
/camelcase/7.0.1:
resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==}
engines: {node: '>=14.16'}
dev: false
/chalk/2.4.2:
resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
engines: {node: '>=4'}
@ -1027,6 +1051,11 @@ packages:
supports-color: 7.2.0
dev: true
/chalk/5.2.0:
resolution: {integrity: sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==}
engines: {node: ^12.17.0 || ^14.13 || >=16.0.0}
dev: false
/chardet/0.7.0:
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
dev: true
@ -1051,6 +1080,11 @@ packages:
engines: {node: '>=6'}
dev: true
/cli-boxes/3.0.0:
resolution: {integrity: sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==}
engines: {node: '>=10'}
dev: false
/cli-cursor/3.1.0:
resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
engines: {node: '>=8'}
@ -1518,7 +1552,6 @@ packages:
/eastasianwidth/0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
dev: true
/ecdsa-sig-formatter/1.0.11:
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
@ -1540,11 +1573,9 @@ packages:
/emoji-regex/8.0.0:
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
dev: true
/emoji-regex/9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
dev: true
/emojilib/2.4.0:
resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==}
@ -2349,7 +2380,6 @@ packages:
/is-fullwidth-code-point/3.0.0:
resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
engines: {node: '>=8'}
dev: true
/is-fullwidth-code-point/4.0.0:
resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
@ -3740,7 +3770,6 @@ packages:
emoji-regex: 8.0.0
is-fullwidth-code-point: 3.0.0
strip-ansi: 6.0.1
dev: true
/string-width/5.1.2:
resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
@ -3749,7 +3778,6 @@ packages:
eastasianwidth: 0.2.0
emoji-regex: 9.2.2
strip-ansi: 7.0.1
dev: true
/string_decoder/1.1.1:
resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==}
@ -3767,14 +3795,12 @@ packages:
engines: {node: '>=8'}
dependencies:
ansi-regex: 5.0.1
dev: true
/strip-ansi/7.0.1:
resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==}
engines: {node: '>=12'}
dependencies:
ansi-regex: 6.0.1
dev: true
/strip-bom/3.0.0:
resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
@ -4024,6 +4050,11 @@ packages:
engines: {node: '>=8'}
dev: true
/type-fest/2.19.0:
resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
engines: {node: '>=12.20'}
dev: false
/type-is/1.6.18:
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
engines: {node: '>= 0.6'}
@ -4132,6 +4163,13 @@ packages:
isexe: 2.0.0
dev: true
/widest-line/4.0.1:
resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==}
engines: {node: '>=12'}
dependencies:
string-width: 5.1.2
dev: false
/word-wrap/1.2.3:
resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
engines: {node: '>=0.10.0'}
@ -4159,6 +4197,15 @@ packages:
strip-ansi: 6.0.1
dev: true
/wrap-ansi/8.1.0:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
dependencies:
ansi-styles: 6.2.1
string-width: 5.1.2
strip-ansi: 7.0.1
dev: false
/wrappy/1.0.2:
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}

View File

@ -183,7 +183,6 @@ commands:
name: tag
tickets:
description: List your own or someone else's tickets
fields:
name: tickets
not_staff:
description: Only staff members can view others' tickets.
@ -346,6 +345,12 @@ misc:
unknown_category:
description: Please try a different category.
title: ❌ That ticket category doesn't exist
update:
description: |
> [View `{version}` on GitHub]({github})
> [Changelog]({changelog})
> [Update guide]({guide})
title: An update is available
modals:
feedback:
comment:

View File

@ -32,7 +32,7 @@ const { colours } = require('leeks.js');
// check node version
if (!semver.satisfies(process.versions.node, pkg.engines.node)) {
console.log('\x07' + colours.redBright(`Error: Your current Node.js version, ${process.versions.node}, does not meet the requirement "${pkg.engines.node}".`));
console.log('\x07' + colours.redBright(`Error: Your current Node.js version, ${process.versions.node}, does not meet the requirement "${pkg.engines.node}". Please update to version ${semver.minVersion(pkg.engines.node).version} or higher.`));
process.exit(1);
}

View File

@ -5,6 +5,7 @@ const {
} = require('leekslazylogger');
const DTF = require('@eartharoid/dtf');
const { short } = require('leeks.js');
const { format } = require('util');
const dtf = new DTF('en-GB');
const colours = {
@ -24,7 +25,13 @@ module.exports = config => {
format: log => {
const timestamp = dtf.fill('DD/MM/YY HH:mm:ss', log.timestamp);
const colour = colours[log.level.name];
return short(`&f&!7 ${timestamp} &r ${colour[0]}[${log.level.name.toUpperCase()}]&r ${log.namespace ? `&d(${log.namespace.toUpperCase()})&r ` : ''}${colour[1]}${log.content}`);
return format(
short(`&f&!7 %s &r ${colour[0]}[%s]&r %s${colour[1]}%s&r`),
timestamp,
log.level.name.toUpperCase(),
log.namespace ? short(`&d(${log.namespace.toUpperCase()})&r `) : '',
log.content,
);
},
level: config.logs.level,
}),

89
src/lib/updates.js Normal file
View File

@ -0,0 +1,89 @@
const semver = require('semver');
const { short } = require('leeks.js');
const ExtendedEmbedBuilder = require('./embed');
const { version: currentVersion } = require('../../package.json');
/** @param {import("client")} client */
module.exports = client => {
client.log.info('Checking for updates...');
fetch('https://api.github.com/repos/discord-tickets/bot/releases')
.then(res => res.json())
.then(async json => {
// releases are ordered by date, so a patch for an old version could be before the latest version
const releases = json
.filter(release => !release.prerelease)
.sort((a, b) => semver.compare(semver.coerce(b.tag_name)?.version, semver.coerce(a.tag_name)?.version));
const latestRelease = releases[0];
const latestVersion = semver.coerce(latestRelease.tag_name)?.version;
const compared = semver.compare(latestVersion, currentVersion);
switch (compared) {
case -1: {
client.log.notice('You are running a pre-release version of Discord Tickets');
break;
}
case 0: {
client.log.info('No updates available');
break;
}
case 1: {
let currentRelease = releases.findIndex(release => semver.coerce(release.tag_name)?.version === currentVersion);
if (currentRelease === -1) return client.log.warn('Failed to find current release');
const behind = currentRelease;
currentRelease = releases[currentRelease];
const changelog = `https://discordtickets.app/changelogs/v${latestVersion.replaceAll('.', '') }/`;
const guide = 'https://discordtickets.app/self-hosting/updating/';
const { default: boxen } = await import('boxen');
client.log.notice(
short('&r&6A new version of Discord Tickets is available (&c%s&6 -> &a%s&6)&r\n'),
currentVersion,
latestVersion,
boxen(
short([ // uses template literals to ensure boxen adds the correct padding
`&6You are &f${behind}&6 version${behind === 1 ? '' : 's'} behind the latest version, &a${latestVersion}&6.&r`,
`&6Changelog: &e${changelog}&r`,
`&6Update guide: &e${guide}&r`,
].join('\n')),
{
align: 'center',
borderColor: 'yellow',
borderStyle: 'round',
margin: 1,
padding: 1,
title: 'Update available',
}),
);
if (process.env.PUBLIC_BOT !== 'true') {
const guilds = await client.prisma.guild.findMany({ where: { logChannel: { not: null } } });
for (const guild of guilds) {
const getMessage = client.i18n.getLocale(guild.locale);
await client.channels.cache.get(guild.logChannel).send({
embeds: [
new ExtendedEmbedBuilder()
.setColor('Blurple')
.setAuthor({
iconURL: latestRelease.author.avatar_url,
name: latestRelease.author.login,
})
.setTitle(getMessage('misc.update.title'))
.setDescription(getMessage('misc.update.fields.links.value', {
changelog,
github: latestRelease.html_url,
guide,
version: latestRelease.tag_name,
})),
],
});
}
}
break;
}
}
})
.catch(error => {
client.log.warn('Failed to check for updates');
client.log.error(error);
});
};

View File

@ -4,6 +4,7 @@ const ms = require('ms');
const { version } = require('../../../package.json');
const { msToMins } = require('../../lib/misc');
const sync = require('../../lib/sync');
const checkForUpdates = require('../../lib/updates');
module.exports = class extends Listener {
constructor(client, options) {
@ -118,6 +119,11 @@ module.exports = class extends Listener {
setInterval(() => send(), ms('12h'));
}
if (client.config.updates) {
checkForUpdates(client);
setInterval(() => checkForUpdates(client), ms('1w'));
}
setInterval(() => {
// TODO: check lastMessageAt and set stale
// this.$stale.set(ticket.id, {

View File

@ -33,3 +33,4 @@ presence:
stats: true
templates:
transcript: transcript.md
updates: true