mirror of
https://github.com/DarthKilroy/Spot.git
synced 2026-02-04 02:26:48 +00:00
rewrite
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:
parent
6688d4dcd8
commit
56091a6df7
33 changed files with 731 additions and 1116 deletions
|
|
@ -1,8 +0,0 @@
|
|||
const CommandCategory = {
|
||||
MODERATION: 'moderation',
|
||||
BOT_MANAGEMENT: 'bot_management',
|
||||
FUN: 'fun',
|
||||
RESOURCE: 'resource'
|
||||
};
|
||||
|
||||
module.exports = CommandCategory;
|
||||
9
model/command-category.ts
Normal file
9
model/command-category.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
enum CommandCategory {
|
||||
INFO = 'Info',
|
||||
MODERATION = 'Moderation',
|
||||
BOT_MANAGEMENT = 'Bot Managenent',
|
||||
FUN = 'Fun',
|
||||
RESOURCE = 'Resource'
|
||||
}
|
||||
|
||||
export default CommandCategory;
|
||||
|
|
@ -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;
|
||||
9
model/command-permission.ts
Normal file
9
model/command-permission.ts
Normal 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);
|
||||
|
||||
105
model/command.js
105
model/command.js
|
|
@ -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;
|
||||
173
model/guild.js
173
model/guild.js
|
|
@ -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;
|
||||
|
|
@ -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
30
model/jira.ts
Normal 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;
|
||||
}
|
||||
|
|
@ -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
58
model/messages.ts
Normal 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,
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue