1
0
mirror of https://github.com/toast-ts/Daggerbot-TS.git synced 2024-11-17 00:10:58 -05:00

im bit bored now, automod needs investigating.

This commit is contained in:
toast-ts 2022-11-18 22:29:03 +11:00
parent 386ee4f54e
commit 3e94afb222
19 changed files with 506 additions and 87 deletions

4
.gitignore vendored
View File

@ -6,6 +6,4 @@ package-lock.json
dist/
# Bot stuff
src/database/MPDB.dat
src/commands/disabled
src/commands/roleinfo.ts
src/commands/rank.ts
src/commands/disabled

View File

@ -1,11 +1,7 @@
{
"name": "daggerbot-ts",
"description": "TypeScript version of the original JavaScript-based bot for Official Daggerwin Discord.",
"main": "src/Sharding.ts",
"scripts": {
"start": "ts-node src/Sharding.ts",
"startDev": "ts-node src/index.ts"
},
"main": "src/index.ts",
"repository": {
"type": "git",
"url": "git+https://github.com/AnxietyisReal/Daggerbot-TS.git"

View File

@ -1,5 +0,0 @@
import {token_toast} from './tokens.json';
import {ShardingManager} from 'discord.js';
const sharder = new ShardingManager('src/index.ts',{token: token_toast, totalShards: 1, mode: 'worker'});
sharder.on('shardCreate',async(shard)=>{console.log(`Shard ${shard.id} launched`)});
sharder.spawn();

View File

@ -174,7 +174,7 @@ export class TClient extends Client {
{name: '\u200b', value: '\u200b', inline: true}
)
}
if (data.cancels) embed.addFields({name: '🔹 Overwrites', value: `This case overwrites Case #${cancels.id} \`${cancels.reason}\``});
if (data.cancels) embed.addFields({name: '🔹 Overwrites', value: `This case overwrites Case #${cancels.id}\n\`${cancels.reason}\``});
// send embed to log channel
(client.channels.cache.get(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [embed]})
}
@ -263,7 +263,7 @@ class punishments extends Database {
switch (type) {
case 'ban':
const banData:Punishment={type, id: this.createId(), member: member.id, moderator, time: now};
const dm1: Discord.Message = await member.send(`You've been banned from ${interaction.guild.name} ${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${banData.id})`).catch(()=>{return interaction.channel.send('Failed to DM user.')})
const dm1: Discord.Message = await member.send(`You've been banned from ${interaction.guild.name} ${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})}` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${banData.id})`).catch(()=>{return interaction.channel.send('Failed to DM user.')})
const banResult = await interaction.guild.bans.create(member.id, {reason: `${reason || 'Reason unspecified'} | Case #${banData.id}`}).catch((err:Error)=>err.message);
if (typeof banResult === 'string'){
dm1.delete()
@ -278,13 +278,13 @@ class punishments extends Database {
this.addData(banData).forceSave();
return new this.client.embed().setColor(this.client.config.embedColor).setTitle(`Case #${banData.id}: Ban`).setDescription(`${member?.user?.tag ?? member?.tag}\n<@${member.id}>\n(\`${member.id}\`)`).addFields(
{name: 'Reason', value: `\`${reason || 'Reason unspecified'}\``},
{name: 'Duration', value: `${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)` : 'forever'}`}
{name: 'Duration', value: `${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})}` : 'forever'}`}
)
}
case 'softban':
const guild = member.guild;
const softbanData:Punishment={type, id: this.createId(), member: member.user.id, moderator, time: now};
const dm2 = Discord.Message = await member.send(`You've been softbanned from ${member.guild.name} ${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${softbanData.id})`).catch(()=>{return interaction.channel.send('Failed to DM user.')})
const dm2 = Discord.Message = await member.send(`You've been softbanned from ${member.guild.name} ${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})}` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${softbanData.id})`).catch(()=>{return interaction.channel.send('Failed to DM user.')})
const softbanResult = await member.ban({deleteMessageDays: 3, reason: `${reason || 'Reason unspecified'} | Case #${softbanData.id}`}).catch((err:Error)=>err.message);
if (typeof softbanResult === 'string') {
dm2.delete();
@ -335,7 +335,7 @@ class punishments extends Database {
const muteData:Punishment={type, id: this.createId(), member: member.user.id, moderator, time: now};
let muteResult;
if (this.client.isStaff(member)) return 'Staff cannot be muted.'
const dm4: Discord.Message = await member.send(`You've been muted in ${member.guild.name} ${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${muteData.id})`).catch(()=>{interaction.channel.send('Failed to DM user.'); return null});
const dm4: Discord.Message = await member.send(`You've been muted in ${member.guild.name} ${timeInMillis ? `for ${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})}` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${muteData.id})`).catch(()=>{interaction.channel.send('Failed to DM user.'); return null});
if (timeInMillis) {
muteResult = await member.timeout(timeInMillis, `${reason || 'Reason unspecified'} | Case #${muteData.id}`).catch((err:Error)=>err.message);
} else {
@ -354,16 +354,57 @@ class punishments extends Database {
this.addData(muteData).forceSave();
const embedm = new this.client.embed().setColor(this.client.config.embedColor).setTitle(`Case #${muteData.id}: Mute`).setDescription(`${member.user.tag}\n<@${member.user.id}>\n(\`${member.user.id}\`)`).addFields(
{name: 'Reason', value: `\`${reason || 'Reason unspecified'}\``},
{name: 'Duration',
value: `${this.client.formatTime(timeInMillis, 4, {
longNames: true,
commas: true
})} (${timeInMillis}ms)`})
{name: 'Duration', value: `${this.client.formatTime(timeInMillis, 4, {longNames: true, commas: true})}`})
if (moderator !== '795443537356521502') {return embedm};
}
}
}
async removePunishment(caseId:number, moderator:any, reason:string):Promise<any>{}
async removePunishment(caseId:number, moderator:any, reason:string):Promise<any>{
const now = Date.now()
const punishment = this._content.find((x:Punishment)=>x.id === caseId);
const id = this.createId();
if (!punishment) return 'Punishment not found';
if (['ban','mute'].includes(punishment.type)) {
const guild = this.client.guilds.cache.get(this.client.config.mainServer.id) as Discord.Guild;
let removePunishmentResult;
if (punishment.type === 'ban'){
removePunishmentResult = await guild.members.unban(punishment.member, `${reason || 'Reason unspecified'} | Case #${id}`).catch((err:TypeError)=>err.message);
} else if (punishment.type === 'mute'){
const member = await guild.members.fetch(punishment.member).catch(err=>undefined);
if (member){
removePunishmentResult = await member
if (typeof removePunishmentResult !== 'string'){
member.timeout(null, `${reason || 'Reason unspecified'} | Case #${id}`)
removePunishmentResult.send(`You've been unmuted in ${removePunishmentResult.guild.name}.`);
removePunishmentResult = removePunishmentResult.user;
}
} else {
// user probably left, quietly remove punishment
const removePunishmentData = {type: `un${punishment.type}`, id, cancels: punishment.id, member: punishment.member, reason, moderator, time: now};
this._content[this._content.findIndex((x:Punishment)=>x.id === punishment.id)].expired = true
this.addData(removePunishmentData).forceSave();
}
}
if (typeof removePunishmentResult === 'string') return `Un${punishment.type} was unsuccessful: ${removePunishmentResult}`;
else {
const removePunishmentData = {type: `un${punishment.type}`, id, cancels: punishment.id, member: punishment.member, reason, moderator, time: now};
this.client.makeModlogEntry(removePunishmentData, this.client);
this._content[this._content.findIndex((x:Punishment)=>x.id === punishment.id)].expired = true;
this.addData(removePunishmentData).forceSave();
return `Successfully ${punishment.type === 'ban' ? 'unbanned' : 'unmuted'} **${removePunishmentResult?.tag}** (${removePunishmentResult?.id}) for reason \`${reason || 'Reason unspecified'}\``
}
} else {
try {
const removePunishmentData = {type: 'removeOtherPunishment', id, cancels: punishment.id, member: punishment.member, reason, moderator, time: now};
this.client.makeModlogEntry(removePunishmentData, this.client);
this._content[this._content.findIndex((x:Punishment)=>x.id === punishment.id)].expired = true;
this.addData(removePunishmentData).forceSave();
return `Successfully removed Case #${punishment.id} (type: ${punishment.type}, user: ${punishment.member}).`;
} catch (error:any){
return `${punishment.type[0].toUpperCase() + punishment.type.slice(1)} removal was unsuccessful: ${error.message}`;
}
}
}
}
class userLevels extends Database {
client: TClient;

View File

@ -8,5 +8,5 @@ export default {
},
data: new SlashCommandBuilder()
.setName('botlog')
.setDescription('Retrieves the log from host and sends it to appropriate channel.')
.setDescription('Retrieves the log from host and sends it to development server.')
}

