From c9e3c225424d6605b4acf795ac8c3f68e36f8700 Mon Sep 17 00:00:00 2001 From: Wessel T Date: Fri, 15 Feb 2019 21:54:19 +0100 Subject: [PATCH] A lot of changes + fixes, added exec and user command. extended avatar --- src/_application.yml | 6 +- src/assets/config/colors.yml | 5 + src/assets/config/emotes.yml | 134 ++++++++++------- src/assets/i18n/en_us/strings.yml | 51 ++++++- src/assets/i18n/nl/strings.yml | 51 ++++++- .../img/emoji/Audit logs/create_role.png | Bin 0 -> 6087 bytes src/assets/img/emoji/join.png | Bin 0 -> 209 bytes src/assets/img/emoji/send.png | Bin 0 -> 649 bytes src/assets/img/thumb/votelocked.png | Bin 0 -> 13131 bytes src/commands/Discord/Core/locale.js | 21 ++- src/commands/Discord/Developer/echo.js | 8 +- src/commands/Discord/Developer/exec.js | 87 +++++++++++ src/commands/Discord/Information/avatar.js | 136 +++++++++++++----- src/commands/Discord/Information/commands.js | 7 +- src/commands/Discord/Information/guild.js | 6 +- src/commands/Discord/Information/user.js | 111 ++++++++++++++ src/core/internal/Discord/Command.js | 6 +- src/core/internal/Discord/Event.js | 2 +- src/core/internal/Discord/__baseCommand.js | 2 +- src/core/internal/Discord/rest.js | 1 + src/core/registry/Discord/Commands.js | 6 +- src/core/registry/Discord/Events.js | 2 +- src/core/registry/Discord/Locales.js | 2 +- src/main.js | 6 +- src/util/Util.js | 23 +-- 25 files changed, 547 insertions(+), 126 deletions(-) create mode 100644 src/assets/img/emoji/Audit logs/create_role.png create mode 100644 src/assets/img/emoji/join.png create mode 100644 src/assets/img/emoji/send.png create mode 100644 src/assets/img/thumb/votelocked.png create mode 100644 src/commands/Discord/Developer/exec.js create mode 100644 src/commands/Discord/Information/user.js diff --git a/src/_application.yml b/src/_application.yml index 38cd486..74ba252 100644 --- a/src/_application.yml +++ b/src/_application.yml @@ -57,9 +57,11 @@ discord : math : true snipe : true # Category : INFORMATION + user : true stats : true guild : true avatar : true + github : true commits : true commands : true # Category : IMAGE @@ -73,6 +75,8 @@ discord : # Category : DEVELOPER eval : true echo : true + exec : true api : - lol: '<--snip-->' \ No newline at end of file + lol: '<--snip-->' + dbl: '<--snip-->' diff --git a/src/assets/config/colors.yml b/src/assets/config/colors.yml index 71584cb..4013786 100644 --- a/src/assets/config/colors.yml +++ b/src/assets/config/colors.yml @@ -1,5 +1,6 @@ error : 0xff0000 # Red cooldown: 0x7289DA # Blurple +votelock: 0xF7AE69 core : locale: 0x7289DA # Blurple @@ -31,5 +32,9 @@ image : developer : eval : + success: 0x00ff00 # Green + failure: 0xff0000 # Red + + exec : success: 0x00ff00 # Green failure: 0xff0000 # Red \ No newline at end of file diff --git a/src/assets/config/emotes.yml b/src/assets/config/emotes.yml index 28c1d49..f05cfa4 100644 --- a/src/assets/config/emotes.yml +++ b/src/assets/config/emotes.yml @@ -1,7 +1,11 @@ +votelock : + 0 : <:exclamation:542368036255039509> + 1 : <:send:544852981254848513> cooldown : <:stopwatch:542477522147868672> # On cooldown owner_only: <:exclamation:542368036255039509> # Only owners guild_only: <:exclamation:542368036255039509> # Only guilds + core : locale: 0 : <:wumpus_blob:541629681120837648> # Dupe locale (user) @@ -13,6 +17,7 @@ core : 6 : <:ok_hand:497396122416054287> # Updated (user) 7 : <:sad:500719642403405824> # Invalid locale 8 : <:sad:500719642403405824> # Missing permissions (guild) + prefix: 0 : <:wumpus_blob:541629681120837648> # Dupe prefix (user) 1 : <:wumpus_blob:541629681120837648> # Dupe prefix (guild) @@ -23,8 +28,9 @@ core : 6 : <:ok_hand:497396122416054287> # Updated (user) 7 : <:sad:500719642403405824> # Missing permissions (guild) -logs : - ready : '<:spacewump:542723940997529641>' +logs : + ready: '<:spacewump:542723940997529641>' + shard : ready : '<:create_webhook:542716503674454016>' resume : '<:update_webhook:542716504089690124>' @@ -36,68 +42,100 @@ util : 1 : <:thumbsup:501844577725054976> # Heartbeat 2 : <:blueberry:506201160382939141> # Round-trip 3 : <:WumpLove:506200781549076482> # Health + math : 0 : <:blueberry:506201160382939141> # Calculating 1 : # Evaluation 2 : # Invalid equation + snipe: <:snipe:508653323788877825> # No snipes found info: - guild : - 0 : <:search:542368817163010057> # Guild Name - 1 : <:wumpus_flag:495525134220787713> # Guild region - 2 : <:cloud:542368816911613963> # Guild creation - 3 : <:alert:542367222279176202> # Content filter - 4 : <:exclamation:542368036255039509> # Verification level - 5 : <:hashtag:542367004024242176> # Channels - 6 : <:owner:542365741844922368> # Owner tag - 7 : <:early:542366345795207178> # Owner creation - 8 : <:store:542366547180781588> # Owner avatar - 9 : <:bot:473670328334155805> # Total bots - 10 : <:member:542362747896201218> # Total users - 11 : <:online:362670805051768833> # Total online members - 12 : <:idle:362670805760475136> # Total idle members - 13 : <:dnd:362670805676720128> # Total DnD members - 14 : <:offline:362670805441708032> # Total offline members - 15 : <:exclamation:542368036255039509> # Invalid query - avatar : <:blueberry:506201160382939141> # User's avatar - commits : - 0 : <:wumplus:542809926414761995> # 10 most recent commits - 1 : <:early:542366345795207178> # Total commits - commands : - 0 : <:blueberry:506201160382939141> # Guild prefix - 1 : <:spirit:332131321759399937> # More info - 2 : <:WumpLove:506200781549076482> # Links - statistics : - 0 : <:ok_hand:497396122416054287> # Fetching - 1 : <:blueberry:506201160382939141> # Operating System - 2 : <:happy:500719642231177220> # Process hash - 3 : <:WumpLove:506200781549076482> # Load average - 4 : # OS uptime - 5 : # Uptime - 6 : <:monkey:369880931630317568> # Memory usage - 7 : <:spirit:332131321759399937> # User agent - 8 : <:dev_server:495573905092902942> # Shards - 9 : <:beetle_tank:445274774579445771> # Cache - 10 : <:srv_server:495680009197977617> # Channels - 11 : <:srv_tags:496035430052134912> # Guilds - 12 : <:srv_user:495917052784148480> # Users + guild: + 0 : <:search:542368817163010057> # Guild Name + 1 : <:wumpus_flag:495525134220787713> # Guild region + 2 : <:cloud:542368816911613963> # Guild creation + 3 : <:alert:542367222279176202> # Content filter + 4 : <:exclamation:542368036255039509> # Verification level + 5 : <:hashtag:542367004024242176> # Channels + 6 : <:owner:542365741844922368> # Owner tag + 7 : <:early:542366345795207178> # Owner creation + 8 : <:store:542366547180781588> # Owner avatar + 9 : <:bot:473670328334155805> # Total bots + 10 : <:member:542362747896201218> # Total users + 11 : <:online:362670805051768833> # Total online members + 12 : <:idle:362670805760475136> # Total idle members + 13 : <:dnd:362670805676720128> # Total DnD members + 14 : <:offline:362670805441708032> # Total offline members + 15 : <:exclamation:542368036255039509> # Invalid query + + user: + 0 : <:hashtag:542367004024242176> # User tag + 1 : <:early:542366345795207178> # Creation date + 2 : <:streaming:362670805185724418> # User status + 3 : <:wumpus_blob:541629681120837648> # Nickname + 4 : <:join:546066271318441994> # Joined date + 5 : <:create_role:546066640417325066> # Roles + 6 : <:exclamation:542368036255039509> # Status (unknown) + 7 : <:online:362670805051768833> # Status (online) + 8 : <:idle:362670805760475136> # Status (away) + 9 : <:dnd:362670805676720128> # Status (DnD) + 10: <:offline:362670805441708032> # Status (offline) + 11: <:exclamation:542368036255039509> # Invalid user + + avatar: <:blueberry:506201160382939141> # User's avatar + + commits: + 0 : <:wumplus:542809926414761995> # 10 most recent commits + 1 : <:early:542366345795207178> # Total commits + + commands: + 0 : <:blueberry:506201160382939141> # Guild prefix + 1 : <:spirit:332131321759399937> # More info + 2 : <:WumpLove:506200781549076482> # Links + 3 : <:Starlinator:332182009680101376> # Name + 4 : <:beetle_tank:445274774579445771> # Syntax + 5 : <:chubi:447107570239537152> # Aliases + 6 : <:reptice:331951247445983235> # Cooldown + 7 : <:the_mole:445271536690003968> # Arguments + + statistics: + 0 : <:ok_hand:497396122416054287> # Fetching + 1 : <:blueberry:506201160382939141> # Operating System + 2 : <:happy:500719642231177220> # Process hash + 3 : <:WumpLove:506200781549076482> # Load average + 4 : # OS uptime + 5 : # Uptime + 6 : <:monkey:369880931630317568> # Memory usage + 7 : <:spirit:332131321759399937> # User agent + 8 : <:dev_server:495573905092902942> # Shards + 9 : <:beetle_tank:445274774579445771> # Cache + 10 : <:srv_server:495680009197977617> # Channels + 11 : <:srv_tags:496035430052134912> # Guilds + 12 : <:srv_user:495917052784148480> # Users + +image: + cat: + 0: <:blueberry:506201160382939141> # Fetching + 1: <:spirit:332131321759399937> # Cat fact + + dog: + 0: <:blueberry:506201160382939141> # Fetching + 1: <:spirit:332131321759399937> # dog fact -image : - cat : - 0 : <:blueberry:506201160382939141> # Fetching - 1 : <:spirit:332131321759399937> # Cat fact - dog : - 0 : <:blueberry:506201160382939141> # Fetching - 1 : <:spirit:332131321759399937> # dog fact fox : <:blueberry:506201160382939141> # Fetching + birb : <:blueberry:506201160382939141> # Fetching + duck : <:blueberry:506201160382939141> # Fetching + lizard : <:blueberry:506201160382939141> # Fetching + penguin: <:blueberry:506201160382939141> # Fetching developer: echo : # Invalid message + eval : 0 : # Invalid snippet 1 : # Evaluating diff --git a/src/assets/i18n/en_us/strings.yml b/src/assets/i18n/en_us/strings.yml index 2133409..0aaf7ec 100644 --- a/src/assets/i18n/en_us/strings.yml +++ b/src/assets/i18n/en_us/strings.yml @@ -19,9 +19,13 @@ error : - 'Message = "$[err:message]"' - '```' cooldown: '$[emoji#0] $[author:mention] **>** This command is on cooldown for **$[cooldown:left]**' +votelock: + - '$[emoji#0] This command is **votelocked**, upvote [**`Wump`**](https://discordbots.org/bot/318057009188438016/vote) to unlock this command/parameters (It can take up to 10 minutes to register your vote)' + - '' + - '$[emoji#1] If you still have issues after that, you can either create an [**`issue`**](https://github.com/PassTheWessel/wump/issues) on [**`GitHub`**](https://github.com/PassTheWessel/wump) or join my [**`Discord`**](https://discord.gg/SV7DAE9) and posting it there' core : - prefix : + prefix : gchanged: '$[emoji#5] Successfully updated the guild''s prefix to `$[guild:prefix]`' changed : '$[emoji#6] Successfully updated your prefix to `$[user:prefix]`' gperms : '$[emoji#8] You''re missing permissions in order to execute this command (`MANAGE_GUILD`)' @@ -66,9 +70,11 @@ util : - '$[emoji#2] **Round-Trip**: $[roundtrip]ms' - '$[emoji#1] **Heartbeat**: $[latency]ms' - '$[emoji#3] **Health**: $[health]' + math : busy : '$[emoji#0] Calculating, this may take some time...' blocked: ' Method "$[math:function]" has been disabled for security reasons ' + snipe : fail : '$[emoji#0] No snipeable messages found' footer : 'Sniped by $[author:tag] ($[author:id])' @@ -76,27 +82,30 @@ util : info : stats : fetching: '$[emoji#0] Fetching statistics, this may take some time...' + avatar : '$[emoji#0] Here''s **$[user:full]** avatar' commits : - '$[emoji#1] **Total commits**: [`$[commits:total]`](https://github.com/PassTheWessel/wump/commits "Click here for a list of all commits")' - '$[emoji#0] **10 most recent commits done on [`PassTheWessel/wump`](https://github.com/PassTheWessel/wump/)**: $[commits:list]' + commands: multi : - '$[emoji#0] The prefix for **$[guild:name]** is `$[guild:prefix]`' - '$[emoji#1] Type `$[guild:prefix]command ` for more information' - '$[emoji#2] **Links**: [`Invite`]($[bot:invite]) **|** [`Support`](https://discord.gg/SV7DAE9) **|** [`GitHub`](https://github.com/PassTheWessel/wump) **|** [`Patreon`](https://patreon.com/wessel)' single: - - '**Name**: $[command:name]' - - '**Syntax**: $[command:syntax]' - - '**Aliases**: $[command:aliases]' - - '**Cooldown**: $[command:cooldown#formatted]' - - '**Arguments**: $[command:arguments]' + - '$[emoji#3] **Name**: $[command:name]' + - '$[emoji#4] **Syntax**: $[command:syntax]' + - '$[emoji#5] **Aliases**: $[command:aliases]' + - '$[emoji#6] **Cooldown**: $[command:cooldown#formatted]' + - '$[emoji#7] **Arguments**: $[command:arguments]' fields: - 'Core' - 'Information' - 'Image' - 'Utility' - 'Developer' + guild: fields: guild: @@ -126,12 +135,29 @@ info : - '$[emoji#14] Offline **⤏** `$[guild:users#offline]`' invalid: '$[emoji#15] No guild found with your query' + user : + invalid : '$[emoji#11] No user found with your query' + fields : + self : + name : '❯ User Information' + value: + - '$[emoji#0] **Tag**: $[user:tag] (`$[user:id]`)' + - '$[emoji#1] **Creation**: $[user:created@precise] ago (`$[user:created@exact]`)' + - '$[emoji#2] **Status**: $[user:status@full]' + member: + name : '❯ Member Information' + value: + - '$[emoji#3] **Nickname**: $[member:nickname]' + - '$[emoji#4] **Joined**: $[member:created@precise] ago (`$[member:created@exact]`)' + - '$[emoji#5] **Roles**: $[member:roles]' + image: fetching : '$[emoji#0] Fetching a random image, this may take some time...' failed_cache: 'Click here if the image didn''t load properly' developer: echo : '$[emoji#0] Invalid content to echo' + eval : args : '$[emoji#0] Invalid snippet of code to evaluate' busy : '$[emoji#1] Evaluating, this may take some time...' @@ -143,4 +169,17 @@ developer: - '$[emoji#3] **Result** (`$[result:type]`):' - '```js' - '$[result:message]' + - '```' + + exec : + args : '$[emoji#0] Invalid command to execute' + busy : '$[emoji#1] Executing, this may take some time...' + silent: '$[emoji#2] Silently executed your command' + types : + - 'succeed' + - 'failed' + result: + - '$[emoji#3] **Result** (`$[result:type]`):' + - '```sh' + - '$[result:message]' - '```' \ No newline at end of file diff --git a/src/assets/i18n/nl/strings.yml b/src/assets/i18n/nl/strings.yml index 9d53f1d..2295332 100644 --- a/src/assets/i18n/nl/strings.yml +++ b/src/assets/i18n/nl/strings.yml @@ -19,6 +19,10 @@ error : - 'Bericht = "$[err:message]"' - '```' cooldown: '$[emoji#0] $[author:mention] **>** Dit commando is op cooldown voor **$[cooldown:left]**' +votelock: + - '$[emoji#0] Dit commando is **votelocked**, stem voor [**`Wump`**](https://discordbots.org/bot/318057009188438016/vote) om dit commando/parameters te ontgrendelen (Het kan tot 10 minuten duren om uw stem te registreren)' + - '' + - '$[emoji#1] Als u nogsteeds problemen hebt ná dat, kunt u een [**`issue`**](https://github.com/PassTheWessel/wump/issues) maken op [**`GitHub`**](https://github.com/PassTheWessel/wump) of mijn [**`Discord`**](https://discord.gg/SV7DAE9) joinen en het daar te plaatsen' core : prefix : @@ -66,9 +70,11 @@ util : - '$[emoji#2] **Rondvaart**: $[roundtrip]ms' - '$[emoji#1] **Hartslag**: $[latency]ms' - '$[emoji#3] **Gezondheid**: $[health]' + math : busy : '$[emoji#0] Aam het berekenen, dit kan enige tijd duren...' blocked: ' De methode "$[math:function]" is om veiligheidsredenen uitgeschakeld ' + snipe : fail : '$[emoji#0] Geen snipebare berichten gevonden' footer : 'Sniped door $[author:tag] ($[author:id])' @@ -76,18 +82,20 @@ util : info : stats : fetching: '$[emoji#0] Statistieken aan het ophalen, dit kan enige tijd duren...' - avatar : '$[emoji#0] Hier is **$[user:full]** avatar' + + avatar : '$[emoji#0] Hier is **$[user:full]** avatar' + commands: multi : - '$[emoji#0] Het voorvoegsel voor **$[guild:name]** is `$[guild:prefix]`' - '$[emoji#1] typ `$[guild:prefix]command ` voor meer informatie' - '$[emoji#2] **Links**: [`Uitnodiging`]($[bot:invite]) **|** [`Ondersteuning`](https://discord.gg/SV7DAE9) **|** [`GitHub`](https://github.com/PassTheWessel/wump) **|** [`Patreon`](https://patreon.com/wessel)' single: - - '**Naam**: $[command:name]' - - '**Syntaxis**: $[command:syntax]' - - '**Aliassen**: $[command:aliases]' - - '**Cooldown**: $[command:cooldown#formatted]' - - '**Argumenten**: $[command:arguments]' + - '$[emoji#3] **Naam**: $[command:name]' + - '$[emoji#4] **Syntaxis**: $[command:syntax]' + - '$[emoji#5] **Aliassen**: $[command:aliases]' + - '$[emoji#6] **Cooldown**: $[command:cooldown#formatted]' + - '$[emoji#7] **Argumenten**: $[command:arguments]' fields: - 'Kern' - 'Informatie' @@ -123,12 +131,30 @@ info : - '$[emoji#14] Offline **⤏** `$[guild:users#offline]`' invalid: '$[emoji#15] Geen gilde gevonden met uw zoekopdracht' + user : + invalid : '$[emoji#11] Geen gebruiker gevonden met uw zoekopdracht' + fields : + self : + name : '❯ Gebruikers Informatie' + value: + - '$[emoji#0] **Label**: $[user:tag] (`$[user:id]`)' + - '$[emoji#1] **Creatie**: $[user:created@precise] ago (`$[user:created@exact]`)' + - '$[emoji#2] **Staat**: $[user:status@full]' + member: + name : '❯ Ledeninformatie' + value: + - '$[emoji#3] **Bijnaam**: $[member:nickname]' + - '$[emoji#4] **Aangesloten**: $[member:created@precise] ago (`$[member:created@exact]`)' + - '$[emoji#5] **Rollen**: $[member:roles]' + + image: fetching : '$[emoji#0] Een willekeurig plaatje aan het ophalen, dit kan enige tijd duren...' failed_cache: 'Klik hier als het plaatje niet is geladen' developer: echo : '$[emoji#0] Ongeldig bericht om te echoën' + eval : args : '$[emoji#0] Ongeldig codefragment om te evalueren' busy : '$[emoji#1] Aan het evalueren, Dit kan enige tijd duren...' @@ -140,4 +166,17 @@ developer: - '$[emoji#3] **Resultaat** (`$[result:type]`):' - '```js' - '$[result:message]' + - '```' + + exec : + args : '$[emoji#0] Ongeldig commando om uit te voeren' + busy : '$[emoji#1] Aan het uitvoeren, dit kan enige tijd duren...' + silent: '$[emoji#2] Commando stil uitgevoerd' + types : + - 'geslaagd' + - 'mislukt' + result: + - '$[emoji#3] **Resultaat** (`$[result:type]`):' + - '```sh' + - '$[result:message]' - '```' \ No newline at end of file diff --git a/src/assets/img/emoji/Audit logs/create_role.png b/src/assets/img/emoji/Audit logs/create_role.png new file mode 100644 index 0000000000000000000000000000000000000000..82e6d59a2e35549d6ec3c78cbafb4b6534979f50 GIT binary patch literal 6087 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 DtXN3Z literal 0 HcmV?d00001 diff --git a/src/assets/img/emoji/send.png b/src/assets/img/emoji/send.png new file mode 100644 index 0000000000000000000000000000000000000000..a0ac89500e8de0b23ee33b937dee61a8e67f4025 GIT binary patch literal 649 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAk#mH?j+S0G*7eSK)5+TDEWKzCc0 z1o;Is9FJxD`=Uwm*D}jfjTZkTmY$3;7c>qnpSk<)Zx$t&w2oD0-aT*lsqC8Gx%%w; zC1xgJ6|?r-s~3-EJ*J*}R)m3p(bLn#F(l&f+v&H14mt3&apy8NPK$b?pjDxDD$wz$ z^S%Fjb&Pp}7wn#$Q*_YvKl=~0_;Pb!ndKf6KYOoybE?7SgLbH-fWzBGddyyz6Qtic zFw||_yogsl%XPh2t(Pf6xj6~OB#JV`O*v3a!7f-44GDGr%> z$>D4*#w;;s+8X)}3UFMMV$$5YVl7i_>8`#1zCLg}_2FBd#jg5_nn_m9DUDLjMU6Y6 zyR<%7W=@awmJFMjXbtWGeHw>PxelF{r5}E+B%Qi#+ literal 0 HcmV?d00001 diff --git a/src/assets/img/thumb/votelocked.png b/src/assets/img/thumb/votelocked.png new file mode 100644 index 0000000000000000000000000000000000000000..77161868fa776a64a2bfd2aded5e0b18b75fd527 GIT binary patch literal 13131 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 literal 0 HcmV?d00001 diff --git a/src/commands/Discord/Core/locale.js b/src/commands/Discord/Core/locale.js index b47bbd7..5d65c63 100644 --- a/src/commands/Discord/Core/locale.js +++ b/src/commands/Discord/Core/locale.js @@ -1,7 +1,5 @@ const { DiscordCommand } = require('../../../core'); -const larg = require('larg'); - module.exports = class Locale extends DiscordCommand { constructor(bot) { super(bot, { @@ -13,7 +11,7 @@ module.exports = class Locale extends DiscordCommand { hidden : false, enabled : true, - cooldown : 1000, + cooldown : 10000, category : 'Core', ownerOnly : false, guildOnly : false, @@ -26,11 +24,20 @@ module.exports = class Locale extends DiscordCommand { if (!guild || guild === null) guild = await this.bot.m.connection.collection('dGuilds').findOne({ guildId: msg.channel.guild.id }); if (args.join(' ').includes('-u') || args.join(' ').includes('--user')) { - for (let i = 0; i < args.length; i++) !this.bot.locales.has(args[i]) ? args.splice(i, -1) : undefined; - if (!this.bot.locales.has(args[0])) return msg.channel.createMessage(this.localize(msg.author.locale['core']['locale']['invalid'])); - if (user.locale === args[0]) return msg.channel.createMessage(this.localize(msg.author.locale['core']['locale']['dupe'], { uLocale: user.locale })); + for (let _ = 0; _ < args.length; _++) { + if (!this.bot.locales.has(args[_])) args.splice(_, -1); + }; + + if (!this.bot.locales.has(args[0])) { + return msg.channel.createMessage(this.localize(msg.author.locale['core']['locale']['invalid'])); + } + if (user.locale === args[0]) { + return msg.channel.createMessage(this.localize(msg.author.locale['core']['locale']['dupe'], { uLocale: user.locale || 'en_us' })); + } - this.bot.m.connection.collection('dUsers').findOneAndUpdate({ 'userId': user.userId }, { $set: { locale: args[0] } }, (err) => { if (err) throw err; }); + this.bot.m.connection.collection('dUsers').findOneAndUpdate({ 'userId': user.userId }, { $set: { locale: args[0] } }, (err) => { + if (err) throw err; + }); this.bot.cache.get('users').some((v, _) => { if (v['userId'] === msg.author.id) this.bot.cache.get('users').splice(_, 1); }); msg.channel.createMessage(this.localize(this.bot.locales.get(args[0])['core']['locale']['changed'], { uLocale: args[0] })); } else if (args.join(' ').includes('-g') || args.join(' ').includes('--guild')) { diff --git a/src/commands/Discord/Developer/echo.js b/src/commands/Discord/Developer/echo.js index 1b008bd..246094d 100644 --- a/src/commands/Discord/Developer/echo.js +++ b/src/commands/Discord/Developer/echo.js @@ -1,13 +1,13 @@ const { DiscordCommand } = require('../../../core'); -module.exports = class Eval extends DiscordCommand { +module.exports = class Echo extends DiscordCommand { constructor(bot) { super(bot, { name : 'echo', syntax : 'echo <...message:str>', aliases : [], argument : [ '<...message:str>' ], - description : 'Evaluate a snippet', + description : 'Repeat your input', hidden : false, enabled : true, @@ -26,7 +26,9 @@ module.exports = class Eval extends DiscordCommand { localize(msg) { // Empty - if (!msg) return ''; + if (!msg) { + return ''; + } // Main return msg.replace(/\$\[emoji#0]/g, this.bot.emote('developer', 'echo')); } diff --git a/src/commands/Discord/Developer/exec.js b/src/commands/Discord/Developer/exec.js new file mode 100644 index 0000000..5a4b346 --- /dev/null +++ b/src/commands/Discord/Developer/exec.js @@ -0,0 +1,87 @@ +const { DiscordCommand } = require('../../../core'); + +const { execSync } = require('child_process'); + +module.exports = class Exec extends DiscordCommand { + constructor(bot) { + super(bot, { + // Information + name : 'exec', + syntax : 'exec <...cmd:str>', + aliases : [], + argument : [ ], + description: 'Execute a command', + // Checks + hidden : false, + enabled : true, + cooldown : 0, + category : 'Developer', + ownerOnly : true, + guildOnly : false, + permissions: [ 'embedLinks' ] + }); + } + + async execute(msg, args, user, guild) { + let result = '$'; + + let raw = args.join(' ').trim().endsWith('--raw') ? args.pop() : false; + let silent = args.join(' ').trim().endsWith('--silent') || args.join(' ').trim().endsWith('-s') ? args.pop() : false; + let errored = false; + + if (args.length <= 0) return msg.channel.createMessage(this.localize(msg.author.locale['developer']['exec']['args'])); + + const message = await msg.channel.createMessage(this.localize(msg.author.locale['developer']['exec']['busy'])); + + try { result = execSync(args.join(' '), { timeout: 100000 }); } + catch (ex) { result = ex; errored = true; } + finally { + if (silent) return message.edit(this.localize(msg.author.locale['developer']['exec']['silent'])); + result = this.bot.util.escapeMarkdown(this.sanitize(String(result))); + + if (raw) return msg.channel.createMessage(`\`\`\`sh\n${result}\`\`\``); + message.edit({ + content: '', + embed: { + color : (errored ? this.bot.col['developer']['exec']['failure'] : this.bot.col['developer']['exec']['success']), + description: this.localize(msg.author.locale['developer']['exec']['result'].join('\n'), { resultType: errored ? msg.author.locale['developer']['exec']['types'][1] : msg.author.locale['developer']['exec']['types'][0], resultMessage: this.bot.util.shorten(result, 1850) || '$' }) + } + }); + }; + } + + sanitize(msg) { + // Return nothing if empty + if (!msg) return msg; + // API tokens + for (let _ in this.bot.conf['api']) { + msg = msg.replace(new RegExp(this.bot.conf['api'][_], 'gi'), '<--snip-->'); + } + // Webhook information + for (let _ in this.bot.conf.discord.webhook) { + msg = msg.replace(new RegExp(this.bot.conf.discord.webhook[_], 'gi'), '<--snip-->'); + } + // Bot tokens + return msg + .replace(new RegExp(this.bot.token, 'gi'), '<--snip-->') + .replace(new RegExp(this.bot.conf['discord']['token'], 'gi'), '<--snip-->'); + } + + localize(msg, extData) { + // Empty + if (!msg) return ''; + // extData + if (extData && extData.resultType) { + msg = msg.replace(/\$\[result:type]/g, extData.resultType); + } + if (extData && extData.resultMessage) { + msg = msg.replace(/\$\[result:message]/g, extData.resultMessage); + } + // Main + return msg + .replace(/\$\[emoji#0]/g, this.bot.emote('developer', 'exec', '0')) + .replace(/\$\[emoji#1]/g, this.bot.emote('developer', 'exec', '1')) + .replace(/\$\[emoji#2]/g, this.bot.emote('developer', 'exec', '2')) + .replace(/\$\[emoji#3]/g, this.bot.emote('developer', 'exec', '3')); + } +}; diff --git a/src/commands/Discord/Information/avatar.js b/src/commands/Discord/Information/avatar.js index c61fc94..cba0cce 100644 --- a/src/commands/Discord/Information/avatar.js +++ b/src/commands/Discord/Information/avatar.js @@ -1,9 +1,10 @@ const { DiscordCommand } = require('../../../core'); -const w = require('wumpfetch'); -const sizeOf = require('../../../util/sizeOf'); -const { extname } = require('path'); - +const w = require('wumpfetch'); +const sizeOf = require('../../../util/sizeOf'); +const { extname } = require('path'); +const { readFileSync } = require('fs'); +// Sharp is an optional dependency let s; try { s = require('sharp'); } catch (ex) { throw new Error('Sharp not found, not loading this command'); } @@ -12,9 +13,9 @@ module.exports = class Avatar extends DiscordCommand { constructor(bot) { super(bot, { name : 'avatar', - syntax : 'avatar [...user:string] [--large] [--(nearest|linear)]', + syntax : 'avatar [...user:string] [--large] [--(nearest|linear)] [--greyscale] [--jpeg] [--blur]', aliases : [ 'avt', 'pfp' ], - argument : [ '[...user:string]', '[--large]', '[--(nearest|linear)]' ], + argument : [ '[...user:string]', '[--large]', '[--(nearest|linear)]', '[--greyscale]', '[--jpeg]', '[--blur]' ], description: 'Get a user\'s avatar', hidden : false, @@ -25,56 +26,127 @@ module.exports = class Avatar extends DiscordCommand { guildOnly : false, permissions: [ 'embedLinks' ] }); + + this.mutable = { + reg: [ /--large/i, /--(nearest|linear)/i, /--greyscale/i, /--jpeg/i, /--blur/i ], + req: { + headers: { + 'User-Agent' : this.bot.ua, + 'Authorization': this.bot.conf.api.dbl + } + } + }; - this.regex = [/--large/i, /--(nearest|linear)/i, /--greyscale/i]; + try { this.mutable.thumb = readFileSync('./assets/img/thumb/votelocked.png'); } + catch(ex) { this.thumb = undefined; } } async execute(msg, args) { + // Resizing method, original args, extras and edited + const oArgs = args; + const edited = this.mutable.reg[0].test(oArgs) || this.mutable.reg[1].test(oArgs) || this.mutable.reg[2].test(oArgs) || this.mutable.reg[3].test(oArgs) || this.mutable.reg[4].test(oArgs); + const extras = `${this.mutable.reg[2].exec(oArgs) ? 'GREYSCALE' : ''} ${this.mutable.reg[3].exec(oArgs) ? 'JPEG' : ''} ${this.mutable.reg[4].exec(oArgs) ? 'BLUR' : ''}`; const resize = - this.regex[1].test(args) && this.regex[1].exec(args)[0].toLowerCase() === '--nearest' ? 'NEAREST' : - this.regex[1].test(args) && this.regex[1].exec(args)[0].toLowerCase() === '--linear' ? 'LINEAR_A' : 'LINEAR_B'; - - const oldArgs = args; - args = args.join(' ').trim().replace(this.regex[0], '').replace(this.regex[1], '').replace(this.regex[2], '').split(' ').filter((v) => { return v !== ''; }); - let user = this.bot.REST.getUser(args[0] ? args.join(' ') : msg.author.id); - if (!user) user = this.bot.users.get(msg.author.id); + this.mutable.reg[1].test(args) && this.mutable.reg[1].exec(args)[0].toLowerCase() === '--nearest' ? 'NEAREST' : + this.mutable.reg[1].test(args) && this.mutable.reg[1].exec(args)[0].toLowerCase() === '--linear' ? 'LINEAR_A' : 'LINEAR_B'; + // Strip args + args = args + .join(' ') + .trim() + .replace(this.mutable.reg[0], '') + .replace(this.mutable.reg[1], '') + .replace(this.mutable.reg[2], '') + .replace(this.mutable.reg[3], '') + .replace(this.mutable.reg[4], '') + .split(' ') + .filter((v) => { return v !== ''; }); + // Fetch user + let user = this.bot.REST.getUser(args[0] ? args.join(' ') : msg.author.id); + if (!user) user = this.bot.users.get(msg.author.id); + // Base avatar URL of `user` + let img; + const avatarURL = (user.avatar ? user.avatarURL : user.defaultAvatarURL).split('?')[0]; - let img, dim; - const avatarURL = (user.avatar ? user.avatarURL : user.defaultAvatarURL).split('?')[0]; + if (edited) { + // Image manipulation + const manipulate = async () => { + let tmp, dim; + // Fetching avatar of `user` + tmp = await w(avatarURL).send(); + tmp = tmp.body; + // Resizing + dim = sizeOf(tmp); + tmp = await s(tmp) + .resize(dim.width * 2, dim.height * 2, { kernel: resize === 'LINEAR_B' ? s.kernel.linearB : resize === 'LINEAR_A' ? s.kernel.linearA : resize === 'NEAREST' ? s.kernel.nearest : s.kernel.linearA }) // { fit: 'inside' } + .toBuffer(); + // Utility customization + if (this.mutable.reg[2].test(oArgs)) tmp = await s(tmp).greyscale().toBuffer(); + if (this.mutable.reg[3].test(oArgs)) tmp = await s(tmp).jpeg({ quality: 5 }).toBuffer(); + if (this.mutable.reg[4].test(oArgs)) tmp = await s(tmp).blur(4).toBuffer(); - if (this.regex[0].test(oldArgs.join(' '))) { - img = await w(avatarURL).send(); - img = img.body; - - dim = sizeOf(img); - img = await s(img) - .resize(dim.width * 2, dim.height * 2, { kernel: resize === 'LINEAR_B' ? s.kernel.linearB : resize === 'LINEAR_A' ? s.kernel.linearA : resize === 'NEAREST' ? s.kernel.nearest : s.kernel.linearA }) // { fit: 'inside' } - .toBuffer(); - if (this.regex[2].exec(args)) img = await s(img).greyscale().toBuffer(); + return tmp; + }; + // Vote lock + const voters = this.bot.cache.get('votes') ? this.bot.cache.get('votes') : this.bot.cache.set('votes', []) && this.bot.cache.get('votes'); + if (!this.bot.op(msg.author.id) && !voters.includes(msg.author.id)) { + // Fetch votes + const req = await w(`https://discordbots.org/api/bots/318057009188438016/check?userId=${msg.author.id}`, this.mutable.req).send(); + const res = req.json(); + // Succeed if code isn't `200` + if(req.statusCode !== 200) { + img = await manipulate(); + } else { + if (res.voted === 0) { + // User didn't vote + return msg.channel.createMessage({ + embed: { + color : this.bot.col['votelock'], + thumbnail : { url: 'attachment://thumb.png' }, + description: this.localize(msg.author.locale['votelock'].join('\n'), { votelocked: true }) + } + }, this.mutable.thumb ? { file: this.mutable.thumb, name: 'thumb.png' } : undefined); + } else if (res.voted === 1) { + // User voted + img = await manipulate(); + voters.push(msg.author.id); + } + } + } else { + img = await manipulate(); + } } else { + // Fetching avatar of `user` img = await w(avatarURL).send(); img = img.body; } - if (this.regex[2].test(oldArgs)) img = await s(img).greyscale().toBuffer(); - msg.channel.createMessage({ embed: { - image: { url: `attachment://thumb${extname(avatarURL)}` }, - color: this.bot.col['info']['avatar'], - description: `${this.localize(msg.author.locale['info']['avatar'], { user: user })} (\`${this.regex[0].test(oldArgs.join(' ')) ? 'LARGE' : 'DEFAULT'} ${resize} ${this.regex[2].exec(oldArgs) ? 'GREYSCALE' : ''}\`)` + image : { url: `attachment://thumb${edited ? '.jpg' : extname(avatarURL)}` }, + color : this.bot.col['info']['avatar'], + description: `${this.localize(msg.author.locale['info']['avatar'], { user: user })} (\`${this.mutable.reg[0].test(oArgs.join(' ')) ? 'LARGE' : 'DEFAULT'} ${resize} ${extras}\`)` } }, { file: img, - name: `thumb${extname(avatarURL)}` + name: `thumb${edited ? '.jpg' : extname(avatarURL)}` }); } localize(msg, extData) { if (!msg) return ''; + + if (extData) { + if (extData.user) { + msg = msg.replace(/\$\[user:full]/g, `${extData.user.username}'${!extData.user.username.toLowerCase().endsWith('s') ? 's' : ''}`); + } + if (extData.votelocked) { + return msg + .replace(/\$\[emoji#0]/g, this.bot.emote('votelock', '0')) + .replace(/\$\[emoji#1]/g, this.bot.emote('votelock', '1')); + } + } return msg - .replace(/\$\[user:full]/g, `${extData.user.username}'${!extData.user.username.toLowerCase().endsWith('s') ? 's' : ''}`) .replace(/\$\[emoji#0]/g, this.bot.emote('info', 'avatar')); } }; \ No newline at end of file diff --git a/src/commands/Discord/Information/commands.js b/src/commands/Discord/Information/commands.js index cb42fd0..210dd6b 100644 --- a/src/commands/Discord/Information/commands.js +++ b/src/commands/Discord/Information/commands.js @@ -93,6 +93,11 @@ module.exports = class Commands extends DiscordCommand { return msg .replace(/\$\[emoji#0]/g, this.bot.emote('info', 'commands', '0')) .replace(/\$\[emoji#1]/g, this.bot.emote('info', 'commands', '1')) - .replace(/\$\[emoji#2]/g, this.bot.emote('info', 'commands', '2')); + .replace(/\$\[emoji#2]/g, this.bot.emote('info', 'commands', '2')) + .replace(/\$\[emoji#3]/g, this.bot.emote('info', 'commands', '3')) + .replace(/\$\[emoji#4]/g, this.bot.emote('info', 'commands', '4')) + .replace(/\$\[emoji#5]/g, this.bot.emote('info', 'commands', '5')) + .replace(/\$\[emoji#6]/g, this.bot.emote('info', 'commands', '6')) + .replace(/\$\[emoji#7]/g, this.bot.emote('info', 'commands', '7')); } }; \ No newline at end of file diff --git a/src/commands/Discord/Information/guild.js b/src/commands/Discord/Information/guild.js index 1b1ef60..747dec1 100644 --- a/src/commands/Discord/Information/guild.js +++ b/src/commands/Discord/Information/guild.js @@ -66,14 +66,16 @@ module.exports = class Guild extends DiscordCommand { o.cs = [ o.c.years >= 1 ? `${o.c.years} years` : undefined, o.c.months >= 1 ? `${o.c.months} months` : undefined, - o.c.days >= 1 ? `${o.c.days} days` : undefined + o.c.days >= 1 ? `${o.c.days} days` : undefined, + o.days <= 0 && o.months <= 0 && o.years <= 0 ? 'Less than a day' : undefined ].join(' '); extData.guild.c = moment.preciseDiff(new Date(), new Date(extData.guild.createdAt), true); extData.guild.cs = [ extData.guild.c.years >= 1 ? `${extData.guild.c.years} years` : undefined, extData.guild.c.months >= 1 ? `${extData.guild.c.months} months` : undefined, - extData.guild.c.days >= 1 ? `${extData.guild.c.days} days` : undefined + extData.guild.c.days >= 1 ? `${extData.guild.c.days} days` : undefined, + extData.guild.days <= 0 && extData.guild.months <= 0 && extData.guild.years <= 0 ? 'Less than a day' : undefined ].join(' '); msg = msg diff --git a/src/commands/Discord/Information/user.js b/src/commands/Discord/Information/user.js new file mode 100644 index 0000000..a2aab31 --- /dev/null +++ b/src/commands/Discord/Information/user.js @@ -0,0 +1,111 @@ +const { DiscordCommand } = require('../../../core'); + +const m = require('moment'); require('../../../util/moment/diff.js'); + +module.exports = class Guild extends DiscordCommand { + constructor(bot) { + super(bot, { + name : 'user', + syntax : 'user <...user:string>', + aliases : [ '<...user:string>' ], + argument : [], + description : 'Get a user\'s information', + + hidden : false, + enabled : true, + cooldown : 1000, + category : 'Information', + ownerOnly : false, + guildOnly : false, + permissions : [ 'embedLinks' ] + }); + + this.mutable.status = { + '_unknown': `${this.bot.emote('info', 'user', '6')}Unknown`, + 'online' : `${this.bot.emote('info', 'user', '7')}online`, + 'idle' : `${this.bot.emote('info', 'user', '8')}away`, + 'dnd' : `${this.bot.emote('info', 'user', '9')}do not disturb`, + 'offline' : `${this.bot.emote('info', 'user', '10')}offline` + }; + } + + async execute(msg, args) { + const u = this.bot.REST.getUser((args.length >= 1 ? args.join(' ') : msg.author.id)); + if (!u) return msg.channel.createMessage(this._localize(msg.author.locale['info']['user']['invalid'])); + + const mem = msg.channel.guild && msg.channel.guild.members.get(u.id) ? msg.channel.guild.members.get(u.id) : undefined; + + const structure = { + embed: { + color: this.bot.col['info']['guild'], + thumbnail: { + url: u.avatar ? u.avatarURL : u.defaultAvatarURL + }, + fields: [ + { + name: msg.author.locale['info']['user']['fields']['self']['name'], + value: this._localize(msg.author.locale['info']['user']['fields']['self']['value'].join('\n'), { user: u, member: mem }), + inline: true + } + ] + } + }; + + if (mem) { + structure.embed.fields.push({ + name: msg.author.locale['info']['user']['fields']['member']['name'], + value: this._localize(msg.author.locale['info']['user']['fields']['member']['value'].join('\n'), { user: u, member: mem, guildId: msg.channel.guild.id }), + inline: true + }); + } + + msg.channel.createMessage(structure); + } + + _localize(msg, extData) { + if (!msg) return ''; + if (extData) { + if (extData.user) { + const u = extData.user; + u.c = m.preciseDiff(new Date(), new Date(u.createdAt), true); + u.cs = [ + u.c.years >= 1 ? `${u.c.years} years` : undefined, + u.c.months >= 1 ? `${u.c.months} months` : undefined, + u.c.days >= 1 ? `${u.c.days} days` : undefined, + u.c.days <= 0 && u.c.months <= 0 && u.c.years <= 0 ? 'Less than a day' : undefined + ].join(' '); + + msg = msg + .replace(/\$\[user:id]/g , u.id) + .replace(/\$\[user:tag]/g , `${u.username}#${u.discriminator}`) + .replace(/\$\[user:created@exact]/g, m(u.createdAt).format('YYYY[/]MM[/]DD HH[:]mm')) + .replace(/\$\[user:created@precise]/g, u.cs) + .replace(/\$\[user:status@full]/g, extData.member && this.mutable.status[extData.member.status] ? this.mutable.status[extData.member.status] : this.mutable.status['_unknown']); + } + if (extData.member) { + const mem = extData.member; + mem.c = m.preciseDiff(new Date(), new Date(mem.joinedAt), true); + mem.cs = [ + mem.c.years >= 1 ? `${mem.c.years} years` : undefined, + mem.c.months >= 1 ? `${mem.c.months} months` : undefined, + mem.c.days >= 1 ? `${mem.c.days} days` : undefined, + mem.c.days <= 0 && mem.c.months <= 0 && mem.c.years <= 0 ? 'Less than a day' : undefined + ].join(' '); + msg = msg + .replace(/\$\[member:nickname]/g, mem && mem.nick ? this.bot.util.escapeMarkdown(mem.nick) : '*No nickname set*') + .replace(/\$\[member:roles]/g, mem.roles.length >= 1 ? mem.roles.map((v) => `<@&${v}>`).slice(0, 25).join(' **/** ') : 'n/a') + .replace(/\$\[member:created@exact]/g, m(mem.joinedAt).format('YYYY[/]MM[/]DD HH[:]mm')) + .replace(/\$\[member:created@precise]/g, mem.cs); + } + } + + return msg + .replace(/\$\[emoji#0]/g, this.bot.emote('info', 'user', '0')) + .replace(/\$\[emoji#1]/g, this.bot.emote('info', 'user', '1')) + .replace(/\$\[emoji#2]/g, this.bot.emote('info', 'user', '2')) + .replace(/\$\[emoji#3]/g, this.bot.emote('info', 'user', '3')) + .replace(/\$\[emoji#4]/g, this.bot.emote('info', 'user', '4')) + .replace(/\$\[emoji#5]/g, this.bot.emote('info', 'user', '5')) + .replace(/\$\[emoji#11]/g, this.bot.emote('info', 'user', '11')); + } +}; \ No newline at end of file diff --git a/src/core/internal/Discord/Command.js b/src/core/internal/Discord/Command.js index 5e0392e..776acd7 100644 --- a/src/core/internal/Discord/Command.js +++ b/src/core/internal/Discord/Command.js @@ -17,9 +17,13 @@ module.exports = class WumpCommand { guildOnly : false, // Guild only ( true / false ) permissions : [] // Bot permissions ( optional ) }, opts); + + this.static = {}; + this.mutable = {}; + Object.freeze(this.static); } async execute(msg, args, user, guild) {} - localize(msg) {} + _localize(msg) {} }; \ No newline at end of file diff --git a/src/core/internal/Discord/Event.js b/src/core/internal/Discord/Event.js index dafcd31..d3162c5 100644 --- a/src/core/internal/Discord/Event.js +++ b/src/core/internal/Discord/Event.js @@ -6,5 +6,5 @@ module.exports = class WumpEvent { emit(...args) {} - localize(msg) {} + _localize(msg) {} }; \ No newline at end of file diff --git a/src/core/internal/Discord/__baseCommand.js b/src/core/internal/Discord/__baseCommand.js index 63432bb..bb6bd96 100644 --- a/src/core/internal/Discord/__baseCommand.js +++ b/src/core/internal/Discord/__baseCommand.js @@ -21,5 +21,5 @@ module.exports = class name extends DiscordCommand { async execute(msg, args, user, guild) {} - localize(msg) {} + _localize(msg) {} }; \ No newline at end of file diff --git a/src/core/internal/Discord/rest.js b/src/core/internal/Discord/rest.js index 3a548dc..fd347d2 100644 --- a/src/core/internal/Discord/rest.js +++ b/src/core/internal/Discord/rest.js @@ -73,6 +73,7 @@ module.exports = class RESTclient { } getMember($, g, n = false) { + if (!$ || !g) return undefined; if (/^\d+$/.test($) && g.members.get($)) return g.members.get($); else if (/^<@!?(\d+)>$/.test($)) { const m = $.match(/^<@!?(\d+)>$/); diff --git a/src/core/registry/Discord/Commands.js b/src/core/registry/Discord/Commands.js index 7bb5114..82577e8 100644 --- a/src/core/registry/Discord/Commands.js +++ b/src/core/registry/Discord/Commands.js @@ -33,13 +33,13 @@ module.exports = class CommandRegistry { for (let i = 0; i < categories.length; i++) { if (categories[i].endsWith('md')) return; readdir(`${directory}/${categories[i]}`, (err, cmds) => { - if (err) process.handleError(err, cyan('Discord'), 'LoadError'); + if (err) process.handleError(err, 'LoadError', cyan('Discord')); this.bot.print(1, `${cyan('Discord')} >> Loading ${green(cmds.length)} commands from category ${yellow(categories[i])}` ); cmds.forEach((cmd) => { try { this.start(directory, categories[i], cmd); - } catch(ex) { process.handleError(err, cyan('Discord'), 'LoadError'); } + } catch(ex) { process.handleError(err, 'LoadError', cyan('Discord')); } }); }); } @@ -167,7 +167,7 @@ module.exports = class CommandRegistry { this.bot.print(3, `${cyan('Discord')} >> event "${yellow('COMMAND_EXECUTE')}" fired`); this.bot.print(3, `${cyan(`DShard #${msg.channel.guild ? msg.channel.guild.shard.id : 0}`)} >> Command '${cmd.extData.name}' executed by ${yellow(msg.author.id)}`); } catch (ex) { - process.handleError(ex, cyan('Discord'), 'CommandError'); + process.handleError(ex, 'CommandError', cyan('Discord')); msg.channel.createMessage({ embed: { color : this.bot.col['error'], diff --git a/src/core/registry/Discord/Events.js b/src/core/registry/Discord/Events.js index ccacd16..428555a 100644 --- a/src/core/registry/Discord/Events.js +++ b/src/core/registry/Discord/Events.js @@ -9,7 +9,7 @@ module.exports = class EventStore { execute(event) { const execAsync = async(...args) => { try { await event.emit(...args); } - catch (ex) { process.handleError(ex, cyan('Discord'), 'EventError'); } + catch (ex) { process.handleError(ex, 'EventError', cyan('Discord')); } }; this.bot.on(event.extData.name, execAsync); diff --git a/src/core/registry/Discord/Locales.js b/src/core/registry/Discord/Locales.js index e8876ca..0bbd183 100644 --- a/src/core/registry/Discord/Locales.js +++ b/src/core/registry/Discord/Locales.js @@ -24,7 +24,7 @@ module.exports = class LocaleRegistry { } } } catch (ex) { - process.handleError(ex, cyan('Discord'), 'LoadError'); + process.handleError(ex, 'LoadError', cyan('Discord')); return { success: false, err: ex }; } diff --git a/src/main.js b/src/main.js index 4df084b..9051ec0 100644 --- a/src/main.js +++ b/src/main.js @@ -38,8 +38,8 @@ const col = safeLoad(readFileSync( 'assets/config/colors.yml')); const conf = safeLoad(readFileSync('application.yml')); print(3, `${magenta('Debug')} >> Configuration files loaded`); -process.on('uncaughtException', process.handleError); -process.on('unhandledRejection', process.handleError); +process.on('uncaughtException', (err) => process.handleError(err)); +process.on('unhandledRejection', (err) => process.handleError(err)); process.on('SIGINT', () => { if (Database) { try { @@ -67,7 +67,7 @@ process.on('SIGINT', () => { if (!conf['db']) process.handleError(undefined, undefined, undefined, 1, red('!! Failed to start: No database found !!')); print(2, `${yellow('Database' )} >> Connecting to database...`); Database = mongoose.connect(conf['db'], { useNewUrlParser: true }); - mongoose.connection.on('error', process.handleError); + mongoose.connection.on('error', (err) => process.handleError(err)); print(2, `${yellow('Database')} >> ${green('Connected to database')}` ); })(); diff --git a/src/util/Util.js b/src/util/Util.js index 53582b1..1eba873 100644 --- a/src/util/Util.js +++ b/src/util/Util.js @@ -7,7 +7,7 @@ const conf = yaml.safeLoad(fs.readFileSync('application.yml', { encoding: 'utf8' module.exports = class WumpUtil { static print(lvl = 0, msg = '') { - const log = `tmp\\log\\${moment(new Date).format('DD[-]MM[-]YYYY')}.log`; + const log = `tmp/log/${moment(new Date).format('DD[-]MM[-]YYYY')}.log`; fs.mkdir('tmp', 777, (err) => { if (err && err.code !== 'EEXIST') throw err; }); fs.mkdir('tmp/log', 777, (err) => { if (err && err.code !== 'EEXIST') throw err; }); @@ -35,13 +35,18 @@ module.exports = class WumpUtil { } } - static getPercentage(o, n) { return ( ( o - n ) / o ) * 100; } - - static shorten(t = '', m = 2000) { return t.length > m ? `${t.substr( 0, m - 3 )}...` : t; } - - static escapeMarkdown(t = '', oc = false, oi = false) { - if (oc) return t.replace(/```/g, '\u200b`\u200b`\u200b`'); - if (oi) return t.replace(/\\([`\\])/g, '$1').replace(/([`\\])/g, '\u200b$1'); - return t.replace(/([*_`~\\])/g , '\u200b$1').replace(/\\([*_`~\\])/g, '$1'); + static getPercentage(o, n) { + return (( o - n ) / o) * 100; } + + static shorten(t = '', m = 2000) { + return t.length > m ? `${t.substr( 0, m - 3 )}...` : t; + } + + static escapeMarkdown(text, onlyCodeBlock = false, onlyInlineCode = false) { + if (onlyCodeBlock) return text.replace(/```/g, '`\u200b``'); + if (onlyInlineCode) return text.replace(/\\(`|\\)/g, '$1').replace(/(`|\\)/g, '\\$1'); + return text.replace(/\\(\*|_|`|~|\\)/g, '$1').replace(/(\*|_|`|~|\\)/g, '\\$1'); + } + }; \ No newline at end of file