async def manualregister( self, ctx: commands.Context, channel: discord.TextChannel, limit: int, after: discord.Message = None, ): """ Effecture un enregistrement manuel en cas de problème. Vous devez donner les éléments suivants dans l'ordre : - `channel` Le channel où a eu lieu l'inscription - `limit` Le nombre de participants maximum à inscrire - `after` (Optionel) Le lien vers le message à partir duquel il faut vérifier les messages """ guild = ctx.guild if not channel.permissions_for(guild.me).read_message_history: await ctx.send( "Je ne peux pas lire l'historique des messages dans ce channel." ) return participants = [] if after: after = after.created_at blacklist = await self.data.guild(guild).blacklisted() await self.data.guild(guild).current.set([]) async with ctx.typing(): async for message in channel.history(oldest_first=True, after=after): if not MESSAGE_CHECK.match(message.content): return member = message.author if member.id in blacklist: return participants.append(member) if len(participants) >= limit: break if len(participants) < limit: await ctx.send( f"Pas assez de participants trouvés ({len(participants)}/{limit})" ) return await self.data.guild(guild).current.set(participants) await ctx.send( f"Inscription terminée, {len(participants)} membres enregistrés. Envoi du fichier..." ) async with ctx.typing(): content = "\n".join( (str(guild.get_member(x)) for x in participants)) file = text_to_file(content, "participants.txt") await ctx.send(file=file)
async def massmove( self, ctx: commands.Context, from_channel: discord.VoiceChannel, to_channel: discord.VoiceChannel = None, ): """Move all members from one voice channel to another Use double quotes if channel name has spaces""" fails = 0 if not from_channel.members: await ctx.send( chat.error( _("There is no users in channel {}.").format( from_channel.mention))) return if not from_channel.permissions_for(ctx.me).move_members: await ctx.send(chat.error(_("I cant move users from that channel")) ) return if to_channel and not to_channel.permissions_for(ctx.me).connect: await ctx.send(chat.error(_("I cant move users to that channel"))) return async with ctx.typing(): for member in from_channel.members: try: await member.move_to(to_channel, reason=get_audit_reason( ctx.author, _("Massmove"))) except discord.HTTPException: fails += 1 continue await ctx.send( _("Finished moving users. {} members could not be moved.").format( fails))
async def _to_zip_server(self, ctx: commands.Context): """ Get a `.zip` archive of all custom emojis in the server. The returned `.zip` archive can be used for the `[p]emojitools add fromzip` command. """ async with ctx.typing(): count = 0 async with TemporaryDirectory() as temp_dir: for e in ctx.guild.emojis: count += 1 if e.animated: await e.url.save( os.path.join(temp_dir, f"{e.name}.gif")) else: await e.url.save( os.path.join(temp_dir, f"{e.name}.png")) aiozip = AioZipStream(await self.getfiles(temp_dir), chunksize=32768) async with NamedTemporaryFile('wb+') as z: async for chunk in aiozip.stream(): await z.write(chunk) await z.seek(0) zip_file_obj = discord.File( z.name, filename=f"{ctx.guild.name}.zip") return await ctx.send( f"{count} emojis were saved to this `.zip` archive!", file=zip_file_obj)
async def _add_all_reactions_from(self, ctx: commands.Context, message: discord.Message): """Add emojis to this server from all reactions in a message.""" async with ctx.typing(): added_emojis = [] for r in message.reactions: if not r.custom_emoji: continue try: fe = await asyncio.wait_for(ctx.guild.create_custom_emoji( name=r.emoji.name, image=await r.emoji.url.read(), reason= f"EmojiTools: emoji added by {ctx.author.name}#{ctx.author.discriminator}" ), timeout=10) added_emojis.append(fe) except asyncio.TimeoutError: return await ctx.send(TIME_OUT) except discord.Forbidden: return await ctx.send(FORBIDDEN) except commands.CommandInvokeError: return await ctx.send(INVOKE_ERROR) except discord.HTTPException: return await ctx.send(HTTP_EXCEPTION) return await ctx.send( f"{len(added_emojis)} emojis were added to this server: {' '.join([str(e) for e in added_emojis])}" )
async def _add_emojis(self, ctx: commands.Context, *emojis: str): """Add some emojis to this server.""" async with ctx.typing(): added_emojis = [] for e in emojis: try: em = await commands.PartialEmojiConverter().convert( ctx=ctx, argument=e) except commands.BadArgument: return await ctx.send(f"Invalid emoji: {e}") em_image = await em.url.read() try: fe = await asyncio.wait_for(ctx.guild.create_custom_emoji( name=em.name, image=em_image, reason= f"EmojiTools: emoji added by {ctx.author.name}#{ctx.author.discriminator}" ), timeout=10) added_emojis.append(fe) except asyncio.TimeoutError: return await ctx.send(TIME_OUT) except discord.Forbidden: return await ctx.send(FORBIDDEN) except commands.CommandInvokeError: return await ctx.send(INVOKE_ERROR) except discord.HTTPException: return await ctx.send(HTTP_EXCEPTION) return await ctx.send( f"{len(added_emojis)} emojis were added to this server: {' '.join([str(e) for e in added_emojis])}" )
async def _getzip(self, ctx: commands.Context, folder_number: int): """Zip and upload an EmojiTools folder.""" async with ctx.typing(): dirs = sorted(os.listdir(f"{data_manager.cog_data_path(self)}")) for d in dirs: if d.endswith(".zip"): os.remove( os.path.join(f"{data_manager.cog_data_path(self)}", f"{d}")) try: to_zip = dirs[folder_number] except IndexError: return await ctx.send("Invalid folder number.") zip_path = os.path.join(f"{data_manager.cog_data_path(self)}", f"{to_zip}") aiozip = AioZipStream(await self.getfiles(zip_path), chunksize=32768) async with aiofiles.open(zip_path + ".zip", mode='wb+') as z: async for chunk in aiozip.stream(): await z.write(chunk) zip_file_obj = discord.File(zip_path + ".zip") return await ctx.send(file=zip_file_obj)
async def leave_guilds(self, ctx: commands.Context, guilds: list, message: str): data = await self.config.all() unwl_guilds = [guild for guild in guilds if guild.id not in data["whitelist"]] if not unwl_guilds: await ctx.send("There are no servers to leave that aren't whitelisted.") return name_ids = "\n".join([f"{guild.name} - ({guild.id})" for guild in unwl_guilds][:5]) guild_preview = name_ids + ( f"\nand {len(unwl_guilds) - 5} other guilds.." if len(unwl_guilds) > 5 else "" ) msg = await ctx.send( f"Are you sure you want me to leave the following {len(unwl_guilds)} guilds?\n" + box(guild_preview, "py") ) start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS) pred = ReactionPredicate.yes_or_no(msg, ctx.author) try: await self.bot.wait_for("reaction_add", check=pred, timeout=60) except asyncio.TimeoutError: await ctx.send("Action cancelled.") if pred.result is True: async with ctx.typing(): for guild in unwl_guilds: await self.notify_guild(guild, message) await guild.leave() await self.baron_log("mass_leave", guilds=unwl_guilds, author=ctx.author) await ctx.send(f"Done. I left {len(unwl_guilds)} servers.") else: await ctx.send("Action cancelled.")
async def super_massrole( self, ctx: commands.Context, members: list, role: discord.Role, fail_message: str = "Everyone in the server has this role.", adding: bool = True, ): member_list = self.get_member_list(members, role, adding) if not member_list: await ctx.send(fail_message) return verb = "add" if adding else "remove" word = "to" if adding else "from" await ctx.send( f"Beginning to {verb} `{role.name}` {word} **{len(member_list)}** members. " f"This will take around {humanize_timedelta(timedelta=datetime.timedelta(seconds=len(member_list) * 0.75))}." ) async with ctx.typing(): result = await self.massrole(member_list, [role], get_audit_reason(ctx.author), adding) result_text = f"{verb.title()[:5]}ed `{role.name}` {word} **{len(result['completed'])}** members." if result["skipped"]: result_text += ( f"\nSkipped {verb[:5]}ing roles for **{len(result['skipped'])}** members." ) if result["failed"]: result_text += ( f"\nFailed {verb[:5]}ing roles for **{len(result['failed'])}** members." ) await ctx.send(result_text)
async def system_red(self, ctx: commands.Context): """ See what resources [botname] is using. Platforms: Windows, Linux, Mac OS Note: SWAP memory information is only available on Linux. """ # i jolly hope we are logged in... if TYPE_CHECKING: assert self.bot.user is not None async with ctx.typing(): red = (await get_red())["red"] botname = self.bot.user.name if await ctx.embed_requested(): embed = discord.Embed(title=f"{botname}'s resource usage", colour=await ctx.embed_colour()) embed.add_field(name="Resource usage", value=box(red)) await ctx.send(embed=self.finalise_embed(embed)) else: msg = f"**{botname}'s resource usage**\n" msg += box(f"Resource usage\n{red}\n") await ctx.send(msg)
async def system_cpu(self, ctx: commands.Context): """ Get metrics about the CPU. This will show the CPU usage as a percent for each core, and frequency depending on platform. It will also show the time spent idle, user and system as well as uptime. Platforms: Windows, Linux, Mac OS Note: CPU frequency is nominal and overall on Windows and Mac OS, on Linux it's current and per-core. """ async with ctx.typing(): data = await get_cpu() percent = data["percent"] time = data["time"] freq = data["freq"] if await ctx.embed_requested(): embed = discord.Embed(title="CPU Metrics", colour=await ctx.embed_colour()) embed.add_field(name="CPU Usage", value=box(percent)) embed.add_field(name="CPU Times", value=box(time)) extra = data["freq_note"] embed.add_field(name=f"CPU Frequency{extra}", value=box(freq)) await ctx.send(embed=self.finalise_embed(embed)) else: msg = "**CPU Metrics**\n" to_box = f"CPU Usage\n{percent}\n" to_box += f"CPU Times\n{time}\n" extra = data["freq_note"] to_box += f"CPU Frequency{extra}\n{freq}\n" msg += box(to_box) await ctx.send(msg)
async def restore_tags(self, ctx: commands.Context, guild: Optional[discord.Guild] = None): slashtags: Dict[str, SlashTag] = ( self.guild_tag_cache[guild.id] if guild else self.global_tag_cache ) if not slashtags: message = "No slash tags have been created" if guild is not None: message += " for this server" return await ctx.send(message + ".") pred = MessagePredicate.yes_or_no(ctx) try: text = f"Are you sure you want to restore {len(slashtags)} slash tags" if guild is not None: text += " on this server" await self.send_and_query_response( ctx, text + " from the database? (Y/n)", pred, ) except asyncio.TimeoutError: return await ctx.send("Timed out, not restoring slash tags.") if not pred.result: return await ctx.send("Ok, not restoring slash tags.") msg = await ctx.send(f"Restoring {len(slashtags)} slash tags...") async with ctx.typing(): for tag in slashtags.copy().values(): await tag.restore() await self.delete_quietly(msg) await ctx.send(f"Restored {len(slashtags)} slash tags.")
async def chatter_model(self, ctx: commands.Context, model_number: int): """ Switch the active model to one of the three. Default after reload is Medium 0: Small 1: Medium 2: Large (Requires additional setup) """ models = [ENG_SM, ENG_MD, ENG_LG] if model_number < 0 or model_number > 2: await ctx.send_help() return if model_number == 2: await ctx.maybe_send_embed( "Additional requirements needed. See guide before continuing.\n" "Continue?") 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 not pred.result: return self.tagger_language = models[model_number] async with ctx.typing(): self.chatbot = self._create_chatbot() await ctx.maybe_send_embed( f"Model has been switched to {self.tagger_language.ISO_639_1}")
async def chatter_algorithm(self, ctx: commands.Context, algo_number: int, threshold: float = None): """ Switch the active logic algorithm to one of the three. Default after reload is Spacy 0: Spacy 1: Jaccard 2: Levenshtein """ algos = [SpacySimilarity, JaccardSimilarity, LevenshteinDistance] if algo_number < 0 or algo_number > 2: await ctx.send_help() return if threshold is not None: if threshold >= 1 or threshold <= 0: await ctx.maybe_send_embed( "Threshold must be a number between 0 and 1 (exclusive)") return else: self.similarity_algo = threshold self.similarity_algo = algos[algo_number] async with ctx.typing(): self.chatbot = self._create_chatbot() await ctx.tick()
async def chatter_cleardata(self, ctx: commands.Context, confirm: bool = False): """ This command will erase all training data and reset your configuration settings Use `[p]chatter cleardata True` """ if not confirm: await ctx.maybe_send_embed( "Warning, this command will erase all your training data and reset your configuration\n" "If you want to proceed, run the command again as `[p]chatter cleardata True`" ) return async with ctx.typing(): await self.config.clear_all() self.chatbot = None await asyncio.sleep( 10 ) # Pause to allow pending commands to complete before deleting sql data if os.path.isfile(self.data_path): try: os.remove(self.data_path) except PermissionError: await ctx.maybe_send_embed( "Failed to clear training database. Please wait a bit and try again" ) self._create_chatbot() await ctx.tick()
async def next(self, ctx: commands.Context, num_launches: int = 1): """ Show the next launches Use `num_launches` to get more than one. """ # launches = await api.async_next_launches(num_launches) # loop = asyncio.get_running_loop() # # launches = await loop.run_in_executor( # None, functools.partial(self.api.fetch_launch, num=num_launches) # ) # launches = await self.api.async_fetch_launch(num=num_launches) # log.debug(str(launches)) async with ctx.typing(): for x, launch in enumerate(launches): if x >= num_launches: return em = await self._embed_launch_data(launch) if em is not None: try: await ctx.send(embed=em) except discord.HTTPException: await ctx.send(str(launch)) log.exception("Failed to send embed") await asyncio.sleep(2)
async def missions(self, ctx: commands.Context): """Returns all missions of SpaceX.""" async with ctx.typing(): resp = await self._get_data(ctx, "missions") if resp is None: return msg = [] page = 1 for data in resp: description = await self._missions_texts(data) manufacturers = ", ".join(data["manufacturers"]) payloads = ", ".join(data["payload_ids"]) em = discord.Embed( color=await ctx.embed_colour(), title="Name: {name} • ID: {id}".format( name=data["mission_name"], id=data["mission_id"]), description=description, ) em.add_field( name="Infos:", value=("Manufacturer{s}: **{manufacturers}**\n" "Payloads IDs: **{payloads_ids}**").format( s="s" if len(data["manufacturers"]) >= 2 else "", manufacturers=manufacturers, payloads_ids=payloads, ), ) em.set_footer(text="Page {} of {}".format(page, len(resp))) page += 1 msg.append(em) await menu(ctx, msg, DEFAULT_CONTROLS)
async def _site_status_add(self, ctx: commands.Context, sitename: str, url: str): """Set up SiteStatus to monitor a website.""" async with ctx.typing(): async with self.config.guild(ctx.guild).sites() as sites: try: async with aiohttp.ClientSession() as session: async with session.get(url) as response: await ctx.send( f"Site returned status code `{response.status}`." ) except (aiohttp.InvalidURL, aiohttp.ClientConnectorError): return await ctx.send( "There was an error connecting to this site. Is the url valid?" ) sites[sitename] = { "url": url, "status": 200, "channel": None, "online": None, "offline": None, "notify_channel": None, "notify_role": None, "last": None } return await ctx.tick()
async def feedback(self, ctx: commands.Context, *, the_feedback: str): #check if we're in tells or not if type(ctx.message.channel) is not discord.DMChannel: #if not in tells, delete the invocation and send a tell to the user #TODO: do we need to make sure the user can receive tells?? await ctx.message.author.send(f"You need to use the <feedback> command here in DMs!") await ctx.message.delete() else: ctx.feedback = the_feedback #look for mutual servers mutuals = self._get_mutual_servers(ctx.message.author) #TODO: interactively ask which server to send to if len(mutuals) > 1: embed_pages = [] for mutual in mutuals: this_embed = discord.Embed( colour=discord.Colour.from_rgb(r=82, g=182, b=119) ) this_embed.set_author( name="Which server do you want to send feedback to?", icon_url=self.choosey_icon_url) this_embed.add_field(name="Server", value=mutual.name) this_embed.set_thumbnail(url=mutual.icon_url) this_embed.set_footer(text=mutual.id) embed_pages.append(this_embed) await menus.menu(ctx, embed_pages, self.guildChooserControls) elif len(mutuals) == 1: await self._process_feedback(ctx, mutuals[0].id) else: await ctx.send("We don't appear to share any servers!")
async def wikipedia(self, ctx: commands.Context, *, query: str): """Get information from Wikipedia.""" can_not_embed_links = not ctx.channel.permissions_for( ctx.me).embed_links can_not_add_reactions = not ctx.channel.permissions_for( ctx.me).add_reactions only_first_result = can_not_embed_links or can_not_add_reactions async with ctx.typing(): embeds, url = await self.perform_search( query, only_first_result=only_first_result) if not embeds: await ctx.send( error(f"I'm sorry, I couldn't find \"{query}\" on Wikipedia")) elif can_not_embed_links: await ctx.send( warning( f"I'm not allowed to do embeds here, so here's the first result:\n{url}" )) elif can_not_add_reactions: embeds[0].set_author( name=f"Result 1 (I need add reactions permission to show more)" ) await ctx.send(embed=embeds[0]) elif len(embeds) == 1: embeds[0].set_author(name="Result 1 of 1") await ctx.send(embed=embeds[0]) else: count = 0 for embed in embeds: count += 1 embed.set_author(name=f"Result {count} of {len(embeds)}") await menu(ctx, embeds, DEFAULT_CONTROLS, timeout=60.0)
async def _server(self, ctx: commands.Context, folder_name: str = None): """Save to a folder all custom emojis from this server (folder name defaults to server name).""" async with ctx.typing(): if folder_name is None: folder_name = ctx.guild.name folder_path = os.path.join(f'{data_manager.cog_data_path(self)}', f'{folder_name}') try: os.mkdir(folder_path) except OSError: await ctx.send( "The emojis will be added to the existing folder with this name." ) count = 0 for e in ctx.guild.emojis: count += 1 if e.animated: await e.url.save(os.path.join(folder_path, f"{e.name}.gif")) else: await e.url.save(os.path.join(folder_path, f"{e.name}.png")) return await ctx.send(f"{count} emojis were saved to `{folder_name}`.")
async def ownercleanup(self, ctx: Context): """ Cleanup old/missing reaction roles and settings on the bot. Note: This will also clear out reaction roles if the bot is just missing permissions to see the reactions. """ async with ctx.typing(): for guild_id in self.settings: guild = self.bot.get_guild(guild_id) if not guild: continue async with self.config.guild( ctx.guild).reaction_roles() as cur_settings: to_remove = [] for key, role_id in cur_settings.items(): chan_id, message_id, emoji = key.split("-") channel = guild.get_channel(int(chan_id)) if not channel: to_remove.append((key, role_id)) continue message = await channel.fetch_message(int(message_id)) if not message: to_remove.append((key, role_id)) continue role = guild.get_role(int(role_id)) if not role: to_remove.append((key, role_id)) for key, role_id in to_remove: del cur_settings[key] del self.settings[guild.id]["reaction_roles"][key] async with self.config.role_from_id( role_id).reactions() as reactions: reactions.remove(key) await ctx.send(_("I am finished deleting old settings."))
async def _add_emoji(self, ctx: commands.Context, emoji: discord.PartialEmoji, name: str = None): """Add an emoji to this server (leave `name` blank to use the emoji's original name).""" async with ctx.typing(): e_image = await emoji.url.read() e_name = name or emoji.name try: final_emoji = await asyncio.wait_for( ctx.guild.create_custom_emoji( name=e_name, image=e_image, reason= f"EmojiTools: emoji added by {ctx.author.name}#{ctx.author.discriminator}" ), timeout=10) except asyncio.TimeoutError: return await ctx.send(TIME_OUT) except discord.Forbidden: return await ctx.send(FORBIDDEN) except commands.CommandInvokeError: return await ctx.send(INVOKE_ERROR) except discord.HTTPException: return await ctx.send(HTTP_EXCEPTION) return await ctx.send(f"{final_emoji} has been added to this server!")
async def _initialize(self, ctx: commands.Context, enter_true_to_confirm: bool): """Adds current members to the already-redeemed list (except those that joined within time limit).""" if not enter_true_to_confirm: return await ctx.send( "Please provide `true` as the parameter to confirm.") async with ctx.typing(): time_limit = await self.config.guild(ctx.guild).time_limit() if not time_limit: return await ctx.send( f"Please set the time limit first using `{ctx.clean_prefix}referset timelimit`!" ) async with self.config.guild( ctx.guild).already_redeemed() as already_redeemed: async for m in AsyncIter(ctx.guild.members, steps=500): if (m.joined_at and (m.joined_at < (datetime.now() - timedelta(hours=time_limit))) and m.id not in already_redeemed): already_redeemed.append(m.id) return await ctx.send( "The already-redeemed list was updated successfully!")
async def _add_from_reaction(self, ctx: commands.Context, specific_reaction: str, message: discord.Message, new_name: str = None): """Add an emoji to this server from a specific reaction on a message.""" final_emoji = None async with ctx.typing(): for r in message.reactions: if r.custom_emoji and r.emoji.name == specific_reaction: try: final_emoji = await asyncio.wait_for( ctx.guild.create_custom_emoji( name=new_name or r.emoji.name, image=await r.emoji.url.read(), reason= f"EmojiTools: emoji added by {ctx.author.name}#{ctx.author.discriminator}" ), timeout=10) except asyncio.TimeoutError: return await ctx.send(TIME_OUT) except discord.Forbidden: return await ctx.send(FORBIDDEN) except commands.CommandInvokeError: return await ctx.send(INVOKE_ERROR) except discord.HTTPException: return await ctx.send(HTTP_EXCEPTION) if final_emoji: return await ctx.send( f"{final_emoji} has been added to this server!") else: return await ctx.send( f"No reaction called `{specific_reaction}` was found on that message!" )
async def super_massrole( self, ctx: commands.Context, members: list, role: discord.Role, fail_message: str = "Everyone in the server has this role.", adding: bool = True, ): if guild_roughly_chunked(ctx.guild) is False and self.bot.intents.members: await ctx.guild.chunk() member_list = self.get_member_list(members, role, adding) if not member_list: await ctx.send(fail_message) return verb = "add" if adding else "remove" word = "to" if adding else "from" await ctx.send(f"Beginning to {verb} `{role.name}` {word} **{len(member_list)}** members.") async with ctx.typing(): result = await self.massrole(member_list, [role], get_audit_reason(ctx.author), adding) result_text = f"{verb.title()[:5]}ed `{role.name}` {word} **{len(result['completed'])}** members." if result["skipped"]: result_text += ( f"\nSkipped {verb[:5]}ing roles for **{len(result['skipped'])}** members." ) if result["failed"]: result_text += ( f"\nFailed {verb[:5]}ing roles for **{len(result['failed'])}** members." ) await ctx.send(result_text)
async def _to_zip_emojis(self, ctx: commands.Context, *emojis: str): """ Get a `.zip` archive of a list of emojis. The returned `.zip` archive can be used for the `[p]emojitools add fromzip` command. """ async with ctx.typing(): async with TemporaryDirectory() as temp_dir: for e in emojis: try: em = await commands.PartialEmojiConverter().convert( ctx=ctx, argument=e) except commands.BadArgument: return await ctx.send(f"Invalid emoji: {e}") if em.animated: await em.url.save( os.path.join(temp_dir, f"{em.name}.gif")) else: await em.url.save( os.path.join(temp_dir, f"{em.name}.png")) aiozip = AioZipStream(await self.getfiles(temp_dir), chunksize=32768) async with NamedTemporaryFile('wb+') as z: async for chunk in aiozip.stream(): await z.write(chunk) await z.seek(0) zip_file_obj = discord.File(z.name, filename="emojis.zip") return await ctx.send( f"{len(emojis)} emojis were saved to this `.zip` archive!", file=zip_file_obj)
async def send_preview( ctx: commands.Context, pages: list, controls: dict, message: discord.Message, page: int, timeout: float, emoji: str, ): with suppress(discord.NotFound): await message.delete() doc = ctx.search_docs[page] async with ctx.typing(): try: async with ctx.cog.session.get( doc.preview_scene, raise_for_status=True ) as video_preview: video_preview = BytesIO(await video_preview.read()) await ctx.send( embed=pages[page], file=discord.File(video_preview, filename=doc.filename), ) video_preview.close() except aiohttp.ClientResponseError as e: await ctx.send(_("Unable to get video preview: {}").format(e.message)) except discord.HTTPException as e: await ctx.send(_("Unable to send video preview: {}").format(e))
async def _emojis(self, ctx: commands.Context, folder_name: str, *emojis: str): """Save to a folder the specified custom emojis (can be from any server).""" async with ctx.typing(): folder_path = os.path.join(f'{data_manager.cog_data_path(self)}', f'{folder_name}') try: os.mkdir(folder_path) except OSError: await ctx.send( "The emojis will be added to the existing folder with this name." ) for e in emojis: try: em = await commands.PartialEmojiConverter().convert( ctx=ctx, argument=e) except commands.BadArgument: return await ctx.send(f"Invalid emoji: {e}") em_image = await em.url.read() if em.animated: ext = ".gif" else: ext = ".png" async with aiofiles.open(os.path.join(folder_path, f"{em.name}{ext}"), mode='wb') as f: await f.write(em_image) return await ctx.send( f"{len(emojis)} emojis were saved to `{folder_name}`.")
async def _about_cog(self, ctx: commands.Context, version: str): async with ctx.typing(): data = await self._get_data(ctx) description = data["description"] docs = data["docs"] project_link = data["project_link"] if data is None: description = ( "Open Source REST API for rocket, core, capsule, pad, and launch data, " "created and maintained by the developers of the r/SpaceX organization" ) docs = "https://documenter.getpostman.com/view/2025350/RWaEzAiG" project_link = "https://github.com/r-spacex/SpaceX-API" return title_api = "About SpaceX-API:\n" title_cog = "About this cog:\n" desc_api = ( description + "\n**[Docs]({docs})** • **[Project Link]({project})**").format( docs=docs, project=project_link) desc_cog = ( f"Cog version: {version}\nYou can also use " "[Space cog from kennnyshiwa](https://github.com/kennnyshiwa/kennnyshiwa-cogs) " "for more about space in general, space pics, Astronomy Picture of the Day from Nasa, " "ISS location ...") em = discord.Embed(color=await ctx.embed_colour()) em.add_field(name=title_api, value=desc_api) em.add_field(name=title_cog, value=desc_cog) return await ctx.send(embed=em)
async def listcases(self, ctx: commands.Context, *, member: Union[discord.Member, int]): """List cases for the specified member.""" async with ctx.typing(): try: if isinstance(member, int): cases = await modlog.get_cases_for_member(bot=ctx.bot, guild=ctx.guild, member_id=member) else: cases = await modlog.get_cases_for_member(bot=ctx.bot, guild=ctx.guild, member=member) except discord.NotFound: return await ctx.send(_("That user does not exist.")) except discord.HTTPException: return await ctx.send( _("Something unexpected went wrong while fetching that user by ID." )) if not cases: return await ctx.send(_("That user does not have any cases.")) rendered_cases = [] message = "" for case in cases: message += _("{case}\n**Timestamp:** {timestamp}\n\n").format( case=await case.message_content(embed=False), timestamp=datetime.utcfromtimestamp( case.created_at).strftime("%Y-%m-%d %H:%M:%S UTC"), ) for page in pagify(message, ["\n\n", "\n"], priority=True): rendered_cases.append(page) await menu(ctx, rendered_cases, DEFAULT_CONTROLS)
async def dataconversioncommand(self, ctx: commands.Context, v2path: str): """Interactive prompt for importing data from Red V2. Takes the path where the V2 install is, and overwrites values which have entries in both V2 and v3; use with caution. """ resolver = SpecResolver(Path(v2path.strip())) if not resolver.available: return await ctx.send( _( "There don't seem to be any data files I know how to " "handle here. Are you sure you gave me the base " "installation path?" ) ) while resolver.available: menu = _("Please select a set of data to import by number, or 'exit' to exit") for index, entry in enumerate(resolver.available, 1): menu += "\n{}. {}".format(index, entry) menu_message = await ctx.send(box(menu)) try: message = await self.bot.wait_for( "message", check=MessagePredicate.same_context(ctx), timeout=60 ) except asyncio.TimeoutError: return await ctx.send(_("Try this again when you are ready.")) else: if message.content.strip().lower() in ["quit", "exit", "-1", "q", "cancel"]: return await ctx.tick() try: message = int(message.content.strip()) to_conv = resolver.available[message - 1] except (ValueError, IndexError): await ctx.send(_("That wasn't a valid choice.")) continue else: async with ctx.typing(): await resolver.convert(self.bot, to_conv) await ctx.send(_("{} converted.").format(to_conv)) await menu_message.delete() else: return await ctx.send( _( "There isn't anything else I know how to convert here.\n" "There might be more things I can convert in the future." ) )