83
src/commands/case.ts Normal file
View File

@ -0,0 +1,83 @@
import Discord,{SlashCommandBuilder} from "discord.js";
import { TClient } from 'src/client';
import { Punishment } from "src/typings/interfaces";
export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
if (!interaction.member.roles.cache.has(client.config.mainServer.roles.dcmod)) return client.youNeedRole(interaction, 'dcmod')
const Subb = interaction.options.getSubcommand();
const caseId = interaction.options.getInteger('id');
if (Subb == 'update'){
const reason = interaction.options.getString('reason')
client.punishments._content.find((x:Punishment)=>x.id==caseId).reason = reason;
client.punishments.forceSave();
const embed = new client.embed().setColor(client.config.embedColorGreen).setTitle('Case updated').setDescription(`Case #${caseId} has been successfully updated with new reason:\n\`${reason}\``);
await interaction.reply({embeds: [embed]})
} else if (Subb == 'view'){
const punishment = client.punishments._content.find((x:Punishment)=>x.id==caseId);
if (!punishment) return interaction.reply('Invalid case #');
const cancelledBy = punishment.expired ? client.punishments._content.find((x:Punishment)=>x.cancels==punishment.id) : null;
const cancels = punishment.cancels ? client.punishments._content.find((x:Punishment)=>x.id==punishment.cancels) : null;
const embed = new client.embed().setColor(client.config.embedColor).setTimestamp(punishment.time).setTitle(`${client.formatPunishmentType(punishment, client, cancels)} | Case #${punishment.id}`).addFields(
{name: '🔹 User', value: `<@${punishment.member}> \`${punishment.member}\``, inline: true},
{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.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]});
} else {
// if caseid is user id, show their punishment history sorted by most recent.
const userId = (interaction.options.getUser('user') as Discord.User).id;
const userName = (interaction.options.getUser('user') as Discord.User).username;
const punishment = client.punishments._content.find((x:Punishment)=>x.member===userId);
if (!punishment) return interaction.reply(`<@${userId}> has a clean record.`)
const cancels = punishment.cancels ? client.punishments._content.find((x:Punishment)=>x.id==punishment.cancels) : null;
const userPunishment = client.punishments._content.filter((x:Punishment)=>x.member==userId).sort((a:Punishment,b:Punishment)=>a.time-b.time).map((punishment:Punishment)=>{
return {
name: `${client.formatPunishmentType(punishment, client, cancels)} | 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 #${client.punishments._content.find((x:Punishment)=>x.cancels==punishment.id).id}` : ''}${punishment.cancels ? `\nOverwrites case #${punishment.cancels}` : ''}`
}
});
// if caseid is not a punishment nor a user, failed
if (!userPunishment || userPunishment.length == 0) return interaction.reply('No punishments found for that case # or User ID');
const pageNum = interaction.options.getInteger('page') ?? 1;
const embed = new client.embed().setColor(client.config.embedColor).setTitle(`${userName}'s punishment history`).setDescription(`**ID:** \`${userId}\``).setFooter({text: `${userPunishment.length} total punishments. Viewing page ${pageNum} out of ${Math.ceil(userPunishment.length/6)}.`}).addFields(userPunishment.slice((pageNum - 1) * 6, pageNum * 6));
return interaction.reply({embeds: [embed]});
}
},
data: new SlashCommandBuilder()
.setName('case')
.setDescription('Retrieve case information or user\'s punishment history')
.addSubcommand((opt)=>opt
.setName('view')
.setDescription('View a single case.')
.addIntegerOption((optt)=>optt
.setName('id')
.setDescription('Case #')
.setRequired(true)))
.addSubcommand((opt)=>opt
.setName('member')
.setDescription('View member\'s punishment history')
.addUserOption((optt)=>optt
.setName('user')
.setDescription('Which user do you want to view their punishment history?')
.setRequired(true))
.addIntegerOption((optt)=>optt
.setName('page')
.setDescription('Select the page number')))
.addSubcommand((opt)=>opt
.setName('update')
.setDescription('Update the case with new reason')
.addIntegerOption((optt)=>optt
.setName('id')
.setDescription('Case # to be updated')
.setRequired(true))
.addStringOption((optt)=>optt
.setName('reason')
.setDescription('New reason for the case')
.setRequired(true)))
};

