def helper(message): increment_usage(message.guild, 'stats') text = message.content # We need to flatten user and role mentions in order to properly query the database for each in message.mentions: text = text.replace('<@!{}>'.format(each.id), each.name) for each in message.channel_mentions: text = text.replace('<#{}>'.format(each.id), each.name) args = text.split() operator = 'cloud' if len(args) > 1: operator = args[1].lower() if operator == 'user' and len(args) > 2: setup(message.guild.id, user='******'.join(args[2:])) banner = word_cloud(' '.join(args[2:]), message.guild.name) return banner elif operator == 'channel' and len(args) > 2: setup(message.guild.id, channel=' '.join(args[2:])) banner = word_cloud(' '.join(args[2:]), message.guild.name) return banner else: setup(message.guild.id) return { 'common': lambda: word_frequency(args), 'cloud': lambda: word_cloud('the server', message.guild.name), 'count': lambda: word_count(args, message.guild.name), 'phrases': lambda: get_ngrams(args, message.guild.name), 'help': lambda: get_help(message), }.get(operator, lambda: None)()
def get_react(message): """ Get a random gif file from ./emotes or the servers folder :param message: <Discord.message object> :return: <String> Describing file location """ increment_usage(message.guild, 'gif') nsfw = True if 'nsfw' in message.content.lower() else False guild = message.guild.id reacts = [ '{}/emotes/{}'.format(DEFAULT_DIR, each) for each in listdir('./emotes') ] if isdir('{}/emotes/{}'.format(DEFAULT_DIR, guild)): reacts.extend([ '{}/emotes/{}/{}'.format(DEFAULT_DIR, guild, each) for each in listdir('./emotes/{}'.format(guild)) ]) if nsfw and isdir('{}/emotes/{}/nsfw'.format(DEFAULT_DIR, guild)): reacts.extend([ '{}/emotes/{}/nsfw/{}'.format(DEFAULT_DIR, guild, each) for each in listdir('./emotes/{}/nsfw'.format(guild)) ]) return random.choice(reacts)
def get_help(message): """ Return the help file located in ./docs/help :param message: <Discord.message object> :return: <String> The local help file """ increment_usage(message.guild, 'help') return wrap(fetch_file('help', 'dict'), 1990)
async def override(message): """ Admin command to manually change a user's saved location :param message: <Discord.message object> Describing which users to change to which locations :return: <String> Describing if the users' location was set or updated successfully """ increment_usage(message.guild, 'sched') if await is_admin(message.author, message): banner = Embed(title='Schedule Override', description='Change a user\'s location.') args = message.content.split() # Args should translate as: 1 id, 2 name, 3 locale if len(args) < 4: banner.add_field( name='Invalid syntax.', value= 'Formatting is: `!schedule override [TIMEZONE] @USER` you may mention any number of users.' ) else: locale = TZ_ABBRS.get(args[2].lower(), args[2]) success = [[], []] for each in message.mentions: user = get_user(int(each.id)) if user: with ENGINE.connect() as conn: query = Users.update().where( Users.c.id == each.id).values(locale=locale) conn.execute(query) if VERBOSE >= 2: print('[+] {} UPDATED LOCALE TO {} FOR USER {}'.format( message.author, locale, each.name)) success[0].append('Changed {} to: {}'.format( each.name, locale)) else: insert_user(each.id, message.guild, each.name, locale) if VERBOSE >= 2: print('[+] {} SETTING {}\'s SCHEDULE TO: {}'.format( message.author, each.name, locale)) success[1].append('Set {} to: {}'.format( each.name, locale)) banner.add_field(name='Updated schedule location.', value='\n'.join(success[0])) banner.add_field(name='Created schedule location.', value='\n'.join(success[1])) return banner
async def get_todays_word(message): """ Pull the word of the day from www.wordsmith.org :param message: <Discord.message object> :return: <str> Banner """ increment_usage(message.guild, 'wotd') async with aiohttp.ClientSession() as session: async with session.get(URL_WOTD) as resp: if resp.status == 200: text = await resp.read() else: if VERBOSE >= 2: print("[!] Status {}".format(resp.status)) soup = BeautifulSoup(text, "html.parser") text = soup.get_text() text = text.split('with Anu Garg') text = text[1].split('A THOUGHT FOR TODAY') text = text[0].strip() text = text.split('\n') fields = { 'header': '', } for idx, line in enumerate(text): if line: if line in URL_KEYWORDS.keys(): fields[line] = '' key = line elif idx == 0: fields['header'] = '{}\n{}'.format(fields['header'], line) else: fields[key] = '{}\n{}'.format(fields[key], line) banner = Embed(title="Word of the Day", description=fields['header']) fields.pop('header') for key, val in fields.items(): banner.add_field(name=key, value=val) banner.set_footer(text=URL_WOTD) return banner
async def get_help(result): increment_usage(result["message"].guild, 'help') help_ = fetch_file('help', 'general') if not await is_admin(result["message"].author, result["message"]): help_ = help_.split('For Admins:')[0] banner = Embed(title='General Help', description=help_) banner.add_field(name='Help support this bot!', value='All donations go to development and server costs.', inline=False) banner.add_field(name='PayPal', value=help_general['paypal']) # banner.add_field(name='Patreon', value=help_general['patreon']) banner.add_field(name='More Information', value='This bot is open source, find it at: {}'.format( help_general['github'])) banner.add_field(name='Invite the bot to your server.', value=help_general['invite'], inline=False) result["embed"] = banner return result
async def fetch_react(message): """ Save a gif a user added with !gif add :param message: <Discord.message object> :return: <String> Notify of gif being added or not """ increment_usage(message.guild, 'gif') url = str(message.attachments[0].url) extension = str(url.split('.')[-1].lower()) if extension != 'gif': return 'File must be a gif' file_name = str(url.split('/')[-1]) nsfw = True if 'nsfw' in message.content.lower() else False if nsfw: Path('{}/emotes/{}/nsfw'.format(DEFAULT_DIR, message.guild.id)).mkdir(parents=True, exist_ok=True) file_path = '{}/emotes/{}/nsfw/{}'.format(DEFAULT_DIR, message.guild.id, file_name) else: Path('{}/emotes/{}'.format(DEFAULT_DIR, message.guild.id)).mkdir(parents=True, exist_ok=True) file_path = '{}/emotes/{}/{}'.format(DEFAULT_DIR, message.guild.id, file_name) async with aiohttp.ClientSession() as session: async with session.get(url) as resp: if resp.status == 200: f = await aiofiles.open(file_path, mode='wb') await f.write(await resp.read()) await f.close() if VERBOSE >= 2: print("[+] Saved: {}".format(file_path)) return 'Added a new gif to !gif'
async def get_help(message): """ Get the command help file from ./docs/help :param message: <Discord.message object> :return: <String> Containing help for the user's available options or list of locations """ increment_usage(message.guild, 'help') args = message.content.split() banner = Embed(title='Schedule') if len(args) < 3: # Get general help raw = fetch_file('help', 'schedule') if not await is_admin(message.author, message): raw = raw.split('For Admins:')[0] banner.add_field(name='Help', value=raw) else: # Get list of cities or continents raw = fetch_file('locales', args[2].lower()) banner.add_field(name='Locales in {}'.format((args[2].lower())), value=raw) return banner
def get_help(message): """ Get the command help file from ./docs/help :param message: <Discord.message object> :return: <String> Containing help for the user's available options or list of locations """ increment_usage(message.guild, 'help') custom = '' try: guild_commands = custom_commands[message.guild.id] banner = Embed(title='Custom Command Help', description=fetch_file('help', 'custom')) for name, value in guild_commands.items(): custom = '{}`{}`: {}\n'.format(custom, name, value) available = Embed( title='Custom commands available in this server are:', description=custom) # banner.add_field(name='Custom commands available in this server are:', value=custom) return None, [banner, available] except Exception as e: if VERBOSE >= 0: print('[!] Exception in get help custom {}'.format(e)) pass
def set_schedule(message): """ User command to set their locale :param message: <Discord.message object> Describing where to set location to :return: <String> Describing new location for user """ increment_usage(message.guild, 'sched') args = message.content.split() config = load_config(message.guild.id) banner = Embed(title='Schedule') if config: server_locale = config[2] else: server_locale = 'Asia/Tokyo' # if no location provided default to server locale # if server has no locale default to Tokyo # if there is no server locale the schedule will not be displayed # however this allows users to still get their locations set locale = server_locale if len(args) < 3 else TZ_ABBRS.get( args[2].lower(), args[2]) user = get_user(message.author.id) old_locale = user[2] if user: with ENGINE.connect() as conn: query = Users.update().where( Users.c.id == message.author.id, ).values(locale=locale) conn.execute(query) banner.add_field(name='Updated your schedule location.', value='From: {}\nTo: {}'.format(old_locale, locale)) else: insert_user(message.author.id, message.guild, message.author.name, locale) banner.add_field(name='Set your schedule location.', value='To: {}'.format(locale)) return banner
async def helper(message): """ Main entry point from main.py, handles majority of argument parsing :param message: <Discord.message object> :return: <lambda function> Internal function dispatch """ text = message.content for each in message.mentions: text = text.replace('<@!{}>'.format(each.id), each.name) args = text.split() # Lore if args[0] == '!lore': increment_usage(message.guild, 'lore') if len(args) > 1: if args[1] == 'add' and await is_admin(message.author, message): return insert_quote(message, Lore) elif args[1] == 'delete' and await is_admin( message.author, message): return delete_quote(message.guild.id, args[2]) elif args[1] == 'help' and await is_admin(message.author, message): return get_help(message) else: return get_quote(message, Lore, ' '.join(args[1:]), True) else: return get_quote(message, Lore, None, True) # Quote with options elif len(args) > 1: increment_usage(message.guild, 'quote') if args[1] == 'help': return get_help(message) elif args[1] == 'delete' and await is_admin(message.author, message): return delete_quote(message.guild.id, args[2]) elif args[1] == 'log' and await is_admin(message.author, message): return get_quote_log(message.guild.id) else: return get_quote(message, Quotes, ' '.join(args[1:]), True) # Any random quote else: increment_usage(message.guild, 'quote') return get_quote(message, Quotes, None, True)
def get_help(message): increment_usage(message.guild, 'help') banner = Embed(title='Stats Help', description=fetch_file('help', 'stats')) return None, banner
async def dispatch(message): # Preprocessing if message.author.bot: return None increment_usage(message.guild, 'raw_messages') await tlog.log_message(message) args = message.content.split() result = {"message": message, "rawText": None, "embed": None, "file": None} if VERBOSE >= 2: print('[-] {} by {} in {} - {}'.format(args[0][1:], message.author.name, message.channel.name, message.guild.name)) # Guild level custom commands custom = tcustom.get_command(message) if custom: if VERBOSE >= 2: print('[-] {} by {} in {} - {}'.format('arg: {}'.format(args[0]), message.author.name, message.channel.name, message.guild.name)) result["rawText"] = custom[0] result["embed"] = custom[1] return result # Correct minor typos spell = Speller('cmd') operator = spell(args[0][1:]) if args[0][0] != '$': return None if VERBOSE >= 2: print('[-] {} by {} in {} - {}'.format(operator, message.author.name, message.channel.name, message.guild.name)) # Quotes if operator in {'quote', 'lore', 'q', 'l'}: result = get_quote(result) # Wolfram Alpha elif operator in {'w', 'wolf', 'wolfram'}: result = await get_wolfram(result) # Word of the Day elif operator in {'word', 'wotd'}: result["embed"] = await tword.get_todays_word(message) # Wiktionary elif operator in {'dict', 'dictionary', 'wiki', 'wiktionary'}: result["embed"] = tword.wiki(message) # Config elif operator == 'config': result["file"] = await config_helper(message) if result["file"] and type(result["file"]) is not DiscordFile: result["file"] = DiscordFile(result["file"]) await message.author.send(file=result["file"]) return None # Log elif operator == 'log' and await is_admin_test(message.author, message): banner = tlog.get_log(message) result["file"] = banner[1] if result["file"] and type(result["file"]) is not DiscordFile: result["file"] = DiscordFile(result["file"]) await message.author.send(content=banner[0], file=result["file"]) return None # Schedule elif operator in {'schedule', 'sched', 's'}: result["embed"] = tsched.helper(message) elif operator in {'next'}: result["embed"] = tsched.helper(message, 'next') # Yandex elif operator in {'yandex', 'image', 'tineye', 'reverse'}: result["rawText"] = tword.yandex(message) # Doip elif operator == 'doip' and int(message.guild.id) == 453859595262361611: increment_usage(message.guild, 'doip') result["rawText"] = tquote.get_quote(message, tquote.Quotes, "LaDoIphin", True) result["file"] = '{}/docs/doip.jpg'.format(DEFAULT_DIR) # GIF elif operator in {'gif', 'react', 'meme'}: return await get_gif(result) # Stats elif operator == 'stats': result["file"], result["embed"] = tstat.helper(message) # Magic Eightball elif operator in {'8ball', '88ball', 'ball', '8', 'eightball', 'eight'}: increment_usage(message.guild, 'eight') result["rawText"] = choice(EIGHTBALL) # General Help elif operator == 'help': result = await get_help(result) if result["file"] and type(result["file"]) is not DiscordFile: result["file"] = DiscordFile(result["file"]) return result
def get_command(message): """ Return a custom command to be sent to Discord. :param message: <Discord.message> Raw message object :return: <str> Banner """ embed_image = None links = '' custom = '.' args = message.content.split() config = tsched.load_config(message.guild.id) quotes = False if VERBOSE >= 2: print("Config: ") print(config) try: if args[0] == '$custom' or args[0:2] == ['$help', 'custom']: if VERBOSE >= 1: print('[-] custom - help by {} in {} - {}'.format( message.author.name, message.channel.name, message.guild.name)) return get_help(message) except Exception as e: if VERBOSE >= 2: print("Exception: ") print(e) if config: server_locale = config[2] url = config[6] timestamp = pendulum.now(tz=server_locale).to_datetime_string() else: url = ' ' timestamp = pendulum.now(tz='America/New_York').to_datetime_string() server_locale = 'America/New_York' try: # all custom commands for the guild guild_commands = custom_commands[message.guild.id] if VERBOSE >= 2: print("Guild Commands: ") print(guild_commands) # the value to be returned to the user, in raw formatting custom = guild_commands[args[0]] if VERBOSE >= 2: print("Custom: ") print(custom) # Check for image links or general links if VERBOSE >= 2: print("Checking for links...") for each in custom.split(): if each.find('http') != -1: if each.split('.')[-1] in extSet['image']: embed_image = each else: links = '{}\n{}'.format(links, each) # Setting up for nested loops prev = deepcopy(custom) count = 0 # for nested commands, not the most efficient solution but it works # if you can improve it please issue a code merge request while True: if VERBOSE >= 2: print("Formatting command, loop {}...".format(count)) # Discord's character limit is 2000 # count is chosen arbitrarily, move up or down for recursion limit if len(custom) + len(prev) > 1999 or count > 50: break else: for key in guild_commands: custom = custom.replace('<{}>'.format(key), guild_commands[key]) # TODO user mentions for <QUOTE> custom = custom.replace('\\n', '\n') custom = custom.replace('\\r', '<\n>') custom = custom.replace('<URL>', url) custom = custom.replace( '<NOW>', '{} in {}'.format(timestamp, server_locale)) custom = custom.replace( '<TIME>', '{} in {}'.format(timestamp, server_locale)) custom = custom.replace('<LOCALE>', server_locale) custom = custom.replace('<LOCATION>', server_locale) custom = custom.replace('<AUTHOR>', message.author.name) custom = custom.replace('<GUILD>', message.guild.name) while '<SCHED>' in custom: custom = custom.replace('<SCHED>', tsched.get_schedule(message, True)) #if '<QUOTE>' in custom: # custom = custom.replace('<QUOTE>', tquote.get_quote(message, tquote.Quotes, raw=True)) # quotes = True custom = custom.replace( '<LORE>', tquote.get_quote(message, tquote.Lore, raw=True)) if custom == prev: break else: count += 1 prev = deepcopy(custom) except Exception as e: if VERBOSE >= 2: print('[!] Exception in tcustom {}'.format(e)) pass finally: # Cleanup from quote with user if quotes: custom = custom.replace('>', '') if custom == '.': return None else: increment_usage(message.guild, 'custom') if VERBOSE >= 1: print('[-] custom - {} by {} in {} - {}'.format( args[0], message.author.name, message.channel.name, message.guild.name)) banner = Embed(title=args[0], description=custom) if embed_image: banner.set_image(url=embed_image) if links == '': links = None return links, banner
def wiki(message): """ Get the www.wiktionary.org entry for a word or phrase :param message: <Discord.message object> :return: <str> Banner or None """ increment_usage(message.guild, 'dict') args = message.content.split() if len(args) == 1 or args[1] == 'help': banner = Embed(title="Wiktionary Help") banner.add_field(name='About', value=help_dict['main']) banner.add_field(name='Aliased Command Names', value=help_dict['alternative']) return banner else: try: word = message.content.split(' ', maxsplit=1)[1] result = parser.fetch(word.strip())[0] etymology = result['etymology'] definitions = result['definitions'][0] pronunciations = result['pronunciations'] banner = Embed(title="Wiktionary", description=word) if definitions['partOfSpeech']: banner.add_field(name="Parts of Speech", value=definitions['partOfSpeech'], inline=False) if etymology: banner.add_field(name="Etymology", value=etymology, inline=False) if definitions['text']: defs = '' for each in definitions['text']: defs += '{} \n'.format(each) banner.add_field(name="Definitions", value=defs, inline=False) if definitions['relatedWords']: defs = '' for each in definitions['relatedWords']: for sub in each['words']: defs += '{}, '.format(sub) defs += '\n' banner.add_field(name="Related Words", value=defs, inline=False) if definitions['examples']: defs = '' for each in definitions['examples']: defs += '{} \n'.format(each) banner.add_field(name="Examples", value=defs, inline=False) if pronunciations['text'] or pronunciations['audio']: defs_text = '' defs_audio = '' for each in pronunciations['text']: defs_text += '{} \n'.format(each) for each in pronunciations['audio']: defs_audio += 'https:{} \n'.format(each) banner.add_field(name="Pronunciation, IPA", value=defs_text, inline=False) banner.add_field(name="Pronunciation, Audio Sample", value=defs_audio, inline=False) return banner except Exception as e: if VERBOSE >= 0: print('[!] Exception in wiki: {}'.format(e)) return None
async def wolfram(message): """ Return an image based response from the Wolfram Alpha API :param message: <Discord.message object> :return: <str> Banner or None """ increment_usage(message.guild, 'wolf') args = message.content.split() banner = Embed(title="Wolfram Alpha") try: if len(args) > 1: if args[1].lower() in {'simple', 'txt', 'text', 'textual'}: query_st = quote_plus(' '.join(args[2:])) query = URL_WOLF_TXT.format(WOLFRAM, query_st) async with aiohttp.ClientSession() as session: async with session.get(query) as resp: if resp.status == 200: text = await resp.read() elif resp.status == 501: return 'Wolfram cannot interpret your request.' else: return [ '[!] Wolfram Server Status {}'.format( resp.status), None ] text = text.decode('UTF-8') banner.add_field(name=' '.join(args[2:]), value=text) return banner elif args[1].lower() in { 'complex', 'graphic', 'graphical', 'image', 'img', 'gif' }: query_st = quote_plus(' '.join(args[2:])) query = URL_WOLF_IMG.format(WOLFRAM, query_st) file_path = '{}/log/wolf/{}.gif'.format( DEFAULT_DIR, message.id) async with aiohttp.ClientSession() as session: async with session.get(query) as resp: if resp.status == 200: f = await aiofiles.open(file_path, mode='wb') await f.write(await resp.read()) await f.close() return [None, file_path] elif resp.status == 501: return [ 'Wolfram cannot interpret your request.', None ] else: return [ '[!] Wolfram Server Status {}'.format( resp.status), None ] banner = Embed(title='Wolfram Help', description=fetch_file('help', 'wolfram')) return banner except Exception as e: if VERBOSE >= 0: print('[!] Wolfram failed to process command on: {}'.format( message.content)) print('[!] {}'.format(e)) return None