Exemple #1
0
class Verification(Cog):
    def __init__(self, bot):
        self.bot = bot
        self.log = bot.log.getChild(type(self).__name__)
        
    async def _verify(self, member, auto=True):
        await ctx.send(embed=Embed(title='Welcome to HackTAMS', color=Color.orange(), description='To start the verification process enter the email you used to register for hackTAMS\nEx: `[email protected]`').set_footer(text="If you haven't registered yet you can do so at hacktams.org/register"))
        msg = await self.bot.wait_for('message',timeout=60 * (int(auto)+1) check=lambda msg:msg.author.id==member.id, msg.channel.id==member.id)
        
    @Cog.listener()
    async def on_member_join(self, member):  # put the member data into storage
        self.log.info(f"{member.name} has joined {member.guild.name}")

        await self._verify(member)

    @command()
    @check_any(dm_only(), has_permissions(administrator=True))
    async def verify(self, ctx, member:Member=None):
        if not ctx.channel:
            member = ctx.author 
        member = member or ctx.author 

        await self._verify(member, auto=False)

    @Cog.listener()
    async def on_member_leave(
        self, member
    ):  # move the member out of storage (?) might not
        self.log.info(f"{member.name} has left {member.guild.name}")

    @Cog.listener()
    async def on_member_verify(
        self, member, data
    ):  # whenever a user verifies we can edit stuff on them like roles
        await member.edit(name=data["name"])
}

logger = logging.getLogger('logs_uploader')
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.INFO)
logger.addHandler(handler)

# Don't post to team channels and force the guild used so testing can you DMs
DISCORD_TESTING = bool(os.getenv('DISCORD_TESTING'))
# Just post all messages to calling channel, allow DMs
DISCORD_DEBUG = bool(os.getenv('DISCORD_DEBUG'))
if DISCORD_TESTING or DISCORD_DEBUG:
    # Allow DMs in testing
    guild_only = commands.check_any(commands.guild_only(),
                                    commands.dm_only())  # type: ignore
    # print all debug messages
    logger.setLevel(logging.DEBUG)
    handler.setLevel(logging.DEBUG)
else:
    guild_only = commands.guild_only()

bot = commands.Bot(command_prefix='!')


async def log_and_reply(ctx: commands.Context, error_str: str) -> None:
    logger.error(error_str)
    await ctx.reply(error_str)