View File

@ -3,6 +3,7 @@ import { TClient } from 'src/client';
export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
const embed = new client.embed().setColor(client.config.embedColor).setTitle('Daggerbot contributors').setDescription([
'**Thanks to those below that contributed to the bot!**',
'Toast <@190407856527376384>',
'TÆMBØ <@615761944154210305>',
'Buzz <@593696856165449749>',
@ -16,5 +17,5 @@ export default {
},
data: new SlashCommandBuilder()
.setName('contributors')
.setDescription('List of people who helped and worked on the bot.')
.setDescription('List of people who contributed to the bot.')
}

39
src/commands/faq.ts Normal file
View File

@ -0,0 +1,39 @@
import Discord,{SlashCommandBuilder} from 'discord.js';
import { TClient } from 'src/client';
export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
const options = interaction.options.getString('question')
if (options == 'srp'){
const embed = new client.embed().setColor(client.config.embedColor).setTitle('When will SRP (Survival Roleplay) return?').setImage('https://cdn.discordapp.com/attachments/898252602603610142/1013097505094893698/Screenshot_20220827-153617_YouTube.jpg');
interaction.reply({embeds: [embed]})
} else if (options == 'dlskin'){
const embed1 = new client.embed().setColor(client.config.embedColor).setTitle('Hex code for Daggerwin Logistics').setDescription('The main color will be Onyx (`#353839`) with red bumpers').setImage('https://cdn.discordapp.com/attachments/801965516947324969/806871878736019456/image0.png');
interaction.reply({embeds: [embed1]})
} else if (options == 'vtcR'){
interaction.reply(`You can get the <@&${client.config.mainServer.roles.vtcmember}> role from <#802283932430106624> by reacting the ProBot\'s message with :truck:\n*VTC skin can also be found in <#801975222609641472> as well.*`)
} else if (options == 'mpR'){
interaction.reply(`You can get the <@&${client.config.mainServer.roles.mpplayer}> role from <#802283932430106624> by reacting the ProBot\'s message with :tractor:`)
} else if (options == 'fsShader'){
const embed2 = new client.embed().setColor(client.config.embedColor).setTitle('Clearing your shader cache folder').setDescription('If your game kees crashing shortly after opening your game, then the shaders might be an issue.\nTo resolve this, you can go to `Documents/My Games/FarmingSimulator2022` and delete the folder called `shader_cache`').setImage('https://cdn.discordapp.com/attachments/1015195575693627442/1015195687970943016/unknown.png');
interaction.reply({embeds: [embed2]})
} else if (options == 'fsLogfile'){
const embed3 = new client.embed().setColor(client.config.embedColor).setTitle('Uploading your log file').setDescription('You can find `log.txt` in `Documents/My Games/FarmingSimulator2022` and upload it into <#596989522395398144> along with your issue, so people can assist you further and help you resolve.').setImage('https://cdn.discordapp.com/attachments/1015195575693627442/1015195643528101958/unknown.png');
interaction.reply({embeds: [embed3]})
}
},
data: new SlashCommandBuilder()
.setName('faq')
.setDescription('List of FAQ for users')
.addStringOption((opt)=>opt
.setName('question')
.setDescription('What question do you want answered?')
.addChoices(
{ name: 'Survival Roleplay', value: 'srp' },
{ name: 'Daggerwin Logistics hex code', value: 'dlskin' },
{ name: 'VTC Role', value: 'vtcR' },
{ name: 'MP Role', value: 'mpR' },
{ name: '[FS22] Resolve shader_cache issue', value: 'fsShader' },
{ name: '[FS22] Log file location', value: 'fsLogfile' }
))
}

View File

@ -3,7 +3,7 @@ import { TClient } from 'src/client';
import MPDB from '../models/MPServer';
export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
if (interaction.guildId !== client.config.mainServer.id) return interaction.reply('This command doesn\'t work in this server.');
if (interaction.guildId !== client.config.mainServer.id) return interaction.reply({content: 'This command doesn\'t work in this server.', ephemeral: true});
if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmanager) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech) && !interaction.member.roles.cache.has(client.config.mainServer.roles.admin)) return client.youNeedRole(interaction, 'mpmanager');
MPDB.sync();

37
src/commands/purge.ts Normal file
View File

@ -0,0 +1,37 @@
import Discord,{SlashCommandBuilder} from 'discord.js';
import { TClient } from 'src/client';
export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
if (!client.isStaff(interaction.member)) return client.youNeedRole(interaction, 'dcmod');
const amount = interaction.options.getInteger('amount') as number;
if (amount > 100) return interaction.reply({content: 'Discord API limits purging up to 100 messages.', ephemeral: true})
const user = interaction.options.getUser('user');
let messagesArray: Array<string> = [];
if (user){
(interaction.channel as Discord.TextChannel).messages.fetch({limit: 100}).then((msgs)=>{
const msgList = msgs.filter(x=>x.author.id == user.id);
(interaction.channel as Discord.TextChannel).bulkDelete(msgList);
})
} else {
(interaction.channel as Discord.TextChannel).messages.fetch({limit: amount}).then(async messages=>{
messages.forEach(message=>{
messagesArray.push(message.id);
});
await (interaction.channel as Discord.TextChannel).bulkDelete(messagesArray);
})
}
await interaction.reply({content: `Successfully purged ${amount} messages.`, ephemeral: true})
},
data: new SlashCommandBuilder()
.setName('purge')
.setDescription('Purge the amount of messages in this channel')
.addIntegerOption((opt)=>opt
.setName('amount')
.setDescription('Amount of messages to be obliterated')
.setRequired(true))
.addUserOption((opt)=>opt
.setName('user')
.setDescription('Which user to have their messages obliterated?'))
}

192
src/commands/rank.ts Normal file
View File

