Repository: TheOdinProject/odin-bot-v2
Pull request #98 (2021-01-24 19:16:17)

Fix regexes and adjust tests for correct Discord mentions syntax
This PR solves this issue: 
- points.js commands regexes need to pass their tests https://github.com/TheOdinProject/odin-bot-v2/issues/92

After discussing it with Tatiana, I changed @user and @odin-bot to <@!1233456679> in the test file to align it with Discord's syntax for mentions and corrected some minor errors. That's also why the snapshots have changed. 

Other than that, I just adjusted the regexes to make them pass the tests. 

I think my Prettier replaced the double quotes by single quotes and removed unnecessary semi-colons and whitespace... Hope that's ok.


Patch:
diff --git a/botCommands/points.js b/botCommands/points.js
index 6d67def..46c629d 100644
--- a/botCommands/points.js
+++ b/botCommands/points.js
@@ -1,32 +1,29 @@
-const axios = require("axios");
-const config = require("../config.js");
-const { registerBotCommand } = require("../botEngine.js");
+const axios = require('axios');
+const config = require('../config.js');
+const { registerBotCommand } = require('../botEngine.js');
 
 const AWARD_POINT_REGEX = /<@!?(\d+)>\s?(\+\+|\u{2b50})/gu;
 
-axios.defaults.headers.post['Authorization'] = `Token ${config.pointsbot.token}`
+axios.defaults.headers.post.Authorization = `Token ${config.pointsbot.token}`;
 
 function getUserIdsFromMessage(text, regex) {
   const matches = [];
   let match;
-  while ((match = regex.exec(text)) !== null)
-    matches.push(match[1].replace("!", ""));
+  while ((match = regex.exec(text)) !== null) matches.push(match[1].replace('!', ''));
   return matches;
 }
 
-
 const deductPoints = {
-  regex: /@!?(\d+)>\s?(\-\-)/,
-  cb :  () =>
-  "http://media.riffsy.com/images/636a97aa416ad674eb2b72d4a6e9ad6c/tenor.gif"
-}
+  regex: /(?<!\S)<@!?(\d+)>\s?(\-\-)(?!\S)/,
+  cb: () => 'http://media.riffsy.com/images/636a97aa416ad674eb2b72d4a6e9ad6c/tenor.gif',
+};
 
