Пример #1
0
    async def set_profile_meta(self, ctx: utils.Context,
                               target_user: typing.Optional[discord.Member]):
        """
        Talks a user through setting up a profile on a given server.
        """

        # Set up some variables
        target_user: discord.Member = target_user or ctx.author
        template: localutils.Template = ctx.template

        # See if the user is already setting up a profile
        if self.set_profile_locks[ctx.author.id].locked():
            return await ctx.send("You're already setting up a profile.")

        # Only mods can see other people's profiles
        if target_user != ctx.author and not localutils.checks.member_is_moderator(
                ctx.bot, ctx.author):
            raise commands.MissingPermissions(["manage_roles"])

        # Check if they're already at the maximum amount of profiles
        async with self.bot.database() as db:
            await template.fetch_fields(db)
            user_profiles: typing.List[
                localutils.
                UserProfile] = await template.fetch_all_profiles_for_user(
                    db, target_user.id)
        if template.max_profile_count == 0:
            await ctx.send(
                f"Currently the template **{template.name}** is not accepting any more applications."
            )
            return
        if len(user_profiles) >= template.max_profile_count:
            if target_user == ctx.author:
                await ctx.send(
                    f"You're already at the maximum number of profiles set for **{template.name}**."
                )
            else:
                await ctx.send(
                    f"{target_user.mention} is already at the maximum number of profiles set up for **{template.name}**."
                )
            return

        # See if you we can send them the PM
        try:
            if target_user == ctx.author:
                await ctx.author.send(
                    f"Now talking you through setting up your **{template.name}** profile."
                )
            else:
                await ctx.author.send(
                    f"Now talking you through setting up {target_user.mention}'s **{template.name}** profile.",
                    allowed_mentions=discord.AllowedMentions(users=False))
            await ctx.send("Sent you a DM!")
        except discord.Forbidden:
            return await ctx.send(
                "I'm unable to send you DMs to set up the profile :/")

        # Drag the user into the create profile lock
        async with self.set_profile_locks[ctx.author.id]:

            # See if we need to ask for a name
            if template.max_profile_count == 1:
                suffix = None
                while True:
                    if suffix is None:
                        name_content = "default"
                    else:
                        name_content = f"default{suffix}"
                    if name_content.lower() in [
                            i.name.lower() for i in user_profiles
                    ]:
                        suffix = (suffix or 0) + 1
                    else:
                        break
            else:
                await ctx.author.send(
                    f"What name would you like to give this profile? This will be used to get the profile information (eg for the name \"test\", you could run `get{template.name.lower()} test`)."
                )
                while True:
                    try:
                        user_message = await self.bot.wait_for(
                            "message",
                            timeout=120,
                            check=lambda m: m.author == ctx.author and
                            isinstance(m.channel, discord.DMChannel))
                    except asyncio.TimeoutError:
                        try:
                            return await ctx.author.send(
                                f"Your input for this field has timed out. Please try running `set{template.name}` on your server again."
                            )
                        except discord.Forbidden:
                            return
                    try:

                        # Get the name they gave
                        name_content = localutils.TextField.get_from_message(
                            user_message)

                        # They've misunderstood
                        if f"get{template.name.lower()} " in name_content.lower(
                        ):
                            raise localutils.errors.FieldCheckFailure(
                                f"Please provide the name for your profile _without_ the command call, eg if you wanted to run `get{template.name.lower()} test`, just say \"test\"."
                            )

                        # See if they're already using the name
                        if name_content.lower() in [
                                i.name.lower() for i in user_profiles
                        ]:
                            raise localutils.errors.FieldCheckFailure(
                                "You're already using that name for this template. Please provide an alternative."
                            )

                        # See if the characters used are invalid
                        if any([
                                i for i in name_content
                                if i not in string.ascii_letters +
                                string.digits + ' '
                        ]):
                            raise localutils.errors.FieldCheckFailure(
                                "You can only use standard lettering and digits in your profile name. Please provide an alternative."
                            )
                        break

                    except localutils.errors.FieldCheckFailure as e:
                        await ctx.author.send(e.message)

            # Talk the user through each field
            filled_field_dict = {}
            for field in sorted(template.fields.values(),
                                key=lambda x: x.index):

                # See if it's a command
                if localutils.CommandProcessor.COMMAND_REGEX.search(
                        field.prompt):
                    filled_field_dict[field.field_id] = localutils.FilledField(
                        user_id=target_user.id,
                        name=name_content,
                        field_id=field.field_id,
                        value="Could not get field information",
                        field=field,
                    )
                    continue

                # Send the user the prompt
                if field.optional:
                    await ctx.author.send(
                        f"{field.prompt.rstrip('.')}. Type **pass** to skip this field."
                    )
                else:
                    await ctx.author.send(field.prompt)

                # Get user input
                while True:
                    try:
                        user_message = await self.bot.wait_for(
                            "message",
                            timeout=field.timeout,
                            check=lambda m: m.author == ctx.author and
                            isinstance(m.channel, discord.DMChannel))
                    except asyncio.TimeoutError:
                        try:
                            return await ctx.author.send(
                                f"Your input for this field has timed out. Running `set{template.name}` on your server again to go back through this setup."
                            )
                        except discord.Forbidden:
                            return
                    try:
                        if user_message.content.lower(
                        ) == 'pass' and field.optional:
                            field_content = None
                        else:
                            field_content = field.field_type.get_from_message(
                                user_message)
                        break
                    except localutils.errors.FieldCheckFailure as e:
                        await ctx.author.send(e.message)

                # Add field to list
                filled_field_dict[field.field_id] = localutils.FilledField(
                    user_id=target_user.id,
                    name=name_content,
                    field_id=field.field_id,
                    value=field_content,
                    field=field,
                )

        # Make the UserProfile object
        user_profile = localutils.UserProfile(
            user_id=target_user.id,
            name=name_content,
            template_id=template.template_id,
            verified=template.verification_channel_id is None)
        user_profile.template = template
        user_profile.all_filled_fields = filled_field_dict

        # Make sure the bot can send the embed at all
        try:
            await ctx.author.send(embed=user_profile.build_embed(target_user))
        except discord.HTTPException as e:
            return await ctx.author.send(
                f"Your profile couldn't be sent to you - `{e}`.\nPlease try again later."
            )

        # Delete the currently archived message, should one exist
        current_profile_message = await user_profile.fetch_message(self.bot)
        if current_profile_message:
            try:
                await current_profile_message.delete()
            except discord.HTTPException:
                pass

        # Let's see if this worked
        sent_profile_message = await self.bot.get_cog(
            "ProfileVerification").send_profile_submission(
                ctx, user_profile, target_user)
        if user_profile.template.should_send_message and sent_profile_message is None:
            return
        send_profile_message_id = None
        send_profile_channel_id = None
        if sent_profile_message:
            send_profile_message_id = sent_profile_message.id
            send_profile_channel_id = sent_profile_message.channel.id

        # Database me up daddy
        async with self.bot.database() as db:
            await db(
                """INSERT INTO created_profile (user_id, name, template_id, verified, posted_message_id, posted_channel_id)
                VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (user_id, name, template_id)
                DO UPDATE SET verified=excluded.verified, posted_message_id=excluded.posted_message_id, posted_channel_id=excluded.posted_channel_id""",
                user_profile.user_id, user_profile.name,
                user_profile.template.template_id, user_profile.verified,
                send_profile_message_id, send_profile_channel_id)
            for field in filled_field_dict.values():
                await db(
                    """INSERT INTO filled_field (user_id, name, field_id, value) VALUES ($1, $2, $3, $4)
                    ON CONFLICT (user_id, name, field_id) DO UPDATE SET value=excluded.value""",
                    field.user_id, name_content, field.field_id, field.value)

        # Respond to user
        if template.get_verification_channel_id(target_user):
            await ctx.author.send(
                f"Your profile has been sent to the **{ctx.guild.name}** staff team for verification - please hold tight!"
            )
        else:
            await ctx.author.send("Your profile has been created and saved.")