@ -0,0 +1,192 @@
interface UserLevels {
messages: number,
level: number
}
import Discord,{SlashCommandBuilder} from 'discord.js';
import { TClient } from 'src/client';
export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
if (interaction.guildId !== client.config.mainServer.id) return interaction.reply({content: 'This command doesn\'t work in this server.', ephemeral: true});
const subCmd = interaction.options.getSubcommand();
if (subCmd === "leaderboard") {
const messageCountsTotal = Object.values<UserLevels>(client.userLevels._content).reduce((a, b) => a + b.messages, 0);
const timeActive = Math.floor((Date.now() - client.config.LRSstart)/1000/60/60/24);
const data = require('../database/dailyMsgs.json').map((x: Array<number>, i: number, a: any) => {
const yesterday = a[i - 1] || [];
return x[1] - (yesterday[1] || x[1]);
}).slice(1).slice(-60);
// handle negative days
data.forEach((change: number, i: number) => {
if (change < 0) data[i] = data[i - 1] || data[i + 1] || 0;
});
const maxValue = Math.max(...data);
const maxValueArr = maxValue.toString().split('');
const first_graph_top = Math.ceil(maxValue * 10 ** (-maxValueArr.length + 1)) * 10 ** (maxValueArr.length - 1);
// console.log({ first_graph_top });
const second_graph_top = Math.ceil(maxValue * 10 ** (-maxValueArr.length + 2)) * 10 ** (maxValueArr.length - 2);
// console.log({ second_graph_top });
const textSize = 32;
const canvas = require('canvas');
const img = canvas.createCanvas(950, 450);
const ctx = img.getContext('2d');
const graphOrigin = [10, 50];
const graphSize = [700, 360];
const nodeWidth = graphSize[0] / (data.length - 1);
ctx.fillStyle = '#36393f';
ctx.fillRect(0, 0, img.width, img.height);
// grey horizontal lines
ctx.lineWidth = 3;
let interval_candidates = [];
for (let i = 4; i < 10; i++) {
const interval = first_graph_top / i;
if (Number.isInteger(interval)) {
let intervalString = interval.toString();
const reference_number = i * Math.max(intervalString.split('').filter(x => x === '0').length / intervalString.length, 0.3) * (['1', '2', '4', '5', '6', '8'].includes(intervalString[0]) ? 1.5 : 0.67)
interval_candidates.push([interval, i, reference_number]);
}
}
// console.log({ interval_candidates });
const chosen_interval = interval_candidates.sort((a, b) => b[2] - a[2])[0];
// console.log({ chosen_interval });
let previousY: Array<number> = [];
ctx.strokeStyle = '#202225';
for (let i = 0; i <= chosen_interval[1]; i++) {
const y = graphOrigin[1] + graphSize[1] - (i * (chosen_interval[0] / second_graph_top) * graphSize[1]);
if (y < graphOrigin[1]) continue;
const even = ((i + 1) % 2) === 0;
if (even) ctx.strokeStyle = '#2c2f33';
ctx.beginPath();
ctx.lineTo(graphOrigin[0], y);
ctx.lineTo(graphOrigin[0] + graphSize[0], y);
ctx.stroke();
ctx.closePath();
if (even) ctx.strokeStyle = '#202225';
previousY = [y, i * chosen_interval[0]];
}
// 30d mark
ctx.setLineDash([8, 16]);
ctx.beginPath();
const lastMonthStart = graphOrigin[0] + (nodeWidth * (data.length - 30));
ctx.lineTo(lastMonthStart, graphOrigin[1]);
ctx.lineTo(lastMonthStart, graphOrigin[1] + graphSize[1]);
ctx.stroke();
ctx.closePath();
ctx.setLineDash([]);
// draw points
ctx.strokeStyle = client.config.embedColor;
ctx.fillStyle = client.config.embedColor;
ctx.lineWidth = 3;
function getYCoordinate(value: number) {
return ((1 - (value / second_graph_top)) * graphSize[1]) + graphOrigin[1];
}
let lastCoords: Array<number> = [];
data.forEach((val: number, i: number) => {
ctx.beginPath();
if (lastCoords) ctx.moveTo(...lastCoords);
if (val < 0) val = 0;
const x = i * nodeWidth + graphOrigin[0];
const y = getYCoordinate(val);
ctx.lineTo(x, y);
lastCoords = [x, y];
ctx.stroke();
ctx.closePath();
// ball
ctx.beginPath();
ctx.arc(x, y, ctx.lineWidth * 1.2, 0, 2 * Math.PI)
ctx.closePath();
ctx.fill();
});
// draw text
ctx.font = '400 ' + textSize + 'px sans-serif';
ctx.fillStyle = 'white';
// highest value
//console.log(previousY)
const maxx = graphOrigin[0] + graphSize[0] + textSize;
const maxy = previousY[0] + (textSize / 3);
ctx.fillText(previousY[1].toLocaleString('en-US'), maxx, maxy);
// lowest value
const lowx = graphOrigin[0] + graphSize[0] + textSize;
const lowy = graphOrigin[1] + graphSize[1] + (textSize / 3);
ctx.fillText('0 msgs/day', lowx, lowy);
// 30d
ctx.fillText('30d ago', lastMonthStart, graphOrigin[1] - (textSize / 3));
// time ->
const tx = graphOrigin[0] + (textSize / 2);
const ty = graphOrigin[1] + graphSize[1] + (textSize);
ctx.fillText('time ->', tx, ty);
const yeahok = new client.attachmentBuilder(img.toBuffer(), {name: "dailymsgs.png"})
const embed = new client.embed()
.setTitle('Ranking leaderboard')
.setDescription(`Level System was created **${timeActive}** days ago. Since then, a total of **${messageCountsTotal.toLocaleString('en-US')}** messages have been sent in this server.`)
.addFields({name: 'Top users by messages sent:', value: Object.entries<UserLevels>(client.userLevels._content).sort((a, b) => b[1].messages - a[1].messages).slice(0, 10).map((x, i) => `\`${i + 1}.\` <@${x[0]}>: ${x[1].messages.toLocaleString('en-US')}`).join('\n')})
.setImage('attachment://dailymsgs.png')
.setColor(client.config.embedColor)
interaction.reply({embeds: [embed], files: [yeahok]});
return;
} else if (subCmd === "view") {
// fetch user or user interaction sender
const member = interaction.options.getMember("member") ?? interaction.member as Discord.GuildMember;
const embed = new client.embed().setColor(member.displayColor)
// information about users progress on level roles
const information = client.userLevels._content[member.user.id];
const pronounBool = (you: string, they: string) => { // takes 2 words and chooses which to use based on if user did this command on themself
if (interaction.user.id === member.user.id) return you || true;
else return they || false;
};
if (!information) {
return interaction.reply(`${pronounBool('You', 'They')} currently don't have a level, send some messages to level up.`)
}
const index = Object.entries<UserLevels>(client.userLevels._content).sort((a, b) => b[1].messages - a[1].messages).map(x => x[0]).indexOf(member.id) + 1;
const memberDifference = information.messages - client.userLevels.algorithm(information.level);
const levelDifference = client.userLevels.algorithm(information.level+1) - client.userLevels.algorithm(information.level);
embed.setThumbnail(member.user.avatarURL({ extension: 'png', size: 256}) || member.user.defaultAvatarURL)
embed.setAuthor({name: `Ranking for ${member.user.tag}`})
embed.setTitle(`Level: **${information.level}**\nRank: **${index ? '#' + index : 'last'}**\nProgress: **${information.messages - client.userLevels.algorithm(information.level)}/${client.userLevels.algorithm(information.level+1) - client.userLevels.algorithm(information.level)} (${(memberDifference/levelDifference*100).toFixed(2)}%)**\nTotal: **${information.messages}**`);
interaction.reply({embeds: [embed]});
}
},
data: new SlashCommandBuilder()
.setName('rank')
.setDescription('Level system')
.addSubcommand((optt)=>optt
.setName('view')
.setDescription('View your rank, someone else\'s rank')
.addUserOption((opt)=>opt
.setName('member')
.setDescription('Which member do you want to view?')))
.addSubcommand((optt)=>optt
.setName('leaderboard')
.setDescription('View top 10 users on leaderboard'))
}

25
src/commands/roleinfo.ts Normal file
View File

@ -0,0 +1,25 @@
import Discord,{SlashCommandBuilder} from 'discord.js';
import { TClient } from 'src/client';
export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
const role = interaction.options.getRole('role') as Discord.Role;
const keyPerms = ['Administrator', 'KickMembers', 'BanMembers', 'ManageChannels', 'ManageGuild', 'ViewAuditLog', 'ManageMessages', 'MentionEveryone', 'UseExternalEmojis', 'ManageRoles', 'Manage EmojiandStickers', 'ModerateMembers']
const permissions = role.permissions.toArray();
const Role = role.members.map((e:Discord.GuildMember)=>`**${e.user.tag}**`).join('\n') || '';
const embed = new client.embed().setColor(role.color || '#fefefe').setThumbnail(role?.iconURL()).setTitle(`Role Info: ${role.name}`).addFields(
{name: '🔹 ID', value: `\`${role.id}\``, inline: true},
{name: '🔹 Color', value: `\`${role.hexColor}\``, inline: true},
{name: '🔹 Creation Date', value: `<t:${Math.round(role.createdTimestamp/1000)}>\n<t:${Math.round(role.createdTimestamp/1000)}:R>`, inline: true},
{name: '🔹 Misc', value: `Hoist: \`${role.hoist}\`\nMentionable: \`${role.mentionable}\`\nPosition: \`${role.position}\` from bottom\nMembers: \`${role.members.size}\`\n${role.members.size < 21 ? Role : ''}`, inline: true},
{name: '🔹 Permissions', value: (permissions.includes('Administrator') ? ['Administrator'] : permissions.filter((x:string)=>keyPerms.includes(x))).map((x:string)=>{return x.split('_').map((y,i)=>y).join(' ')}).join(', ') || 'None', inline: true}
)
interaction.reply({embeds: [embed]})
},
data: new SlashCommandBuilder()
.setName('roleinfo')
.setDescription('View information about the selected role')
.addRoleOption((opt)=>opt
.setName('role')
.setDescription('Role name to view information')
.setRequired(true))
}

