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.")
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.")
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.")