async def nowplaying(self, ctx: cmds.Context, channel: str = None): """See what is now playing on the radio. Use "!nowplaying [<channel>]" to show what is now playing on the radio. Short version is "!np[<channel>]". Leave off <channel> to auto-detect the channel you are tuned to.""" async with ctx.typing(): cmd = ctx.invoked_with chan = None if cmd in ['npgame', 'nprw']: chan = RainwaveChannel.game elif cmd in ['npoc', 'npocr']: chan = RainwaveChannel.ocr elif cmd in ['npcover', 'npcovers', 'npmw', 'npvw']: chan = RainwaveChannel.cover elif cmd in ['npbw', 'npch', 'npchip']: chan = RainwaveChannel.chip elif cmd in ['npall', 'npomni', 'npow']: chan = RainwaveChannel.all elif cmd in ['nowplaying', 'np']: if channel: if channel.lower() in RainwaveChannel.__members__.keys(): chan = RainwaveChannel[channel.lower()] if chan is None: listener_id = await self.get_id_for_user(ctx.author) chan = await self.get_current_channel_for_id(listener_id) if chan is None: await ctx.author.send(self.not_tuned_in) return m = f'Now playing on the {chan.long_name}' d = await self.rw_info(chan.channel_id) event = d.get('sched_current') sched_id = int(event.get('id')) sched_type = event.get('type') sched_name = event.get('name') if sched_type == 'Election' and sched_name: m += f' ({sched_name})' elif sched_type == 'OneUp': m += f' ({sched_name} Power Hour)' song = event.get('songs')[0] embed = self.build_embed(song) m += f': {self.song_string(song)}' if ctx.guild: last = self.bot.config.get(f'rainwave:np:{chan.channel_id}', 0) if sched_id == last: c = f'You can only use **{cmd}** in {ctx.channel.mention} once per song.' await ctx.author.send(c) await ctx.author.send(m, embed=embed) else: self.bot.config.set(f'rainwave:np:{chan.channel_id}', sched_id) await ctx.send(m, embed=embed) else: await ctx.send(m, embed=embed)
async def _play(self, ctx: commands.Context, *, search: str): """Plays a song. If there are songs in the queue, this will be queued until the other songs finished playing. This command automatically searches from various sites if no URL is provided. A list of these sites can be found here: https://rg3.github.io/youtube-dl/supportedsites.html """ if not ctx.voice_state.voice: await ctx.invoke(self._join) async with ctx.typing(): # We are parsing a Spotify link if ("open.spotify.com" in search): song_info = "" if ("playlist" in search): entries = await self.spotify.get_playlist_songs(search) await ctx.send( 'Hai hai! :blush: Time to queue up some songs...') count = 0 for entry in entries: try: time.sleep(5) source = await YTDLSource.create_source( ctx, "%s %s" % (entry["name"], entry["artist"]), loop=self.bot.loop) song = Song(source) await ctx.voice_state.songs.put(song) except Exception as e: await ctx.send("Couldn't queue up %s" % (entry['name'])) await ctx.send("Reason: {}".format(e)) continue count += 1 await ctx.send('Queued up {} songs!'.format(count)) else: song_info = await self.spotify.get_song_info(search) source = await YTDLSource.create_source( ctx, "%s %s" % (song_info["name"], song_info["artist"]), loop=self.bot.loop) song = Song(source) await ctx.voice_state.songs.put(song) await ctx.send('Hai hai! :blush: I shall play {} !'.format( str(source))) # We are parsing a YouTube link else: try: if ("playlist" in search): # we will need to parse a playlist entries = await YTDLSource.get_playlist_entries( ctx, search, loop=self.bot.loop) await ctx.send( 'Hai hai! :blush: Time to queue up some songs...') count = 0 for entry in entries: try: time.sleep(5) source = await YTDLSource.create_source( ctx, entry["id"], loop=self.bot.loop, using_id=True) song = Song(source) await ctx.voice_state.songs.put(song) except Exception as e: await ctx.send("Couldn't queue up %s" % (entry['title'])) await ctx.seed("Reason: {}".format(e)) continue count += 1 await ctx.send('Queued up {} songs!'.format(count)) else: source = await YTDLSource.create_source( ctx, search, loop=self.bot.loop) song = Song(source) await ctx.voice_state.songs.put(song) await ctx.send( 'Hai hai! :blush: I shall play {} !'.format( str(source))) except Exception as e: await ctx.send( 'Gomenasai, Traveler-dono! Something happened :cry: ... {}' .format(str(e)))
async def add_(self, ctx: commands.Context, member: MemberOrID, *, reason: str) -> None: """Warn a user Can also be used as `warn <member> [reason]`""" if get_perm_level(member, await self.bot.db.get_guild_config( ctx.guild.id))[0] >= get_perm_level( ctx.author, await self.bot.db.get_guild_config(ctx.guild.id ))[0]: await ctx.send('User has insufficient permissions') else: guild_config = await self.bot.db.get_guild_config(ctx.guild.id) guild_warns = guild_config.warns warn_punishments = guild_config.warn_punishments warn_punishment_limits = [i.warn_number for i in warn_punishments] warns = list( filter(lambda w: w['member_id'] == str(member.id), guild_warns)) cmd = None punish = False num_warns = len(warns) + 1 fmt = f'You have been warned in **{ctx.guild.name}**, reason: {reason}. This is warning #{num_warns}.' if warn_punishments: punishments = list( filter(lambda x: int(x) == num_warns, warn_punishment_limits)) if not punishments: punish = False above = list( filter(lambda x: int(x) > num_warns, warn_punishment_limits)) if above: closest = min(map(int, above)) cmd = warn_punishments.get_kv('warn_number', closest).punishment if cmd == 'ban': cmd = 'bann' fmt += f' You will be {cmd}ed on warning {closest}.' else: punish = True cmd = warn_punishments.get_kv( 'warn_number', max(map(int, punishments))).punishment if cmd == 'ban': cmd = 'bann' fmt += f' You have been {cmd}ed from the server.' try: await member.send(fmt) except discord.Forbidden: if ctx.author != ctx.guild.me: await ctx.send( 'The user has PMs disabled or blocked the bot.') finally: guild_config = await self.bot.db.get_guild_config(ctx.guild.id) current_date = (ctx.message.created_at + timedelta( hours=guild_config.time_offset)).strftime('%Y-%m-%d') if len(guild_warns) == 0: case_number = 1 else: case_number = guild_warns[-1]['case_number'] + 1 push = { 'case_number': case_number, 'date': current_date, 'member_id': str(member.id), 'moderator_id': str(ctx.author.id), 'reason': reason } await self.bot.db.update_guild_config( ctx.guild.id, {'$push': { 'warns': push }}) if ctx.author != ctx.guild.me: await ctx.send(self.bot.accept) await self.send_log(ctx, member, reason, case_number) # apply punishment if punish: if cmd == 'bann': cmd = 'ban' ctx.command = self.bot.get_command(cmd) ctx.author = ctx.guild.me await ctx.invoke(ctx.command, member, reason=f'Hit warn limit {num_warns}')
'topic': 'topic', 'position': 1, 'nsfw': False, 'last_message_id': 1, } channel_instance = discord.TextChannel(state=unittest.mock.MagicMock(), guild=unittest.mock.MagicMock(), data=channel_data) class MockTextChannel(unittest.mock.AsyncMock): """A mock subclass to mock `discord.TextChannel` objects.""" spec_set = channel_instance # Create a `discord.ext.commands.Context` instance context_instance = Context(message=unittest.mock.MagicMock(), prefix=unittest.mock.MagicMock()) class MockContext(unittest.mock.Mock): """A mock subclass to mock `discord.ext.commands.Context` objects.""" spec_set = context_instance def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self.bot = kwargs.get('bot', MockBot()) self.guild = kwargs.get('guild', MockGuild()) self.author = kwargs.get('author', MockMember()) self.channel = kwargs.get('channel', MockTextChannel())
async def cog_before_invoke( self, ctx: commands.Context ): #Get ourselves a music context! (Only accessable throughout this cog) ctx.voice_state = self.get_voice_state(ctx)
async def giveaway_cmd(self, ctx: Context): if ctx.error: return msg = ctx.message user = ctx.user # Check paused if await RedisDB.instance().is_paused(): await Messages.send_error_dm( msg.author, f"Transaction activity is currently suspended. I'll be back online soon!" ) return # Check roles if not await self.role_check(msg): return elif msg.channel.id in config.Config.instance().get_no_spam_channels(): await Messages.send_error_dm( msg.author, f"You can't start giveaways in this channel") return if 'fee=' not in msg.content or 'duration=' not in msg.content: await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO) await Messages.add_x_reaction(msg) return # Parse message split_content = msg.content.split(' ') cleaned_content = msg.content for split in split_content: if split.startswith('fee='): cleaned_content.replace(split, "") split = split.replace('fee=', '').strip() if not split: continue try: fee = float(split) except ValueError as e: await Messages.add_x_reaction(msg) await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO) return elif split.startswith('duration='): cleaned_content.replace(split, "") split = split.replace('duration=', '').strip() if not split: continue try: duration = int(split) if not ctx.god and (duration < config.Config.instance( ).get_giveaway_min_duration() or duration > config.Config.instance( ).get_giveaway_max_duration()): raise ValueError("Bad duration specified") except ValueError as e: await Messages.add_x_reaction(msg) await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO) return # Find giveaway amount try: giveaway_amount = RegexUtil.find_float(cleaned_content) if Validators.too_many_decimals(giveaway_amount): await Messages.send_error_dm( ctx.message.author, f"You are only allowed to use {Env.precision_digits()} digits after the decimal for giveaway amount." ) ctx.error = True return elif fee > giveaway_amount * config.Config.instance( ).get_giveaway_max_fee_multiplier(): await Messages.add_x_reaction(msg) await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO) return elif giveaway_amount < config.Config.instance( ).get_giveaway_minimum(): await Messages.add_x_reaction(msg) await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO) return except AmountMissingException: await Messages.add_x_reaction(msg) await Messages.send_usage_dm(msg.author, START_GIVEAWAY_INFO) return # See how much they need to make this tip. available_balance = Env.raw_to_amount(await user.get_available_balance()) if giveaway_amount > available_balance: await Messages.add_x_reaction(ctx.message) await Messages.send_error_dm( msg.author, f"Your balance isn't high enough to start this giveaway. You have **{available_balance} {Env.currency_symbol()}**, but this tip would cost you **{giveaway_amount} {Env.currency_symbol()}**" ) return try: # Lock this so concurrent giveaways can't be started/avoid race condition async with RedisLock( await RedisDB.instance().get_redis(), key= f"{Env.currency_symbol().lower()}giveawaylock:{msg.guild.id}", timeout=30, wait_timeout=30) as lock: # See if giveaway already in progress active_giveaway = await Giveaway.get_active_giveaway( server_id=msg.guild.id) if active_giveaway is not None: await Messages.add_x_reaction(msg) await Messages.send_error_dm( msg.author, "There's already a giveaway in progress on this server" ) return # Start giveaway async with in_transaction() as conn: gw = await Giveaway.start_giveaway_user( server_id=msg.guild.id, started_by=user, amount=giveaway_amount, entry_fee=fee, duration=duration, started_in_channel=msg.channel.id, conn=conn) # Create pending TX for this user await Transaction.create_transaction_giveaway( sending_user=user, amount=giveaway_amount, giveaway=gw, conn=conn) # Announce giveaway embed = self.format_giveaway_announcement(gw) await msg.channel.send(embed=embed) for ch in config.Config.instance( ).get_giveaway_announce_channels(): if ch != msg.channel.id: channel = msg.guild.get_channel(ch) if channel is not None: try: await channel.send(embed=embed) except Exception: pass # Start the timer asyncio.create_task(self.start_giveaway_timer(gw)) except LockTimeoutError: await Messages.add_x_reaction(msg) await Messages.send_error_dm( msg.author, "I couldn't start a giveaway, maybe someone else beat you to it as there can only be 1 active at a time." )
async def movie_command(self, ctx: Context) -> None: """ Gets a random snake-related movie from TMDB. Written by Samuel. Modified by gdude. Modified by Will Da Silva. """ # Initially 8 pages are fetched. The actual number of pages is set after the first request. page = random.randint(1, self.num_movie_pages or 8) async with ctx.typing(): response = await self.bot.http_session.get( "https://api.themoviedb.org/3/search/movie", params={ "query": "snake", "page": page, "language": "en-US", "api_key": Tokens.tmdb, }) data = await response.json() if self.num_movie_pages is None: self.num_movie_pages = data["total_pages"] movie = random.choice(data["results"])["id"] response = await self.bot.http_session.get( f"https://api.themoviedb.org/3/movie/{movie}", params={ "language": "en-US", "api_key": Tokens.tmdb, }) data = await response.json() embed = Embed(title=data["title"], color=SNAKE_COLOR) if data["poster_path"] is not None: embed.set_image( url=f"https://images.tmdb.org/t/p/original{data['poster_path']}" ) if data["overview"]: embed.add_field(name="Overview", value=data["overview"]) if data["release_date"]: embed.add_field(name="Release Date", value=data["release_date"]) if data["genres"]: embed.add_field(name="Genres", value=", ".join( [x["name"] for x in data["genres"]])) if data["vote_count"]: embed.add_field( name="Rating", value=f"{data['vote_average']}/10 ({data['vote_count']} votes)", inline=True) if data["budget"] and data["revenue"]: embed.add_field(name="Budget", value=data["budget"], inline=True) embed.add_field(name="Revenue", value=data["revenue"], inline=True) embed.set_footer( text= "This product uses the TMDb API but is not endorsed or certified by TMDb." ) embed.set_thumbnail(url="https://i.imgur.com/LtFtC8H.png") try: await ctx.send(embed=embed) except HTTPException as err: await ctx.send( "An error occurred while fetching a snake-related movie!") raise err from None
async def github_repo_info(self, ctx: commands.Context, *repo: str) -> None: """ Fetches a repositories' GitHub information. The repository should look like `user/reponame` or `user reponame`. """ repo = "/".join(repo) if repo.count("/") != 1: embed = discord.Embed( title=random.choice(NEGATIVE_REPLIES), description="The repository should look like `user/reponame` or `user reponame`.", colour=Colours.soft_red ) await ctx.send(embed=embed) return async with ctx.typing(): repo_data = await self.fetch_data(f"{GITHUB_API_URL}/repos/{quote(repo)}") # There won't be a message key if this repo exists if "message" in repo_data: embed = discord.Embed( title=random.choice(NEGATIVE_REPLIES), description="The requested repository was not found.", colour=Colours.soft_red ) await ctx.send(embed=embed) return embed = discord.Embed( title=repo_data["name"], description=repo_data["description"], colour=discord.Colour.blurple(), url=repo_data["html_url"] ) # If it's a fork, then it will have a parent key try: parent = repo_data["parent"] embed.description += f"\n\nForked from [{parent['full_name']}]({parent['html_url']})" except KeyError: log.debug("Repository is not a fork.") repo_owner = repo_data["owner"] embed.set_author( name=repo_owner["login"], url=repo_owner["html_url"], icon_url=repo_owner["avatar_url"] ) repo_created_at = datetime.strptime(repo_data["created_at"], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y") last_pushed = datetime.strptime(repo_data["pushed_at"], "%Y-%m-%dT%H:%M:%SZ").strftime("%d/%m/%Y at %H:%M") embed.set_footer( text=( f"{repo_data['forks_count']} ⑂ " f"• {repo_data['stargazers_count']} ⭐ " f"• Created At {repo_created_at} " f"• Last Commit {last_pushed}" ) ) await ctx.send(embed=embed)
async def github_user_info(self, ctx: commands.Context, username: str) -> None: """Fetches a user's GitHub information.""" async with ctx.typing(): user_data = await self.fetch_data(f"{GITHUB_API_URL}/users/{quote_plus(username)}") # User_data will not have a message key if the user exists if "message" in user_data: embed = discord.Embed( title=random.choice(NEGATIVE_REPLIES), description=f"The profile for `{username}` was not found.", colour=Colours.soft_red ) await ctx.send(embed=embed) return org_data = await self.fetch_data(user_data["organizations_url"]) orgs = [f"[{org['login']}](https://github.com/{org['login']})" for org in org_data] orgs_to_add = " | ".join(orgs) gists = user_data["public_gists"] # Forming blog link if user_data["blog"].startswith("http"): # Blog link is complete blog = user_data["blog"] elif user_data["blog"]: # Blog exists but the link is not complete blog = f"https://{user_data['blog']}" else: blog = "No website link available" embed = discord.Embed( title=f"`{user_data['login']}`'s GitHub profile info", description=f"```\n{user_data['bio']}\n```\n" if user_data["bio"] else "", colour=discord.Colour.blurple(), url=user_data["html_url"], timestamp=datetime.strptime(user_data["created_at"], "%Y-%m-%dT%H:%M:%SZ") ) embed.set_thumbnail(url=user_data["avatar_url"]) embed.set_footer(text="Account created at") if user_data["type"] == "User": embed.add_field( name="Followers", value=f"[{user_data['followers']}]({user_data['html_url']}?tab=followers)" ) embed.add_field( name="Following", value=f"[{user_data['following']}]({user_data['html_url']}?tab=following)" ) embed.add_field( name="Public repos", value=f"[{user_data['public_repos']}]({user_data['html_url']}?tab=repositories)" ) if user_data["type"] == "User": embed.add_field( name="Gists", value=f"[{gists}](https://gist.github.com/{quote_plus(username, safe='')})" ) embed.add_field( name=f"Organization{'s' if len(orgs)!=1 else ''}", value=orgs_to_add if orgs else "No organizations." ) embed.add_field(name="Website", value=blog) await ctx.send(embed=embed)
async def archiveserveremojis(self, ctx: commands.Context): async with ctx.typing(): await self.partial_emoji_list_to_uploaded_zip( ctx, ctx.guild.emojis)
async def c_add(self, ctx: Context, channel: TextChannel, *, content: str): perm_target: Permissions = channel.permissions_for(ctx.me) perm_this: Permissions = ctx.channel.permissions_for(ctx.me) if not perm_target.send_messages: return ctx.send(f"{ctx.author.mention} 채널 {channel.mention} 에 메시지를 보낼 수 없습니다. " f"권한을 확인해주세요.") if not perm_this.add_reactions: return ctx.send(f"{ctx.author.mention} 이 채널에 이모지를 달 수 없습니다. 권한을 확인해주세요.") if not perm_this.manage_messages: return ctx.send(f"{ctx.author.mention} 이 채널에서 메시지를 지울 수 없습니다. 권한을 확인해주세요.") embed_data = Embed(title="이모지 -> 역할: 새로운 메시지", description=f"제목: {content}").to_dict() embed_data["fields"] = [ { "name": "등록된 이모지 목록", "value": "<없음>", "inline": False, }, { "name": "이모지 추가", "value": "반응할 이모지를 이 메시지에 달아주세요.\n" "끝내려면 ✅ 이모지를 눌러주세요.", "inline": False, } ] print(embed_data) msg = await ctx.send(embed=Embed.from_dict(embed_data)) await msg.add_reaction("✅") def check_same_context(r: Reaction, u: Union[User, Member]): print(r, u) return u.id == ctx.author.id and r.message.id == msg.id def check_same_user(m: Message): print(m) return m.author.id == ctx.author.id first = True reactions: Dict[Reaction, Role] = {} while True: try: if not first and reactions: embed_data["fields"][0]["value"] = "\n".join(f"{k} {v}" for k, v in reactions.items()) else: embed_data["fields"][0]["value"] = "<없음>" embed_data["fields"][1] = { "name": "이모지 추가", "value": "반응할 이모지를 이 메시지에 달아주세요.\n" "끝내려면 ✅ 이모지를 눌러주세요.", "inline": False, } await msg.edit(embed=Embed.from_dict(embed_data)) reaction, user = await self.bot.wait_for("reaction_add", check=check_same_context) if reaction.emoji == "✅": break if (isinstance(reaction.emoji, Emoji) and not reaction.emoji.available) or \ isinstance(reaction.emoji, PartialEmoji): await ctx.send("봇이 이용할 수 없는 이모지입니다. 이모지 등록으로 돌아갑니다.", delete_after=5) continue embed_data["fields"][1]["name"] = f"{reaction}의 역할 설정" embed_data["fields"][1]["value"] = "해당 이모지에 지급할 역할을 멘션해주세요." await msg.edit(embed=Embed.from_dict(embed_data)) try: role_message: Message = await self.bot.wait_for("message", check=check_same_user, timeout=60*10) if not role_message.role_mentions: await ctx.send("올바른 역할을 멘션하지 않았습니다. 이모지 등록으로 돌아갑니다.", delete_after=5) continue target_role = role_message.role_mentions[0] reactions[reaction] = target_role first = False continue except asyncio.TimeoutError: await ctx.send("시간 초과로 등록을 취소했습니다. 이모지 등록으로 돌아갑니다.", delete_after=5) continue except asyncio.TimeoutError: embed_data["fields"][1] = { "name": "이모지 추가", "value": "반응할 이모지를 이 메시지에 달아주세요.\n" "끝내려면 ✅ 이모지를 눌러주세요.", "inline": False, } await msg.edit(embed=Embed.from_dict(embed_data)) except: await ctx.send("오류로 인해 등록을 취소했습니다.") return await ctx.send(traceback.format_exc()) await msg.clear_reactions() del embed_data["fields"][1] embed_data["title"] = content embed_data["description"] = "" embed_data["fields"][0]["name"] = "・" g_c = f"{ctx.guild.id}, {ctx.channel.id}" target_msg = await channel.send(embed=Embed.from_dict(embed_data)) for k in reactions: await target_msg.add_reaction(k.emoji) if g_c not in self.target_messages: self.target_messages[g_c] = {} self.target_messages[g_c][str(target_msg.id)] = \ dict((k.emoji if isinstance(k.emoji, str) else str(k.emoji.id), v.id) for k, v in reactions.items()) print(self.target_messages) self.save_messages() await ctx.send(f"{ctx.author.mention} 등록이 완료되었습니다.")
async def _ore(self, ctx: commands.Context): hm: discord.Message = await ctx.send(embed=discord.Embed( colour=discord.Colour(WHITE))) async with ctx.typing(): await self.ore_inventory(hm, ctx.author)
async def _bal(self, ctx: commands.Context): hm: discord.Message = await ctx.send(embed=discord.Embed( colour=discord.Colour(WHITE))) async with ctx.typing(): await self.detail_money(hm, ctx.author)
async def on_user_error(self, ctx: commands.Context, error: Exception): if isinstance(error, commands.BadArgument): ctx.handled = True await self.bot.send_error( ctx, "ユーザーが見つかりませんでした!", f"`{ctx.message.content.split()[1]}`というユーザーは見つかりませんでした...")
async def adminrole(self, ctx: Context, role: Role): async with ctx.typing(): self._db.set_admin_role(ctx.guild.id, role.id) await ctx.send(':white_check_mark: Role ' + '`{}` is now set as admin role.'.format(role.name))
async def reloadconfigs(self, ctx: commands.Context): """Reloads all server configs from disk""" async with ctx.typing(): Configuration.load_master() await Configuration.initialize(self.bot) await ctx.send("Configs reloaded")
async def hypixel(ctx: commands.Context, player: str, bot: commands.Bot, token, key: str = None): with ctx.typing(): data = await _get(player, token) if data["player"] is None: await ctx.send("That player doesn't seem to exist on hypixel.") return player = data["player"] player_name = player["displayname"] e = _mk_embed(player_name, key.split(" ")[0] if key else None) key = key.lower() if key else key if key is None: e.add_field(name="Previous Names", value=_("\n".join(player["knownAliases"]))) if "mcVersionRp" in player.keys(): e.add_field(name="Minecraft Version", value=_(player["mcVersionRp"])) # .strftime("%x %X") tz = pytz.timezone("America/Winnipeg") time = tz.localize(datetime.fromtimestamp(player["lastLogout"] / 1000)) player_tz = bot.db.lookup_timezone(ctx.author.id)[1] timel = time.astimezone( pytz.timezone(player_tz)) if player_tz else None # e.add_field( name="Last Seen", value= f"{time.strftime('%x %X')} (Server)\n{timel.strftime('%x %X') if timel else 'N/A'} (Local)" if player["lastLogout"] > player["lastLogin"] else "Now") e.add_field(name="Hypixel Level", value=f"{_get_level(player['networkExp'])}") elif key in ["bedwars", "bw"]: player = player["stats"]["Bedwars"] prefixes = { "2v2": "eight_two", "3v3v3v3": "four_three", "4v4v4v4": "four_four", "4v4": "two_four" } s = "```" s += f"Level {_get_level(player['Experience'])}\n" for _prefix in prefixes: w = player[f"{prefixes[_prefix]}_wins_bedwars"] l = player[f"{prefixes[_prefix]}_losses_bedwars"] fk = player[f"{prefixes[_prefix]}_final_kills_bedwars"] k = player[f"{prefixes[_prefix]}_kills_bedwars"] d = player[f"{prefixes[_prefix]}_deaths_bedwars"] s += _prefix.center(13, "=") + "\n" s += f"{w}/{w + l} Won\n" s += f"{k}:{d} KDR ({round(k / d, 2)}\n" s += f"({fk} Final Kills)\n" e.description = s + "```" elif key in ["skywars", "sw"]: player = player["stats"]["SkyWars"] prefixes = { "Solo": "solo", "Solo Normal": "solo_normal", "Solo Insane": "solo_insane", "Teams": "team", "Teams Normal": "teams_normal", "Teams Insane": "teams_insane" } results = [] for _prefix in prefixes: s = [_prefix] vk = player.get(f'void_kills_{prefixes[_prefix]}', "-") w = player.get(f'wins_{prefixes[_prefix]}', "-") g = player.get(f'games_{prefixes[_prefix]}', "-") k = player.get(f'kills_{prefixes[_prefix]}', "-") d = player.get(f'deaths_{prefixes[_prefix]}', "-") s.append(f"{w}/{g} Won".ljust(21, " ")) s.append( f"{k}:{d} KDR ({round(k / d, 2) if '-' not in [k, d] else ''})" .ljust(21, " ")) s.append(f"{vk} Void kills".ljust(21, " ")) results.append(s) e.description = "" for r in results: e.add_field(name=r[0], value="```" + "\n".join(r[1:4]) + "```") elif key in ["uhc"]: player = player["stats"]["UHC"] e.description = "```" \ f"{player['kills']} K\n" \ f"{player['deaths']} D" \ "```" elif key.split(" ")[0] in ["sb", "skyblock"]: player = player["stats"]["SkyBlock"]["profiles"] if len(key.split(" ")) == 1: e.description = f"Profiles:\n" + \ '\n'.join(['-' + pro['cute_name'] for pro in player.values()]) else: for pro in player.values(): if pro["cute_name"].lower() == key.split(" ")[1].lower(): profile_id = pro["profile_id"] name = pro["cute_name"].title() e.url = f"https://sky.lea.moe/stats/{player_name}/{name}" e.title = "Skyblock temporarily disabled" e.description = f"This is currently disabled as it is being redone. Visit " \ f"[here](https://sky.lea.moe/stats/{player_name}/{name}) to view profile info" await ctx.send(embed=e) return # break else: e.description = f"Profiles:\n" + \ '\n'.join(['-' + pro['cute_name'] for pro in player.values()]) await ctx.send(embed=e) return e.title += f" ({name})" async with aiohttp.ClientSession() as sess: async with sess.get( f"https://api.hypixel.net/skyblock/profile?key={token}&profile={profile_id}" ) as resp: status = resp.status data2 = await resp.json() profile = data2["profile"] player_sb_id = data["player"]["uuid"] player_sb = profile["members"][player_sb_id] if "slayer_bosses" in player_sb: e.add_field( name="Slayers", value="\n".join([ f"{k.title()}: {_slayer_level(v.get('xp', 0), k)}" for k, v in player_sb["slayer_bosses"].items() ])) e.add_field( name="Money", value= f"Bank: {round(float(profile.get('banking', {'balance': 0})['balance']), 1)} Coins\n" f"Purse: {round(float(player_sb['coin_purse']), 1)} Coins") e.add_field( name="Experience", value="\n".join([ k.title() + ": " + str(_get_level(player_sb.get(f"experience_skill_{k}", 0))) for k in "alchemy,runecrafting,farming,combat,mining,enchanting,fishing,foraging,carpentry" .split(",") ])) e.add_field( name="Misc", value= f"Fairy Souls: {player_sb.get('fairy_souls_collected', 0)}\n") e.set_footer(text="Click the link for more data.") print(player_sb) elif key in ["raw"]: e.description = await asyncio.get_event_loop().run_in_executor( None, _raw, data) await ctx.send(embed=e)
async def quote(self, ctx: commands.Context, messageid: int): """Quotes the requested message.""" async with ctx.typing(): try: message = LoggedMessage.get(messageid=messageid) embed = discord.Embed(colour=discord.Color(0xd5fff), timestamp=datetime.utcfromtimestamp( message.timestamp)) user = await ctx.bot.get_user_info(message.author) embed.set_author(name=user.name, icon_url=user.avatar_url) embed.set_footer( text= f"Sent in #{self.bot.get_channel(message.channel).name} | Quote requested by {ctx.author.display_name} | {messageid}" ) attachmentsraw = LoggedAttachment.select().where( LoggedAttachment.messageid == messageid) attachments = [] for attachmentraw in attachmentsraw: attachments.append(attachmentraw) if attachments != [] and attachments != None: if len(attachments) == 1 and attachments[0].isImage: embed.set_image( url=self.bot.aes.decrypt(attachments[0].url)) else: for attachment in attachments: embed.add_field(name="Attachment link", value=self.bot.aes.decrypt( attachment.url)) if message.content is not None and message.content is not "": embed.description = self.bot.aes.decrypt(message.content) await ctx.send(embed=embed) await ctx.message.delete() except LoggedMessage.DoesNotExist: dmessage = None for server in ctx.bot.guilds: for txtchannel in server.text_channels: try: dmessage = await txtchannel.get_message(messageid) embed = discord.Embed( colour=discord.Color(0xd5fff), timestamp=datetime.utcfromtimestamp( dmessage.created_at.timestamp())) embed.set_author( name=dmessage.author.name, icon_url=dmessage.author.avatar_url) embed.set_footer( text= f"Sent in #{dmessage.channel.name} | Quote Requested by {ctx.author.display_name} | {dmessage.id}" ) if dmessage.attachments != []: if len(dmessage.attachments) == 1: embed.set_image( url=dmessage.attachments[0].url) else: for attachment in dmessage.attachments: embed.add_field(name="Attachment link", value=attachment.url) if dmessage.content is not None: embed.description = dmessage.content await ctx.send(embed=embed) await ctx.message.delete() for a in dmessage.attachments: LoggedAttachment.get_or_create( id=a.id, url=self.bot.aes.encrypt(a.url), isImage=(a.width is not None or a.width is 0), messageid=dmessage.id) LoggedMessage.create( messageid=messageid, content=self.bot.aes.encrypt(dmessage.content), author=dmessage.author.id, timestamp=dmessage.created_at.timestamp(), channel=dmessage.channel.id) except discord.Forbidden: pass except discord.NotFound: pass if dmessage is None: await ctx.send( "Sorry, I couldn't find that message anywhere")
async def _pict(self, ctx: commands.Context, *args): if len(args) == 0: await ctx.send( content= f"**Insert your argument.**\nExample Command : {get_prefix(ctx.guild.id)}img Maldives" ) else: # Connect with Google Custom Search async with ctx.typing(): search_term: str = " ".join(args) response = self.srv.list(q=search_term, cx=CSE_ID, searchType="image", num=10, fileType='jpg,jpeg,png', safe='active').execute() index: int = 0 max_index = len(response["items"]) link_url: list = [i["link"] for i in response["items"]] # Initiate Embed menus: list = ["⏮️", "⬅️", "⏹", "➡️"] emb = discord.Embed( title=f"Search Picture | Image : {index + 1}/{max_index}", description= f"Searching for {search_term}, Result ({max_index} Entries) : ", colour=WHITE) emb.set_image(url=link_url[index]) emb.set_footer( text= f"Image : {index + 1}/{max_index} | Searched by {ctx.author.nick if ctx.author.nick is not None else ctx.author.name}" ) hm: discord.Message = await ctx.send(embed=emb) for i in menus: await hm.add_reaction(i) try: r: discord.Reaction u: discord.User while True: r, u = await self.bot.wait_for( event="reaction_add", timeout=30.0, check=lambda reaction, user: True if str(reaction.emoji ) in menus and user == ctx.author else False) if str(r.emoji) == "⏮️": if index == 0: continue else: index = 0 elif str(r.emoji) == "⬅️": if index > 0: index -= 1 else: continue elif str(r.emoji) == "⏹": emb = discord.Embed( title= f"Search Picture | Image : {index + 1}/{max_index}", description= f"Searching for {search_term}, Result ({max_index} Entries) : ", colour=WHITE) emb.set_image(url=link_url[index]) emb.set_author(name=ctx.author.name, icon_url=ctx.author.avatar_url) await r.remove(u) await hm.edit(embed=emb) for j in menus: await hm.remove_reaction(j, ctx.me) return else: if index < max_index - 1: index += 1 else: continue emb = discord.Embed( title= f"Search Picture | Image : {index + 1}/{max_index}", description= f"Searching for {search_term}, Result ({max_index} Entries) : ", colour=WHITE) emb.set_image(url=link_url[index]) emb.set_footer( text= f"Searched by {ctx.author.nick if ctx.author.nick is not None else ctx.author.name}" ) await r.remove(u) await hm.edit(embed=emb) except asyncio.TimeoutError: emb.set_footer( text= f"Searched by {ctx.author.nick if ctx.author.nick is not None else ctx.author.name} | Request Timeout" ) await hm.edit(embed=emb) for j in menus: await hm.remove_reaction(j, ctx.me)
async def send(self, ctx: commands.Context, to_user=None, amount=None): try: amount = BotUtils.parseMoney(amount) except: await ctx.send(f"{ctx.author.mention} That's not a valid amount! 🤔" ) return if amount <= 0: await ctx.send(f"{ctx.author.mention} Amount must be over $1") return if to_user == None: ctx.send("You can't send money to no one.") to_user = to_user.strip("<!@>") try: to_user = self.Bot.get_user(int(to_user)) except: await ctx.send(f"{ctx.author.mention} Not a valid user!") return if to_user.id == ctx.author.id: # Blocks people sending themselves money await ctx.send( f"{ctx.author.mention} You can't send yourself money!") return # Get users in the database targetQuery = self.Database.GetFromTable("Users", f"id = {to_user.id}")[0] userQuery = self.Database.GetFromTable("Users", f"id = {ctx.author.id}")[0] targetQueryMoney = BotUtils.parseMoney(targetQuery[3]) userMoney = BotUtils.parseMoney(userQuery[3]) if userMoney >= amount: confirmation = await ctx.send( f"Do you want to give **{to_user}** **${amount:,.2f}**?") conf = await self.createConfirmation(confirmation, ["✅", "❌"], ctx.author) if conf: userMoney -= amount targetQueryMoney += amount # Gives target $amount self.Database.GiveUserMoney(ctx.author.id, userMoney) self.Database.GiveUserMoney(to_user.id, targetQueryMoney) transactionEmbed = discord.Embed( title=f"Transaction {ctx.author} => {to_user}", timestamp=datetime.now(), color=0xF3BF0C) # Sender transactionEmbed.add_field(name="**SENDER**", value=f"{ctx.author}", inline=True) transactionEmbed.add_field(name="**NEW BALANCE**", value=f"||${userMoney:,.2f}||", inline=True) transactionEmbed.add_field(name="**ID**", value=f"{ctx.author.id}", inline=True) # Receiver transactionEmbed.add_field(name="**RECEIVER**", value=f"{to_user}", inline=True) transactionEmbed.add_field( name="**NEW BALANCE**", value=f"||${targetQueryMoney:,.2f}||", inline=True) transactionEmbed.add_field(name="**ID**", value=f"{to_user.id}", inline=True) await ctx.send(embed=transactionEmbed) elif conf == False: await confirmation.edit(content="Transaction cancelled!") return else: await ctx.send( f"{ctx.author.mention} You don't have that much money!")
async def branding_refresh(self, ctx: commands.Context) -> None: """Sync currently available assets with branding repository.""" async with ctx.typing(): await self.refresh() await self.branding_info(ctx)
async def cog_before_invoke(self, ctx: Context): ctx.error = False msg = ctx.message # Check paused if await RedisDB.instance().is_paused(): ctx.error = True await Messages.send_error_dm(msg.author, f"Transaction activity is currently suspended. I'll be back online soon!") return if ctx.command.name == 'send_cmd': try: ctx.send_amount = RegexUtil.find_send_amounts(msg.content) if Validators.too_many_decimals(ctx.send_amount): await Messages.send_error_dm(msg.author, f"You are only allowed to use {Env.precision_digits()} digits after the decimal.") ctx.error = True return elif (ctx.send_amount < 0.01 and Env.banano()) or (ctx.send_amount < 0.000001 and not Env.banano()): await Messages.send_error_dm(msg.author, f"Amount too small") ctx.error = True return except AmountMissingException: await Messages.send_usage_dm(msg.author, SEND_INFO) ctx.error = True return except AmountAmbiguousException: await Messages.send_error_dm(msg.author, "You can only specify 1 amount to send") ctx.error = True return if ctx.command.name in ['send_cmd', 'sendmax_cmd']: # See if user exists in DB user = await User.get_user(msg.author) if user is None: await Messages.send_error_dm(msg.author, f"You should create an account with me first, send me `{config.Config.instance().command_prefix}help` to get started.") ctx.error = True return elif user.frozen: ctx.error = True await Messages.send_error_dm(msg.author, f"Your account is frozen. Contact an admin if you need further assistance.") return # Update name, if applicable await user.update_name(msg.author.name) ctx.user = user # See if they are spammin' withdraw_delay = await user.get_next_withdraw_s() if withdraw_delay > 0: await Messages.send_error_dm(msg.author, f"You need to wait {withdraw_delay}s before you can withdraw again") ctx.error = True return try: ctx.destination = RegexUtil.find_address_match(msg.content) except AddressMissingException: await Messages.send_usage_dm(msg.author, SEND_INFO) ctx.error = True return except AddressAmbiguousException: await Messages.send_error_dm(msg.author, "You can only specify 1 destination address") ctx.error = True return if not Validators.is_valid_address(ctx.destination): await Messages.send_error_dm(msg.author, "The destination address you specified is invalid") ctx.error = True return
async def _play(self, ctx: commands.Context, *, search: str): """Plays a song Can either find a song via its Youtube URL or its keywords """ if not ctx.voice_state.voice: await ctx.invoke(self._join) httpFlag = False if (search.__contains__("https://")): httpFlag = True else: search = "ytsearch10:" + search async with ctx.typing(): try: if not httpFlag: searchList = await YTDLSource.find_source( ctx, search, loop=self.bot.loop) else: source = await YTDLSource.create_source(ctx, search, loop=self.bot.loop) except YTDLError as e: await ctx.send( 'An error occurred while processing this request: {}'. format(str(e))) await ctx.voice_state.stop() del self.voice_states[ctx.guild.id] else: if not httpFlag: embed = (discord.Embed( title='These are the results I found:', color=discord.Color.blurple())) i = 0 for entry in searchList: if i == 5: break else: try: embed.add_field(name='Song #%d' % (i + 1), value=str(entry['title']), inline=False) i += 1 except KeyError: pass sent = await ctx.send( embed=embed) #creates a message object called "sent" i = 0 reactionEmotes = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '❌'] for i in range(0, 6): await sent.add_reaction(reactionEmotes[i]) def check(reaction, user): if user == ctx.author: if str(reaction.emoji) == '1️⃣' or str( reaction.emoji ) == '2️⃣' or str(reaction.emoji) == '3️⃣' or str( reaction.emoji) == '4️⃣' or str( reaction.emoji) == '5️⃣' or str( reaction.emoji) == '❌': return True else: return False else: return False def convertToIdx(emote): if str(emote) == '1️⃣': return 0 elif str(emote) == '2️⃣': return 1 elif str(emote) == '3️⃣': return 2 elif str(emote) == '4️⃣': return 3 elif str(emote) == '5️⃣': return 4 elif str(emote) == '❌': return -1 else: return -2 try: result = await self.bot.wait_for('reaction_add', timeout=30.0, check=check) opt = convertToIdx(result[0]) if opt < 0: await sent.delete() if ctx.voice_state.current == None: await ctx.voice_state.stop() del self.voice_states[ctx.guild.id] if (opt == -2): raise commands.CommandError( 'Invalid reaction!') except asyncio.TimeoutError: await ctx.voice_state.stop() del self.voice_states[ctx.guild.id] else: if (opt >= 0): await sent.delete() urlPost = searchList[opt]['url'] ytURL = "https://www.youtube.com/watch?v=" + urlPost source = await YTDLSource.create_source( ctx, ytURL, loop=self.bot.loop) song = Song(source) await ctx.voice_state.songs.put(song) await ctx.send('Enqueued {}'.format(str(source))) else: song = Song(source) await ctx.voice_state.songs.put(song) await ctx.send('Enqueued {}'.format(str(source)))
async def servers(self, ctx: commands.Context): async with ctx.typing(): async with self.bot.db_engine.acquire() as conn: if ctx.invoked_subcommand is None: servers = await BotServers.load_all(conn, ctx.guild) await ctx.send(servers.printable)
async def train_command(self, ctx: disextc.Context): async with ctx.typing(): time = await self.train() await ctx.send(f'training finished in {time} seconds.')
async def show(self, ctx: commands.Context, *, tank_name: str): """ This command produces a gif of the specified tank. DO NOT USE SLASH COMMANDS """ # Typing Indicator async with ctx.typing(): # variables move_x = [] min_max_y = {"Fish Bowl": (20, 50), "Small Tank": (15, 200), "Medium Tank": (20, 200)} min_max_x = {"Fish Bowl": (-180, 150), "Small Tank": (-180, 360), "Medium Tank": (-800, 720)} fish_size_speed = {'Fish Bowl': 17, 'Small Tank': 18, 'Medium Tank': 25} im = [] fishes = {} fish_y_value = [] files = [] dead_alive = [] golden_inverted_normal = 'normal' fish_selections = [] gif_name = random.randint(1, 1000) tank_types = {"Fish Bowl": "fishbowl", "Small Tank": "Small_Tank_2D", "Medium Tank": "Medium_Tank_2D"} tank_slot = 0 # gets database info for tank async with self.bot.database() as db: selected_fish = await db("""SELECT * FROM user_fish_inventory WHERE user_id = $1 AND tank_fish = $2""", ctx.author.id, tank_name) tank_row = await db("""SELECT * FROM user_tank_inventory WHERE user_id =$1""", ctx.author.id) # Check if the tank exists if not tank_row: return await ctx.send("You have no tanks! use the `firsttank` command to get one!") # finds the tank slot for tank_slot_in in tank_row[0]['tank_name']: if tank_slot_in == tank_name: break else: tank_slot += 1 # finds the type of tank it is and checks if it exists if tank_name not in tank_row[0]['tank_name']: return await ctx.send( f"You have no tank named **{tank_name}**!", allowed_mentions=discord.AllowedMentions.none(), ) tank_info = tank_row[0]['tank_type'][tank_slot] # finds what type of fish it is, then adds the paths to a list, as well as finding the fish's random starting position for selected_fish_types in selected_fish: fishes[selected_fish_types['fish']] = [selected_fish_types['fish_alive']] for name, info in fishes.items(): if "golden" in name: fishes[name].append(name.lstrip("golden_")) name = name.lstrip("golden_") golden_inverted_normal = 'golden' if "inverted" in name: fishes[name].append(name.lstrip("inverted_")) name = name.lstrip("inverted_") golden_inverted_normal = 'inverted' else: fishes[name].append(name) for _, fish_types in self.bot.fish.items(): for fish_type, fish_data in fish_types.items(): if info[1] == fish_data['raw_name']: move_x.append(random.randint(min_max_x[tank_info][0], min_max_x[tank_info][1])) fish_y_value.append(random.randint(min_max_y[tank_info][0], min_max_y[tank_info][1])) fish_selections.append(f"C:/Users/JT/Pictures/Aqua/assets/images/{golden_inverted_normal}_fish_size{fish_data['image'][44:]}") if info[0] is True: dead_alive.append(True) else: dead_alive.append(False) # gif variables file_prefix = "C:/Users/JT/Pictures/Aqua/assets/images" gif_filename = f'{file_prefix}/gifs/actual_gifs/user_tank{gif_name}.gif' # Open our constant images tank_theme = tank_row[0]['tank_theme'][tank_slot] background = Image.open(f"{file_prefix}/background/{tank_theme}_background_{tank_types[tank_info]}.png") midground = Image.open(f"{file_prefix}/background/{tank_theme}_background_{tank_types[tank_info]}_midground.png") foreground = Image.open(f"{file_prefix}/background/{tank_types[tank_info]}.png") for x in range(0, len(fish_selections)): im.append(Image.open(fish_selections[x])) # For each frame of the gif... for _ in range(60): # Add a fish to the background image this_background = background.copy() # adds multiple fish and a midground if its a fishbowl for x in range(0, len(im)): if dead_alive[x] is False: this_background.paste(im[x].rotate(180), (move_x[x], fish_y_value[x]), im[x].rotate(180)) else: this_background.paste(im[x], (move_x[x], fish_y_value[x]), im[x]) move_x[x] += fish_size_speed[tank_info] if move_x[x] > min_max_x[tank_info][1]: move_x[x] = min_max_x[tank_info][0] this_background.paste(midground, (0, 0), midground) this_background.paste(foreground, (0, 0), foreground) # Save the generated image to memory f = io.BytesIO() this_background.save(f, format="PNG") f.seek(0) files.append(f) # Move fish # Save the image sequence to a gif image_handles = [imageio.imread(i) for i in files] imageio.mimsave(gif_filename, image_handles) # Close all our file handles because oh no for i in files: i.close() # Send gif to Discord await ctx.send(file=discord.File(gif_filename))
async def cog_before_invoke(self, ctx: commands.Context): ctx.voice_state = self.get_voice_state(ctx)
async def on_all_error(self, ctx: commands.Context, error): if isinstance(error, commands.CommandOnCooldown): ctx.handled = True await self.bot.send_cooldown_error( ctx, error, 1, 10 )
async def vote_analyze(self, ctx: commands.Context, id: str): ''' 投票の集計結果を表示します ''' msg1 = await ctx.send(f'集計中 {self.bot.custom_emojis.loading}') vote = await q.Vote(self.bot, id).get() if vote is None: return await ctx.send("該当の投票は存在しません") with ctx.typing(): try: msg: discord.Message = await ctx.fetch_message(vote.message) except discord.NotFound: return await self.bot.send_error(ctx, 'メッセージが見つかりません', '該当のメッセージは削除されているようです') args = [ arg[2:] for arg in "".join( msg.embeds[0] .description .split("\n\n")[1:] ).split(" ") if arg != '' ] if not args: args = ['はい', 'いいえ'] points = {i: [k, 0, "", None] for i, k in enumerate(args)} for reaction in msg.reactions: try: idx = alphabet_emojis.index(reaction.emoji) except ValueError: continue points[idx][3] = reaction.emoji points[idx][1] = reaction.count - 1 us = [u for u in await reaction.users().flatten() if u.id != self.bot.user.id] t = 5 if len(us) == 0: points[idx][2] = '-' elif len(us) > t: points[idx][2] = ' '.join( [f'{u.mention}' for u in us[:t]]) + f' ほか{len(us) - t}名' else: points[idx][2] = ' '.join( [f'{u.mention}' for u in us]) if sum([point[1] for point in points.values()]) == 0: return await ctx.send("まだ誰も投票していないようです") def create_image(vt, pts): plt.style.use('dark_background') plt.figure(figsize=(7, 4)) plt.axes().set_aspect('equal') x = [v[1] for v in pts.values()] l = [f"{v[0]}: {v[1]}" for v in pts.values()] f = io.BytesIO() plt.title(vote.description, fontsize=16) plt.rcParams["font.size"] = 14 plt.pie(x, labels=l, startangle=90) plt.savefig(f, format="png") f.seek(0) return discord.File(f, filename="chart.png") file = await self.bot.loop.run_in_executor(None, create_image, vote, points) url = f"https://discord.com/channels/{vote.guild}/{vote.channel}/{vote.message}" embed = discord.Embed(title="投票集計", color=config.theme_color) embed.description = f"[{vote.description}]({url}) by <@{vote.user}>" for idx, point in points.items(): if point[3]: embed.add_field( name=f"{point[3]}: {point[0]} ({point[1]})", value=point[2], inline=False) embed.set_author(name=f"投票ID: {id}", icon_url=self.bot.user.avatar_url) await msg1.edit(content='', embed=embed) await ctx.send(file=file)
async def eval_command(self, ctx: Context, *, code: str = None): """ Run some code. get the result back. We've done our best to make this safe, but do let us know if you manage to find an issue with it! This command supports multiple lines of code, including code wrapped inside a formatted code block. """ if ctx.author.id in self.jobs: await ctx.send( f"{ctx.author.mention} You've already got a job running - please wait for it to finish!" ) return if not code: # None or empty string return await ctx.invoke(self.bot.get_command("help"), "eval") log.info( f"Received code from {ctx.author.name}#{ctx.author.discriminator} for evaluation:\n{code}" ) self.jobs[ctx.author.id] = datetime.datetime.now() # Strip whitespace and inline or block code markdown and extract the code and some formatting info match = FORMATTED_CODE_REGEX.fullmatch(code) if match: code, block, lang, delim = match.group("code", "block", "lang", "delim") code = textwrap.dedent(code) if block: info = (f"'{lang}' highlighted" if lang else "plain") + " code block" else: info = f"{delim}-enclosed inline code" log.trace(f"Extracted {info} for evaluation:\n{code}") else: code = textwrap.dedent( RAW_CODE_REGEX.fullmatch(code).group("code")) log.trace( f"Eval message contains not or badly formatted code, stripping whitespace only:\n{code}" ) try: stripped_lines = [ln.strip() for ln in code.split('\n')] if all([line.startswith('#') for line in stripped_lines]): return await ctx.send( f"{ctx.author.mention} Your eval job has completed.\n\n```[No output]```" ) code = textwrap.indent(code, " ") code = CODE_TEMPLATE.replace("{CODE}", code) await self.rmq.send_json("input", snekid=str(ctx.author.id), message=code) async with ctx.typing(): message = await self.rmq.consume(str(ctx.author.id), **RMQ_ARGS) paste_link = None if isinstance(message, str): output = str.strip(" \n") else: output = message.body.decode().strip(" \n") if "<@" in output: output = output.replace("<@", "<@\u200B") # Zero-width space if "<!@" in output: output = output.replace("<!@", "<!@\u200B") # Zero-width space if ESCAPE_REGEX.findall(output): output = "Code block escape attempt detected; will not output result" else: # the original output, to send to a pasting service if needed full_output = output truncated = False if output.count("\n") > 0: output = [ f"{i:03d} | {line}" for i, line in enumerate(output.split("\n"), start=1) ] output = "\n".join(output) if output.count("\n") > 10: output = "\n".join(output.split("\n")[:10]) if len(output) >= 1000: output = f"{output[:1000]}\n... (truncated - too long, too many lines)" else: output = f"{output}\n... (truncated - too many lines)" truncated = True elif len(output) >= 1000: output = f"{output[:1000]}\n... (truncated - too long)" truncated = True if truncated: try: response = await self.bot.http_session.post( URLs.paste_service.format(key="documents"), data=full_output) data = await response.json() if "key" in data: paste_link = URLs.paste_service.format( key=data["key"]) except Exception: log.exception( "Failed to upload full output to paste service!" ) if output.strip(): if paste_link: msg = f"{ctx.author.mention} Your eval job has completed.\n\n```py\n{output}\n```" \ f"\nFull output: {paste_link}" else: msg = f"{ctx.author.mention} Your eval job has completed.\n\n```py\n{output}\n```" response = await ctx.send(msg) self.bot.loop.create_task( wait_for_deletion(response, user_ids=(ctx.author.id, ), client=ctx.bot)) else: await ctx.send( f"{ctx.author.mention} Your eval job has completed.\n\n```[No output]```" ) finally: del self.jobs[ctx.author.id]
async def prevplayed(self, ctx: cmds.Context, *, args: str = None): """Show what was previously playing on the radio. Use "!prevplayed [<channel>] [<index>]" to show what was previously playing on the radio. Short version is "!pp[<channel>] [<index>]". Leave off <channel> to auto-detect the channel you are tuned to. <index> should be a number from 0 to 4 (default 0). The higher the number, the further back in time you go.""" async with ctx.typing(): cmd = ctx.invoked_with if args is None: args = '' tokens = args.split() chan = None idx = 0 if cmd in ['ppgame', 'pprw']: chan = RainwaveChannel.game elif cmd in ['ppoc', 'ppocr']: chan = RainwaveChannel.ocr elif cmd in ['ppcover', 'ppcovers', 'ppmw', 'ppvw']: chan = RainwaveChannel.cover elif cmd in ['ppbw', 'ppch', 'ppchip']: chan = RainwaveChannel.chip elif cmd in ['ppall', 'ppomni', 'ppow']: chan = RainwaveChannel.all if chan in RainwaveChannel and len(tokens) > 0 and tokens[0].isdigit(): if int(tokens[0]) in range(5): idx = int(tokens[0]) if cmd in ['prevplayed', 'pp']: if len(tokens) > 0: if tokens[0].isdigit() and int(tokens[0]) in range(5): idx = int(tokens[0]) else: if tokens[0].lower() in RainwaveChannel.__members__.keys(): chan = RainwaveChannel[tokens[0].lower()] if len(tokens) > 1: if tokens[1].isdigit() and int(tokens[1]) in range(5): idx = int(tokens[1]) if chan is None: listener_id = await self.get_id_for_user(ctx.author) if listener_id is None: await ctx.author.send(self.nick_not_recognized) return chan = await self.get_current_channel_for_id(listener_id) if chan is None: await ctx.author.send(self.not_tuned_in) return m = f'Previously on the {chan.long_name}' d = await self.rw_info(chan.channel_id) event = d.get('sched_history')[idx] sched_id = int(event.get('id')) sched_type = event.get('type') sched_name = event.get('name') if sched_type == 'Election' and sched_name: m += f' ({sched_name})' elif sched_type == 'OneUp': m += f' ({sched_name} Power Hour)' song = event.get('songs')[0] embed = self.build_embed(song) m += f': {self.song_string(song)}' if ctx.guild: last_sched_id = f'rainwave:pp:{chan.channel_id}:{idx}' if sched_id == self.bot.config.get(last_sched_id, 0): await ctx.author.send(f'You can only use {cmd} in {ctx.channel.mention} once per song.') await ctx.author.send(m, embed=embed) else: self.bot.config.set(last_sched_id, sched_id) await ctx.send(m, embed=embed) else: await ctx.send(m, embed=embed)
async def _parse_arguments(self, ctx: Context): interaction = ctx.interaction if interaction is None: return await self.run_parser(ctx) else: ctx.kwargs = await self.app_command._transform_arguments(interaction, interaction.namespace)