async def perform_calculation(self, arg, message): with (await LOCKS[message.channel.id]): await self.ensure_loaded(message.channel, message.author) # Yeah this is kinda not great... arg = arg.strip('` \n\t') if arg == '': # If no equation was given, spit out the help. if not message.content.startswith('=='): await self.send_message( message, 'Type `=help calc` for information on how to use this command.' ) elif arg == 'help': prefix = await core.settings.get_channel_prefix(message.channel ) await self.send_message( message, SHORTCUT_HELP_CLARIFICATION.format(prefix=prefix)) else: safe.sprint('Doing calculation:', arg) scope = await get_scope(message.channel.id) result, worked, details = await scope.execute_async(arg) if result.count('\n') > 7: lines = result.split('\n') num_removed_lines = len(lines) - 8 selected = '\n'.join(lines[:8]).replace( '`', '`\N{zero width non-joiner}') result = '```\n{}\n```\n{} lines were removed.'.format( selected, num_removed_lines) elif result.count('\n') > 0: result = '```\n{}\n```'.format( result.replace('`', '`\N{zero width non-joiner}')) else: for special_char in ('\\', '*', '_', '~~', '`'): result = result.replace(special_char, '\\' + special_char) result = result.replace('@', '@\N{zero width non-joiner}') if result == '' and (message.channel.is_private or message.channel.permissions_for( message.server.me).add_reactions): await self.client.add_reaction(message, '👍') else: if result == '': result = ':thumbsup:' elif len(result) > 2000: result = 'Result was too large to display.' elif worked and len(result) < 1000: if await advertising.should_advertise_to( message.author, message.channel): result += '\nSupport the bot on Patreon: <https://www.patreon.com/dxsmiley>' await self.send_message(message, result) if worked and expression_has_side_effect(arg): await self.add_command_to_history(message.channel, arg)
async def perform_calculation(self, arg, message, should_sort = False): if arg == '': # If no equation was given, spit out the help. if not message.content.startswith('=='): await self.send_message(message.channel, 'Type `=help calc` for information on how to use this command.', blame = message.author) else: safe.sprint('Doing calculation:', arg) if arg.count(':') > 1: await self.send_message(message.channel, 'There are too many `:` characters in that equation.', blame = message.author) else: error = None scope = SCOPES[message.channel.id] # Determine the stack size and time limit depending on whether # the person has the sufficient patreon reward tier limits = {'stack_size': 200, 'warnings': True} time_limit = 10 if patrons.tier(message.author.id) >= patrons.TIER_QUADRATIC: limits['stack_size'] = 500 time_limit = 20 # Actually run the command, and handles the errors try: future = process_command(arg, scope, limits) warnings, values = await asyncio.wait_for(future, timeout = time_limit) except asyncio.TimeoutError: result = 'Calculation took too long' except calculator.EvaluationError as e: # traceback.print_exc() result = 'Error: ' + str(e) if len(result) > 2000: result = 'An error occurred, but it was too large to display.' except calculator.attempt6.ImbalancedBraces as e: result = 'Invalid syntax: Imbalanced braces' except calculator.attempt6.TokenizationFailed as e: result = format_parse_error('Invalid token', arg, e.position) except calculator.attempt6.ParseFailed as e: result = format_parse_error('Invalid syntax', arg, e.position) else: if should_sort: values.sort() if len(values) == 0: result = 'There were no results :thinking:' else: result = '\n'.join(warnings + ['']) + ' '.join(map(format_result, values)) if len(result) > 2000: result = 'Result was too big :(' if len(result) < 1000 and await advertising.should_advertise_to(message.author, message.channel): result += '\nSupport the bot on Patreon: <https://www.patreon.com/dxsmiley>' safe.sprint(result) await self.send_message(message.channel, result, blame = message.author)
async def perform_calculation(self, arg, message, send): async with LOCKS[message.channel.id]: await self.ensure_loaded(message.channel, message.author) # Yeah this is kinda not great... arg = arg.strip('` \n\t') if arg == '': # If no equation was given, spit out the help. if not message.content.startswith('=='): await send( 'Type `=help calc` for information on how to use this command.' ) elif arg == 'help': prefix = await self.bot.settings.get_server_prefix(message) await send(SHORTCUT_HELP_CLARIFICATION.format(prefix=prefix)) else: safe.sprint('Doing calculation:', arg) scope = await get_scope(message.channel.id) result, worked, details = await scope.execute_async(arg) if result.count('\n') > 7: lines = result.split('\n') num_removed_lines = len(lines) - 8 selected = '\n'.join(lines[:8]).replace( '`', '`\N{zero width non-joiner}') result = '```\n{}\n```\n{} lines were removed.'.format( selected, num_removed_lines) elif result.count('\n') > 0: result = '```\n{}\n```'.format( result.replace('`', '`\N{zero width non-joiner}')) else: for special_char in ('\\', '*', '_', '~~', '`'): result = result.replace(special_char, '\\' + special_char) result = result.replace('@', '@\N{zero width non-joiner}') if result == '': await message.add_reaction('👍') else: if result == '': result = ':thumbsup:' elif len(result) > 2000: result = 'Result was too large to display.' await send(result) if worked: await self.bot.advertise_to(message.author, message.channel, message.channel) if expression_has_side_effect(arg): await self.add_command_to_history(message.channel, arg) safe.sprint('Finished calculation:', arg)
def test_sprint_working(capsys): safe.sprint('Hello, world!') captured = capsys.readouterr() assert captured.out == 'Hello, world!\n' safe.sprint('One', end='') safe.sprint('Two') captured = capsys.readouterr() assert captured.out == 'OneTwo\n' safe.sprint('A', 'B', 'C') captured = capsys.readouterr() assert captured.out == 'A B C\n'
async def handle(self, message, latex, template): safe.sprint('Latex ({}, {}) : {}'.format(message.author.name, template, latex)) await self.send_typing(message.channel) colour_back, colour_text = await get_colours(message) tpl = ({'normal': TEMPLATE, 'inline': TEMPLATE_INLINE})[template] latex = tpl.replace( '#COLOUR', colour_text ).replace( '#CONTENT', process_latex(latex) ) sent_message = await self.render_and_reply(message, latex, colour_back, colour_text) if sent_message != None: self.connections[message.id] = { 'message': sent_message, 'template': template }
async def answer_query(self, ctx, query, assumptions=[], small=False, debug=False): safe.sprint('wolfram|alpha :', ctx.author.name, ':', query) channel = ctx.channel author = ctx.author api = get_api(ctx.bot) # Filter out bad words enable_filter = False if not is_private(channel): enable_filter = await ctx.bot.settings.resolve('f-wolf-filter', channel, channel.guild, default='nsfw' not in channel.name) if enable_filter and wordfilter.is_bad(query): await ctx.send(FILTER_FAILURE) return # Perform the query try: async with ctx.typing(): units = await ctx.bot.keystore.get(f'p-wolf-units:{author.id}') result = await api.request(query, assumptions, imperial=(units == 'imperial'), debug=debug) except (wolfapi.WolframError, wolfapi.WolframDidntSucceed): await ctx.send(ERROR_MESSAGE_NO_RESULTS) except asyncio.TimeoutError: print('W|A timeout:', query) await ctx.send(ERROR_MESSAGE_TIMEOUT.format(query)) except aiohttp.ClientError as error: print('Wolf: HTTP processing error:', error.message) await ctx.send('The server threw an error. Try again in a moment.') except xml.parsers.expat.ExpatError as error: print('Wolf: XML processing error:', error) await ctx.send( 'The server returned some malformed data. Try again in a moment.' ) else: if len(result.sections) == 0: await ctx.send(ERROR_MESSAGE_NO_RESULTS) return is_dark = ( await ctx.bot.keystore.get(f'p-tex-colour:{author.id}')) == 'dark' sections_reduced = result.sections if not small else list( cleanup_section_list( itertools.chain( [find_first(section_is_input, result.sections, None)], list(filter(section_is_important, result.sections)) or [ find_first(section_is_not_input, result.sections, None) ]))) # Post images messages = [] for img in process_images(sections_reduced, is_dark): messages.append( await ctx.send(file=image_to_discord_file(img, 'result.png'))) await asyncio.sleep(1.05) # Assuptions are currently disabled because of a 'bug'. # I think it's that discord sends emoji reaction updates # on a different shard to the one that handles the channel # that the message is in, which means that the shard # receiving the notif doesn't know what to do with the info # it receives. embed, show_assuptions = await self.format_adm( ctx, result.assumptions, small) posted = await ctx.send(embed=embed) try: await posted.add_reaction(DELETE_EMOJI) if not small and show_assuptions: await self.add_reaction_emoji(posted, result.assumptions) except discord.errors.NotFound: pass payload = { 'assumptions': result.assumptions.to_json(), 'query': query, 'used': False, 'blame': author.id, 'channel id': posted.channel.id, 'message id': posted.id, 'image ids': [i.id for i in messages], 'no change warning': False } await ctx.bot.keystore.set_json('wolfram', 'message', str(posted.id), payload, expire=60 * 60 * 24) print('Done.')
async def answer_query(self, query, channel, blame, assumptions=[], debug=False): safe.sprint('wolfram|alpha :', blame.name, ':', query) await self.client.send_typing(channel) images = [] text = [] error = 0 error_message = 'No details' # Dummy message. This is a sign that I need to work on the settings module somewhat. class Dummy: def __init__(self, channel): self.channel = channel self.server = channel.server enable_filter = False if not channel.is_private: enable_filter = await core.settings.channel_get_setting( Dummy(channel), 'f-wolf-filter', 'nsfw' not in channel.name) # print(core.get_setting_context(Dummy(channel), 'f-wolf-filter', 'channel')) # print(core.get_setting_context(Dummy(channel), 'f-wolf-filter', 'server')) # print(enable_filter) if enable_filter and wordfilter.is_bad(query): await self.send_message(channel, FILTER_FAILURE, blame=blame) return try: print('Making request') result = await api.request(query, assumptions, debug=debug) print('Done?') except asyncio.TimeoutError: print('W|A timeout:', query) await self.send_message(channel, ERROR_MESSAGE_TIMEOUT.format(query), blame=blame) except aiohttp.ClientError as e: print('Wolf: HTTP processing error:', e.message) await self.send_message( channel, 'The server threw an error. Try again in a moment.', blame=blame) except xml.parsers.expat.ExpatError as e: print('Wolf: XML processing error:', e.message) await self.send_message( channel, 'The server returned some malformed data. Try again in a moment.', blame=blame) else: if len(result.sections) == 0: await self.send_message(channel, ERROR_MESSAGE_NO_RESULTS, blame=blame) elif result.did_fail: m = 'Something went wrong: {}'.format(result.error_text) await self.send_message(channel, m, blame=blame) else: # Get theme setting (TODO: Don't construct this myself) key = 'p-tex-colour:' + blame.id theme = await core.keystore.get(key) is_dark = (theme == 'dark') # print('The theme:', theme) # Send the image results background_colour = hex_to_tuple_a( '36393EFF') if is_dark else hex_to_tuple_a('FFFFFFFF') if not debug: strip = sections_to_image_strip(result.sections) strip = retheme_images( strip, image_recolour_to_dark_theme if is_dark else lambda x: None) for img in conjoin_image_results(strip, background_colour): img = paste_to_background(img, background_colour) # await self.send_image(channel, img, 'result.png', blame = blame) # if theme == 'dark': # image_recolour_to_dark_theme(img) await self.send_image(channel, img, 'result.png', blame=blame) await asyncio.sleep(1.05) # Text section textitems = [] # Assumptions assumption_text = self.get_assumption_text(result.assumptions) hidden_assumptions = assumption_text.count('\n') > 5 if hidden_assumptions: assumption_text = ASSUMPTIONS_MADE_MESSAGE textitems.append(assumption_text) # Tips if len(result.tips) > 0: textitems += ['**Tips**\n', '\n'.join(result.tips), '\n\n'] # Timeouts if len(result.timeouts) > 0: textitems += [ '**Timeouts**\n', ', '.join(result.timeouts), '\n\n' ] textout_joined = ''.join(textitems) url = urllib.parse.urlencode({'i': query}) # Determine if the footer should be long or short adm = FOOTER_MESSAGE.format(mention=blame.mention, query=url) if len(textout_joined) + len(adm) > 1950: adm = FOOTER_MESSAGE_SHORT.format(mention=blame.mention) # Send the result posted = await self.send_message(channel, textout_joined + adm, blame=blame) if result.assumptions.count - result.assumptions.count_unknown > 0: try: if hidden_assumptions: await self.client.add_reaction( posted, EXPAND_EMOJI) else: await self.add_reaction_emoji( posted, result.assumptions) payload = { 'assumptions': result.assumptions.to_json(), 'query': query, 'used': False, 'blame': blame.id, 'channel id': posted.channel.id, 'message id': posted.id, 'no change warning': False, 'hidden': hidden_assumptions } print(json.dumps(payload, indent=4)) # self.sent_footer_messages[str(posted.id)] = payload await core.keystore.set_json('wolfram', 'message', str(posted.id), payload, expire=60 * 60 * 24) print(self.shard_id, 'Footer message id:', posted.id) except discord.errors.Forbidden: await self.send_message(channel, REACTION_PERM_FAILURE, blame=blame) # Complete! print('Done.')
async def answer_query_short(self, query, channel, blame): safe.sprint('wolfram|alpha :', blame.name, ':', query) await self.client.send_typing(channel) images = [] text = [] error = 0 error_message = 'No details' # Dummy message. This is a sign that I need to work on the settings module somewhat. class Dummy: def __init__(self, channel): self.channel = channel self.server = channel.server enable_filter = False if not channel.is_private: enable_filter = await core.settings.channel_get_setting( Dummy(channel), 'f-wolf-filter', 'nsfw' not in channel.name) if enable_filter and wordfilter.is_bad(query): await self.send_message(channel, FILTER_FAILURE, blame=blame) return try: print('Making request') result = await api.request(query, [], debug=False) print('Done?') except asyncio.TimeoutError: print('W|A timeout:', query) await self.send_message(channel, ERROR_MESSAGE_TIMEOUT.format(query), blame=blame) except aiohttp.ClientError as e: print('Wolf: HTTP processing error:', e.message) await self.send_message( channel, 'The server threw an error. Try again in a moment.', blame=blame) except xml.parsers.expat.ExpatError as e: print('Wolf: XML processing error:', e.message) await self.send_message( channel, 'The server returned some malformed data. Try again in a moment.', blame=blame) else: # print(json.dumps(result.sections, indent = 4)) if len(result.sections) == 0: await self.send_message(channel, ERROR_MESSAGE_NO_RESULTS, blame=blame) elif result.did_fail: m = 'Something went wrong: {}'.format(result.error_text) await self.send_message(channel, m, blame=blame) else: # for i in result.sections: # print(' -', i.title) sections_reduced = list( cleanup_section_list( itertools.chain( [ find_first(section_is_input, result.sections, None) ], list(filter(section_is_important, result.sections)) or [ find_first(section_is_not_input, result.sections, None) ]))) is_dark = ((await core.keystore.get('p-tex-colour', blame.id)) == 'dark') # Send the image results background_colour = hex_to_tuple_a( '36393EFF' if is_dark else 'FFFFFFFF') strip = sections_to_image_strip(sections_reduced) if is_dark: strip = retheme_images(strip, image_recolour_to_dark_theme) else: strip = (i for i, _, _ in strip) for img in conjoin_image_results(strip, background_colour): img = paste_to_background(img, background_colour) await self.send_image(channel, img, 'result.png', blame=blame) await asyncio.sleep(1.05) # Text section url = urllib.parse.urlencode({'i': query}) adm = FOOTER_MESSAGE.format(mention=blame.mention, query=url) adm += '\n**This command is in development.** Suggest improvements on the MathBot server (type `=about` for the link).' posted = await self.send_message(channel, adm, blame=blame) print('Done.')
async def answer_query(self, query, channel, blame, assumptions=[], small=False, debug=False): safe.sprint('wolfram|alpha :', blame.name, ':', query) await self.send_typing(channel) enable_filter = False if not channel.is_private: enable_filter = await core.settings.resolve('f-wolf-filter', channel, channel.server, default='nsfw' not in channel.name) if enable_filter and wordfilter.is_bad(query): await self.send_message(channel, FILTER_FAILURE, blame=blame) return try: print('Making request') units = await core.keystore.get('p-wolf-units:' + str(blame.id)) result = await api.request(query, assumptions, imperial=(units == 'imperial'), debug=debug) except (wolfapi.WolframError, wolfapi.WolframDidntSucceed): await self.send_message(channel, ERROR_MESSAGE_NO_RESULTS, blame=blame) except asyncio.TimeoutError: print('W|A timeout:', query) await self.send_message(channel, ERROR_MESSAGE_TIMEOUT.format(query), blame=blame) except aiohttp.ClientError as error: print('Wolf: HTTP processing error:', error.message) await self.send_message( channel, 'The server threw an error. Try again in a moment.', blame=blame) except xml.parsers.expat.ExpatError as error: print('Wolf: XML processing error:', error) await self.send_message( channel, 'The server returned some malformed data. Try again in a moment.', blame=blame) else: if len(result.sections) == 0: await self.send_message(channel, ERROR_MESSAGE_NO_RESULTS, blame=blame) return is_dark = (await core.keystore.get('p-tex-colour:' + blame.id)) == 'dark' sections_reduced = result.sections if not small else list( cleanup_section_list( itertools.chain( [find_first(section_is_input, result.sections, None)], list(filter(section_is_important, result.sections)) or [ find_first(section_is_not_input, result.sections, None) ]))) # Post images for img in process_images(sections_reduced, is_dark): await self.send_image(channel, img, 'result.png', blame=blame) await asyncio.sleep(1.05) embed, show_assuptions = await self.format_adm( channel, blame, query, result.assumptions, small) posted = await self.send_message(channel, embed=embed, blame=blame) if not small and show_assuptions: try: await self.add_reaction_emoji(posted, result.assumptions) payload = { 'assumptions': result.assumptions.to_json(), 'query': query, 'used': False, 'blame': blame.id, 'channel id': posted.channel.id, 'message id': posted.id, 'no change warning': False } await core.keystore.set_json('wolfram', 'message', str(posted.id), payload, expire=60 * 60 * 24) except discord.errors.Forbidden: await self.send_message(channel, REACTION_PERM_FAILURE, blame=blame) print('Done.')
def test_sprint_throwing(): safe.sprint(ThrowOnPrint())
async def answer_query(self, query, channel, blame, assumptions=[], small=False, debug=False): safe.sprint('wolfram|alpha :', blame.name, ':', query) await self.send_typing(channel) enable_filter = False if not channel.is_private: enable_filter = await core.settings.resolve('f-wolf-filter', channel, channel.server, default='nsfw' not in channel.name) if enable_filter and wordfilter.is_bad(query): await self.send_message(channel, FILTER_FAILURE, blame=blame) return try: print('Making request') result = await api.request(query, assumptions, debug=debug) except (wolfapi.WolframError, wolfapi.WolframDidntSucceed): await self.send_message(channel, ERROR_MESSAGE_NO_RESULTS, blame=blame) except asyncio.TimeoutError: print('W|A timeout:', query) await self.send_message(channel, ERROR_MESSAGE_TIMEOUT.format(query), blame=blame) except aiohttp.ClientError as error: print('Wolf: HTTP processing error:', error.message) await self.send_message( channel, 'The server threw an error. Try again in a moment.', blame=blame) except xml.parsers.expat.ExpatError as error: print('Wolf: XML processing error:', error) await self.send_message( channel, 'The server returned some malformed data. Try again in a moment.', blame=blame) else: if len(result.sections) == 0: await self.send_message(channel, ERROR_MESSAGE_NO_RESULTS, blame=blame) return is_dark = (await core.keystore.get('p-tex-colour:' + blame.id)) == 'dark' sections_reduced = result.sections if not small else list( cleanup_section_list( itertools.chain( [find_first(section_is_input, result.sections, None)], list(filter(section_is_important, result.sections)) or [ find_first(section_is_not_input, result.sections, None) ]))) # Post images for img in process_images(sections_reduced, is_dark): await self.send_image(channel, img, 'result.png', blame=blame) await asyncio.sleep(1.05) # Assumptions assumption_text = '' if not small: assumption_text = self.get_assumption_text(result.assumptions) hidden_assumptions = assumption_text.count('\n') > 5 if hidden_assumptions: assumption_text = ASSUMPTIONS_MADE_MESSAGE url = urllib.parse.urlencode({'i': query}) # Determine if the footer should be long or short adm = await self.format_adm(channel, blame, query, small) output = assumption_text + adm too_long = False if len(output) >= 2000: too_long = True output = adm # Send the result posted = await self.send_message(channel, output, blame=blame) if not small and result.assumptions.count_known > 0 and not too_long: try: if hidden_assumptions: await self.client.add_reaction(posted, EXPAND_EMOJI) else: await self.add_reaction_emoji(posted, result.assumptions) payload = { 'assumptions': result.assumptions.to_json(), 'query': query, 'used': False, 'blame': blame.id, 'channel id': posted.channel.id, 'message id': posted.id, 'no change warning': False, 'hidden': hidden_assumptions } # print(json.dumps(payload, indent=4)) # self.sent_footer_messages[str(posted.id)] = payload await core.keystore.set_json('wolfram', 'message', str(posted.id), payload, expire=60 * 60 * 24) # print(self.shard_id, 'Footer message id:', posted.id) except discord.errors.Forbidden: await self.send_message(channel, REACTION_PERM_FAILURE, blame=blame) print('Done.')