Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
Rauf | c7b55b1aae | |
Rauf | 07029ffc52 | |
Rauf | 2e3e0bbe0a | |
Rauf | a62c0e3b78 | |
Rauf | b12269f132 | |
Rauf | 685b1c1d39 | |
Rauf | bf24b9f46b | |
Rauf | 2fb4842994 |
|
@ -107,3 +107,7 @@ dist
|
|||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# Config Files
|
||||
src/config/*
|
||||
build/*
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
40
package.json
40
package.json
|
@ -2,8 +2,44 @@
|
|||
"name": "lifeguard",
|
||||
"version": "0.0.1-alpha",
|
||||
"description": "Lifeguard Bot",
|
||||
"main": "dist/bot.js",
|
||||
"main": "build/src/index.js",
|
||||
"repository": "https://gitdab.com/lifeguard/bot.git",
|
||||
"author": "Rauf",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/bson": "^4.0.1",
|
||||
"@types/node": "^10.17.13",
|
||||
"@types/ws": "^6.0.4",
|
||||
"bson": "^4.0.3",
|
||||
"gts": "^1.1.2",
|
||||
"typescript": "^3.7.5"
|
||||
},
|
||||
"scripts": {
|
||||
"check": "gts check",
|
||||
"clean": "gts clean",
|
||||
"compile": "tsc -p .",
|
||||
"watch": "tsc -w -p .",
|
||||
"fix": "gts fix",
|
||||
"prepare": "npm.cmd run compile",
|
||||
"pretest": "npm.cmd run compile",
|
||||
"posttest": "npm.cmd run check"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/mongodb": "^3.3.14",
|
||||
"discord.js": "github:discordjs/discord.js",
|
||||
"module-alias": "^2.2.2",
|
||||
"mongodb": "^3.5.2",
|
||||
"typy": "^3.3.0"
|
||||
},
|
||||
"_moduleAliases": {
|
||||
"@lifeguard": "./build/src",
|
||||
"@lifeguard/base": "./",
|
||||
"@assertions": "./build/src/assertions",
|
||||
"@config": "./build/src/config",
|
||||
"@events": "./build/src/events",
|
||||
"@models": "./build/src/models",
|
||||
"@plugins": "./build/src/plugins",
|
||||
"@structures": "./build/src/structures",
|
||||
"@util": "./build/src/util"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
singleQuote: true,
|
||||
trailingComma: 'es5',
|
||||
};
|
|
@ -0,0 +1,20 @@
|
|||
import { name, url } from '@config/mongodb';
|
||||
import { Plugin } from '@plugins/Plugin';
|
||||
import { Database } from '@util/Database';
|
||||
import { Client, ClientOptions, Collection } from 'discord.js';
|
||||
|
||||
export class PluginClient extends Client {
|
||||
plugins!: Collection<string, Plugin>;
|
||||
db: Database;
|
||||
constructor(options?: ClientOptions) {
|
||||
super(options);
|
||||
this.db = new Database({
|
||||
name,
|
||||
url,
|
||||
MongoOptions: {
|
||||
useNewUrlParser: true,
|
||||
useUnifiedTopology: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
import { GuildMember, Guild } from 'discord.js';
|
||||
import { developers } from '@config/bot';
|
||||
|
||||
export function calcUserLevel(user: GuildMember, guild: Guild) {
|
||||
if (developers.includes(user.id)) {
|
||||
return 5;
|
||||
}
|
||||
if (user.id === guild.ownerID) {
|
||||
return 3;
|
||||
}
|
||||
if (user.permissions.has('ADMINISTRATOR', true)) {
|
||||
return 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import { PluginClient } from '../PluginClient';
|
||||
|
||||
// tslint:disable-next-line: no-any
|
||||
type EventFunc = (lifeguard: PluginClient, ...args: any[]) => void;
|
||||
|
||||
export class Event {
|
||||
constructor(public name: string, public func: EventFunc) {}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import { Event } from '@events/Event';
|
||||
import { GuildStructure } from '@structures/GuildStructure';
|
||||
import { defaultEmbed } from '@util/DefaultEmbed';
|
||||
import { GuildChannel, TextChannel } from 'discord.js';
|
||||
|
||||
export const event = new Event(
|
||||
'channelCreate',
|
||||
async (lifeguard, channel: GuildChannel) => {
|
||||
const dbGuild = await (channel.guild as GuildStructure).db;
|
||||
if (dbGuild?.config.channels?.logging) {
|
||||
const modlog = channel.guild.channels.get(
|
||||
dbGuild.config.channels.logging
|
||||
) as TextChannel;
|
||||
|
||||
const auditLog = await channel.guild.fetchAuditLogs({
|
||||
type: 'CHANNEL_CREATE',
|
||||
});
|
||||
const auditLogEntry = auditLog.entries.last();
|
||||
|
||||
// const embed = defaultEmbed()
|
||||
// .setDescription(
|
||||
// `:pencil: Channel ${channel.toString()} (${channel.id}) was created`
|
||||
// )
|
||||
// .setTitle('Channel Create');
|
||||
|
||||
// if (auditLogEntry) {
|
||||
// embed.setAuthor(`${auditLogEntry.executor.tag}`);
|
||||
// }
|
||||
|
||||
const embed = defaultEmbed();
|
||||
|
||||
modlog.send(embed);
|
||||
}
|
||||
}
|
||||
);
|
|
@ -0,0 +1,21 @@
|
|||
import { Event } from '@events/Event';
|
||||
import { PluginClient } from '@lifeguard/PluginClient';
|
||||
import { readdir } from 'fs';
|
||||
import { promisify } from 'util';
|
||||
|
||||
const readDir = promisify(readdir);
|
||||
|
||||
export async function EventLoader(lifeguard: PluginClient) {
|
||||
const eventFiles = await readDir('./build/src/events');
|
||||
|
||||
for await (const file of eventFiles) {
|
||||
if (file.endsWith('js') && file !== 'Event.js') {
|
||||
const { event } = require(`./${file}`);
|
||||
if (event instanceof Event) {
|
||||
lifeguard.on(event.name, (...args: []) => {
|
||||
event.func(lifeguard, ...args);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
import { Event } from '@events/Event';
|
||||
import { Guild } from 'discord.js';
|
||||
|
||||
export const event = new Event(
|
||||
'guildCreate',
|
||||
async (lifeguard, guild: Guild) => {
|
||||
await lifeguard.db.guilds.insertOne({ id: guild.id, config: {} });
|
||||
if (lifeguard.user) {
|
||||
lifeguard.user.setPresence({
|
||||
activity: {
|
||||
name: `${lifeguard.users.size} people in the pool`,
|
||||
type: 'WATCHING',
|
||||
},
|
||||
status: 'online',
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
|
@ -0,0 +1,49 @@
|
|||
import { calcUserLevel } from '@assertions/userLevel';
|
||||
import { prefix } from '@config/bot';
|
||||
import { Event } from '@events/Event';
|
||||
import { PluginClient } from '@lifeguard/PluginClient';
|
||||
import { UserDoc } from '@models/User';
|
||||
import { Message } from 'discord.js';
|
||||
|
||||
function parseContent(content: string) {
|
||||
const split = content.split(' ');
|
||||
const cmdName = split[0].slice(prefix.length);
|
||||
split.shift();
|
||||
return [cmdName, ...split];
|
||||
}
|
||||
|
||||
function getCommandFromPlugin(lifeguard: PluginClient, cmdName: string) {
|
||||
const plugin = lifeguard.plugins.find(p => p.has(cmdName));
|
||||
if (plugin) {
|
||||
return plugin?.get(cmdName);
|
||||
} else {
|
||||
const plugins = [...lifeguard.plugins.values()];
|
||||
const cmds = plugins
|
||||
.map(p => [...p.values()])
|
||||
.reduce((acc, val) => acc.concat(val), []);
|
||||
return cmds.find(cmd => cmd.options.alias?.includes(cmdName));
|
||||
}
|
||||
}
|
||||
|
||||
export const event = new Event(
|
||||
'lifeguardCommandUsed',
|
||||
async (lifeguard, msg: Message, dbUser: UserDoc) => {
|
||||
if (msg.author.bot) {
|
||||
return;
|
||||
}
|
||||
if (dbUser.blacklisted) {
|
||||
return;
|
||||
}
|
||||
const [cmdName, ...args] = parseContent(msg.content);
|
||||
const cmd = getCommandFromPlugin(lifeguard, cmdName);
|
||||
|
||||
if (cmd) {
|
||||
if (msg.member && msg.guild) {
|
||||
const userLevel = calcUserLevel(msg.member, msg.guild);
|
||||
if (userLevel >= cmd.options.level) {
|
||||
cmd.func(lifeguard, msg, args, dbUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
|
@ -0,0 +1,43 @@
|
|||
import { prefix } from '@config/bot';
|
||||
import { Event } from '@events/Event';
|
||||
import { Guild } from '@models/Guild';
|
||||
import { User } from '@models/User';
|
||||
import { GuildStructure } from '@structures/GuildStructure';
|
||||
import { Message } from 'discord.js';
|
||||
|
||||
export const event = new Event('message', async (lifeguard, msg: Message) => {
|
||||
let dbUser = await lifeguard.db.users.findOne({
|
||||
id: msg.author.id,
|
||||
});
|
||||
if (!dbUser) {
|
||||
await lifeguard.db.users.insertOne(new User({ id: msg.author.id }));
|
||||
dbUser = await lifeguard.db.users.findOne({
|
||||
id: msg.author.id,
|
||||
});
|
||||
}
|
||||
|
||||
if (msg.guild) {
|
||||
const dbGuild = await (msg.guild as GuildStructure).db;
|
||||
if (!dbGuild) {
|
||||
await lifeguard.db.guilds.insertOne(new Guild({ id: msg.guild.id }));
|
||||
}
|
||||
}
|
||||
|
||||
if (dbUser) {
|
||||
dbUser.stats.totalSentMessages++;
|
||||
dbUser.stats.totalSentCharacters += msg.content.length;
|
||||
dbUser.stats.totalCustomEmojisUsed +=
|
||||
msg.content.match(/<.[^ ]*>/)?.length ?? 0;
|
||||
dbUser.stats.totalTimesMentionedAUser += msg.mentions.users.size;
|
||||
dbUser.stats.totalSentAttachments += msg.attachments.size;
|
||||
|
||||
await lifeguard.db.users.updateOne(
|
||||
{ id: dbUser.id },
|
||||
{ $set: { stats: dbUser.stats } }
|
||||
);
|
||||
}
|
||||
|
||||
if (msg.content.startsWith(prefix)) {
|
||||
lifeguard.emit('lifeguardCommandUsed', msg, dbUser);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
import { Event } from '@events/Event';
|
||||
import { GuildStructure } from '@structures/GuildStructure';
|
||||
import { MessageReaction, User } from 'discord.js';
|
||||
|
||||
export const event = new Event(
|
||||
'messageReactionAdd',
|
||||
async (lifeguard, reaction: MessageReaction, user: User) => {
|
||||
await lifeguard.db.users.updateOne(
|
||||
{ id: user.id },
|
||||
{ $inc: { 'stats.totalTimesReacted': 1 } }
|
||||
);
|
||||
const dbGuild = await (reaction.message.guild as GuildStructure).db;
|
||||
if (
|
||||
dbGuild?.config.channels?.starboard &&
|
||||
reaction.emoji.name === dbGuild?.config.starboard?.emoji
|
||||
) {
|
||||
lifeguard.emit('starboardReactionAdd', reaction);
|
||||
}
|
||||
}
|
||||
);
|
|
@ -0,0 +1,14 @@
|
|||
import { Event } from '@events/Event';
|
||||
|
||||
export const event = new Event('ready', lifeguard => {
|
||||
console.log('Connected to Discord');
|
||||
if (lifeguard.user) {
|
||||
lifeguard.user.setPresence({
|
||||
activity: {
|
||||
name: `${lifeguard.users.size} people in the pool`,
|
||||
type: 'WATCHING',
|
||||
},
|
||||
status: 'online',
|
||||
});
|
||||
}
|
||||
});
|
|
@ -0,0 +1,64 @@
|
|||
import { Event } from '@events/Event';
|
||||
import { GuildStructure } from '@structures/GuildStructure';
|
||||
import { defaultEmbed } from '@util/DefaultEmbed';
|
||||
import { TextChannel, MessageReaction } from 'discord.js';
|
||||
|
||||
export const event = new Event(
|
||||
'starboardReactionAdd',
|
||||
async (lifeguard, reaction: MessageReaction) => {
|
||||
const dbGuild = await (reaction.message.guild as GuildStructure).db;
|
||||
if (dbGuild?.config.starboard && dbGuild.config.channels?.starboard) {
|
||||
const starboardChannel = reaction.message.guild?.channels.get(
|
||||
dbGuild.config.channels.starboard
|
||||
) as TextChannel;
|
||||
const starboard = dbGuild.config.starboard;
|
||||
if (
|
||||
starboardChannel &&
|
||||
!starboard.ignoredChannels.includes(reaction.message.channel.id)
|
||||
) {
|
||||
if (reaction.count ?? 0 >= starboard.minCount) {
|
||||
const starboardMessage = starboard.messages.find(
|
||||
m => m.id === reaction.message.id
|
||||
);
|
||||
if (starboardMessage) {
|
||||
const starboardMessageInChannel = starboardChannel.messages.get(
|
||||
starboardMessage.starboardID
|
||||
);
|
||||
starboardMessage.count = reaction.count ?? starboardMessage.count;
|
||||
const embed = defaultEmbed()
|
||||
.setAuthor(
|
||||
starboardMessageInChannel?.author.tag,
|
||||
starboardMessageInChannel?.author.avatarURL() ?? ''
|
||||
)
|
||||
.setDescription(reaction.message.content);
|
||||
starboardMessageInChannel?.edit(
|
||||
`${starboard.emoji} ${reaction.count} ${reaction.message.channel} (${reaction.message.id})`,
|
||||
embed
|
||||
);
|
||||
} else {
|
||||
const embed = defaultEmbed()
|
||||
.setAuthor(
|
||||
reaction.message.author.tag,
|
||||
reaction.message.author.avatarURL() ?? ''
|
||||
)
|
||||
.setDescription(reaction.message.content);
|
||||
const starboardMessage = await starboardChannel.send(
|
||||
`${starboard.emoji} ${reaction.count} ${reaction.message.channel} (${reaction.message.id})`,
|
||||
embed
|
||||
);
|
||||
starboard.messages.push({
|
||||
id: reaction.message.id,
|
||||
starboardID: starboardMessage.id,
|
||||
content: reaction.message.content,
|
||||
count: reaction.count ?? 0,
|
||||
});
|
||||
}
|
||||
await lifeguard.db.guilds.updateOne(
|
||||
{ id: dbGuild.id },
|
||||
{ $set: { 'config.starboard': starboard } }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
|
@ -0,0 +1,29 @@
|
|||
import 'module-alias/register';
|
||||
|
||||
import { token } from '@config/bot';
|
||||
import { EventLoader } from '@events/eventLoader';
|
||||
import { PluginClient } from '@lifeguard/PluginClient';
|
||||
import { PluginLoader } from '@plugins/pluginLoader';
|
||||
import { StructureLoader } from '@structures/structureLoader';
|
||||
|
||||
StructureLoader();
|
||||
|
||||
const lifeguard = new PluginClient();
|
||||
|
||||
EventLoader(lifeguard);
|
||||
PluginLoader().then(plugins => {
|
||||
lifeguard.plugins = plugins;
|
||||
});
|
||||
|
||||
lifeguard.db
|
||||
.connect()
|
||||
.then(() => console.log('Connected to MongoDB'))
|
||||
.catch(err => console.error(err));
|
||||
|
||||
lifeguard.login(token).then(() => {
|
||||
if (lifeguard.user) {
|
||||
console.log(
|
||||
`Logged in to ${lifeguard.user.username}#${lifeguard.user.discriminator}`
|
||||
);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,52 @@
|
|||
import { ObjectId } from 'bson';
|
||||
|
||||
export interface GuildChannels {
|
||||
logging?: string;
|
||||
starboard?: string;
|
||||
}
|
||||
|
||||
export interface GuildRoles {
|
||||
[key: string]: string | string[] | undefined;
|
||||
muted?: string;
|
||||
moderator?: string;
|
||||
groupRoles?: string[];
|
||||
lockedRoles?: string[];
|
||||
}
|
||||
|
||||
export interface GuildStarboardMessage {
|
||||
id: string;
|
||||
starboardID: string;
|
||||
count: number;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export interface GuildStarboard {
|
||||
emoji: string;
|
||||
minCount: number;
|
||||
ignoredChannels: string[];
|
||||
messages: GuildStarboardMessage[];
|
||||
}
|
||||
|
||||
export interface GuildConfig {
|
||||
blacklisted?: boolean;
|
||||
channels?: GuildChannels;
|
||||
roles?: GuildRoles;
|
||||
starboard?: GuildStarboard;
|
||||
}
|
||||
|
||||
export interface GuildDoc {
|
||||
id: string;
|
||||
config?: GuildConfig;
|
||||
}
|
||||
|
||||
export class Guild implements GuildDoc {
|
||||
_id: ObjectId;
|
||||
id: string;
|
||||
config: GuildConfig;
|
||||
constructor(data: GuildDoc) {
|
||||
this._id = new ObjectId();
|
||||
this.id = data.id;
|
||||
this.config = data.config ?? {};
|
||||
this.config.blacklisted = data.config?.blacklisted ?? false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
import { ObjectId } from 'bson';
|
||||
import { PermissionOverwriteOption } from 'discord.js';
|
||||
|
||||
export interface UserInfraction {
|
||||
action: 'Warn' | 'Kick' | 'Mute' | 'Ban';
|
||||
active: boolean;
|
||||
guild: string;
|
||||
id: number;
|
||||
moderator: string;
|
||||
reason: string;
|
||||
time: Date;
|
||||
}
|
||||
|
||||
export interface UserBackup {
|
||||
guild: string;
|
||||
id: number;
|
||||
roles: string[];
|
||||
nickname: string;
|
||||
channelOverrides: UserBackupChannelOverride[];
|
||||
deafened: boolean;
|
||||
muted: boolean;
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export interface UserBackupChannelOverride {
|
||||
channelId: number;
|
||||
overrides: PermissionOverwriteOption;
|
||||
}
|
||||
|
||||
export interface UserStats {
|
||||
totalSentMessages: number;
|
||||
totalSentCharacters: number;
|
||||
totalDeletedMessages: number;
|
||||
totalCustomEmojisUsed: number;
|
||||
totalTimesMentionedAUser: number;
|
||||
totalSentAttachments: number;
|
||||
totalTimesReacted: number;
|
||||
mostUsedReaction: string;
|
||||
}
|
||||
|
||||
export interface UserReactions {
|
||||
[name: string]: number;
|
||||
}
|
||||
|
||||
export interface UserDoc {
|
||||
blacklisted?: boolean;
|
||||
id: string;
|
||||
infractions?: UserInfraction[];
|
||||
backups?: UserBackup[];
|
||||
stats?: UserStats;
|
||||
reactions?: UserReactions;
|
||||
}
|
||||
|
||||
export class User implements UserDoc {
|
||||
_id: ObjectId;
|
||||
blacklisted: boolean;
|
||||
id: string;
|
||||
infractions: UserInfraction[];
|
||||
backups: UserBackup[];
|
||||
stats: UserStats;
|
||||
constructor(data: UserDoc) {
|
||||
this._id = new ObjectId();
|
||||
this.blacklisted = data.blacklisted ?? false;
|
||||
this.id = data.id;
|
||||
this.infractions = data.infractions ?? [];
|
||||
this.backups = data.backups ?? [];
|
||||
this.stats = data.stats ?? {
|
||||
totalSentMessages: 0,
|
||||
totalSentCharacters: 0,
|
||||
totalDeletedMessages: 0,
|
||||
totalCustomEmojisUsed: 0,
|
||||
totalTimesMentionedAUser: 0,
|
||||
totalSentAttachments: 0,
|
||||
totalTimesReacted: 0,
|
||||
mostUsedReaction: '',
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import { Message, PermissionString } from 'discord.js';
|
||||
import { PluginClient } from '@lifeguard/PluginClient';
|
||||
import { UserDoc } from '@models/User';
|
||||
|
||||
type CommandFunction = (
|
||||
lifeguard: PluginClient,
|
||||
msg: Message,
|
||||
args: string[],
|
||||
dbUser?: UserDoc
|
||||
) => void;
|
||||
|
||||
interface CommandOptions {
|
||||
alias?: string[];
|
||||
guildOnly?: boolean;
|
||||
hidden?: boolean;
|
||||
level: number;
|
||||
usage: string[];
|
||||
permissions?: PermissionString[];
|
||||
}
|
||||
|
||||
export class Command {
|
||||
constructor(
|
||||
public name: string,
|
||||
public func: CommandFunction,
|
||||
public options: CommandOptions
|
||||
) {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import { Collection } from 'discord.js';
|
||||
import { Command } from '@plugins/Command';
|
||||
|
||||
export class Plugin extends Collection<string, Command> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { t as typy } from 'typy';
|
||||
|
||||
export const command = new Command(
|
||||
'config',
|
||||
async (lifeguard, msg, [cmd, ...args]) => {
|
||||
const path = args[0];
|
||||
switch (cmd) {
|
||||
case 'get':
|
||||
const guild = await lifeguard.db.guilds.findOne({ id: msg.guild?.id });
|
||||
if (guild) {
|
||||
const config = guild['config'];
|
||||
if (path) {
|
||||
msg.channel.send(`${path} - ${typy(config, path).safeObject}`);
|
||||
} else {
|
||||
msg.channel.send(
|
||||
`\`\`\`json\n${JSON.stringify(config, null, 2)}\`\`\``
|
||||
);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'set':
|
||||
const res = await lifeguard.db.guilds.findOneAndUpdate(
|
||||
{
|
||||
id: msg.guild?.id,
|
||||
},
|
||||
{
|
||||
$set: { [`config.${path}`]: JSON.parse(args[1]) },
|
||||
}
|
||||
);
|
||||
if (res.ok) {
|
||||
msg.channel.send('Value has been set successfully');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 3,
|
||||
usage: ['config get', 'config get {key}', 'config set {key} {value}'],
|
||||
hidden: true,
|
||||
}
|
||||
);
|
|
@ -0,0 +1,38 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { parseUser } from '@util/parseUser';
|
||||
|
||||
export const command = new Command(
|
||||
'role',
|
||||
async (lifeguard, msg, args) => {
|
||||
const [cmd, uid, rid, ...r] = args;
|
||||
const u = parseUser(uid);
|
||||
const role = msg.guild?.roles.get(rid);
|
||||
switch (cmd) {
|
||||
case 'add':
|
||||
if (role) {
|
||||
const member = msg.guild?.members.get(u);
|
||||
member?.roles.add(role, r.join(' '));
|
||||
msg.channel.send(`Added ${role.name} to ${member}`);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'rmv':
|
||||
if (role) {
|
||||
const member = msg.guild?.members.get(u);
|
||||
member?.roles.remove(role, r.join(' '));
|
||||
msg.channel.send(`Removed ${role.name} from ${member}`);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: [
|
||||
'role add {user} {role id} [reason]',
|
||||
'role rmv {user} {role id} [reason]',
|
||||
],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,31 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
|
||||
export const command = new Command(
|
||||
'roles',
|
||||
async (lifeguard, msg) => {
|
||||
const roleList = msg.guild?.roles
|
||||
?.sort((ra, rb) => rb.position - ra.position)
|
||||
.map(r => `${r.id} - ${r.name} (${r.members.size} members)`);
|
||||
const blocks: string[] = [''];
|
||||
|
||||
roleList?.forEach(r => {
|
||||
let currentBlockIndex: number = blocks.length - 1;
|
||||
|
||||
if (
|
||||
blocks[currentBlockIndex].length > 1990 ||
|
||||
blocks[currentBlockIndex].concat(`\n${r}`).length > 1990
|
||||
) {
|
||||
blocks.push('');
|
||||
currentBlockIndex++;
|
||||
}
|
||||
|
||||
blocks[currentBlockIndex] += `\n${r}`;
|
||||
});
|
||||
|
||||
blocks.forEach(b => msg.channel.send(`\`\`\`dns${b}\`\`\``));
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['roles'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,33 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { TextChannel } from 'discord.js';
|
||||
|
||||
export const command = new Command(
|
||||
'slowmode',
|
||||
async (lifeguard, msg, args) => {
|
||||
const [cmd, time] = args;
|
||||
switch (cmd) {
|
||||
case 'set':
|
||||
if (msg.guild) {
|
||||
(msg.channel as TextChannel).setRateLimitPerUser(+time);
|
||||
msg.channel.send(
|
||||
`Slowmode has been set to 1 message every ${time} seconds`
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'off':
|
||||
if (msg.guild) {
|
||||
(msg.channel as TextChannel).setRateLimitPerUser(0);
|
||||
msg.channel.send(`Slowmode has been turned off`);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['slowmode set {time}', 'slowmode off'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,28 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { defaultEmbed } from '@util/DefaultEmbed';
|
||||
|
||||
export const command = new Command(
|
||||
'ping',
|
||||
async (lifeguard, msg, args) => {
|
||||
const m = await msg.channel.send('Ping?');
|
||||
m.delete({ timeout: 100 });
|
||||
|
||||
const embed = defaultEmbed()
|
||||
.setTitle('Pong! :ping_pong:')
|
||||
.addField('Bot Latency', `${Math.round(lifeguard.ws.ping)}ms`)
|
||||
.addField(
|
||||
'Message Latency',
|
||||
`${m.createdTimestamp - msg.createdTimestamp}ms`
|
||||
)
|
||||
.setFooter(
|
||||
`Executed By ${msg.author.tag}`,
|
||||
msg.author.avatarURL() ?? msg.author.defaultAvatarURL
|
||||
);
|
||||
|
||||
msg.channel.send(embed);
|
||||
},
|
||||
{
|
||||
level: 0,
|
||||
usage: ['ping'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,76 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { defaultEmbed } from '@util/DefaultEmbed';
|
||||
import { inspect } from 'util';
|
||||
import { runInNewContext } from 'vm';
|
||||
|
||||
function parseBlock(script: string) {
|
||||
const cbr = /^(([ \t]*`{3,4})([^\n]*)([\s\S]+?)(^[ \t]*\2))/gm;
|
||||
const result = cbr.exec(script);
|
||||
if (result) {
|
||||
return result[4];
|
||||
}
|
||||
return script;
|
||||
}
|
||||
|
||||
async function run(
|
||||
script: string,
|
||||
ctx: object,
|
||||
opts: object
|
||||
): Promise<string | Error> {
|
||||
try {
|
||||
const result = await runInNewContext(
|
||||
`(async () => { ${script} })()`,
|
||||
ctx,
|
||||
opts
|
||||
);
|
||||
if (typeof result !== 'string') {
|
||||
return inspect(result);
|
||||
}
|
||||
return result;
|
||||
} catch (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
function makeCodeBlock(data: string, lang?: string) {
|
||||
return `\`\`\`${lang}\n${data}\n\`\`\``;
|
||||
}
|
||||
|
||||
export const command = new Command(
|
||||
'eval',
|
||||
async (lifeguard, msg, args, dbUser) => {
|
||||
const start = Date.now();
|
||||
|
||||
const script = parseBlock(args.join(' '));
|
||||
const exec = await run(
|
||||
script,
|
||||
{
|
||||
lifeguard,
|
||||
msg,
|
||||
defaultEmbed,
|
||||
dbUser,
|
||||
},
|
||||
{ filename: msg.guild?.id.toString() }
|
||||
);
|
||||
|
||||
const end = Date.now();
|
||||
|
||||
if (typeof exec === 'string') {
|
||||
const embed = defaultEmbed()
|
||||
.addField('Input', makeCodeBlock(script, 'js'))
|
||||
.addField('Output', makeCodeBlock(exec, 'js'))
|
||||
.setFooter(`Script Executed in ${end - start}ms`);
|
||||
msg.channel.send(embed);
|
||||
} else {
|
||||
const embed = defaultEmbed()
|
||||
.addField('Input', makeCodeBlock(script, 'js'))
|
||||
.addField('Output', makeCodeBlock(`${exec.name}: ${exec.message}`))
|
||||
.setFooter(`Script Executed in ${end - start}ms`);
|
||||
msg.channel.send(embed);
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 5,
|
||||
usage: ['eval {code}'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,23 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { parseUser } from '@util/parseUser';
|
||||
|
||||
export const command = new Command(
|
||||
'blacklist',
|
||||
async (lifeguard, msg, args) => {
|
||||
const u = parseUser(args[0]);
|
||||
try {
|
||||
await lifeguard.db.users.findOneAndUpdate(
|
||||
{ id: u },
|
||||
{ $set: { blacklisted: true } },
|
||||
{ returnOriginal: false }
|
||||
);
|
||||
msg.channel.send(`<@${u}> was sucessfully blacklisted`);
|
||||
} catch (err) {
|
||||
msg.channel.send(err.message);
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 4,
|
||||
usage: ['blacklist {user}'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,51 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { exec } from 'child_process';
|
||||
import { MessageEmbed } from 'discord.js';
|
||||
import { resolve } from 'path';
|
||||
import { promisify } from 'util';
|
||||
|
||||
export const command = new Command(
|
||||
'about',
|
||||
async (lifeguard, msg, args) => {
|
||||
// Convert child_process#exec to async/await
|
||||
const run = promisify(exec);
|
||||
|
||||
// Get Git Commit ID
|
||||
const { stdout: gitCommitID } = await run('git rev-parse HEAD', {
|
||||
cwd: resolve(__dirname),
|
||||
});
|
||||
const gitCommitURL = `https://gitdab.com/lifeguard/bot/commit/${gitCommitID}`;
|
||||
|
||||
// Get Node Version
|
||||
const { stdout: nodeVersion } = await run('node -v');
|
||||
|
||||
// Get Discord.js Version
|
||||
const { version: discordjsVersion } = require('discord.js/package.json');
|
||||
|
||||
// Get Lifeguard Version
|
||||
const {
|
||||
version: lifeguardVersion,
|
||||
} = require('@lifeguard/base/package.json');
|
||||
|
||||
const embed = new MessageEmbed()
|
||||
.setTitle('About Lifeguard')
|
||||
.addField(
|
||||
'Git Commit',
|
||||
`[${gitCommitID}](${gitCommitURL}) (https://gitdab.com/lifeguard/bot/)`
|
||||
)
|
||||
.addField('Node Version', nodeVersion)
|
||||
.addField('Discord.js Version', discordjsVersion)
|
||||
.addField('Lifeguard Version', lifeguardVersion)
|
||||
.setColor(0x7289da)
|
||||
.setFooter(
|
||||
`Executed By ${msg.author.tag}`,
|
||||
msg.author.avatarURL() ?? msg.author.defaultAvatarURL
|
||||
)
|
||||
.setTimestamp();
|
||||
msg.channel.send(embed);
|
||||
},
|
||||
{
|
||||
level: 0,
|
||||
usage: ['about'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,77 @@
|
|||
import { calcUserLevel } from '@assertions/userLevel';
|
||||
import { Command } from '@plugins/Command';
|
||||
import { Plugin } from '@plugins/Plugin';
|
||||
import { defaultEmbed } from '@util/DefaultEmbed';
|
||||
import { Collection, Guild, GuildMember } from 'discord.js';
|
||||
|
||||
function convertPlugins(
|
||||
plugins: Collection<string, Plugin>,
|
||||
member: GuildMember,
|
||||
guild: Guild
|
||||
) {
|
||||
return plugins
|
||||
.map((plugin, key) => ({
|
||||
name: key,
|
||||
cmds: [...plugin.values()]
|
||||
.filter(cmd => !cmd.options.hidden)
|
||||
.filter(cmd => calcUserLevel(member, guild) >= cmd.options.level)
|
||||
.map(cmd => cmd.name)
|
||||
.sort((a, b) => a.localeCompare(b)),
|
||||
}))
|
||||
.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
|
||||
export const command = new Command(
|
||||
'help',
|
||||
(lifeguard, msg, args) => {
|
||||
if (!args.length) {
|
||||
const plugins = convertPlugins(
|
||||
lifeguard.plugins,
|
||||
msg.member as GuildMember,
|
||||
msg.guild as Guild
|
||||
);
|
||||
|
||||
const embed = defaultEmbed()
|
||||
.setTitle('Lifeguard Help')
|
||||
.setFooter(
|
||||
`Executed By ${msg.author.tag}`,
|
||||
msg.author.avatarURL() ?? msg.author.defaultAvatarURL
|
||||
);
|
||||
|
||||
for (const plugin of plugins) {
|
||||
if (plugin.cmds.length > 0) {
|
||||
embed.addField(plugin.name, plugin.cmds.join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
msg.channel.send(embed);
|
||||
} else {
|
||||
const plugin = lifeguard.plugins.find(plugin => plugin.has(args[0]));
|
||||
const cmd = plugin?.get(args[0]);
|
||||
|
||||
if (cmd) {
|
||||
const embed = defaultEmbed()
|
||||
.setTitle(cmd.name)
|
||||
.setFooter(
|
||||
`Executed By ${msg.author.tag}`,
|
||||
msg.author.avatarURL() ?? msg.author.defaultAvatarURL
|
||||
);
|
||||
|
||||
const options = Object.entries(cmd.options);
|
||||
options.map(([key, val]) => {
|
||||
if (key === 'usage') {
|
||||
embed.addField(key, val.join('\n'));
|
||||
return;
|
||||
}
|
||||
embed.addField(key, `${val}`);
|
||||
});
|
||||
|
||||
msg.channel.send(embed);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 0,
|
||||
usage: ['help', 'help [name]'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,55 @@
|
|||
import { UserInfraction } from '@models/User';
|
||||
import { Command } from '@plugins/Command';
|
||||
import { parseUser } from '@util/parseUser';
|
||||
|
||||
export const command = new Command(
|
||||
'ban',
|
||||
async (lifeguard, msg, [uid, ...reason]) => {
|
||||
// Parse user id from mention
|
||||
const u = parseUser(uid);
|
||||
try {
|
||||
// Create Infraction
|
||||
const inf: UserInfraction = {
|
||||
action: 'Ban',
|
||||
active: true,
|
||||
guild: msg.guild?.id as string,
|
||||
id: (await lifeguard.db.users.findOne({ id: u }))?.infractions
|
||||
.length as number,
|
||||
moderator: msg.author.id,
|
||||
reason: reason.join(' '),
|
||||
time: new Date(),
|
||||
};
|
||||
|
||||
// Update User in Database
|
||||
await lifeguard.db.users.findOneAndUpdate(
|
||||
{ id: u },
|
||||
{ $push: { infractions: inf } },
|
||||
{ returnOriginal: false }
|
||||
);
|
||||
|
||||
// Get User
|
||||
const member = await msg.guild?.members.get(u);
|
||||
// Notify User about Action
|
||||
member?.send(
|
||||
`You have been banned from **${msg.guild?.name}** for \`${
|
||||
reason.length > 0 ? reason.join(' ') : 'No Reason Specified'
|
||||
}\``
|
||||
);
|
||||
// Ban User
|
||||
member?.ban({ reason: reason.join(' ') });
|
||||
|
||||
// Tell moderator action was successful
|
||||
msg.channel.send(
|
||||
`${member?.user.tag} was banned by ${msg.author.tag} for \`${
|
||||
reason.length > 0 ? reason.join(' ') : 'No Reason Specified'
|
||||
}\``
|
||||
);
|
||||
} catch (err) {
|
||||
msg.channel.send(err.message);
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['ban {user} [reason]'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,60 @@
|
|||
import { UserInfraction } from '@models/User';
|
||||
import { Command } from '@plugins/Command';
|
||||
import { parseUser } from '@util/parseUser';
|
||||
import { GuildMember, User } from 'discord.js';
|
||||
|
||||
export const command = new Command(
|
||||
'forceban',
|
||||
async (lifeguard, msg, [uid, ...reason]) => {
|
||||
// Parse user id from mention
|
||||
const u = parseUser(uid);
|
||||
try {
|
||||
// Create Infraction
|
||||
const inf: UserInfraction = {
|
||||
action: 'Ban',
|
||||
active: true,
|
||||
guild: msg.guild?.id as string,
|
||||
id: (await lifeguard.db.users.findOne({ id: u }))?.infractions
|
||||
.length as number,
|
||||
moderator: msg.author.id,
|
||||
reason: reason.join(' '),
|
||||
time: new Date(),
|
||||
};
|
||||
|
||||
// Update User in Database
|
||||
await lifeguard.db.users.findOneAndUpdate(
|
||||
{ id: u },
|
||||
{ $push: { infractions: inf } },
|
||||
{ returnOriginal: false }
|
||||
);
|
||||
|
||||
// Ban user from guild
|
||||
const member = await msg.guild?.members.ban(u, {
|
||||
reason: reason.join(' '),
|
||||
});
|
||||
|
||||
// Retreive user tag
|
||||
let tag;
|
||||
if (member instanceof GuildMember) {
|
||||
tag = member.user.tag;
|
||||
} else if (member instanceof User) {
|
||||
tag = member.tag;
|
||||
} else {
|
||||
tag = member;
|
||||
}
|
||||
|
||||
// Tell moderator ban was successful
|
||||
msg.channel.send(
|
||||
`${tag} was force-banned by ${msg.author.toString()} for \`${
|
||||
reason.length > 0 ? reason.join(' ') : 'No Reason Specified'
|
||||
}\``
|
||||
);
|
||||
} catch (err) {
|
||||
msg.channel.send(err.message);
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['forceban {user} [reason]'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,54 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { parseUser } from '@util/parseUser';
|
||||
import { MessageAttachment } from 'discord.js';
|
||||
|
||||
export const command = new Command(
|
||||
'infractions',
|
||||
async (lifeguard, msg, [cmd, ...args], dbUser) => {
|
||||
switch (cmd) {
|
||||
case 'archive':
|
||||
// Get users from Database that have infractions
|
||||
const dbUsers = lifeguard.db.users.find({
|
||||
infractions: { $elemMatch: { guild: msg.guild?.id } },
|
||||
});
|
||||
|
||||
const guildInfractions = (
|
||||
await dbUsers
|
||||
// Filter out infractions not from current guild
|
||||
.map(u => u.infractions.filter(inf => inf.guild === msg.guild?.id))
|
||||
.toArray()
|
||||
)
|
||||
// Flatten Array
|
||||
.reduce((acc, val) => acc.concat(val), []);
|
||||
|
||||
// Send archive as JSON file
|
||||
msg.channel.send(
|
||||
new MessageAttachment(
|
||||
Buffer.from(JSON.stringify(guildInfractions, null, 2)),
|
||||
`${msg.guild?.id}.infractions.json`
|
||||
)
|
||||
);
|
||||
break;
|
||||
|
||||
case 'info':
|
||||
const [user, infID] = args;
|
||||
const u = parseUser(user);
|
||||
const dbUser = await lifeguard.db.users.findOne({ id: u });
|
||||
const inf = dbUser?.infractions.find(inf => inf.id === +infID);
|
||||
msg.channel.send(`\`\`\`json\n${JSON.stringify(inf, null, 2)}\n\`\`\``);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: [
|
||||
'infractions archive',
|
||||
'infractions search {user}',
|
||||
'infractions info {user} {id}',
|
||||
],
|
||||
alias: ['inf'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,55 @@
|
|||
import { UserInfraction } from '@models/User';
|
||||
import { Command } from '@plugins/Command';
|
||||
import { parseUser } from '@util/parseUser';
|
||||
|
||||
export const command = new Command(
|
||||
'kick',
|
||||
async (lifeguard, msg, [uid, ...reason]) => {
|
||||
// Parse user id from mention
|
||||
const u = parseUser(uid);
|
||||
try {
|
||||
// Create Infraction
|
||||
const inf: UserInfraction = {
|
||||
action: 'Kick',
|
||||
active: true,
|
||||
guild: msg.guild?.id as string,
|
||||
id: (await lifeguard.db.users.findOne({ id: u }))?.infractions
|
||||
.length as number,
|
||||
moderator: msg.author.id,
|
||||
reason: reason.join(' '),
|
||||
time: new Date(),
|
||||
};
|
||||
|
||||
// Update User in Database
|
||||
await lifeguard.db.users.findOneAndUpdate(
|
||||
{ id: u },
|
||||
{ $push: { infractions: inf } },
|
||||
{ returnOriginal: false }
|
||||
);
|
||||
|
||||
// Get User
|
||||
const member = await msg.guild?.members.get(u);
|
||||
// Notify User about Action
|
||||
member?.send(
|
||||
`You have been kicked from **${msg.guild?.name}** for \`${reason.join(
|
||||
' '
|
||||
)}\``
|
||||
);
|
||||
// Ban the User
|
||||
member?.kick(reason.join(' '));
|
||||
|
||||
// Tell moderator action was successful
|
||||
msg.channel.send(
|
||||
`${member?.user.tag} was kicked by ${msg.author.tag} for \`${
|
||||
reason.length > 0 ? reason.join(' ') : 'No Reason Specified'
|
||||
}\``
|
||||
);
|
||||
} catch (err) {
|
||||
msg.channel.send(err.message);
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['kick {user} [reason]'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,20 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { command as ban } from '@plugins/moderation/ban';
|
||||
|
||||
export const command = new Command(
|
||||
'mban',
|
||||
async (lifeguard, msg, args) => {
|
||||
// Find where '-r' is in the args
|
||||
const reasonFlagIndex = args.indexOf('-r') || args.length;
|
||||
// Get users from args
|
||||
const users = args.slice(0, reasonFlagIndex);
|
||||
// Get reason from args
|
||||
const reason = args.slice(reasonFlagIndex + 1).join(' ');
|
||||
// Run ban command for each user
|
||||
users.forEach(user => ban.func(lifeguard, msg, [user, reason]));
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['mban {users} -r [reason]'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,20 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { command as kick } from '@plugins/moderation/kick';
|
||||
|
||||
export const command = new Command(
|
||||
'mkick',
|
||||
async (lifeguard, msg, args) => {
|
||||
// Find where '-r' is in the args
|
||||
const reasonFlagIndex = args.indexOf('-r');
|
||||
// Get users from args
|
||||
const uids = args.slice(0, reasonFlagIndex);
|
||||
// Get reason from args
|
||||
const reason = args.slice(reasonFlagIndex + 1).join(' ');
|
||||
// Run ban command for each user
|
||||
uids.forEach(uid => kick.func(lifeguard, msg, [uid, reason]));
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['mkick {users} -r [reason]'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,59 @@
|
|||
import { UserInfraction } from '@models/User';
|
||||
import { Command } from '@plugins/Command';
|
||||
import { defaultEmbed } from '@util/DefaultEmbed';
|
||||
import { parseUser } from '@util/parseUser';
|
||||
|
||||
export const command = new Command(
|
||||
'mute',
|
||||
async (lifeguard, msg, [uid, ...reason]) => {
|
||||
// Get guild from db
|
||||
const guild = await lifeguard.db.guilds.findOne({ id: msg.guild?.id });
|
||||
// Check if muted role exists
|
||||
if (guild?.config.roles?.muted) {
|
||||
// Parse user id from mention
|
||||
const u = parseUser(uid);
|
||||
try {
|
||||
// Create Infracrion
|
||||
const inf: UserInfraction = {
|
||||
action: 'Mute',
|
||||
active: true,
|
||||
guild: msg.guild?.id as string,
|
||||
id: (await lifeguard.db.users.findOne({ id: u }))?.infractions
|
||||
.length as number,
|
||||
moderator: msg.author.id,
|
||||
reason: reason.join(' '),
|
||||
time: new Date(),
|
||||
};
|
||||
// Update User in Database
|
||||
await lifeguard.db.users.findOneAndUpdate(
|
||||
{ id: u },
|
||||
{ $push: { infractions: inf } },
|
||||
{ returnOriginal: false }
|
||||
);
|
||||
|
||||
// Get User
|
||||
const member = msg.guild?.members.get(u);
|
||||
// Add role to user
|
||||
await member?.roles.add(guild.config.roles.muted);
|
||||
|
||||
// Tell moderator action was successfull
|
||||
msg.channel.send(
|
||||
`${member?.user.tag} was muted by ${msg.author.tag} for \`${
|
||||
reason.length > 0 ? reason.join(' ') : 'No Reason Specified'
|
||||
}\``
|
||||
);
|
||||
} catch (err) {
|
||||
msg.channel.send(err.message);
|
||||
}
|
||||
} else {
|
||||
const embed = defaultEmbed()
|
||||
.setTitle(':rotating_light: Error! :rotating_light:')
|
||||
.setDescription('No mute role configured!');
|
||||
msg.channel.send(embed);
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['mute {user} [reason]'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,57 @@
|
|||
import { UserInfraction } from '@models/User';
|
||||
import { Command } from '@plugins/Command';
|
||||
import { parseUser } from '@util/parseUser';
|
||||
|
||||
export const command = new Command(
|
||||
'softban',
|
||||
async (lifeguard, msg, [uid, ...reason]) => {
|
||||
// Parse user id from mention
|
||||
const u = parseUser(uid);
|
||||
try {
|
||||
// Create Infraction
|
||||
const inf: UserInfraction = {
|
||||
action: 'Ban',
|
||||
active: true,
|
||||
guild: msg.guild?.id as string,
|
||||
id: (await lifeguard.db.users.findOne({ id: u }))?.infractions
|
||||
.length as number,
|
||||
moderator: msg.author.id,
|
||||
reason: reason.join(' '),
|
||||
time: new Date(),
|
||||
};
|
||||
|
||||
// Update User in Database
|
||||
await lifeguard.db.users.findOneAndUpdate(
|
||||
{ id: u },
|
||||
{ $push: { infractions: inf } },
|
||||
{ returnOriginal: false }
|
||||
);
|
||||
|
||||
// Get User
|
||||
const member = await msg.guild?.members.get(u);
|
||||
// Notify user of action
|
||||
member?.send(
|
||||
`You have been soft-banned from **${msg.guild?.name}** for \`${
|
||||
reason.length > 0 ? reason.join(' ') : 'No Reason Specified'
|
||||
}`
|
||||
);
|
||||
// Ban User
|
||||
member?.ban({ reason: reason.join(' '), days: 7 });
|
||||
// Unban User
|
||||
await msg.guild?.members.unban(u, reason.join(' '));
|
||||
|
||||
// Tell moderator action was successfull
|
||||
msg.channel.send(
|
||||
`${member?.user.tag} was soft-banned by ${msg.author.tag} for \`${
|
||||
reason.length > 0 ? reason.join(' ') : 'No Reason Specified'
|
||||
}`
|
||||
);
|
||||
} catch (err) {
|
||||
msg.channel.send(err.message);
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['softban {user} [reason]'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,39 @@
|
|||
import { Command } from '@plugins/Command';
|
||||
import { defaultEmbed } from '@util/DefaultEmbed';
|
||||
import { parseUser } from '@util/parseUser';
|
||||
|
||||
export const command = new Command(
|
||||
'unmute',
|
||||
async (lifeguard, msg, [uid, ...reason]) => {
|
||||
// Get guild from database
|
||||
const guild = await lifeguard.db.guilds.findOne({ id: msg.guild?.id });
|
||||
// Check if muted role exists
|
||||
if (guild?.config.roles?.muted) {
|
||||
// Parse user id from mention
|
||||
const u = parseUser(uid);
|
||||
try {
|
||||
// Get User
|
||||
const member = msg.guild?.members.get(u);
|
||||
// Remove Role
|
||||
await member?.roles.remove(guild.config.roles.muted);
|
||||
// Tell moderator action was successfull
|
||||
msg.channel.send(
|
||||
`${member?.user.tag} was unmuted by ${msg.author.tag} for \`${
|
||||
reason.length > 0 ? reason.join(' ') : 'No Reason Specified'
|
||||
}\``
|
||||
);
|
||||
} catch (err) {
|
||||
msg.channel.send(err.message);
|
||||
}
|
||||
} else {
|
||||
const embed = defaultEmbed()
|
||||
.setTitle(':rotating_light: Error! :rotating_light:')
|
||||
.setDescription('No mute role configured!');
|
||||
msg.channel.send(embed);
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['unmute {user} [reason]'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,47 @@
|
|||
import { UserInfraction } from '@models/User';
|
||||
import { Command } from '@plugins/Command';
|
||||
import { parseUser } from '@util/parseUser';
|
||||
|
||||
export const command = new Command(
|
||||
'warn',
|
||||
async (lifeguard, msg, [uid, ...reason]) => {
|
||||
// Parse user id from mention
|
||||
const u = parseUser(uid);
|
||||
try {
|
||||
// Create Infracion
|
||||
const inf: UserInfraction = {
|
||||
action: 'Warn',
|
||||
active: true,
|
||||
guild: msg.guild?.id as string,
|
||||
id: (await lifeguard.db.users.findOne({ id: u }))?.infractions
|
||||
.length as number,
|
||||
moderator: msg.author.id,
|
||||
reason: reason.join(' '),
|
||||
time: new Date(),
|
||||
};
|
||||
|
||||
// Update User in Database
|
||||
await lifeguard.db.users.findOneAndUpdate(
|
||||
{ id: u },
|
||||
{ $push: { infractions: inf } },
|
||||
{ returnOriginal: false }
|
||||
);
|
||||
|
||||
// Get User
|
||||
const member = msg.guild?.members.get(u);
|
||||
|
||||
// Tell moderator action was sucessfull
|
||||
msg.channel.send(
|
||||
`${member?.user.tag} was warned by ${msg.author.tag} for \`${
|
||||
reason.length > 0 ? reason.join(' ') : 'No Reason Specified'
|
||||
}\``
|
||||
);
|
||||
} catch (err) {
|
||||
msg.channel.send(err.message);
|
||||
}
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
usage: ['warn {user} [reason]'],
|
||||
}
|
||||
);
|
|
@ -0,0 +1,36 @@
|
|||
import { promisify } from 'util';
|
||||
import { readdir, lstat } from 'fs';
|
||||
import { Collection } from 'discord.js';
|
||||
import { Plugin } from './Plugin';
|
||||
import { Command } from './Command';
|
||||
|
||||
export async function PluginLoader() {
|
||||
const readDir = promisify(readdir);
|
||||
const stats = promisify(lstat);
|
||||
|
||||
const plugins = new Collection<string, Plugin>();
|
||||
|
||||
const pluginDir = './build/src/plugins';
|
||||
const folders = await readDir(pluginDir);
|
||||
for await (const folder of folders) {
|
||||
const folderDir = `${pluginDir}/${folder}`;
|
||||
const info = await stats(folderDir);
|
||||
if (info.isDirectory()) {
|
||||
const plugin = new Plugin();
|
||||
|
||||
const files = await readDir(`${folderDir}`);
|
||||
for await (const file of files) {
|
||||
if (file.endsWith('.js')) {
|
||||
const command = require(`./${folder}/${file}`).command;
|
||||
if (command instanceof Command) {
|
||||
plugin.set(command.name, command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
plugins.set(folder, plugin);
|
||||
}
|
||||
}
|
||||
|
||||
return plugins;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import { Structures, Guild, GuildMember } from 'discord.js';
|
||||
import { PluginClient } from 'PluginClient';
|
||||
import { inspect } from 'util';
|
||||
|
||||
Structures.extend('GuildMember', guildMember => {
|
||||
return class extends guildMember {
|
||||
_client: PluginClient;
|
||||
constructor(client: PluginClient, data: object, guild: Guild) {
|
||||
super(client, data, guild);
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
get db() {
|
||||
return this._client.db.users.findOne({ id: this.user.id });
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export class GuildMemberStructure extends GuildMember {
|
||||
_client: PluginClient;
|
||||
constructor(client: PluginClient, data: object, guild: Guild) {
|
||||
super(client, data, guild);
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
get db() {
|
||||
return this._client.db.users.findOne({ id: this.user.id });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import { Structures, Guild } from 'discord.js';
|
||||
import { PluginClient } from 'PluginClient';
|
||||
import { inspect } from 'util';
|
||||
|
||||
Structures.extend('Guild', guildClass => {
|
||||
return class extends guildClass {
|
||||
_client: PluginClient;
|
||||
constructor(client: PluginClient, data: object) {
|
||||
super(client, data);
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
get db() {
|
||||
return this._client.db.guilds.findOne({ id: this.id });
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
export class GuildStructure extends Guild {
|
||||
_client: PluginClient;
|
||||
constructor(client: PluginClient, data: object) {
|
||||
super(client, data);
|
||||
this._client = client;
|
||||
}
|
||||
|
||||
get db() {
|
||||
return this._client.db.guilds.findOne({ id: this.id });
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import { readdirSync } from 'fs';
|
||||
|
||||
export function StructureLoader() {
|
||||
const structureFiles = readdirSync('./build/src/structures');
|
||||
|
||||
for (const file of structureFiles) {
|
||||
if (file !== 'structureLoader.js' && file.endsWith('js')) {
|
||||
require(`./${file}`);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { connect, Db, MongoClientOptions, Collection } from 'mongodb';
|
||||
import { User } from '../models/User';
|
||||
import { Guild } from '../models/Guild';
|
||||
|
||||
interface DatabaseConfig {
|
||||
url: string;
|
||||
name: string;
|
||||
MongoOptions?: MongoClientOptions;
|
||||
}
|
||||
|
||||
export class Database {
|
||||
db!: Db;
|
||||
constructor(protected config: DatabaseConfig) {}
|
||||
|
||||
async connect() {
|
||||
const client = await connect(
|
||||
this.config.url,
|
||||
this.config.MongoOptions
|
||||
).catch(err => {
|
||||
throw err;
|
||||
});
|
||||
this.db = client.db(this.config.name);
|
||||
}
|
||||
|
||||
get guilds(): Collection<Guild> {
|
||||
return this.db.collection('guilds');
|
||||
}
|
||||
|
||||
get users(): Collection<User> {
|
||||
return this.db.collection('users');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import { MessageEmbed } from 'discord.js';
|
||||
|
||||
export function defaultEmbed() {
|
||||
return new MessageEmbed().setColor(0x7289da).setTimestamp();
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
export function parseUser(user: string) {
|
||||
if (user.startsWith("<@") && user.endsWith(">")) {
|
||||
user = user.slice(2, -1);
|
||||
|
||||
if (user.startsWith("!")) {
|
||||
user = user.slice(1);
|
||||
}
|
||||
|
||||
return user;
|
||||
} else {
|
||||
return user;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"extends": "./node_modules/gts/tsconfig-google.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"outDir": "build",
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
"@lifeguard/*": ["./*"],
|
||||
"@lifegaurd/base/*": ["../*"],
|
||||
"@assertions/*": ["./assertions/*"],
|
||||
"@config/*": ["./config/*"],
|
||||
"@events/*": ["./events/*"],
|
||||
"@models/*": ["./models/*"],
|
||||
"@plugins/*": ["./plugins/*"],
|
||||
"@structures/*": ["./structures/*"],
|
||||
"@util/*": ["./util/*"]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "test/**/*.ts"]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": "gts/tslint.json",
|
||||
"linterOptions": {
|
||||
"exclude": [
|
||||
"**/*.json"
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue