Beispiel #1
0
    async def username(self, ctx, username: filter_username):
        """
        See what number a specified user has.
        """
        user_number = self.bot.numbers.search.user_to_num(username)

        # Ensure that the user has a number.
        if user_number is None:
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="NGB - User Search",
                    description="That user doesn't have a number.",
                    colour=0x3CBD70,
                    user=ctx.author,
                ),
            )
            return

        # That user has a number, so send a message.
        await ctx.send(
            "",
            embed=NumEmbed(
                title="NGB - User Search",
                colour=0x3CBD70,
                fields={
                    "Number": f"#{user_number}",
                    "Username": f"u/{username}",
                },
                user=ctx.author,
            ),
        )
Beispiel #2
0
 async def checkeligiblity(self, ctx, username: filter_username):
     """
     (MOD) Check if a user meets the requirements to get a number.
     """
     if is_allowed_number(username):
         await ctx.send(
             "",
             embed=NumEmbed(
                 title="Eligibility Check",
                 description=
                 f"u/{username} meets the karma and age requirements for a number.",
                 colour="success",
                 user=ctx.author,
                 footer_text="Restricted Cmd",
             ),
         )
     else:
         await ctx.send(
             "",
             embed=NumEmbed(
                 title="Eligibility Check",
                 description=
                 f"u/{username} DOES NOT meet a karma/age requirement.",
                 colour="failure",
                 user=ctx.author,
                 footer_text="Restricted Cmd",
             ),
         )
Beispiel #3
0
 async def confirm_error(self, ctx, error) -> None:
     """
     Handles errors raised from the confirm command.
     """
     print(f"{type(error)}: {error}")
     if isinstance(error, AlreadyVerifiedCheckFailure):
         await ctx.send(
             "",
             embed=NumEmbed(
                 title="Verification",
                 description=
                 "You are already verified.\nTo update your information use the '$update' command.",
                 colour="failure",
                 user=ctx.author,
             ),
         )
     elif isinstance(error, MissingRequiredArgument):
         await ctx.send(
             "",
             embed=NumEmbed(
                 title="Confirmation",
                 description=
                 f"The correct usage is \"{self.bot.settings.discord.prefix}confirm (CONFIRMATION CODE)\".\nYou can find the confirmation code in your Reddit inbox after using the verify command.",
                 colour="failure",
                 user=ctx.author,
             ),
         )
     elif isinstance(error, BadArgument):
         await ctx.send(
             "",
             embed=NumEmbed(
                 title="Confirmation",
                 description=
                 "You've input something incorrectly (or not accepted).",
                 colour="failure",
                 user=ctx.author,
             ),
         )
     else:
         await ctx.send(
             "",
             embed=NumEmbed(
                 title="Confirmation",
                 description="Something went wrong. Please try again later.",
                 colour="failure",
                 user=ctx.author,
             ),
         )
Beispiel #4
0
    async def modidea(self, ctx, *, idea: str):
        """
        (MOD) Send a message to the mod idea channel.
        """
        # Load the ideas channel channel and then send the idea to it.
        ideas_channel = self.bot.get_channel(
            self.bot.settings.discord.ids["idea_channels"]["mod"])
        message = await ideas_channel.send(
            "",
            embed=NumEmbed(
                title="Moderator Idea",
                colour=0x000080,
                fields={
                    "Added by": f"{ctx.author.mention}",
                    "Idea": idea,
                },
            ),
        )

        # Adds vote reactions to the idea message.
        await message.add_reaction("✅")
        await message.add_reaction("❌")

        # React to the invoking message.
        await ctx.message.add_reaction("👍")
Beispiel #5
0
    async def number(self, ctx, number: int) -> None:
        """
        Find which user has a number and which countries a number is eligible for.
        """
        number_user = self.bot.numbers.search.num_to_user(number)
        nation_and_countries = self.bot.numbers.checks.nation_and_countries(
            number)

        countries = "\n".join([
            f"• {country[0]}" for country in nation_and_countries["countries"]
        ])
        await ctx.send(
            "",
            embed=NumEmbed(
                title="NGB - Number Search",
                colour=0x00C9CC,
                fields={
                    "Number":
                    f"#{number}",
                    "Number User":
                    f"u/{number_user}" if number_user is not None else
                    "No One" if number != 404 else "Error 404\nNot Found",
                    "Nation":
                    nation_and_countries["nation"][0],
                    "Odd/Even":
                    self.bot.numbers.checks.parity(number),
                    "Eligible Countries":
                    countries,
                },
                user=ctx.author,
            ),
        )