Пример #2
0
    async def set_profile_meta(self,
                               ctx: utils.Context,
                               *,
                               target_user: discord.Member = None):
        """Talks a user through setting up a profile on a given server"""

        # Set up some variables
        user: discord.Member = ctx.author
        target_user: discord.Member = target_user or user
        profile: utils.Profile = ctx.profile
        fields: typing.List[utils.Field] = profile.fields

        # Only mods can see other people's profiles
        if target_user != ctx.author and not utils.checks.member_is_moderator(
                ctx.bot, ctx.author):
            return await ctx.send(
                f"You're missing the `manage_roles` permission required to do this."
            )

        # Check if they already have a profile set
        user_profile: utils.UserProfile = profile.get_profile_for_member(
            target_user)
        if user_profile is not None:
            if target_user == user:
                await ctx.send(
                    f"You already have a profile set for `{profile.name}`.")
            else:
                await ctx.send(
                    f"{target_user.mention} already has a profile set up for `{profile.name}`."
                )
            return

        # See if you we can send them the PM
        try:
            if target_user == user:
                await user.send(
                    f"Now talking you through setting up a `{profile.name}` profile."
                )
            else:
                await user.send(
                    f"Now talking you through setting up a `{profile.name}` profile for {target_user.mention}."
                )
            await ctx.send("Sent you a DM!")
        except discord.Forbidden:
            return await ctx.send(
                "I'm unable to send you DMs to set up the profile :/")

        # Talk the user through each field
        filled_field_list = []
        for field in fields:

            # Send the user the prompt
            if field.optional:
                await user.send(field.prompt +
                                " (send `skip` to skip this field.)")
            else:
                await user.send(field.prompt)

            # Get user input
            while True:
                try:
                    user_message = await self.bot.wait_for(
                        "message",
                        timeout=field.timeout,
                        check=lambda m: m.author == user and isinstance(
                            m.channel, discord.DMChannel))
                except asyncio.TimeoutError:
                    try:
                        return await user.send(
                            f"Your input for this field has timed out. Please try running `set{profile.name}` on your server again."
                        )
                    except discord.Forbidden:
                        return
                try:
                    if user_message.content.lower(
                    ) == 'skip' and field.optional:
                        field_content = None
                    else:
                        field_content = field.field_type.get_from_message(
                            user_message)
                    break
                except utils.errors.FieldCheckFailure as e:
                    await user.send(e.message)

            # Add field to list
            filled_field = utils.FilledField(target_user.id, field.field_id,
                                             field_content)
            filled_field_list.append(filled_field)

        # Make the UserProfile object
        user_profile = utils.UserProfile(
            user_id=target_user.id,
            profile_id=profile.profile_id,
            verified=profile.verification_channel_id is None)

        # Make sure the bot can send the embed at all
        try:
            await user.send(embed=user_profile.build_embed())
        except discord.HTTPException as e:
            return await user.send(
                f"Your profile couldn't be sent to you, so the embed was probably hecked - `{e}`.\nPlease try again later."
            )

        # Send the profile in for verification
        if profile.verification_channel_id:
            try:
                channel = await self.bot.fetch_channel(
                    profile.verification_channel_id)
                embed = user_profile.build_embed()
                embed.set_footer(
                    text=f'{profile.name.upper()} // Verification Check')
                v = await channel.send(
                    f"New **{profile.name}** submission from {target_user.mention}\n{target_user.id}/{profile.profile_id}",
                    embed=embed)
                await v.add_reaction(self.TICK_EMOJI)
                await v.add_reaction(self.CROSS_EMOJI)
            except discord.HTTPException as e:
                return await user.send(
                    f"Your profile couldn't be sent to the verification channel - `{e}`."
                )
            except AttributeError:
                return await user.send(
                    "The verification channel was deleted from the server - please tell an admin."
                )
        elif profile.archive_channel_id:
            try:
                channel = await self.bot.fetch_channel(
                    profile.archive_channel_id)
                embed = user_profile.build_embed()
                await channel.send(embed=embed)
            except discord.HTTPException as e:
                return await user.send(
                    f"Your profile couldn't be sent to the archive channel - `{e}`."
                )
            except AttributeError:
                pass  # The archive channel being deleted isn't too bad tbh

        # Database me up daddy
        async with self.bot.database() as db:
            try:
                await db(
                    'INSERT INTO created_profile (user_id, profile_id, verified) VALUES ($1, $2, $3)',
                    user_profile.user_id, user_profile.profile.profile_id,
                    user_profile.verified)
            except asyncpg.UniqueViolationError:
                await db(
                    'UPDATE created_profile SET verified=$3 WHERE user_id=$1 AND profile_id=$2',
                    user_profile.user_id, user_profile.profile.profile_id,
                    user_profile.verified)
                await db(
                    'DELETE FROM filled_field WHERE user_id=$1 AND field_id in (SELECT field_id FROM field WHERE profile_id=$2)',
                    user_profile.user_id, user_profile.profile.profile_id)
                self.logger.info(
                    f"Deleted profile for {user_profile.user_id} on UniqueViolationError"
                )
            for field in filled_field_list:
                await db(
                    'INSERT INTO filled_field (user_id, field_id, value) VALUES ($1, $2, $3) ON CONFLICT (user_id, field_id) DO UPDATE SET value=excluded.value',
                    field.user_id, field.field_id, field.value)

        # Respond to user
        if profile.verification_channel_id:
            await user.send(
                f"Your profile has been sent to the **{ctx.guild.name}** staff team for verification - please hold tight!"
            )
        else:
            await user.send("Your profile has been created and saved.")
