push time

This commit is contained in:
Rauf 2020-02-01 18:23:36 -05:00
parent 07029ffc52
commit c7b55b1aae
41 changed files with 1150 additions and 96 deletions

157
package-lock.json generated
View File

@ -232,6 +232,21 @@
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
"dev": true
},
"base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
"dev": true
},
"bl": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz",
"integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==",
"requires": {
"readable-stream": "^2.3.5",
"safe-buffer": "^5.1.1"
}
},
"boxen": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/boxen/-/boxen-3.2.0.tgz",
@ -346,9 +361,24 @@
}
},
"bson": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.3.tgz",
"integrity": "sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg=="
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/bson/-/bson-4.0.3.tgz",
"integrity": "sha512-7uBjjxwOSuGLmoqGI1UXWpDGc0K2WjR7dC6iaOg4iriNZo6M2EEBb8co4dEPJ5ArYCebPMie0ecgX0TWF+ZUrQ==",
"dev": true,
"requires": {
"buffer": "^5.1.0",
"long": "^4.0.0"
}
},
"buffer": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz",
"integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==",
"dev": true,
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
},
"builtin-modules": {
"version": "1.1.1",
@ -519,6 +549,11 @@
}
}
},
"core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@ -595,6 +630,11 @@
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
},
"denque": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
},
"diff": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz",
@ -602,7 +642,7 @@
"dev": true
},
"discord.js": {
"version": "github:discordjs/discord.js#ee0b7c155a1767ed42e2756e53d56368e8b69929",
"version": "github:discordjs/discord.js#030d263a9e018a766c5f399624fe4f8ec2b7349c",
"from": "github:discordjs/discord.js",
"requires": {
"@discordjs/collection": "^0.1.1",
@ -854,6 +894,12 @@
"safer-buffer": ">= 2.1.2 < 3"
}
},
"ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==",
"dev": true
},
"import-lazy": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
@ -885,8 +931,7 @@
"inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
},
"ini": {
"version": "1.3.5",
@ -1049,6 +1094,11 @@
"integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==",
"dev": true
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@ -1129,6 +1179,12 @@
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
"dev": true
},
"long": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==",
"dev": true
},
"loud-rejection": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
@ -1252,15 +1308,29 @@
"minimist": "0.0.8"
}
},
"module-alias": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz",
"integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q=="
},
"mongodb": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.4.1.tgz",
"integrity": "sha512-juqt5/Z42J4DcE7tG7UdVaTKmUC6zinF4yioPfpeOSNBieWSK6qCY+0tfGQcHLKrauWPDdMZVROHJOa8q2pWsA==",
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.2.tgz",
"integrity": "sha512-Lxt4th2tK2MxmkDBR5cMik+xEnkvhwg0BC5kGcHm9RBwaNEsrIryvV5istGXOHbnif5KslMpY1FbX6YbGJ/Trg==",
"requires": {
"bl": "^2.2.0",
"bson": "^1.1.1",
"denque": "^1.4.1",
"require_optional": "^1.0.1",
"safe-buffer": "^5.1.2",
"saslprep": "^1.0.0"
},
"dependencies": {
"bson": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/bson/-/bson-1.1.3.tgz",
"integrity": "sha512-TdiJxMVnodVS7r0BdL42y/pqC9cL2iKynVwA0Ho3qbsQYr428veL3l7BQyuqiw+Q5SqqoT0m4srSY/BlZ9AxXg=="
}
}
},
"mute-stream": {
@ -1455,9 +1525,14 @@
"dev": true
},
"prism-media": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.1.0.tgz",
"integrity": "sha512-W+oxjRyjtd7hw3pefNZuc7YEZ6VICORJvVNfCPs0+7CsJ43CqMjGAYGjPL3hQ82vw03EVra+CiX4zisqOBUUGw=="
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.2.0.tgz",
"integrity": "sha512-zjcO/BLVlfxWqFpEUlDyL1R9XXMquasNP4xpeYDPPZi/Zcz0i6OXoqcvxOLgbRVPsJXVd29vlYmRx2bts+hzEw=="
},
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
"pseudomap": {
"version": "1.0.2",
@ -1522,6 +1597,27 @@
"read-pkg": "^3.0.0"
}
},
"readable-stream": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz",
"integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==",
"requires": {
"core-util-is": "~1.0.0",
"inherits": "~2.0.3",
"isarray": "~1.0.0",
"process-nextick-args": "~2.0.0",
"safe-buffer": "~5.1.1",
"string_decoder": "~1.1.1",
"util-deprecate": "~1.0.1"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"redent": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-2.0.0.tgz",
@ -1749,6 +1845,21 @@
}
}
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
},
"dependencies": {
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}
}
},
"strip-ansi": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz",
@ -1930,9 +2041,9 @@
}
},
"tweetnacl": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz",
"integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A=="
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.2.tgz",
"integrity": "sha512-+8aPRjmXgf1VqvyxSlBUzKzeYqVS9Ai8vZ28g+mL7dNQl1jlUTCMDZnvNQdAS1xTywMkIXwJsfipsR/6s2+syw=="
},
"type-fest": {
"version": "0.8.1",
@ -1950,11 +2061,16 @@
}
},
"typescript": {
"version": "3.7.4",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.4.tgz",
"integrity": "sha512-A25xv5XCtarLwXpcDNZzCGvW2D1S3/bACratYBx2sax8PefsFhlYmkQicKHvpYflFS8if4zne5zT5kpJ7pzuvw==",
"version": "3.7.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
"dev": true
},
"typy": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/typy/-/typy-3.3.0.tgz",
"integrity": "sha512-Du53deMF9X9pSM3gVXDjLBq14BUfZWSGKfmmR1kTlg953RaIZehfc8fQuoAiW+SRO6bJsP+59mv1tsH8vwKghg=="
},
"unique-string": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
@ -2045,6 +2161,11 @@
"prepend-http": "^2.0.0"
}
},
"util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",