Beispiel #6
0
    async def suggest(self, ctx, *, suggestion: str):
        """
        Suggest an idea in the main r/Num guild.
        """
        # Load the suggestions channel.
        suggestions_channel = self.bot.get_channel(
            self.bot.settings.discord.ids["idea_channels"]["standard"])

        # Send a message to the suggestions channel.
        message = await suggestions_channel.send(
            "",
            embed=NumEmbed(
                title="Suggestion",
                colour=0x000080,
                fields={
                    "Added by": f"{ctx.author.mention}",
                    "User Nickname": f"{ctx.author.display_name}",
                    "User ID": f"{ctx.author.id}",
                    "Suggestion Text": f"{suggestion}"
                },
            ),
        )

        # Adds vote reactions to the suggestion message.
        await message.add_reaction("✅")
        await message.add_reaction("❌")

        # Send a message to the user.
        await ctx.send(
            f"{ctx.author.mention} Your suggestion has been added to the suggestions channel."
        )
Beispiel #7
0
 async def stats(self, ctx) -> None:
     """
     Gets some statistics on the assigned numbers.
     """
     stats = self.bot.numbers.statistics
     await ctx.send(
         "",
         embed=NumEmbed(
             title="Number Statistics",
             colour=0x007E80,
             fields={
                 "Numbers Given": stats["numbers_given"],
                 "Even Numbers": stats["evens"],
                 "Odd Numbers": stats["odds"],
                 "Highest Number": stats["highest_info"],
                 "Lowest Positive": stats["lowest_positive_info"],
                 "Lowest Number": stats["lowest_info"],
                 "Sum of Numbers": stats["sum"],
                 "Mean": stats["mean"],
                 "Median": stats["median"],
                 "Below 500": stats["below_500"],
                 "Below 1000": stats["below_1000"],
                 "Below 2500": stats["below_2500"],
             },
             user=ctx.author,
         ),
     )
Beispiel #8
0
 async def eightball(self, ctx) -> None:
     """
     Generate an 8-ball response to a question.
     """
     await ctx.send(
         "",
         embed=NumEmbed(
             title="8-Ball",
             description=choice([
                 "As I see it, yes.",
                 "Ask again later.",
                 "Better not tell you now.",
                 "Cannot predict now.",
                 "Concentrate and ask again.",
                 "Don’t count on it.",
                 "It is certain.",
                 "It is decidedly so.",
                 "Most likely.",
                 "My reply is no.",
                 "My sources say no.",
                 "Outlook not so good.",
                 "Outlook good.",
                 "Reply hazy, try again.",
                 "Signs point to yes.",
                 "Very doubtful.",
                 "Without a doubt.",
                 "Yes.",
                 "Yes – definitely.",
                 "You may rely on it.",
             ]),
             footer_text="Please do not take responses from this seriously.",
             user=ctx.author,
         ),
     )
Beispiel #9
0
    async def numberfact(self, ctx, number: int = None) -> None:
        """
        Get a fact about a random (or a specific) number.
        """
        fetched_info = {}
        if number is None:
            fetched_info = get(
                "http://numbersapi.com/random/math?default=No%20fact%20found.&json"
            ).json()
        else:
            fetched_info = get(
                f"http://numbersapi.com/{number}/math?default=No%20fact%20found.&json"
            ).json()

        await ctx.send(
            "",
            embed=NumEmbed(
                title="Number Fact",
                colour=0x4F2D4E,
                fields={
                    "Number":
                    fetched_info["number"],
                    "Result":
                    f"**{fetched_info['type'].capitalize()}**: {fetched_info['text']}",
                },
                user=ctx.author,
            ),
        )
Beispiel #10
0
    async def contact(self, ctx, *, text: str) -> None:
        """
        Contact the Num moderators through Discord.
        """
        num_guild = self.bot.get_guild(self.bot.settings.discord.main_guild)
        guild_user = num_guild.get_member(ctx.author.id)

        # Check that the user is in the main guild.
        # This is to prevent misuse of the command.
        if guild_user is None:
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="Contacting Staff",
                    description=
                    "To prevent misuse of this command, you are required to be in the main Discord guild in order to use it.",
                    colour="failure",
                    user=ctx.author,
                ),
            )
            return

        # Attempt to delete the user's contact message.
        try:
            await ctx.message.delete()
        except Exception:
            pass

        # Send the message to the staff contact channel
        contact_channel = self.bot.get_channel(
            self.bot.settings.discord.ids["contact_channel"])
        await contact_channel.send(
            "",
            embed=NumEmbed(
                title="Staff Contact",
                fields={
                    "Sent by":
                    f"{ctx.author.mention}\n{guild_user.display_name}\n{ctx.author}",
                    "Message": text,
                },
                colour=0xFF5554,
            ),
        )

        # Let the user know that we've received their message.
        await ctx.author.send(
            "We've received your message. We should get back to you shortly.")
