示例#1
0
文件: calcmod.py 项目: mxbi/mathbot
 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)
示例#2
0
	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)
示例#3
0
 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)
示例#4
0
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'
示例#5
0
	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
			}
示例#6
0
    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.')
示例#7
0
    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.')
示例#8
0
    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.')
示例#9
0
文件: wolfram.py 项目: mxbi/mathbot
    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.')
示例#10
0
def test_sprint_throwing():
    safe.sprint(ThrowOnPrint())
示例#11
0
    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.')