1
0
mirror of https://github.com/toast-ts/Daggerbot-TS.git synced 2024-09-30 00:50:59 -04:00
Daggerbot-TS/src/commands/dev.ts

246 lines
14 KiB
TypeScript
Raw Normal View History

2023-05-23 01:14:17 -04:00
import Discord from 'discord.js';
import {Octokit} from '@octokit/rest';
import {createTokenAuth} from '@octokit/auth-token';
2023-01-06 19:41:20 -05:00
import {exec} from 'node:child_process';
2023-08-29 20:21:53 -04:00
import MessageTool from '../helpers/MessageTool.js';
2023-08-30 04:34:59 -04:00
import UsernameHelper from '../helpers/UsernameHelper.js';
import FormatTime from '../helpers/FormatTime.js';
import TSClient from '../helpers/TSClient.js';
2023-10-05 04:43:29 -04:00
import TClient from '../client.js';
2023-04-14 06:47:58 -04:00
import fs from 'node:fs';
2023-02-24 19:55:11 -05:00
import util from 'node:util';
2023-01-06 19:41:20 -05:00
export default {
2023-03-05 05:04:10 -05:00
run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>) {
2023-10-06 01:54:27 -04:00
if (!client.config.whitelist.includes(interaction.user.id)) return MessageTool.youNeedRole(interaction, 'bottech');
2023-03-05 05:04:10 -05:00
({
eval: async()=>{
2023-05-02 00:19:05 -04:00
if (!client.config.eval) return interaction.reply({content: 'Eval is currently disabled.', ephemeral: true});
2023-03-05 05:04:10 -05:00
const code = interaction.options.getString('code') as string;
2023-10-06 01:54:27 -04:00
let consoleOutput:string = '';
const deleteEmbedBtn = new Discord.ButtonBuilder().setCustomId('deleteEmbed').setLabel('Delete').setStyle(Discord.ButtonStyle.Danger).setEmoji('🗑️');
const deleteEmbedRow = new Discord.ActionRowBuilder<Discord.ButtonBuilder>().addComponents(deleteEmbedBtn);
const deleteEmbedCollector = interaction.channel.createMessageComponentCollector({componentType: Discord.ComponentType.Button});
deleteEmbedCollector.on('collect', async i=>{
if (i.customId === 'deleteEmbed') deleteEmbedCollector.stop();
});
2023-10-05 04:43:29 -04:00
2023-03-05 05:04:10 -05:00
try {
2023-10-06 01:54:27 -04:00
const consoleLog = console.log;
console.log = (...args: any[])=>{
consoleLog(...args);
2023-10-06 02:19:24 -04:00
consoleOutput += util.formatWithOptions({depth: 3, colors: true}, ...args) + '\n';
2023-10-06 01:54:27 -04:00
}
2023-10-05 04:43:29 -04:00
const output = await eval(interaction.options.getBoolean('async') ? `(async()=>{${code}})()` : code);
2023-10-06 01:54:27 -04:00
let outVal = output !== undefined ? output : 'No output';
if (outVal && outVal.includes && outVal.includes(client.token)) outVal = outVal.replace(client.token, '*'.repeat(8));
const embedFields:Discord.APIEmbedField[] = [
{name: 'Input', value: `\`\`\`js\n${code.slice(0,1020)}\n\`\`\``},
2023-10-06 02:19:24 -04:00
{name: 'Output', value: `**\`\`\`${UsernameHelper.stripName(outVal === 'string' ? String(outVal) : 'ansi\n'+util.formatWithOptions({depth: 3, colors: true}, '%O', outVal)).slice(0,1012)}\n\`\`\`**`}
2023-10-06 01:54:27 -04:00
];
if (consoleOutput) embedFields.push({name: 'Console', value: `**\`\`\`ansi\n${UsernameHelper.stripName(consoleOutput).slice(0,1008)}\n\`\`\`**`});
2023-10-05 04:43:29 -04:00
if (typeof output === 'object') {
2023-10-06 01:54:27 -04:00
const embed = new client.embed().setColor(client.config.embedColor).addFields(embedFields);
interaction.reply({embeds: [embed], components: [deleteEmbedRow]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed], components: [deleteEmbedRow]}));
2023-10-05 04:43:29 -04:00
} else {
2023-10-06 01:54:27 -04:00
const embed = new client.embed().setColor(client.config.embedColor).addFields(embedFields);
interaction.reply({embeds: [embed], components: [deleteEmbedRow]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed], components: [deleteEmbedRow]}));
2023-10-05 04:43:29 -04:00
}
} catch (err) {
const embed = new client.embed().setColor('#560000').addFields(
{name: 'Input', value: `\`\`\`js\n${code.slice(0, 1020)}\n\`\`\``},
2023-03-05 05:04:10 -05:00
{name: 'Output', value: `\`\`\`\n${err}\`\`\``}
2023-10-05 04:43:29 -04:00
);
2023-10-06 01:54:27 -04:00
interaction.reply({embeds: [embed], components: [deleteEmbedRow]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed], components: [deleteEmbedRow]})).then(()=>{
2023-10-05 04:43:29 -04:00
const filter = (x:Discord.Message)=>x.content.includes('console') && x.author.id === interaction.user.id
2023-03-05 05:04:10 -05:00
const messagecollector = (interaction.channel as Discord.TextChannel).createMessageCollector({filter, max: 1, time: 60000});
messagecollector.on('collect', collected=>{
2023-10-05 04:43:29 -04:00
console.log(err)
collected.reply(`\`\`\`\n${UsernameHelper.stripName(err.stack)}\n\`\`\``);
2023-03-05 05:04:10 -05:00
});
});
2023-10-06 01:54:27 -04:00
} finally {
console.log = console.log;
2023-03-05 05:04:10 -05:00
}
},
update: async()=>{
const SummonAuthentication = createTokenAuth((await TSClient.Token()).octokit);
const {token} = await SummonAuthentication();
var githubRepo = {owner: 'AnxietyisReal', repo: 'Daggerbot-TS', ref: 'HEAD'};
2023-09-01 03:50:44 -04:00
const hammond = await interaction.reply({content: 'Pulling from repository...', fetchReply: true});
const octokit = new Octokit({auth: token, timeZone: 'Australia/NSW', userAgent: 'Daggerbot-TS'});
2023-05-12 03:39:20 -04:00
const github = {
fetchCommit: {
msg: await octokit.repos.getCommit(githubRepo).then(x=>x.data.commit.message).catch((err:Error)=>err.message),
author: await octokit.repos.getCommit(githubRepo).then(x=>x.data.commit.author.name).catch((err:Error)=>err.message),
url: await octokit.repos.getCommit(githubRepo).then(x=>x.data.html_url).catch((err:Error)=>err.message)
},
fetchChanges: {
total: await octokit.repos.getCommit(githubRepo).then(x=>x.data.stats.total.toLocaleString('en-US')).catch((err:Error)=>err.message),
addition: await octokit.repos.getCommit(githubRepo).then(x=>x.data.stats.additions.toLocaleString('en-US')).catch((err:Error)=>err.message),
deletion: await octokit.repos.getCommit(githubRepo).then(x=>x.data.stats.deletions.toLocaleString('en-US')).catch((err:Error)=>err.message)
}
};
exec('git pull',{windowsHide:true},(err:Error,stdout)=>{
2023-09-01 03:50:44 -04:00
if (err) hammond.edit(`\`\`\`${UsernameHelper.stripName(err.message)}\`\`\``)
else if (stdout.includes('Already up to date')) hammond.edit('I am already up to date with the upstream repository.')
else hammond.edit('Compiling TypeScript files...').then(()=>exec('yarn tsc', {windowsHide:true}, (err:Error)=>{
if (err) hammond.edit(`\`\`\`${UsernameHelper.stripName(err.message)}\`\`\``)
2023-09-01 03:51:38 -04:00
if (interaction.options.getBoolean('restart')) hammond.edit(`[Commit:](<${github.fetchCommit.url}>) **${github.fetchCommit.msg.length === 0 ? 'No commit message' : 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}));
2023-09-01 03:50:44 -04:00
else hammond.edit(`[Commit:](<${github.fetchCommit.url}>) **${github.fetchCommit.msg.length === 0 ? 'No commit message' : 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!`)
2023-04-16 01:47:49 -04:00
}))
2023-08-24 12:06:39 -04:00
})
2023-03-05 05:04:10 -05:00
},
presence: ()=>{
function convertType(Type?: number){
switch (Type) {
case 0: return 'Playing';
case 1: return 'Streaming';
case 2: return 'Listening to';
case 3: return 'Watching';
2023-08-24 12:06:39 -04:00
case 4: return 'Custom Status';
2023-03-05 05:04:10 -05:00
case 5: return 'Competing in';
2023-02-24 19:55:11 -05:00
}
2023-03-05 05:04:10 -05:00
};
const status = interaction.options.getString('status') as Discord.PresenceStatusData | null;
const type = interaction.options.getInteger('type');
const name = interaction.options.getString('name');
const url = interaction.options.getString('url');
const currentActivities = client.config.botPresence.activities as Discord.ActivitiesOptions[];
if (status) client.config.botPresence.status = status;
if (type) currentActivities[0].type = type;
if (name) currentActivities[0].name = name;
if (url) currentActivities[0].url = url;
client.user.setPresence(client.config.botPresence);
2023-08-29 20:21:53 -04:00
interaction.reply(MessageTool.concatMessage(
2023-03-05 05:04:10 -05:00
'Presence updated:',
`Status: **${client.config.botPresence.status}**`,
`Type: **${convertType(currentActivities[0].type)}**`,
`Name: **${currentActivities[0].name}**`,
`URL: \`${currentActivities[0].url}\``
2023-08-29 20:21:53 -04:00
))
2023-03-05 05:04:10 -05:00
},
statsgraph: ()=>{
client.statsGraph = -(interaction.options.getInteger('number', true));
2023-08-24 12:06:39 -04:00
interaction.reply(`Successfully set to \`${client.statsGraph}\`\n*Total data points: **${JSON.parse(fs.readFileSync(`src/database/${interaction.options.getString('server')}PlayerData.json`, {encoding: 'utf8'})).length.toLocaleString()}***`)
2023-03-05 05:04:10 -05:00
},
2023-09-03 05:07:15 -04:00
logs: ()=>(client.channels.resolve(client.config.mainServer.channels.console) as Discord.TextChannel).send({content: `Uploaded the current console dump as of <t:${Math.round(Date.now()/1000)}:R>`, files: [`${process.env.pm2_home}/logs/Daggerbot-out.log`, `${process.env.pm2_home}/logs/Daggerbot-error.log`]}).then(()=>interaction.reply('It has been uploaded to dev server.')).catch((e:Error)=>interaction.reply(`\`${e.message}\``)),
2023-04-16 01:47:49 -04:00
restart: async()=>{
const i = await interaction.reply({content: 'Compiling TypeScript files...', fetchReply: true});
exec('yarn tsc',{windowsHide:true},(err:Error)=>{
2023-08-30 04:34:59 -04:00
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}))
2023-04-16 01:47:49 -04:00
})
2023-07-04 01:56:47 -04:00
},
2023-08-12 04:17:51 -04:00
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});
2023-08-24 12:06:39 -04:00
exec(`cd "../../Desktop/System Tools/wakemeonlan" && WakeMeOnLan.exe /wakeup ${interaction.options.getString('name')}`, {windowsHide:true}, (err:Error)=>{
2023-08-30 04:34:59 -04:00
if (err) i.edit(UsernameHelper.stripName(err.message))
2023-08-12 04:17:51 -04:00
else i.edit('Your device should be awake by now!\n||Don\'t blame me if it isn\'t on.||')
})
2023-09-06 10:27:41 -04:00
},
dm: async()=>{
const member = interaction.options.getMember('member');
const message = interaction.options.getString('message');
const int = await interaction.reply({content: '*Sending...*', fetchReply: true});
member.send(message).then(()=>int.edit(`Successfully sent a DM to **${member.user.username}** with the following message:\n\`\`\`${message}\`\`\``)).catch((e:Error)=>int.edit(`\`${e.message}\``))
2023-08-12 04:17:51 -04:00
}
2023-03-05 05:04:10 -05:00
} as any)[interaction.options.getSubcommand()]();
2023-02-24 19:55:11 -05:00
},
2023-05-23 01:14:17 -04:00
data: new Discord.SlashCommandBuilder()
2023-03-05 05:04:10 -05:00
.setName('dev')
.setDescription('Developer commands')
2023-05-23 01:14:17 -04:00
.addSubcommand(x=>x
2023-03-05 05:04:10 -05:00
.setName('eval')
.setDescription('Execute the code to the bot')
2023-05-23 01:14:17 -04:00
.addStringOption(x=>x
2023-03-05 05:04:10 -05:00
.setName('code')
.setDescription('Execute your code')
2023-10-05 04:43:29 -04:00
.setRequired(true))
.addBooleanOption(x=>x
.setName('async')
.setDescription('Asynchronously execute your code')
.setRequired(false)))
2023-05-23 01:14:17 -04:00
.addSubcommand(x=>x
2023-03-05 05:04:10 -05:00
.setName('logs')
.setDescription('Retrieve the logs from host and sends it to dev server'))
2023-05-23 01:14:17 -04:00
.addSubcommand(x=>x
2023-03-05 05:04:10 -05:00
.setName('restart')
.setDescription('Restart the bot for technical reasons'))
2023-05-23 01:14:17 -04:00
.addSubcommand(x=>x
2023-03-05 05:04:10 -05:00
.setName('update')
2023-08-24 12:06:39 -04:00
.setDescription('Pull from repository and restart')
.addBooleanOption(x=>x
.setName('restart')
.setDescription('Restart the bot after pulling from repository')
2023-08-24 12:27:23 -04:00
.setRequired(true)
2023-08-24 12:06:39 -04:00
))
2023-08-12 04:17:51 -04:00
.addSubcommand(x=>x
.setName('wake_device')
.setDescription('Remotely wake up a device in the same network as the bot')
.addStringOption(x=>x
.setName('name')
.setDescription('Device name')
.setRequired(true)))
2023-05-23 01:14:17 -04:00
.addSubcommand(x=>x
2023-03-05 05:04:10 -05:00
.setName('statsgraph')
.setDescription('Edit the number of data points to pull')
2023-08-24 12:06:39 -04:00
.addStringOption(x=>x
.setName('server')
.setDescription('Server name')
.setRequired(true))
2023-05-23 01:14:17 -04:00
.addIntegerOption(x=>x
2023-03-05 05:04:10 -05:00
.setName('number')
.setDescription('Number of data points to pull')
.setRequired(true)))
2023-05-23 01:14:17 -04:00
.addSubcommand(x=>x
2023-03-05 05:04:10 -05:00
.setName('presence')
.setDescription('Update the bot\'s presence')
2023-05-23 01:14:17 -04:00
.addIntegerOption(x=>x
2023-03-05 05:04:10 -05:00
.setName('type')
.setDescription('Set an activity type')
.addChoices(
{name: 'Playing', value: Discord.ActivityType.Playing},
{name: 'Streaming', value: Discord.ActivityType.Streaming},
{name: 'Listening to', value: Discord.ActivityType.Listening},
{name: 'Watching', value: Discord.ActivityType.Watching},
2023-08-12 13:18:46 -04:00
{name: 'Custom Status', value: Discord.ActivityType.Custom},
2023-03-05 05:04:10 -05:00
{name: 'Competing in', value: Discord.ActivityType.Competing}
))
2023-05-23 01:14:17 -04:00
.addStringOption(x=>x
2023-03-05 05:04:10 -05:00
.setName('name')
.setDescription('Set a message for the activity status'))
2023-05-23 01:14:17 -04:00
.addStringOption(x=>x
2023-03-05 05:04:10 -05:00
.setName('url')
.setDescription('Set an url for streaming status'))
2023-05-23 01:14:17 -04:00
.addStringOption(x=>x
2023-03-05 05:04:10 -05:00
.setName('status')
.setDescription('Set a status indicator for the bot')
2023-08-12 12:40:18 -04:00
.addChoices(
2023-03-05 05:04:10 -05:00
{name: 'Online', value: Discord.PresenceUpdateStatus.Online},
{name: 'Idle', value: Discord.PresenceUpdateStatus.Idle},
{name: 'Do Not Distrub', value: Discord.PresenceUpdateStatus.DoNotDisturb},
{name: 'Invisible', value: Discord.PresenceUpdateStatus.Offline}
)))
2023-07-04 01:56:47 -04:00
.addSubcommand(x=>x
.setName('file')
.setDescription('Send a JSON file from database directory on the host')
.addStringOption(x=>x
.setName('name')
.setDescription('JSON filename, don\'t include an extension')
.setRequired(true)))
2023-09-06 10:27:41 -04:00
.addSubcommand(x=>x
.setName('dm')
.setDescription('Reply or send a DM to a member')
.addUserOption(x=>x
.setName('member')
.setDescription('Member to send a DM to')
.setRequired(true))
.addStringOption(x=>x
.setName('message')
.setDescription('Message to send')
.setRequired(true)))
2023-01-06 19:41:20 -05:00
}