Пример #3
0
    async def set_profile_meta(self, ctx: utils.Context,
                               target_user: typing.Optional[discord.Member]):
        """
        Talks a user through setting up a profile on a given server.
        """

        # Set up some variables
        target_user: discord.Member = target_user or ctx.author
        template: localutils.Template = ctx.template

        # See if the user is already setting up a profile
        if self.set_profile_locks[ctx.author.id].locked():
            return await ctx.send("You're already setting up a profile.")

        # Only mods can see other people's profiles
        if target_user != ctx.author and not localutils.checks.member_is_moderator(
                ctx.bot, ctx.author):
            raise commands.MissingPermissions(["manage_roles"])

        # Check if they're already at the maximum amount of profiles
        async with self.bot.database() as db:
            await template.fetch_fields(db)
            user_profiles: typing.List[
                localutils.
                UserProfile] = await template.fetch_all_profiles_for_user(
                    db, target_user.id)
        if len(user_profiles) >= template.max_profile_count:
            if target_user == ctx.author:
                await ctx.send(
                    f"You're already at the maximum number of profiles set for **{template.name}**."
                )
            else:
                await ctx.send(
                    f"{target_user.mention} is already at the maximum number of profiles set up for **{template.name}**."
                )
            return

        # See if the template is accepting more profiles
        if template.max_profile_count == 0:
            return await ctx.send(
                f"Currently the template **{template.name}** is not accepting any more applications."
            )

        # See if you we can send them DMs
        try:
            if target_user == ctx.author:
                await ctx.author.send(
                    f"Now talking you through setting up your **{template.name}** profile.",
                )
            else:
                await ctx.author.send(
                    f"Now talking you through setting up {target_user.mention}'s **{template.name}** profile.",
                    allowed_mentions=discord.AllowedMentions(users=False),
                )
            await ctx.send("Sent you a DM!")
        except discord.Forbidden:
            return await ctx.send(
                "I'm unable to send you DMs to set up the profile :/")

        # Drag the user into the create profile lock
        async with self.set_profile_locks[ctx.author.id]:

            # Get a name for the profile
            profile_name = await self.get_profile_name(ctx, template,
                                                       user_profiles)
            if not profile_name:
                return

            # Talk the user through each field
            filled_field_dict = {}
            for field in template.field_list:
                response_field = await self.get_field_content(
                    ctx, profile_name, field, target_user)
                if response_field is None:
                    return
                filled_field_dict[field.field_id] = response_field

        # Make the user profile object and add all of the filled fields
        user_profile = localutils.UserProfile(
            user_id=target_user.id,
            name=profile_name,
            template_id=template.template_id,
            verified=template.verification_channel_id is None)
        user_profile.template = template
        user_profile.all_filled_fields = filled_field_dict

        # Make sure that the embed sends
        try:
            await ctx.author.send(
                embed=user_profile.build_embed(self.bot, target_user))
        except discord.HTTPException as e:
            return await ctx.author.send(
                f"Your profile couldn't be sent to you - `{e}`.\nPlease try again later."
            )

        # Delete the currently archived message
        await user_profile.delete_message(self.bot)

        # Let's see if this worked
        sent_profile_message = await self.bot.get_cog(
            "ProfileVerification").send_profile_submission(
                ctx, user_profile, target_user)
        if user_profile.template.should_send_message and sent_profile_message is None:
            return
        send_profile_message_id = None
        send_profile_channel_id = None
        if sent_profile_message:
            send_profile_message_id = sent_profile_message.id
            send_profile_channel_id = sent_profile_message.channel.id

        # Database me up daddy
        async with self.bot.database() as db:
            try:
                await db(
                    """INSERT INTO created_profile (user_id, name, template_id, verified, posted_message_id, posted_channel_id)
                    VALUES ($1, $2, $3, $4, $5, $6) ON CONFLICT (user_id, name, template_id)
                    DO UPDATE SET verified=excluded.verified, posted_message_id=excluded.posted_message_id,
                    posted_channel_id=excluded.posted_channel_id""",
                    user_profile.user_id, user_profile.name,
                    user_profile.template.template_id, user_profile.verified,
                    send_profile_message_id, send_profile_channel_id)
            except asyncpg.ForeignKeyViolationError:
                return await ctx.author.send(
                    "Unfortunately, it looks like the template was deleted while you were setting up your profile.",
                )
            for field in filled_field_dict.values():
                await db(
                    """INSERT INTO filled_field (user_id, name, field_id, value) VALUES ($1, $2, $3, $4)
                    ON CONFLICT (user_id, name, field_id) DO UPDATE SET value=excluded.value""",
                    field.user_id, profile_name, field.field_id, field.value)

        # Respond to user
        if template.get_verification_channel_id(target_user):
            await ctx.author.send(
                f"Your profile has been sent to the **{ctx.guild.name}** staff team for verification - please hold tight!"
            )
        else:
            await ctx.author.send("Your profile has been created and saved.")