🌟tex
This commit is contained in:
parent
a1ed7f8dd1
commit
e0b9f35a76
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -4,7 +4,7 @@
|
|||||||
"python.testing.pytestEnabled": false,
|
"python.testing.pytestEnabled": false,
|
||||||
"python.testing.unittestEnabled": true,
|
"python.testing.unittestEnabled": true,
|
||||||
"search.exclude": {
|
"search.exclude": {
|
||||||
"**/venv": true
|
"**/.venv": true
|
||||||
},
|
},
|
||||||
"isort.args": ["--profile", "black"],
|
"isort.args": ["--profile", "black"],
|
||||||
"black-formatter.args": ["--line-length", "120"]
|
"black-formatter.args": ["--line-length", "120"]
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
FROM python:3.11 as base
|
FROM python:3.11 as base
|
||||||
WORKDIR /app/
|
WORKDIR /app/
|
||||||
|
RUN apt-get update && apt-get install -y texlive
|
||||||
|
RUN apt-get update && apt-get install -y texlive-latex-extra
|
||||||
|
RUN apt-get update && apt-get install -y dvipng
|
||||||
COPY requirements.txt requirements.txt
|
COPY requirements.txt requirements.txt
|
||||||
RUN pip --no-cache-dir install -r requirements.txt
|
RUN pip --no-cache-dir install -r requirements.txt
|
||||||
CMD ["python3", "-m", "nightly"]
|
CMD ["python3", "-m", "nightly"]
|
||||||
|
@ -1,10 +1,60 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
import re
|
import re
|
||||||
|
from io import BytesIO
|
||||||
|
from textwrap import dedent
|
||||||
|
from typing import Annotated
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
|
import sympy
|
||||||
from aiohttp import ClientSession
|
from aiohttp import ClientSession
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
# https://github.com/python-discord/bot-core/blob/main/pydis_core/utils/regex.py#L29
|
||||||
|
FORMATTED_CODE_REGEX = re.compile(
|
||||||
|
r"(?P<delim>(?P<block>```)|``?)"
|
||||||
|
r"(?(block)(?:(?P<lang>[a-z]+)\n)?)"
|
||||||
|
r"(?:[ \t]*\n)*"
|
||||||
|
r"(?P<code>.*?)"
|
||||||
|
r"\s*"
|
||||||
|
r"(?P=delim)",
|
||||||
|
flags=re.DOTALL | re.IGNORECASE,
|
||||||
|
)
|
||||||
|
|
||||||
|
# https://github.com/python-discord/bot-core/blob/main/pydis_core/utils/regex.py#L44C1-L49C2
|
||||||
|
RAW_CODE_REGEX = re.compile(r"^(?:[ \t]*\n)*" r"(?P<code>.*?)" r"\s*$", flags=re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
|
# https://github.com/python-discord/bot/blob/main/bot/exts/utils/snekbox/_cog.py#L97
|
||||||
|
class CodeblockConverter(commands.Converter):
|
||||||
|
@classmethod
|
||||||
|
async def convert(cls, ctx: commands.Context, code: str) -> list[str]:
|
||||||
|
if match := list(FORMATTED_CODE_REGEX.finditer(code)):
|
||||||
|
blocks = [block for block in match if block.group("block")]
|
||||||
|
|
||||||
|
if len(blocks) > 1:
|
||||||
|
codeblocks = [block.group("code") for block in blocks]
|
||||||
|
info = "several code blocks"
|
||||||
|
else:
|
||||||
|
match = match[0] if len(blocks) == 0 else blocks[0]
|
||||||
|
code, block, lang, delim = match.group("code", "block", "lang", "delim")
|
||||||
|
codeblocks = [dedent(code)]
|
||||||
|
if block:
|
||||||
|
info = (f"'{lang}' highlighted" if lang else "plain") + " code block"
|
||||||
|
else:
|
||||||
|
info = f"{delim}-enclosed inline code"
|
||||||
|
else:
|
||||||
|
m = RAW_CODE_REGEX.fullmatch(code)
|
||||||
|
if m is None:
|
||||||
|
raise RuntimeError("what")
|
||||||
|
codeblocks = [dedent(m.group("code"))]
|
||||||
|
info = "unformatted or badly formatted code"
|
||||||
|
|
||||||
|
code = "\n".join(codeblocks)
|
||||||
|
print(f"Extracted {info} for evaluation:\n{code}")
|
||||||
|
return codeblocks
|
||||||
|
|
||||||
|
|
||||||
class Night(commands.Cog):
|
class Night(commands.Cog):
|
||||||
@ -63,6 +113,31 @@ class Night(commands.Cog):
|
|||||||
return
|
return
|
||||||
await message.pin()
|
await message.pin()
|
||||||
|
|
||||||
|
@commands.hybrid_command()
|
||||||
|
async def tex(self, ctx: commands.Context, *, code: Annotated[list[str], CodeblockConverter]):
|
||||||
|
print(code)
|
||||||
|
buf = BytesIO()
|
||||||
|
|
||||||
|
def preview():
|
||||||
|
sympy.preview(
|
||||||
|
code[0],
|
||||||
|
viewer="BytesIO",
|
||||||
|
euler=False,
|
||||||
|
outputbuffer=buf,
|
||||||
|
dvioptions=["-fg", "White", "-bg", "Black"],
|
||||||
|
)
|
||||||
|
|
||||||
|
await asyncio.to_thread(preview)
|
||||||
|
buf.seek(0)
|
||||||
|
im0 = Image.open(buf)
|
||||||
|
print(im0.size)
|
||||||
|
im1 = Image.new(im0.mode, (im0.width + 40, im0.height + 40))
|
||||||
|
im1.paste(im0, (20, 20))
|
||||||
|
buf = BytesIO()
|
||||||
|
im1.save(buf, format="png")
|
||||||
|
buf.seek(0)
|
||||||
|
await ctx.reply(file=discord.File(buf, filename="tex.png"))
|
||||||
|
|
||||||
|
|
||||||
async def setup(bot: commands.Bot):
|
async def setup(bot: commands.Bot):
|
||||||
global cog
|
global cog
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
aiohttp>=3.7.4,<4
|
aiohttp>=3.7.4,<4
|
||||||
discord.py~=2.3.2
|
discord.py~=2.3.2
|
||||||
|
pillow~=10.2.0
|
||||||
|
sympy~=1.12
|
||||||
|
Loading…
Reference in New Issue
Block a user