this is a rewrite of the bot in typescript. detritus is used as a discord library instead of discord.js
This commit is contained in:
spiral 2022-10-21 17:22:01 +00:00
parent 6688d4dcd8
commit 56091a6df7
No known key found for this signature in database
GPG key ID: 244A11E4B0BCF40E
33 changed files with 731 additions and 1116 deletions

View file

@ -1,8 +0,0 @@
const CommandCategory = {
MODERATION: 'moderation',
BOT_MANAGEMENT: 'bot_management',
FUN: 'fun',
RESOURCE: 'resource'
};
module.exports = CommandCategory;

View file

@ -0,0 +1,9 @@
enum CommandCategory {
INFO = 'Info',
MODERATION = 'Moderation',
BOT_MANAGEMENT = 'Bot Managenent',
FUN = 'Fun',
RESOURCE = 'Resource'
}
export default CommandCategory;

View file

@ -1,36 +0,0 @@
const Config = require('../config.json');
const Guild = require('./guild');
const CommandPermission = {
/**
* @param {Message} message
* @returns {Promise.<boolean>}
*/
isMommy: async (message) => {
const member = await Guild.getMemberFromMessage(message);
return member.id === Config.mom;
},
/**
* @param {Message} message
* @returns {Promise.<boolean>}
*/
isMemberMod: async (message) => {
const member = await Guild.getMemberFromMessage(message);
return member.id === Config.mom || await Guild.isMemberMod(member);
},
/**
* @param {Message} message
* @returns {Promise.<boolean>}
*/
isMemberModOrHelper: async (message) => {
const member = await Guild.getMemberFromMessage(message);
return await CommandPermission.isMemberMod(message) || await Guild.isMemberHelper(member);
},
};
module.exports = CommandPermission;

View file

@ -0,0 +1,9 @@
import { GatewayMessageCreateDispatchData } from 'discord-api-types/v10';
import config from '../config';
export const isMommy = (message: GatewayMessageCreateDispatchData) => message.author.id == config.mom;
export const isMemberMod = (message: GatewayMessageCreateDispatchData) => message.member!.roles.includes(config.roles.mod);
export const isMemberModOrHelper = (message: GatewayMessageCreateDispatchData) =>
message.member!.roles.includes(config.roles.mod)
|| message.member!.roles.includes(config.roles.helper);

View file

@ -1,105 +0,0 @@
const fs = require('fs');
const Discord = require('discord.js');
const Config = require('../config.json');
const Guild = require('./guild');
const customMessages = require("./messages");
const cachelessRequire = (path) => {
if (typeof path === 'string') {
delete require.cache[require.resolve(path)];
}
return typeof path === 'string' ? require(path) : null;
};
const Command = {
commandList: new Discord.Collection(),
commandAliases: {},
init: () => {
Command.commandList = new Discord.Collection();
Command.commandAliases = {};
fs.readdirSync('command/').forEach(file => {
if (file.substr(file.lastIndexOf('.')).toLowerCase() === '.js') {
const commandPath = `../command/${file}`;
const commandInstance = cachelessRequire(commandPath);
if (commandInstance !== null) {
const commandName = file.substr(0, file.lastIndexOf('.')).toLowerCase();
Command.commandList.set(commandName, commandPath);
if (commandInstance.aliases !== undefined && commandInstance.aliases !== null) {
commandInstance.aliases.forEach(alias => {
Command.commandAliases[alias.toLowerCase()] = commandName;
});
}
}
}
});
},
/**
* @param {Message} message
* @returns {boolean}
*/
parseMessage: async (message) => {
if (!message.content.toLowerCase().startsWith(Config.prefix))
return false;
let content = message.content.substr(Config.prefix.length).trim().split(' ');
const calledCommand = content.shift().toLowerCase();
if (embed = customMessages.getEmbed(calledCommand))
return await message.channel.send({embed});
if (!await Command.isValid(calledCommand, message)) return;
const member = await Guild.getMemberFromMessage(message);
if (member === null)
return message.reply('sorry, you do not seem to be on the server.');
let commandName = calledCommand;
if (Command.commandAliases.hasOwnProperty(calledCommand))
commandName = Command.commandAliases[calledCommand];
const commandInstance = cachelessRequire(Command.commandList.get(commandName));
if (commandInstance !== null)
commandInstance.process(message, content, Command);
else
Command.commandList.delete(commandName);
},
/**
* @param {string} command
* @param {Message} message
* @return {Promise.<boolean>}
*/
isValid: async (command, message) => {
let canonicalCommand = command.toLowerCase();
let valid = Command.commandList.has(canonicalCommand);
if (!valid && Command.commandAliases.hasOwnProperty(canonicalCommand)) {
canonicalCommand = Command.commandAliases[command];
valid = Command.commandList.has(canonicalCommand);
}
const commandInstance = cachelessRequire(Command.commandList.get(canonicalCommand));
if (commandInstance === null) {
Command.commandList.delete(canonicalCommand);
}
valid = valid
&& commandInstance !== null
&& await commandInstance.isAllowedForContext(message);
return valid;
}
};
module.exports = Command;

