async def awoo_whitelist(bot, context): """(De)whitelists the given user.""" user = context.arguments[0] whitelist = data.get(bot, __name__, 'whitelist', guild_id=context.guild.id, default=[]) # (De)whitelist user if user: if user.id in whitelist: action = 'removed from' data.list_data_remove(bot, __name__, 'whitelist', value=user.id, guild_id=context.guild.id) else: action = 'added to' data.list_data_append(bot, __name__, 'whitelist', user.id, guild_id=context.guild.id) return Response(content="User {} the whitelist.".format(action)) # Show whitelisted users else: if not whitelist: raise CBException("There are no whitelisted users.") users = [(data.get_member(bot, it, attribute='mention') or 'Unknown ({})'.format(it)) for it in whitelist] return Response(embed=discord.Embed(title="Whitelisted users", description=', '.join(users)))
async def character_create(bot, context): """Creates a new character entry.""" # Check if an entry is currently being created/edited tracker = data.get(bot, __name__, 'tracker', user_id=context.author.id, volatile=True) if tracker: return Response( content=( "You are currently already creating or editing a character entry. " "Would you like to cancel your current session?"), message_type=MessageTypes.INTERACTIVE, extra_function=_cancel_menu, extra={'buttons': ['🇾', '🇳']}) # 10 character limit cursor = data.db_select( bot, from_arg='characters', where_arg='owner_id=%s', input_args=[context.author.id]) characters = cursor.fetchall() if cursor else [] if len(characters) >= 10: raise CBException("Cannot create more than 10 characters.") # Use the provided character file if context.message.attachments: content = await _process_data( bot, context.author, context.message.attachments[0].url, propagate_error=True) return Response(content=content) # Use the online entry creator else: await _create_session(bot, context.author) if not context.direct: await context.message.add_reaction('📨')
async def role_list(bot, context): """Lists the available roles for self-assignment.""" if context.arguments[0]: # List members that have this role role = context.arguments[0] if role.is_default(): raise CBException("Cannot list members with the @everyone role.") if not role.members: raise CBException("No members have the role {}.".format(role.mention)) elif len(role.members) > 80: name_file = discord.File( utilities.get_text_as_file('\n'.join(str(it) for it in role.members)), filename='members.txt') embed = discord.Embed( description='{} members have this role.'.format(len(role.members))) return Response(file=name_file, embed=embed) else: plural = len(role.members) > 1 return Response(embed=discord.Embed( title='{} member{} {} this role:'.format( len(role.members), 's' if plural else '', 'have' if plural else 'has'), description=', '.join(it.mention for it in role.members))) else: # List self-assignable roles available_roles = _check_roles(bot, context.guild) if not available_roles: raise CBException("There are no self-assignable roles available.") embed = discord.Embed( title='Self-assignable roles:', description='\n'.join(it.mention for it in available_roles)) embed.set_footer(text='To join a role, use {}role join'.format( utilities.get_invoker(bot, guild=context.guild))) return Response(embed=embed)
async def role_verification(bot, context): """Sets or clears a verification role to allow self-assignment.""" role = context.arguments[0] if role: data.add_custom_role(bot, __name__, 'verified', role) return Response(embed=discord.Embed( description='Only users with the {} role can self-assign roles.'.format(role.mention))) else: data.remove_custom_role(bot, __name__, 'verified', context.guild) return Response(embed=discord.Embed(description='Anybody can now self-assign roles.'))
async def awoo_leaderboard(bot, context): """Displays the top 10 violators.""" cursor = data.db_select(bot, from_arg='awoo', additional='ORDER BY debt DESC', limit=10) entries = cursor.fetchall() if cursor else [] if not entries: raise CBException("Nobody has made any awoo violations yet!") stats = [[], []] # debt/violations, user for index, entry in enumerate(entries): stats[0].append('`{0}.` ${1.debt} | {1.violations}'.format( index + 1, entry)) user = data.get_member(bot, entry.user_id, safe=True, attribute='mention') user = user or 'Unknown ({})'.format(entry.user_id) stats[1].append('`\u200b`{}'.format(user)) embed = discord.Embed(title=':scales: Awoo violation leaderboard') embed.add_field(name='Debt | Violations', value='\n'.join(stats[0])) embed.add_field(name='User', value='\n'.join(stats[1])) return Response(embed=embed)
async def get_roll(bot, context): """Gets a random roll in the D&D syntax style.""" rolls, sides, bonus = context.arguments[0] max_characters = len(str(sides)) results = [random.randint(1, sides) for it in range(rolls)] text_results = [ '`{: <{}}\u200b`'.format(it, max_characters) for it in results ] split_results = [ text_results[it:it + 10] for it in range(0, len(text_results), 10) ] result_text = '\n'.join(', '.join(it) for it in split_results) embed = discord.Embed(title=':game_die: Dice roll', description=result_text) total = sum(results) if rolls > 1: embed.add_field(name='Sum', value=str(total)) embed.add_field(name='Mean', value='{:.2f}'.format(total / len(results))) if bonus: embed.add_field(name='Final', value=str(total + bonus)) return Response(embed=embed)
async def role_create(bot, context): """Creates the given role.""" role_name = context.arguments[0] guild_role_names = list(it.name.lower() for it in context.guild.roles) if role_name.lower() in guild_role_names: raise CBException("A similarly named role already exists.") color = context.options.get('color', discord.Color.default()) hoisted, mentionable = 'hoisted' in context.options, 'mentionable' in context.options try: new_role = await context.guild.create_role( name=role_name, color=color, hoist=hoisted, mentionable=mentionable, reason='Created self-assigned role') except discord.Forbidden: raise CBException("The bot is missing the `Manage Roles` permission.") data.list_data_append( bot, __name__, 'roles', new_role.id, guild_id=context.guild.id, duplicates=False) embed = discord.Embed( title='Created a new self-assignable role:', description='{}\n{}\n{}'.format( new_role.mention, 'Hoisted' if hoisted else '', 'Mentionable' if mentionable else '').strip(), color=color if 'color' in context.options else discord.Embed.Empty) return Response(embed=embed)
async def verification_karma(bot, context): """Checks if the given user has karma in the r/furry subreddit.""" verified_role = _get_verified_role(bot, context.guild) karma = await _check_reddit_karma(bot, context.arguments[0]) threshold = configurations.get(bot, __name__, 'karma_threshold') # Replace karma amount with limits if it is past that limits = [ configurations.get(bot, __name__, 'submission_karma_limit'), configurations.get(bot, __name__, 'comment_karma_limit') ] zipped = zip(karma, limits) karma_strings = [(str(it) if it <= ts else (str(ts) + '+')) for it, ts in zipped] if sum(karma) >= threshold: response = ':white_check_mark: {} submission karma, {} comment karma' qualifies = 'qualifies' color = discord.Color(0x77b255) else: response = ':x: {} submission karma, {} comment karma' qualifies = 'needs at least {} combined karma'.format(threshold) color = discord.Color(0xdd2e44) description = '{0}\n[u/{1}](https://www.reddit.com/user/{1}) {2} for {3}'.format( response.format(*karma_strings), context.arguments[0], qualifies, verified_role.mention) return Response(embed=discord.Embed(description=description, color=color))
async def get_response(bot, context): response = Response() if context.author.voice: voice_channel = context.author.voice.channel voice_client = await utilities.join_and_ready(bot, voice_channel) options = {'format': 'bestaudio/best', 'noplaylist': True} downloader = YoutubeDL(options) url = context.arguments[0] try: file_location = data.get_from_cache(bot, None, url=url) if not file_location: logger.info("Not found in cache. Downloading...") info = await utilities.future(downloader.extract_info, url, download=False) download_url = info['formats'][0]['url'] file_location = await data.add_to_cache(bot, download_url, name=url) ffmpeg_options = '-protocol_whitelist "file,http,https,tcp,tls"' voice_client.play( discord.FFmpegPCMAudio(file_location, before_options=ffmpeg_options)) except BotException as e: raise e # Pass up except Exception as e: raise CBException("Something bad happened.", e=e) response.content = "Playing your stuff." else: raise CBException("You're not in a voice channel.") return response
async def commission_configure(bot, context): """Configures the channel and cooldown for the commission channel rules.""" rules = data.get(bot, __name__, 'rules', guild_id=context.guild.id, default={}) default_cooldown = configurations.get(bot, __name__, 'default_cooldown') replace_channel = context.options.get('channel', rules.get('channel')) replace_cooldown = context.options.get( 'cooldown', rules.get('cooldown', default_cooldown)) if not replace_channel: raise CBException("No commission channel configured.") # Reset advertisement data rules = {'channel': replace_channel, 'cooldown': replace_cooldown} data.add(bot, __name__, 'rules', rules, guild_id=context.guild.id) data.remove(bot, __name__, 'advertisements', guild_id=context.guild.id, volatile=True, safe=True) await _get_advertisement_data(bot, context.guild) description = 'Channel: {0.mention}\nCooldown: {1}'.format( data.get_channel(bot, replace_channel), utilities.get_time_string(replace_cooldown, text=True, full=True)) embed = discord.Embed(title='Commission channel configuration:', description=description) return Response(embed=embed)
async def tagremote_update(bot, context): """Renames the webhook with an updated tag list file.""" # Check for an existing session session_data = data.get(bot, __name__, 'data', guild_id=context.guild.id) if not session_data: raise CBException("No session available.") channel = data.get_channel(bot, session_data['channel']) if not channel: await _delete_session(bot, context.guild) raise CBException("Failed to get the channel.") voice_channel = data.get_channel(bot, session_data['voice_channel']) if not voice_channel: await _delete_session(bot, context.guild) raise CBException("Failed to get the voice channel.") webhooks = await channel.webhooks() if not webhooks: await _delete_session(bot, context.guild) raise CBException("No webhooks available.") for webhook in webhooks: if webhook.id == session_data['webhook']: break else: await _delete_session(bot, context.guild) raise CBException("Webhook not found.") tag_dictionary = _get_tag_dictionary(bot, context.guild) session_code = await _upload_session_data(bot, channel, voice_channel, webhook, tag_dictionary) updated_code = session_code.split(':')[1] await webhook.edit(name='Tag Remote [{}]'.format(updated_code)) return Response( content="Tag data refreshed. Update the remote on your phone via the options menu.")
async def translate(bot, context): """Translates the given text.""" source = context.options.get('from', 'auto') if 'to' in context.options: destination = context.options['to'] else: if context.direct: destination = 'en' else: destination = data.get( bot, __name__, 'default', guild_id=context.guild.id, default='en') try: result = await utilities.future( TRANSLATOR.translate, context.arguments[0], src=source, dest=destination) except ValueError as e: if 'source' in e.args[0]: issue, language = 'source', source else: issue, language = 'destination', destination raise CBException("Invalid {} language (`{}`).\n{}".format(issue, language, LANGUAGE_LINK)) except Exception as e: raise CBException("Failed to translate the text.", e) full_source = googletrans.constants.LANGUAGES[result.src.lower()].title() full_destination = googletrans.constants.LANGUAGES[result.dest.lower()].title() embed = discord.Embed( title=':arrows_counterclockwise: Google Translate', color=discord.Color(0x3b88c3)) embed.add_field(name=full_source, value=context.arguments[0], inline=False) embed.add_field(name=full_destination, value=result.text, inline=False) return Response(embed=embed)
async def role_joinleave(bot, context): """Adds/removes the given role(s) to/from the member.""" # Check for a verified role verified_role = data.get_custom_role(bot, __name__, 'verified', context.guild) if verified_role: if not data.has_custom_role(bot, __name__, 'verified', member=context.author): raise CBException("You must have the role {} in order to self-assign roles.".format( verified_role.mention)) joining = context.id == 'join' available_role_ids = data.get(bot, __name__, 'roles', guild_id=context.guild.id, default=[]) for role in context.arguments: if role.id not in available_role_ids: raise CBException("The role {} is not self-assignable.".format(role.mention)) try: action_function = context.author.add_roles if joining else context.author.remove_roles await action_function(*context.arguments, reason="Self-assignable role") except discord.Forbidden: if not context.guild.me.guild_permissions.manage_roles: raise CBException("The bot is missing the `Manage Roles` permission.") _check_roles(bot, context.guild) action = 'assign' if joining else 'remov' raise CBException("The role(s) could not be {}ed due to a hierarchy issue.".format(action)) embed = discord.Embed( title='You have {} the role{}:'.format( 'joined' if joining else 'left', '' if len(context.arguments) == 1 else 's'), description='\n'.join(it.mention for it in context.arguments)) return Response(embed=embed)
async def character_remove(bot, context): """Removes a character entry.""" clean_name = context.arguments[0].clean_name data.db_delete( bot, 'characters', where_arg='clean_name=%s AND owner_id=%s', input_args=(clean_name, context.author.id)) return Response(content="Character deleted.")
async def character_browse(bot, context): """Browses a list of all characters.""" character_listing = data.db_select( bot, from_arg='characters', additional='ORDER BY clean_name ASC').fetchall() if not character_listing: raise CBException("There are no characters in the database.") page_index = 0 if context.arguments[0]: search = context.arguments[0] closest_index = 0 for index, character in enumerate(character_listing): if search <= character.clean_name: closest_index = index else: break page_index = int(closest_index / 10) # 10 entries per page embed = discord.Embed( title=':book: Character browser', description='{} total character{}'.format( len(character_listing), '' if len(character_listing) == 1 else 's')) state_data = [page_index, character_listing] return Response( embed=_build_browser_menu(bot, embed, *state_data), message_type=MessageTypes.INTERACTIVE, extra_function=_browser_menu, extra={'buttons': ['⬅', '➡'], 'userlock': False}, state_data=state_data)
async def run_query(bot, context): results = context.options['results'] text_result = 'text' in context.options query_url, result, warning = await get_query_result( bot, context.guild, context.arguments[0], text_result=text_result, result_lines=results) if configurations.get(bot, __name__, 'ads'): location = context.channel if context.direct else context.guild advertisement = get_advertisement(bot, location) else: advertisement = None embed = discord.Embed( description=advertisement if advertisement else discord.Embed.Empty, colour=discord.Colour(0xdd1100)) embed.set_author(name='Query', url=query_url, icon_url='https://i.imgur.com/mFfV1zk.png') if text_result: for name, value in result: embed.add_field(name=name, value=value, inline=False) else: embed.set_image(url=result) if warning: embed.set_footer(text=warning, icon_url='https://i.imgur.com/CGl7njZ.png') return Response(embed=embed)
async def kick_member(bot, context): """Deletes member from spreadsheet.""" global gc family_name = context.arguments[0] family_name = family_name.lower() author_id = context.author.id response = Response() # VAR index_of_member_list = None list_of_family_names = [] index_of_user = None # Check for malicious family name. if not (len(family_name) > 1 and '=' not in family_name[0]): response.content = f'{family_name} is not a valid family name.' return response # opens spreadsheet sh = await utilities.future(gc.open, SPREADSHEET_NAME) # gets all available worksheets worksheet = await utilities.future(sh.worksheets) # get index of member list for i, sheet in enumerate(worksheet): if MEMBER_LIST_NAME in str(sheet): index_of_member_list = i if index_of_member_list == None: response.content = 'Something went wrong when initalizing sheets (check member list name).' return response # get member list form_member_data = await utilities.future(sh.get_worksheet, index_of_member_list) form_member_list = await utilities.future(form_member_data.get_all_values) for i, row in enumerate(form_member_list): if i >= 4: # names start on row 4 here if len(row[0]) != 0: list_of_family_names.append(row[0].lower()) # invalid family name if family_name not in list_of_family_names: response.content = f'{family_name} not a valid family name.' return response # get index of user index_of_user = list_of_family_names.index(family_name) index_of_user += 5 # offset, sheet specific # deletes entries in that row await utilities.future(form_member_data.update, f'A{index_of_user}:E{index_of_user}', [['', '', '', '', '']], value_input_option='user_entered') form_member_data.sort((1, 'asc'), range='A5:Z105') current_time = datetime.datetime.now().strftime('%m/%d/%Y %H:%M:%S') response.content = f'<@{author_id}> successfully kicked {family_name} on {current_time} from the spreadsheet.' return response
async def custom_notify(bot, context): """This is only called with the notify command. This function is called with the same arguments as get_response. """ await utilities.notify_owners( bot, '{0.author} from {0.guild}: {0.arguments[0]}'.format(context)) return Response(content="Notified the owners with your message!")
async def commission_list(bot, context): """Lists the users that have advertised in the commission channel.""" advertisement_data = await _get_advertisement_data(bot, context.guild) return Response( embed=discord.Embed(title='List of advertisers:', description=', '.join( it.author.mention for it in advertisement_data.values())))
async def autolog_setchannel(bot, context): """Sets the channel where logs will be dumped.""" data.add(bot, __name__, 'log_channel', context.arguments[0].id, guild_id=context.guild.id) return Response("The logging channel is now set to {}".format( context.arguments[0].mention))
async def translate_languages(bot, context): """Gets a list of valid language codes.""" codes = ['{} [`{}`]'.format(v.title(), k) for k, v in googletrans.constants.LANGUAGES.items()] code_break = int(len(codes)/3) + 1 code_split = [codes[it:it + code_break] for it in range(0, len(codes), code_break)] embed = discord.Embed(title='Language list') for split in code_split: embed.add_field(name='\u200b', value='\n'.join(split)) return Response(embed=embed)
async def translate_default(bot, context): """Sets the default translation language.""" language = context.arguments[0] if language: data.add(bot, __name__, 'default', language, guild_id=context.guild.id) else: data.remove(bot, __name__, 'default', guild_id=context.guild.id) return Response( content='Default language set to {}.'.format(language if language else 'English'))
async def add_text(bot, context): text_type, new_text = context.arguments table_name = 'txyz_' + TYPE_NAMES[text_type] data.db_insert(bot, table_name, specifiers='value', input_args=new_text, safe=False) return Response(content='Added a {}.'.format(table_name[5:-1]))
async def remove_text(bot, context): text_type, entry_id = context.arguments table_name = 'txyz_' + TYPE_NAMES[text_type] data.db_delete(bot, table_name, where_arg='key=%s', input_args=[entry_id], safe=False) return Response( content='Removed {} entry {}.'.format(table_name[5:-1], entry_id))
async def verification_set(bot, context): """Sets the verification role.""" role = context.arguments[0] data.add(bot, __name__, 'verification_role', role.id, guild_id=context.guild.id) return Response(embed=discord.Embed( description='Verification role set to {}'.format(role.mention)))
async def awoo_reset(bot, context): """Removes the given user from the database.""" user = context.arguments[0] removed = data.db_delete(bot, 'awoo', where_arg='user_id=%s', input_args=[user.id]) if not removed: raise CBException("User not in violation database.") return Response(content="User removed from the database.")
async def list_text(bot, context): list_lines = [] for text_type in TYPE_NAMES: list_lines.append('\n\n' + text_type) cursor = data.db_select(bot, from_arg='txyz_' + text_type) for key, value in cursor.fetchall(): list_lines.append('\t{}: {}'.format(key, value)) text_file = utilities.get_text_as_file('\n'.join(list_lines)) discord_file = discord.File(text_file, 'txyz_list.txt') return Response(content='Table contents:', file=discord_file)
async def live_enable(bot, context): url_suffix = context.message.attachments[0].url.split('/', 4)[-1] channel_name = '_1{visible}{fullscreen}{theme}{weather}{audio}|{url_suffix}'.format( visible=0 if 'invisible' in context.options else 1, url_suffix=url_suffix, **context.options) txyz_guild = bot.get_guild(TXYZ_GUILD) await txyz_guild.voice_channels[3].edit(name=channel_name) return Response( content='Live page enabled with code: {}'.format(channel_name))
async def commission_whitelist(bot, context): """Whitelists the given user from the commission channel limits.""" if context.arguments[0]: # Toggle user added = data.list_data_toggle(bot, __name__, 'whitelist', context.arguments[0], guild_id=context.guild.id) return Response( '{}ed user {} the commission channel rules whitelist.'.format( *(('Add', 'to') if added else ('Remov', 'from')))) else: # List users users = data.get(bot, __name__, 'whitelist', guild_id=context.guild.id) if not users: raise CBException( "No users in the commission channel rules whitelist to list.") return Response(embed=discord.Embed(title='Whitelisted users:', description=', '.join( '<@{}>'.format(it) for it in users)))
async def pride_fill(bot, context): """Fills the given image's background with the flag.""" image = await _get_image(bot, context=context) flag = await _generate_pride_flag(bot, context.arguments[0][1], image.size) result = _process_fill(image, flag, rotation=context.options['rotation']) discord_file = _file_from_image(result) embed = discord.Embed( title='{} pride flag fill'.format(context.arguments[0][1]['title'])) embed.set_image(url='attachment://result.png') return Response(file=discord_file, embed=embed, message_type=MessageTypes.PERMANENT)