import discord import requests import json import urllib.parse import datetime import subprocess import os import binascii from loguru import logger TOKEN = 'OTQwMDY3MDk5NDI1MDA5NzU1.YgB_9Q.u95iaPy2gix5ZqrQFA_rmyK5Bzw' IS_BOT = False # Set to false if this is a selfbot DO_TEST = False # Set to True if you want to run test ACTIVE_PHRASE = '$rtex' # Change to trigger phrase RAW_ACTIVE_PHRASE = '$rawtex' # Change to trigger phrase for raw mode EQUATION_ACTIVE_PHRASE = ';;' # Change to trigger phrase for an equation WOLFRAM_APPKEY = 'X36K7Y-JT8GXU5698' # Change to your Wolfram|Alpha api key logger.info('initializing', 'bot' if IS_BOT else 'selfbot', 'mode') client = discord.Client() def upload_to_term(content): logger.info('uploading content to pastebin') try: c = subprocess.run(['nc', 'termbin.com', '9999'], capture_output=True, check=True, text=True, input=content) except subprocess.CalledProcessError as e: logger.error('failed to upload to pastebin: ' + e.content) return None return c.stdout @client.event async def on_ready(): logger.info('ready') await client.change_presence(activity=discord.Game('with math | v1.24.1')) tex_wrapper_start = '\\documentclass{article}\n\\usepackage{xcolor}\n\\usepackage{amsmath}\n\\begin{document}\n\\color{gray}\n' tex_wrapper_end = '\n\\pagenumbering{gobble}\n\\end{document}' def wrap_tex(tex): return tex_wrapper_start + tex + tex_wrapper_end RTEX = 'https://rtex.probablyaweb.site/' def render_tex(tex): logger.info('accepted job') logger.info('wrapping tex') tex = wrap_tex(tex) logger.info('building request to rtex api') payload = { "code": tex, "format": "png" } logger.info('build payload', payload) logger.info(f'contacting api ({RTEX})') logger.info('waiting for response') response = requests.post(RTEX + 'api/v2', data=payload) response.raise_for_status() logger.info('decoding response') data = response.json() c = None if data['status'] != 'success': logger.error('job failed') c = upload_to_term(data['log']) return (False, c) logger.info('fetching file') uri = RTEX + 'api/v2/' + data['filename'] logger.info(f'render job done {uri}') return (True, uri) async def wolfram_get_plot_implicit(equ, qurl): url = f'https://api.wolframalpha.com/v2/query?appid={WOLFRAM_APPKEY}&input={equ}&format=image&output=json&includepodid=ImplicitPlot' logger.info('querying for implicit plot') response = requests.get(url) response.raise_for_status() logger.info('decoding response') data = response.json() if data['queryresult']['success'] != True: logger.error('query failed:', data['queryresult']['error']) return (False, 'Sorry, but Wolfram|Alpha returned an error. Try checking on wolframalpha.com: ' + qurl) if data['queryresult']['numpods'] > 1: logger.error('unexpectected result, more than 1 pod') return (False, 'Sorry, but Wolfram|Alpha returned too much data for the bot to process. Try checking on wolframalpha.com: ' + qurl) if data['queryresult']['numpods'] < 1: logger.warning('no graph') return (False, 'Sorry, but I couldn\'t find any plots for that equation.') plotpod = data['queryresult']['pods'][0] if plotpod['id'] != 'ImplicitPlot': logger.error('unexpected result, wrong pod id') return (False, 'Sorry, but Wolfram|Alpha returned an unexpected response. Try checking on wolframalpha.com: ' + qurl) plotimg = plotpod['subpods'][0]['img']['src'] return (True, plotimg) async def wolfram_get_plot(equ, qurl): url = f'https://api.wolframalpha.com/v2/query?appid={WOLFRAM_APPKEY}&input={equ}&format=image&output=json&includepodid=Plot' logger.info('querying for plot') response = requests.get(url) response.raise_for_status() logger.info('decoding response') data = response.json() if data['queryresult']['success'] != True: logger.error('query failed:', data['queryresult']['error']) return (False, 'Sorry, but Wolfram|Alpha returned an error. Try checking on wolframalpha.com: ' + qurl) if data['queryresult']['numpods'] > 1: logger.error('unexpectected result, more than 1 pod') return (False, 'Sorry, but Wolfram|Alpha returned too much data for the bot to process. Try checking on wolframalpha.com: ' + qurl) if data['queryresult']['numpods'] < 1: logger.warning('no graph') return (False, 'Sorry, but I couldn\'t find any plots for that equation.') plotpod = data['queryresult']['pods'][0] if plotpod['id'] != 'Plot': logger.error('unexpected result, wrong pod id') return (False, 'Sorry, but Wolfram|Alpha returned an unexpected response. Try checking on wolframalpha.com: ' + qurl) plotimg = plotpod['subpods'][0]['img']['src'] return (True, plotimg) async def wolfram_get_plot_3d(equ, qurl): url = f'https://api.wolframalpha.com/v2/query?appid={WOLFRAM_APPKEY}&input={equ}&format=image&output=json&includepodid=3DPlot' logger.info('querying for plot') response = requests.get(url) response.raise_for_status() logger.info('decoding response') data = response.json() if data['queryresult']['success'] != True: logger.error('query failed:', data['queryresult']['error']) return (False, 'Sorry, but Wolfram|Alpha returned an error. Try checking on wolframalpha.com: ' + qurl) if data['queryresult']['numpods'] > 1: logger.error('unexpectected result, more than 1 pod') return (False, 'Sorry, but Wolfram|Alpha returned too much data for the bot to process. Try checking on wolframalpha.com: ' + qurl) if data['queryresult']['numpods'] < 1: logger.warning('no graph') return (False, 'Sorry, but I couldn\'t find any plots for that equation.') plotpod = data['queryresult']['pods'][0] if plotpod['id'] != '3DPlot': logger.error('unexpected result, wrong pod id') return (False, 'Sorry, but Wolfram|Alpha returned an unexpected response. Try checking on wolframalpha.com: ' + qurl) plotimg = plotpod['subpods'][0]['img']['src'] return (True, plotimg) async def wolfram_graph3d(equation, ctx): logger.info('job accepted, equ: ' + equation) urlencoded = urllib.parse.quote(equation, safe='') logger.debug('urlencoded is ' + urlencoded) qurl = 'https://wolframalpha.com/input?i=' + urlencoded logger.debug('qurl is ' + qurl) logger.debug('done, creating embed') success, msg = await wolfram_get_plot_3d(urlencoded, qurl) if not success: logger.error('failed') await ctx.reply('Sorry, but something went wrong while rendering your equation:\n```' + msg + '\n```') return else: await ctx.reply(msg) return async def wolfram_graph(equation, ctx): logger.info('job accepted, equ: ' + equation) urlencoded = urllib.parse.quote(equation, safe='') logger.debug('urlencoded is ' + urlencoded) qurl = 'https://wolframalpha.com/input?i=' + urlencoded logger.debug('qurl is ' + qurl) logger.debug('done, creating embed') success, msg = await wolfram_get_plot(urlencoded, qurl) if not success: logger.info('try 1 failed, attempting implicit plotting') success, msgi = await wolfram_get_plot_implicit(urlencoded, qurl) if not success: logger.error('failed') await ctx.reply('Sorry, but something went wrong while rendering your equation:\n```' + msg + '\n' + msgi + '\n```') return logger.info('implicit plot found') await ctx.reply(msgi) return else: await ctx.reply(msg) return # selfbots cant use embeds :( #imgurl = plotimg #queryurl = 'https://wolframalpha.com/input?i=' + urlencoded #embed = discord.Embed( # title='Graph: ' + equation, # url=queryurl, # timestamp=datetime.datetime.now(), # footer='Made with <3 by c0repwn3r', # description='Here\'s your plot!' #) #embed.set_author(name='Wolfram|Alpha', icon_url='https://wolframalpha.com/favicon.ico') #embed.set_image(url=imgurl) #await ctx.channel.send(imgurl) #return def test_render_engine(): logger.info('testing rendering engine') if render_tex('lbtest')[0] == False: logger.warning('render test 1 failed, attempting render 2') if render_tex('ftest')[0] == False: logger.error('render test failed, exiting') exit(-1) logger.info('render test successful') if DO_TEST: test_render_engine() else: logger.info('skipping rendering test') logger.info('creating msg event handler') @client.event async def on_message(message): if message.author == client.user: logger.debug('ignoring my own message') return if message.content in ['.authtest', '.help', '.embed', '.dumpconfig', '.writeconfig'] or message.content.startswith('.graphequ3d') or message.content.startswith('.writeconfig') or ACTIVE_PHRASE in message.content or RAW_ACTIVE_PHRASE in message.content or len(message.content.split(EQUATION_ACTIVE_PHRASE)) > 2 or message.content.startswith('.graphequ'): async with message.channel.typing(): await process_message(message) async def process_message(message): global config logger.info('processing message') if message.author == client.user: logger.debug('ignoring my own message') return if message.content == '.dumpconfig': await message.reply(json.dumps(config)) return elif message.content == '.authtest': await message.reply(f'Hi there! Unfortunately, I\'m not authorized to issue authentication codes for you. Check with core if you should be able to get one. Sorry :/') # await message.reply(f'Hi! Your auth code is `{binascii.b2a_hex(os.urandom(3))[2:-1]}`. It will be valid for 60 seconds.\n**Nobody should be asking you for this code. Ensure that your browser shows the URL https://hotel.security.internalwg.e3t.cc/discord_3fa, otherwise you might compromise access to the VPN.**') return elif message.content.startswith('.writeconfig'): newconfig = ' '.join(message.content.split(' ')[1:]) config = newconfig write_config() await message.reply('Updated config.') return elif message.content.startswith('.graphequ3d'): equation = message.content[12:] logger.info('job accepted (equation', equation, 'TBR by Wolfram in 3d)') await wolfram_graph3d(equation, message) return elif message.content.startswith('.graphequ'): equation = message.content[10:] logger.info('job accepted (equation', equation, 'TBR by Wolfram)') await wolfram_graph(equation, message) return elif message.content == '.embed': embed=discord.Embed(title="Test Embed", url="https://coredoes.dev", description="This is an embed to figure out what discord.py is doing.") await message.channel.send(embed=embed) return elif message.content == '.help': await message.channel.send(":wave: **Hi, I'm c0remath3r**\nI'm a math bot with several functions, but primarily rendering LaTeX expressions and querying Wolfram|Alpha right within discord. Here's how it works!\r**To render an entire message as a LaTeX document^**: place \$rtex *somewhere* in your message. It does not matter where.\nAn easier way to do this, however, is to simply **surround the equation you want to render with \;\;^** which will render it as an equation, and the rest of the message as text.\n*^: some preprocessing is applied.*\n\n**To graph an equation:** simply write `.graphequ `. This will query Wolfram|Alpha to fetch a (implicit, if nessecary) plot and will send it in the channel.\n**For 3-D graphs:** simply use `.graphequ3d ` instead.\n\nHave fun!") return content = '' if RAW_ACTIVE_PHRASE in message.content: logger.info('found raw trigger phrase, processing') logger.debug('preprocessing: removing trigger phrase') content = message.content.replace(RAW_ACTIVE_PHRASE, '') logger.info('rendering') url = render_tex(content) if url[0]: logger.error('rendering failed, notifying user') await message.reply('Sorry, but I failed to render your message. Please try again later or contact core.\nYou can also check the render log: ' + url[1]) else: logger.info('rendered, replying') await message.reply(url[1]) if len(message.content.split(EQUATION_ACTIVE_PHRASE)) >= 3: logger.info('found equation message, processing') logger.debug('preprocessing: modifying equation phrases') content = message.content.replace(EQUATION_ACTIVE_PHRASE, '$') logger.info('rendering') url = render_tex(content) if url[0] == False: logger.error('rendering failed, notifying user') await message.reply('Sorry, but I failed to render your message. Please try again later or contact core.\nYou can also check the render log: ' + url[1]) else: logger.info('rendered, replying') await message.reply(url[1]) if ACTIVE_PHRASE in message.content: logger.info('found trigger phrase, processing') logger.debug('preprocessing (trigger phrase removal)') content = message.content.replace(ACTIVE_PHRASE, '') logger.info('rendering') url = render_tex(content) if url[0] == False: logger.error('rendering failed, notifying user') await message.reply('Sorry, but I failed to render your message. Please try again later or contact core.\nYou can also check the render log: ' + url[1]) else: logger.info('rendered, replying') await message.reply(url[1]) logger.info('ready to initialize, logging in') client.run(TOKEN)