-registerBotCommand(deductPoints.regex, deductPoints.cb)
+registerBotCommand(deductPoints.regex, deductPoints.cb);
 
 async function addPointsToUser(discord_id) {
   try {
     const pointsBotResponse = await axios.post(
-      `https://theodinproject.com/api/points?discord_id=${discord_id}`
+      `https://theodinproject.com/api/points?discord_id=${discord_id}`,
     );
     return pointsBotResponse.data;
   } catch (err) {
@@ -37,7 +34,7 @@ async function addPointsToUser(discord_id) {
 async function lookUpUser(discord_id) {
   try {
     const pointsBotResponse = await axios.get(
-      `https://theodinproject.com/api/points/${discord_id}`
+      `https://theodinproject.com/api/points/${discord_id}`,
     );
     return pointsBotResponse.data;
   } catch (err) {}
@@ -45,128 +42,144 @@ async function lookUpUser(discord_id) {
 
 function exclamation(points) {
   if (points < 5) {
-    return "Nice!";
-  } else if (points < 25) {
-    return "Sweet!";
-  } else if (points < 99) {
-    return "Woot!";
-  } else if (points < 105) {
-    return "HOLY CRAP!!";
-  } else if (points > 199 && points < 206) {
-    return "DAM SON:";
-  } else if (points > 299 && points < 306) {
-    return "OK YOU CAN STOP NOW:";
-  } else {
-    return "Woot!";
+    return 'Nice!';
+  } if (points < 25) {
+    return 'Sweet!';
+  } if (points < 99) {
+    return 'Woot!';
+  } if (points < 105) {
+    return 'HOLY CRAP!!';
+  } if (points > 199 && points < 206) {
+    return 'DAM SON:';
+  } if (points > 299 && points < 306) {
+    return 'OK YOU CAN STOP NOW:';
   }
+  return 'Woot!';
 }
 
 function plural(points) {
-  return points === 1 ? "point" : "points";
+  return points === 1 ? 'point' : 'points';
 }
 
 const awardPoints = {
-  regex: /<@!?(\d+)>\s?(\+\+|\u{2b50})/gu,
-  cb: async function pointsBotCommand({ author, content, channel, client, guild }) {
+  regex: /(?<!\S)<@!?(\d+)>\s?(\+\+)(?!\S)/,
+  cb: async function pointsBotCommand({
+    author,
+    content,
+    channel,
+    client,
+    guild,
+  }) {
     const userIds = getUserIdsFromMessage(content, AWARD_POINT_REGEX);
-    return Promise.all(userIds.map(async(userId, i) => {
-      // this limits the number of calls per message to 5 to avoid abuse
-      if (i > 4) {
-        return
-      }
-      if (i == 4 ) {
-        channel.send('you can only do 5 at a time..... ')
-      }
-      const user = await client.users.get(userId);
-      if (user == author) {
-        channel.send("http://media0.giphy.com/media/RddAJiGxTPQFa/200.gif");
-        channel.send("You can't do that!");
-        return;
-      } else if (user === client.user) {
-        channel.send("awwwww shucks... :heart_eyes:");
-        return;
-      }
-      try {
-        const pointsUser = await addPointsToUser(user.id);
-        if (user) {
-          const member = await guild.member(user)
-          if (member && !member.roles.find(r => r.name==="club-40") && pointsUser.points > 39) {
-            let pointsRole = guild.roles.find(r => r.name === "club-40")
-            member.addRole(pointsRole)
-            let clubChannel = client.channels.get('707225752608964628')
-        
-            if (clubChannel) {
-              clubChannel.send(`HEYYY EVERYONE SAY HI TO ${user} the newest member of CLUB 40`)
+    return Promise.all(
+      userIds.map(async (userId, i) => {
+        // this limits the number of calls per message to 5 to avoid abuse
+        if (i > 4) {
+          return;
+        }
+        if (i == 4) {
+          channel.send('you can only do 5 at a time..... ');
+        }
+        const user = await client.users.get(userId);
+        if (user == author) {
+          channel.send('http://media0.giphy.com/media/RddAJiGxTPQFa/200.gif');
+          channel.send("You can't do that!");
+          return;
+        } if (user === client.user) {
+          channel.send('awwwww shucks... :heart_eyes:');
+          return;
+        }
+        try {
+          const pointsUser = await addPointsToUser(user.id);
+          if (user) {
+            const member = await guild.member(user);
+            if (
+              member
+              && !member.roles.find((r) => r.name === 'club-40')
+              && pointsUser.points > 39
+            ) {
+              const pointsRole = guild.roles.find((r) => r.name === 'club-40');
+              member.addRole(pointsRole);
+              const clubChannel = client.channels.get('707225752608964628');
+
+              if (clubChannel) {
+                clubChannel.send(
+                  `HEYYY EVERYONE SAY HI TO ${user} the newest member of CLUB 40`,
+                );
+              }
             }
+            channel.send(
+              `${exclamation(pointsUser.points)} ${user} now has ${
+                pointsUser.points
+              } ${plural(pointsUser.points)}`,
+            );
           }
-          channel.send(
-            `${exclamation(pointsUser.points)} ${user} now has ${
-              pointsUser.points
-            } ${plural(pointsUser.points)}`
-          );
+        } catch (err) {
+          console.log(err);
         }
-      } catch (err) {console.log(err)}
-    }));
-  }
-}
+      }),
+    );
+  },
+};
 
 registerBotCommand(awardPoints.regex, awardPoints.cb);
 
 const points = {
-  regex: /\/points/,
-  cb : async function({
-    content,
-    client,
-    channel,
-    guild
+  regex: /(?<!\S)\/points(?!\S)/,
+  async cb({
+    content, client, channel, guild,
   }) {
     const userIds = getUserIdsFromMessage(content, /<@!?(\d+)>/g);
-    userIds.forEach(async userId => {
+    userIds.forEach(async (userId) => {
       const user = await client.users.get(userId);
       try {
         const userPoints = await lookUpUser(user.id);
-        const username = guild.members.get(userPoints.discord_id).displayName.replace(/\//g, "\\/");
+        const username = guild.members
+          .get(userPoints.discord_id)
+          .displayName.replace(/\//g, '\\/');
         if (userPoints) {
           channel.send(`${username} has ${userPoints.points} points!`);
         }
       } catch (err) {}
     });
-  }
-}
+  },
+};
 
-registerBotCommand(points.regex, points.cb)
+registerBotCommand(points.regex, points.cb);
 
 const leaderboard = {
-  regex: /\/leaderboard/,
-  cb: async function({ guild, content }) {
+  regex: /(?<!\S)\/leaderboard(?!\S)/,
+  async cb({ guild, content }) {
     try {
-      const sEquals = content.split(" ").find(word => word.includes("start="));
-      let start = sEquals ? sEquals.replace("start=", "") : 1;
+      const sEquals = content.split(' ').find((word) => word.includes('start='));
+      let start = sEquals ? sEquals.replace('start=', '') : 1;
       start = Math.max(start, 1);
-  
-      const nEquals = content.split(" ").find(word => word.includes("n="));
-      let length = nEquals ? nEquals.replace("n=", "") : 5;
+
+      const nEquals = content.split(' ').find((word) => word.includes('n='));
+      let length = nEquals ? nEquals.replace('n=', '') : 5;
       length = Math.min(length, 25);
       length = Math.max(length, 1);
-  
-      const users = await axios.get(`https://theodinproject.com/api/points`);
-      const data = users.data.filter(user => guild.members.get(user.discord_id));
-      let usersList = "**leaderboard** \n";
-      for (let i = (start-1); i < (length+start-1); i++) {
+
+      const users = await axios.get('https://theodinproject.com/api/points');
+      const data = users.data.filter((user) => guild.members.get(user.discord_id));
+      let usersList = '**leaderboard** \n';
+      for (let i = start - 1; i < length + start - 1; i++) {
         const user = data[i];
         if (user) {
           const member = guild.members.get(user.discord_id);
-          const username = member ? member.displayName.replace(/\//g, "\\/") : undefined;
+          const username = member
+            ? member.displayName.replace(/\//g, '\\/')
+            : undefined;
           if (username) {
             if (i == 0) {
-              usersList += `${i+1} - ${username} [${
+              usersList += `${i + 1} - ${username} [${
                 user.points
               } points] :tada: \n`;
             } else {
-              usersList += `${i+1} - ${username} [${user.points} points] \n`;
+              usersList += `${i + 1} - ${username} [${user.points} points] \n`;
             }
           } else {
-            usersList += 'UNDEFINED \n'
+            usersList += 'UNDEFINED \n';
           }
         }
       }
@@ -174,16 +187,16 @@ const leaderboard = {
     } catch (err) {
       throw new Error(err.message);
     }
-  }
-}
+  },
+};
 
-registerBotCommand(leaderboard.regex, leaderboard.cb)
+registerBotCommand(leaderboard.regex, leaderboard.cb);
 
 module.exports = {
   addPointsToUser,
-  awardPoints, 
+  awardPoints,
   deductPoints,
   getUserIdsFromMessage,
   points,
-  leaderboard
-}
+  leaderboard,
+};


Test patch:
diff --git a/botCommands/__snapshots__/points.test.js.snap b/botCommands/__snapshots__/points.test.js.snap
index 5f4ce5a..71738e0 100644
--- a/botCommands/__snapshots__/points.test.js.snap
+++ b/botCommands/__snapshots__/points.test.js.snap
@@ -55,36 +55,36 @@ exports[`/leaderboard callback returns correct output 8`] = `
 "
 `;
 
-exports[`@user ++ callback returns correct output for a single user entering club-40 1`] = `"HEYYY EVERYONE SAY HI TO <@2> the newest member of CLUB 40"`;
+exports[`<@!123456789> ++ callback returns correct output for a single user entering club-40 1`] = `"HEYYY EVERYONE SAY HI TO <@2> the newest member of CLUB 40"`;
 
-exports[`@user ++ callback returns correct output for a single user entering club-40 2`] = `"Woot! <@2> now has 40 points"`;
+exports[`<@!123456789> ++ callback returns correct output for a single user entering club-40 2`] = `"Woot! <@2> now has 40 points"`;
 
-exports[`@user ++ callback returns correct output for a single user w/o club-40 1`] = `"Sweet! <@2> now has 21 points"`;
+exports[`<@!123456789> ++ callback returns correct output for a single user w/o club-40 1`] = `"Sweet! <@2> now has 21 points"`;
 
-exports[`@user ++ callback returns correct output for a user mentioning Odin Bot 1`] = `"awwwww shucks... :heart_eyes:"`;
+exports[`<@!123456789> ++ callback returns correct output for a user mentioning Odin Bot 1`] = `"awwwww shucks... :heart_eyes:"`;
 
-exports[`@user ++ callback returns correct output for a user mentioning themselves 1`] = `"http://media0.giphy.com/media/RddAJiGxTPQFa/200.gif"`;
+exports[`<@!123456789> ++ callback returns correct output for a user mentioning themselves 1`] = `"http://media0.giphy.com/media/RddAJiGxTPQFa/200.gif"`;
 
-exports[`@user ++ callback returns correct output for a user mentioning themselves 2`] = `"You can't do that!"`;
+exports[`<@!123456789> ++ callback returns correct output for a user mentioning themselves 2`] = `"You can't do that!"`;
 
-exports[`@user ++ callback returns correct output for more than five mentioned users 1`] = `"you can only do 5 at a time..... "`;
+exports[`<@!123456789> ++ callback returns correct output for more than five mentioned users 1`] = `"you can only do 5 at a time..... "`;
 
-exports[`@user ++ callback returns correct output for more than five mentioned users 2`] = `"Sweet! <@2> now has 11 points"`;
+exports[`<@!123456789> ++ callback returns correct output for more than five mentioned users 2`] = `"Sweet! <@2> now has 11 points"`;
 
-exports[`@user ++ callback returns correct output for more than five mentioned users 3`] = `"Nice! <@2> now has 4 points"`;
+exports[`<@!123456789> ++ callback returns correct output for more than five mentioned users 3`] = `"Nice! <@2> now has 4 points"`;
 
-exports[`@user ++ callback returns correct output for more than five mentioned users 4`] = `"Nice! <@2> now has 2 points"`;
+exports[`<@!123456789> ++ callback returns correct output for more than five mentioned users 4`] = `"Nice! <@2> now has 2 points"`;
 
-exports[`@user ++ callback returns correct output for more than five mentioned users 5`] = `"Nice! <@2> now has 1 point"`;
+exports[`<@!123456789> ++ callback returns correct output for more than five mentioned users 5`] = `"Nice! <@2> now has 1 point"`;
 
-exports[`@user ++ callback returns correct output for more than five mentioned users 6`] = `"Sweet! <@2> now has 22 points"`;
+exports[`<@!123456789> ++ callback returns correct output for more than five mentioned users 6`] = `"Sweet! <@2> now has 22 points"`;
 
-exports[`@user ++ callback returns correct output for up to five mentioned users 1`] = `"Woot! <@2> now has 34 points"`;
+exports[`<@!123456789> ++ callback returns correct output for up to five mentioned users 1`] = `"Woot! <@2> now has 34 points"`;
 
-exports[`@user ++ callback returns correct output for up to five mentioned users 2`] = `"Sweet! <@2> now has 22 points"`;
+exports[`<@!123456789> ++ callback returns correct output for up to five mentioned users 2`] = `"Sweet! <@2> now has 22 points"`;
 
-exports[`@user ++ callback returns correct output for up to five mentioned users 3`] = `"Nice! <@2> now has 3 points"`;
+exports[`<@!123456789> ++ callback returns correct output for up to five mentioned users 3`] = `"Nice! <@2> now has 3 points"`;
 
-exports[`@user ++ callback returns correct output for up to five mentioned users 4`] = `"Nice! <@2> now has 1 point"`;
+exports[`<@!123456789> ++ callback returns correct output for up to five mentioned users 4`] = `"Nice! <@2> now has 1 point"`;
 
 exports[`@user -- callback returns correct output 1`] = `"http://media.riffsy.com/images/636a97aa416ad674eb2b72d4a6e9ad6c/tenor.gif"`;
diff --git a/botCommands/points.test.js b/botCommands/points.test.js
index d06c84d..67d8ad5 100644
--- a/botCommands/points.test.js
+++ b/botCommands/points.test.js
@@ -1,79 +1,65 @@
-const commands = require('./points')
-const {generateLeaderData} = require('./mockData')
-const axios = require('axios')
-const {Guild, Channel, Client, User} = require('discord.js')
-axios.post = jest.fn()
-
-const mockSend = jest.fn()
-mockSend.mockImplementation(message => {
- return message
-})
-
-jest.mock('discord.js', () => {
-  return {
-    Client : jest.fn().mockImplementation((users, channel, user) => {
-      return {
-        channels : {
-          get : () => channel
-        },
-        users : {
-          get : (userId) => users.filter(user => `<@${userId}>` === user.id)[0]
-        },
-        user : user
-      }
-    }),
-
-    Guild : jest.fn().mockImplementation((users) => {
-      return {
-        members : {
-          members : users,
-          get : (id) => {
-            return users.filter(member => member.discord_id === id)[0]
-          }
-        },
-        roles : [{name : "club-40"}],
-        member : (user) => {
-         return users.filter(member => member === user)[0]
-        }
-      }
-    }),
-
-    Channel : jest.fn().mockImplementation(() => {
-      return {
-        send: mockSend
-      }
-    }),
-
-    User : jest.fn().mockImplementation((roles, id, points) => {
-      return {
-        roles: roles,
-        id: `<@${id}>`,
-        points: points,
-        addRole: () => {
-          roles.push('club-40')
-        },
-        toString : () => `<@${id}>`
-      }
-    })
-  }
-})
+const axios = require('axios');
+const {
+  Guild, Channel, Client, User,
+} = require('discord.js');
+const commands = require('./points');
+const { generateLeaderData } = require('./mockData');
+
+axios.post = jest.fn();
+
+const mockSend = jest.fn();
+mockSend.mockImplementation((message) => message);
+
+jest.mock('discord.js', () => ({
+  Client: jest.fn().mockImplementation((users, channel, user) => ({
+    channels: {
+      get: () => channel,
+    },
+    users: {
+      get: (userId) => users.filter((user) => `<@${userId}>` === user.id)[0],
+    },
+    user,
+  })),
+
+  Guild: jest.fn().mockImplementation((users) => ({
+    members: {
+      members: users,
+      get: (id) => users.filter((member) => member.discord_id === id)[0],
+    },
+    roles: [{ name: 'club-40' }],
+    member: (user) => users.filter((member) => member === user)[0],
+  })),
+
+  Channel: jest.fn().mockImplementation(() => ({
+    send: mockSend,
+  })),
+
+  User: jest.fn().mockImplementation((roles, id, points) => ({
+    roles,
+    id: `<@${id}>`,
+    points,
+    addRole: () => {
+      roles.push('club-40');
+    },
+    toString: () => `<@${id}>`,
+  })),
+}));
 
 beforeEach(() => {
- axios.post.mockClear();
- mockSend.mockClear()
- User.mockClear()
+  axios.post.mockClear();
+  mockSend.mockClear();
+  User.mockClear();
 });
 
-describe('@user ++', ()=>{
-
-  describe('regex',()=> {
+describe('<@!123456789> ++', () => {
+  describe('regex', () => {
     it.each([
-      ['@odin-bot ++'],
-      ['thanks @odin-bot ++'],
-      ['@odin-bot++']
+      ['<@!123456789> ++'],
+      ['thanks <@!123456789> ++'],
+      ['<@!123456789>++'],
     ])('correct strings trigger the callback', (string) => {
-      expect(commands.awardPoints.regex.test(string)).toBeTruthy()
-    })
+      expect(commands.awardPoints.regex.test(string)).toBeTruthy();
+    });
 
     it.each([
       ['++'],
@@ -82,241 +68,276 @@ describe('@user ++', ()=>{
       [' /'],
       ['odin-bot++'],
       ['/++'],
-      ['```function("@odin-bot ++", () => {}```'],
+      ['```function("<@!123456789> ++", () => {}```'],
     ])("'%s' does not trigger the callback", (string) => {
-      expect(commands.leaderboard.regex.test(string)).toBeFalsy()
-    })
+      expect(commands.awardPoints.regex.test(string)).toBeFalsy();
+    });
 
     it.each([
-      ['Check this out! @odin-bot ++'],
-      ['Don\'t worry about it @odin-bot ++'],
-      ['Hey @odin-bot ++'],
-      ['/ @odin-bot++ ^ /me /leaderboard /tests$*']
+      ['Check this out! <@!123456789> ++'],
+      ["Don't worry about it <@!123456789> ++"],
+      ['Hey <@!123456789> ++'],
+      ['/ <@!123456789>++ ^ /me /leaderboard /tests$*'],
     ])("'%s' - command can be anywhere in the string", (string) => {
-      expect(commands.leaderboard.regex.test(string)).toBeTruthy()
-    })
-    
+      expect(commands.awardPoints.regex.test(string)).toBeTruthy();
+    });
+
     it.each([
       ['@user/++'],
-      ['it\'s about/@odin-bot++'],
-      ['@odin-bot++isanillusion'],
-      ['@odin-bot++/'],
-      ['@odin-bot++*'],
-      ['@odin-bot++...']
-    ])("'%s' - command should be its own word/group - no leading or trailing characters", (string) => {
-      expect(commands.leaderboard.regex.test(string)).toBeFalsy()
-    })
-  })
+      ["it's about/<@!123456789>++"],
+      ['<@!123456789>++isanillusion'],
+      ['<@!123456789>++/'],
+      ['<@!123456789>++*'],
+      ['<@!123456789>++...'],
+    ])(
+      "'%s' - command should be its own word/group - no leading or trailing characters",
+      (string) => {
+        expect(commands.awardPoints.regex.test(string)).toBeFalsy();
+      },
+    );
+  });
 
   describe('callback', () => {
-    const author = User([], 1, 10)
-    const channel = Channel()
-     
+    const author = User([], 1, 10);
+    const channel = Channel();
+
     it('returns correct output for a single user w/o club-40', async () => {
-      const mentionedUser = User([], 2, 20)
+      const mentionedUser = User([], 2, 20);
       // users must be passed in as an array
-      const client = Client([author, mentionedUser], channel)  
+      const client = Client([author, mentionedUser], channel);
       const data = {
-        author : author,
+        author,
         content: `${mentionedUser.id} ++`,
-        channel : channel,
-        client :  client,
-        guild : Guild([author, mentionedUser])
-      }
-
-      axios.post.mockResolvedValue({data: 
-        {
-          ...mentionedUser, 
-          points: mentionedUser.points +=1
-        }
-      })
-
-      await commands.awardPoints.cb(data)
-      expect(data.channel.send).toHaveBeenCalled()
-      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot()    
-    
-    })
+        channel,
+        client,
+        guild: Guild([author, mentionedUser]),
+      };
+
+      axios.post.mockResolvedValue({
+        data: {
+          ...mentionedUser,
+          points: (mentionedUser.points += 1),
+        },
+      });
+
+      await commands.awardPoints.cb(data);
+      expect(data.channel.send).toHaveBeenCalled();
+      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot();
+    });
 
     it('returns correct output for a single user entering club-40', async () => {
-      const mentionedUser = User([], 2, 39)
-      const client = Client([author, mentionedUser], channel)  
+      const mentionedUser = User([], 2, 39);
+      const client = Client([author, mentionedUser], channel);
       const data = {
-        author : author,
+        author,
         content: `${mentionedUser.id} ++`,
-        channel : channel,
-        client :  client,
-        guild : Guild([author, mentionedUser])
-      }
-
-      axios.post.mockResolvedValue({data: 
-        {
-          ...mentionedUser, 
-          points: mentionedUser.points +=1
-        }
-      })
-
-      await commands.awardPoints.cb(data)
-      expect(data.channel.send).toHaveBeenCalled()
-      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot()   
-      expect(data.channel.send.mock.calls[1][0]).toMatchSnapshot()    
-    })
+        channel,
+        client,
+        guild: Guild([author, mentionedUser]),
+      };
+
+      axios.post.mockResolvedValue({
+        data: {
+          ...mentionedUser,
+          points: (mentionedUser.points += 1),
+        },
+      });
+
+      await commands.awardPoints.cb(data);
+      expect(data.channel.send).toHaveBeenCalled();
+      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot();
+      expect(data.channel.send.mock.calls[1][0]).toMatchSnapshot();
+    });
 
     it('returns correct output for up to five mentioned users', async () => {
-      const mentionedUser1 = User([], 2, 33)
-      const mentionedUser2 = User([], 2, 21)
-      const mentionedUser3 = User([], 2, 2)
-      const mentionedUser4 = User([], 2, 0)
-      const client = Client([author, mentionedUser1, mentionedUser2, mentionedUser3, mentionedUser4], channel)  
+      const mentionedUser1 = User([], 2, 33);
+      const mentionedUser2 = User([], 2, 21);
+      const mentionedUser3 = User([], 2, 2);
+      const mentionedUser4 = User([], 2, 0);
+      const client = Client(
+        [
+          author,
+          mentionedUser1,
+          mentionedUser2,
+          mentionedUser3,
+          mentionedUser4,
+        ],
+        channel,
+      );
 
       const data = {
-        author : author,
+        author,
         content: `${mentionedUser1.id} ++ ${mentionedUser2.id} ++ ${mentionedUser3.id} ++ ${mentionedUser4.id} ++`,
-        channel : channel,
-        client :  client,
-        guild : Guild([author, mentionedUser1, mentionedUser2, mentionedUser3, mentionedUser4])
-      }
-
-      axios.post.mockResolvedValueOnce({data: 
-        {
-          ...mentionedUser1, 
-          points: mentionedUser1.points +=1
-        }
-      })
-      .mockResolvedValueOnce({data: 
-        {
-          ...mentionedUser2, 
-          points: mentionedUser2.points +=1
-        }
-      })
-      .mockResolvedValueOnce({data: 
-        {
-          ...mentionedUser3, 
-          points: mentionedUser3.points +=1
-        }
-      })
-      .mockResolvedValueOnce({data: 
-        {
-          ...mentionedUser4, 
-          points: mentionedUser4.points +=1
-        }
-      })
-
-      await commands.awardPoints.cb(data)
-      expect(data.channel.send).toHaveBeenCalledTimes(4)
-      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot()   
-      expect(data.channel.send.mock.calls[1][0]).toMatchSnapshot()    
-      expect(data.channel.send.mock.calls[2][0]).toMatchSnapshot()   
-      expect(data.channel.send.mock.calls[3][0]).toMatchSnapshot()    
-    })
+        channel,
+        client,
+        guild: Guild([
+          author,
+          mentionedUser1,
+          mentionedUser2,
+          mentionedUser3,
+          mentionedUser4,
+        ]),
+      };
+
+      axios.post
+        .mockResolvedValueOnce({
+          data: {
+            ...mentionedUser1,
+            points: (mentionedUser1.points += 1),
+          },
+        })
+        .mockResolvedValueOnce({
+          data: {
+            ...mentionedUser2,
+            points: (mentionedUser2.points += 1),
+          },
+        })
+        .mockResolvedValueOnce({
+          data: {
+            ...mentionedUser3,
+            points: (mentionedUser3.points += 1),
+          },
+        })
+        .mockResolvedValueOnce({
+          data: {
+            ...mentionedUser4,
+            points: (mentionedUser4.points += 1),
+          },
+        });
+
+      await commands.awardPoints.cb(data);
+      expect(data.channel.send).toHaveBeenCalledTimes(4);
+      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot();
+      expect(data.channel.send.mock.calls[1][0]).toMatchSnapshot();
+      expect(data.channel.send.mock.calls[2][0]).toMatchSnapshot();
+      expect(data.channel.send.mock.calls[3][0]).toMatchSnapshot();
+    });
 
     it('returns correct output for more than five mentioned users', async () => {
-      const mentionedUser1 = User([], 2, 10)
-      const mentionedUser2 = User([], 2, 3)
-      const mentionedUser3 = User([], 2, 1)
-      const mentionedUser4 = User([], 2, 0)
-      const mentionedUser5 = User([], 2, 21)
-      const client = Client([author, mentionedUser1, mentionedUser2, mentionedUser3, mentionedUser4, mentionedUser5], channel)  
-    
+      const mentionedUser1 = User([], 2, 10);
+      const mentionedUser2 = User([], 2, 3);
+      const mentionedUser3 = User([], 2, 1);
+      const mentionedUser4 = User([], 2, 0);
+      const mentionedUser5 = User([], 2, 21);
+      const client = Client(
+        [
+          author,
+          mentionedUser1,
+          mentionedUser2,
+          mentionedUser3,
+          mentionedUser4,
+          mentionedUser5,
+        ],
+        channel,
+      );
+
       const data = {
-        author : author,
+        author,
         content: `${mentionedUser1.id} ++ ${mentionedUser2.id} ++ ${mentionedUser3.id} ++ ${mentionedUser4.id} ++ ${mentionedUser5.id} ++`,
-        channel : channel,
-        client :  client,
-        guild : Guild([author, mentionedUser1, mentionedUser2, mentionedUser3, mentionedUser4, mentionedUser5])
-      }
-
-      axios.post.mockResolvedValueOnce({data: 
-        {
-          ...mentionedUser1, 
-          points: mentionedUser1.points +=1
-        }
-      })
-      .mockResolvedValueOnce({data: 
-        {
-          ...mentionedUser2, 
-          points: mentionedUser2.points +=1
-        }
-      })
-      .mockResolvedValueOnce({data: 
-        {
-          ...mentionedUser3, 
-          points: mentionedUser3.points +=1
-        }
-      })
-      .mockResolvedValueOnce({data: 
-        {
-          ...mentionedUser4, 
-          points: mentionedUser4.points +=1
-        }
-      })
-      .mockResolvedValueOnce({data: 
-        {
-          ...mentionedUser5, 
-          points: mentionedUser5.points +=1
-        }
-      })
-
-      await commands.awardPoints.cb(data)
-      expect(data.channel.send).toHaveBeenCalledTimes(6)
-      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot()   
-      expect(data.channel.send.mock.calls[1][0]).toMatchSnapshot()    
-      expect(data.channel.send.mock.calls[2][0]).toMatchSnapshot()   
-      expect(data.channel.send.mock.calls[3][0]).toMatchSnapshot()   
-      expect(data.channel.send.mock.calls[4][0]).toMatchSnapshot()   
-      expect(data.channel.send.mock.calls[5][0]).toMatchSnapshot()   
-    })
+        channel,
+        client,
+        guild: Guild([
+          author,
+          mentionedUser1,
+          mentionedUser2,
+          mentionedUser3,
+          mentionedUser4,
+          mentionedUser5,
+        ]),
+      };
+
+      axios.post
+        .mockResolvedValueOnce({
+          data: {
+            ...mentionedUser1,
+            points: (mentionedUser1.points += 1),
+          },
+        })
+        .mockResolvedValueOnce({
+          data: {
+            ...mentionedUser2,
+            points: (mentionedUser2.points += 1),
+          },
+        })
+        .mockResolvedValueOnce({
+          data: {
+            ...mentionedUser3,
+            points: (mentionedUser3.points += 1),
+          },
+        })
+        .mockResolvedValueOnce({
+          data: {
+            ...mentionedUser4,
+            points: (mentionedUser4.points += 1),
+          },
+        })
+        .mockResolvedValueOnce({
+          data: {
+            ...mentionedUser5,
+            points: (mentionedUser5.points += 1),
+          },
+        });
+
+      await commands.awardPoints.cb(data);
+      expect(data.channel.send).toHaveBeenCalledTimes(6);
+      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot();
+      expect(data.channel.send.mock.calls[1][0]).toMatchSnapshot();
+      expect(data.channel.send.mock.calls[2][0]).toMatchSnapshot();
+      expect(data.channel.send.mock.calls[3][0]).toMatchSnapshot();
+      expect(data.channel.send.mock.calls[4][0]).toMatchSnapshot();
+      expect(data.channel.send.mock.calls[5][0]).toMatchSnapshot();
+    });
 
     it('returns correct output for a user mentioning themselves', async () => {
-      const client = Client([author], channel)  
+      const client = Client([author], channel);
       const data = {
-        author : author,
+        author,
         content: `${author.id} ++`,
-        channel : channel,
-        client :  client,
-        guild : Guild([author])
-      }
-
-      axios.post.mockResolvedValue({data: 
-        {
-          ...author, 
-          points: author.points +=1
-        }
-      }) 
- 
-      await commands.awardPoints.cb(data)
-      expect(data.channel.send).toHaveBeenCalled()
-      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot()  
-      expect(data.channel.send.mock.calls[1][0]).toMatchSnapshot()  
-    })
+        channel,
+        client,
+        guild: Guild([author]),
+      };
+
+      axios.post.mockResolvedValue({
+        data: {
+          ...author,
+          points: (author.points += 1),
+        },
+      });
+
+      await commands.awardPoints.cb(data);
+      expect(data.channel.send).toHaveBeenCalled();
+      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot();
+      expect(data.channel.send.mock.calls[1][0]).toMatchSnapshot();
+    });
 
     it('returns correct output for a user mentioning Odin Bot', async () => {
-      const odinBot = User([], 0, 0)
-      const client = Client([author, odinBot], channel, odinBot)  
+      const odinBot = User([], 0, 0);
+      const client = Client([author, odinBot], channel, odinBot);
       const data = {
-        author : author,
+        author,
         content: `${odinBot.id} ++`,
-        channel : channel,
-        client :  client,
-        guild : Guild([author])
-      }
-
-      await commands.awardPoints.cb(data)
-      expect(data.channel.send).toHaveBeenCalled()
-      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot()   
-    })
-  })
-})
-
-describe('@user --', ()=>{
+        channel,
+        client,
+        guild: Guild([author]),
+      };
+
+      await commands.awardPoints.cb(data);
+      expect(data.channel.send).toHaveBeenCalled();
+      expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot();
+    });
+  });
+});
 
-  describe('regex',()=> {
+describe('@user --', () => {
+  describe('regex', () => {
     it.each([
-      ['@odin-bot --'],
-      ['thanks @odin-bot --'],
-      ['@odin-bot--']
+      ['<@!123456789> --'],
+      ['thanks <@!123456789> --'],
+      ['<@!123456789>--'],
     ])('correct strings trigger the callback', (string) => {
-    
-    })
+      expect(commands.deductPoints.regex.test(string)).toBeTruthy();
+    });
     it.each([
       ['--'],
       [''],
@@ -324,64 +345,66 @@ describe('@user --', ()=>{
       [' /'],
       ['odin-bot--'],
       ['/--'],
-      ['```function("@odin-bot --", () => {}```'],
+      ['```function("<@!123456789> --", () => {}```'],
     ])("'%s' does not trigger the callback", (string) => {
-      expect(commands.leaderboard.regex.test(string)).toBeFalsy()
-    })
+      expect(commands.deductPoints.regex.test(string)).toBeFalsy();
+    });
 
     it.each([
-      ['Check this out! @odin-bot --'],
-      ['Don\'t worry about it @odin-bot --'],
-      ['Hey @odin-bot --'],
-      ['/ @odin-bot-- ^ /me /leaderboard /tests$*']
+      ['Check this out! <@!123456789> --'],
+      ["Don't worry about it <@!123456789> --"],
+      ['Hey <@!123456789> --'],
+      ['/ <@!123456789>-- ^ /me /leaderboard /tests$*'],
     ])("'%s' - command can be anywhere in the string", (string) => {
-      expect(commands.leaderboard.regex.test(string)).toBeTruthy()
-    })
-    
+      expect(commands.deductPoints.regex.test(string)).toBeTruthy();
+    });
+
     it.each([
       ['@user/--'],
-      ['it\'s about/@odin-bot--'],
-      ['@odin-bot--isanillusion'],
-      ['@odin-bot--/'],
-      ['@odin-bot--*'],
-      ['@odin-bot--...']
-    ])("'%s' - command should be its own word/group - no leading or trailing characters", (string) => {
-      expect(commands.leaderboard.regex.test(string)).toBeFalsy()
-    })
-  })
+      ["it's about/<@!123456789>--"],
+      ['<@!123456789>--isanillusion'],
+      ['<@!123456789>--/'],
+      ['<@!123456789>--*'],
+      ['<@!123456789>--...'],
+    ])(
+      "'%s' - command should be its own word/group - no leading or trailing characters",
+      (string) => {
+        expect(commands.deductPoints.regex.test(string)).toBeFalsy();
+      },
+    );
+  });
 
   describe('callback', () => {
     it('returns correct output', async () => {
-      expect(commands.deductPoints.cb()).toMatchSnapshot()
-    })
-  })
-})
+      expect(commands.deductPoints.cb()).toMatchSnapshot();
+    });
+  });
+});
 
-describe('/points', ()=>{
-  describe('regex',()=> {
+describe('/points', () => {
+  describe('regex', () => {
     it.each([
-      ['/points @odin-bot'],
-      ['let me check out my /points @odin-bot'],
-      ['/points @odin-bot @odin-bot-v2']
+      ['/points <@!123456789>'],
+      ['let me check out my /points <@!123456789>'],
+      ['/points <@!123456789> <@!123456789>-v2'],
     ])('correct strings trigger the callback', (string) => {
-      expect(commands.points.regex.test(string)).toBeTruthy()
-    })
-  })
-})
-
-describe('/leaderboard', ()=>{
+      expect(commands.points.regex.test(string)).toBeTruthy();
+    });
+  });
+});
 
-  describe('regex',()=> {
+describe('/leaderboard', () => {
+  describe('regex', () => {
     it.each([
       ['/leaderboard'],
-      ['@odin-bot /leaderboard'],
+      ['<@!123456789> /leaderboard'],
       ['/leaderboard n=10 start=30'],
       ['/leaderboard n=20 start=50'],
       ['/leaderboard n=10'],
-      ['/leaderboard start=30']
+      ['/leaderboard start=30'],
     ])('correct strings trigger the callback', (string) => {
-      expect(commands.leaderboard.regex.test(string)).toBeTruthy()
-    })
+      expect(commands.leaderboard.regex.test(string)).toBeTruthy();
+    });
 
     it.each([
       ['/leaderboad'],
@@ -394,77 +417,94 @@ describe('/leaderboard', ()=>{
       ['/leaderboards'],
       ['```function("/leaderboard", () => {}```'],
       ['/leader'],
-      ['@odin-bot / leaderboard'],
-      ['@odin-bot /leaderbard'],
-      ['/leaderbord n=10 start=30']
+      ['<@!123456789> / leaderboard'],
+      ['<@!123456789> /leaderbard'],
+      ['/leaderbord n=10 start=30'],
     ])("'%s' does not trigger the callback", (string) => {
-      expect(commands.leaderboard.regex.test(string)).toBeFalsy()
-    })
+      expect(commands.leaderboard.regex.test(string)).toBeFalsy();
+    });
 
     it.each([
       ['Check this out! /leaderboard'],
-      ['Don\'t worry about /leaderboard'],
-      ['Hey @odin-bot, /leaderboard'],
-      ['/@odin-bot ^ /me /leaderboard /tests$*']
+      ["Don't worry about /leaderboard"],
+      ['Hey <@!123456789>, /leaderboard'],
+      ['/<@!123456789> ^ /me /leaderboard /tests$*'],
     ])("'%s' - command can be anywhere in the string", (string) => {
-      expect(commands.leaderboard.regex.test(string)).toBeTruthy()
-    })
+      expect(commands.leaderboard.regex.test(string)).toBeTruthy();
+    });
 
     it.each([
       ['@user/leaderboard'],
-      ['it\'s about/leaderboard'],
+      ["it's about/leaderboard"],
       ['/leaderboardisanillusion'],
       ['/leaderboard/'],
       ['/leaderboard*'],
-      ['/leaderboard...']
-    ])("'%s' - command should be its own word/group - no leading or trailing characters", (string) => {
-      expect(commands.leaderboard.regex.test(string)).toBeFalsy()
-    })
-  })
+      ['/leaderboard...'],
+    ])(
+      "'%s' - command should be its own word/group - no leading or trailing characters",
+      (string) => {
+        expect(commands.leaderboard.regex.test(string)).toBeFalsy();
+      },
+    );
+  });
 
   describe('callback', () => {
     it('returns correct output', async () => {
+      const members = generateLeaderData(5);
 
-            
-      const members = generateLeaderData(5)
-
-      axios.get = jest.fn()
+      axios.get = jest.fn();
       axios.get.mockResolvedValue({
-          data : members
-        })
-
-      expect(await commands.leaderboard.cb({
-        guild: Guild(members),
-        content: "/leaderboard n=5 start=1"
-      })).toMatchSnapshot()
-      expect(await commands.leaderboard.cb({
-        guild: Guild(members),
-        content: "/leaderboard n=3 start=1"
-      })).toMatchSnapshot()
-      expect(await commands.leaderboard.cb({
-        guild: Guild(members),
-        content: "/leaderboard n=2 start=3"
-      })).toMatchSnapshot()
-      expect(await commands.leaderboard.cb({
-        guild: Guild(members),
-        content: "/leaderboard start=3"
-      })).toMatchSnapshot()
-      expect(await commands.leaderboard.cb({
-        guild: Guild(members),
-        content: "/leaderboard n=2"
-      })).toMatchSnapshot()
-      expect(await commands.leaderboard.cb({
-        guild: Guild(members),
-        content: "/leaderboard n=2 start=wtf"
-      })).toMatchSnapshot()
-      expect(await commands.leaderboard.cb({
-        guild: Guild(members),
-        content: "/leaderboard n=wtf start=3"
-      })).toMatchSnapshot()
-      expect(await commands.leaderboard.cb({
-        guild: Guild(members),
-        content: "/leaderboard n=25 start=9999"
-      })).toMatchSnapshot()
-    })
-  })
-})
+        data: members,
+      });
+
+      expect(
+        await commands.leaderboard.cb({
+          guild: Guild(members),
+          content: '/leaderboard n=5 start=1',
+        }),
+      ).toMatchSnapshot();
+      expect(
+        await commands.leaderboard.cb({
+          guild: Guild(members),
+          content: '/leaderboard n=3 start=1',
+        }),
+      ).toMatchSnapshot();
+      expect(
+        await commands.leaderboard.cb({
+          guild: Guild(members),
+          content: '/leaderboard n=2 start=3',
+        }),
+      ).toMatchSnapshot();
+      expect(
+        await commands.leaderboard.cb({
+          guild: Guild(members),
+          content: '/leaderboard start=3',
+        }),
+      ).toMatchSnapshot();
+      expect(
+        await commands.leaderboard.cb({
+          guild: Guild(members),
+          content: '/leaderboard n=2',
+        }),
+      ).toMatchSnapshot();
+      expect(
+        await commands.leaderboard.cb({
+          guild: Guild(members),
+          content: '/leaderboard n=2 start=wtf',
+        }),
+      ).toMatchSnapshot();
+      expect(
+        await commands.leaderboard.cb({
+          guild: Guild(members),
+          content: '/leaderboard n=wtf start=3',
+        }),
+      ).toMatchSnapshot();
+      expect(
+        await commands.leaderboard.cb({
+          guild: Guild(members),
+          content: '/leaderboard n=25 start=9999',
+        }),
+      ).toMatchSnapshot();
+    });
+  });
+});