Beispiel #11
0
    async def send_submissions_feed_message(self,
                                            submission: Submission) -> None:
        """
        Sends a message to the submissions feed channel.
        :param submission: The submission to send a message for.
        """
        submissions_feed_channel = self.bot.get_channel(
            self.bot.settings.discord.ids["feed_channels"]["submissions_feed"])

        submission_embed = NumEmbed(
            title=submission.title,
            description=f"Posted by u/{submission.author.name}",
            url=f"https://reddit.com{submission.permalink}",
            colour=0x202225,
        )

        if submission.domain.lower() == "i.redd.it":
            submission_embed.set_thumbnail(url=submission.url)

        submission_embed.set_author(
            name=f"r/{self.bot.reddit.main_sub_name} - New Submission",
            icon_url=
            "https://styles.redditmedia.com/t5_2z5lj/styles/communityIcon_nnjpwqb5ozb41.png"
        )

        submission_embed.timestamp = timestamp_to_datetime(
            submission.created_utc)

        await submissions_feed_channel.send("", embed=submission_embed)
Beispiel #12
0
 async def leaderboard(self, ctx) -> None:
     """
     View the current points leaderboard.
     """
     await ctx.send(
         "",
         embed=NumEmbed(
             title="Points Leaderboard",
             fields=self.bot.points_leaderboard.field_representation,
             user=ctx.author,
         ),
     )
Beispiel #13
0
 async def dice(self, ctx) -> None:
     """
     Roll a 6-sided die.
     """
     await ctx.send(
         "",
         embed=NumEmbed(
             title="Dice Roll",
             description=f"You rolled a {randint(1, 6)}.",
             user=ctx.author,
         ),
     )
Beispiel #14
0
 async def randomnum(self, ctx):
     """
     (MOD) Generates a random avaliable number within the assignment range.
     """
     await ctx.send(
         "",
         embed=NumEmbed(
             title="Random Avaliable Number",
             description=self.bot.numbers.generation.get_random_number(),
             user=ctx.author,
             footer_text="Restricted Cmd",
         ),
     )
Beispiel #15
0
    async def reaction_role_remove(self, user: Union[User,
                                                     Member], role_name: str,
                                   category: str, message: Message):
        """
        Handles removing a role upon a reaction being removed.
        :param user: The user who to remove a role from.
        :param role_name: The role (name) to remove.
        :param category: The specific category of reaction role (e.g ping notifications).
        :param message: The reaction role category message.
        """
        name_info = user.display_name.split(" | ")
        number = int(name_info[0])

        # Ignore the reaction removal if the user was never eligible for the role.
        if category == "countries":
            if not self.bot.numbers.checks.is_eligible_for(number, role_name):
                return

        # Fetch the role.
        role = get(message.guild.roles, id=self.role_ids[category][role_name])

        # Ignore if the user never had the role.
        if role not in user.roles:
            return

        # Remove the role from the user.
        await user.remove_roles(role)

        # Attempt to send a message to the user, then send a message to the log channel.
        try:
            await user.send(f"Removed the '{role_name}' role from you.")
        except Exception:
            pass
        finally:
            log_channel = self.bot.get_channel(
                self.bot.settings.discord.ids["log_channels"]["selection_logs"]
                [category])
            await log_channel.send(
                "",
                embed=NumEmbed(
                    title=f"{self.singular_names[category]} Left",
                    colour="failure",
                    fields={
                        "Number": number,
                        "Username": f"u/{name_info[1]}",
                        "Discord User":
                        f"@{user}\n{user.mention}\nID: {user.id}",
                        "Role": role_name,
                    },
                ),
            )
Beispiel #16
0
 async def verify_error(self, ctx, error) -> None:
     """
     Handles errors raised from the verify command.
     """
     print(f"{type(error)}: {error}")
     if isinstance(error, AlreadyVerifiedCheckFailure):
         await ctx.send(
             "",
             embed=NumEmbed(
                 title="Verification",
                 description=
                 "You are already verified.\nTo update your information use the '$update' command.",
                 colour="failure",
                 user=ctx.author,
             ),
         )
     elif isinstance(error, MissingRequiredArgument):
         await ctx.send(
             "",
             embed=NumEmbed(
                 title="Verification",
                 description=
                 "The correct usage is \"$verify (REDDIT USERNAME)\".",
                 colour="failure",
                 user=ctx.author,
             ),
         )
     else:
         await ctx.send(
             "",
             embed=NumEmbed(
                 title="Verification",
                 description="Something went wrong. Please try again later.",
                 colour="failure",
                 user=ctx.author,
             ),
         )
Beispiel #17
0
    async def list(self, ctx):
        """
        (MOD) Gets a list of all the currently assigned numbers.
        """
        # Generate the list.
        text = f"r/{self.bot.reddit.main_sub_name} Number List - {len(self.bot.numbers.numbers.keys())} Assigned - {get_date_time()} UTC"
        for number, user in self.bot.numbers.numbers.items():
            text += f"\n#{number} (u/{user})"

        # Attempt to upload the list and send a message if there was an error uploading it.
        link = upload_text(text)
        if link is None:
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="Number List",
                    description=
                    "There was an error uploading the list.\nPlease try again later.",
                    colour="failure",
                    user=ctx.author,
                    footer_text="Restricted Cmd",
                ),
            )
            return

        # Uploading was a success. Send a message.
        await ctx.send(
            "",
            embed=NumEmbed(
                title="Click here to view the list.",
                description="Generated a list of the current assigned numbers.",
                url=link,
                user=ctx.author,
                footer_text="Restricted Cmd",
            ),
        )