11
src/commands/smell.ts Normal file
View File

@ -0,0 +1,11 @@
import Discord,{SlashCommandBuilder} from 'discord.js';
import { TClient } from 'src/client';
export default {
async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){
const embed = new client.embed().setColor(client.config.embedColor).setTitle('Who smells?').setDescription('Dave and Monster! (Monster smells of staircases)').setImage('https://media.tenor.com/QW9S-Oq9INQAAAAC/patrick-smell.gif')
interaction.reply({embeds: [embed]});
},
data: new SlashCommandBuilder()
.setName('smell')
.setDescription('Who smells?')
}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
[[0,674965],[1,675885],[2,676639],[3,677245],[4,677609],[5,678188],[6,678916],[7,679465],[8,679938],[9,680540],[10,681614],[11,682079],[12,682780],[13,683187],[14,683547],[15,683900],[16,684592],[17,685466],[18,686078],[19,686481],[20,676748],[21,676968],[22,677427],[23,677592],[24,677894],[25,678116],[26,676785],[27,677570],[28,678491],[29,679137],[30,679818],[31,680094],[32,680417],[33,680783],[34,681563],[35,682070],[36,682670],[37,683504],[38,684078],[39,684383],[40,684692],[41,685448],[42,685664],[43,685994],[44,686366],[45,687118],[46,687626],[47,688008],[48,688754],[49,688942],[50,689107],[51,689472],[52,690143],[53,690644],[54,691124],[55,692196],[56,692624],[57,692906],[58,693456],[59,693952],[60,694586],[61,695070],[62,696163],[63,696564],[64,697315],[65,698548],[66,699138],[67,699558],[68,700307],[69,701063],[70,701394],[71,701868],[72,702453],[73,702917],[76,705187],[77,705243],[78,705488],[79,705961],[80,706585],[81,707467],[82,708212],[83,709024],[84,709773],[85,710256],[86,710980]]
[[0,674965],[1,675885],[2,676639],[3,677245],[4,677609],[5,678188],[6,678916],[7,679465],[8,679938],[9,680540],[10,681614],[11,682079],[12,682780],[13,683187],[14,683547],[15,683900],[16,684592],[17,685466],[18,686078],[19,686481],[20,676748],[21,676968],[22,677427],[23,677592],[24,677894],[25,678116],[26,676785],[27,677570],[28,678491],[29,679137],[30,679818],[31,680094],[32,680417],[33,680783],[34,681563],[35,682070],[36,682670],[37,683504],[38,684078],[39,684383],[40,684692],[41,685448],[42,685664],[43,685994],[44,686366],[45,687118],[46,687626],[47,688008],[48,688754],[49,688942],[50,689107],[51,689472],[52,690143],[53,690644],[54,691124],[55,692196],[56,692624],[57,692906],[58,693456],[59,693952],[60,694586],[61,695070],[62,696163],[63,696564],[64,697315],[65,698548],[66,699138],[67,699558],[68,700307],[69,701063],[70,701394],[71,701868],[72,702453],[73,702917],[76,705187],[77,705243],[78,705488],[79,705961],[80,706585],[81,707467],[82,708212],[83,709024],[84,709773],[85,710256],[86,710980],[87,711220]]

View File