View file

@ -1,173 +0,0 @@
const Logger = require('@lilywonhalf/pretty-logger');
const Config = require('../config.json');
const Discord = require('discord.js');
const cachelessRequire = (path) => {
if (typeof path === 'string') {
delete require.cache[require.resolve(path)];
}
return typeof path === 'string' ? require(path) : null;
};
const Guild = {
/** {Guild} */
discordGuild: null,
/** {TextChannel} */
joinsChannel: null,
/** {Collection} */
messagesCache: new Discord.Collection(),
init: async () => {
Guild.discordGuild = bot.guilds.cache.find(guild => guild.id === Config.guild);
Guild.joinsChannel = Guild.discordGuild.channels.cache.find(channel => channel.id === Config.channels.joins);
},
/**
* @param message
* @returns {Promise.<GuildMember|null>}
*/
getMemberFromMessage: async (message) => {
return await Guild.discordGuild.members.fetch(message.author).catch(exception => {
Logger.error(exception.toString());
return null;
});
},
/**
* @param {GuildMember} member
*/
isMemberMod: (member) => {
return member !== undefined && member !== null && member.roles.cache.has(Config.roles.mod);
},
/**
* @param {GuildMember} member
*/
isMemberHelper: (member) => {
return member !== undefined && member !== null && member.roles.cache.has(Config.roles.helper);
},
/**
* @param {string} roleName
* @returns {Role|null}
*/
getRoleByName: (roleName) => {
return roleName === undefined || roleName === null ? null : Guild.discordGuild.roles.cache.find(
role => role.name.toLowerCase() === roleName.toLowerCase()
);
},
/**
* @param {GuildMember} member
* @param {Snowflake} snowflake - The Role snowflake.
* @returns {boolean}
*/
memberHasRole: (member, snowflake) => {
return member !== undefined && member !== null && member.roles.cache.some(role => role.id === snowflake);
},
/**
* @param {Message} message
* @returns {Discord.MessageEmbed}
*/
messageToEmbed: async (message) => {
const member = await Guild.getMemberFromMessage(message);
const suffix = member !== null && member.nickname !== null && member.nickname !== undefined ? ` aka ${member.nickname}` : '';
const embeds = message.embeds.filter(embed => embed.author.name && embed.author.iconURL);
let authorName = `${message.author.username}#${message.author.discriminator}${suffix}`;
let authorImage = message.author.displayAvatarURL({ dynamic: true });
let description = message.content;
let timestamp = message.createdTimestamp;
let image = null;
if (message.attachments.size > 0) {
image = message.attachments.first().url;
}
if (description.length < 1 && embeds.length > 0) {
const embed = embeds[0];
description = embed.description ? embed.description.trim() : '';
if (message.author.bot) {
if (embed.author) {
authorName = embed.author.name;
authorImage = embed.author.iconURL;
}
if (embed.timestamp) {
timestamp = embed.timestamp;
}
if (embed.image) {
image = embed.image.url;
}
}
}
return new Discord.MessageEmbed()
.setAuthor(authorName, authorImage)
.setColor(0x00FF00)
.setDescription(description)
.setTimestamp(timestamp)
.setImage(image);
},
/**
* @param {Message} message
* @returns {{certain: boolean, foundMembers: Array}}
*/
findDesignatedMemberInMessage: (message) => {
let foundMembers = [];
let certain = true;
const memberList = bot.users.cache.concat(Guild.discordGuild.members.cache);
if (message.mentions.members !== null && message.mentions.members.size > 0) {
foundMembers = message.mentions.members.array();
} else if (message.content.match(/[0-9]{18}/u) !== null) {
const ids = message.content.match(/[0-9]{18}/gu);
ids.map(id => {
if (memberList.has(id)) {
foundMembers.push(memberList.get(id));
}
});
} else {
certain = false;
memberList.forEach(member => {
const user = member.user === undefined ? member : member.user;
const hasNickname = member.nickname !== undefined && member.nickname !== null;
const nickname = hasNickname ? `${member.nickname.toLowerCase()}#${user.discriminator}` : '';
const username = `${user.username.toLowerCase()}#${user.discriminator}`;
const content = message.cleanContent.toLowerCase().split(' ').splice(1).join(' ');
if (content.length > 0) {
const contentInNickname = hasNickname ? nickname.indexOf(content) > -1 : false;
const contentInUsername = username.indexOf(content) > -1;
const nicknameInContent = hasNickname ? content.indexOf(nickname) > -1 : false;
const usernameInContent = content.indexOf(username) > -1;
if (contentInNickname || contentInUsername || nicknameInContent || usernameInContent) {
foundMembers.push(member);
}
}
});
}
return {
certain,
foundMembers
};
},
guildMemberAddHandler: (member) => {
Guild.joinsChannel.send(`Welcome, ${member}! If you joined for any specific support questions please check out <#863171642905591830> first to see if your issue is known, and make sure that your app is up-to-date before posting.`);
}
};
module.exports = Guild;

View file