Beispiel #18
0
 async def refresh(self, ctx):
     """
     (ADMIN) Refresh the Reddit wiki settings.
     """
     self.bot.settings.load_wiki_settings(self.bot.reddit)
     self.bot.numbers.set_max_number()
     await ctx.send(
         "",
         embed=NumEmbed(
             title="Settings",
             description="Succesfully refreshed the Reddit wiki settings.",
             colour="success",
             user=ctx.author,
             footer_text="Restricted Cmd",
         ),
     )
Beispiel #19
0
 async def rangeinfo(self, ctx):
     """
     (MOD) Get some info on the current number assignment range.
     """
     await ctx.send(
         "",
         embed=NumEmbed(
             title="Assignment Number Range",
             fields={
                 "Minimum":
                 self.bot.settings.reddit.assignment.numbers["min"],
                 "Maximum": self.bot.numbers.current_max_number,
             },
             user=ctx.author,
             footer_text="Restricted Cmd",
         ),
     )
Beispiel #20
0
 async def info(self, ctx) -> None:
     """
     View some information about the bot.
     """
     await ctx.send(
         "",
         embed=NumEmbed(
             title="The Number God Bot",
             description=
             f"The bot that helps run r/{self.bot.settings.reddit.subreddit}",
             fields={
                 "CPU/Memory Usage":
                 f"{cpu_percent()}%/{virtual_memory().percent}%",
                 "Lines of Code": self.bot.lines_of_code,
                 "Created by": "u/OneUpPotato for r/Num",
             },
             footer_text="TNG v2.1",
             user=ctx.author,
         ),
     )
Beispiel #21
0
    async def update(self, ctx) -> None:
        """
        Update your number information on Discord (should your number change).
        """
        current_info = ctx.author.display_name.split(" | ")
        username = current_info[1]
        nick_number = int(current_info[0])

        # Check that the user is valid on Reddit. If not then remove their verification roles.
        if not self.bot.reddit.is_valid_user(username):
            try:
                await ctx.author.send(
                    "",
                    embed=NumEmbed(
                        title="Updating",
                        description=
                        "Your username was not found to be a valid Reddit user.\nYou've been unverified, but are free to verify again.",
                        colour="failure",
                        user=ctx.author,
                    ),
                )
            except Exception:
                pass
            finally:
                await self.verification_handler.remove_roles(ctx.author)
            return

        # Check to see if the user's number has changed.
        number = self.bot.numbers.search.user_to_num(username)
        if number == nick_number:
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="Updating",
                    description="Your number has not changed.",
                    colour="failure",
                    user=ctx.author,
                ),
            )
            return

        # Process the update.
        await self.verification_handler.remove_roles(ctx.author)
        await ctx.author.edit(nick=f"{number} | {username}")

        # Get the initial roles and then assign them.
        initial_roles = self.verification_handler.get_initial_roles(number)
        await self.verification_handler.assign_roles(ctx.author, initial_roles,
                                                     ctx.guild)

        # Send a message to the user.
        await ctx.send(
            "",
            embed=NumEmbed(
                title="Update",
                description=
                "Succesfully updated your nickname and roles to your new number.",
                user=ctx.author,
            ),
        )

        # Send a message to the update log.
        update_log_channel = self.bot.get_channel(
            self.bot.settings.discord.ids["log_channels"]["update_log"])
        await update_log_channel.send(
            "",
            embed=NumEmbed(
                title="Successful Update",
                colour="success",
                fields={
                    "Old Number":
                    nick_number,
                    "New Number":
                    number,
                    "Username":
                    f"u/{username}",
                    "Discord User":
                    f"{ctx.author}\n{ctx.author.mention}\n{ctx.author.id}",
                },
            ),
        )
Beispiel #22
0
    async def nick(self, ctx, *, nick: str = None):
        """
        Give yourself a nickname.
        """
        name_info = ctx.author.display_name.split(" | ")
        number = int(name_info[0])
        username = name_info[1]

        # Load the nickname log channel.
        nickname_log_channel = self.bot.get_channel(
            self.bot.settings.discord.ids["log_channels"]["nickname_log"])

        # Attempt to set the user's nickname.
        if nick is not None:
            # Check that their new nickname will be under the Discord 32 character nickname limit.
            new_nickname = f"{number} | {username} | {nick}"
            if len(new_nickname) > 32:
                # Send a message to the user.
                await ctx.send(
                    "",
                    embed=NumEmbed(
                        title="Nickname",
                        description=
                        "Failed to set your nickname as (in total) it would be over 32 characters (the Discord nickname limit).",
                        colour="failure",
                        fields={
                            "Input Nickname": nick,
                            "Attempted Nickname (Full)": new_nickname,
                            "Nickname (cut off at 32 chars)":
                            new_nickname[:32],
                        },
                        user=ctx.author,
                    ),
                )

                # Send a message to the log channel.
                await nickname_log_channel.send(
                    "",
                    embed=NumEmbed(
                        title="Failed Nickname Set (Too Long)",
                        colour="failure",
                        fields={
                            "Current Nickname":
                            " | ".join(name_info),
                            "Failed Nickname (Full)":
                            new_nickname,
                            "Discord User":
                            f"{ctx.author}\n{ctx.author.id}\n{ctx.author.mention}",
                        },
                    ),
                )
                return

            # Set the user's nickname.
            await ctx.author.edit(nick=new_nickname)

            # Send a message to the channel.
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="Nickname",
                    description=
                    f"Succesfully changed your nickname to '{new_nickname}'.",
                    user=ctx.author,
                ))

            # Send a message to the log channel that it was a success.
            await nickname_log_channel.send(
                "",
                embed=NumEmbed(
                    title="Successful Nickname Set",
                    colour="success",
                    fields={
                        "Old Nickname":
                        " | ".join(name_info),
                        "New Nickname":
                        new_nickname,
                        "Discord User":
                        f"{ctx.author}\n{ctx.author.id}\n{ctx.author.mention}",
                    },
                ),
            )
        else:
            # Remove the user's custom nickname (if any).
            await ctx.author.edit(nick=f"{number} | {username}")

            # Send a message to the log channel that it was succesfully removed.
            await nickname_log_channel.send(
                "",
                embed=NumEmbed(
                    title="Successful Nickname Removal",
                    colour="success",
                    fields={
                        "New Nickname":
                        ctx.author.display_name,
                        "Old Nickname":
                        " | ".join(name_info),
                        "Discord User":
                        f"{ctx.author}\n{ctx.author.id}\n{ctx.author.mention}",
                    },
                ),
            )
Beispiel #23
0
    async def number_day_update(self) -> None:
        """
        This updates the number day and assigns the daily points.
        """
        current_date = get_date()
        current_num_day = get_num_day()

        # Get a random user for Number of the Day.
        notd, notd_user = self.bot.numbers.generation.get_random_user()

        # Get the statistics for the currently assigned numbers.
        number_statistics = self.bot.numbers.statistics

        # Assign points for the top 3 submissions of the day.
        submissions = []
        for submission in self.bot.reddit.main_subreddit.top("day", limit=4):
            if submission.author.name != self.bot.reddit.username:
                submissions.append(submission)

        assigned_points_text = dedent("""
            |**Submission**|**Username**|**Nation**|**Points Awarded**|
            |:-|:-|:-|:-|
        """).strip()
        assigned_points_row = "|[{title}](https://reddit.com{permalink})|u/{author}|{nation}|{points_awarded}|"

        points_leaderboard = self.bot.points_leaderboard

        submissions = submissions[:3]
        for submission in submissions:
            try:
                username = submission.author.name
                user_number = self.bot.numbers.search.user_to_num(username)
                user_nation = "Numberless"
                if user_number is not None:
                    user_nation = get_number_nation(user_number)
                elif username == "OneUpPotato":
                    user_nation = "000s"

                # Increase the points for that nation.
                points_leaderboard.leaderboard[user_nation] += 1

                assigned_points_text += "\n" + assigned_points_row.format(
                    title=submission.title,
                    permalink=submission.permalink,
                    author=submission.author.name,
                    nation=user_nation,
                    points_awarded=1,
                )
            except Exception:
                pass

        if len(submissions) == 0:
            assigned_points_text = "No points have been assigned today."

        # Post the Num Day Update submission.
        submission_text = self.bot.settings.templates.num_day_update[
            "submission_text"].format(
                num_day=current_num_day,
                notd=notd,
                notd_user=notd_user,

                # Statistics
                stats_assigned=number_statistics["numbers_given"],
                stats_odd=number_statistics["odds"],
                stats_even=number_statistics["evens"],
                stats_mean=number_statistics["mean"],
                stats_median=number_statistics["median"],
                stats_sum=number_statistics["sum"],
                points_leaderboard_text=points_leaderboard.leaderboard_table(
                    header=False),
                new_assigned_points_text=assigned_points_text.strip(),
                subreddit=self.bot.settings.reddit.subreddit,
                assignment_thread=self.bot.settings.reddit.assignment.id,
            )

        submission = self.bot.reddit.main_subreddit.submit(
            f"Daily Update ({current_date}) - Day #{current_num_day}",
            selftext=submission_text)

        # Send a message to the Number of the Day feed on Discord.
        notd_channel = self.bot.get_channel(
            self.bot.settings.discord.ids["feed_channels"]["notd"])
        notd_ping = f"<@&{self.bot.settings.discord.role_ids.ping_notifications['Number of the Day']}>"
        await notd_channel.send(
            notd_ping,
            embed=NumEmbed(
                title=f"Num Day #{current_num_day} - {current_date}",
                fields={
                    "Number of the Day": f"#{notd} (u/{notd_user})",
                    "Awarded Points": "View the submission.",
                },
                colour=0x739AAF,
                url=f"https://reddit.com{submission.permalink}",
            ),
        )

        # Save the updated leaderboard and update the widgets.
        points_leaderboard.save()
        await self.update_widgets()
Beispiel #24
0
    async def on_command_error(self, ctx, error) -> None:
        """
        Handles general command errors.
        :param ctx: The command context.
        :param error: The error encountered.
        """

        # Capture the error using Sentry.
        non_logged_errors = [
            commands.MissingRequiredArgument,
            commands.TooManyArguments,
            commands.DisabledCommand,
            commands.NoPrivateMessage,
            commands.CommandNotFound,
            commands.CheckFailure,
            commands.BadArgument,
            errors.AdminCheckFailure,
            errors.VerifiedCheckFailure,
            errors.MainGuildCheckFailure,
            errors.ModeratorCheckFailure,
            errors.DeveloperCheckFailure,
            errors.NotMainGuildCheckFailure,
            errors.AlreadyVerifiedCheckFailure,
        ]
        if not any([
                isinstance(error, non_log_error)
                for non_log_error in non_logged_errors
        ]):
            self.bot.sentry.capture_exception(error)

        # Ignore if the command already has its own error handler.
        if hasattr(ctx.command, "on_error"):
            return

        # Since the error isn't handled elsewhere, prepare a message to send to the user.
        embed = NumEmbed(title="Error",
                         description="Something went wrong.",
                         colour=0x8B0000)

        if isinstance(error, commands.MissingRequiredArgument):
            missing_argument = error.param.name
            embed.description = f"You are missing the following argument: '{missing_argument}'."
        elif isinstance(error, commands.TooManyArguments):
            embed.description = "Too many arguments have been given."
        elif isinstance(error, errors.ModeratorCheckFailure):
            embed.description = "You are not able to run this command as you are not a Moderator."
        elif isinstance(error, errors.AdminCheckFailure):
            embed.description = "You are not authorised to run this command as you are not a bot admin."
        elif isinstance(error, errors.DeveloperCheckFailure):
            embed.description = "You are not authorised to use this command."
        elif isinstance(error, errors.VerifiedCheckFailure):
            embed.description = "You need to be verified in order to do this."
        elif isinstance(error, errors.AlreadyVerifiedCheckFailure):
            embed.description = "You are already verified meaning you cannot use this command."
        elif isinstance(error, errors.MainGuildCheckFailure):
            embed.description = "This command can only run on the main r/Num Discord guild."
        elif isinstance(error, errors.NotMainGuildCheckFailure):
            embed.description = "This command cannot run in this guild."
        elif isinstance(error, commands.DisabledCommand):
            embed.description = "This command is currently disabled.\nThis may be due to bug fixing or improvement."
        elif isinstance(error, commands.NoPrivateMessage):
            embed.description = "You cannot run this command in DMs, you need to be on a guild."
        elif isinstance(error, commands.CommandNotFound):
            embed.description = f"That command doesn't exist. You can view a list of cmds by using '{self.bot.settings.discord.prefix}help'."
        elif isinstance(error, commands.CheckFailure):
            embed.description = "You are not able to run this command currently.\nThis may be because you don't have the required role, or aren't running it from the main Discord guild."
        elif isinstance(error, commands.BadArgument):
            embed.description = "You've input something incorrectly (or not accepted)."
        else:
            embed.description = "Something went wrong. The error has been logged."

        print(f"{type(error)}: {error}")
        await ctx.send("", embed=embed)
Beispiel #25
0
    async def assign(self, ctx, username: filter_username, number: int = None):
        """
        (ADMIN) Assigns a specific or random number to a user.
        """
        # Check that the user is valid.
        if not self.bot.reddit.is_valid_user(username):
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="Number Assignment",
                    description=f"u/{username} is not a valid user.",
                    colour="failure",
                    user=ctx.author,
                    footer_text="Restricted Cmd",
                ),
            )
            return

        # Get a random avaliable number if one wasn't given.
        if number is None:
            number = self.bot.numbers.generation.get_random_number()

        # Send a confirmation message.
        confirmation_message = await ctx.send(
            "",
            embed=NumEmbed(
                title="Number Assignment (Pending Confirmation)",
                description=
                "Please react with '✅' (in the next 30 seconds) to go through with the following assignment:",
                fields={
                    "User": f"u/{username}",
                    "Number": number,
                },
                user=ctx.author,
                footer_text="Restricted Cmd",
            ),
        )
        await confirmation_message.add_reaction("✅")

        # Handle the confirmation reaction.
        try:
            reaction, user = await self.bot.wait_for(
                "reaction_add",
                timeout=30.0,
                check=lambda reaction, user: str(
                    reaction.emoji) == "✅" and user == ctx.author)
        except TimeoutError:
            # No reaction was given in time.
            await confirmation_message.edit(embed=NumEmbed(
                title="Number Assignment (Expired)",
                description="A confirmation was not given in time.",
                colour="failure",
                user=ctx.author,
                footer_text="Restricted Cmd",
            ), )
        else:
            # The assignment was confirmed.
            self.bot.numbers.assignment.assign_number(username, number)

            await confirmation_message.edit(embed=NumEmbed(
                title="Number Assignment",
                description="Succesfully assigned that user a number.",
                fields={
                    field.name: field.value
                    for field in confirmation_message.embeds[0].fields
                },
                colour="success",
                user=ctx.author,
                footer_text="Restricted Cmd",
            ), )
Beispiel #26
0
    async def confirm(self, ctx, code: int) -> None:
        """
        Input your verification code. (use the verify command first)
        """
        # Ensure that the user is already pending verification.
        if ctx.author.id not in self.pending_verification.keys():
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="Confirmation",
                    description=
                    f"You are not currently pending verification.\nPlease use '{self.bot.settings.discord.prefix}verify (reddit username)' first.",
                    colour="failure",
                    user=ctx.author,
                ),
            )
            return

        # Check that the input code is correct.
        verification_info = self.pending_verification[ctx.author.id]
        if verification_info["code"] != code:
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="Confirmation",
                    description="That is not the correct verification code.",
                    colour="failure",
                    user=ctx.author,
                ),
            )
            return

        # Ensure that the user is still valid.
        if not self.bot.reddit.is_valid_user(verification_info["username"]):
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="Confirmation",
                    description=
                    f"u/{verification_info['username']} is no longer a valid Reddit user.",
                    colour="failure",
                    user=ctx.author,
                ),
            )
            del self.pending_verification[ctx.author.id]
            return

        # Process the verification confirmation.
        # Try to delete the user's confirm message, then remove their old roles (if any).
        try:
            await ctx.message.delete()
        except Exception:
            pass
        finally:
            await self.verification_handler.remove_roles(ctx.author)

        # Get the user's number then set their nickname.
        username = self.bot.reddit.get_username_casing(
            verification_info["username"])
        number = self.bot.numbers.search.user_to_num(
            verification_info["username"])
        await ctx.author.edit(nick=f"{number} | {username}")

        # Get the user's initial roles and then assign them.
        initial_roles = self.verification_handler.get_initial_roles(number)
        await self.verification_handler.assign_roles(ctx.author, initial_roles,
                                                     ctx.guild)

        # Attempt to send a message to the user. Then send a message to the confirmation log.
        info_msg = self.bot.settings.templates.verification["verified_pm"]
        number_nation = "Numberless"
        number_parity = "N/A"
        if number is not None:
            nation_and_countries = self.bot.numbers.checks.nation_and_countries(
                number)
            number_nation = nation_and_countries["nation"][0]
            number_parity = self.bot.numbers.checks.parity(number)

            countries = "\n".join([
                f"* {country_info[0]}"
                for country_info in nation_and_countries["countries"]
            ])

            info_msg = info_msg.format(
                username=username,
                number_information=
                f"You are number #{number}. Your number nation is the {number_nation}.\nYou are eligible for the following countries:\n{countries}",
            )
        else:
            info_msg = info_msg.format(
                username=username,
                number_information=
                f"You currently do not have a number assigned. When/if you get a number, use the '{self.bot.settings.discord.prefix}update' command to update it on Discord."
            )

        try:
            await ctx.author.send(info_msg)
        except Exception:
            pass
        finally:
            confirm_log_channel = self.bot.get_channel(
                self.bot.settings.discord.ids["log_channels"]
                ["confirmation_log"])
            await confirm_log_channel.send(
                "",
                embed=NumEmbed(
                    title="Successful User Confirmation",
                    colour="success",
                    fields={
                        "Number": number,
                        "Reddit": f"u/{username}",
                        "Discord User":
                        f"{ctx.author}\n{ctx.author.mention}\n{ctx.author.id}",
                        "Nation": number_nation,
                        "Odd/Even": number_parity,
                    },
                ),
            )
Beispiel #27
0
    async def reaction_role_add(self, user: Union[User,
                                                  Member], role_name: str,
                                category: str, message: Message, emoji: str):
        """
        Handles adding a reaction role after being called from the reaction event.
        :param user: The user who to remove a role from.
        :param role_name: The role (name) to validate eligiblity for (and possibly add to the user).
        :param category: The specific category of reaction role (e.g ping notifications).
        :param message: The reaction role category message.
        :param emoji: The emoji reacted with (used if the reaction needs to be removed).
        """
        name_info = user.display_name.split(" | ")
        number = int(name_info[0])

        # Load the log channel.
        log_channel = self.bot.get_channel(
            self.bot.settings.discord.ids["log_channels"]["selection_logs"]
            [category])

        # If the role category is a country then check if they are eligible.
        if category == "countries":
            if not self.bot.numbers.checks.is_eligible_for(number, role_name):
                # The user is not eligible. Attempt to send a message to them and then the log channel.
                try:
                    await user.send(
                        f"Your number is not eligible for the '{role_name}' role."
                    )
                except Exception:
                    pass
                finally:
                    await log_channel.send(
                        "",
                        embed=NumEmbed(
                            title=
                            f"(Failed) {self.singular_names[category]} Join",
                            description=
                            "That user is not eligible for this role.",
                            colour=0xFFFF00,
                            fields={
                                "Number": number,
                                "Username": f"u/{name_info[1]}",
                                "Discord User":
                                f"@{user}\n{user.mention}\nID: {user.id}",
                                "Role": role_name,
                            },
                        ),
                    )

                # Remove their reaction.
                await message.remove_reaction(emoji, user)

                return

        # If the role is a country then ensure they are not at the country limit.
        if category == "countries":
            current_countries = [
                role.id for role in user.roles
                if role.id in self.role_ids[category].values()
            ]
            if len(current_countries) >= 2:
                # The user is already at the maximum amount of countries.
                try:
                    await user.send(
                        f"Failed to join '{role_name}'. You are already in two countries, leave one and try again."
                    )
                except Exception:
                    pass
                finally:
                    await log_channel.send(
                        "",
                        embed=NumEmbed(
                            title=
                            f"(Failed) {self.singular_names[category]} Join",
                            description=
                            "That user is already at the maximum amount of countries.",
                            colour=0xFFFF00,
                            fields={
                                "Number": number,
                                "Username": f"u/{name_info[1]}",
                                "Discord User":
                                f"@{user}\n{user.mention}\nID: {user.id}",
                                "Role": role_name,
                            },
                        ),
                    )

                # Remove their reaction.
                await message.remove_reaction(emoji, user)

                return

        # Fetch the role and then assign it to the user.
        role = get(message.guild.roles, id=self.role_ids[category][role_name])
        await user.add_roles(role)

        # Attempt to send a message to the user, then send a message to the log channel.
        try:
            await user.send(f"Succesfully added the '{role_name}' role.")
        except Exception:
            pass
        finally:
            await log_channel.send(
                "",
                embed=NumEmbed(
                    title=f"{self.singular_names[category]} Join",
                    colour="success",
                    fields={
                        "Number": number,
                        "Username": f"u/{name_info[1]}",
                        "Discord User":
                        f"@{user}\n{user.mention}\nID: {user.id}",
                        "Role": role_name,
                    },
                ),
            )
Beispiel #28
0
    async def verify(self, ctx, username: filter_username) -> None:
        """
        Verify yourself using your Reddit account.
        """
        # Make sure that the user isn't already pending verification.
        if ctx.author.id in self.pending_verification:
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="Verification",
                    description="You are already pending verification.",
                    colour="failure",
                    user=ctx.author,
                ),
            )
            return

        # Make sure that the username is valid.
        if not self.bot.reddit.is_valid_user(username):
            await ctx.send(
                "",
                embed=NumEmbed(
                    title="Verification",
                    description="That is an invalid Reddit user.",
                    colour="failure",
                    user=ctx.author,
                ),
            )

        # Generate a random verification code.
        # Add the user to the pending verification dict.
        verification_code = self.verification_handler.generate_code()
        self.bot.reddit.redditor(username).message(
            "Discord Verification for r/Num",
            self.bot.settings.templates.verification["reddit_pm"].format(
                verification_code=verification_code,
                discord_user_str=str(ctx.author),
                discord_user_id=ctx.author.id,
            ),
        )
        self.pending_verification[ctx.author.id] = {
            "code": verification_code,
            "username": username
        }

        # Send a message to the user asking them to check their Reddit inbox.
        await ctx.send(
            ctx.author.mention,
            embed=NumEmbed(
                title="Verification",
                description=
                "A code has been sent to your Reddit inbox.\nPlease reply with '$confirm (code)' here.",
                colour=0X007E80,
                user=ctx.author,
            ),
        )

        # Send a message to the (pending) verification log channel.
        verif_log_channel = self.bot.get_channel(
            self.bot.settings.discord.ids["log_channels"]["verification_log"])
        await verif_log_channel.send(
            "",
            embed=NumEmbed(
                title="Pending Verification",
                colour=0x000080,
                fields={
                    "Claimed Reddit Name":
                    f"u/{username}",
                    "Discord User":
                    f"{ctx.author}\n{ctx.author.mention}\n{ctx.author.id}",
                },
            ),
        )