1
0
mirror of https://github.com/toast-ts/Daggerbot-TS.git synced 2024-12-25 23:15:38 -05:00

Upgrade Node.js to version 21.7.1 and implement efficiency enhancements

This commit is contained in:
toast-ts 2024-03-24 12:36:12 +11:00
parent e49e3511c8
commit 44c01c8733
22 changed files with 74 additions and 56 deletions

View File

@ -1,4 +1,4 @@
FROM node:21.1.0-bookworm-slim
FROM node:21.7.1-bookworm-slim
ENV YARN_VERSION 4.1.1
ENV TZ Australia/Sydney
RUN yarn policies set-version $YARN_VERSION

View File

@ -44,7 +44,7 @@
"redis": "4.6.13",
"simple-git": "3.23.0",
"systeminformation": "5.22.6",
"undici": "6.9.0"
"undici": "6.10.1"
},
"devDependencies": {
"@types/ms": "0.7.34",

View File

@ -1,6 +1,7 @@
import Discord from 'discord.js';
import {exec} from 'node:child_process';
import MessageTool from '../helpers/MessageTool.js';
import DatabaseServer from '../components/DatabaseServer.js';
import UsernameHelper from '../helpers/UsernameHelper.js';
import Formatters from '../helpers/Formatters.js';
import GitHub from '../helpers/GitHub.js';
@ -13,6 +14,8 @@ export default class Developer {
({
eval: async()=>{
fs;
DatabaseServer;
const code = interaction.options.getString('code') as string;
let consoleOutput:string = '';

View File

@ -6,7 +6,7 @@ import RanIntoHumor from '../helpers/RanIntoHumor.js';
import MessageTool from '../helpers/MessageTool.js';
import PalletLibrary from '../helpers/PalletLibrary.js';
import {FSData} from 'src/interfaces';
import {requestServerData, mpModuleDisabled, refreshTimerSecs, playtimeStat} from '../modules/MPModule.js';
import {requestServerData, mpModuleDisabled, refreshTimerSecs, playtimeStat, MPChannels} from '../modules/MPModule.js';
async function fetchData(client:TClient, interaction:Discord.ChatInputCommandInteraction, serverName:string):Promise<FSData|Discord.InteractionResponse> {
try {
@ -21,12 +21,7 @@ async function fetchData(client:TClient, interaction:Discord.ChatInputCommandInt
const logPrefix = 'MPDB';
const MAP_POOL_HOOKMSG = '1141293129673232435';
const channels = {
activePlayers: '739084625862852715',
announcements: '1084864116776251463',
mainMpChat: '468835769092669461',
serverInfo: '543494084363288637',
}
export default class MP {
static async autocomplete(client:TClient, interaction:Discord.AutocompleteInteraction<'cached'>) {
const serversInCache = await client.MPServer?.findInCache();
@ -36,7 +31,7 @@ export default class MP {
static async run(client:TClient, interaction:Discord.ChatInputCommandInteraction<'cached'>) {
if (client.config.botSwitches.mpSys === false) return interaction.reply({embeds: [mpModuleDisabled(client)]});
if (client.uptime < refreshTimerSecs) return interaction.reply('MPModule isn\'t initialized yet, please wait a moment and try again.');
if ([channels.mainMpChat, client.config.dcServer.channels.multifarm_chat].includes(interaction.channelId) && !MessageTool.isStaff(interaction.member) && ['status', 'players'].includes(interaction.options.getSubcommand())) return interaction.reply(`Please use <#${channels.activePlayers}> for \`/mp status/players\` commands to prevent clutter in this channel.`).then(()=>setTimeout(()=>interaction.deleteReply(), 6000));
if ([MPChannels.mainMpChat, client.config.dcServer.channels.multifarm_chat].includes(interaction.channelId) && !MessageTool.isStaff(interaction.member) && ['status', 'players'].includes(interaction.options.getSubcommand())) return interaction.reply(`Please use <#${MPChannels.activePlayers}> for \`/mp status/players\` commands to prevent clutter in this channel.`).then(()=>setTimeout(()=>interaction.deleteReply(), 6000));
const choiceSelector = interaction.options.getString('server');
({
players: async()=>{
@ -87,7 +82,7 @@ export default class MP {
`**Map:** \`${DSS.server?.mapName.length > 0 ? DSS.server.mapName : 'No map'}\``,
`**Mods:** [Click here](http://${server.ip}/mods.html) **|** [Direct link](http://${server.ip}/all_mods_download?onlyActive=true)`,
'**Filters:** [Click here](https://discord.com/channels/468835415093411861/468835769092669461/926581585938120724)',
`Please see <#${channels.serverInfo}> for more additional information and rules.`
`Please see <#${MPChannels.serverInfo}> for more additional information and rules.`
));
if (DSS.server?.name.length < 1) dEmbed.setFooter({text: 'Server is currently offline'});
DSS.server ? await interaction.reply({embeds: [dEmbed]}) : null;
@ -122,14 +117,14 @@ export default class MP {
}
const reason = interaction.options.getString('reason');
const channel = interaction.guild.channels.cache.get(channels.activePlayers) as Discord.TextChannel;
const channel = interaction.guild.channels.cache.get(MPChannels.activePlayers) as Discord.TextChannel;
const embed = new client.embed().setColor(client.config.embedColor).setAuthor({name: interaction.member.displayName, iconURL: interaction.member.displayAvatarURL({size:1024})}).setTimestamp();
const isLocked = channel.permissionsFor(interaction.guildId).has('SendMessages');
const titleAction = isLocked ? '🔒 Locked' : '🔓 Unlocked';
channel.permissionOverwrites.edit(interaction.guildId, {SendMessages: !isLocked}, {type: 0, reason: `${isLocked ? 'Locked' : 'Unlocked'} by ${interaction.member.displayName}`});
channel.send({embeds: [embed.setTitle(titleAction).setDescription(`**Reason:**\n${reason}`)]});
interaction.reply({content: `${MessageTool.formatMention(channels.activePlayers, 'channel')} ${isLocked ? 'locked' : 'unlocked'} successfully`, ephemeral: true});
interaction.reply({content: `${MessageTool.formatMention(MPChannels.activePlayers, 'channel')} ${isLocked ? 'locked' : 'unlocked'} successfully`, ephemeral: true});
},
start: async()=>{
if (client.config.dcServer.id === interaction.guildId) {
@ -138,14 +133,14 @@ export default class MP {
const map_names = interaction.options.getString('map_names', true).split('|');
if (map_names.length > 10) return interaction.reply('You can only have up to 10 maps in a poll!');
const msg = await (interaction.guild.channels.cache.get(channels.announcements) as Discord.TextChannel).send({content: MessageTool.formatMention(client.config.dcServer.roles.mpplayer, 'role'), embeds: [
const msg = await (interaction.guild.channels.cache.get(MPChannels.announcements) as Discord.TextChannel).send({content: MessageTool.formatMention(client.config.dcServer.roles.mpplayer, 'role'), embeds: [
new client.embed()
.setColor(client.config.embedColor)
.setTitle('Vote for next map!')
.setDescription(map_names.map((map,i)=>`${i+1}. **${map}**`).join('\n'))
.setFooter({text: `Poll started by ${interaction.user.tag}`, iconURL: interaction.member.displayAvatarURL({extension: 'webp', size: 1024})})
], allowedMentions: {parse: ['roles']}});
await interaction.reply(`Successfully created a poll in <#${channels.announcements}>`)
await interaction.reply(`Successfully created a poll in <#${MPChannels.announcements}>`)
this.reactionSystem(msg, map_names.length);
},
end: async()=>{
@ -154,7 +149,7 @@ export default class MP {
}
const msg_id = interaction.options.getString('message_id', true);
const stripUrl = msg_id.replace(/https:\/\/discord.com\/channels\/\d+\/\d+\/(\d+)/, '$1');
const msg = await (interaction.guild.channels.cache.get(channels.announcements) as Discord.TextChannel).messages.fetch(stripUrl).catch(()=>null);
const msg = await (interaction.guild.channels.cache.get(MPChannels.announcements) as Discord.TextChannel).messages.fetch(stripUrl).catch(()=>null);
if (!msg) return interaction.reply('Message not found, please make sure you have the correct message ID.');
if (msg.embeds[0].title !== 'Vote for next map!') return interaction.reply('This message is not a poll!');
@ -168,7 +163,7 @@ export default class MP {
]});
msg.edit({content: null, embeds: [new client.embed().setColor(client.config.embedColor).setTitle('Voting has ended!').setDescription('The next map will be '+msg.embeds[0].description.split('\n')[msg.reactions.cache.map(x=>x.count).indexOf(Math.max(...msg.reactions.cache.map(x=>x.count)))].slice(3)).setFooter({text: `Poll ended by ${interaction.user.tag}`, iconURL: interaction.member.displayAvatarURL({extension: 'webp', size: 1024})})]}).then(()=>msg.reactions.removeAll());
await interaction.reply(`Successfully ended the [poll](<https://discord.com/channels/${interaction.guildId}/${channels.announcements}/${msg.id}>) in <#${channels.announcements}>`)
await interaction.reply(`Successfully ended the [poll](<https://discord.com/channels/${interaction.guildId}/${MPChannels.announcements}/${msg.id}>) in <#${MPChannels.announcements}>`)
},
maps: async()=>{
if (client.config.dcServer.id === interaction.guildId) {

View File

@ -54,6 +54,7 @@ export default class Statistics {
`**Discord.js:** ${pkg.dependencies['discord.js']}`,
`**TypeScript:** ${ts.version}`,
`**TokenService:** ${pkg.dependencies['@toast/tokenservice-client']}`,
`**Sequelize:** ${pkg.dependencies['@sequelize/core']}`,
`**Postgres:** ${pkg.dependencies.pg}`,
`**Redis:** ${pkg.dependencies.redis}`
)},

View File

@ -1,6 +1,8 @@
import Discord from 'discord.js';
import TClient from '../client.js';
import MessageTool from '../helpers/MessageTool.js';
import DatabaseServer from '../components/DatabaseServer.js';
import ConfigHelper from '../helpers/ConfigHelper.js';
import HookMgr from '../components/HookManager.js';
export default class Suggest {
static async run(client:TClient, interaction:Discord.ChatInputCommandInteraction<'cached'>) {
@ -35,10 +37,13 @@ export default class Suggest {
return await client.suggestions.updateStatus(id, status);
}
static async deleteSuggestion(client:TClient, id:number) {
return await client.suggestions.delete(id);
await client.suggestions.delete(id);
await DatabaseServer.query("SELECT setval(pg_get_serial_sequence('suggestions', 'id'), (SELECT MAX(id) FROM suggestions))");
return true;
}
static newWebhookMessage(client:TClient, id:number, suggestion:string, username:string) {
const hook = new HookMgr(client, 'bot_suggestions', '1079621523561779272');
const hookId = ConfigHelper.isDevMode() ? '1079586978808463372' : '1079621523561779272';
const hook = new HookMgr(client, 'bot_suggestions', hookId);
if (hook) return hook.send({embeds: [new client.embed().setColor(client.config.embedColor).setTitle(`Suggestion #${id}`).setAuthor({name: username}).setDescription(`\`\`\`${suggestion}\`\`\``)]});
else throw new Error('[SUGGESTION-HOOK] Provided webhook cannot be fetched, not sending message.')
}

View File

@ -5,7 +5,10 @@ import TSClient from '../helpers/TSClient.js';
const postgresUri = (await TSClient()).postgres_uri;
export default class DatabaseServer {
private static logPrefix:string = 'Database';
public static seq:Sequelize = new Sequelize(postgresUri, {dialect: 'postgres', logging: false, ssl: false, pool: {max: 10, min: 0, acquire: 15000, idle: 8000}})
public static seq:Sequelize = new Sequelize(postgresUri, {dialect: 'postgres', logging: false, ssl: false, pool: {max: 10, min: 0, acquire: 15000, idle: 8000}});
public static async query(pattern:string) {
return await this.seq.query(pattern);
}
public static async init() {
try {
await this.seq.authenticate();

View File

@ -45,8 +45,7 @@
"633345781780185099",
"215497515934416896",
"141304507249197057",
"178941218510602240",
"700641965787709520"
"178941218510602240"
],
"dcServer": {
"id": "468835415093411861",

View File

@ -4,7 +4,7 @@ export default class GuildMemberAdd {
static async run(client:TClient, member:Discord.GuildMember){
if (member.partial || member.guild?.id != client.config.dcServer.id) return;
const index = member.guild.memberCount;
const suffix = (index=>{
/* const suffix = (index=>{
const numbers = index.toString().split('').reverse(); // eg 1850 --> [0,5,8,1]
if (numbers[1] === '1') return 'th'; // this is some -teen
else {
@ -13,7 +13,15 @@ export default class GuildMemberAdd {
else if (numbers[0] === '3') return 'rd';
else return 'th';
}
})(index);
})(index); */
const suffix = {// Trial run, just discovered Intl.PluralRules this morning (as of March 24th) when I was browsing MDN Docs.
one: 'st',
two: 'nd',
few: 'rd',
other: 'th'
}[new Intl.PluralRules('en', {type: 'ordinal'}).select(index)];
let isBot = 'Bot';
if (!member.user.bot) isBot = 'Member';
if (!client.config.botSwitches.logs) return;

View File

@ -3,6 +3,7 @@ import TClient from '../client.js';
import Response from '../modules/ResponseModule.js';
import CmdTrigger from '../modules/CmdModule.js';
import Logger from '../helpers/Logger.js';
import {MPChannels} from '../modules/MPModule.js';
import ConfigHelper from '../helpers/ConfigHelper.js';
import Automoderator from '../components/Automod.js';
import __PRIVATE__ from '../private/_.js';
@ -111,10 +112,10 @@ export default class MessageCreate {
if (message.type === Discord.MessageType.GuildBoost && message.channelId === GeneralChatID) message.channel.send({content: outgoingArrays.guildBoost[Math.floor(Math.random() * outgoingArrays.guildBoost.length)], allowedMentions: {parse: ['users']}})
if (dontMention.some(e=>message.mentions.members.has(e.user_id) && !MessageTool.isStaff(message.member)) && (dontMention.find(e => message.mentions.has(e.user_id)).type === undefined || message.type === dontMention.find(e => message.mentions.has(e.user_id)).type)) message.reply(dontMention.find(e=>message.mentions.members.has(e.user_id)).message);
if (incomingArrays.password.some(e=>message.content.toLowerCase().includes(e))) message.reply('Password and other details can be found in <#543494084363288637>');
if (incomingArrays.password.some(e=>message.content.toLowerCase().includes(e))) message.reply(`Password and other details can be found in <#${MPChannels.serverInfo}>`);
if (incomingArrays.cantRead.some(e=>message.content.toLowerCase().includes(e))) message.reply(picStorage.cantRead);
if (message.content.toLowerCase().includes('is daggerbot working')) message.reply(picStorage.amAlive);
if (message.channelId === '468835769092669461' && incomingArrays.mpsrv.some(e=>message.content.toLowerCase().includes(e))) message.reply('You can take a look at the embeds in <#543494084363288637> to see if anyone is on the server.');
if (message.channelId === MPChannels.mainMpChat && incomingArrays.mpsrv.some(e=>message.content.toLowerCase().includes(e))) message.reply(`You can take a look at the embeds in <#${MPChannels.serverInfo}> to see if anyone is on the server.`);
for (const thisPerson of ModsGoGetThisPerson) {
if (incomingArrays.theyBrokeIt.some(x=>Automoderator.scanMsg(message).includes(x) && Automoderator.scanMsg(message).includes(thisPerson.user)) && MessageTool.isStaff(message.member) && message.channelId !== client.config.dcServer.channels.mpmod_chat)

View File

@ -1,3 +1,2 @@
import TokenService from '@toast/tokenservice-client';
export default async()=>new TokenService(process.argv[3] ?? 'daggerbot', false).connect();

4
src/interfaces.d.ts vendored
View File

@ -165,8 +165,8 @@ export interface Config {
}
}
}
// Credits to FlyingSixtySix/neurobot for inspiration.
// https://github.com/FlyingSixtySix/neurobot/blob/0dee4ea4f72872e2df240700eb56e1d38da1f8bb/src/interactions/jp.ts#L37-L85
// Credits to VanillaSixtySix/neurobot for inspiration.
// https://github.com/VanillaSixtySix/neurobot/blob/0dee4ea4f72872e2df240700eb56e1d38da1f8bb/src/interactions/jp.ts#L37-L85
export interface RawGatewayPacket<T> {
t: 'MESSAGE_DELETE'|'MESSAGE_UPDATE';
d: T;

View File

@ -55,10 +55,9 @@ export class MPServerSvc {
})
this.model.sync();
}
query = async(pattern:string)=>await this.model.sequelize.query(pattern);
async fetchPlayerData(serverName:string) {
const server = await this.model.findOne({where: {serverName: serverName}});
return server ? server.dataValues.playerData : [];
const server = await this.model.findOne({where: {serverName}});
return server.dataValues.playerData ??= [];
}
async addServer(serverName:string, ip:string, code:string) {
await this.model.upsert({

View File

@ -27,14 +27,11 @@ export class DailyMsgsSvc {
tableName: 'dailymsgs',
createdAt: false,
updatedAt: false,
indexes: [
{name: 'day_index', fields: ['day'], unique: true}
],
indexes: [{name: 'day_index', fields: ['day'], unique: true}],
sequelize: DatabaseServer.seq
})
this.model.sync();
}
query = async(pattern:string)=>await this.model.sequelize.query(pattern);
nukeDays = async()=>await this.model.destroy();
fetchDays = async()=>await this.model.findAll();
async newDay(formattedDate:number, total:number) {

View File

@ -25,9 +25,8 @@ export class ProhibitedWordsSvc {
})
this.model.sync();
}
query = async(pattern:string)=>await this.model.sequelize.query(pattern);
findWord = async(word:string)=>await this.model.findByPk(word);
getAllWords = async()=>await this.model.findAll();
insertWord = async(word:string)=>await this.model.create({word: word});
removeWord = async(word:string)=>await this.model.destroy({where: {word: word}})
insertWord = async(word:string)=>await this.model.create({word});
removeWord = async(word:string)=>await this.model.destroy({where: {word}})
}

View File

@ -94,7 +94,6 @@ export class PunishmentsSvc {
});
this.model.sync();
}
query = async(pattern:string)=>await this.model.sequelize.query(pattern);
async updateReason(caseId:number, reason:string) {
const findCase = this.findCaseOrCancels('case_id', caseId);
if (findCase) return this.model.update({reason}, {where: {case_id: caseId}});

View File

@ -40,9 +40,8 @@ export class SuggestionsSvc {
})
this.model.sync();
}
query = async(pattern:string)=>await this.model.sequelize.query(pattern);
fetchById = async(id:number)=>await this.model.findByPk(id);
updateStatus = async(id:number, status:string)=>await this.model.update({status: status}, {where: {id: id}});
create =(userid:string, description:string)=>this.model.create({userid: userid, suggestion: description, status: 'Pending'})
delete =(id:number)=>this.model.destroy({where: {id: id}});
updateStatus = async(id:number, status:string)=>await this.model.update({status}, {where: {id}});
create =(userid:string, description:string)=>this.model.create({userid, suggestion: description, status: 'Pending'})
delete =(id:number)=>this.model.destroy({where: {id}});
}

View File

@ -50,22 +50,21 @@ export class TagSystemSvc {
});
this.model.sync();
}
query = async(pattern:string)=>await this.model.sequelize.query(pattern);
async createTag(userid:string, tagName:string, message:string, embedFlag:boolean) {
async createTag(userid:string, tagname:string, message:string, embedFlag:boolean) {
CacheServer.delete('tags');
return await this.model.create({userid: userid, tagname: tagName, message: message.replace(/\\n/g, '\n'), embedFlag: embedFlag});
return await this.model.create({userid, tagname, message: message.replace(/\\n/g, '\n'), embedFlag});
}
async deleteTag(tagname:string) {
CacheServer.delete('tags');
return await this.model.destroy({where: {tagname: tagname}});
return await this.model.destroy({where: {tagname}});
}
async sendTag(interaction:ChatInputCommandInteraction, tagName:string, targetId:Snowflake) {
const getTag = await this.model.findOne({where: {tagname: tagName}});
async sendTag(interaction:ChatInputCommandInteraction, tagname:string, targetId:Snowflake) {
const getTag = await this.model.findOne({where: {tagname}});
const targetMsg = targetId ? `*This tag is directed at ${MessageTool.formatMention(targetId, 'user')}*` : '';
const fetchUser = await interaction.guild?.members.fetch(getTag.dataValues.userid);
const ic = interaction.client as TClient;
const embedFormat = [
new ic.embed().setTitle(tagName).setColor(ic.config.embedColor)
new ic.embed().setTitle(tagname).setColor(ic.config.embedColor)
.setAuthor({name: interaction.user.username, iconURL: interaction.user.avatarURL({size: 2048, extension: 'webp'})})
.setDescription(getTag.dataValues.message)
];
@ -74,7 +73,7 @@ export class TagSystemSvc {
}
async modifyTag(tagname:string, message:string) {
CacheServer.delete('tags');
return await this.model.update({message: message.replace(/\\n/g, '\n')}, {where: {tagname: tagname}});
return await this.model.update({message: message.replace(/\\n/g, '\n')}, {where: {tagname}});
}
async findInCache(): Promise<Tags[]> {
const cacheKey = 'tags';

View File

@ -57,7 +57,6 @@ export class UserLevelsSvc {
});
this.model.sync();
}
query = async(pattern:string)=>await this.model.sequelize.query(pattern);
fetchEveryone = async()=>await this.model.findAll();
fetchUser = async(userId:string)=>await this.model.findByPk(userId);
deleteUser = async(userId:string)=>await this.model.destroy({where: {id: userId}});

View File

@ -34,7 +34,6 @@ export class YouTubeChannelsSvc {
})
this.model.sync();
}
query = async(pattern:string)=>await this.model.sequelize.query(pattern);
async addChannel(YTChannelID:string, DCChannelID:string, DCRole:string) {
const [_, created] = await this.model.findOrCreate({where: {ytchannel: YTChannelID}, defaults: {dcchannel: DCChannelID, dcrole: DCRole}});
return created;

View File

@ -16,12 +16,19 @@ let refreshIntrvlTxt:string = `Refreshes every ${refreshTimerSecs/1000} seconds.
let offlineStatus:string = 'Server is offline';
let unavailableStatus:string = 'Server didn\'t respond';
export const MPChannels = {
activePlayers: '739084625862852715',
announcements: '1084864116776251463',
mainMpChat: '468835769092669461',
serverInfo: '543494084363288637',
}
interface IServerExt extends IServer {
failureCount?:number;
}
export default async(client:TClient)=>{
const message = await (client.channels.resolve(isBotInDevMode ? '1091300529696673792' : '543494084363288637') as Discord.TextChannel).messages.fetch(isBotInDevMode ? '1104563309451161742' : '1149141188079779900');
const message = await (client.channels.resolve(isBotInDevMode ? '1091300529696673792' : MPChannels.serverInfo) as Discord.TextChannel).messages.fetch(isBotInDevMode ? '1104563309451161742' : '1149141188079779900');
if (!client.config.botSwitches.mpSys) return message.edit({content: null, embeds: [mpModuleDisabled(client)]});
async function newServerEntry(server:IServer) {

View File

@ -677,7 +677,7 @@ __metadata:
simple-git: "npm:3.23.0"
systeminformation: "npm:5.22.6"
typescript: "npm:5.4.3"
undici: "npm:6.9.0"
undici: "npm:6.10.1"
languageName: unknown
linkType: soft
@ -1371,6 +1371,13 @@ __metadata:
languageName: node
linkType: hard
"undici@npm:6.10.1":
version: 6.10.1
resolution: "undici@npm:6.10.1"
checksum: 10/42bf07ff5c652e1ed609827c5a02b25adacf69b8845c787a3e919c1f13ea5e5ebed1cdd0e82c5ca92aa5d895561db9cad0a442a15d72ed7f20a66730f71aedcd
languageName: node
linkType: hard
"undici@npm:6.9.0":
version: 6.9.0
resolution: "undici@npm:6.9.0"