@ -1,30 +1,30 @@
{
"190407856527376384": {
"messages": 53029,
"messages": 53026,
"level": 59
},
"593696856165449749": {
"messages": 51456,
"messages": 51467,
"level": 58
},
"141304507249197057": {
"messages": 67631,
"messages": 67632,
"level": 67
},
"533707949831487488": {
"messages": 57038,
"messages": 57042,
"level": 61
},
"532662366354276352": {
"messages": 33775,
"messages": 33785,
"level": 47
},
"824043915539513406": {
"messages": 18452,
"messages": 18457,
"level": 35
},
"178941218510602240": {
"messages": 6394,
"messages": 6396,
"level": 20
},
"215497515934416896": {
@ -32,15 +32,15 @@
"level": 46
},
"301350210926280704": {
"messages": 14334,
"messages": 14343,
"level": 30
},
"695323013813633076": {
"messages": 14090,
"messages": 14092,
"level": 30
},
"389237487094071337": {
"messages": 12254,
"messages": 12256,
"level": 28
},
"716355511552966737": {
@ -68,7 +68,7 @@
"level": 20
},
"475037861725339649": {
"messages": 10201,
"messages": 10207,
"level": 26
},
"392699530912727041": {
@ -76,15 +76,15 @@
"level": 18
},
"468837263577579524": {
"messages": 7695,
"messages": 7696,
"level": 22
},
"734703851558535188": {
"messages": 20211,
"messages": 20224,
"level": 36
},
"488683638310043677": {
"messages": 15477,
"messages": 15478,
"level": 32
},
"606595407769894995": {
@ -92,7 +92,7 @@
"level": 39
},
"322835877027905547": {
"messages": 10019,
"messages": 10021,
"level": 25
},
"485793265568841728": {
@ -100,7 +100,7 @@
"level": 50
},
"837979120142778388": {
"messages": 3636,
"messages": 3637,
"level": 15
},
"509467946146332682": {
@ -112,7 +112,7 @@
"level": 18
},
"690090143008555064": {
"messages": 9978,
"messages": 9981,
"level": 25
},
"849633082440941628": {
@ -124,7 +124,7 @@
"level": 6
},
"452576735494406175": {
"messages": 1684,
"messages": 1685,
"level": 10
},
"763055599654666291": {
@ -148,8 +148,8 @@
"level": 0
},
"169891949464125441": {
"messages": 538,
"level": 5
"messages": 541,
"level": 6
},
"718453763932946432": {
"messages": 13549,
@ -164,7 +164,7 @@
"level": 6
},
"931816463113814066": {
"messages": 673,
"messages": 674,
"level": 6
},
"958108557503524904": {
@ -224,11 +224,11 @@
"level": 0
},
"528003225375473674": {
"messages": 79,
"messages": 80,
"level": 2
},
"645342896312156181": {
"messages": 693,
"messages": 696,
"level": 6
},
"488505753133645826": {
@ -236,7 +236,7 @@
"level": 0
},
"313461397457600512": {
"messages": 79,
"messages": 80,
"level": 2
},
"848489550065827850": {
@ -272,8 +272,8 @@
"level": 1
},
"771433946890108989": {
"messages": 21,
"level": 0
"messages": 22,
"level": 1
},
"847575717369872384": {
"messages": 10,
@ -348,7 +348,7 @@
"level": 0
},
"549295707304099846": {
"messages": 46,
"messages": 47,
"level": 1
},
"759390179064283178": {
@ -512,7 +512,7 @@
"level": 0
},
"660117183699091506": {
"messages": 9,
"messages": 10,
"level": 0
},
"634416842152345630": {
@ -524,7 +524,7 @@
"level": 0
},
"398164488077180938": {
"messages": 8,
"messages": 9,
"level": 0
},
"818595993121062974": {
@ -540,7 +540,7 @@
"level": 0
},
"869718328313278555": {
"messages": 98,
"messages": 101,
"level": 2
},
"772197160372404234": {
@ -1340,7 +1340,7 @@
"level": 0
},
"98464148379148288": {
"messages": 1504,
"messages": 1507,
"level": 10
},
"1025723411680460840": {
@ -1472,7 +1472,7 @@
"level": 0
},
"889624632724963329": {
"messages": 51,
"messages": 54,
"level": 1
},
"687692546314600530": {
@ -1856,7 +1856,7 @@
"level": 0
},
"1031950921233600662": {
"messages": 17,
"messages": 19,
"level": 1
},
"907951346769092618": {
@ -1924,7 +1924,7 @@
"level": 0
},
"813866707365265419": {
"messages": 114,
"messages": 119,
"level": 2
},
"726188513518813275": {
@ -2048,7 +2048,7 @@
"level": 0
},
"1023915974690340946": {
"messages": 21,
"messages": 22,
"level": 1
},
"871113268633174128": {
@ -2312,7 +2312,7 @@
"level": 0
},
"231907580748759040": {
"messages": 2,
"messages": 3,
"level": 0
},
"461177771801182223": {
@ -2320,7 +2320,7 @@
"level": 0
},
"1029465066174677132": {
"messages": 11,
"messages": 12,
"level": 0
},
"387345064927428611": {
@ -2544,7 +2544,7 @@
"level": 0
},
"697953686487564348": {
"messages": 123,
"messages": 128,
"level": 2
},
"664233182941413427": {
@ -2592,7 +2592,7 @@
"level": 0
},
"340587045250531358": {
"messages": 86,
"messages": 90,
"level": 2
},
"911709434554777660": {
@ -2680,7 +2680,7 @@
"level": 0
},
"999736341220839464": {
"messages": 9,
"messages": 11,
"level": 0
},
"901565970974392331": {
@ -2700,7 +2700,7 @@
"level": 0
},
"995622955897864272": {
"messages": 92,
"messages": 96,
"level": 2
},
"153323923633733632": {
@ -2772,6 +2772,10 @@
"level": 0
},
"248226483967885312": {
"messages": 3,
"level": 0
},
"985545842368864326": {
"messages": 2,
"level": 0
}

View File

@ -7,10 +7,12 @@ export default {
const channel = client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel;
if (msg.partial) return;
if (msg.author.bot) return;
const embed = new client.embed().setColor(client.config.embedColorRed).setTimestamp().setAuthor({name: `Author: ${msg.author.tag} (${msg.author.id})`, iconURL: `${msg.author.displayAvatarURL()}`}).setTitle('Message deleted').setDescription(`<@${msg.author.id}>\nContent:\n\`\`\`\n${msg.content}\n\`\`\`\nChannel: <#${msg.channelId}>`)
channel.send({embeds: [embed]})
const embed = new client.embed().setColor(client.config.embedColorRed).setTimestamp().setAuthor({name: `Author: ${msg.author.tag} (${msg.author.id})`, iconURL: `${msg.author.displayAvatarURL()}`}).setTitle('Message deleted').setDescription(`<@${msg.author.id}>\nContent:\n\`\`\`\n${msg?.content}\n\`\`\`\nChannel: <#${msg.channelId}>`)
let image;
if (msg.attachments?.first()?.width && ['png', 'jpeg', 'jpg', 'gif', 'webp'].some(x=>((msg.attachments.first() as Discord.Attachment).name as string).endsWith(x))) {
embed.setImage(`${[msg.attachments?.first() as Discord.Attachment]}`)
image = msg.attachments?.first().url
embed.setImage(image)
}
channel.send({embeds: [embed]})
}
}