View File

@ -7,10 +7,12 @@
"author": "Rauf",
"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.0",
"@types/node": "^10.0.3",
"@types/ws": "^6.0.4"
"typescript": "^3.7.5"
},
"scripts": {
"check": "gts check",
@ -25,6 +27,19 @@
"dependencies": {
"@types/mongodb": "^3.3.14",
"discord.js": "github:discordjs/discord.js",
"mongodb": "^3.4.1"
"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"
}
}

View File

@ -1,7 +1,7 @@
import { name, url } from '@config/mongodb';
import { Plugin } from '@plugins/Plugin';
import { Database } from '@util/Database';
import { Client, ClientOptions, Collection } from 'discord.js';
import { Plugin } from './plugins/Plugin';
import { Database } from './util/Database';
import { name, url } from './config/mongodb';
export class PluginClient extends Client {
plugins!: Collection<string, Plugin>;

View File

@ -1,5 +1,5 @@
import { GuildMember, Guild } from 'discord.js';
import { developers } from '../config/bot';
import { developers } from '@config/bot';
export function calcUserLevel(user: GuildMember, guild: Guild) {
if (developers.includes(user.id)) {

View File

@ -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);
}
}
);

View File

@ -1,7 +1,7 @@
import { Event } from '@events/Event';
import { PluginClient } from '@lifeguard/PluginClient';
import { readdir } from 'fs';
import { promisify } from 'util';
import { Event } from './Event';
import { PluginClient } from '../PluginClient';
const readDir = promisify(readdir);

View File

@ -1,13 +1,18 @@
import { Event } from './Event';
import { Event } from '@events/Event';
import { Guild } from 'discord.js';
export const event = new Event('guildCreate', lifeguard => {
if (lifeguard.user) {
lifeguard.user.setPresence({
activity: {
name: `${lifeguard.users.size} people in the pool`,
type: 'WATCHING',
},
status: 'online',
});
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',
});
}
}
});
);

View File

@ -1,9 +1,9 @@
import { Event } from './Event';
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';
import { prefix } from '../config/bot';
import { PluginClient } from '../PluginClient';
import { calcUserLevel } from '../assertions/userLevel';
import { UserDoc } from '../models/User';
function parseContent(content: string) {
const split = content.split(' ');
@ -14,8 +14,15 @@ function parseContent(content: string) {
function getCommandFromPlugin(lifeguard: PluginClient, cmdName: string) {
const plugin = lifeguard.plugins.find(p => p.has(cmdName));
const command = plugin?.get(cmdName);
return command;
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(

View File

@ -1,20 +1,42 @@
import { Event } from './Event';
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';
import { prefix } from '../config/bot';
import { User, UserDoc } from '../models/User';
export const event = new Event('message', async (lifeguard, msg: Message) => {
let dbUser = (await lifeguard.db.users.findOne({
let dbUser = await lifeguard.db.users.findOne({
id: msg.author.id,
})) as UserDoc;
});
if (!dbUser) {
await lifeguard.db.users.insertOne(
new User({ id: msg.author.id, infractions: [] })
);
dbUser = (await lifeguard.db.users.findOne({
await lifeguard.db.users.insertOne(new User({ id: msg.author.id }));
dbUser = await lifeguard.db.users.findOne({
id: msg.author.id,
})) as UserDoc;
});
}
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);
}

View File

@ -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);
}
}
);

View File

@ -1,4 +1,4 @@
import { Event } from './Event';
import { Event } from '@events/Event';
export const event = new Event('ready', lifeguard => {
console.log('Connected to Discord');

View File

@ -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 } }
);
}
}
}
}
);

View File

@ -1,7 +1,12 @@
import { token } from './config/bot';
import { EventLoader } from './events/eventLoader';
import { PluginLoader } from './plugins/pluginLoader';
import { PluginClient } from './PluginClient';
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();

52
src/models/Guild.ts Normal file
View File

@ -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;
}
}

View File

