mirror of
https://github.com/toast-ts/Daggerbot-TS.git
synced 2024-11-17 08:20:58 -05:00
Move essentials out of client.ts
This commit is contained in:
parent
6a5123ff34
commit
838b743f68
119
src/client.ts
119
src/client.ts
@ -1,6 +1,6 @@
|
||||
import Discord, {Client, GatewayIntentBits, Partials} from 'discord.js';
|
||||
import Discord from 'discord.js';
|
||||
import {readFileSync, readdirSync} from 'node:fs';
|
||||
import {formatTimeOpt, Tokens, Config, repeatedMessages, type MPServerCache} from './typings/interfaces';
|
||||
import {Tokens, Config, repeatedMessages, type MPServerCache} from './typings/interfaces';
|
||||
import bannedWords from './models/bannedWords.js';
|
||||
import userLevels from './models/userLevels.js';
|
||||
import suggestion from './models/suggestion.js';
|
||||
@ -23,7 +23,7 @@ try{
|
||||
console.log('Using production config')
|
||||
}
|
||||
|
||||
export default class TClient extends Client {
|
||||
export default class TClient extends Discord.Client {
|
||||
invites: Map<any, any>;
|
||||
commands: Discord.Collection<string, any>;
|
||||
registry: Array<Discord.ApplicationCommandDataResolvable>;
|
||||
@ -36,7 +36,6 @@ export default class TClient extends Client {
|
||||
attachmentBuilder: any;
|
||||
moment: typeof moment;
|
||||
xjs: typeof xjs;
|
||||
ms: any;
|
||||
userLevels: userLevels;
|
||||
punishments: punishments;
|
||||
bonkCount: bonkCount;
|
||||
@ -51,13 +50,13 @@ export default class TClient extends Client {
|
||||
constructor(){
|
||||
super({
|
||||
intents: [
|
||||
GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers,
|
||||
GatewayIntentBits.GuildModeration, GatewayIntentBits.GuildInvites,
|
||||
GatewayIntentBits.GuildMessageReactions, GatewayIntentBits.GuildPresences,
|
||||
GatewayIntentBits.MessageContent, GatewayIntentBits.GuildMessages,
|
||||
GatewayIntentBits.GuildVoiceStates, GatewayIntentBits.DirectMessages
|
||||
Discord.GatewayIntentBits.Guilds, Discord.GatewayIntentBits.GuildMembers,
|
||||
Discord.GatewayIntentBits.GuildModeration, Discord.GatewayIntentBits.GuildInvites,
|
||||
Discord.GatewayIntentBits.GuildMessageReactions, Discord.GatewayIntentBits.GuildPresences,
|
||||
Discord.GatewayIntentBits.MessageContent, Discord.GatewayIntentBits.GuildMessages,
|
||||
Discord.GatewayIntentBits.GuildVoiceStates, Discord.GatewayIntentBits.DirectMessages
|
||||
], partials: [
|
||||
Partials.Channel, Partials.Reaction, Partials.Message
|
||||
Discord.Partials.Channel, Discord.Partials.Reaction, Discord.Partials.Message
|
||||
], allowedMentions: {users:[],roles:[]}
|
||||
})
|
||||
this.invites = new Map();
|
||||
@ -75,7 +74,6 @@ export default class TClient extends Client {
|
||||
this.attachmentBuilder = Discord.AttachmentBuilder;
|
||||
this.moment = moment;
|
||||
this.xjs = xjs;
|
||||
this.ms = import('ms').then(i=>i);
|
||||
this.userLevels = new userLevels(this);
|
||||
this.bonkCount = new bonkCount(this);
|
||||
this.punishments = new punishments(this);
|
||||
@ -109,106 +107,7 @@ export default class TClient extends Client {
|
||||
}
|
||||
}
|
||||
}
|
||||
formatTime(integer: number, accuracy = 1, options?: formatTimeOpt){
|
||||
let achievedAccuracy = 0;
|
||||
let text:any = '';
|
||||
for (const timeName of [
|
||||
{name: 'year', length: 31536000000},
|
||||
{name: 'month', length: 2592000000},
|
||||
{name: 'week', length: 604800000},
|
||||
{name: 'day', length: 86400000},
|
||||
{name: 'hour', length: 3600000},
|
||||
{name: 'minute', length: 60000},
|
||||
{name: 'second', length: 1000}
|
||||
]){
|
||||
if (achievedAccuracy < accuracy){
|
||||
const fullTimelengths = Math.floor(integer/timeName.length);
|
||||
if (fullTimelengths === 0) continue;
|
||||
achievedAccuracy++;
|
||||
text += fullTimelengths + (options?.longNames ? (' '+timeName.name+(fullTimelengths === 1 ? '' : 's')) : timeName.name.slice(0, timeName.name === 'month' ? 2 : 1)) + (options?.commas ? ', ' : ' ');
|
||||
integer -= fullTimelengths*timeName.length;
|
||||
} else break;
|
||||
}
|
||||
if (text.length == 0) text = integer + (options?.longNames ? ' milliseconds' : 'ms') + (options?.commas ? ', ' : '');
|
||||
if (options?.commas){
|
||||
text = text.slice(0, -2);
|
||||
if (options?.longNames){
|
||||
text = text.split('');
|
||||
text[text.lastIndexOf(',')] = ' and';
|
||||
text = text.join('');
|
||||
}
|
||||
} return text.trim();
|
||||
}
|
||||
formatPlayerUptime(oldTime:number){
|
||||
var Hours=0;
|
||||
oldTime=Math.floor(Number(oldTime));
|
||||
if(oldTime>=60){
|
||||
var Hours=Math.floor(Number(oldTime)/60);
|
||||
var Minutes=(Number(oldTime)-(Hours*60));
|
||||
} else Minutes=Number(oldTime)
|
||||
if(Hours>=24){
|
||||
var Days=Math.floor(Number(Hours)/24);
|
||||
var Hours=(Hours-(Days*24));
|
||||
} return (Days>0?Days+' d ':'')+(Hours>0?Hours+' h ':'')+(Minutes>0?Minutes+' m':'')
|
||||
}
|
||||
isStaff = (guildMember:Discord.GuildMember)=>this.config.mainServer.staffRoles.map((x: string)=>this.config.mainServer.roles[x]).some((x: string)=>guildMember.roles.cache.has(x));
|
||||
|
||||
youNeedRole = (interaction:Discord.CommandInteraction, role:string)=>interaction.reply(`This command is restricted to <@&${this.config.mainServer.roles[role]}>`);
|
||||
|
||||
logTime = ()=>`[${this.moment().format('DD/MM/YY HH:mm:ss')}]`;
|
||||
|
||||
async punish(interaction: Discord.ChatInputCommandInteraction<'cached'>, type: string){
|
||||
if (!this.isStaff(interaction.member as Discord.GuildMember)) return this.youNeedRole(interaction, "dcmod");
|
||||
|
||||
const time = interaction.options.getString('time') ?? undefined;
|
||||
const reason = interaction.options.getString('reason') ?? 'Reason unspecified';
|
||||
const GuildMember = interaction.options.getMember('member') ?? undefined;
|
||||
const User = interaction.options.getUser('member', true);
|
||||
|
||||
console.log(this.logTime(), `[PunishmentLog] ${GuildMember?.user?.username ?? User?.username ?? 'No user data'} ${time ? ['warn', 'kick'].includes(this.punishments.type) ? 'and no duration set' : `and ${time} (duration)` : ''} was used in /${interaction.commandName} for ${reason}`);
|
||||
(this.channels.cache.get(this.config.mainServer.channels.punishment_log) as Discord.TextChannel).send({embeds:[new this.embed().setColor(this.config.embedColor).setAuthor({name: interaction?.user?.username, iconURL: interaction?.user?.displayAvatarURL({size:2048})}).setTitle('Punishment Log').setDescription(`${GuildMember?.user?.username ?? User?.username ?? 'No user data'} ${time ? ['warn', 'kick'].includes(this.punishments.type) ? 'and no duration set' : `and ${time} (duration)` : ''} was used in \`/${interaction.commandName}\` for \`${reason}\``).setTimestamp()]});
|
||||
if (interaction.user.id === User.id) return interaction.reply(`You cannot ${type} yourself.`);
|
||||
if (!GuildMember && type != 'unban') return interaction.reply(`You cannot ${type} someone who is not in the server.`);
|
||||
if (User.bot) return interaction.reply(`You cannot ${type} a bot!`);
|
||||
|
||||
await interaction.deferReply();
|
||||
await this.punishments.addPunishment(type, { time, interaction }, interaction.user.id, reason, User, GuildMember);
|
||||
}
|
||||
async YTLoop(YTChannelID: string, YTChannelName: string, DCChannelID: string){
|
||||
let Data:any;
|
||||
|
||||
try {
|
||||
await fetch(`https://www.youtube.com/feeds/videos.xml?channel_id=${YTChannelID}`, {signal: AbortSignal.timeout(6000),headers:{'User-Agent':`Daggerbot - Notification/undici`}}).then(async xml=>Data = this.xjs.xml2js(await xml.text(), {compact: true}))
|
||||
} catch(err){
|
||||
console.log(this.logTime(), `${YTChannelName} YT fail`)
|
||||
}
|
||||
|
||||
if (!Data) return;
|
||||
if (!this.YTCache[YTChannelID]) return this.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text;
|
||||
if (Data.feed.entry[1]['yt:videoId']._text === this.YTCache[YTChannelID]){
|
||||
this.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text;
|
||||
(this.channels.resolve(DCChannelID) as Discord.TextChannel).send(`**${YTChannelName}** just uploaded a video!\n${Data.feed.entry[0].link._attributes.href}`)
|
||||
}
|
||||
}
|
||||
formatBytes(bytes:number, decimals:number = 2) {
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const k = 1024;
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals < 0 ? 0 : decimals)) + ' ' + ['Bytes', 'KB', 'MB', 'GB'][i];
|
||||
}
|
||||
removeUsername = (text: string)=>{
|
||||
let matchesLeft = true;
|
||||
const dirSlash = process.platform === 'linux' ? '\/' : '\\';
|
||||
const array = text.split(dirSlash);
|
||||
while (matchesLeft){
|
||||
let usersIndex = array.indexOf(process.platform === 'linux' ? 'home' : 'Users');
|
||||
if (usersIndex<1) matchesLeft = false;
|
||||
else {
|
||||
let usernameIndex = usersIndex+1;
|
||||
if(array[usernameIndex].length == 0) usernameIndex += 1;
|
||||
array[usernameIndex] = '・'.repeat(array[usernameIndex].length);
|
||||
array[usersIndex] = process.platform === 'linux' ? 'ho\u200bme' : 'Us\u200bers';
|
||||
}
|
||||
} return array.join(dirSlash);
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import Discord from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
import Punish from '../funcs/Punish.js';
|
||||
export default {
|
||||
run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
|
||||
client.punish(interaction, 'ban');
|
||||
Punish(client, interaction, 'ban');
|
||||
},
|
||||
data: new Discord.SlashCommandBuilder()
|
||||
.setName('ban')
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Discord from "discord.js";
|
||||
import Discord from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
import FormatTime from '../helpers/FormatTime.js';
|
||||
export default {
|
||||
run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
|
||||
if (!client.isStaff(interaction.member)) return client.youNeedRole(interaction, 'dcmod');
|
||||
@ -21,7 +22,7 @@ export default {
|
||||
{name: '🔹 Moderator', value: `<@${punishment.moderator}> \`${punishment.moderator}\``, inline: true},
|
||||
{name: '\u200b', value: '\u200b', inline: true},
|
||||
{name: '🔹 Reason', value: `\`${punishment.reason || 'Reason unspecified'}\``, inline: true})
|
||||
if (punishment.duration) embed.addFields({name: '🔹 Duration', value: client.formatTime(punishment.duration, 100)})
|
||||
if (punishment.duration) embed.addFields({name: '🔹 Duration', value: `${FormatTime(punishment.duration, 100)}`})
|
||||
if (punishment.expired) embed.addFields({name: '🔹 Expired', value: `This case has been overwritten by Case #${cancelledBy.id} for reason \`${cancelledBy.reason}\``})
|
||||
if (punishment.cancels) embed.addFields({name: '🔹 Overwrites', value: `This case overwrites Case #${cancels.id} with reason \`${cancels.reason}\``})
|
||||
interaction.reply({embeds: [embed]});
|
||||
@ -34,7 +35,7 @@ export default {
|
||||
const userPunishment = userPunishmentData.sort((a,b)=>a.time-b.time).map((punishment)=>{
|
||||
return {
|
||||
name: `${punishment.type[0].toUpperCase()+punishment.type.slice(1)} | Case #${punishment.id}`,
|
||||
value: `Reason: \`${punishment.reason}\`\n${punishment.duration ? `Duration: ${client.formatTime(punishment.duration, 3)}\n` : ''}Moderator: <@${punishment.moderator}>${punishment.expired ? `\nOverwritten by Case #${punishments.find(x=>x.cancels===punishment._id)?._id}` : ''}${punishment.cancels ? `\nOverwrites Case #${punishment.cancels}` : ''}`
|
||||
value: `Reason: \`${punishment.reason}\`\n${punishment.duration ? `Duration: ${FormatTime(punishment.duration, 3)}\n` : ''}Moderator: <@${punishment.moderator}>${punishment.expired ? `\nOverwritten by Case #${punishments.find(x=>x.cancels===punishment._id)?._id}` : ''}${punishment.cancels ? `\nOverwrites Case #${punishment.cancels}` : ''}`
|
||||
}
|
||||
});
|
||||
if (!punishments || !userPunishment) return interaction.reply(`**${user.username}** has a clean record.`)
|
||||
|
@ -3,6 +3,8 @@ import {Octokit} from '@octokit/rest';
|
||||
import {createTokenAuth} from '@octokit/auth-token';
|
||||
import {exec} from 'node:child_process';
|
||||
import MessageTool from '../helpers/MessageTool.js';
|
||||
import UsernameHelper from '../helpers/UsernameHelper.js';
|
||||
import FormatTime from '../helpers/FormatTime.js';
|
||||
import fs from 'node:fs';
|
||||
import util from 'node:util';
|
||||
import TClient from '../client.js';
|
||||
@ -27,7 +29,7 @@ export default {
|
||||
const filter = (x:any)=>x.content === 'stack' && x.author.id === interaction.user.id
|
||||
const messagecollector = (interaction.channel as Discord.TextChannel).createMessageCollector({filter, max: 1, time: 60000});
|
||||
messagecollector.on('collect', collected=>{
|
||||
collected.reply({content: `\`\`\`\n${client.removeUsername(err.stack)}\n\`\`\``, allowedMentions: {repliedUser: false}});
|
||||
collected.reply({content: `\`\`\`\n${UsernameHelper.stripName(err.stack)}\n\`\`\``, allowedMentions: {repliedUser: false}});
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -40,7 +42,7 @@ export default {
|
||||
].forEach(x=>output = output.replace(new RegExp(x as string,'g'),':noblank: No token?'));
|
||||
const embed = new client.embed().setColor(client.config.embedColor).setTitle('__Eval__').addFields(
|
||||
{name: 'Input', value: `\`\`\`js\n${code.slice(0,1010)}\n\`\`\``},
|
||||
{name: 'Output', value: `\`\`\`${client.removeUsername(output).slice(0,1016)}\n\`\`\``}
|
||||
{name: 'Output', value: `\`\`\`${UsernameHelper.stripName(output).slice(0,1016)}\n\`\`\``}
|
||||
);
|
||||
interaction.reply({embeds: [embed]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed]}));
|
||||
},
|
||||
@ -63,11 +65,11 @@ export default {
|
||||
}
|
||||
};
|
||||
exec('git pull',{windowsHide:true},(err:Error,stdout)=>{
|
||||
if (err) clarkson.edit(`\`\`\`${client.removeUsername(err.message)}\`\`\``)
|
||||
if (err) clarkson.edit(`\`\`\`${UsernameHelper.stripName(err.message)}\`\`\``)
|
||||
else if (stdout.includes('Already up to date')) clarkson.edit('I am already up to date with the upstream repository.')
|
||||
else clarkson.edit('Compiling TypeScript files...').then(()=>exec('yarn tsc', {windowsHide:true}, (err:Error)=>{
|
||||
if (err) clarkson.edit(`\`\`\`${client.removeUsername(err.message)}\`\`\``)
|
||||
if (interaction.options.getBoolean('restart')) clarkson.edit(`[Commit:](<${github.fetchCommit.url}>) **${github.fetchCommit.msg}**\nCommit author: **${github.fetchCommit.author}**\n\n__Commit changes__\nTotal: **${github.fetchChanges.total}**\nAdditions: **${github.fetchChanges.addition}**\nDeletions: **${github.fetchChanges.deletion}**\n\nSuccessfully compiled TypeScript files into JavaScript!\nUptime before restarting: **${client.formatTime(client.uptime, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot', {windowsHide:true}));
|
||||
if (err) clarkson.edit(`\`\`\`${UsernameHelper.stripName(err.message)}\`\`\``)
|
||||
if (interaction.options.getBoolean('restart')) clarkson.edit(`[Commit:](<${github.fetchCommit.url}>) **${github.fetchCommit.msg}**\nCommit author: **${github.fetchCommit.author}**\n\n__Commit changes__\nTotal: **${github.fetchChanges.total}**\nAdditions: **${github.fetchChanges.addition}**\nDeletions: **${github.fetchChanges.deletion}**\n\nSuccessfully compiled TypeScript files into JavaScript!\nUptime before restarting: **${FormatTime(client.uptime, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot', {windowsHide:true}));
|
||||
else clarkson.edit(`[Commit:](<${github.fetchCommit.url}>) **${github.fetchCommit.msg}**\nCommit author: **${github.fetchCommit.author}**\n\n__Commit changes__\nTotal: **${github.fetchChanges.total}**\nAdditions: **${github.fetchChanges.addition}**\nDeletions: **${github.fetchChanges.deletion}**\n\nSuccessfully compiled TypeScript files into JavaScript!`)
|
||||
}))
|
||||
})
|
||||
@ -112,15 +114,15 @@ export default {
|
||||
restart: async()=>{
|
||||
const i = await interaction.reply({content: 'Compiling TypeScript files...', fetchReply: true});
|
||||
exec('yarn tsc',{windowsHide:true},(err:Error)=>{
|
||||
if (err) i.edit(`\`\`\`${client.removeUsername(err.message)}\`\`\``)
|
||||
else i.edit(`Successfully compiled TypeScript files into JavaScript!\nUptime before restarting: **${client.formatTime(client.uptime, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot', {windowsHide:true}))
|
||||
if (err) i.edit(`\`\`\`${UsernameHelper.stripName(err.message)}\`\`\``)
|
||||
else i.edit(`Successfully compiled TypeScript files into JavaScript!\nUptime before restarting: **${FormatTime(client.uptime, 3, {commas: true, longNames: true})}**`).then(()=>exec('pm2 restart Daggerbot', {windowsHide:true}))
|
||||
})
|
||||
},
|
||||
file: ()=>interaction.reply({files:[`./src/database/${interaction.options.getString('name')}.json`]}).catch(()=>'Filesize is too large, upload cancelled.'),
|
||||
wake_device: async()=>{
|
||||
const i = await interaction.reply({content: 'Spawning a task...', fetchReply: true});
|
||||
exec(`cd "../../Desktop/System Tools/wakemeonlan" && WakeMeOnLan.exe /wakeup ${interaction.options.getString('name')}`, {windowsHide:true}, (err:Error)=>{
|
||||
if (err) i.edit(client.removeUsername(err.message))
|
||||
if (err) i.edit(UsernameHelper.stripName(err.message))
|
||||
else i.edit('Your device should be awake by now!\n||Don\'t blame me if it isn\'t on.||')
|
||||
})
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
import Discord from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
import Punish from '../funcs/Punish.js';
|
||||
export default {
|
||||
run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
|
||||
client.punish(interaction, 'kick');
|
||||
Punish(client, interaction, 'kick');
|
||||
},
|
||||
data: new Discord.SlashCommandBuilder()
|
||||
.setName('kick')
|
||||
|
@ -2,9 +2,10 @@ import Discord from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
import path from 'node:path';
|
||||
import canvas from 'canvas';
|
||||
import FormatPlayer from '../helpers/FormatPlayer.js';
|
||||
import MessageTool from '../helpers/MessageTool.js';
|
||||
import {readFileSync} from 'node:fs';
|
||||
import {FSData} from 'src/typings/interfaces.js';
|
||||
import {FSData} from '../typings/interfaces.js';
|
||||
|
||||
const serverChoices = [
|
||||
{name: 'Main Server', value: 'mainServer'},
|
||||
@ -149,11 +150,7 @@ export default {
|
||||
else if (endpoint.slots.used > 8) Color = client.config.embedColorYellow;
|
||||
else Color = client.config.embedColorGreen;
|
||||
|
||||
for (const player of endpoint.slots.players.filter(x=>x.isUsed)){
|
||||
let decorator = player.isAdmin ? ':detective:' : '';
|
||||
decorator += player.name.includes('Toast') ? '<:toastv2:1132681026662056079>' : '';
|
||||
playerData.push(`**${player.name}${decorator}**\nFarming for ${client.formatPlayerUptime(player.uptime)}`)
|
||||
}
|
||||
for (const player of endpoint.slots.players.filter(x=>x.isUsed)) playerData.push(`**${player.name}${FormatPlayer.decoratePlayerIcons(player)}**\nFarming for ${FormatPlayer.uptimeFormat(player.uptime)}`)
|
||||
|
||||
const slot = `${endpoint.slots.used}/${endpoint.slots.capacity}`;
|
||||
const ingameTime = `${('0'+Math.floor((endpoint.server.dayTime/3600/1000))).slice(-2)}:${('0'+Math.floor((endpoint.server.dayTime/60/1000)%60)).slice(-2)}`;
|
||||
|
@ -1,8 +1,9 @@
|
||||
import Discord from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
import Punish from '../funcs/Punish.js';
|
||||
export default {
|
||||
run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
|
||||
client.punish(interaction, 'mute');
|
||||
Punish(client, interaction, 'mute');
|
||||
},
|
||||
data: new Discord.SlashCommandBuilder()
|
||||
.setName('mute')
|
||||
|
@ -1,10 +1,11 @@
|
||||
import Discord from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
import FormatTime from '../helpers/FormatTime.js';
|
||||
export default {
|
||||
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
|
||||
if (client.uptime < 15500) return interaction.reply('I just restarted, wait 15 seconds and try again.')
|
||||
const msg = await interaction.reply({content: 'Pinging...', fetchReply: true})
|
||||
msg.edit(`API Latency: \`${client.formatTime(client.ws.ping, 3, {longNames: false, commas: true})}\`\nBot Latency: \`${client.formatTime(msg.createdTimestamp - interaction.createdTimestamp, 3, {longNames: false, commas: true})}\``)
|
||||
msg.edit(`API Latency: \`${FormatTime(client.ws.ping, 3, {longNames: false, commas: true})}\`\nBot Latency: \`${FormatTime(msg.createdTimestamp - interaction.createdTimestamp, 3, {longNames: false, commas: true})}\``)
|
||||
},
|
||||
data: new Discord.SlashCommandBuilder()
|
||||
.setName('ping')
|
||||
|
@ -1,8 +1,9 @@
|
||||
import Discord from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
import Punish from '../funcs/Punish.js';
|
||||
export default {
|
||||
run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
|
||||
client.punish(interaction, 'softban');
|
||||
Punish(client, interaction, 'softban');
|
||||
},
|
||||
data: new Discord.SlashCommandBuilder()
|
||||
.setName('softban')
|
||||
|
@ -1,6 +1,8 @@
|
||||
import Discord from 'discord.js';
|
||||
import pkg from 'typescript';
|
||||
import MessageTool from '../helpers/MessageTool.js';
|
||||
import FormatBytes from '../helpers/FormatBytes.js';
|
||||
import FormatTime from '../helpers/FormatTime.js';
|
||||
import si from 'systeminformation';
|
||||
import TClient from '../client.js';
|
||||
import os from 'node:os';
|
||||
@ -16,7 +18,7 @@ export default {
|
||||
|
||||
const columns = ['Command name', 'Count'];
|
||||
const includedCommands = client.commands.filter(x=>x.uses).sort((a,b)=>b.uses - a.uses);
|
||||
if (includedCommands.size === 0) return interaction.reply(`No commands have been used yet.\nUptime: **${client.formatTime(client.uptime, 3, {longNames: true, commas: true})}**`);
|
||||
if (includedCommands.size === 0) return interaction.reply(`No commands have been used yet.\nUptime: **${FormatTime(client.uptime, 3, {longNames: true, commas: true})}**`);
|
||||
const nameLength = Math.max(...includedCommands.map(x=>x.command.default.data.name.length), columns[0].length) + 2;
|
||||
const amountLength = Math.max(...includedCommands.map(x=>x.uses.toString().length), columns[1].length) + 1;
|
||||
const rows = [`${columns[0] + ' '.repeat(nameLength - columns[0].length)}|${' '.repeat(amountLength - columns[1].length) + columns[1]}\n`, '-'.repeat(nameLength) + '-'.repeat(amountLength) + '\n'];
|
||||
@ -50,13 +52,13 @@ export default {
|
||||
{name: '> __Host__', value: MessageTool.concatMessage(
|
||||
`**Operating System:** ${osInfo.distro + ' ' + osInfo.release}`,
|
||||
`**CPU:** ${cpu.manufacturer} ${cpu.brand}`,
|
||||
`**Memory:** ${client.formatBytes(ram.used)}/${client.formatBytes(ram.total)}`,
|
||||
`**Process:** ${client.formatBytes(process.memoryUsage().heapUsed)}/${client.formatBytes(process.memoryUsage().heapTotal)}`,
|
||||
`**Memory:** ${FormatBytes(ram.used)}/${FormatBytes(ram.total)}`,
|
||||
`**Process:** ${FormatBytes(process.memoryUsage().heapUsed)}/${FormatBytes(process.memoryUsage().heapTotal)}`,
|
||||
`**Load Usage:**\nUser: ${currentLoad.currentLoadUser.toFixed(1)}%\nSystem: ${currentLoad.currentLoadSystem.toFixed(1)}%`,
|
||||
`**Uptime:**\nHost: ${client.formatTime((os.uptime()*1000), 2, {longNames: true, commas: true})}\nBot: ${client.formatTime(client.uptime, 2, {commas: true, longNames: true})}`
|
||||
`**Uptime:**\nHost: ${FormatTime((os.uptime()*1000), 2, {longNames: true, commas: true})}\nBot: ${FormatTime(client.uptime, 2, {commas: true, longNames: true})}`
|
||||
)}
|
||||
);
|
||||
waitForData.edit({content:null,embeds:[embed]}).then(x=>x.edit({embeds:[new client.embed(x.embeds[0].data).setFooter({text: `Load time: ${client.formatTime(x.createdTimestamp - interaction.createdTimestamp, 2, {longNames: true, commas: true})}`})]}))
|
||||
waitForData.edit({content:null,embeds:[embed]}).then(x=>x.edit({embeds:[new client.embed(x.embeds[0].data).setFooter({text: `Load time: ${FormatTime(x.createdTimestamp - interaction.createdTimestamp, 2, {longNames: true, commas: true})}`})]}))
|
||||
},
|
||||
data: new Discord.SlashCommandBuilder()
|
||||
.setName('statistics')
|
||||
|
@ -1,8 +1,9 @@
|
||||
import Discord from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
import Punish from '../funcs/Punish.js';
|
||||
export default {
|
||||
run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
|
||||
client.punish(interaction, 'warn');
|
||||
Punish(client, interaction, 'warn');
|
||||
},
|
||||
data: new Discord.SlashCommandBuilder()
|
||||
.setName('warn')
|
||||
|
@ -1,5 +1,6 @@
|
||||
import Discord from 'discord.js';
|
||||
import TClient from '../client';
|
||||
import FormatPlayer from '../helpers/FormatPlayer.js';
|
||||
import {writeFileSync, readFileSync} from 'node:fs';
|
||||
import {FSPlayer, FSData, FSCareerSavegame, TServer} from '../typings/interfaces';
|
||||
|
||||
@ -12,12 +13,6 @@ export default async(client:TClient, Channel:string, Message:string, Server:TSer
|
||||
const serverErrorEmbed = new client.embed().setColor(client.config.embedColorRed).setTitle('Host did not respond back in time');
|
||||
const genericEmbed = new client.embed();
|
||||
|
||||
const decoPlayer = (player:FSPlayer)=>{
|
||||
let decorator = player.isAdmin ? ':detective:' : '';
|
||||
decorator += player.name.includes('Toast') ? '<:toastv2:1132681026662056079>' : '';
|
||||
return decorator
|
||||
}
|
||||
|
||||
const HITALL = async()=>{
|
||||
let sessionInit = {signal: AbortSignal.timeout(8200),headers:{'User-Agent':`Daggerbot - HITALL/undici`}};
|
||||
try {
|
||||
@ -42,17 +37,17 @@ export default async(client:TClient, Channel:string, Message:string, Server:TSer
|
||||
|
||||
// Join/Leave log
|
||||
function playerLogEmbed(player:FSPlayer,joinLog:boolean){
|
||||
const logEmbed = new client.embed().setDescription(`**${player.name}${decoPlayer(player)}** ${joinLog ? 'joined' : 'left'} **${client.MPServerCache[ServerName].name}** at <t:${Math.round(Date.now()/1000)}:t>`);
|
||||
const logEmbed = new client.embed().setDescription(`**${player.name}${FormatPlayer.decoratePlayerIcons(player)}** ${joinLog ? 'joined' : 'left'} **${client.MPServerCache[ServerName].name}** at <t:${Math.round(Date.now()/1000)}:t>`);
|
||||
if (joinLog) return logEmbed.setColor(client.config.embedColorGreen);
|
||||
else if (player.uptime > 0) return logEmbed.setColor(client.config.embedColorRed).setFooter({text:`Farmed for ${client.formatPlayerUptime(player.uptime)}`});
|
||||
else if (player.uptime > 0) return logEmbed.setColor(client.config.embedColorRed).setFooter({text:`Farmed for ${FormatPlayer.uptimeFormat(player.uptime)}`});
|
||||
else return logEmbed.setColor(client.config.embedColorRed);
|
||||
}
|
||||
|
||||
const serverLog = client.channels.resolve(client.config.mainServer.channels.fs_server_log) as Discord.TextChannel;
|
||||
const playersOnServer = hitDSS.slots?.players.filter(x=>x.isUsed);
|
||||
const playersInCache = client.MPServerCache[ServerName].players;
|
||||
if (!playersOnServer ?? playersOnServer === undefined) return new Error('[MPLoop] Empty array, ignoring...'); // For the love of god, stop throwing errors everytime.
|
||||
playersOnServer.forEach(player=>playerData.push(`**${player.name}${decoPlayer(player)}**\nFarming for ${client.formatPlayerUptime(player.uptime)}`));
|
||||
if (!playersOnServer ?? playersOnServer === undefined) return console.log('[MPLoop] Empty array, ignoring...'); // For the love of god, stop throwing errors everytime.
|
||||
playersOnServer.forEach(player=>playerData.push(`**${player.name}${FormatPlayer.decoratePlayerIcons(player)}**\nFarming for ${FormatPlayer.uptimeFormat(player.uptime)}`));
|
||||
|
||||
// Player leaving
|
||||
for (const player of playersInCache.filter(x=>!playersOnServer.some(y=>y.name === x.name))){
|
||||
|
20
src/funcs/Punish.ts
Normal file
20
src/funcs/Punish.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import Discord from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
|
||||
export default async(client:TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>, type: string)=>{
|
||||
if (!client.isStaff(interaction.member as Discord.GuildMember)) return client.youNeedRole(interaction, 'dcmod');
|
||||
|
||||
const time = interaction.options.getString('time') ?? undefined;
|
||||
const reason = interaction.options.getString('reason') ?? 'Reason unspecified';
|
||||
const GuildMember = interaction.options.getMember('member') ?? undefined;
|
||||
const User = interaction.options.getUser('member', true);
|
||||
|
||||
console.log(client.logTime(), `[PunishmentLog] ${GuildMember?.user?.username ?? User?.username ?? 'No user data'} ${time ? ['warn', 'kick'].includes(type) ? 'and no duration set' : `and ${time} (duration)` : ''} was used in /${interaction.commandName} for ${reason}`);
|
||||
(client.channels.cache.get(client.config.mainServer.channels.punishment_log) as Discord.TextChannel).send({embeds:[new client.embed().setColor(client.config.embedColor).setAuthor({name: interaction?.user?.username, iconURL: interaction?.user?.displayAvatarURL({size:2048})}).setTitle('Punishment Log').setDescription(`${GuildMember?.user?.username ?? User?.username ?? 'No user data'} ${time ? ['warn', 'kick'].includes(client.punishments.type) ? 'and no duration set' : `and ${time} (duration)` : ''} was used in \`/${interaction.commandName}\` for \`${reason}\``).setTimestamp()]});
|
||||
if (interaction.user.id === User.id) return interaction.reply(`You cannot ${type} yourself.`);
|
||||
if (!GuildMember && type != 'unban') return interaction.reply(`You cannot ${type} someone who is not in the server.`);
|
||||
if (User.bot) return interaction.reply(`You cannot ${type} a bot!`);
|
||||
|
||||
await interaction.deferReply();
|
||||
await client.punishments.addPunishment(type, {time, interaction}, interaction.user.id, reason, User, GuildMember);
|
||||
}
|
18
src/funcs/YTLoop.ts
Normal file
18
src/funcs/YTLoop.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import {TextChannel} from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
|
||||
export default async(client: TClient, YTChannelID: string, YTChannelName: string, DiscordChannelID: string, DiscordRoleID: string)=>{
|
||||
let Data: any;
|
||||
try {
|
||||
await fetch(`https://www.youtube.com/feeds/videos.xml?channel_id=${YTChannelID}`, {signal: AbortSignal.timeout(8000), headers: {'User-Agent': 'Daggerbot - Notification/undici'}}).then(async xml=>Data = client.xjs.xml2js(await xml.text(), {compact: true}))
|
||||
} catch(err){
|
||||
console.log(client.logTime(), `Failed to fetch "${YTChannelName}" from YouTube`)
|
||||
}
|
||||
|
||||
if (!Data) return;
|
||||
if (!client.YTCache[YTChannelID]) return client.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text;
|
||||
if (Data.feed.entry[1]['yt:videoId']._text === client.YTCache[YTChannelID]){
|
||||
client.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text;
|
||||
(client.channels.resolve(DiscordChannelID) as TextChannel).send({content: `<@&${DiscordRoleID}> (Ping notification are currently WIP, no eta when complete, the mentioned role is a placeholder for now)\n**${YTChannelName}** just uploaded a video!\n${Data.feed.entry[0].link._attributes.href}`, allowedMentions: {parse: ['roles']}})
|
||||
}
|
||||
}
|
5
src/helpers/FormatBytes.ts
Normal file
5
src/helpers/FormatBytes.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export default (bytes:number, decimals:number = 2)=>{
|
||||
if (bytes === 0) return '0 Bytes';
|
||||
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
||||
return parseFloat((bytes / Math.pow(1024, i)).toFixed(decimals < 0 ? 0 : decimals))+ ' ' +['Bytes', 'KB', 'MB', 'GB', 'TB'][i]
|
||||
}
|
22
src/helpers/FormatPlayer.ts
Normal file
22
src/helpers/FormatPlayer.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import {FSPlayer} from '../typings/interfaces';
|
||||
|
||||
export default class FormatPlayer {
|
||||
static uptimeFormat(playTime: number){
|
||||
var Hours = 0;
|
||||
playTime = Math.floor(Number(playTime));
|
||||
if(playTime >= 60){
|
||||
var Hours = Math.floor(Number(playTime)/60);
|
||||
var Minutes = (Number(playTime)-(Hours*60));
|
||||
} else Minutes = Number(playTime)
|
||||
if(Hours >= 24){
|
||||
var Days = Math.floor(Number(Hours)/24);
|
||||
var Hours = (Hours-(Days*24));
|
||||
} return (Days > 0 ? Days+' d ':'')+(Hours > 0 ? Hours+' h ':'')+(Minutes > 0 ? Minutes+' m':'')
|
||||
}
|
||||
static decoratePlayerIcons(player:FSPlayer){
|
||||
let decorator = player.isAdmin ? ':detective:' : '';
|
||||
decorator += player.name.includes('Toast') ? '<:toast:1132681026662056079>' : '';
|
||||
decorator += player.name.includes('Daggerwin') ? '<:Daggerwin:549283056079339520>' : ''; // Probably useless lol, but we'll see.
|
||||
return decorator
|
||||
}
|
||||
}
|
35
src/helpers/FormatTime.ts
Normal file
35
src/helpers/FormatTime.ts
Normal file
@ -0,0 +1,35 @@
|
||||
interface formatTimeOpt {
|
||||
longNames: boolean,
|
||||
commas: boolean
|
||||
}
|
||||
|
||||
export default (integer:number, accuracy:number = 1, options?:formatTimeOpt)=>{
|
||||
let achievedAccuracy = 0;
|
||||
let text:any = '';
|
||||
for (const timeName of [
|
||||
{name: 'year', length: 31536000000},
|
||||
{name: 'month', length: 2592000000},
|
||||
{name: 'week', length: 604800000},
|
||||
{name: 'day', length: 86400000},
|
||||
{name: 'hour', length: 3600000},
|
||||
{name: 'minute', length: 60000},
|
||||
{name: 'second', length: 1000}
|
||||
]){
|
||||
if (achievedAccuracy < accuracy){
|
||||
const fullTimelengths = Math.floor(integer/timeName.length);
|
||||
if (fullTimelengths === 0) continue;
|
||||
achievedAccuracy++;
|
||||
text += fullTimelengths + (options?.longNames ? (' '+timeName.name+(fullTimelengths === 1 ? '' : 's')) : timeName.name.slice(0, timeName.name === 'month' ? 2 : 1)) + (options?.commas ? ', ' : ' ');
|
||||
integer -= fullTimelengths*timeName.length;
|
||||
} else break;
|
||||
}
|
||||
if (text.length === 0) text = integer + (options?.longNames ? ' milliseconds' : 'ms') + (options?.commas ? ', ' : '');
|
||||
if (options?.commas){
|
||||
text = text.slice(0, -2);
|
||||
if (options?.longNames){
|
||||
text = text.split('');
|
||||
text[text.lastIndexOf(',')] = ' and';
|
||||
text = text.join('');
|
||||
}
|
||||
} return text.trim();
|
||||
}
|
18
src/helpers/UsernameHelper.ts
Normal file
18
src/helpers/UsernameHelper.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export default class UsernameHelper {
|
||||
static stripName(text: string){
|
||||
let matchesLeft = true;
|
||||
const dirSlash = process.platform === 'linux' ? '\/' : '\\';
|
||||
const array = text.split(dirSlash);
|
||||
while (matchesLeft) {
|
||||
let usersIndex = array.indexOf(process.platform === 'linux' ? 'media' : 'Users');
|
||||
if (usersIndex < 1) matchesLeft = false;
|
||||
else {
|
||||
let usernameIndex = usersIndex + 1;
|
||||
if (array[usernameIndex].length === 0) usernameIndex += 1;
|
||||
array[usernameIndex] = '・'.repeat(array[usernameIndex].length);
|
||||
array[usersIndex] = process.platform === 'linux' ? 'med\u200bia' : 'Us\u200bers';
|
||||
}
|
||||
return array.join(dirSlash);
|
||||
}
|
||||
}
|
||||
}
|
13
src/index.ts
13
src/index.ts
@ -2,6 +2,7 @@ import Discord from 'discord.js';
|
||||
import TClient from './client.js';
|
||||
const client = new TClient;
|
||||
client.init();
|
||||
import YTLoop from './funcs/YTLoop.js';
|
||||
import MPLoop from './funcs/MPLoop.js';
|
||||
import {Player} from 'discord-player';
|
||||
const player = Player.singleton(client);
|
||||
@ -10,11 +11,7 @@ import {writeFileSync, readFileSync} from 'node:fs';
|
||||
|
||||
// Error handler
|
||||
function DZ(error:Error, type:string){// Yes, I may have shiternet but I don't need to wake up to like a hundred messages or so.
|
||||
if ([
|
||||
'ConnectTimeoutError: Connect Timeout Error', 'getaddrinfo EAI_AGAIN discord.com',
|
||||
'[Error: 30130000:error:0A000410:SSL', '[Error: F8200000:error:0A000410:SSL',
|
||||
'HTTPError: Internal Server Error'
|
||||
].includes(error.message)) return;
|
||||
if (JSON.parse(readFileSync('src/errorBlocklist.json', 'utf8')).includes(error.message)) return;// I wonder if my idea works, if not then please run me over with a bulldozer.
|
||||
console.error(error);
|
||||
(client.channels.resolve(client.config.mainServer.channels.errors) as Discord.TextChannel | null)?.send({embeds: [new client.embed().setColor('#560000').setTitle('Error caught!').setFooter({text: 'Error type: ' + type}).setDescription(`**Error:**\n\`\`\`${error.message}\`\`\`**Stack:**\n\`\`\`${`${error.stack}`.slice(0, 2500)}\`\`\``)]})
|
||||
}
|
||||
@ -41,9 +38,9 @@ if (client.config.botSwitches.mpstats) setInterval(async()=>{
|
||||
const serverlake = (await client.MPServer._content.findById(client.config.mainServer.id));
|
||||
for await (const [locName, locArea] of Object.entries(client.config.MPStatsLocation)) await MPLoop(client, locArea.channel, locArea.message, serverlake[locName], locName)
|
||||
}, 35000);
|
||||
setInterval(async()=>{
|
||||
client.YTLoop('UCQ8k8yTDLITldfWYKDs3xFg', 'Daggerwin', '528967918772551702'); // 528967918772551702 = #videos-and-streams
|
||||
client.YTLoop('UCguI73--UraJpso4NizXNzA', 'Machinery Restorer', '767444045520961567') // 767444045520961567 = #machinery-restorer
|
||||
setInterval(async()=>{// Ping notification is currently WIP, it might be active in production but I want to see how it goes with role mentions first so I can make any further changes.
|
||||
YTLoop(client, 'UCQ8k8yTDLITldfWYKDs3xFg', 'Daggerwin', '528967918772551702', '1011341005389307925'); // 528967918772551702 = #videos-and-streams; 1011341005389307925 = Bot Tech;
|
||||
YTLoop(client, 'UCguI73--UraJpso4NizXNzA', 'Machinery Restorer', '767444045520961567', '989591094524276796') // 767444045520961567 = #machinery-restorer; 989591094524276796 = Temp;
|
||||
}, 300000)
|
||||
|
||||
// Event loop for punishments and daily msgs
|
||||
|
@ -2,6 +2,7 @@ import Discord from 'discord.js';
|
||||
import TClient from '../client.js';
|
||||
import mongoose from 'mongoose';
|
||||
import ms from 'ms';
|
||||
import FormatTime from '../helpers/FormatTime.js';
|
||||
import {Punishment} from '../typings/interfaces.js';
|
||||
|
||||
const Schema = mongoose.model('punishments', new mongoose.Schema({
|
||||
@ -36,7 +37,7 @@ export default class punishments extends Schema {
|
||||
{name: '\u200b', value: '\u200b', inline: true},
|
||||
{name: '🔹 Reason', value: `\`${punishment.reason}\``, inline: true})
|
||||
.setColor(this.client.config.embedColor).setTimestamp(punishment.time)
|
||||
if (punishment.duration) embed.addFields({name: '🔹 Duration', value: this.client.formatTime(punishment.duration, 100), inline: true}, {name: '\u200b', value: '\u200b', inline: true})
|
||||
if (punishment.duration) embed.addFields({name: '🔹 Duration', value: `${FormatTime(punishment.duration, 100)}`, inline: true}, {name: '\u200b', value: '\u200b', inline: true})
|
||||
if (punishment.cancels) {
|
||||
const cancels = await this._content.findById(punishment.cancels);
|
||||
embed.addFields({name: '🔹 Overwrites', value: `This case overwrites Case #${cancels.id}\n\`${cancels.reason}\``})
|
||||
@ -72,7 +73,7 @@ export default class punishments extends Schema {
|
||||
if (type == 'mute') timeInMillis = time ? ms(time) : 2419140000; // Timeouts have a limit of 4 weeks
|
||||
else timeInMillis = time ? ms(time) : null;
|
||||
|
||||
const durationText = timeInMillis ? ` for ${this.client.formatTime(timeInMillis, 4, {longNames:true,commas:true})}` : '';
|
||||
const durationText = timeInMillis ? ` for ${FormatTime(timeInMillis, 4, {longNames:true,commas:true})}` : '';
|
||||
if (time) embed.addFields({name: 'Duration', value: durationText});
|
||||
|
||||
if (GuildMember){
|
||||
|
4
src/typings/interfaces.d.ts
vendored
4
src/typings/interfaces.d.ts
vendored
@ -4,10 +4,6 @@ export interface UserLevels {
|
||||
messages: number,
|
||||
level: number
|
||||
}
|
||||
export interface formatTimeOpt {
|
||||
longNames: boolean,
|
||||
commas: boolean
|
||||
}
|
||||
export interface punOpt {
|
||||
time?: string,
|
||||
reason?: string,
|
||||
|
Loading…
Reference in New Issue
Block a user