View File

@ -5,11 +5,11 @@ export default {
execute: async(client:TClient, messages:Discord.Collection<Snowflake, Message>)=>{
const channel = client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel;
if (!client.config.botSwitches.logs) return;
let text = '';
let msgArray: Array<string> = [];
messages.forEach((m)=>{
text += `${m.author.username}: ${m.content}\n`;
msgArray.push(`${m.author.username}: ${m.content}`);
});
const embed = new client.embed().setColor(client.config.embedColorRed).setTimestamp().setTitle(`${messages.size} messages were purged`).setDescription(`\`\`\`${text}\`\`\``.slice(0,3900)).addFields({name: 'Channel', value: `<#${(messages.first() as Discord.Message).channel.id}>`});
const embed = new client.embed().setColor(client.config.embedColorRed).setTimestamp().setTitle(`${messages.size} messages were purged`).setDescription(`\`\`\`${msgArray.reverse().join('\n')}\`\`\``.slice(0,3900)).addFields({name: 'Channel', value: `<#${(messages.first() as Discord.Message).channel.id}>`});
channel.send({embeds: [embed]})
}
}

View File

@ -1,15 +1,10 @@
interface punOpt {
time?: string,
reason?: string,
interaction?: any
}
import Discord from 'discord.js';
import { TClient } from './client';
const client = new TClient;
client.init();
import fs from 'node:fs';
import MPDB from './models/MPServer';
import { Punishment } from './typings/interfaces';
import { Punishment, punOpt } from './typings/interfaces';
client.on('ready', async()=>{
client.guilds.cache.forEach(async(e: { members: { fetch: () => any; }; })=>{await e.members.fetch()});
@ -190,7 +185,7 @@ Object.assign(client.punishments,{
const banData: Punishment = {type, id: this.createId(), member: member.id, moderator, time: now};
let dm1:any;
try {
dm1 = await member.send(`You've been banned from ${interaction.guild.name} ${timeInMillis ? `for ${client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${banData.id})`)
dm1 = await member.send(`You've been banned from ${interaction.guild.name} ${timeInMillis ? `for ${client.formatTime(timeInMillis, 4, {longNames: true, commas: true})}` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${banData.id})`)
} catch (err) {
setTimeout(()=>interaction.channel.send('Failed to DM user.'), 500)
}
@ -209,7 +204,7 @@ Object.assign(client.punishments,{
this.forceSave();
return new client.embed().setColor(client.config.embedColor).setTitle(`Case #${banData.id}: Ban`).setDescription(`${member.tag}\n<@${member.id}>\n(\`${member.id}\`)`).addFields(
{name: 'Reason', value: `\`${reason || 'Reason unspecified'}\``},
{name: 'Duration', value: `${timeInMillis ? `for ${client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)` : 'forever'}`}
{name: 'Duration', value: `${timeInMillis ? `for ${client.formatTime(timeInMillis, 4, {longNames: true, commas: true})}` : 'forever'}`}
)
}
case 'softban':
@ -253,7 +248,7 @@ Object.assign(client.punishments,{
case 'mute':
const muteData: Punishment = {type, id: this.createId(), member: member.user.id, moderator, time: now};
let muteResult;
const dm4 = await member.send(`You've been muted in ${member.guild.name} ${timeInMillis ? `for ${client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${muteData.id})`).catch((err:Error)=>setTimeout(()=>interaction.channel.send('Failed to DM user.'), 500));
const dm4 = await member.send(`You've been muted in ${member.guild.name} ${timeInMillis ? `for ${client.formatTime(timeInMillis, 4, {longNames: true, commas: true})}` : 'forever'} for reason \`${reason || 'Reason unspecified'}\` (Case #${muteData.id})`).catch((err:Error)=>setTimeout(()=>interaction.channel.send('Failed to DM user.'), 500));
if (timeInMillis){
muteResult = await member.timeout(timeInMillis, `${reason || 'Reason unspecified'} | Case #${muteData.id}`).catch((err: Error)=>err.message);
} else {
@ -273,7 +268,7 @@ Object.assign(client.punishments,{
this.forceSave();
const embedm = new client.embed().setColor(client.config.embedColor).setTitle(`Case #${muteData.id}: Mute`).setDescription(`${member.user.tag}\n<@${member.user.id}>\n(\`${member.user.id}\`)`).addFields(
{name: 'Reason', value: `\`${reason || 'Reason unspecified'}\``},
{name: 'Duration', value: `${client.formatTime(timeInMillis, 4, {longNames: true, commas: true})} (${timeInMillis}ms)`}
{name: 'Duration', value: `${client.formatTime(timeInMillis, 4, {longNames: true, commas: true})}`}
)
if (moderator !== '795443537356521502') {return embedm};
}
@ -328,7 +323,7 @@ Object.assign(client.punishments,{
client.makeModlogEntry(removePunishmentData, client);
this._content[this._content.findIndex(x=>x.id === punishment.id)].expired = true;
this.addData(removePunishmentData).forceSave();
return `Successfully ${punishment.type === 'ban' ? 'unbanned' : 'unmuted'} ${removePunishmentResult?.tag} (\`${removePunishmentResult?.id}\`) for reason \`${reason || 'Reason unspecified'}\``;
return `Successfully ${punishment.type === 'ban' ? 'unbanned' : 'unmuted'} **${removePunishmentResult?.tag}** (\`${removePunishmentResult?.id}\`) for reason \`${reason || 'Reason unspecified'}\``;
}
} else {
try {