async def pug(self, ctx: commands.Context, *args): self.logger.debug( f'{ctx.author}: {ctx.prefix}{ctx.invoked_with} {ctx.args[2:]}') random_teams: bool = False map_arg: str = None team1_captain_arg: discord.Member = None team2_captain_arg: discord.Member = None for arg in args: if arg == 'random': random_teams = True self.logger.debug('Random Teams Enabled') elif arg in current_map_pool: map_arg = arg self.logger.debug(f'Force Selected Map = {map_arg}') else: member: discord.Member = await commands.MemberConverter( ).convert(ctx, arg) if member in ctx.author.voice.channel.members: if team1_captain_arg is None: team1_captain_arg = member self.logger.debug( f'Forced Team 1 Captain = {team1_captain_arg}') elif team2_captain_arg is None and member is not team1_captain_arg: team2_captain_arg = member self.logger.debug( f'Forced Team 2 Captain = {team2_captain_arg}') else: if member is team1_captain_arg: raise commands.CommandError( message= f'One user cannot be captain of 2 teams.') else: raise commands.CommandError( message=f'You can only set 2 captains.') else: raise commands.CommandError( message=f'Invalid Argument: `{arg}`') if not self.pug.enabled: self.logger.info('Pug called from queue as pug is disabled') if len(self.bot.queue_captains) > 0: team1_captain_arg = self.bot.queue_captains.pop(0) self.logger.debug( f'Forced Team 1 Captain = {team1_captain_arg}') if len(self.bot.queue_captains) > 0: team2_captain_arg = self.bot.queue_captains.pop(0) self.logger.debug( f'Forced Team 2 Captain = {team2_captain_arg}') # TODO: Refactor this mess db = Database('sqlite:///main.sqlite') await db.connect() csgo_server = self.bot.servers[0] for server in self.bot.servers: if server.available: server.available = False csgo_server = server break channel_original = ctx.author.voice.channel players: List[discord.Member] = ctx.author.voice.channel.members.copy() players = players[:self.bot.match_size] if self.bot.dev: players = [ctx.author] * 10 self.logger.info( 'Filling list of players with the message author because bot is in dev mode' ) if random_teams: shuffle(players) team1 = players[:len(players) // 2] team2 = players[len(players) // 2:] team1_captain = team1[0] team2_captain = team2[0] message_text = 'Random Teams' message = await ctx.send(message_text) embed = self.player_veto_embed(players_text='Random Teams', team1=team1, team1_captain=team1_captain, team2=team2, team2_captain=team2_captain) await message.edit(content=message_text, embed=embed) self.logger.debug(f'Random Team1: {team1}') self.logger.debug(f'Random Team2: {team2}') else: emojis = emoji_bank.copy() del emojis[len(players) - 2:len(emojis)] emojis_selected = [] team1 = [] team2 = [] if team1_captain_arg is not None: team1_captain = team1_captain_arg else: team1_captain = players[randint(0, len(players) - 1)] self.logger.debug(f'team1_captain = {team1_captain}') team1.append(team1_captain) players.remove(team1_captain) if team2_captain_arg is not None: team2_captain = team2_captain_arg else: team2_captain = players[randint(0, len(players) - 1)] self.logger.debug(f'team2_captain = {team1_captain}') team2.append(team2_captain) players.remove(team2_captain) current_team_player_select = 1 current_captain = team1_captain player_veto_count = 0 message = await ctx.send( f'{self.bot.match_size} man time\nLoading player selection...') for emoji in emojis: await message.add_reaction(emoji) emoji_remove = [] player_veto = [] if self.bot.match_size == 2: player_veto = [1, 1] for i in range(self.bot.match_size - 2): if i == 0 or i == self.bot.match_size - 3: player_veto.append(1) elif i % 2 == 0: player_veto.append(2) self.logger.debug(f'player_veto = {player_veto}') while len(players) > 0: message_text = '' players_text = '' if current_team_player_select == 1: message_text += f'<@{team1_captain.id}>' current_captain = team1_captain elif current_team_player_select == 2: message_text += f'<@{team2_captain.id}>' current_captain = team2_captain self.logger.debug( f'current_captain (captain currently selected) = {current_captain}' ) message_text += f' select {player_veto[player_veto_count]}\n' message_text += 'You have 60 seconds to choose your player(s)\n' i = 0 for player in players: players_text += f'{emojis[i]} - <@{player.id}>\n' i += 1 embed = self.player_veto_embed(players_text=players_text, team1=team1, team1_captain=team1_captain, team2=team2, team2_captain=team2_captain) await message.edit(content=message_text, embed=embed) if len(emoji_remove) > 0: for emoji in emoji_remove: await message.clear_reaction(emoji) emoji_remove = [] selected_players = 0 seconds = 0 while True: await asyncio.sleep(1) message = await ctx.fetch_message(message.id) for reaction in message.reactions: users = await reaction.users().flatten() if current_captain in users and selected_players < player_veto[ player_veto_count] and not ( reaction.emoji in emojis_selected): index = emojis.index(reaction.emoji) if current_team_player_select == 1: team1.append(players[index]) if current_team_player_select == 2: team2.append(players[index]) self.logger.debug( f'{current_captain} selected {players[index]}') emojis_selected.append(reaction.emoji) emoji_remove.append(reaction.emoji) del emojis[index] del players[index] selected_players += 1 seconds += 1 if seconds % 60 == 0: for _ in range(0, player_veto[player_veto_count]): index = randint(0, len(players) - 1) self.logger.debug( f'{current_captain} selected {players[index]}') if current_team_player_select == 1: team1.append(players[index]) if current_team_player_select == 2: team2.append(players[index]) emojis_selected.append(emojis[index]) del emojis[index] del players[index] selected_players += 1 if selected_players == player_veto[player_veto_count]: if current_team_player_select == 1: current_team_player_select = 2 elif current_team_player_select == 2: current_team_player_select = 1 break player_veto_count += 1 if map_arg is None: message_text = 'Map Veto Loading' else: message_text = f'Map is `{map_arg}`' players_text = 'None' embed = self.player_veto_embed(players_text=players_text, team1=team1, team1_captain=team1_captain, team2=team2, team2_captain=team2_captain) await message.edit(content=message_text, embed=embed) await message.clear_reactions() if map_arg is not None: chosen_map_embed = await self.get_chosen_map_embed(map_arg) await ctx.send(embed=chosen_map_embed) team1_steamIDs = {} team2_steamIDs = {} spectator_steamIDs = {} if ctx.author.voice.channel.category is None: team1_channel = await ctx.guild.create_voice_channel( name=f'team_{team1_captain.display_name}', user_limit=int(self.bot.match_size / 2) + 1) team2_channel = await ctx.guild.create_voice_channel( name=f'team_{team2_captain.display_name}', user_limit=int(self.bot.match_size / 2) + 1) else: team1_channel = await ctx.author.voice.channel.category.create_voice_channel( name=f'team_{team1_captain.display_name}', user_limit=int(self.bot.match_size / 2) + 1) team2_channel = await ctx.author.voice.channel.category.create_voice_channel( name=f'team_{team2_captain.display_name}', user_limit=int(self.bot.match_size / 2) + 1) for player in team1: await player.move_to(channel=team1_channel, reason=f'You are on {team1_captain}\'s Team') data = await db.fetch_one( 'SELECT steam_id FROM users WHERE discord_id = :player', {"player": str(player.id)}) team1_steamIDs[data[0]] = unidecode(player.display_name) self.logger.debug(f'Moved all team1 players to {team1_channel}') for player in team2: await player.move_to(channel=team2_channel, reason=f'You are on {team2_captain}\'s Team') data = await db.fetch_one( 'SELECT steam_id FROM users WHERE discord_id = :player', {"player": str(player.id)}) team2_steamIDs[data[0]] = unidecode(player.display_name) self.logger.debug(f'Moved all team2 players to {team2_channel}') if len(self.bot.spectators) > 0: for spec in self.bot.spectators: data = await db.fetch_one( 'SELECT steam_id FROM users WHERE discord_id = :spectator', {"spectator": str(spec.id)}) spectator_steamIDs[data[0]] = unidecode(spec.display_name) self.logger.info('Added Spectators') if map_arg is None: map_list = await self.map_veto(ctx, team1_captain, team2_captain) else: map_list = [map_arg] bot_ip = self.bot.web_server.IP if self.bot.bot_IP != '': bot_ip = self.bot.bot_IP team1_country = 'IE' team2_country = 'IE' team1_flags = [] team2_flags = [] team1_flag_request = '' team2_flag_request = '' for player in team1_steamIDs: team1_flag_request += SteamID(player).__str__() + ',' team1_flag_request = team1_flag_request[:-1] for player in team2_steamIDs: team2_flag_request += SteamID(player).__str__() + ',' team2_flag_request = team2_flag_request[:-1] self.logger.info('Making request to the Steam API to get player flags') session = aiohttp.ClientSession() async with session.get( f'https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/' f'?key={self.bot.steam_web_api_key}' f'&steamids={team1_flag_request}') as resp: player_info = await resp.json() for player in player_info['response']['players']: if 'loccountrycode' in player: team1_flags.append(player['loccountrycode']) await session.close() session = aiohttp.ClientSession() async with session.get( f'https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/' f'?key={self.bot.steam_web_api_key}' f'&steamids={team2_flag_request}') as resp: player_info = await resp.json() for player in player_info['response']['players']: if 'loccountrycode' in player: team2_flags.append(player['loccountrycode']) await session.close() # TODO: Add check for EU/CIS flag if len(team1_flags) > 0: team1_country = Counter(team1_flags).most_common(1)[0][0] if len(team2_flags) > 0: team2_country = Counter(team2_flags).most_common(1)[0][0] team1_name = f'team_{unidecode(team1_captain.display_name)}' team2_name = f'team_{unidecode(team2_captain.display_name)}' match_id = f'PUG_{datetime.now().strftime("%Y-%m-%d-%H-%M-%S")}' match_config = { 'matchid': match_id, 'num_maps': 1, 'maplist': map_list, 'skip_veto': True, 'veto_first': 'team1', 'side_type': 'always_knife', 'players_per_team': int(self.bot.match_size / 2), 'min_players_to_ready': 1, 'spectators': { 'players': spectator_steamIDs, }, 'team1': { 'name': team1_name, 'tag': 'team1', 'flag': team1_country, 'players': team1_steamIDs }, 'team2': { 'name': team2_name, 'tag': 'team2', 'flag': team2_country, 'players': team2_steamIDs }, 'cvars': { 'get5_event_api_url': f'http://{bot_ip}:{self.bot.web_server.port}/', 'get5_print_damage': 1, } } self.logger.debug(f'Match Config =\n {pprint.pformat(match_config)}') with open(f'./matches/{match_id}.json', 'w') as outfile: json.dump(match_config, outfile, ensure_ascii=False, indent=4) await ctx.send( 'If you are coaching, once you join the server, type .coach') loading_map_message = await ctx.send('Server is being configured') await asyncio.sleep(0.5) get5_trigger = valve.rcon.execute( (csgo_server.server_address, csgo_server.server_port), csgo_server.RCON_password, 'exec triggers/get5') self.logger.debug( f'Executing get5_trigger (something for Yannicks Server) \n {get5_trigger}' ) await asyncio.sleep(10) await loading_map_message.delete() load_match = valve.rcon.execute( (csgo_server.server_address, csgo_server.server_port), csgo_server.RCON_password, f'get5_loadmatch_url "{bot_ip}:{self.bot.web_server.port}/{match_id}"' ) self.logger.debug(f'Load Match via URL\n {load_match}') await asyncio.sleep(5) connect_embed = await self.connect_embed(csgo_server) if self.bot.connect_dm: for player in team1 + team2 + self.bot.spectators: try: await player.send(embed=connect_embed) except (discord.HTTPException, discord.Forbidden): await ctx.send( f'Unable to PM <@{player.id}> the server details.') self.logger.warning(f'{player} was not sent the IP via DM') else: await ctx.send(embed=connect_embed) score_embed = discord.Embed() score_embed.add_field(name='0', value=team1_name, inline=True) score_embed.add_field(name='0', value=team2_name, inline=True) score_message = await ctx.send('Match in Progress', embed=score_embed) csgo_server.get_context( ctx=ctx, channels=[channel_original, team1_channel, team2_channel], players=team1 + team2, score_message=score_message) csgo_server.set_team_names([team1_name, team2_name]) self.bot.web_server.add_server(csgo_server) if not self.pug.enabled: self.queue_check.start() self.logger.info('Queue Starting Back')
async def create(self, ctx, backup_id=None, name=None, *, description=None): """ Turn a private backup into a PUBLIC template. __Arguments__ **backup_id**: The id of the backup that you want to turn into a template **name**: A name for the template **description**: A description for the template __Examples__ ```{c.prefix}template create oj1xky11871fzrbu start-template A good start for new servers``` """ backup_id = backup_id or await helpers.ask_question( ctx, "Please respond with the **id of the backup** that you want to turn into a template." ) name = name or await helpers.ask_question(ctx, "Please respond with your desired **name for template**.") description = description or await helpers.ask_question( ctx, "Please respond with a **meaningful description** for the template." ) name = name.lower().replace(" ", "_") backup = await ctx.db.backups.find_one(backup_id) if backup is None or backup.get("creator") != ctx.author.id: raise cmd.CommandError(f"You have **no backup** with the id `{backup_id}`.") already_exists = (await ctx.db.templates.find_one(name)) is not None if already_exists: raise cmd.CommandError( f"There is **already a template with that name**, please choose another one." ) if len(description) < 30: raise cmd.CommandError("The template description must be **at least 30 characters** long.") backup["backup"]["members"] = [] warning = await ctx.send(**ctx.em( "Are you sure you want to turn this backup into a template?\n\n" "Templates must not be a copy of your server, they are for **public use** and must be generic. " "Use `x!backup load` if you just want to load or clone your server.", type="warning" )) await warning.add_reaction("✅") await warning.add_reaction("❌") try: reaction, user = await self.bot.wait_for( "reaction_add", check=lambda r, u: r.message.id == warning.id and u.id == ctx.author.id, timeout=60 ) except TimeoutError: await warning.delete() raise cmd.CommandError( "Please make sure to **click the ✅ reaction** in order to create a template." ) if str(reaction.emoji) != "✅": await warning.delete() return template = { "_id": name, "creator": backup["creator"], "used": 0, "featured": False, "approved": False, "original": backup_id, "description": description, "template": backup["backup"] } await ctx.db.templates.insert_one(template) await ctx.send(**ctx.em("Successfully **created template**.\n" f"The template **will not be available** until a moderator approves it.\n" f"Please join the [support server](https://discord.club/discord) and enable direct " f"messages to get updates about your template.", type="success")) await self.approval_webhook.send(embed=self._template_info(template))
if empire is None: raise commands.CommandError(f"Target does not have an empire.") if (last_battle := empire.get("last_battle")) is None: time_since_attack = BattleValues.COOLDOWN else: time_since_attack = (dt.datetime.utcnow() - last_battle).total_seconds() if time_since_attack < BattleValues.COOLDOWN: delta = dt.timedelta(seconds=int(BattleValues.COOLDOWN - time_since_attack)) raise commands.CommandError( f"Target is recovering from a previous battle. Try again in `{delta}`" ) return user class Range(commands.Converter): def __init__(self, min_: int, max_: int = None): self.min_ = min_ self.max_ = max_ async def convert(self, ctx: commands.Context, argument: str) -> int: try: val = int(argument) if (self.max_ is not None and val > self.max_) or val < self.min_:
async def cloudahk_call(self, ctx, code, lang='ahk'): '''Call to CloudAHK to run "code" written in "lang". Replies to invoking user with stdout/runtime of code. ''' token = '{0}:{1}'.format(CLOUDAHK_USER, CLOUDAHK_PASS) encoded = b64encode(bytes(token, 'utf-8')).decode('utf-8') headers = {'Authorization': 'Basic ' + encoded} # remove first line with backticks and highlighting lang if re.match('^```.*\n', code): code = code[code.find('\n') + 1:] # strip backticks on both sides code = code.strip('`').strip() url = f'{CLOUDAHK_URL}/{lang}/run' # call cloudahk with 20 in timeout async with self.bot.aiohttp.post(url, data=code, headers=headers, timeout=20) as resp: if resp.status == 200: result = await resp.json() else: raise commands.CommandError('Something went wrong.') stdout, time = result['stdout'].strip(), result['time'] file = None stdout = stdout.replace('\r', '') if time is None: resp = 'Program ran for too long and was aborted.' else: stdout_len = len(stdout) display_time = f'Runtime: `{time:.2f}` seconds' if stdout_len < 1800 and stdout.count('\n') < 20: # upload as plaintext stdout = stdout.replace('``', '`\u200b`') resp = '```autoit\n{0}\n```{1}'.format( stdout if stdout else 'No output.', display_time) elif stdout_len < DISCORD_UPLOAD_LIMIT: fp = io.BytesIO(bytes(stdout.encode('utf-8'))) file = discord.File(fp, 'output.txt') resp = f'Output dumped to file.\n{display_time}' else: raise commands.CommandError('Output greater than 8 MB.') # logging for security purposes and checking for abuse filename = 'ahk_eval/{0}_{1}_{2}_{3}'.format(ctx.guild.id, ctx.author.id, ctx.message.id, lang) with open(filename, 'w', encoding='utf-8-sig') as f: f.write( '{0}\n\nLANG: {1}\n\nCODE:\n{2}\n\nPROCESSING TIME: {3}\n\nSTDOUT:\n{4}\n' .format(ctx.stamp, lang, code, time, stdout)) # because of how the api works, if the person has deleted their message then # the api won't take the request and returns an error, so we catch it and don't # reply. This ensures the output gets sent even if they delete their message. try: await ctx.reply(content=resp, file=file) except discord.HTTPException: await ctx.send(content=resp, file=file)
from cogs.ahk.ids import * from cogs.mixins import AceMixin from config import CLOUDAHK_PASS, CLOUDAHK_URL, CLOUDAHK_USER from utils.docs_parser import parse_docs from utils.html2markdown import HTML2Markdown from utils.pager import Pager from utils.string import po from utils.time import pretty_timedelta log = logging.getLogger(__name__) AHK_COLOR = 0x95CD95 RSS_URL = 'https://www.autohotkey.com/boards/feed' DOCS_FORMAT = 'https://autohotkey.com/docs/{}' DOCS_NO_MATCH = commands.CommandError( 'Sorry, couldn\'t find an entry similar to that.') SUGGESTION_PREFIX = 'suggestion:' UPVOTE_EMOJI = '\N{Thumbs Up Sign}' DOWNVOTE_EMOJI = '\N{Thumbs Down Sign}' INACTIVITY_LIMIT = timedelta(weeks=4) class DocsPagePager(Pager): async def craft_page(self, e, page, entries): e.title = self.header.get('page') e.url = DOCS_FORMAT.format(self.header.get('link')) e.color = AHK_COLOR e.description = '\n'.join('[`{}`]({})'.format(
async def queue_running(ctx: commands.Context): if not ctx.bot.cogs['CSGO'].queue_check.is_running(): raise commands.CommandError(message='Queue not running.') return True
async def convert(self, ctx, argument): with contextlib.suppress(commands.MessageNotFound): message = await commands.MessageConverter().convert(ctx, argument) return message.jump_url raise commands.CommandError( f"I can't find {argument}. Is this even a real message?")
async def promote(self, ctx, *memberNames): ''' Promotes the given user(s) (Admin+) (Portables only). Arguments: member(s) Members can be either one name or one or more mentions. ''' addCommand() await ctx.channel.trigger_typing() msg = ctx.message user = ctx.author roles = user.roles guild = ctx.guild if len(msg.mentions) < 1 and not memberNames: raise commands.CommandError( message=f'Required argument missing: `user`.') members = msg.mentions if not members: if memberNames: memberName = '' for part in memberNames: memberName += part + ' ' memberName = memberName.strip() memberName = pattern.sub('', memberName).upper() member = discord.utils.find(lambda m: isName(memberName, m), guild.members) if not member: raise commands.CommandError( message=f'Could not find user: `{memberName}`.') members.append(member) isLeader = False for r in roles: if r.id == config['leaderRole'] or user.id == config['owner']: isLeader = True break txt = "" for r in guild.roles: if r.id == config['smileyRole']: smileyRole = r if r.id == config['rankRole']: rankRole = r if r.id == config['editorRole']: editorRole = r if r.id == config['modRole']: modRole = r if r.id == config['adminRole']: adminRole = r for m in members: name = m.display_name if m.top_role < smileyRole: await m.add_roles(smileyRole) txt += f'**{name}** has been promoted to **Smiley**.\n' elif m.top_role < rankRole: await m.add_roles(rankRole, editorRole) txt += f'**{name}** has been promoted to **Editor**.\n' elif m.top_role < modRole: await m.add_roles(modRole) txt += f'**{name}** has been promoted to **Moderator**.\n' elif m.top_role < adminRole: if isLeader: await m.add_roles(adminRole) txt += f'**{name}** has been promoted to **Admin**.\n' else: raise commands.CommandError( message=f'Missing permissions: `Admin`.') else: raise commands.CommandError( message=f'Missing permissions: `Admin`.') if txt: await ctx.send(txt)
async def derank(self, ctx, *memberNames): ''' Deranks the given user(s) (Admin+) (Portables only). Arguments: members Members can be either one name or one or more mentions. ''' addCommand() await ctx.channel.trigger_typing() msg = ctx.message user = ctx.author roles = user.roles guild = ctx.guild if len(msg.mentions) < 1 and not memberNames: raise commands.CommandError( message=f'Required argument missing: `user`.') members = msg.mentions if not members: if memberNames: memberName = '' for part in memberNames: memberName += part + ' ' memberName = memberName.strip() memberName = pattern.sub('', memberName).upper() member = discord.utils.find(lambda m: isName(memberName, m), guild.members) if not member: raise commands.CommandError( message=f'Could not find user: `{memberName}`.') members.append(member) isLeader = False for r in roles: if r.id == config['leaderRole'] or user.id == config['owner']: isLeader = True break txt = "" for r in guild.roles: if r.id == config['smileyRole']: smileyRole = r if r.id == config['vetRole']: vetRole = r if r.id == config['rankRole']: rankRole = r if r.id == config['editorRole']: editorRole = r if r.id == config['modRole']: modRole = r if r.id == config['adminRole']: adminRole = r if r.id == config['leaderRole']: leaderRole = r for m in members: roles = m.roles[ 1:] #get all except first role, because first role is always @everyone name = m.display_name if m.top_role >= leaderRole: await ctx.send( f'Sorry, I do not have sufficient permissions to derank **{name}**.' ) raise commands.CommandError( message=f'Insufficient permissions.') elif m.top_role >= adminRole: if isLeader: try: await m.remove_roles(adminRole, modRole, editorRole, rankRole) txt += f'{name} has been deranked.\n' except discord.Forbidden: raise commands.CommandError( message=f'Missing permissions: `manage_roles`.') else: raise commands.CommandError( message=f'Missing permissions: `Admin`.') elif m.top_role >= modRole: try: await m.remove_roles(modRole, editorRole, rankRole) txt += f'**{name}** has been deranked.\n' except discord.Forbidden: raise commands.CommandError( message=f'Missing permissions: `manage_roles`.') elif m.top_role >= rankRole: try: await m.remove_roles(editorRole, rankRole) txt += f'**{name}** has been deranked.\n' except discord.Forbidden: raise commands.CommandError( message=f'Missing permissions: `manage_roles`.') elif m.top_role >= smileyRole: try: if vetRole in roles: await m.remove_roles(smileyRole, vetRole) else: await m.remove_roles(smileyRole) txt += f'**{name}** has been deranked.\n' except discord.Forbidden: raise commands.CommandError( message=f'Missing permissions: `manage_roles`.') else: raise commands.CommandError( message=f'Error: `{name}` cannot be deranked any further.') if txt: await ctx.send(txt)
async def mute(self, ctx, member='', duration='', reason='N/A'): ''' Assigns a role 'Muted' to given member. (Admin+) Arguments: member: name, nickname, id, mention duration: [number][unit], where unit in {d, h, m} (optional) reason: string (optional) ''' addCommand() await ctx.channel.trigger_typing() msg = ctx.message guild = ctx.guild if not member: raise commands.CommandError( message=f'Required argument missing: `member`.') temp = None if msg.mentions: temp = msg.mentions[0] if not temp: if is_int(member): temp = await guild.fetch_member(int(member)) if not temp: temp = discord.utils.get(guild.members, display_name=member) if not temp: temp = discord.utils.get(guild.members, name=member) if not temp: raise commands.CommandError( message=f'Could not find member: `{member}`.') member = temp mute_role = discord.utils.find(lambda r: 'MUTE' in r.name.upper(), guild.roles) if not mute_role: try: mute_role = await guild.create_role( name='Muted', permissions=discord.Permissions.none()) except discord.Forbidden: raise commands.CommandError( message=f'Missing permissions: `create_role`.') if mute_role in member.roles: raise commands.CommandError( message=f'Error: {member.mention} is already muted.') try: if not reason == 'N/A': await member.add_roles(mute_role, reason=reason) else: await member.add_roles(mute_role) except discord.Forbidden: raise commands.CommandError( message=f'Missing permissions: `role_management`.') await ctx.send(f'{member.mention} has been **muted**.') if duration: # format: [num][unit] where unit in {d, h, m} temp = duration.replace(' ', '') units = ['d', 'h', 'm'] input = [] num = '' for char in temp: if not is_int(char) and not char.lower() in units: raise commands.CommandError( message=f'Invalid argument: `duration`.') elif is_int(char): num += char elif char.lower() in units: if not num: raise commands.CommandError( message=f'Invalid argument: `duration`.') input.append((int(num), char.lower())) num = '' days = 0 hours = 0 minutes = 0 for i in input: num = i[0] unit = i[1] if unit == 'd': days += num elif unit == 'h': hours += num elif unit == 'm': minutes += num if days * 24 * 60 + hours * 60 + minutes <= 0: raise commands.CommandError( message=f'Invalid argument: `duration`.') elif days * 24 * 60 + hours * 60 + minutes > 60 * 24 * 366: raise commands.CommandError( message=f'Invalid argument: `duration`.') duration = timedelta(days=days, hours=hours, minutes=minutes) end = str(datetime.utcnow().replace(second=0, microsecond=0) + duration) await Mute.create(guild_id=ctx.guild.id, user_id=member.id, expiration=end, reason=reason) await ctx.send(f'Mute expiration date set to: `{end}`.') for c in guild.text_channels: send = c.permissions_for(member).send_messages if not send: continue overwritten = False for o in c.overwrites: try: obj = o[0] except: obj = o if obj == mute_role: overwritten = True break if overwritten: o = c.overwrites_for(mute_role) if o.send_messages: try: await c.set_permissions(mute_role, send_messages=False) except discord.Forbidden: raise commands.CommandError( message= f'Missing permissions: `channel_permission_overwrites`.' ) else: try: await c.set_permissions(mute_role, send_messages=False) except discord.Forbidden: raise commands.CommandError( message= f'Missing permissions: `channel_permission_overwrites`.' ) send = c.permissions_for(member).send_messages if send: try: await c.set_permissions(member, send_messages=False) except discord.Forbidden: raise commands.CommandError( message= f'Missing permissions: `channel_permission_overwrites`.' )
async def unmute(self, ctx, member): ''' Unmute a member. (Admin+) member: name, nickname, id, mention ''' addCommand() await ctx.channel.trigger_typing() msg = ctx.message guild = ctx.guild temp = None if msg.mentions: temp = msg.mentions[0] if not temp: if is_int(member): temp = await guild.fetch_member(int(member)) if not temp: temp = discord.utils.get(guild.members, display_name=member) if not temp: temp = discord.utils.get(guild.members, name=member) if not temp: raise commands.CommandError( message=f'Could not find member: `{member}`.') member = temp mute_role = discord.utils.find(lambda r: 'MUTE' in r.name.upper(), guild.roles) if not mute_role: raise commands.CommandError(message=f'Missing role: `muted`.') if not mute_role in member.roles: raise commands.CommandError( message=f'Error: `{member.display_name}` is not muted.') else: try: await member.remove_roles(mute_role) except discord.Forbidden: raise commands.CommandError( message=f'Missing permissions: `role_management`.') mute = await Mute.query.where(Mute.guild_id == ctx.guild.id ).where(Mute.user_id == member.id ).gino.first() if mute: await mute.delete() await ctx.send(f'{member.mention} has been **unmuted**.') for c in guild.text_channels: send = c.permissions_for(member).send_messages if send: continue if member in c.overwrites: overwrite = c.overwrites[member] if not overwrite.pair()[1].send_messages: try: await c.set_permissions(member, send_messages=None) c = guild.get_channel(c.id) if member in c.overwrites: overwrite = c.overwrites[member] if overwrite[1].is_empty(): await c.set_permissions(member, overwrite=None) except discord.Forbidden: raise commands.CommandError( message= f'Missing permissions: `channel_permission_overwrites`.' )
import discord import logging import asyncio from discord.ext import commands from cogs.mixins import AceMixin from utils.string import po from utils.configtable import ConfigTable, ConfigTableRecord log = logging.getLogger(__name__) WELCOME_NOT_SET_UP_ERROR = commands.CommandError( 'You don\'t seem to have set up a welcome message yet, do `welcome` to see available commands.' ) class WelcomeRecord(ConfigTableRecord): @property def channel(self): if self.channel_id is None: return None guild = self._config.bot.get_guild(self.guild_id) if guild is None: return None return guild.get_channel(self.channel_id) class Welcome(AceMixin, commands.Cog):
async def lyrics(self, ctx, *query): """Get song lyrics""" raw = requests.get( "https://api.genius.com/search?q={}&access_token={}".format( query, primebot.conf['genius_api_key'])) raw = raw.json() titles = [] i = 0 j = 1 s = "" reactions = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣'] if not raw['response']['hits']: raise commands.CommandError("Song not Found") try: if raw['error'] == "invalid_token": raise commands.CommandError("Invalid Genius API Key") except KeyError: pass while i < 9: titles.append(raw['response']['hits'][i]['result']['full_title']) i += 1 for title in titles: s += str(j) + " " + title + "\n" j += 1 react_message = await ctx.send(s) for reaction in reactions[:len(titles)]: await react_message.add_reaction(reaction) # iterate over reactions try: def check(rctn, user): return user.id == ctx.author.id and str(rctn) in reactions rctn, user = await self.bot.wait_for("reaction_add", check=check, timeout=30) cache_msg = discord.utils.get(self.bot.cached_messages, id=react_message.id) for reaction in cache_msg.reactions: users = await reaction.users().flatten() for user in users: if user == ctx.message.author: selected_music = str(reaction) except asyncio.TimeoutError: pass # replace emoji with int reaction = str(selected_music) reaction = reaction.replace('1⃣', '1') reaction = reaction.replace('2⃣', '2') reaction = reaction.replace('3⃣', '3') reaction = reaction.replace('4⃣', '4') reaction = reaction.replace('5⃣', '5') reaction = reaction.replace('6⃣', '6') reaction = reaction.replace('7⃣', '7') reaction = reaction.replace('8⃣', '8') reaction = reaction.replace('9⃣', '9') lyric_url = raw['response']['hits'][int( str(reaction))]['result']['url'] song_title = raw['response']['hits'][int( str(reaction))]['result']['full_title'] lyrics = scrape_song_lyrics(lyric_url) emb = discord.Embed(title=f"{song_title}", description=f"{lyrics}", color=0xa3a3ff, url=lyric_url) await ctx.send(embed=emb)
async def pug(self, ctx: commands.Context, *args): random_teams: bool = False map_arg: str = None team1_captain_arg: discord.Member = None team2_captain_arg: discord.Member = None for arg in args: if arg == 'random': random_teams = True elif arg.startswith('de_'): map_arg = arg if map_arg not in current_map_pool: raise commands.CommandError(message=f'`{map_arg}` is not in Map Pool') else: member: discord.Member = await commands.MemberConverter().convert(ctx, arg) if member in ctx.author.voice.channel.members: if team1_captain_arg is None: team1_captain_arg = member elif team2_captain_arg is None and member is not team1_captain_arg: team2_captain_arg = member else: if member is team1_captain_arg: raise commands.CommandError(message=f'One user cannot be captain of 2 teams.') else: raise commands.CommandError(message=f'You can only set 2 captains.') else: raise commands.CommandError(message=f'Invalid Argument: `{arg}`') # TODO: Refactor this mess db = Database('sqlite:///main.sqlite') await db.connect() csgo_server = self.bot.servers[0] for server in self.bot.servers: if server.available: server.available = False csgo_server = server break channel_original = ctx.author.voice.channel players: List[discord.Member] = ctx.author.voice.channel.members.copy() players = players[0: 13] if self.bot.dev: players = [ctx.author] * 10 if random_teams: shuffle(players) team1 = players[:len(players) // 2] team2 = players[len(players) // 2:] team1_captain = team1[0] team2_captain = team2[0] message_text = 'Random Teams' message = await ctx.send(message_text) embed = self.player_veto_embed(message_text=message_text, players_text='Random Teams', team1=team1, team1_captain=team1_captain, team2=team2, team2_captain=team2_captain) await message.edit(content=message_text, embed=embed) else: emojis = emoji_bank.copy() del emojis[len(players) - 2:len(emojis)] emojis_selected = [] team1 = [] team2 = [] if team1_captain_arg is not None: team1_captain = team1_captain_arg else: team1_captain = players[randint(0, len(players) - 1)] team1.append(team1_captain) players.remove(team1_captain) if team2_captain_arg is not None: team2_captain = team2_captain_arg else: team2_captain = players[randint(0, len(players) - 1)] team2.append(team2_captain) players.remove(team2_captain) current_team_player_select = 1 current_captain = team1_captain player_veto_count = 0 message = await ctx.send('10 man time\nLoading player selection...') for emoji in emojis: await message.add_reaction(emoji) emoji_remove = [] while len(players) > 0: message_text = '' players_text = '' if current_team_player_select == 1: message_text += f'<@{team1_captain.id}>' current_captain = team1_captain elif current_team_player_select == 2: message_text += f'<@{team2_captain.id}>' current_captain = team2_captain message_text += f' select {player_veto[player_veto_count]}\n' message_text += 'You have 60 seconds to choose your player(s)\n' i = 0 for player in players: players_text += f'{emojis[i]} - <@{player.id}>\n' i += 1 embed = self.player_veto_embed(message_text=message_text, players_text=players_text, team1=team1, team1_captain=team1_captain, team2=team2, team2_captain=team2_captain) await message.edit(content=message_text, embed=embed) if len(emoji_remove) > 0: for emoji in emoji_remove: await message.clear_reaction(emoji) emoji_remove = [] selected_players = 0 seconds = 0 while True: await asyncio.sleep(1) message = await ctx.fetch_message(message.id) for reaction in message.reactions: users = await reaction.users().flatten() if current_captain in users and selected_players < player_veto[player_veto_count] and not ( reaction.emoji in emojis_selected): index = emojis.index(reaction.emoji) if current_team_player_select == 1: team1.append(players[index]) if current_team_player_select == 2: team2.append(players[index]) emojis_selected.append(reaction.emoji) emoji_remove.append(reaction.emoji) del emojis[index] del players[index] selected_players += 1 seconds += 1 if seconds % 60 == 0: for _ in range(0, player_veto[player_veto_count]): index = randint(0, len(players) - 1) if current_team_player_select == 1: team1.append(players[index]) if current_team_player_select == 2: team2.append(players[index]) emojis_selected.append(emojis[index]) del emojis[index] del players[index] selected_players += 1 if selected_players == player_veto[player_veto_count]: if current_team_player_select == 1: current_team_player_select = 2 elif current_team_player_select == 2: current_team_player_select = 1 break player_veto_count += 1 if map_arg is None: message_text = 'Map Veto Loading' else: message_text = f'Map is `{map_arg}`' players_text = 'None' embed = self.player_veto_embed(message_text=message_text, players_text=players_text, team1=team1, team1_captain=team1_captain, team2=team2, team2_captain=team2_captain) await message.edit(content=message_text, embed=embed) await message.clear_reactions() if map_arg is not None: chosen_map_embed = await self.get_chosen_map_embed(map_arg) await ctx.send(embed=chosen_map_embed) team1_steamIDs = [] team2_steamIDs = [] if ctx.author.voice.channel.category is None: team1_channel = await ctx.guild.create_voice_channel(name=f'team_{team1_captain.display_name}', user_limit=7) team2_channel = await ctx.guild.create_voice_channel(name=f'team_{team2_captain.display_name}', user_limit=7) else: team1_channel = await ctx.author.voice.channel.category.create_voice_channel( name=f'team_{team1_captain.display_name}', user_limit=7) team2_channel = await ctx.author.voice.channel.category.create_voice_channel( name=f'team_{team2_captain.display_name}', user_limit=7) for player in team1: await player.move_to(channel=team1_channel, reason=f'You are on {team1_captain}\'s Team') data = await db.fetch_one('SELECT steam_id FROM users WHERE discord_id = :player', {"player": str(player.id)}) team1_steamIDs.append(data[0]) for player in team2: await player.move_to(channel=team2_channel, reason=f'You are on {team2_captain}\'s Team') data = await db.fetch_one('SELECT steam_id FROM users WHERE discord_id = :player', {"player": str(player.id)}) team2_steamIDs.append(data[0]) if map_arg is None: map_list = await self.map_veto(ctx, team1_captain, team2_captain) else: map_list = map_arg bot_ip = self.bot.web_server.IP if self.bot.bot_IP != "": bot_ip = self.bot.bot_IP team1_country = 'IE' team2_country = 'IE' session = aiohttp.ClientSession() async with session.get(f'http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/' f'?key={self.bot.steam_web_api_key}' f'&steamids={SteamID(team1_steamIDs[0]).as_64},{SteamID(team2_steamIDs[0]).as_64}') as resp: captain_info = await resp.json() if 'loccountrycode' in captain_info['response']['players'][0]: team1_country = captain_info['response']['players'][0]['loccountrycode'] if 'loccountrycode' in captain_info['response']['players'][1]: team2_country = captain_info['response']['players'][1]['loccountrycode'] await session.close() match_config = { 'matchid': f'PUG-{date.today().strftime("%d-%B-%Y")}', 'num_maps': 1, 'maplist': map_list, 'skip_veto': True, 'veto_first': 'team1', 'side_type': 'always_knife', 'players_per_team': len(team2), 'min_players_to_ready': 1, 'team1': { 'name': f'team_{team1_captain.display_name}', 'tag': 'team1', 'flag': team1_country, 'players': team1_steamIDs }, 'team2': { 'name': f'team_{team2_captain.display_name}', 'tag': 'team2', 'flag': team2_country, 'players': team2_steamIDs }, 'cvars': { 'get5_event_api_url': f'http://{bot_ip}:{self.bot.web_server.port}/', 'get5_print_damage': 1 } } with open('./match_config.json', 'w') as outfile: json.dump(match_config, outfile, ensure_ascii=False, indent=4) await ctx.send('If you are coaching, once you join the server, type .coach') loading_map_message = await ctx.send('Server is being configured') await asyncio.sleep(0.3) valve.rcon.execute((csgo_server.server_address, csgo_server.server_port), csgo_server.RCON_password, 'exec triggers/get5') await asyncio.sleep(10) await loading_map_message.delete() valve.rcon.execute((csgo_server.server_address, csgo_server.server_port), csgo_server.RCON_password, f'get5_loadmatch_url "{bot_ip}:{self.bot.web_server.port}/match"') await asyncio.sleep(5) connect_embed = await self.connect_embed(csgo_server) await ctx.send(embed=connect_embed) score_embed = discord.Embed() score_embed.add_field(name='0', value=f'team_{team1_captain.display_name}', inline=True) score_embed.add_field(name='0', value=f'team_{team2_captain.display_name}', inline=True) score_message = await ctx.send('Match in Progress', embed=score_embed) csgo_server.get_context(ctx=ctx, channels=[channel_original, team1_channel, team2_channel], players=team1 + team2, score_message=score_message) csgo_server.set_team_names(['team1', 'team2']) self.bot.web_server.add_server(csgo_server) if not self.pug.enabled: self.queue_check.start()
async def spawn(self, ctx): '''Spawn role selectors.''' await ctx.message.delete() conf = await self.config.get_entry(ctx.guild.id) selectors = await self.db.fetch( '''SELECT rs.* FROM role_selector as rs JOIN unnest($1::INTEGER[]) WITH ORDINALITY t(id, ord) USING (id) WHERE id=ANY($1::INTEGER[]) ORDER BY t.ord ''', conf.selectors) if not selectors: raise commands.CommandError( 'No selectors configured. Do `roles editor` to set one up.') if any(not selector.get('roles') for selector in selectors): raise commands.CommandError( 'You have empty selectors. Delete these or add roles to them before spawning.' ) if conf.message_ids: channel = ctx.guild.get_channel(conf.channel_id) if channel: for message_id in conf.message_ids: try: msg = await channel.fetch_message(message_id) if msg: await msg.delete() except discord.HTTPException: pass msgs = list() async def delete_all(): for m in msgs: try: await m.delete() except discord.HTTPException: pass for selector in selectors: # https://stackoverflow.com/questions/866465/order-by-the-in-value-list roles = await self.db.fetch( ''' SELECT re.* FROM role_entry as re JOIN unnest($1::INTEGER[]) WITH ORDINALITY t(id, ord) USING (id) WHERE id=ANY($1::INTEGER[]) ORDER BY t.ord ''', selector.get('roles')) if not roles: continue e = discord.Embed( description=selector.get('description') or 'Click the reactions to give or remove roles.') icon = selector.get('icon') e.set_author(name=selector.get('title') or 'Role Selector', icon_url=icon if icon else ctx.guild.icon_url) for role in roles: e.add_field(name='{} {}'.format(role.get('emoji'), role.get('name')), value=role.get('description'), inline=selector.get('inline')) msg = await ctx.send(embed=e) msgs.append(msg) try: for role in roles: emoj = role.get('emoji') await msg.add_reaction(emoj) except discord.HTTPException: await delete_all() raise commands.CommandError( 'Failed adding the emoji {}.\nIf the emoji has been deleted, change it in the editor.' .format(emoj)) await conf.update(channel_id=ctx.channel.id, message_ids=list(msg.id for msg in msgs))
class Money(commands.Cog, command_attrs=(dict(cooldown_after_parsing=True))): def __init__(self, bot): self.bot = bot @commands.cooldown(1, 60 * 60 * 24, commands.BucketType.user) @commands.command(name="daily") async def daily(self, ctx): """ Get some free coins! """ daily_money = random.randint(250, 2_500) await self.bot.pool.execute(BankSQL.ADD_MONEY, ctx.author.id, daily_money) await ctx.send(f"You gained **${daily_money}**!") @commands.command(name="balance", aliases=["bal", "money"]) async def balance(self, ctx, target: DiscordUser(author_ok=True) = None): """ Show the bank balances of the user, or supply an optional target user. """ target = target if target is not None else ctx.author bal = await utils.bank.get_user_bal(ctx.bot.pool, target) await ctx.send( f":moneybag: **{target.display_name}** has **${bal['money']:,}**.") @commands.cooldown(1, 60 * 60, commands.BucketType.user) @commands.command(name="steal") async def steal_coins(self, ctx, target: DiscordUser()): """ Attempt to steal from another user. """ if random.randint(0, 2) != 0: return await ctx.send( f"You stole nothing from **{target.display_name}**") bals = await utils.bank.get_users_bals(ctx.bot.pool, author=ctx.author, target=target) author_bal = bals["author"] target_bal = bals["target"] max_amount = random.randint( 1, int(min(author_bal["money"], target_bal["money"]) * 0.05)) stolen_amount = min(10_000, max_amount) await self.bot.pool.execute(BankSQL.ADD_MONEY, ctx.author.id, stolen_amount) await self.bot.pool.execute(BankSQL.SUB_MONEY, target.id, stolen_amount) await ctx.send( f"You stole **${stolen_amount:,}** from **{target.display_name}**") @commands.cooldown(1, 30, commands.BucketType.user) @commands.command(name="gift", aliases=["give"]) async def gift(self, ctx, target: DiscordUser(), amount: Range(1, 1_000_000)): """ Gift some money to another user. """ bal = await utils.bank.get_author_bal(ctx) if bal["money"] < amount: raise commands.CommandError("You do not have enough money.") await self.bot.pool.execute(BankSQL.SUB_MONEY, ctx.author.id, amount) await self.bot.pool.execute(BankSQL.ADD_MONEY, target.id, amount) await ctx.send(f"You gave **${amount:,}** to **{target.display_name}**" )
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 match_size_check(ctx: commands.Context): if ctx.author.voice is not None and (len(ctx.author.voice.channel.members) < ctx.bot.match_size and not ctx.bot.dev): raise commands.CommandError(message=f'There must be {ctx.bot.match_size} members connected to the voice channel') return True
async def voice_channel(ctx: commands.Context): if ctx.author.voice is None: raise commands.CommandError(message='You must be in a voice channel.') return True
async def ensure_call(self, ctx): if (not ctx.author.voice or ctx.author.voice.channel != ctx.voice_client.channel): await ctx.send("You are not in the same voice channel as the bot.") raise commands.CommandError( "Author not connected to the correct voice channel.")
async def think_command(self, ctx: disextc.Context, text_id: str): if self._synapse_0 is None or self._synapse_1 is None: raise disextc.CommandError(f"I have no mind.") results = await self.think(text_id=text_id) await ctx.send(f'think results: ```{results}```')
async def ensure_voice_state(self, ctx: commands.Context): if not ctx.author.voice or not ctx.author.voice.channel: raise commands.CommandError( 'You are not connected to any voice channel.')
async def get_url(self, ctx, thing): # sourcery no-metrics if ctx.message.reference: if ctx.message.reference.cached_message: if (ctx.message.reference.cached_message.embeds and ctx.message.reference.cached_message.embeds[0].type == "image"): url = ctx.message.reference.cached_message.embeds[ 0].thumbnail.proxy_url url = url.replace("cdn.discordapp.com", "media.discordapp.net") return url elif (ctx.message.reference.cached_message.embeds and ctx.message.reference.cached_message.embeds[0].type == "rich"): url = ctx.message.reference.cached_message.embeds[ 0].image.proxy_url url = url.replace("cdn.discordapp.com", "media.discordapp.net") return url elif (ctx.message.reference.cached_message.attachments and ctx.message.reference.cached_message.attachments[0].width and ctx.message.reference.cached_message.attachments[0]. height): url = ctx.message.reference.cached_message.attachments[ 0].proxy_url url = url.replace("cdn.discordapp.com", "media.discordapp.net") return url else: message = await self.bot.get_channel( ctx.message.reference.channel_id ).fetch_message(ctx.message.reference.message_id) if message.embeds and message.embeds[0].type == "image": url = message.embeds[0].thumbnail.proxy_url url = url.replace("cdn.discordapp.com", "media.discordapp.net") return url elif (message.attachments and message.attachments[0].width and message.attachments[0].height): url = message.attachments[0].proxy_url url = url.replace("cdn.discordapp.com", "media.discordapp.net") return url if (ctx.message.attachments and ctx.message.attachments[0].width and ctx.message.attachments[0].height): return ctx.message.attachments[0].proxy_url.replace( "cdn.discordapp.com", "media.discordapp.net") if thing is None: url = str(ctx.author.avatar_url_as(format="png")) elif isinstance(thing, (discord.PartialEmoji, discord.Emoji)): url = str(thing.url) elif isinstance(thing, (discord.Member, discord.User)): url = str(thing.avatar_url_as(format="png")) else: thing = str(thing).strip("<>") if (thing.startswith("http") or thing.startswith("https") or thing.startswith("www")): url = thing else: url = await emoji_to_url(thing) async with self.bot.session.get(url) as resp: if resp.status != 200: raise commands.CommandError("Invalid Picture") url = url.replace("cdn.discordapp.com", "media.discordapp.net") return url
async def create(self, ctx, backup_id, name, *, description): """ Turn a private backup into a PUBLIC template. backup_id :: The id of the backup that you want to turn into a template name :: A name for the template description :: A description for the template """ name = name.lower().replace(" ", "_") backup = await ctx.db.backups.find_one(backup_id) if backup is None or backup.get("creator") != ctx.author.id: raise cmd.CommandError( f"You have **no backup** with the id `{backup_id}`.") already_exists = (await ctx.db.templates.find_one(name)) is not None if already_exists: raise cmd.CommandError( f"There is **already a template with that name**, please choose another one." ) backup["backup"]["members"] = [] warning = await ctx.send(**ctx.em( "Are you sure you want to turn this backup into a template? **All templates are public!**", type="warning")) await warning.add_reaction("✅") await warning.add_reaction("❌") try: reaction, user = await self.bot.wait_for( "reaction_add", check=lambda r, u: r.message.id == warning.id and u.id == ctx. author.id, timeout=60) except TimeoutError: await warning.delete() raise cmd.CommandError( "Please make sure to **click the ✅ reaction** in order to create a template." ) if str(reaction.emoji) != "✅": await warning.delete() return template = { "_id": name, "creator": backup["creator"], "used": 0, "featured": False, "approved": False, "original": backup_id, "description": description, "template": backup["backup"] } await ctx.db.templates.insert_one(template) await ctx.send(**ctx.em( "Successfully **created template**.\n" f"The template **will not be available** until a moderator approves it.\n" f"Please join the [support server](https://discord.club/discord) and enable direct " f"messages to get updates about your template.", type="success")) await self.approval_webhook.send(embed=self._template_info(template))
async def play(self, ctx, *, url=None, showembed=True): if (url == None): url = "https://youtu.be/dQw4w9WgXcQ" embed = discord.Embed(color=0xc061cb) embed.add_field(name="p.play [ url / search / spotify url ] ", value="```-loop number``` to loop the song", inline=False) embed.add_field(name="p.play <> -loop number", value="loops the song in queue", inline=False) embed.add_field(name="p.queue", value="shows the current queue", inline=False) embed.add_field(name="p.move [ from ] [ to ]", value="moves song index : from → to", inline=False) embed.add_field(name="p.remove [ index ]", value="removes the last song by default ", inline=False) embed.add_field(name="p.nowplaying / p.np", value="shows now playing song", inline=False) embed.add_field(name="p.clearqueue / p.cq", value="clears the queue", inline=False) embed.add_field(name="p.lofi", value="plays lofigirl's lofi stream", inline=False) embed.add_field(name="p.pod", value="Show Podcast help section", inline=True) embed.set_footer(text="nub now get rollllled :D") await ctx.send(embed=embed) url_split = url.split("-loop") try: loopcount = int(url_split[1]) except: loopcount = 0 url = url_split[0] if (loopcount > 0): await ctx.send("> looping the song".format(loopcount)) progbar = await ctx.send("```[==========] 0%```") for i in range(int(loopcount)): prog = int((i / loopcount) * 100) await progbar.edit( content= f"```[{'#'*int(prog/10)} {'='*int(10-(prog/10))}] {prog}%```" ) await ctx.invoke(self.client.get_command('play'), url=url, showembed=False) await progbar.edit(content=f"```[{'#'*10}] Done```") return try: if ("/playlist?list=" in url): await ctx.send("> Parsing playlists", delete_after=5.0) ytplaylist = basicYTPlaylist(url) progbar = await ctx.send("```[==========] 0%```") i = 0 for song in ytplaylist: tot = len(ytplaylist) prog = int((i / tot) * 100) i += 1 await progbar.edit( content= f"```[{'#'*int(prog/10)} {'='*int(10-(prog/10))}] {prog}%```" ) await ctx.invoke(self.client.get_command('play'), url=song, showembed=False) await ctx.send("> Added {} songs to queue".format( len(ytplaylist))) await progbar.edit(content=f"``` [{'#'*10}] Done```") return except Exception as e: await ctx.send(f"> Something somewhere went wrong \n ||{e}||", delete_after=5.0) return try: timegx = r"[\?&]t=\d*" seekamt = int(re.findall(timegx, url)[0][1:].replace("t=", '')) await ctx.send( f"> Seeking based on url {seconds_to_hhmmss(seekamt)} hours") except IndexError: seekamt = 0 url, whrc = handle_spotify(url) if (whrc == "SP"): await ctx.send("> Fetching from spotify", delete_after=5.0) client = ctx.guild.voice_client state = self.get_state(ctx.guild) # get the guild's state if client and client.channel: try: video = Video(url, ctx.author, src=whrc) except youtube_dl.DownloadError as e: logging.warn(f"Error downloading video: {e}") await ctx.send("> Something went wrong in getting the video") return await ctx.message.add_reaction(Emotes.PACPLAY) state.playlist.append(video) if (showembed): message = await ctx.send("> Added to queue", embed=video.get_embed()) else: if ctx.author.voice is not None and ctx.author.voice.channel is not None: channel = ctx.author.voice.channel try: video = Video(url, ctx.author, whrc) except youtube_dl.DownloadError as e: await ctx.send("> Something went wrong!! \n```{e}```") return client = await channel.connect() self._play_song(client, state, video, seekamt) await ctx.message.add_reaction(Emotes.PACPLAY) message = await ctx.send("", embed=video.get_embed(), components=[ Button(style=ButtonStyle.URL, label="Youtube", url=video.video_url) ]) logging.info(f"Now playing '{video.title}'") else: await ctx.message.add_reaction(Emotes.PACNO) raise commands.CommandError( "> I might be your music bot but I can't get your lazy ass to join voice channel :/" )
async def editor(self, ctx): '''Editor for selectors and roles.''' # ignore command input from user while editor is open self.set_editing(ctx) conf = await self.config.get_entry(ctx.guild.id) slcs = await self.db.fetch( ''' SELECT rs.* FROM role_selector as rs JOIN unnest($1::INTEGER[]) WITH ORDINALITY t(id, ord) USING (id) WHERE id=ANY($1::INTEGER[]) ORDER BY t.ord ''', conf.selectors) selectors = list() for slc in slcs: roles = await self.db.fetch( ''' SELECT re.* FROM role_entry as re JOIN unnest($1::INTEGER[]) WITH ORDINALITY t(id, ord) USING (id) WHERE id=ANY($1::INTEGER[]) ORDER BY t.ord ''', slc.get('roles')) selector = Selector.from_record( slc, list(Role.from_record(role) for role in roles)) selectors.append(selector) head = RoleHead(conf, selectors) # so converters can access the head for data integrity tests... ctx.head = head msg = await ctx.send(embed=discord.Embed( description='Please wait while reactions are being added...')) self.messages[ctx.guild.id] = msg for emoji in EMBED_EMOJIS: await msg.add_reaction(emoji) def pred(reaction, user): return reaction.message.id == msg.id and user.id == ctx.author.id async def close(): self.unset_editing(ctx) try: await msg.delete() self.messages.pop(ctx.guild.id) except discord.HTTPException: pass while True: await msg.edit(embed=head.embed()) try: reaction, user = await self.bot.wait_for('reaction_add', check=pred, timeout=300.0) except asyncio.TimeoutError: await close() raise commands.CommandError( 'Role editor closed after 5 minutes of inactivity.') else: await msg.remove_reaction(reaction.emoji, user) reac = str(reaction) if reac == ADD_SEL_EMOJI: if len(head.selectors) > 7: await ctx.send(embed=discord.Embed( description='No more than 8 selectors, sorry!'), delete_after=6) continue selector_data = await self._multiprompt( ctx, msg, NEW_SEL_PREDS) if selector_data is None: continue selector = Selector(selector_data[0], None, list()) selector.set_dirty() new_pos = 0 if not head.selectors else head.selector_pos + 1 head.add_selector(new_pos, selector) head.selector_pos = new_pos head.role_pos = None if reac == ABORT_EMOJI: await close() raise commands.CommandError( 'Editing aborted, no changes saved.') if reac == SAVE_EMOJI: await head.store(ctx) await close() await ctx.send( 'New role selectors saved. Do `roles spawn` to see!') break # rest of the actions assume at least one item (selector) is present if not head.selectors: continue if reac == ADD_ROLE_EMOJI: if len(head.selector.roles) > 24: await ctx.send(embed=discord.Embed( description= 'No more than 25 roles in one selector, sorry!'), delete_after=6) continue role_data = await self._multiprompt( ctx, msg, NEW_ROLE_PREDS) if role_data is None: continue role = Role(*role_data) new_pos = 0 if head.role_pos is None else head.role_pos + 1 head.selector.add_role(new_pos, role) head.role_pos = new_pos if reac == DOWN_EMOJI: head.down() if reac == UP_EMOJI: head.up() if reac in (MOVEUP_EMOJI, MOVEDOWN_EMOJI): direction = -1 if reac == MOVEUP_EMOJI else 1 if head.role_pos is None: head.move_selector(direction) else: head.move_role(direction) if reac == DEL_EMOJI: if head.role_pos is None: if len(head.selector.roles): p = ctx.prompt( 'Delete selector?', 'The selector you\'re trying to delete has {} roles inside it.' .format(len(head.selector.roles))) if not await p: continue head.selectors.pop(head.selector_pos) if head.selector_pos > head.selector_max: head.selector_pos = head.selector_max head.role_pos = None else: head.selector.roles.pop(head.role_pos) if len(head.selector.roles) == 0: head.role_pos = None elif head.role_pos > head.role_max: head.role_pos = head.role_max if reac == EDIT_EMOJI: await self._edit_item( ctx, msg, head.selector if head.role_pos is None else head.selector.roles[head.role_pos])
async def audio_playing(ctx): client = ctx.guild.voice_client if client and client.channel and client.source: return True else: raise commands.CommandError("Not currently playing any audio.")
async def _edit_item(self, ctx, msg, item): if isinstance(item, Selector): questions = dict( title=selector_title_converter, description=selector_desc_converter, inline=SelectorInlineConverter(), ) elif isinstance(item, Role): questions = dict( name=role_title_converter, description=role_desc_converter, emoji=SelectorEmojiConverter(), ) else: raise TypeError('Unknown item type: ' + str(type(item))) opts = {emoji: q for emoji, q in zip(EMBED_EMOJIS, questions.keys())} opt_string = '\n'.join('{} {}'.format(key, value) for key, value in opts.items()) e = discord.Embed(description='What would you like to edit?\n\n' + opt_string) e.set_footer(text=ABORT_EMOJI + ' to abort.') await msg.edit(embed=e) def reac_pred(reaction, user): return reaction.message.id == msg.id and user.id == ctx.author.id while True: try: reaction, user = await self.bot.wait_for('reaction_add', check=reac_pred, timeout=300.0) except asyncio.TimeoutError: return else: await msg.remove_reaction(reaction.emoji, user) reac = str(reaction) if reac == ABORT_EMOJI: return elif reac in opts.keys(): attr = opts[reac] conv = questions[attr] break else: continue e.description = 'Please input a new value for \'{}\'.'.format(attr) e.set_footer(text='Send \'exit\' to abort.') await msg.edit(embed=e) def msg_pred(message): return message.channel.id == msg.channel.id and message.author.id == ctx.author.id while True: try: message = await self.bot.wait_for('message', check=msg_pred, timeout=60.0) except asyncio.TimeoutError: return await message.delete() if message.content.lower() == 'exit': return try: value = await conv.convert(ctx, message.content) except commands.CommandError as exc: if not msg.embeds: try: await msg.delete() except discord.HTTPException: pass raise commands.CommandError( 'Embed seems to have been removed, aborting.') e = msg.embeds[0] e.set_footer(text='NOTE: ' + str(exc) + ' ' + RETRY_MSG) await msg.edit(embed=e) continue setattr(item, attr, value) item.set_dirty() return
def author_is_server_owner(ctx): if ctx.author.id != ctx.guild.owner.id: raise commands.CommandError(f"You do not have access to this command.") return True
async def trivia(self, ctx, category: Optional[CategoryConverter] = None, *, difficulty: DifficultyConverter = None): '''Trivia time! Optionally specify a difficulty or category and difficulty as arguments. Valid difficulties are `easy`, `medium` and `hard`. Valid categories can be listed with `trivia categories`.''' raise commands.CommandError( 'The trivia API is down currently. Sorry about that!') diff = difficulty if diff is None: diff = choice(list(Difficulty)) params = dict( amount=1, encode='url3986', difficulty=diff.name.lower(), ) # if we have a category ID, insert it into the query params for the question request if category is not None: params['category'] = choice( category) if category is list else category try: async with ctx.http.get(API_URL, params=params) as resp: if resp.status != 200: self.trivia.reset_cooldown(ctx) raise REQUEST_FAILED res = await resp.json() except asyncio.TimeoutError: self.trivia.reset_cooldown(ctx) raise REQUEST_FAILED result = res['results'][0] question_type = result['type'] category = unquote(result['category']) correct_answer = unquote(result['correct_answer']) question = unquote(result['question']) question_hash = hash(question) if question_type == 'multiple': options = list( unquote(option) for option in result['incorrect_answers']) correct_pos = randrange(0, len(options) + 1) correct_emoji = MULTIPLE_MAP[correct_pos] options.insert(correct_pos, correct_answer) elif question_type == 'boolean': options = ('True', 'False') correct_emoji = BOOLEAN_MAP[int(correct_answer == 'False')] else: self.trivia.reset_cooldown(ctx) raise ValueError('Unknown question type: {}'.format(question_type)) option_emojis = BOOLEAN_MAP if question_type == 'boolean' else MULTIPLE_MAP question_string = '{}\n\n{}\n'.format( question, '\n'.join('{} {}'.format(emoji, option) for emoji, option in zip(option_emojis, options))) e = discord.Embed( title='Trivia time!', description='**Category**: {}\n**Difficulty**: {}'.format( category, diff.name.lower()), color=DIFFICULTY_COLORS[diff]) e.add_field(name='Question', value=question_string, inline=False) e.set_footer( text= 'Answer by pressing a reaction after all options have appeared.') msg = await ctx.send(embed=e) for emoji in option_emojis: await msg.add_reaction(emoji) now = datetime.utcnow() def check(reaction, user): return reaction.message.id == msg.id and user == ctx.author and str( reaction) in option_emojis try: reaction, user = await self.bot.wait_for('reaction_add', check=check, timeout=QUESTION_TIMEOUT) answered_at = datetime.utcnow() score = self._calculate_score(SCORE_POT[diff], answered_at - now) if str(reaction) == correct_emoji: # apply penalty if category was specified if 'category' in params: score = int(score / CATEGORY_PENALTY) current_score = await self._on_correct(ctx, answered_at, question_hash, score) e = discord.Embed( title='{} {}'.format(CORRECT_EMOJI, choice(CORRECT_MESSAGES)), description='You gained {} points.'.format(score), color=discord.Color.green()) e.set_footer(text=FOOTER_FORMAT.format(current_score)) await ctx.send(embed=e) else: score = int(score / PENALTY_DIV) current_score = await self._on_wrong(ctx, answered_at, question_hash, score) e = discord.Embed( title='{} {}'.format(WRONG_EMOJI, choice(WRONG_MESSAGES)), description='You lost {} points.'.format(score), color=discord.Color.red()) e.set_footer(text=FOOTER_FORMAT.format(current_score)) if question_type == 'multiple': e.description += '\nThe correct answer is ***`{}`***'.format( correct_answer) await ctx.send(embed=e) except asyncio.TimeoutError: score = int(SCORE_POT[diff] / 4) answered_at = datetime.utcnow() try: await msg.clear_reactions() except discord.HTTPException: pass await ctx.send( 'Question timed out and you lost {} points. Answer within {} seconds next time!' .format(score, int(QUESTION_TIMEOUT))) await self._on_wrong(ctx, answered_at, question_hash, score)