@ -1,5 +1,8 @@
interface UserInfraction {
action: 'Warn' | 'Mute' | 'Tempban' | 'Ban';
import { ObjectId } from 'bson';
import { PermissionOverwriteOption } from 'discord.js';
export interface UserInfraction {
action: 'Warn' | 'Kick' | 'Mute' | 'Ban';
active: boolean;
guild: string;
id: number;
@ -8,19 +11,68 @@ interface UserInfraction {
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[];
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.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: '',
};
}
}

View File

@ -1,12 +1,12 @@
import { Message, PermissionString } from 'discord.js';
import { PluginClient } from '../PluginClient';
import { UserDoc } from '../models/User';
import { PluginClient } from '@lifeguard/PluginClient';
import { UserDoc } from '@models/User';
type CommandFunction = (
lifeguard: PluginClient,
msg: Message,
args: string[],
dbUser: UserDoc
dbUser?: UserDoc
) => void;
interface CommandOptions {

View File

@ -1,5 +1,5 @@
import { Collection } from 'discord.js';
import { Command } from './Command';
import { Command } from '@plugins/Command';
export class Plugin extends Collection<string, Command> {
constructor() {

View File

@ -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,
}
);

View File

@ -1,5 +1,5 @@
import { Command } from '../Command';
import { parseUser } from '../../util/parseUser';
import { Command } from '@plugins/Command';
import { parseUser } from '@util/parseUser';
export const command = new Command(
'role',

View File

@ -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'],
}
);

View File

@ -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'],
}
);

View File

@ -1,5 +1,5 @@
import { Command } from '../Command';
import { defaultEmbed } from '../../util/DefaultEmbed';
import { Command } from '@plugins/Command';
import { defaultEmbed } from '@util/DefaultEmbed';
export const command = new Command(
'ping',

View File

@ -1,7 +1,7 @@
import { Command } from '../Command';
import { Command } from '@plugins/Command';
import { defaultEmbed } from '@util/DefaultEmbed';
import { inspect } from 'util';
import { runInNewContext } from 'vm';
import { defaultEmbed } from '../../util/DefaultEmbed';
function parseBlock(script: string) {
const cbr = /^(([ \t]*`{3,4})([^\n]*)([\s\S]+?)(^[ \t]*\2))/gm;

View File

@ -1,5 +1,5 @@
import { Command } from '../Command';
import { parseUser } from '../../util/parseUser';
import { Command } from '@plugins/Command';
import { parseUser } from '@util/parseUser';
export const command = new Command(
'blacklist',

View File

@ -1,8 +1,8 @@
import { Command } from '../Command';
import { promisify } from 'util';
import { Command } from '@plugins/Command';
import { exec } from 'child_process';
import { resolve } from 'path';
import { MessageEmbed } from 'discord.js';
import { resolve } from 'path';
import { promisify } from 'util';
export const command = new Command(
'about',
@ -23,7 +23,9 @@ export const command = new Command(
const { version: discordjsVersion } = require('discord.js/package.json');
// Get Lifeguard Version
const { version: lifeguardVersion } = require('../../../../package.json');
const {
version: lifeguardVersion,
} = require('@lifeguard/base/package.json');
const embed = new MessageEmbed()
.setTitle('About Lifeguard')

View File

@ -1,8 +1,8 @@
import { Command } from '../Command';
import { MessageEmbed, Collection, GuildMember, Guild } from 'discord.js';
import { Plugin } from '../Plugin';
import { calcUserLevel } from '../../assertions/userLevel';
import { defaultEmbed } from '../../util/DefaultEmbed';
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>,
@ -39,8 +39,6 @@ export const command = new Command(
);
for (const plugin of plugins) {
// console.log(plugin)
// embed.addField(plugin.name, plugin.cmds.join('\n'));
if (plugin.cmds.length > 0) {
embed.addField(plugin.name, plugin.cmds.join('\n'));
}

View File

@ -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]'],
}
);

View File

@ -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]'],
}
);

View File

@ -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'],
}
);

View File

@ -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]'],
}
);

View File

@ -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]'],
}
);

View File

@ -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]'],
}
);

View File

@ -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]'],
}
);

View File

@ -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]'],
}
);

View File

@ -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]'],
}
);

View File

@ -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]'],
}
);

View File

@ -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 });
}
}

View File

@ -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 });
}
}

View File

@ -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}`);
}
}
}

View File

@ -1,4 +1,6 @@
import { connect, Db, MongoClientOptions } from 'mongodb';
import { connect, Db, MongoClientOptions, Collection } from 'mongodb';
import { User } from '../models/User';
import { Guild } from '../models/Guild';
interface DatabaseConfig {
url: string;
@ -20,11 +22,11 @@ export class Database {
this.db = client.db(this.config.name);
}
get guilds() {
get guilds(): Collection<Guild> {
return this.db.collection('guilds');
}
get users() {
get users(): Collection<User> {
return this.db.collection('users');
}
}

View File

@ -2,10 +2,19 @@
"extends": "./node_modules/gts/tsconfig-google.json",
"compilerOptions": {
"rootDir": ".",
"outDir": "build"
"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"
]
"include": ["src/**/*.ts", "test/**/*.ts"]
}