async def get_channel(
Exemple #3
0
    Bot command check: Returns `true` if the message is sent to the
    proper channel.
    Throws `WrongChannelError` on failure.
    """
    def predicate(context: commands.Context):
        if postentries_channel != 0 and context.channel.id == postentries_channel:
            return True

        raise WrongChannelError()

    return commands.check(predicate)


@client.command()
@commands.check(is_admin)
@commands.check_any(is_postentries_channel(), commands.dm_only())
async def postentries(context: commands.Context) -> None:
    """
    Post the entries of the week to the current channel.
    Works in the postentries channel or in DMs.
    """
    week = compo.get_week(False)
    await publish_entries(context, week)


@client.command()
@commands.check(is_admin)
@commands.dm_only()
async def postentriespreview(context: commands.Context) -> None:
    """
    Post the entries of the next week. Only works in DMs.
Exemple #4
0
class management(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        self.brackets = ["()", "{}", "[]", "<>", None]

    @commands.command(
        aliases=["set_prefix"],
        description="Changes the prefix for the bot in the server")
    @commands.check_any(commands.has_permissions(manage_guild=True),
                        commands.dm_only())
    async def change_prefix(self, ctx, new_prefix):
        if not ctx.guild:
            f = open("data/prefixes-dm", "r")
            prefixes = f.read().split("\n")[:-1]
            f.close()
            for i in range(len(prefixes)):
                if prefixes[i][:prefixes[i].index(":")] == str(ctx.author.id):
                    prefixes[i] = f"{ctx.author.id}:{new_prefix}"
                    await ctx.send("Prefix changed")
            output = ""
            f = open("data/prefixes-dm", "w+")
            for user in prefixes:
                output += f"{user}\n"
            f.write(output)
            return
        f = open("data/prefixes", "r")
        prefixes = f.read().split("\n")[:-1]
        f.close()
        for i in range(len(prefixes)):
            if prefixes[i][:prefixes[i].index(":")] == str(ctx.guild.id):
                prefixes[i] = f"{ctx.guild.id}:{new_prefix}"
                await ctx.send("Prefix changed")
        output = ""
        f = open("data/prefixes", "w+")
        for guild in prefixes:
            output += f"{guild}\n"
        f.write(output)

    @commands.command(
        description=
        f"Changes the bot's nickname to include the prefix in a specified set of brackets in a specified place."
    )
    @commands.has_permissions(manage_nicknames=True)
    async def update_username(self, ctx, brackets=None, place=None):
        if not ctx.guild:
            await ctx.send("I have no nickname here")
            return
        if not brackets:
            await ctx.send("Resetting username")
            await ctx.guild.me.edit(nick=self.bot.user.name)
        if not brackets in self.brackets:
            await ctx.send(
                f"Unrecognised brackets\nSuitable brackets are {self.brackets}"
            )
            return
        place = place.lower()
        if not place in ["end", "start"]:
            await ctx.send(
                "Unsuitable place - Please type \"end\" or \"start\"")
            return
        try:
            if place == "end":
                await ctx.guild.me.edit(
                    nick=
                    f"{self.bot.user.name} {brackets[0]}{ctx.prefix}{brackets[1]}"
                )
            else:
                await ctx.guild.me.edit(
                    nick=
                    f"{brackets[0]}{ctx.prefix}{brackets[1]} {self.bot.user.name}"
                )
        except Exception as e:
            await ctx.send(e)
class ElectionCog(commands.Cog):
    def __init__(self, bot):
        self.bot = bot
        data = load(
            open("electionsbot/applications.json", "r", encoding="UTF-8"))
        self.candidateData = data["candidates"]
        self.candidates = {}
        self.voteSessions = {}
        # CONFIG SETTINGS
        self.CHOICE_MINIMUM = 2
        self.CHOICE_MAXIMUM = 2
        self.ready = False
        self.START_TIME = datetime.utcfromtimestamp(data["starttime"])
        self.END_TIME = datetime.utcfromtimestamp(data["endtime"])
        self.CREATION_CUTOFF = datetime.utcfromtimestamp(
            data["creationcutoff"])
        # This will be automatically disabled if the number of candidates reaches 20.
        self.REACTION_INTERFACE = False

    @commands.Cog.listener()
    async def on_ready(self):
        # Load up all the relevant candidates!
        self.backendGuild = self.bot.get_guild(EMOJI_SERVER_ID)
        emoji = await self.backendGuild.fetch_emojis()
        for id, info in self.candidateData.items():
            candidate = Candidate(id)
            user = self.bot.get_user(int(id))
            if user:
                candidate.discorduser = user
                candidate.username = user.name + "#" + user.discriminator
                candidate.avatar = user.avatar_url_as(format='png', size=32)
                emojiimage = await candidate.avatar.read()
                emojiname = re.sub(r"\W+", "",
                                   (user.name[:28] +
                                    user.discriminator).replace(" ", "_"))
                for x in emoji:
                    if x.name == emojiname:
                        candidate.emoji = x
                        break
                else:
                    candidate.emoji = await self.backendGuild.create_custom_emoji(
                        name=emojiname, image=emojiimage)
            else:
                candidate.username = info.get("username")
                candidate.avatar = info.get("avatar")
                try:
                    if candidate.avatar:
                        emojiimage = urllib.request.urlopen(
                            url=candidate.avatar)
                    else:
                        emojiimage = urllib.request.urlopen(
                            url="https://cdn.beano.dev/question-mark.png")
                except urllib.error.HTTPError:
                    emojiimage = urllib.request.urlopen(
                        url="https://cdn.beano.dev/question-mark.png")
                emojiname = re.sub(r"\W+", "",
                                   candidate.username.replace(" ", "_"))
                for x in emoji:
                    if x.name == emojiname:
                        candidate.emoji = x
                        break
                else:
                    candidate.emoji = await self.backendGuild.create_custom_emoji(
                        name=emojiname, image=emojiimage.read())
            candidate.campaign = info.get("campaign")
            self.candidates[int(id)] = candidate
        print(self.candidates)
        if len(self.candidates) >= 20:
            self.REACTION_INTERFACE = False
        self.ready = True

    def getCandidate(self, candidateID):
        return self.candidates.get(int(candidateID))

    def getCandidateFromName(self, candidateName):
        for c in self.candidates.values():
            if c.username == candidateName:
                return c
        return None

    def getCandidateFromEmoji(self, emoji):
        for c in self.candidates.values():
            if c.emoji == emoji:
                return c
        return None

    def getAllCandidates(self):
        candidates = list(self.candidates.values())
        print(candidates)
        random.shuffle(
            candidates)  # Randomise the order each time for neutrality.
        return candidates

    @commands.command(aliases=["list"])
    async def candidateList(self, ctx):
        names = [
            str(candidate.emoji) + " " + candidate.username
            for candidate in self.getAllCandidates()
        ]
        await ctx.send(
            f"In a random order, the candidates currently standing are:\n"
            f"{chr(10).join(names)}")

    @commands.check_any(commands.has_role(ROOT_ROLE_ID), commands.dm_only())
    @commands.command(aliases=["listAll"])
    async def allCandidateDetails(self, ctx):
        for candidate in self.getAllCandidates():
            await ctx.send(embed=candidate.getEmbed())

    @commands.has_role(ROOT_ROLE_ID)
    @commands.command(aliases=["electiontotals"])
    async def viewTotals(self, ctx):
        votes = await (await connectPostgres()).fetch(
            """select candidate, count(*) as votecount from (
        select vote_1 as candidate from votes
        union all select vote_2 from votes
    ) t1 group by candidate order by votecount DESC""")
        out = ""
        for candidateid, count in votes:
            candidate = self.getCandidate(candidateid)
            out += f"{str(candidate.emoji)} {candidate.username}[{candidate.id}]: {count}\n"
        await ctx.send(
            f"The following is each member, followed by their number of votes.\n {out}"
        )

    # @commands.has_role(ROOT_ROLE_ID)
    @commands.command(aliases=["myvote"])
    async def viewMyVote(self, ctx):
        if not isinstance(ctx.channel, discord.DMChannel):
            return await ctx.channel.send(
                "You can only use this command in DMs.")
        votes = await (await connectPostgres()).fetch(
            "SELECT voter_id, vote_1, vote_2, datetime FROM votes WHERE voter_id=$1",
            ctx.author.id,
        )
        if len(votes) > 0:
            vote = votes[0]
            chosen = [vote[1], vote[2]]
            candidates = [
                c for c in self.getAllCandidates() if int(c.id) in chosen
            ]
            names = [
                str(candidate.emoji) + " " + candidate.username
                for candidate in candidates
            ]
            return await ctx.send("You voted for: \n" + chr(10).join(names))
        else:
            return await ctx.send("You haven't voted yet!")

    @commands.command(aliases=["start"])
    async def vote(self, ctx):
        if not self.ready:
            return await ctx.send("I'm just getting ready, hold on!")
        if not isinstance(ctx.channel, discord.DMChannel):
            return await ctx.send("You can only use this command in DMs.",
                                  delete_after=20)
        if self.START_TIME > datetime.utcnow(
        ) or self.END_TIME < datetime.utcnow():
            return await ctx.send("Voting is currently closed.")
        if self.CREATION_CUTOFF < ctx.author.created_at:
            return await ctx.send(
                "This account was created after the cutoff, and is therefore not eligible to vote."
            )
        guild = self.bot.get_guild(VOTE_SERVER_ID)
        member = guild.get_member(ctx.author.id)
        # If the member exists on the server, the user has a role equal to or greater than the level 5 role in list
        # OR has Death To Bots; and Muted isn't the greatest role, continue, else return a message and abort vote.
        if not (member and (guild.get_role(LEVEL_ROLE_ID) <= member.top_role
                            or guild.get_role(DTB_ROLE_ID) in member.roles)
                and not guild.get_role(MUTED_ROLE_ID) == member.top_role):
            return await ctx.send(
                "In order to be eligible to vote, you must have reached at least Mee6 Level 5, "
                "and not currently be muted.")
        if (len(await (await connectPostgres()).fetch(
                "SELECT voter_id FROM votes WHERE voter_id=$1", ctx.author.id))
                > 0):
            return await ctx.send("You have already voted!")
        currentSession = self.voteSessions.get(ctx.author.id)
        if currentSession and not currentSession.hasTimedOut():
            return await ctx.send(
                "You already have an active voting session! Please use that, or wait for it to expire."
            )
        # Check to see if the person has already voted
        self.voteSessions[ctx.author.id] = VoteSession(user=ctx.author,
                                                       timeout=300)

        if self.REACTION_INTERFACE:
            message = await ctx.send(
                "Click on the reactions representing the users you wish to vote for. Once you're "
                "done, react with a ✅ to confirm. If you need more time to decide, just ignore this message.\n\n"
                "**Remember, you can only vote for exactly two candidates, and "
                "you can't change your mind once you confirm!**\n\n"
                "*This prompt will expire in 5 minutes.*")
            for emoji in [
                    candidate.emoji for candidate in self.getAllCandidates()
            ]:
                await message.add_reaction(emoji)
            self.voteSessions[ctx.author.id].setMessage(message)
            await message.add_reaction("✅")
        else:
            names = [
                str(candidate.emoji) + " " + candidate.username
                for candidate in self.getAllCandidates()
            ]
            message = await ctx.send(
                "Run the `:choose <candidate>` command, specifying the Discord Name of the user you wish to "
                "vote for; and repeat this for each choice you wish to make.\n"
                "If you want to cancel a choice, use `unchoose <candidate>`. \nOnce you're "
                "done, run the `confirm` command to confirm. To view a candidate's statement, use "
                "`candidate <candidate>`, or `listall` if you want them all at once."
                " If you need more time to decide, just ignore this message.\n\n"
                "**Remember, you can only vote for exactly two candidates, and"
                " you can't change your mind once you confirm!**\n\n"
                "As a reminder, in a random order, the candidates currently standing are:\n"
                f"**{chr(10).join(names)}**\n\n"
                "*This session will expire in 5 minutes.*")

    @commands.command(aliases=["lock", "submit"])
    async def confirm(self, ctx):
        await self.confirm_callback(ctx.channel, ctx.author)

    async def confirm_callback(self, channel, author):
        if not self.ready:
            return await channel.send("I'm just getting ready, hold on!")
        if not isinstance(channel, discord.DMChannel):
            return await channel.send("You can only use this command in DMs.",
                                      delete_after=20)
        voteSession = self.voteSessions.get(author.id)
        if not voteSession:
            return await author.send(
                "You don't have a vote session active to confirm.")
        user = author
        chosenCandidates = voteSession.choices
        await user.send(
            f"You are about to vote for the following candidates:\n** "
            f'{chr(10).join([str(c.emoji) + " " + c.username for c in chosenCandidates])}**'
        )
        if len(chosenCandidates) > self.CHOICE_MAXIMUM:
            return await user.send(
                f"""You've selected too many candidates. You can only select a maximum of {self.CHOICE_MAXIMUM} \
                candidates.
                Please modify your choices, and confirm again once done.""")
        if len(chosenCandidates) < self.CHOICE_MINIMUM:
            return await user.send(
                f"""You've selected too few candidates! You need to select a minimum of {self.CHOICE_MINIMUM} \
                candidates.
                Please modify your choices, and confirm again once done.""")
        else:
            m = await user.send(
                "Are you sure you wish to vote this way? React with a ✅ to finalise, or a 🚫 to cancel.\n"
                "*This prompt will expire in 90 seconds. Reactions will appear after 5 seconds.*"
            )
            self.voteSessions[user.id].setMessage(m)
            self.voteSessions[user.id].confirm()
            self.voteSessions[user.id].setTimeout(95)

            await sleep(5)
            await m.add_reaction("✅")
            await m.add_reaction("🚫")

    @commands.command(aliases=["info", "candidate"])
    async def candidateInfo(self, ctx, *, candidate: User):
        info = self.candidates.get(int(candidate.id))
        print(info)
        if not info:
            await ctx.send(
                "We couldn't find any information on that candidate! They may not be running, or we might "
                "have identified the wrong user (We think you're asking about "
                f"`{candidate.name + '#' + candidate.discriminator}`).")
        else:
            await ctx.send(embed=info.getEmbed())

    @commands.command(aliases=["pick", "select"])
    async def choose(self, ctx, *, candidate: User):
        if not isinstance(ctx.channel, discord.DMChannel):
            await ctx.message.delete()
            return await ctx.send("You can only use this command in DMs.",
                                  delete_after=20)
        info = self.candidates.get(int(candidate.id))
        voteSession = self.voteSessions.get(ctx.author.id)
        if not voteSession:
            return await ctx.send(
                "You must have an active votesession to make a choice. Use the vote "
                "command to start a votesession.")
        if voteSession.state != "PICK":
            return await ctx.send(
                "You cannot modify your choices if you are confirming them.")
        if not info:
            await ctx.send(
                "We couldn't find that candidate! (We think you're asking about "
                f"`{candidate.name + '#' + candidate.discriminator}`).")
        else:
            voteSession.addChoice(info)
            await ctx.send(f"Added {info.username} as a choice.")

    @commands.command(aliases=["unpick", "deselect"])
    async def unchoose(self, ctx, *, candidate: User):
        if not isinstance(ctx.channel, discord.DMChannel):
            await ctx.message.delete()
            return await ctx.send("You can only use this command in DMs.",
                                  delete_after=20)
        info = self.candidates.get(int(candidate.id))
        voteSession = self.voteSessions.get(ctx.author.id)
        if not voteSession:
            return await ctx.send(
                "You must have an active votesession to make a choice. Use the vote "
                "command to start a votesession.")
        if voteSession.state != "PICK":
            return await ctx.send(
                "You cannot modify your choices if you are confirming them.")
        if not info:
            await ctx.send(
                "We couldn't find that candidate! (We think you're asking about "
                f"`{candidate.name + '#' + candidate.discriminator}`).")
        else:
            voteSession.removeChoice(info)
            await ctx.send(f"Removed {info.username} as a choice.")

    @commands.has_role(ROOT_ROLE_ID)
    @commands.command()
    async def clearvote(self, ctx, voter: User):
        await (await connectPostgres()).execute(
            "DELETE FROM votes WHERE voter_id=$1", voter.id)
        await ctx.send(f"{voter.mention}'s votes have been cleared")
        return await voter.send(
            f"Your votes were cleared by <@{ctx.author.id}> - you can now place a new set."
        )

    @commands.Cog.listener()
    async def on_reaction_add(self, reaction, user):
        """Reaction interface - Suited for under 20 members"""
        voteSession = self.voteSessions.get(user.id)
        if not voteSession or not self.ready:
            return
        if voteSession.message.id != reaction.message.id:
            return
        if (reaction.emoji == "✅" and not voteSession.hasTimedOut()
                and voteSession.state == "PICK"):
            voteSession.clearChoice()
            reactions = reaction.message.reactions
            for reaction in reactions:
                if user in await reaction.users().flatten():
                    choice = self.getCandidateFromEmoji(reaction.emoji)
                    if choice:
                        voteSession.addChoice(choice)
            await self.confirm_callback(reaction.channel, user)
        elif not voteSession.hasTimedOut() and voteSession.state == "CONFIRM":
            if reaction.emoji == "✅":
                # Votes have been confirmed. Go for it!
                await self.voteSessions[user.id].commit()
                # Committed the vote, now delete the session
                del self.voteSessions[user.id]
                await user.send("Your vote has been confirmed! Thank you :)")
            elif reaction.emoji == "🚫":
                # Vote was canceled, just delete the session without committing.
                del self.voteSessions[user.id]
                await user.send(
                    "OK, I've canceled the voting session. When you've made up your mind, use the command again."
                )
        elif voteSession.hasTimedOut():
            del self.voteSessions[user.id]
            await user.send(
                "The voting session you had has now expired; you need to start a new one."
            )

    @commands.Cog.listener()
    async def on_user_update(self, before, after):
        # Check if the user is a candidate
        if after.id in self.candidates.keys():
            # Update the relevant candidate information
            candidate = self.getCandidate(after.id)
            user = after
            await self.backendGuild.fetch_emojis()
            candidate.discorduser = after
            candidate.username = user.name + "#" + user.discriminator
            candidate.avatar = user.avatar_url.avatar_url_as(format='png',
                                                             size=32)
            emojiimage = await candidate.avatar.read()
            emojiname = re.sub(r"\W+", "",
                               candidate.username.replace(" ", "_"))
            await candidate.emoji.delete()
            candidate.emoji = await self.backendGuild.create_custom_emoji(
                name=emojiname, image=emojiimage)

# Variables

# Constants

LOCALISATION_PATH = os.path.join(os.path.dirname(__file__), 'localisation', '')

COMMANDES_PATH = os.path.join(os.path.dirname(__file__), 'commandes', '')
COMMANDES = import_modules_from_path(COMMANDES_PATH)

DM_COMMANDES_PATH = os.path.join(os.path.dirname(__file__), 'dm_commandes', '')
DM_COMMANDES = import_modules_from_path(DM_COMMANDES_PATH)

BOT = customBot(command_prefix="!",
                localisation_path=LOCALISATION_PATH,
                localisation='es')

# Commands
for commande in COMMANDES:
    BOT.command()(commande)

for dm_commande in DM_COMMANDES:
    BOT.command()(commands.dm_only()(dm_commande))


# Events
@client.event
async def on_ready():
    print('Beepboop o0/')