async def convert(self, ctx, argument): try: val = int(argument) except ValueError: raise commands.UserInputError( f"A hero with the ID `{argument}` could not be found.") else: hero = ChestHeroes.get(id=val) if hero is None: raise commands.UserInputError( f"A hero with the ID `{val}` could not be found.") return hero
async def on_raw_reaction_add(self, payload): if payload.user_id == self.bot.user.id: return # stdrole: if not payload.guild_id: self.cursor.execute(f'SELECT _guild_id FROM sr_messages WHERE _jump_url="0" AND _user_id={payload.user_id} AND _message_id={payload.message_id}') guild_id = self.cursor.fetchone() if guild_id: self.cursor.execute(f'SELECT _stdrole FROM guilds WHERE _id={guild_id[0]}') stdrole = self.cursor.fetchone()[0] if stdrole: guild = discord.utils.get(self.bot.guilds, id=guild_id[0]) member = discord.utils.get(guild.members, id=payload.user_id) # channel = discord.utils.get(bot.private_channels, id=payload.channel_id) channel = await member.create_dm() message = await channel.fetch_message(payload.message_id) if payload.emoji.name == self.REGISTER_EMOJI_ACCEPT: try: role = discord.utils.get(guild.roles, id=stdrole) await member.add_roles(role) self.cursor.execute(f'UPDATE members SET _regist=1 WHERE _id={payload.user_id} AND _guild={guild.id}') self.cursor.execute(f'DELETE FROM sr_messages WHERE _user_id={payload.user_id} AND _guild_id={guild.id} AND _jump_url="0"') await send_private(member, embed=discord.Embed(description=f"Welcome at the {guild.name} Discord!")) await message.delete() except: raise commands.UserInputError(f"Registration failed, please contact an admin or the developer (Whitekeks#3762)") elif payload.emoji.name == self.REGISTER_EMOJI_DENY: self.cursor.execute(f'DELETE FROM members WHERE _id={member.id} AND _guild={guild.id}') await send_private(member, embed=discord.Embed(description="Conditions must be accepted!")) await message.delete() await member.kick() self.conn.commit()
async def seek(self, ctx: commands.Context, seektime: str): player = self.getPlayer(ctx) if not player.actually_playing: return match = SEEK_PATTERN.fullmatch(seektime.strip()) if match is None: raise commands.UserInputError( "Invalid time format, try [+-][min:]sec") mins = match.group('min') if mins is not None: seekpos = (60 * int(mins) + int(match.group('sec'))) * 1000 else: seekpos = int(match.group('allsec')) * 1000 curpos = int(player.position) delta = match.group('delta') if delta is not None: if delta == '+': seekpos += curpos else: seekpos = curpos - seekpos seekpos = max(0, seekpos) await player.seek(seekpos) await ctx.message.add_reaction(THUMBS_UP)
async def convert(cls, ctx: commands.Context, argument: str): """Method tries to infer a user's input and parse it as a problem.""" db: sqlite3.Connection = ctx.bot.db cursor = db.cursor() # Check if it's an ID if argument.isnumeric(): cursor.execute( 'SELECT EXISTS (SELECT 1 from problems where id = ?)', (int(argument), )) if not cursor.fetchall()[0][0]: raise discord.ext.commands.UserInputError( f'No potd with such an ID (`{argument}`)') else: return cls(int(argument), db) # Check if it's an date as_datetime = dateparser.parse(argument) if as_datetime is not None: as_date = as_datetime.date() cursor.execute('SELECT id from problems where date = ?', (as_date, )) result = cursor.fetchall() if len(result) > 0: return cls(result[0][0], db) else: raise discord.ext.commands.UserInputError( f'No potd with that date! (`{str(as_date)}`)') raise commands.UserInputError( f'Failed to parse {argument} as a valid problem. ')
async def _duckduckgo(ctx, *, query): """Retrieve an answer from DuckDuckGo, using the Instant Answers JSON API. * query - A list of strings to be used in the search criteria. This command is both powerful and dangerous! It isn't its own command for a reason. """ if len(query) == 0: message = "Query not specified." raise commands.UserInputError("", message) logger.info(f"Retrieving DuckDuckGo answer with tags {query}.") params = urllib.parse.urlencode({ "q": query, "t": "ffsb", "format": "json", "ia": "answer" }) url = BASE_URL_DUCKDUCKGO.format(params) async with ctx.bot.session.get(url) as response: if response.status == 200: # This should be response.json() directly, but DuckDuckGo returns an incorrect MIME. data = await response.text() data = json.loads(data) if len(data) == 0: raise errors.ZeroDataLengthError() answer = html.unescape(data["Answer"]) logger.info("Answer retrieved!") return answer else: message = "Failed to fetch answer. :(" logger.info(message) return message
async def _profile_delete(self, ctx): if ctx.author.id not in profile.profiles: raise commands.UserInputError("You don't got a profile.") embed = discord.Embed( title="Profile", color=colors.special, description=f"Do you really want to delete your profile?") embed.set_footer(text=f"Requested by {ctx.author}", icon_url=ctx.author.avatar_url) msg = await ctx.send(embed=embed) await msg.add_reaction(emojis.check_mark) def check(reaction, user): return user == ctx.author and str( reaction.emoji) == emojis.check_mark try: reaction, user = await self.bot.wait_for("reaction_add", timeout=120, check=check) except asyncio.TimeoutError: await msg.edit(content="Your time ran out. Try again!") else: await Profile.delete(Profile(self.bot), ctx.author.id) await ctx.send( f"Successfully deleted the profile of **{ctx.author.mention}**" )
async def link(self, ctx: commands.Context, faceit: str): self.logger.debug(f'{ctx.author}: {ctx.prefix}{ctx.invoked_with} {ctx.args[2:]}') faceit_nick = faceit if re.match(r'.*faceit.com\/.*\/players.*', faceit): faceit_nick = faceit[faceit.rfind('players/')+8:] headers = {f'Authorization': f'Bearer {self.bot.faceit_token}'} async with aiohttp.ClientSession(headers=headers) as session: async with session.get(f'https://open.faceit.com/data/v4/players?nickname={faceit_nick}') as r: json_body = await r.json() if 'errors' in json_body: raise commands.UserInputError('No user found with that url/nickname') faceit_id = json_body['player_id'] db = Database('sqlite:///main.sqlite') await db.connect() await db.execute(''' REPLACE INTO users (discord_id, faceit_id) VALUES( :discord_id, :faceit_id ) ''', {"discord_id": str(ctx.author.id), "faceit_id": str(faceit_id)}) embed = discord.Embed(description=f'Connected {ctx.author.mention} to {faceit_nick} \n `{faceit_id}`', color=0x00FF00) await ctx.send(embed=embed) self.logger.info(f'{ctx.author} connected to {faceit}') await ctx.author.add_roles(ctx.guild.get_role(793186930220597269)) self.logger.info(f'Added Member role to {ctx.author}') await ctx.author.edit(nick=faceit_nick) self.logger.info(f'Changed {ctx.author}\'s nickname.')
async def skip(self, ctx, count: int = None): """Skip the current playing song Parameters ------------ count: int number of songs to skip """ lim = 15 vc = ctx.voice_client if not vc or not vc.is_connected(): return await ctx.send("I am not currently playing anything!", delete_after=20) if vc.is_paused(): pass elif not vc.is_playing(): return # Remove multiple items from queue if count != None and (count >= 1 and count <= lim): count -= 1 #removing 1 to account for currently playing song queue = (self.get_player(ctx)).queue itemsQueue = queue.qsize() x = min(count, itemsQueue) for i in range(x): queue.get_nowait() elif count != None and (count >= 0 or count <= lim): raise commands.UserInputError( f"Specify a number between 1 and {lim}") vc.stop() await ctx.send(f"**{ctx.author}**: Skipped the song!")
async def connect_cmd(self, ctx: commands.Context, url: str = None, realm: str = None) -> None: """Connect to the router. If a config exists this can be called without providing the details. To establish a new connection, provide the url and realm. """ if bool(url) != bool(realm): raise commands.UserInputError("if url is specified realm cannot be omitted") conn_id = get_conn_id(ctx) if url: client = LazyClient(libwampli.ConnectionConfig(realm, url), lambda e: self.on_subscription_event(conn_id, e)) else: client = self._cmd_get_lazy_client(ctx) try: await client except OSError: raise commands.CommandError("Couldn't connect") from None await self._switch_client(conn_id, client) embed = discord.Embed(title="Joined session", colour=discord.Colour.green()) await ctx.send(embed=embed)
async def queue(self, ctx): """Displays the current request queue.""" session = self._get_session(ctx.guild) total_length = session.current_track.length - session.current_track_play_time total_length += sum(track.length for track in session.queue.requests) length_str = str(datetime.timedelta(seconds=total_length)) paginator = EmbedPaginator( colour=discord.Colour.dark_green(), title=f'Upcoming requests - Total Queue Length: {length_str}', max_fields=10) if not session.queue.requests: raise commands.UserInputError('There are currently no requests.') for index, track in enumerate(session.queue.requests, 1): paginator.add_field( name=f'{index} - Requested by {track.requester}', value=track.information, inline=False) try: if len(paginator.pages) == 1: return await ctx.send(embed=paginator.pages[0]) menu = menus.MenuPages(paginator, clear_reactions_after=True, check_embeds=True) await menu.start(ctx) except discord.HTTPException: raise commands.BadArgument( 'I couldn\'t post the queue in this channel.')
async def proposal_info(self, ctx, *proposal_nums: int): """View information about proposals, such as age, number of for/against votes, and author. If no argument is specified, all open proposals will be selected. """ game = get_game(ctx) proposal_nums = dedupe(proposal_nums) description = '' if not proposal_nums: proposal_nums = (int(n) for n, p in game.proposals.items() if p['status'] == 'voting') proposal_nums = sorted(proposal_nums) if not proposal_nums: raise commands.UserInputError( "There are no open proposals. Please specify at least one proposal number." ) async def do_it(include_url): description = '\n'.join( map(lambda m: self.get_proposal_message(ctx, m, include_url), proposal_nums)) await ctx.send(embed=make_embed(color=colors.EMBED_INFO, title="Proposal information", description=description)) try: await do_it(True) except discord.HTTPException: await do_it(False)
async def convert(self, ctx: Context, argument: str): stripped = re.sub('[^0-9+]+', '', argument) if not re.match('^\+?[1-9]\d{1,14}$', stripped): raise commands.UserInputError( "A valid E.164 phone no. is required.") return stripped
async def link(self, ctx: commands.Context, steamID_input: str): self.logger.debug( f'{ctx.author}: {ctx.prefix}{ctx.invoked_with} {ctx.args[2:]}') steamID = SteamID(steamID_input) if not steamID.is_valid(): steamID = from_url(steamID_input, http_timeout=15) if steamID is None: steamID = from_url( f'https://steamcommunity.com/id/{steamID_input}/', http_timeout=15) if steamID is None: raise commands.UserInputError( message='Please enter a valid SteamID or community url.' ) db = Database('sqlite:///main.sqlite') await db.connect() await db.execute( ''' REPLACE INTO users (discord_id, steam_id) VALUES( :discord_id, :steam_id ) ''', { "discord_id": str(ctx.author.id), "steam_id": str(steamID.as_steam2_zero) }) embed = discord.Embed( description= f'Connected {ctx.author.mention} \n [{steamID.as_steam2}]({steamID_input})', color=0x00FF00) await ctx.send(embed=embed) #add another way to add roles for the users after login. #await ctx.author.add_roles(ctx.guild.get_role(808304852676378624)) self.logger.info(f'{ctx.author} connected to {steamID.as_steam2}')
async def convert(self, ctx, argument): try: val = int(argument) except ValueError: raise commands.UserInputError( f"Upgrade with ID `{argument}` could not be found") else: item = EmpireQuests.get(id=val) if item is None: raise commands.UserInputError( f"Quest with ID `{argument}` could not be found.") return item
async def remove_perms_command(self, ctx, command: str, *, user_or_role: Union[User, Role, str]): """Remove a user, role, or everyone permission to use a command.""" if command not in self.bot.all_commands: embed = Embed( title='Error', color=Color.red(), description='The command you are attempting to point ' f'to does not exist: `{command}`.') return await ctx.send(embed=embed) if hasattr(user_or_role, 'id'): value = user_or_role.id elif user_or_role in {'everyone', 'all'}: value = -1 else: raise commands.UserInputError('Invalid user or role.') await self.bot.update_perms(self.bot.all_commands[command].name, value, add=False) embed = Embed( title='Success', color=self.bot.main_color, description=f'Permission for {command} was successfully updated.') return await ctx.send(embed=embed)
async def macro_add_cmd(self, ctx: commands.Context, name: str, operation: str, *, args: str) -> None: """Define a new macro. You can use the function-style syntax to define a macro: macro add call wamp.session.get($GUILD_ID) """ if operation not in ("call", "publish"): raise commands.UserInputError(f"Unknown operation {operation}, expected call or publish") args = libwampli.split_arg_string(args) try: sub_args = await substitute_variables(ctx, args) libwampli.parse_args(sub_args) except Exception as e: raise commands.CommandError(f"Couldn't parse arguments: {e}") with self._with_db_writeback(get_conn_id(ctx)) as item: item = cast(DBItem, item) item.macros[name] = (operation, tuple(args)) await ctx.send(embed=discord.Embed( title=f"Added macro {name}", colour=discord.Colour.green(), ))
async def remove_perms_level(self, ctx, level: str, *, user_or_role: Union[User, Role, str]): """Remove a user, role, or everyone permission to use commands of a permission level.""" if level.upper() not in PermissionLevel.__members__: embed = Embed( title='Error', color=Color.red(), description='The permission level you are attempting to point ' f'to does not exist: `{level}`.') return await ctx.send(embed=embed) if hasattr(user_or_role, 'id'): value = user_or_role.id elif user_or_role in {'everyone', 'all'}: value = -1 else: raise commands.UserInputError('Invalid user or role.') await self.bot.update_perms(PermissionLevel[level.upper()], value, add=False) embed = Embed( title='Success', color=self.bot.main_color, description=f'Permission for {level} was successfully updated.') return await ctx.send(embed=embed)
async def substitute_variable(ctx: commands.Context, arg: str) -> str: """Perform a substitution for a single argument.""" match = RE_SNOWFLAKE_MATCH.match(arg) if match: return match.group(1) match = RE_VARIABLE_MATCH.match(arg) if match: var = match.group(1).lower() if var == "guild_id": try: return str(ctx.guild.id) except AttributeError: raise commands.UserInputError("no guild id available") from None match = RE_CONVERSION_MATCH.match(arg) if match: value, typ = match.groups() try: converter = CONVERTERS[typ] except KeyError: pass else: repl = await call_converter(converter, ctx, value) return str(repl.id) return arg
async def set_format(self, ctx, level: str, *, format_info: str): if level in ["normal", "hard"]: self.config.hset("config:pkmn_tourney:leaders:{}:{}".format(ctx.message.author.id, level), "format", format_info) await ctx.send("\N{OK HAND SIGN}") else: raise commands.UserInputError("Levels are `normal` and `hard`.")
async def copy_bracket(self, ctx, index_from: int, index_to: int): """Copies the settings of a bracket to another one.""" tournament = self.get_tournament(ctx.guild.id) brackets = tournament.brackets if index_from > 0 and index_from <= len( brackets) and index_to > 0 and index_to <= len(brackets): bracket_from = brackets[index_from - 1] bracket_to = brackets[index_to - 1] bracket_to.post_result_channel_id = bracket_from.post_result_channel_id bracket_to.current_round = bracket_from.current_round for spreadsheet_type in Bracket.get_spreadsheet_types().keys(): spreadsheet_from = bracket_from.get_spreadsheet_from_type( spreadsheet_type) if spreadsheet_from: spreadsheet_to = bracket_to.get_spreadsheet_from_type( spreadsheet_type) if not spreadsheet_to: spreadsheet_to = getattr( tosurnament_api, "create_" + spreadsheet_type)(tournament.id, bracket_to.id, spreadsheet_from) else: spreadsheet_from.copy_to(spreadsheet_to) getattr(tosurnament_api, "update_" + spreadsheet_type)(tournament.id, bracket_to.id, spreadsheet_to) await self.send_reply(ctx, "success", bracket_from.name, bracket_to.name) return raise commands.UserInputError()
async def link(self, ctx: commands.Context, steamID_input: str): steamID = SteamID(steamID_input) if not steamID.is_valid(): steamID = from_url(steamID_input, http_timeout=15) if steamID is None: steamID = from_url( f'https://steamcommunity.com/id/{steamID_input}/', http_timeout=15) if steamID is None: raise commands.UserInputError( message='Please enter a valid SteamID or community url.' ) db = Database('sqlite:///main.sqlite') await db.connect() await db.execute( ''' REPLACE INTO users (discord_id, steam_id) VALUES( :discord_id, :steam_id ) ''', { "discord_id": str(ctx.author.id), "steam_id": str(steamID.as_steam2_zero) }) embed = discord.Embed( description= f'Connected {ctx.author.mention} \n `{steamID.as_steam2}`', color=0x00FF00) await ctx.send(embed=embed)
def option_converter(argument): if len(argument) > 1 and argument.startswith( "-") and argument[1:].isalpha(): return argument[1:] else: raise commands.UserInputError( f"Could not convert '{arg}' to an option.")
async def nick(self, ctx, user: Optional[discord.Member] = False, *, nickname): if user: if nickname: if not ctx.channel.permissions_for(ctx.author).administrator: return await ctx.send( "Error: You need admin to set other people's nicknames!" ) else: nickname = ctx.author.display_name else: user = ctx.author if not nickname: raise commands.UserInputError(ctx.message) if "<" in nickname and ">" in nickname: await ctx.send( "Hey! You can't have mentions/emotes in your nickname!") elif not re.match(r'^(\w|\s)+$', nickname): await ctx.send( "Hey! Please keep your nickname to only letters, numbers, and spaces!" ) else: settings.nickname.set(ctx.guild, user, nickname) await ctx.send(embed=discord.Embed( title="Nickname Change", description=f"Changed {user.name}'s nickname to {nickname}"))
async def quote_add(self, ctx: commands.Context, user: str, *, message: str): """!kazhelp description: | Add a new quote manually. TIP: To automatically find and add a recent message, use {{!quote grab}}. parameters: - name: user type: "@user" description: > The user being quoted. Should be an @mention or a discord ID. - name: message type: string description: The quote text to add. examples: - command: .quote add @JaneDoe#0921 Ready for the mosh pit, shaka brah. """ if len(message) > Quote.MAX_MESSAGE_LEN: raise commands.UserInputError( "That quote is too long! Maximum length {:d} characters.". format(Quote.MAX_MESSAGE_LEN)) quote = c.store_quote(user=c.query_user(self.server, user), saved_by=c.query_user(self.server, ctx.message.author.id), channel_id=ctx.message.channel.id, message=message, timestamp=ctx.message.timestamp) message = "Added quote: {}".format(self.format_quote(quote)) logger.info(message) await self.bot.say( embed=self.make_single_embed(quote, title="Added quote.")) await self.send_output(message)
async def stdrole_delete(self, ctx): guild = ctx.guild self.cursor.execute(f'UPDATE guilds SET _stdrole = 0, _autodelete = {False} WHERE _id = {guild.id}') self.conn.commit() try: os.remove(self.PATH + f"/stdrole_messages/{guild.id}.txt") except: commands.UserInputError("No stdrole was set!") await ctx.send(embed=discord.Embed(description="autoregistration and autodelete successfully turned off"))
async def connect_(self, ctx, *, channel: VoiceChannel = None): """PFXconnect <channel>""" try: await ctx.message.delete() except discord.HTTPException: pass if not channel: try: channel = ctx.author.voice.channel except AttributeError: raise commands.UserInputError( 'No channel to join. Please either specify a valid channel or join one.' ) player = self.bot.wavelink.get_player(ctx.guild.id, cls=Player) cperms = self.bot.getperms(ctx.guild.me, channel) if 'connect' in cperms and 'speak' in cperms: do = 'nothing' else: raise commands.BotMissingPermissions(['connect', 'speak']) try: if player.is_connected: if ctx.author.voice.channel and ctx.guild.me.voice.channel: if ctx.author.voice.channel == ctx.guild.me.voice.channel: return except AttributeError: await player.connect(channel.id) player.cmdchannel_id = ctx.channel.id await player.connect(channel.id) player.cmdchannel_id = ctx.channel.id
async def add_currency(self, ctx, currency_name: str, color: discord.Color, *aliases: str): """Create a new currency.""" game = get_game(ctx) currency_name = currency_name.lower() aliases = list(map(str.lower, aliases)) for s in [currency_name] + aliases: if game.get_currency(s): raise commands.UserInputError( f"Currency name '{s}' is already used.") description = f"Color: {format_discord_color(color)}" description += "\nAliases: " + (", ".join(f"`{a}`" for a in aliases) or "(none)") m = await ctx.send( embed=make_embed(color=colors.EMBED_ASK, title=f"Create currency '{currency_name}'?", description=description)) response = await react_yes_no(ctx, m) await m.edit( embed=make_embed(color=YES_NO_EMBED_COLORS[response], title=f"Currency '{currency_name}' established" if response else "Currency creation " + YES_NO_HUMAN_RESULT[response], description=description)) if response != 'y': return game.add_currency(currency_name, color=color, aliases=aliases)
async def set_players_spreadsheet_range_value(self, ctx, range_name, range_value): """Puts the input values into the corresponding bracket.""" if not spreadsheet_api.check_range(range_value): raise commands.UserInputError() await self.set_players_spreadsheet_values(ctx, {range_name: range_value})
async def add_safe_bytes(self, context, name, author_id, image_data: bytes, *, reason=None): """Try to add an emote from bytes. On error, return a string that should be sent to the user. If the image is static and there are not enough free static slots, convert the image to a gif instead. """ counts = collections.Counter(map(operator.attrgetter('animated'), context.guild.emojis)) # >= rather than == because there are sneaky ways to exceed the limit if counts[False] >= context.guild.emoji_limit and counts[True] >= context.guild.emoji_limit: # we raise instead of returning a string in order to abort commands that run this function in a loop raise commands.UserInputError('This server is out of emote slots.') static = utils.image.mime_type_for_image(image_data) != 'image/gif' converted = False if static and counts[False] >= context.guild.emoji_limit: image_data = await utils.image.convert_to_gif_in_subprocess(image_data) converted = True try: emote = await self.create_emote_from_bytes(context.guild, name, author_id, image_data, reason=reason) except discord.InvalidArgument: return discord.utils.escape_mentions(f'{name}: The file supplied was not a valid GIF, PNG, JPEG, or WEBP file.') except discord.HTTPException as ex: return discord.utils.escape_mentions( f'{name}: An error occurred while creating the the emote:\n' + utils.format_http_exception(ex)) s = f'Emote {emote} successfully created' return s + ' as a GIF.' if converted else s + '.'
async def sr_message_set(self, ctx, message_id=None, *args): try: message = await ctx.channel.fetch_message( int(message_id[int(len(message_id) - 18):])) if not message: raise CustomError() except CustomError: raise commands.UserInputError( "Could not find Message, make sure the Command is in the same Channel as the Message" ) except: message = await ctx.send(embed=discord.Embed( description=message_id)) # test if Message allready exists in Database (then this is a update): self.cursor.execute( f'SELECT * FROM sr_messages WHERE _message_id={message.id} AND _guild_id={ctx.guild.id} AND _jump_url="{message.jump_url}" AND _user_id={message.author.id}' ) if not self.cursor.fetchone(): self.cursor.execute( f'INSERT INTO sr_messages VALUES ({message.id}, {ctx.guild.id}, "{message.jump_url}", {message.author.id})' ) self.conn.commit() for emoji in args: try: await message.add_reaction(emoji) except: None