diff --git a/.gitignore b/.gitignore index df93a76..cc1ceca 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ web/ src/tmp node_modules/ src/application.yml -.vscode/ \ No newline at end of file +.vscode/ +src/assets/util/ \ No newline at end of file diff --git a/TODO.md b/TODO.md index 761afe0..d1a52e5 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,6 @@ - [x] = In progress - [ ] Global - - [ ] Clean code / refractor - [ ] Clean locales - [ ] Information - [ ] Commands @@ -11,15 +10,9 @@ - [x] Utility - [x] Commands - [x] Tesseract (Donator) -- [x] Tags - - [x] Commands - - [x] Use - - [x] List +- [ ] Tags + - [ ] Commands - [ ] Edit - - [x] Info - - [x] Create - - [ ] Delete - - [x] Source - [ ] Transfer - [ ] Moderation - [ ] Logging @@ -41,7 +34,16 @@ - [ ] Infractions ## Finished (for now): +- [x] Tags + - [x] Commands + - [x] Use + - [x] List + - [x] Info + - [x] Create + - [x] Delete + - [x] Source - [x] Global + - [x] Clean code / refractor - [x] Commands - [x] user lookup - [x] guild lookup \ No newline at end of file diff --git a/src/assets/config/colors.yml b/src/assets/config/colors.yml index 9cac6ae..3fe2e32 100644 --- a/src/assets/config/colors.yml +++ b/src/assets/config/colors.yml @@ -2,6 +2,10 @@ error : 0xff0000 # Red cooldown: 0x7289DA # Blurple votelock: 0xF7AE69 +logs: + message: + delete: 0x7289DA # Blurple + core : locale: 0x7289DA # Blurple prefix: 0x7289DA # Blurple diff --git a/src/assets/config/emotes.yml b/src/assets/config/emotes.yml index 0c0ab6e..fa3aaae 100644 --- a/src/assets/config/emotes.yml +++ b/src/assets/config/emotes.yml @@ -5,6 +5,21 @@ cooldown : <:stopwatch:542477522147868672> # On cooldown owner_only: <:exclamation:542368036255039509> # Only owners guild_only: <:exclamation:542368036255039509> # Only guilds +logs: + ready: <:spacewump:542723940997529641> + + message_delete: + 0 : <:sad:500719642403405824> + 1 : <:exclamation:542368036255039509> + 2 : <:stopwatch:542477522147868672> + 3 : + 4 : <:alert:542367222279176202> + 5 : <:alert:542367222279176202> + + shard: + ready: '<:create_webhook:542716503674454016>' + resume: '<:update_webhook:542716504089690124>' + disconnect: '<:remove_webhook:542716503624253461>' core : locale: @@ -65,14 +80,6 @@ tags: 1 : <:ok_hand:497396122416054287> 2 : <:wumpus_love:506200781549076482> -logs : - ready: '<:spacewump:542723940997529641>' - - shard : - ready : '<:create_webhook:542716503674454016>' - resume : '<:update_webhook:542716504089690124>' - disconnect: '<:remove_webhook:542716503624253461>' - util : ping : 0 : <:bored:500719642164199425> # Pinging diff --git a/src/assets/i18n/en_us/badwords.yml b/src/assets/i18n/en_us/badwords.yml new file mode 100644 index 0000000..bdd3ec8 --- /dev/null +++ b/src/assets/i18n/en_us/badwords.yml @@ -0,0 +1,45 @@ +# This is an array of inappropriate language, please delete or add any inappropriate language that exist in your country +# (All should be lower case) +# for example, dutch: +# +# [English] +# - anal +# - dick +# - twat +# +# [Dutch] +# - anaal +# - penis + +- anal +- anus +- ballsack +- blowjob +- blow job +- boner +- clitoris +- cock +- cunt +- dick +- dildo +- dyke +- fag +- fuck +- jizz +- labia +- muff +- nigger +- nigga +- penis +- piss +- pussy +- scrotum +- sex +- shit +- slut +- smegma +- spunk +- twat +- vagina +- wank +- whore \ No newline at end of file diff --git a/src/assets/i18n/en_us/logs.yml b/src/assets/i18n/en_us/logs.yml new file mode 100644 index 0000000..d3fbcb0 --- /dev/null +++ b/src/assets/i18n/en_us/logs.yml @@ -0,0 +1,22 @@ +translation_code: 'en_us' + +message: + delete: '`[{date@now}]` {emoji} A message by {e.user!s} (`{e.user.id}`) was deleted from **{e.channel!s}**:' + +member: + add: + main: '`[{date@now}]` {emoji} {e.user!s} (`{e.user.id}`) joined, user created at **{e.user.created@exact}** **(**{e.user.created@precise} ago**)**' + notes: + - 'Username **may** contain inappropriate language' + - 'User created less than a day ago' + - 'User has no custom avatar' + - 'User is banned from discord.services ({e.ban.reason})' + - 'User is banned from ksoft.si ({e.ban.reason})' + +connection: + ready: '`[{date@now}] / {process.hash}]` {emoji} Master client on Wump (`v{global.version}`) connected' + + shard : + ready : '`[{date@now}] / {process.hash}]` {emoji} Shard `#{e.shard.id}` connected to Wump (`v{global.version}`)' + resume : '`[{date@now}] / {process.hash}]` {emoji} Shard `#{e.shard.id}` resumed to Wump (`v{global.version}`)' + disconnect: '`[{date@now}] / {process.hash}]` {emoji} Shard `#{e.shard.id}` disconnected from Wump (`v{global.version}`)' \ No newline at end of file diff --git a/src/assets/i18n/en_us/logs_simple.yml b/src/assets/i18n/en_us/logs_simple.yml deleted file mode 100644 index 8323573..0000000 --- a/src/assets/i18n/en_us/logs_simple.yml +++ /dev/null @@ -1,6 +0,0 @@ -connection: - ready : '`[$[date:now] / $[process:hash]]` $[emoji#0] Master client on Wump (`v$[wump:version]`) connected' - shard : - ready : '`[$[date:now] / $[process:hash]]` $[emoji#0] Shard `#$[shard:id]` connected to Wump (`v$[wump:version]`)' - resume : '`[$[date:now] / $[process:hash]]` $[emoji#0] Shard `#$[shard:id]` resumed to Wump (`v$[wump:version]`)' - disconnect: '`[$[date:now] / $[process:hash]]` $[emoji#0] Shard `#$[shard:id]` disconnected from Wump (`v$[wump:version]`)' \ No newline at end of file diff --git a/src/assets/i18n/en_us/strings.yml b/src/assets/i18n/en_us/strings.yml index de25622..8da2f3a 100644 --- a/src/assets/i18n/en_us/strings.yml +++ b/src/assets/i18n/en_us/strings.yml @@ -81,6 +81,12 @@ util: embed: 'Embed Body' footer: 'Sniped by $[author:tag] ($[author:id])' + monstercat: + result: |- + **Track Title**: $[track:name] + **Duration**: $[track:duration]s + **Artists Title**: $[track:artist] + **Beats per Minute**: $[track:bpm] info: stats: fetching: '$[emoji#0] Fetching statistics, this may take some time...' diff --git a/src/assets/i18n/nl/badwords.yml b/src/assets/i18n/nl/badwords.yml new file mode 100644 index 0000000..d051685 --- /dev/null +++ b/src/assets/i18n/nl/badwords.yml @@ -0,0 +1,30 @@ +# This is an array of inappropriate language, please delete or add any inappropriate language that exist in your country +# (All should be lower case) +# for example, dutch: +# +# [English] +# - anal +# - dick +# - twat +# +# [Dutch] +# - anaal +# - penis + +- anaal +- anus +- balzak +- blowjob +- blow job +- clitoris +- cunt +- dildo +- neuk +- sperma +- neger +- negr +- penis +- vagina +- slet +- smegma +- hoer \ No newline at end of file diff --git a/src/assets/i18n/nl/logs.yml b/src/assets/i18n/nl/logs.yml new file mode 100644 index 0000000..6eb7151 --- /dev/null +++ b/src/assets/i18n/nl/logs.yml @@ -0,0 +1,21 @@ +translation_code: 'nl' + +message: + delete: '`[{date@now}]` {emoji} Een bericht van {e.user!s} (`{e.user.id}`) was was verwijderd van **{e.channel!s}**:' + +member: + add: + main: '`[{date@now}]` {emoji} {e.user!s} (`{e.user.id}`) is toegetreden, gebruiker aangemaakt op **{e.user.created@exact}** **(**{e.user.created@precise} ago**)**' + notes: + - 'Gebruikersnaam **kan** ongepast taalgebruik bevatten' + - 'Gebruiker is minder dan een dag geleden gemaakt' + - 'Gebruiker heeft geen aangepaste profielfoto' + - 'Gebruiker is verbannen van discord.services ({e.ban.reason})' + - 'Gebruiker is verbannen van ksoft.si ({e.ban.reason})' + +connection: + ready : '`[$[date:now] / $[process:hash]]` $[emoji#0] Meester client op Wump (`v$[wump:version]`) is verbonden' + shard : + ready : '`[$[date:now] / $[process:hash]]` $[emoji#0] Shard `#$[shard:id]` is verbonden naar Wump (`v$[wump:version]`)' + resume : '`[$[date:now] / $[process:hash]]` $[emoji#0] Shard `#$[shard:id]` is hervat naar Wump (`v$[wump:version]`)' + disconnect: '`[$[date:now] / $[process:hash]]` $[emoji#0] Shard `#$[shard:id]` is losgekoppeld van Wump (`v$[wump:version]`)' \ No newline at end of file diff --git a/src/assets/i18n/nl/logs_simple.yml b/src/assets/i18n/nl/logs_simple.yml deleted file mode 100644 index 5008ced..0000000 --- a/src/assets/i18n/nl/logs_simple.yml +++ /dev/null @@ -1,6 +0,0 @@ -connection: - ready : '`[$[date:now] / $[process:hash]]` $[emoji#0] Meester client op Wump (`v$[wump:version]`) is verbonden' - shard : - ready : '`[$[date:now] / $[process:hash]]` $[emoji#0] Shard `#$[shard:id]` is verbonden naar Wump (`v$[wump:version]`)' - resume : '`[$[date:now] / $[process:hash]]` $[emoji#0] Shard `#$[shard:id]` is hervat naar Wump (`v$[wump:version]`)' - disconnect: '`[$[date:now] / $[process:hash]]` $[emoji#0] Shard `#$[shard:id]` is losgekoppeld van Wump (`v$[wump:version]`)' \ No newline at end of file diff --git a/src/assets/img/emoji/DiscordLove.gif b/src/assets/img/emoji/DiscordLove.gif new file mode 100644 index 0000000..42a4846 Binary files /dev/null and b/src/assets/img/emoji/DiscordLove.gif differ diff --git a/src/commands/Discord/Core/locale.js b/src/commands/Discord/Core/locale.js index 0a03d85..a41f6fd 100644 --- a/src/commands/Discord/Core/locale.js +++ b/src/commands/Discord/Core/locale.js @@ -1,3 +1,4 @@ +// @TODO: refractor const { DiscordCommand } = require('../../../core'); module.exports = class Locale extends DiscordCommand { diff --git a/src/commands/Discord/Core/prefix.js b/src/commands/Discord/Core/prefix.js index fd27017..a966721 100644 --- a/src/commands/Discord/Core/prefix.js +++ b/src/commands/Discord/Core/prefix.js @@ -1,14 +1,12 @@ const { DiscordCommand } = require('../../../core'); -const larg = require('larg'); - module.exports = class Prefix extends DiscordCommand { constructor(bot) { super(bot, { name : 'prefix', - syntax : 'prefix <..prefix:string> <-u|-g>', + syntax : 'prefix <...prefix:str> <-u|-g>', aliases : [], - argument : [ '<...prefix:string>', '-' ], + argument : [ '<...prefix:string>', '-<(u|g)>' ], description: 'Change your prefix', hidden : false, @@ -22,25 +20,27 @@ module.exports = class Prefix extends DiscordCommand { } async execute(msg, args, user, guild) { - if (!user || user === null) user = await this.bot.m.connection.collection('dUsers').findOne({ userId: msg.author.id }); - if (!guild || guild === null) guild = await this.bot.m.connection.collection('dGuilds').findOne({ guildId: msg.channel.guild.id }); + if (!user || user === null) { + user = await this.bot.m.connection.collection('dUsers').findOne({ userId: msg.author.id }); + } + if (!guild || guild === null) { + guild = await this.bot.m.connection.collection('dGuilds').findOne({ guildId: msg.channel.guild.id }); + } - // Delete all empty elements in `args` args = args.filter((v) => { return v.length >= 1; }); - // Avoid an `undefined` prefix + if (!user || !user.prefix) { user.prefix = this.bot.conf.discord.prefix; } - if (!user || !guild.prefix) { + if (!guild || !guild.prefix) { guild.prefix = this.bot.conf.discord.prefix; } if (args.join(' ').endsWith('-u') || args.join(' ').endsWith('--user')) { - /* User prefix */ let prefix = args.splice(0, 1).join(' ').slice(0, 32).toLowerCase(); - // Checks + if (prefix.length <= 0) prefix = this.bot.conf.discord.prefix; if (user.prefix === prefix) { return msg.channel.createMessage(this.localize(msg.author.locale['core']['prefix']['dupe'], { uPrefix: user.prefix })); diff --git a/src/commands/Discord/Developer/eval.js b/src/commands/Discord/Developer/eval.js index efa7ecb..69a81ea 100644 --- a/src/commands/Discord/Developer/eval.js +++ b/src/commands/Discord/Developer/eval.js @@ -41,14 +41,14 @@ module.exports = class Eval extends DiscordCommand { errored = true; } finally { if (silent) return message.edit(this._localize(msg.author.locale.developer.eval.silent)); - result = this.bot.util.escapeMarkdown(this.bot.util.shorten(this._sanitize(String(result)), true, 1850)); + result = this.bot.util.escapeMarkdown(this._sanitize(String(result)), true); if (raw) return msg.channel.createMessage(`\`\`\`js\n${result}\`\`\``); message.edit({ content: '', embed: { color: errored ? this.bot.col.developer.eval.failure : this.bot.col.developer.eval.success, - description: this._localize(msg.author.locale.developer.eval.result.join('\n'), { resultType: errored ? msg.author.locale.developer.eval.types[1] : msg.author.locale.developer.eval.types[0], resultMessage: result || '{}' }) + description: this._localize(msg.author.locale.developer.eval.result.join('\n'), { resultType: errored ? msg.author.locale.developer.eval.types[1] : msg.author.locale.developer.eval.types[0], resultMessage: this.bot.util.shorten(result, 1850) || '{}' }) } }); }; diff --git a/src/commands/Discord/Developer/exec.js b/src/commands/Discord/Developer/exec.js index b3613e4..3c3793a 100644 --- a/src/commands/Discord/Developer/exec.js +++ b/src/commands/Discord/Developer/exec.js @@ -22,10 +22,10 @@ module.exports = class Exec extends DiscordCommand { async execute(msg, args) { let result = '$'; + let errored = false; const raw = args.join(' ').trim().endsWith('--raw') ? args.pop() : false; const silent = args.join(' ').trim().endsWith('--silent') || args.join(' ').trim().endsWith('-s') ? args.pop() : false; - const errored = false; if (args.length <= 0) return msg.channel.createMessage(this._localize(msg.author.locale.developer.exec.args)); diff --git a/src/commands/Discord/Utility/snipe.js b/src/commands/Discord/Utility/snipe.js index 6bdb15a..0bd8807 100644 --- a/src/commands/Discord/Utility/snipe.js +++ b/src/commands/Discord/Utility/snipe.js @@ -3,19 +3,19 @@ const { DiscordCommand } = require('../../../core/'); module.exports = class Snipe extends DiscordCommand { constructor(bot) { super(bot, { - name : 'snipe', - syntax : 'snipe', - aliases : [], - argument : [], + name: 'snipe', + syntax: 'snipe', + aliases: [], + argument: [], description: 'Re-post a deleted message', - hidden : false, - enabled : true, - cooldown : 1000, - category : 'Utility', - ownerOnly : false, - guildOnly : true, - permissions: [ 'embedLinks' ] + hidden: false, + enabled: true, + cooldown: 1000, + category: 'Utility', + ownerOnly: false, + guildOnly: true, + permissions: ['embedLinks'] }); this.static = { @@ -29,7 +29,7 @@ module.exports = class Snipe extends DiscordCommand { execute(msg) { const message = this.bot.cache.get(`${msg.channel.id}:SNIPE`); if (!message) return msg.channel.createMessage(this._localize(msg.author.locale.util.snipe.fail)); - + let structure = { author: { name: `${message.author.username}#${message.author.discriminator} (${message.author.id})`, @@ -62,19 +62,22 @@ module.exports = class Snipe extends DiscordCommand { name: 'Embed body', value: message.embeds[0].description }); - } else {{ - structure.description = message.content; - structure.fields.push({ - name: msg.author.locale.util.snipe.embed, - value: message.embeds[0].description.substring(0, this.mutable.EMBED_FIELD_LIMIT) - }, { - name: '.', - value: message.embeds[0].description.substring(this.mutable.EMBED_FIELD_LIMIT, this.mutable.EMBED_FIELD_LIMIT * 2) - }); - }} + } else { + { + structure.description = message.content; + structure.fields.push({ + name: msg.author.locale.util.snipe.embed, + value: message.embeds[0].description.substring(0, this.mutable.EMBED_FIELD_LIMIT) + }, { + name: '.', + value: message.embeds[0].description.substring(this.mutable.EMBED_FIELD_LIMIT, this.mutable.EMBED_FIELD_LIMIT * 2) + }); + } + } } } - } else if (message.content !== '' ) { + } + if (message.content.length >= 1) { structure.description = message.content; } @@ -100,10 +103,10 @@ module.exports = class Snipe extends DiscordCommand { .replace(/\$\[author:id]/g, extData.author.id) .replace(/\$\[author:tag]/g, `${extData.author.username}#${extData.author.discriminator}`); } - + return msg.replace(/\$\[emoji#0]/g, this.bot.emote('util', 'snipe')); } catch (ex) { - return `LOCALIZE_ERROR:${ex.code}`; + return `LOCALIZE_ERROR:${ex.code}`; } } }; \ No newline at end of file diff --git a/src/core/internal/Discord/Event.js b/src/core/internal/Discord/Event.js index d3162c5..9b112b1 100644 --- a/src/core/internal/Discord/Event.js +++ b/src/core/internal/Discord/Event.js @@ -2,6 +2,9 @@ module.exports = class WumpEvent { constructor(bot, opts = {}) { this.bot = bot; this.extData = Object.assign({ name: null }, opts); + + this.static = {}; + this.mutable = {}; } emit(...args) {} diff --git a/src/core/internal/Discord/schema.js b/src/core/internal/Discord/schema.js index 995c20b..7e71a95 100644 --- a/src/core/internal/Discord/schema.js +++ b/src/core/internal/Discord/schema.js @@ -7,6 +7,10 @@ const conf = safeLoad(readFileSync('application.yml', { encoding: 'utf8' })); const guild = new Schema({ guildId: { type: String, default: undefined }, + logger : { + channel: { type: String, default: '0' }, + disabled: [] + }, prefix : { type: String, default: conf.discord.prefix }, locale : { type: String, default: conf.discord.locale } }); diff --git a/src/core/registry/Discord/Commands.js b/src/core/registry/Discord/Commands.js index c0777d1..358d042 100644 --- a/src/core/registry/Discord/Commands.js +++ b/src/core/registry/Discord/Commands.js @@ -75,10 +75,10 @@ module.exports = class CommandRegistry { if (doc === null) { guild = new this.bot.schema.guild({ guildId: msg.channel.guild.id }); guild.save((err) => { if (err) process.handleError(err); }); - gCache.push({ 'guildId': msg.channel.guild.id, 'prefix': guild.prefix, 'entryAge': Date.now() }); + gCache.push({ 'guildId': msg.channel.guild.id, 'prefix': guild.prefix, 'locale': guild.locale, 'entryAge': Date.now() }); } else { guild = await this.bot.m.connection.collection('dGuilds').findOne({ guildId: msg.channel.guild.id }); - gCache.push({ 'guildId': msg.channel.guild.id, 'prefix': guild.prefix, 'entryAge': Date.now() }); + gCache.push({ 'guildId': msg.channel.guild.id, 'prefix': guild.prefix, 'locale': guild.locale, 'logger': guild.logger, 'entryAge': Date.now() }); } }); } else guild = gCache.filter((v) => v['guildId'] === msg.channel.guild.id )[0]; diff --git a/src/core/registry/Discord/Locales.js b/src/core/registry/Discord/Locales.js index 0bbd183..4dfe0c3 100644 --- a/src/core/registry/Discord/Locales.js +++ b/src/core/registry/Discord/Locales.js @@ -21,6 +21,17 @@ module.exports = class LocaleRegistry { this.bot.locales.set(l['translation']['code'].toLowerCase(), l); this.bot.localeDic.push(l['translation']['code']); this.bot.localeMap.push(`${l['translation']['flag']} \`${l['translation']['code']}\` **:** ${l['translation']['orig']} (*${l['translation']['full']}*) \`[${l['translation']['progress']}%]\` {***${l['translation']['translator']['discord'][0]['name']}#${l['translation']['translator']['discord'][0]['discrim']}***}`); + } else if (f.startsWith('logs')) { + this.bot.locales.set(`${l['translation_code'].toLowerCase()}:LOGS`, l); + } else if (f.startsWith('badwords')) { + if (!this.bot.locales.has('badwords')) { + this.bot.locales.set('badwords', l); + } else { + l.forEach((v) => { + this.bot.locales.get('badwords').push(v); + }); + } + } } } catch (ex) { diff --git a/src/events/Discord/disconnected.js b/src/events/Discord/disconnected.js index dc672ba..69ddc08 100644 --- a/src/events/Discord/disconnected.js +++ b/src/events/Discord/disconnected.js @@ -1,6 +1,6 @@ const { DiscordEvent } = require('../../core'); -const { green, cyan } = require('../../util/colors'); +const { green, cyan } = require('../../util/colors'); module.exports = class Disconnect extends DiscordEvent { constructor(bot) { diff --git a/src/events/Discord/guildMemberAdd.js b/src/events/Discord/guildMemberAdd.js new file mode 100644 index 0000000..44a5bca --- /dev/null +++ b/src/events/Discord/guildMemberAdd.js @@ -0,0 +1,155 @@ +const { DiscordEvent } = require('../../core'); + +const moment = require('moment'); require('../../util/moment/diff'); +const { get } = require('wumpfetch'); +const { yellow } = require('../../util/colors'); + +module.exports = class GuildMemberAdd extends DiscordEvent { + constructor(bot) { + super(bot, { name: 'guildMemberAdd' }); + + this.static = { + BASE_URL: [ + 'https://discord.services', + 'https://api.ksoft.si' + ], + REQ_DATA: [ + { + parse: 'json', + headers: { + 'User-Agent': this.bot.ua + } + }, + { + parse: 'json', + headers: { + 'User-Agent': this.bot.ua, + 'Authorization': this.bot.conf.api.ksoft + } + } + ] + }; + + Object.freeze(this); + Object.freeze(this.static); + } + + async emit(guild, member) { + /*----------[ Logging ]---------- */ + let entry = {}; + let cache = this.bot.cache.has('guilds') ? this.bot.cache.get('guilds') : this.bot.cache.set('guilds', []) && this.bot.cache.get('guilds'); + + + if (!this._checkArray('guildId', guild.id, cache)) { + this.bot.m.connection.collection('dGuilds').findOne({ guildId: guild.id }, async (err, doc) => { + if (err) { + process.handleError(err, 'IndexError', yellow('Database')); + } + + if (doc === null) { + entry = new this.bot.schema.guild({ guildId: guild.id }); + entry.save((err) => { + if (err) process.handleError(err, 'InsertError', yellow('Database')); + }); + + cache.push({ + 'prefix': entry.prefix, + 'logger': entry.logger, + 'locale': entry.locale, + 'guildId': guild.id, + 'entryAge': Date.now() + }); + } else { + entry = await this.bot.m.connection.collection('dGuilds').findOne({ guildId: guild.id }); + + cache.push({ + 'prefix': entry.prefix, + 'logger': entry.logger, + 'locale': entry.locale, + 'guildId': guild.id, + 'entryAge': Date.now() + }); + } + }); + } else { + entry = cache.filter((v) => v.guildId === guild.id)[0]; + } + + if (entry && entry.logger && entry.logger.channel !== '0' && !entry.logger.disabled.includes(this.extData.name)) { + try { + const strings = this.bot.locales.has(`${entry.locale}:LOGS`) ? this.bot.locales.get(`${entry.locale}:LOGS`) : this.bot.locales.get(`${this.bot.conf.discord.locale}:LOGS`); + const prohibited = new RegExp((this.bot.locales.get('badwords') || []).join('|'), 'gi'); + + member.bans = { + 'ksoft.si': this.bot.conf.api.ksoft ? await get(`${this.static.BASE_URL[0]}/bans/info/?user=${member.id}`, this.static.REQ_DATA[1]).send() : undefined, + 'discord.services': await get(`${this.static.BASE_URL[0]}/api/ban/${member.id}`, this.static.REQ_DATA[0]).send() + }; + + member.c = moment.preciseDiff(new Date(), new Date(member.createdAt), true); + let notes = []; + + if (prohibited.test(member.username)) { + notes.push(`${this.bot.emote('logs', 'message_delete', '1')} ${strings.member.add.notes[0]}`); + } + + if (member.c.days <= 0 && member.c.months <= 0 && member.c.years <= 0) { + notes.push(`${this.bot.emote('logs', 'message_delete', '2')} ${strings.member.add.notes[1]}`); + } + + if (!member.avatar) { + notes.push(`${this.bot.emote('logs', 'message_delete', '3')} ${strings.member.add.notes[2]}`); + } + + if (member.bans['discord.services'] && member.bans['discord.services'].body.msg !== 'No ban found') { + notes.push(`${this.bot.emote('logs', 'message_delete', '4')} ${strings.member.add.notes[3].replace(/{e\.ban\.reason}/, member.bans['discord.services'].body.reason || 'Unknown')}`); + } + if (member.bans['ksoft.si'] && !member.bans['ksoft.si'].body.code) { + notes.push(`${this.bot.emote('logs', 'message_delete', '5')} ${strings.member.add.notes[5].replace(/{e\.ban\.reason}/, member.bans['ksoft.si'].body.reason || 'Unknown')}`); + } + + this.bot.createMessage(entry.logger.channel, { + content: this._localize(strings.member.add.main, member), + embed: { + description: notes.length >= 1 ? notes.join('\n') : undefined + } + }); + } catch (ex) { + throw ex; + } + } + } + + _checkArray(field, needle, array) { + if (array instanceof Array) { + return array.some((v) => { + return v[field] === needle; + }); + } else { + return false; + } + } + + _localize(msg, extData) { + if (extData) { + extData.creation = { + date: moment(extData.createdAt).format('YYYY[/]MM[/]DD HH[:]mm'), + precise: [ + extData.c.years >= 1 ? `${extData.c.years} years` : undefined, + extData.c.months >= 1 ? `${extData.c.months} months` : undefined, + extData.c.days >= 1 ? `${extData.c.days} days` : undefined, + extData.c.days <= 0 && extData.c.months <= 0 && extData.c.years <= 0 ? 'Less than a day' : undefined + ].join(' ').trim() + }; + + msg = msg + .replace(/{e\.user!s}/g, `${extData.username}#${extData.discriminator}`) + .replace(/{e\.user\.id}/g, extData.id) + .replace(/{e\.user\.created@exact}/g, extData.creation.date) + .replace(/{e\.user\.created@precise}/g, extData.creation.precise); + } + + return msg + .replace(/{emoji}/g, this.bot.emote('logs', 'message_delete', '0')) + .replace(/{date@now}/g, moment(new Date).format(`HH[:]mm[:]ss[.]SS`)); + } +}; \ No newline at end of file diff --git a/src/events/Discord/messageDelete.js b/src/events/Discord/messageDelete.js index f9c1a06..1186974 100644 --- a/src/events/Discord/messageDelete.js +++ b/src/events/Discord/messageDelete.js @@ -1,12 +1,154 @@ const { DiscordEvent } = require('../../core'); +const moment = require('moment'); +const { yellow } = require('../../util/colors'); module.exports = class MessageDelete extends DiscordEvent { constructor(bot) { super(bot, { name: 'messageDelete' }); + + this.static = { + EMBED_FIELD_LIMIT: 1024 + }; + + Object.freeze(this); + Object.freeze(this.static); } emit(msg) { if (!msg.channel.guild || !msg.author || !this.bot.ready) return; this.bot.cache.set(`${msg.channel.id}:SNIPE`, msg); + + if (msg.author.id === this.bot.user.id) return; + + let guild = {}; + let cache = this.bot.cache.has('guilds') ? this.bot.cache.get('guilds') : this.bot.cache.set('guilds', []) && this.bot.cache.get('guilds'); + + if (!this._checkArray('guildId', msg.channel.guild.id, cache)) { + this.bot.m.connection.collection('dGuilds').findOne({ guildId: msg.channel.guild.id }, async (err, doc) => { + if (err) { + process.handleError(err, 'IndexError', yellow('Database')); + } + + if (doc === null) { + guild = new this.bot.schema.guild({ guildId: msg.channel.guild.id }); + guild.save((err) => { + if (err) process.handleError(err, 'InsertError', yellow('Database')); + }); + + cache.push({ + 'prefix': guild.prefix, + 'logger': guild.logger, + 'locale': guild.locale, + 'guildId': msg.channel.guild.id, + 'entryAge': Date.now() + }); + } else { + guild = await this.bot.m.connection.collection('dGuilds').findOne({ guildId: msg.channel.guild.id }); + + cache.push({ + 'prefix': guild.prefix, + 'logger': guild.logger, + 'locale': guild.locale, + 'guildId': msg.channel.guild.id, + 'entryAge': Date.now() + }); + } + }); + } else { + guild = cache.filter((v) => v.guildId === msg.channel.guild.id)[0]; + } + + if (guild && guild.logger && guild.logger.channel !== '0' && msg.channel.id !== guild.logger.channel && !guild.logger.disabled.includes(this.extData.name)) { + try { + const strings = this.bot.locales.has(`${guild.locale}:LOGS`) ? this.bot.locales.get(`${guild.locale}:LOGS`) : this.bot.locales.get(`${this.bot.conf.discord.locale}:LOGS`); + + let structure = { + author: { + name: `${msg.author.username}#${msg.author.discriminator} (${msg.author.id})`, + icon_url: msg.author.avatar ? msg.author.avatarURL : msg.author.defaultAvatarURL + }, + color: this.bot.col.logs.message.delete, + fields: [], + timestamp: new Date(msg.timestamp).toISOString() + }; + + if (msg.embeds.length > 0) { + if ('url' in msg.embeds[0]) structure.url = msg.embeds[0].url; + if ('type' in msg.embeds[0]) structure.type = msg.embeds[0].type; + if ('title' in msg.embeds[0]) structure.title = msg.embeds[0].title; + if ('image' in msg.embeds[0]) structure.image = msg.embeds[0].image; + if ('video' in msg.embeds[0]) structure.video = msg.embeds[0].video; + if ('fields' in msg.embeds[0]) structure.fields = msg.embeds[0].fields; + if ('provider' in msg.embeds[0]) structure.provider = msg.embeds[0].provider; + if ('thumbnail' in msg.embeds[0]) structure.thumbnail = msg.embeds[0].thumbnail; + if ('description' in msg.embeds[0]) { + if (msg.content === '') { + structure.description = msg.embeds[0].description; + } else { + if (msg.embeds[0].description.length <= this.static.EMBED_FIELD_LIMIT) { + structure.description = msg.content; + structure.fields.push({ + name: 'Embed body', + value: msg.embeds[0].description + }); + } else if (msg.embeds[0].description.length >= this.static.EMBED_FIELD_LIMIT) { + structure.description = msg.content; + structure.fields.push({ + name: msg.author.locale.util.snipe.embed, + value: msg.embeds[0].description.substring(0, this.static.EMBED_FIELD_LIMIT) + }, + { + name: '.', + value: msg.embeds[0].description.substring(this.static.EMBED_FIELD_LIMIT, this.static.EMBED_FIELD_LIMIT * 2) + }); + } + }; + } + } + + if (msg.content.length >= 1) { + structure.description = msg.content; + } + + if (!('image' in structure) && msg.attachments.length > 0) { + structure.image = { + url: msg.attachments[0].url, + width: msg.attachments[0].width, + height: msg.attachments[0].height, + proxy_url: msg.attachments[0].proxy_url + }; + } + + this.bot.createMessage(guild.logger.channel, { + content: this._localize(strings.message.delete, msg), + embed: structure + }); + } catch (ex) { + throw ex; + } + } + } + + _checkArray(field, needle, array) { + if (array instanceof Array) { + return array.some((v) => { + return v[field] === needle; + }); + } else { + return false; + } + } + + _localize(msg, extData) { + if (extData) { + msg = msg + .replace(/{e\.user!s}/, `${extData.author.username}#${extData.author.discriminator}`) + .replace(/{e\.user\.id}/, extData.author.id) + .replace(/{e\.channel!s}/, `#${extData.channel.name}`); + } + + return msg + .replace(/{emoji}/, this.bot.emote('logs', 'message', 'delete')) + .replace(/{date@now}/, moment(new Date).format(`HH[:]mm[:]ss`)); } }; \ No newline at end of file diff --git a/src/events/Discord/ready.js b/src/events/Discord/ready.js index 1310178..b4b62f2 100644 --- a/src/events/Discord/ready.js +++ b/src/events/Discord/ready.js @@ -1,40 +1,40 @@ const { DiscordEvent } = require('../../core'); -const moment = require('moment'); -const { safeLoad } = require('js-yaml'); -const { green, cyan } = require('../../util/colors'); -const { join: pJoin } = require('path'); -const { readFileSync } = require('fs'); +const moment = require('moment'); +const { green, cyan } = require('../../util/colors'); module.exports = class Ready extends DiscordEvent { constructor(bot) { super(bot, { name: 'ready' }); - - this.strings = safeLoad(readFileSync(pJoin(__dirname, '..', '..', 'assets', 'i18n', bot.conf['discord']['locale'], 'logs_simple.yml'), { encoding: 'utf8' })); } emit() { this.bot.print(1, `${cyan( 'Discord' )} >> All websockets connected to ${green(`${this.bot.user.username}#${this.bot.user.discriminator}`)}`); - if (this.bot.conf['discord']['playing']) { + if (this.bot.conf.discord.playing) { this.bot.editStatus({ - url : this.bot.conf['discord']['playing']['url'] || null, - type : this.bot.conf['discord']['playing']['type'] || 3, - name : this.bot.conf['discord']['playing']['name'] || 'Wump', - status : this.bot.conf['discord']['playing']['status'] || 'online' + url : this.bot.conf.discord.playing.url || null, + type : this.bot.conf.discord.playing.type || 3, + name : this.bot.conf.discord.playing.name || 'Wump', + status : this.bot.conf.discord.playing.status || 'online' }); } - this.bot.hook.send({ content: this.localize(this.strings['connection']['ready']) }); + this.bot.hook.send({ content: this.localize(this.bot.locales.get('en_us:LOGS').connection.ready) }); - // Cache flushing if entry isn't used within `flushTime` minutes - const flushTime = 1800000; + // Cache flushing if entry isn't used within `flushTime` milliseconds + const flushTime = (30 * 60) * 1000; setInterval(() => { const uCache = this.bot.cache.get('users') || []; const gCache = this.bot.cache.get('guilds') || []; const vCache = this.bot.cache.get('voters') || []; - gCache.forEach((v, _) => (new Date(v.entryAge) <= new Date(new Date() - flushTime)) ? gCache.splice(_, 1) : undefined); + gCache.forEach((v, _) => { + if (new Date(v.entryAge) <= new Date(new Date() - flushTime)) { + gCache.splice(_, 1); + } + }); + uCache.forEach((v, _) => { if (new Date(v.entryAge) <= new Date(new Date() - flushTime)) { vCache.forEach((n, _) => n === v.userId ? vCache.splice(_, 1) : undefined); @@ -45,12 +45,16 @@ module.exports = class Ready extends DiscordEvent { } localize(msg) { - if (!msg) return ''; - return msg - .replace(/\$\[wump:version]/g, `${this.bot.pkg.version} ${this.bot.conf['nightly'] ? 'NIGHTLY' : 'DISTRIBUTIONs'}`) - .replace(/\$\[process:hash]/g, process.hash) - .replace(/\$\[date:now]/g, moment(Date.now()).format('HH[:]mm[:]ss')) - .replace(/\$\[emoji#0]/g, this.bot.emote('logs', 'ready')); + try { + if (!msg) throw 'INVALID_STRING'; + + return msg + .replace(/{global\.version}/, `${this.bot.pkg.version} ${this.bot.conf.nightly ? 'NIGHTLY' : 'DISTRIBUTION'}`) + .replace(/{process\.hash}/, process.hash) + .replace(/{date@now}/, moment(Date.now()).format('HH[:]mm[:]ss')) + .replace(/{emoji}/, this.bot.emote('logs', 'shard', 'disconnect')); + } catch (ex) { + return `LOCALIZE_ERROR:${ex.code}`; + } } -}; -// YYYY[y] M[M] DD[d] H[h] m[m] ${startTime > 1000 ? 's[.]SS[s]' : 's[s] SS[ms]'} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/events/Discord/shardDisconnect.js b/src/events/Discord/shardDisconnect.js index b4225c7..4de7430 100644 --- a/src/events/Discord/shardDisconnect.js +++ b/src/events/Discord/shardDisconnect.js @@ -1,28 +1,31 @@ const { DiscordEvent } = require('../../core'); -const moment = require('moment'); -const { safeLoad } = require('js-yaml'); -const { join: pJoin } = require('path'); -const { readFileSync } = require('fs'); +const moment = require('moment'); module.exports = class shardDisconnect extends DiscordEvent { constructor(bot) { super(bot, { name: 'shardDisconnect' }); - - this.strings = safeLoad(readFileSync(pJoin(__dirname, '..', '..', 'assets', 'i18n', bot.conf['discord']['locale'], 'logs_simple.yml'), { encoding: 'utf8' })); } - emit(err, id) { - this.bot.hook.send({ content: this.localize(this.strings['connection']['shard']['disconnect'], { shard: id }) }); + emit(err, shard) { + this.bot.hook.send({ content: this.localize(this.bot.locales.get('en_us:LOGS').connection.shard.disconnect, shard) }); } localize(msg, extData = {}) { - if (!msg) return ''; - return msg - .replace(/\$\[shard:id]/g, extData.shard) - .replace(/\$\[wump:version]/g, `${this.bot.pkg.version} ${this.bot.conf['nightly'] ? 'NIGHTLY' : 'DISTRIBUTION'}`) - .replace(/\$\[process:hash]/g, process.hash) - .replace(/\$\[date:now]/g, moment(Date.now()).format('HH[:]mm[:]ss')) - .replace(/\$\[emoji#0]/g, this.bot.emote('logs', 'shard', 'disconnect')); + try { + if (!msg) throw 'INVALID_STRING'; + + if (extData) { + msg = msg.replace(/{e\.shard\.id}/, extData); + } + + return msg + .replace(/{global\.version}/, `${this.bot.pkg.version} ${this.bot.conf.nightly ? 'NIGHTLY' : 'DISTRIBUTION'}`) + .replace(/{process\.hash}/, process.hash) + .replace(/{date@now}/, moment(Date.now()).format('HH[:]mm[:]ss')) + .replace(/{emoji}/, this.bot.emote('logs', 'shard', 'disconnect')); + } catch (ex) { + return `LOCALIZE_ERROR:${ex.code}`; + } } }; \ No newline at end of file diff --git a/src/events/Discord/shardReady.js b/src/events/Discord/shardReady.js index 8174e4d..0b847a7 100644 --- a/src/events/Discord/shardReady.js +++ b/src/events/Discord/shardReady.js @@ -1,28 +1,31 @@ const { DiscordEvent } = require('../../core'); -const moment = require('moment'); -const { safeLoad } = require('js-yaml'); -const { join: pJoin } = require('path'); -const { readFileSync } = require('fs'); +const moment = require('moment'); module.exports = class ShardReady extends DiscordEvent { constructor(bot) { super(bot, { name: 'shardReady' }); - - this.strings = safeLoad(readFileSync(pJoin(__dirname, '..', '..', 'assets', 'i18n', bot.conf['discord']['locale'], 'logs_simple.yml'), { encoding: 'utf8' })); } - emit(id) { - this.bot.hook.send({ content: this.localize(this.strings['connection']['shard']['ready'], { shard: id }) }); + emit(shard) { + this.bot.hook.send({ content: this.localize(this.bot.locales.get('en_us:LOGS').connection.shard.ready, shard) }); } localize(msg, extData = {}) { - if (!msg) return ''; - return msg - .replace(/\$\[shard:id]/g, extData.shard) - .replace(/\$\[wump:version]/g, `${this.bot.pkg.version} ${this.bot.conf['nightly'] ? 'NIGHTLY' : 'DISTRIBUTION'}`) - .replace(/\$\[process:hash]/g, process.hash) - .replace(/\$\[date:now]/g, moment(Date.now()).format('HH[:]mm[:]ss')) - .replace(/\$\[emoji#0]/g, this.bot.emote('logs', 'shard', 'ready')); + try { + if (!msg) throw 'INVALID_STRING'; + + if (extData) { + msg = msg.replace(/{e\.shard\.id}/, extData); + } + + return msg + .replace(/{global\.version}/, `${this.bot.pkg.version} ${this.bot.conf.nightly ? 'NIGHTLY' : 'DISTRIBUTION'}`) + .replace(/{process\.hash}/, process.hash) + .replace(/{date@now}/, moment(Date.now()).format('HH[:]mm[:]ss')) + .replace(/{emoji}/, this.bot.emote('logs', 'shard', 'disconnect')); + } catch (ex) { + return `LOCALIZE_ERROR:${ex.code}`; + } } }; \ No newline at end of file diff --git a/src/events/Discord/shardResume.js b/src/events/Discord/shardResume.js index 2fd1d00..8d460c2 100644 --- a/src/events/Discord/shardResume.js +++ b/src/events/Discord/shardResume.js @@ -1,28 +1,31 @@ const { DiscordEvent } = require('../../core'); -const moment = require('moment'); -const { safeLoad } = require('js-yaml'); -const { join: pJoin } = require('path'); -const { readFileSync } = require('fs'); +const moment = require('moment'); module.exports = class ShardResume extends DiscordEvent { constructor(bot) { super(bot, { name: 'shardResume' }); - - this.strings = safeLoad(readFileSync(pJoin(__dirname, '..', '..', 'assets', 'i18n', bot.conf['discord']['locale'], 'logs_simple.yml'), { encoding: 'utf8' })); } - emit(id) { - this.bot.hook.send({ content: this.localize(this.strings['connection']['shard']['resume'], { shard: id }) }); + emit(shard) { + this.bot.hook.send({ content: this.localize(this.bot.locales.get('en_us:LOGS').connection.shard.resume, shard) }); } localize(msg, extData = {}) { - if (!msg) return ''; - return msg - .replace(/\$\[shard:id]/g, extData.shard) - .replace(/\$\[wump:version]/g, `${this.bot.pkg.version} ${this.bot.conf['nightly'] ? 'NIGHTLY' : 'DISTRIBUTION'}`) - .replace(/\$\[process:hash]/g, process.hash) - .replace(/\$\[date:now]/g, moment(Date.now()).format('HH[:]mm[:]ss')) - .replace(/\$\[emoji#0]/g, this.bot.emote('logs', 'shard', 'resume')); + try { + if (!msg) throw 'INVALID_STRING'; + + if (extData) { + msg = msg.replace(/{e\.shard\.id}/, extData); + } + + return msg + .replace(/{global\.version}/, `${this.bot.pkg.version} ${this.bot.conf.nightly ? 'NIGHTLY' : 'DISTRIBUTION'}`) + .replace(/{process\.hash}/, process.hash) + .replace(/{date@now}/, moment(Date.now()).format('HH[:]mm[:]ss')) + .replace(/{emoji}/, this.bot.emote('logs', 'shard', 'disconnect')); + } catch (ex) { + return `LOCALIZE_ERROR:${ex.code}`; + } } }; \ No newline at end of file diff --git a/src/main.js b/src/main.js index 2d6806c..2814c13 100644 --- a/src/main.js +++ b/src/main.js @@ -85,7 +85,7 @@ if (conf['discord']['enabled']) { autoreconnect: true }, - ua : `${pkg.displayName}/${pkg.version}/${conf['nightly'] ? 'nightly' : 'distribution'}`, + ua : `${pkg.displayName}/${conf['nightly'] ? 'nightly' : 'distribution'} (https://github.com/PassTheWessel/wump)`, db : mongoose, pkg : pkg, colors: col,