async def _confirm(ctx: commands.Context) -> bool: """Ask "Are you sure?" and get the response as a bool.""" if ctx.guild is None or ctx.guild.me.permissions_in(ctx.channel).add_reactions: msg = await ctx.send(_("Are you sure?")) # noinspection PyAsyncCall task = start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS, ctx.bot.loop) pred = ReactionPredicate.yes_or_no(msg, ctx.author) try: await ctx.bot.wait_for("reaction_add", check=pred, timeout=30) except asyncio.TimeoutError: await ctx.send(_("Response timed out.")) return False else: task.cancel() agreed = pred.result finally: await msg.delete() else: await ctx.send(_("Are you sure? (y/n)")) pred = MessagePredicate.yes_or_no(ctx) try: await ctx.bot.wait_for("message", check=pred, timeout=30) except asyncio.TimeoutError: await ctx.send(_("Response timed out.")) return False else: agreed = pred.result if agreed is False: await ctx.send(_("Action cancelled.")) return agreed
async def edit( self, ctx: commands.Context, command: str, *, response=None, cooldowns: Mapping[str, int] = None, ask_for: bool = True, ): """Edit an already existing custom command""" ccinfo = await self.db(ctx.guild).commands.get_raw(command, default=None) # Check if this command is registered if not ccinfo: raise NotFound() author = ctx.message.author if ask_for and not response: await ctx.send(_("Do you want to create a 'randomized' custom command? (y/n)")) pred = MessagePredicate.yes_or_no(ctx) try: await self.bot.wait_for("message", check=pred, timeout=30) except TimeoutError: await ctx.send(_("Response timed out, please try again later.")) return if pred.result is True: response = await self.get_responses(ctx=ctx) else: await ctx.send(_("What response do you want?")) try: resp = await self.bot.wait_for( "message", check=MessagePredicate.same_context(ctx), timeout=180 ) except TimeoutError: await ctx.send(_("Response timed out, please try again later.")) return response = resp.content if response: # test to raise ctx.cog.prepare_args(response if isinstance(response, str) else response[0]) ccinfo["response"] = response if cooldowns: ccinfo.setdefault("cooldowns", {}).update(cooldowns) for key, value in ccinfo["cooldowns"].copy().items(): if value <= 0: del ccinfo["cooldowns"][key] if author.id not in ccinfo["editors"]: # Add the person who invoked the `edit` coroutine to the list of # editors, if the person is not yet in there ccinfo["editors"].append(author.id) ccinfo["edited_at"] = self.get_now() await self.db(ctx.guild).commands.set_raw(command, value=ccinfo)
async def _cog_update(self, ctx, cog_name: InstalledCog = None): """Update all cogs, or one of your choosing.""" installed_cogs = set(await self.installed_cogs()) async with ctx.typing(): if cog_name is None: updated = await self._repo_manager.update_all_repos() else: try: updated = await self._repo_manager.update_repo(cog_name.repo_name) except KeyError: # Thrown if the repo no longer exists updated = {} updated_cogs = set(cog for repo in updated for cog in repo.available_cogs) installed_and_updated = updated_cogs & installed_cogs if installed_and_updated: await self._reinstall_requirements(installed_and_updated) await self._reinstall_cogs(installed_and_updated) await self._reinstall_libraries(installed_and_updated) message = _("Cog update completed successfully.") cognames = {c.name for c in installed_and_updated} message += _("\nUpdated: ") + humanize_list(tuple(map(inline, cognames))) else: await ctx.send(_("All installed cogs are already up to date.")) return await ctx.send(message) cognames &= set(ctx.bot.extensions.keys()) # only reload loaded cogs if not cognames: return await ctx.send( _("None of the updated cogs were previously loaded. Update complete.") ) message = _("Would you like to reload the updated cogs?") can_react = ctx.channel.permissions_for(ctx.me).add_reactions if not can_react: message += " (y/n)" query: discord.Message = await ctx.send(message) if can_react: # noinspection PyAsyncCall start_adding_reactions(query, ReactionPredicate.YES_OR_NO_EMOJIS, ctx.bot.loop) pred = ReactionPredicate.yes_or_no(query, ctx.author) event = "reaction_add" else: pred = MessagePredicate.yes_or_no(ctx) event = "message" try: await ctx.bot.wait_for(event, check=pred, timeout=30) except asyncio.TimeoutError: await query.delete() return if pred.result is True: if can_react: with contextlib.suppress(discord.Forbidden): await query.clear_reactions() await ctx.invoke(ctx.bot.get_cog("Core").reload, *cognames) else: if can_react: await query.delete() else: await ctx.send(_("OK then."))
async def mute( self, ctx, users: commands.Greedy[discord.Member], duration: Optional[TimedeltaConverter] = None, *, reason: str = None, ): """Mute users.""" if not users: return await ctx.send_help() if duration is None: duration = timedelta(minutes=10) duration_seconds = duration.total_seconds() guild = ctx.guild roleid = await self.__config.guild(guild).muterole() if roleid is None: await ctx.send( "There is currently no mute role set for this server. If you would like one to be automatically setup then type yes, otherwise type no then one can be set via {}mute roleset <role>" .format(ctx.prefix)) try: pred = MessagePredicate.yes_or_no(ctx, user=ctx.author) msg = await ctx.bot.wait_for("message", check=pred, timeout=60) except asyncio.TimeoutError: return await ctx.send("Alright, cancelling the operation.") if pred.result: await msg.add_reaction("\N{WHITE HEAVY CHECK MARK}") await self.create_muted_role(guild) roleid = await self.__config.guild(guild).muterole() else: await msg.add_reaction("\N{WHITE HEAVY CHECK MARK}") return mutedrole = guild.get_role(roleid) if mutedrole is None: return await ctx.send( f"The mute role for this server is invalid. Please set one up using {ctx.prefix}mute roleset <role>." ) completed = [] failed = [] async with self.__config.muted() as muted: if str(ctx.guild.id) not in muted: muted[str(ctx.guild.id)] = {} for user in users: if user == ctx.author: failed.append(f"{user} - Self harm is bad.") continue if not await is_allowed_by_hierarchy(self.bot, self.__config, guild, ctx.author, user): failed.append( f"{user} - You are not higher than this user in the role hierarchy" ) continue if guild.me.top_role <= user.top_role or user == guild.owner: failed.append( f"{user} - Discord hierarcy rules prevent you from muting this user." ) continue await user.add_roles( mutedrole, reason="Muted by {} for {}{}".format( ctx.author, humanize_timedelta(timedelta=duration), f" | Reason: {reason}" if reason is not None else "", ), ) expiry = datetime.now() + timedelta(seconds=duration_seconds) muted[str(ctx.guild.id)][str(user.id)] = { "time": datetime.now().timestamp(), "expiry": expiry.timestamp(), } await modlog.create_case( ctx.bot, ctx.guild, ctx.message.created_at, "smute", user, ctx.author, reason, expiry, ) log.info( f"{user} muted by {ctx.author} in {ctx.guild} for {humanize_timedelta(timedelta=duration)}" ) completed.append(user) msg = "{}".format( "\n**Reason**: {}".format(reason) if reason is not None else "") if completed: await ctx.send( f"`{humanize_list(list(map(inline, [str(x) for x in completed])))}` has been muted for {humanize_timedelta(timedelta=duration)}.{msg}" ) if failed: failemsg = "\n{}".format("\n".join(failed)) await ctx.send( f"{len(failed)} user{'s' if len(failed) > 1 else ''} failed to be muted for the following reasons.{failemsg}" )
async def bots(self, ctx, state: bool, channel: Optional[discord.TextChannel] = None, *, word: str = None): """Enable highlighting of bot messages. Expects a valid bool. Not passing a word will enable/disable bot highlighting for all highlights. """ channel = channel or ctx.channel check = self.channel_check(ctx, channel) if not check: await ctx.send( "Either you or the bot does not have permission for that channel." ) return if word is None: msg = "enable" if state else "disable" await ctx.send( f"Are you sure you wish to {msg} the highlighting of bot messages for all your highlights? Type yes to confirm otherwise type no." ) try: pred = MessagePredicate.yes_or_no(ctx, user=ctx.author) await ctx.bot.wait_for("message", check=pred, timeout=20) except asyncio.TimeoutError: await ctx.send("Exiting operation.") return if pred.result: async with self.config.channel( channel).highlight() as highlight: highlights = highlight.get(str(ctx.author.id)) if not highlights: return await ctx.send( "You do not have any highlights setup.") for word in highlights: highlight[str(ctx.author.id)][word]["bots"] = state if state: await ctx.send( "Bots will now trigger all of your highlights.") else: await ctx.send( "Bots will no longer trigger on any of your highlights." ) await self.generate_cache() return else: await ctx.send("Cancelling.") return word = word.lower() async with self.config.channel(channel).highlight() as highlight: highlights = highlight.get(str(ctx.author.id)) if not highlights: return await ctx.send("You do not have any highlights setup.") if word not in highlight[str(ctx.author.id)]: return await ctx.send( f"You do not have a highlight for `{word}` setup in {channel}" ) highlight[str(ctx.author.id)][word]["bots"] = state if state: await ctx.send( f"The highlight `{word}` will now be triggered by bots in {channel}." ) else: await ctx.send( f"The highlight `{word}` will no longer be trigged by bots in {channel}." ) await self.generate_cache()
async def joinmessage( self, ctx, raffle: RaffleFactoryConverter, *, join_message: Union[bool, str] ): """Edit the join message of a raffle. Once you provide a join message, you will have the chance to add additional messages, which will be selected at random when a user joins the raffle. Use `0` or `false` to disable this condition. **Arguments:** - `<raffle>` - The name of the raffle. - `<join_message>` - The new joining message. """ async with self.config.guild(ctx.guild).raffles() as r: raffle_data = r.get(raffle, None) if not join_message: with contextlib.suppress(KeyError): del raffle_data["join_message"] return await ctx.send( _( "Join message feature removed from this raffle. It will now use the default." ) ) elif join_message is True: return await ctx.send( _('Please provide a number, or "false" to disable this condition.') ) else: try: raffle_safe_member_scanner(join_message, "join_message") except InvalidArgument as e: return await ctx.send(format_traceback(e)) message = _( "Would you like to add additional end messages to be selected from at random?" ) can_react = ctx.channel.permissions_for(ctx.me).add_reactions if not can_react: message += " (y/n)" message = await ctx.send(message) if can_react: start_adding_reactions(message, ReactionPredicate.YES_OR_NO_EMOJIS) predicate = ReactionPredicate.yes_or_no(message, ctx.author) event_type = "reaction_add" else: predicate = MessagePredicate.yes_or_no(ctx) event_type = "message" try: await self.bot.wait_for(event_type, check=predicate, timeout=30) except asyncio.TimeoutError: await ctx.send( _( 'You took too long to respond. Saving join message as "{}".'.format( join_message ) ) ) if predicate.result: interaction = await start_interactive_message_session( ctx, self.bot, "join_message", message ) if interaction is False: data = join_message await ctx.send( _( "Join message set to what you provided previously: {}".format( join_message ) ) ) else: data = [join_message] + interaction await ctx.send(_("Join messages updated for this raffle.")) else: data = join_message await ctx.send(_("Join message updated for this raffle.")) raffle_data["join_message"] = data await self.clean_guild_raffles(ctx)
await question.delete() await ctx.send("Okay then :D") if not pred.result: await question.delete() return await ctx.send("Canceled!") else: if can_react: with suppress(discord.Forbidden): await question.clear_reactions() await self.config.guild(ctx.guild).set_raw(action, value=None) await ctx.send("Removed the {}!".format(action)) pred = ReactionPredicate.yes_or_no(question, ctx.author) event = "reaction_add" else: pred = MessagePredicate.yes_or_no(ctx) event = "message" try: await ctx.bot.wait_for(event, check=pred, timeout=20) except asyncio.TimeoutError: await question.delete() await ctx.send("Okay then :D") if not pred.result: await question.delete() return await ctx.send("Canceled!") else: if can_react: with suppress(discord.Forbidden): await question.clear_reactions() await self.config.guild(ctx.guild).set_raw(action, value=None) await ctx.send("Removed the {}!".format(action))
async def get_option( self, ctx: commands.Context, *, added_required: bool = False ) -> SlashOption: name_desc = [ "What should the argument name be and description be?", "The argument name and description should be split by a `:`.", "Example: `member:A member of this server.`\n", "*Slash argument names may not exceed 32 characters and can only contain characters " "that are alphanumeric or '_' or '-'.", "The argument description must be less than or equal to 100 characters.*", ] name_pred = MessagePredicate.regex(ARGUMENT_NAME_DESCRIPTION, ctx) await self.send_and_query_response(ctx, "\n".join(name_desc), name_pred) match = name_pred.result name, description = match.group(1), match.group(2) valid_option_types = [ name.lower() for name in SlashOptionType.__members__.keys() if not name.startswith("SUB") ] valid_option_types.append("choices") option_query = [ "What should the argument type be?", f"Valid option types: {humanize_list([inline(n) for n in valid_option_types])}", "(select `string` if you don't understand)", ] option_type = await self.send_and_query_response( ctx, "\n".join(option_query), MessagePredicate.lower_contained_in(valid_option_types, ctx), ) if option_type == "choices": choices = await self.get_choices(ctx) option_type = "STRING" else: choices = [] option_type = SlashOptionType[option_type.upper()] if not added_required: pred = MessagePredicate.yes_or_no(ctx) await self.send_and_query_response( ctx, "Is this argument required? (Y/n)\n*Keep in mind that if you choose to make this argument optional, all following arguments must also be optional.*", pred, ) required = pred.result else: await ctx.send( "This argument was automatically made optional as the previous one was optional.", delete_after=15, ) required = False return SlashOption( name=name, description=description, option_type=option_type, required=required, choices=choices, )
async def _user_report( self, ctx: commands.Context, image_proof_url: str, do_imgur_upload: bool, member: Union[discord.Member, int], ban_message: str, ): """Perform user report.""" description = "" sent = [] is_error = False config_services = await self.config.guild(ctx.guild).services() if isinstance(member, discord.Member): member_id = member.id member_avatar_url = member.avatar_url else: member_id = member member_avatar_url = None # Gather services that can have reports sent to them report_services = [] for service_name, service_config in config_services.items(): if not service_config.get("enabled", False): continue # This service is not enabled service_class = self.all_supported_services.get(service_name, None) if not service_class: continue # This service is not supported try: service_class().report except AttributeError: continue # This service does not support reporting api_key = await self.get_api_key(service_name, config_services) if not api_key: continue # This service needs an API key set to work report_services.append((service_class(), api_key)) # Send error if there are no services to send to if not report_services: await self.send_embed( ctx.channel, self.embed_maker( "Error", discord.Colour.red(), "No services have been set up. Please check `[p]bancheckset service settings` for more details.", member_avatar_url, ), ) return # Upload to Imgur if needed if do_imgur_upload: service_keys = await self.bot.get_shared_api_tokens("imgur") imgur_client_id = service_keys.get("client_id", False) if not imgur_client_id: await ctx.send( error( "This command requires that you have an Imgur Client ID. Please set one with `.imgurcreds`." ) ) return image_proof_url = await Imgur.upload(image_proof_url, imgur_client_id) if not image_proof_url: await ctx.send( error( "Uploading image to Imgur failed. Ban report has not been sent." ) ) return # Ask if the user really wants to do this pred = MessagePredicate.yes_or_no(ctx) await ctx.send( question( f"Are you **sure** you want to send this ban report for **{member}**? (yes/no)" ) ) try: await ctx.bot.wait_for("message", check=pred, timeout=30) except asyncio.TimeoutError: pass if pred.result: pass else: await ctx.send(error("Sending ban report has been canceled.")) return # Send report to services for report_service_tuple in report_services: response = await report_service_tuple[0].report( member_id, report_service_tuple[1], ctx.author.id, ban_message, image_proof_url, ) sent.append(response.service) if response.result and response.reason: description += checkmark( f"**{response.service}:** Sent ({response.reason})\n" ) elif response.result: description += checkmark(f"**{response.service}:** Sent\n") else: is_error = True description += error( f"**{response.service}:** Failure ({response.reason if response.reason else 'No reason given'})\n" ) # Generate results if is_error: await self.send_embed( ctx.channel, self.embed_maker( f"Errors occurred while sending reports for **{member}**", discord.Colour.red(), description, member_avatar_url, ), ) else: await self.send_embed( ctx.channel, self.embed_maker( f"Reports sent for **{member}**", discord.Colour.green(), f"Services: {', '.join(sent)}", member_avatar_url, ), )
async def challengeuser(self, ctx: commands.Context, user: discord.Member, *, reason: str = None): """Make an user pass the captcha again.""" if user.bot: # Do not challenge bot. await ctx.send( "Bots are my friend, I cannot let you do that to them.") return if user is ctx.author: await ctx.send( "Really... REALLY? ARE YOU TRYING TO CHALLENGE YOURSELF?") return if user.top_role >= ctx.author.top_role: await ctx.send( "This user has a role who is higher or equal to your higher role, I " "cannot let you do that.") return data = await self.data.guild(ctx.guild).all() # Get channel verifchannel = data["verifchannel"] if not verifchannel: await ctx.send("There is no verification channel registered.") return channel = self.bot.get_channel(verifchannel) if not channel: await ctx.send( "I cannot find the verification channel, please add one again." ) return # Permissions checker (In case someone changed something meanwhile) needed_permissions = [ "manage_messages", "read_messages", "send_messages", "manage_roles", ] checker = self._permissions_checker(needed_permissions, channel) if isinstance(checker, str): await ctx.send(checker) return # Missing perm(s) await ctx.send( "This will remove all roles to the users that will get challenged, he will" "receive his roles back after passing the captcha or get kicked if fail, " "would you like to continue? (Y/N)") pred = MessagePredicate.yes_or_no(ctx) await self.bot.wait_for("message", check=pred) if not pred.result: await ctx.send("We're sleeping, for now...") return # Start challenge if not reason: reason = ( "Hello [user], a server administrator challenged you for a second time in " "this server, please complete the following captcha. If you fail or take " "too much time to answer (5 minutes), you will be automatically kicked " "from this server.\nNote: The captcha doesn't include space.". replace("[user]", user.mention)) roles = self._roles_keeper(user) await self._roles_remover(user, roles) if data["temprole"]: role = ctx.guild.get_role(data["temprole"]) await user.add_roles(role, reason="Temporary role given by captcha.") async with ctx.typing(): captched, bot_message, user_message = await self.challenger( user, channel, f"Challenged manually by {ctx.author}", reason) final = await channel.send("You {term} the captcha.".format( term="completed" if captched else "failed")) has_been_kicked = False if captched: await self._add_roles(user, roles) await self._report_log(user, "completed", f"Completed captcha.") else: await self._report_log(user, "kick", "Failed captcha.") result = await self._mute_or_unmute_user(channel, user, False) if not result: # Immediate kick await self._kicker(user, "Failed the captcha. (Immediate kick)") has_been_kicked = True await asyncio.sleep(5) if not captched and not has_been_kicked: await self._kicker(user, "Failed the captcha.") await bot_message.delete() await user_message.delete() await final.delete() del self.in_challenge[user.id]
async def applysetup(self, ctx: commands.Context): """Go through the initial setup process.""" bot = self.bot guild = ctx.guild pred = MessagePredicate.yes_or_no(ctx) applicant = get(guild.roles, name="Staff Applicant") channel = get(guild.text_channels, name="applications") await ctx.send( "This will create required channel and role. Do you wish to continue? (yes/no)" ) try: await bot.wait_for("message", timeout=30, check=pred) except asyncio.TimeoutError: return await ctx.send("You took too long. Try again, please.") if pred.result is False: return await ctx.send("Setup cancelled.") if applicant is None: try: await guild.create_role( name="Staff Applicant", reason="Application cog setup" ) except discord.Forbidden: return await ctx.send( "Uh oh. Looks like I don't have permissions to manage roles." ) if channel is None: await ctx.send( "Do you want everyone to see the applications channel? (yes/no)" ) try: await bot.wait_for("message", timeout=30, check=pred) except asyncio.TimeoutError: return await ctx.send("You took too long. Try again, please.") if pred.result is True: overwrites = { guild.default_role: discord.PermissionOverwrite( send_messages=False ), guild.me: discord.PermissionOverwrite(send_messages=True), } else: overwrites = { guild.default_role: discord.PermissionOverwrite( read_messages=False ), guild.me: discord.PermissionOverwrite(read_messages=True), } try: await guild.create_text_channel( "applications", overwrites=overwrites, reason="Application cog setup", ) except discord.Forbidden: return await ctx.send( "Uh oh. Looks like I don't have permissions to manage channels." ) await ctx.send( "You have finished the setup! Please, move your new channel to the category you want it in." )
async def release(self, ctx, id: int): """Release a pokémon.""" conf = await self.user_is_global(ctx.author) if not await conf.has_starter(): return await ctx.send( _( "You haven't picked a starter pokemon yet! Check out {prefix} before trying to release a pokemon." ).format(prefix=ctx.clean_prefix) ) if id <= 0: return await ctx.send(_("The ID must be greater than 0!")) async with ctx.typing(): result = self.cursor.execute( SELECT_POKEMON, (ctx.author.id,), ).fetchall() pokemons = [None] for data in result: pokemons.append([json.loads(data[0]), data[1]]) if not pokemons: return await ctx.send(_("You don't have any pokémon, trainer!")) if id >= len(pokemons): return await ctx.send( _( "You don't have a pokemon at that slot.\nID refers to the position within your pokémon listing.\nThis is found at the bottom of the pokemon on `[p]list`" ) ) pokemon = pokemons[id] name = self.get_name(pokemon[0]["name"], ctx.author) await ctx.send( _( "You are about to free {name}, if you wish to continue type `yes`, otherwise type `no`." ).format(name=name) ) try: pred = MessagePredicate.yes_or_no(ctx, user=ctx.author) await ctx.bot.wait_for("message", check=pred, timeout=20) except asyncio.TimeoutError: await ctx.send("Exiting operation.") return if pred.result: msg = "" userconf = await self.user_is_global(ctx.author) pokeid = await userconf.pokeid() if id < pokeid: msg += _( "\nYour default pokemon may have changed. I have tried to account for this change." ) await userconf.pokeid.set(pokeid - 1) elif id == pokeid: msg += _( "\nYou have released your selected pokemon. I have reset your selected pokemon to your first pokemon." ) await userconf.pokeid.set(1) self.cursor.execute( "DELETE FROM users where message_id = ?", (pokemon[1],), ) await ctx.send(_("Your {name} has been freed.{msg}").format(name=name, msg=msg)) else: await ctx.send(_("Operation cancelled."))
async def open(self, ctx: commands.Context, *, title: str): """Open a new issue. Does NOT reopen.""" try: token = await self._get_token(ctx) repo = await self._get_repo(ctx) except EXCEPTIONS as e: return await self._handle_error(ctx, e) await ctx.send( "Your next message will be the description of the issue. If you answer exactly " "`cancel` I won't make an issue.\n" "You've got 5 minutes, remember the 2000 Discord character limit!") try: answer: discord.Message = await self.bot.wait_for( "message", check=MessagePredicate.same_context(ctx), timeout=300.0) except TimeoutError: return await ctx.send("Aborting.") if answer.content.casefold() == "cancel": return await ctx.send("Aborting.") else: description = answer.content await ctx.send( "Do you want to add one or more labels to this issue? (yes or no, 15 seconds)" ) pred = MessagePredicate.yes_or_no(ctx) try: answer = await self.bot.wait_for("message", check=pred, timeout=15.0) except TimeoutError: return await ctx.send("Aborting.") to_add: List[str] = [] if pred.result is True: repo_labels = await GitHubAPI.get_repo_labels(token, repo) rl_names = [] for label in repo_labels: rl_names.append(label["name"]) avaliable_labels = inline_hum_list(rl_names) await ctx.send( "You have 30 seconds, please say what label you want to add. Any invalid input " "will be ignored. This is case sensitive. Say `exit` to abort creating the issue, " f"or **`create` to make the issue**.\n\nAvaliable labels: {avaliable_labels}" ) def check(msg: discord.Message): return (msg.author == ctx.author and msg.channel == ctx.channel and (msg.content in rl_names or msg.content.casefold() in ["create", "exit"])) to_add = [] while True: try: answer = await self.bot.wait_for("message", check=check, timeout=30.0) except TimeoutError: await ctx.send("Timeout on this label.") break if answer.content.casefold() == "exit": await ctx.send("Exiting. No changes were saved.") return if answer.content.casefold() == "create": break elif answer.content in to_add: await ctx.send( "It looks like that label's already on the issue. Choose another, 30 " "seconds.") continue to_add.append(answer.content) rl_names.remove(answer.content) avaliable_labels = inline_hum_list(rl_names) used_labels = inline_hum_list(to_add) await ctx.send( "Label added. Again, 30 seconds. Say another label name if you want to add " "more, `create` to create the issue or `exit` to exit without saving.\n\n" f"Avaliable labels: {avaliable_labels}\nLabels currently on issue: " f"{used_labels}") try: resp = await GitHubAPI.create_issue(token, repo, title, description, to_add) except EXCEPTIONS as e: return await self._handle_error(ctx, e) await ctx.send("Created issue {}: {}".format( resp.get("number"), "<{}>".format(resp.get("html_url"))))
async def _user_report( self, ctx: commands.Context, image_proof_url: str, do_imgur_upload: bool, member: Union[discord.Member, int], ban_message: str, ): """Perform user report.""" description = "" sent = [] is_error = False config_services = await self.config.guild(ctx.guild).services() for service_name, service_config in config_services.items(): if not service_config.get("enabled", False): continue service_class = self.all_supported_services.get( service_name, False) if not service_class: continue api_key = await self.get_api_key(service_name, config_services) if not api_key: continue try: service_class().report except AttributeError: continue # This service does not support reporting if do_imgur_upload: service_keys = await self.bot.get_shared_api_tokens("imgur") imgur_client_id = service_keys.get("client_id", False) if not imgur_client_id: await ctx.send( error( "This command requires that you have an Imgur Client ID. Please set one with `.imgurcreds`." )) return image_proof_url = await Imgur.upload(image_proof_url, imgur_client_id) if not image_proof_url: await ctx.send( error( "Uploading image to Imgur failed. Ban report has not been sent." )) return pred = MessagePredicate.yes_or_no(ctx) await ctx.send( question( "Are you **sure** you want to send this ban report for **{}**? (yes/no)" .format(member))) try: await ctx.bot.wait_for("message", check=pred, timeout=30) except asyncio.TimeoutError: pass if pred.result: pass else: await ctx.send(error("Sending ban report has been canceled.")) return if isinstance(member, discord.Member): member_id = member.id member_avatar_url = member.avatar_url else: member_id = member member_avatar_url = None response = await service_class().report(member_id, api_key, ctx.author.id, ban_message, image_proof_url) sent.append(response.service) if response.result and response.reason: description += "**{}:** Sent ({})\n".format( response.service, response.reason) elif response.result: description += "**{}:** Sent\n".format(response.service) elif not response.result and response.reason: is_error = True description += "**{}:** Failure ({})\n".format( response.service, response.reason) else: is_error = True description += "**{}:** Failure (HTTP error {})\n".format( response.service, response.http_status) if is_error: await self.send_embed( ctx.channel, self.embed_maker( "Errors occured while sending reports for **{}**".format( member), discord.Colour.red(), description, member_avatar_url, ), ) elif not sent: await self.send_embed( ctx.channel, self.embed_maker( "Error", discord.Colour.red(), "No services have been set up. Please check `[p]bancheckset` for more details.", member_avatar_url, ), ) else: await self.send_embed( ctx.channel, self.embed_maker( "Reports sent for **{}**".format(member), discord.Colour.green(), "Services: {}".format(", ".join(sent)), member_avatar_url, ), )