@ -1,37 +0,0 @@
const axios = require('axios');
const Config = require('../config.json');
const ENDPOINTS = {
search: '/search'
};
class Jira
{
async search(version) {
const params = {
'jql': 'project%20%3D%20SP%20AND%20status%20in%20(%22Claimed%20Fixed%22%2C%20%22In%20Pre-Testing%22)%20AND%20updated%20%3E%3D%20-24h%20ORDER%20BY%20created%20DESC',
'maxResults': 100,
'startAt': 0
};
if (version) {
params.jql = `project%20%3D%20SP%20AND%20status%20in%20(%22Claimed%20Fixed%22%2C%20%22In%20Pre-Testing%22)%20AND%20%22Fixed%20In%20Version%5BNumber%5D%22%20%3D%20%22${version}%22%20ORDER%20BY%20created%20DESC`;
}
const issues = [];
let response = null;
do {
const httpParams = Object.keys(params).map(key => `${key}=${params[key]}`).join('&');
const url = `${Config.jira.baseUrl}${ENDPOINTS.search}?${httpParams}`;
response = await axios(url);
issues.push(...response.data.issues);
params.startAt += 100;
} while (response.data.issues.length > 99 && response.data.total > 100);
return issues;
}
}
module.exports = new Jira();

30
model/jira.ts Normal file
View file

@ -0,0 +1,30 @@
import * as axios from 'axios';
import config from '../config';
export default async function search(version?: string): Promise<any[]> {
const params = {
'jql': 'project%20%3D%20SP%20AND%20status%20in%20(%22Claimed%20Fixed%22%2C%20%22In%20Pre-Testing%22)%20AND%20updated%20%3E%3D%20-24h%20ORDER%20BY%20created%20DESC',
'maxResults': 100,
'startAt': 0
};
if (version) {
params.jql = `project%20%3D%20SP%20AND%20status%20in%20(%22Claimed%20Fixed%22%2C%20%22In%20Pre-Testing%22)%20AND%20%22Fixed%20In%20Version%5BNumber%5D%22%20%3D%20%22${version}%22%20ORDER%20BY%20created%20DESC`;
}
const issues: any[] = [];
let response: any | null = null;
do {
// @ts-expect-error
const httpParams = Object.keys(params).map(key => `${key}=${params[key]}`).join('&');
const url = `${config.jira.baseUrl}/search?${httpParams}`;
// what even is this
response = JSON.parse((await new axios.Axios({}).get(url)).data);
issues.push(...response!.issues);
params.startAt += 100;
} while (response!.issues.length > 99 && response!.total > 100);
return issues;
}

View file

@ -1,65 +0,0 @@
const fs = require("fs")
const path = require('path');
const messages = {};
const load = () =>
{
const messagesFile = fs.readFileSync(path.join(__dirname, '..', 'messages.json'))
const messagesJson = JSON.parse(messagesFile);
const messagesList = messagesJson["faq"];
messagesList.forEach((msg) => {
messages[msg["title"]] = msg
})
}
load();
// interface Message {
// names string[];
// description string;
// title string;
// text string;
// friendlyName string;
// category string;
// }
module.exports = {
get names() {
return names;
},
get messages() {
return messages;
},
get: (name) => messages[name],
getList: () => Object.keys(messages).map(msg => ({ name: messages[msg].names[0], value: messages[msg].description })),
getEmbed: (name) => {
let foundMsg = null
Object.keys(messages).forEach((msgKey) =>
{
if (foundMsg) return;
const msg = messages[msgKey]
if (msg.names.includes(name))
{
foundMsg = msg;
return;
}
});
if (!foundMsg) return;
return {
color: "A95B44",
author: {
name: foundMsg.title,
iconURL: bot.user.displayAvatarURL({ dynamic: true }),
},
description: foundMsg.text,
}
}
}

58
model/messages.ts Normal file
View file

@ -0,0 +1,58 @@
import { restClient } from "../bot";
interface Message {
names: string[];
description: string;
title: string;
text: string;
friendlyName: string;
category: string;
}
export const messages: Record<string, Message> = {};
let avatarUrl = "";
const load = async () =>
{
const messagesList = require('../messages.json')["faq"];
messagesList.forEach((msg: any) => {
messages[msg["title"]] = msg
})
const userInfo = await restClient.fetchMe();
avatarUrl = `https://cdn.discordapp.com/avatars/${userInfo.id}/${userInfo.avatar}.png`;
console.log(avatarUrl);
}
load();
export const get = (name: string) => messages[name];
export const getList = () => Object.keys(messages).map(msg => ({ name: messages[msg].names[0], value: messages[msg].description }));
export const getEmbed = (name: string) => {
let foundMsg: Message | undefined;
Object.keys(messages).forEach((msgKey) =>
{
if (foundMsg) return;
const msg = messages[msgKey]
if (msg.names.includes(name))
{
foundMsg = msg;
return;
}
});
if (foundMsg == null) return;
return {
color: 0xA95B44,
author: {
name: foundMsg.title,
iconUrl: avatarUrl,
},
description: foundMsg.text,
}
}