Repository: TheOdinProject/odin-bot-v2
Pull request #174 (2021-06-02 00:39:39)

Feature/remove award points from bot spam playground
<!-- 
Thanks for your interest in The Odin Project's Odin Bot. In order to get PRs closed in a reasonable amount of time, we request that you include a baseline of information about the changes you are proposing. Please answer the following triage questions:
-->

 - [X] You have read the [Odin Bot Contributing Guide](https://github.com/TheOdinProject/odin-bot-v2/wiki/Contributing-Guide).

#### 1.Describe the changes made:

Adds config property and functionality so awarding points may be limited in any number of channels. 

#### 2. If this PR is related to an open issue please reference it with the `#` operator and the issue number below:

#168 


Patch:
diff --git a/botCommands/points.js b/botCommands/points.js
index d5e871b..0a407e9 100644
--- a/botCommands/points.js
+++ b/botCommands/points.js
@@ -46,15 +46,20 @@ async function lookUpUser(discordId) {
 function exclamation(points) {
   if (points < 5) {
     return 'Nice!';
-  } if (points < 25) {
+  }
+  if (points < 25) {
     return 'Sweet!';
-  } if (points < 99) {
+  }
+  if (points < 99) {
     return 'Woot!';
-  } if (points < 105) {
+  }
+  if (points < 105) {
     return 'HOLY CRAP!!';
-  } if (points > 199 && points < 206) {
+  }
+  if (points > 199 && points < 206) {
     return 'DAM SON:';
-  } if (points > 299 && points < 306) {
+  }
+  if (points > 299 && points < 306) {
     return 'OK YOU CAN STOP NOW:';
   }
   return 'Woot!';
@@ -72,7 +77,10 @@ const plusRegex = '(\\+){2,}';
 const awardPoints = {
   // uses a negative lookback to isolate the command
   // followed by the Discord User, a whitespace character and either the star or plus incrementer
-  regex: new RegExp(`(?<!\\S)${userRegex}\\s?(${plusRegex}|${starRegex})(?!\\S)`, 'gu'),
+  regex: new RegExp(
+    `(?<!\\S)${userRegex}\\s?(${plusRegex}|${starRegex})(?!\\S)`,
+    'gu',
+  ),
   cb: async function pointsBotCommand({
     author,
     content,
@@ -80,9 +88,20 @@ const awardPoints = {
     client,
     guild,
   }) {
-    const userIds = getUserIdsFromMessage(content, new RegExp(`(?<!\\S)${userRegex}\\s?(${plusRegex}|${starRegex})(?!\\S)`, 'gu'));
+    const userIds = getUserIdsFromMessage(
+      content,
+      new RegExp(
+        `(?<!\\S)${userRegex}\\s?(${plusRegex}|${starRegex})(?!\\S)`,
+        'gu',
+      ),
+    );
+
     return Promise.all(
       userIds.map(async (userId, i) => {
+        if (config.noPointsChannels.includes(channel.id)) {
+          channel.send("You can't do that here!");
+          return;
+        }
         // this limits the number of calls per message to 5 to avoid abuse
         if (i > 4) {
           return;
@@ -95,7 +114,8 @@ const awardPoints = {
           channel.send('http://media0.giphy.com/media/RddAJiGxTPQFa/200.gif');
           channel.send("You can't do that!");
           return;
-        } if (user === client.user) {
+        }
+        if (user === client.user) {
           channel.send('awwwww shucks... :heart_eyes:');
           return;
         }
@@ -108,18 +128,23 @@ const awardPoints = {
               && !member.roles.cache.find((r) => r.name === 'club-40')
               && pointsUser.points > 39
             ) {
-              const pointsRole = guild.roles.cache.find((r) => r.name === 'club-40');
+              const pointsRole = guild.roles.cache.find(
+                (r) => r.name === 'club-40',
+              );
               member.roles.add(pointsRole);
-              const clubChannel = client.channels.cache.get('707225752608964628');
+              const clubChannel = client.channels.cache.get(
+                '707225752608964628',
+              );
 
               if (clubChannel) {
                 clubChannel.send(
-                  `HEYYY EVERYONE SAY HI TO ${user} the newest member of CLUB 40. Please check the pins at the top right!`,
+                  `HEYYY EVERYONE SAY HI TO ${user} the newest member of CLUB 40`,
                 );
               }
             }
             channel.send(
-              `${exclamation(pointsUser.points)} ${user} now has ${pointsUser.points
+              `${exclamation(pointsUser.points)} ${user} now has ${
+                pointsUser.points
               } ${plural(pointsUser.points)}`,
             );
           }
@@ -144,8 +169,7 @@ const points = {
       try {
         const userPoints = await lookUpUser(user.id);
         if (userPoints) {
-          const username = guild.members
-            .cache
+          const username = guild.members.cache
             .get(userPoints.discord_id)
             .displayName.replace(/\//g, '\\/');
           channel.send(`${username} has ${userPoints.points} points!`);
@@ -163,7 +187,9 @@ const leaderboard = {
   regex: /(?<!\S)\/leaderboard(?!\S)/,
   async cb({ guild, content }) {
     try {
-      const sEquals = content.split(' ').find((word) => word.includes('start='));
+      const sEquals = content
+        .split(' ')
+        .find((word) => word.includes('start='));
       let start = sEquals ? sEquals.replace('start=', '') : 1;
       start = Math.max(start, 1);
 
@@ -172,7 +198,9 @@ const leaderboard = {
       length = Math.min(length, 25);
       length = Math.max(length, 1);
 
-      const users = await axios.get('https://www.theodinproject.com/api/points');
+      const users = await axios.get(
+        'https://www.theodinproject.com/api/points',
+      );
       const data = users.data.filter((user) => guild.members.cache.get(user.discord_id));
       let usersList = '**leaderboard** \n';
       for (let i = start - 1; i < length + start - 1; i += 1) {
@@ -184,7 +212,9 @@ const leaderboard = {
             : undefined;
           if (username) {
             if (i === 0) {
-              usersList += `${i + 1} - ${username} [${user.points} points] :tada: \n`;
+              usersList += `${i + 1} - ${username} [${
+                user.points
+              } points] :tada: \n`;
             } else {
               usersList += `${i + 1} - ${username} [${user.points} points] \n`;
             }
diff --git a/botEngine.js b/botEngine.js
index 653715e..822c2f6 100644
--- a/botEngine.js
+++ b/botEngine.js
@@ -3,11 +3,7 @@ const botCommands = [];
 let authorBuffer = [];
 
 let creationsMessage = null;
-
-const adminRoles = ['core', 'maintainer', 'admin'];
-
-let currentIntroductionsMessage = null;
-const introductionsWelcomeMessage = 'Welcome to The Odin Project! Take a moment to survey all of the channels on the sidebar, especially the <#823266307293839401> channel for answers to commonly asked questions. We\'re excited for you to join us on your programming journey. Happy learning!';
+let introductionMessage = null;
 
 const createAuthorEntry = function (message) {
   const entry = {
@@ -36,7 +32,6 @@ async function listenToMessages(client) {
     if (message.author === client.user) {
       return;
     }
-    const isAdminMessage = message.member.roles.cache.some((r) => adminRoles.includes(r.name));
 
     const NOBOT_ROLE_ID = '783764176178774036';
 
@@ -59,6 +54,14 @@ async function listenToMessages(client) {
       return;
     }
 
+    if (message.channel.id === '690618925494566912') { // introductions-and-checkins
+      if (introductionMessage) {
+        introductionMessage.delete();
+      }
+      introductionMessage = await message.channel.send('Welcome to The Odin Project! Take a moment to survey all of the channels on the sidebar, especially the <#823266307293839401> channel for answers to commonly asked questions. We\'re excited for you to join us on your programming journey. Happy learning!');
+      return;
+    }
+
     const authorEntryCount = authorBuffer.reduce((count, current) => {
       if (current.author === message.author.id) {
         return count + 1;
@@ -94,18 +97,6 @@ async function listenToMessages(client) {
         }
       }
     });
-
-    if (message.channel.id === '690618925494566912') { // introductions
-      if (!isAdminMessage) {
-        if (
-          currentIntroductionsMessage
-          && currentIntroductionsMessage.content === introductionsWelcomeMessage
-        ) {
-          currentIntroductionsMessage.delete();
-        }
-        currentIntroductionsMessage = await message.channel.send(introductionsWelcomeMessage);
-      }
-    }
   });
 }
 
diff --git a/config.js b/config.js
index 2c46251..6440abb 100644
--- a/config.js
+++ b/config.js
@@ -4,6 +4,7 @@ const config = {
   pointsbot: {
     token: process.env.POINTSBOT_TOKEN,
   },
+  noPointsChannels: ['513125912070455296'],
 };
 
 module.exports = config;


Test patch:
diff --git a/botCommands/__snapshots__/points.test.js.snap b/botCommands/__snapshots__/points.test.js.snap
index 809d63f..b9b97ab 100644
--- a/botCommands/__snapshots__/points.test.js.snap
+++ b/botCommands/__snapshots__/points.test.js.snap
@@ -57,12 +57,16 @@ exports[`/leaderboard callback returns correct output 8`] = `
 
 exports[`@user -- callback returns correct output 1`] = `"http://media.riffsy.com/images/636a97aa416ad674eb2b72d4a6e9ad6c/tenor.gif"`;
 
-exports[`award points callback returns correct output for a single user entering club-40 1`] = `"HEYYY EVERYONE SAY HI TO <@2> the newest member of CLUB 40. Please check the pins at the top right!"`;
+exports[`award points 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[`award points callback returns correct output for a single user entering club-40 2`] = `"Woot! <@2> now has 40 points"`;
 
 exports[`award points callback returns correct output for a single user w/o club-40 1`] = `"Sweet! <@2> now has 21 points"`;
 
+exports[`award points callback returns correct output for a user awarding points in a channel listed in the config file 1`] = `"You can't do that here!"`;
+
+exports[`award points callback returns correct output for a user awarding points in a channel listed in the config file 2`] = `"You can't do that here!"`;
+
 exports[`award points callback returns correct output for a user mentioning Odin Bot 1`] = `"awwwww shucks... :heart_eyes:"`;
 
 exports[`award points callback returns correct output for a user mentioning themselves 1`] = `"http://media0.giphy.com/media/RddAJiGxTPQFa/200.gif"`;
diff --git a/botCommands/points.test.js b/botCommands/points.test.js
index 08f419d..643a627 100644
--- a/botCommands/points.test.js
+++ b/botCommands/points.test.js
@@ -38,7 +38,8 @@ jest.mock('discord.js', () => ({
     member: (user) => users.filter((member) => member === user)[0],
   })),
 
-  Channel: jest.fn().mockImplementation(() => ({
+  Channel: jest.fn().mockImplementation((id) => ({
+    id,
     send: mockSend,
   })),
 
@@ -109,12 +110,12 @@ describe('award points', () => {
   });
 
   describe('regex ⭐', () => {
-    it.each([
-      ['<@!123456789> ⭐'],
-      ['thanks <@!123456789> ⭐'],
-    ])("'%s' - correct strings trigger the callback", (string) => {
-      expect(string.match(commands.awardPoints.regex)).toBeTruthy();
-    });
+    it.each([['<@!123456789> ⭐'], ['thanks <@!123456789> ⭐']])(
+      "'%s' - correct strings trigger the callback",
+      (string) => {
+        expect(string.match(commands.awardPoints.regex)).toBeTruthy();
+      },
+    );
 
     it.each([
       ['⭐'],
@@ -381,6 +382,46 @@ describe('award points', () => {
       expect(data.channel.send).toHaveBeenCalled();
       expect(data.channel.send.mock.calls[0][0]).toMatchSnapshot();
     });
+
+    it('returns correct output for a user awarding points in a channel listed in the config file', async () => {
+      jest.mock(
+        '../config',
+        () => ({
+          noPointsChannels: ['513125912070455296', '123456789'],
+        }),
+        { virtual: true },
+      );
+      const mentionedUser = User([], 2, 20);
+      const botSpamChannel = Channel('513125912070455296');
+      const bannedChannel = Channel('123456789');
+      const client = Client([author, mentionedUser], botSpamChannel);
+
+      const botSpamChannelData = {
+        author,
+        content: `${mentionedUser.id} ++`,
+        channel: botSpamChannel,
+        client,
+        guild: Guild([author, mentionedUser]),
+      };
+
+      const bannedChannelData = {
+        author,
+        content: `${mentionedUser.id} ++`,
+        channel: bannedChannel,
+        client,
+        guild: Guild([author, mentionedUser]),
+      };
+
+      await commands.awardPoints.cb(botSpamChannelData);
+      expect(botSpamChannelData.channel.send).toHaveBeenCalled();
+      expect(
+        botSpamChannelData.channel.send.mock.calls[0][0],
+      ).toMatchSnapshot();
+
+      await commands.awardPoints.cb(bannedChannelData);
+      expect(bannedChannelData.channel.send).toHaveBeenCalled();
+      expect(bannedChannelData.channel.send.mock.calls[0][0]).toMatchSnapshot();
+    });
   });
 });