mirror of
https://github.com/migatu/conjurer.git
synced 2026-07-14 21:38:38 +00:00
upgrade
This commit is contained in:
+81
-27
@@ -3,11 +3,10 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import logging
|
||||||
import re
|
import re
|
||||||
import shlex
|
|
||||||
import tempfile
|
import tempfile
|
||||||
import zipfile
|
import zipfile
|
||||||
import logging
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Optional, Tuple
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
@@ -29,25 +28,67 @@ def is_tex_attachment(att, max_mb: int) -> bool:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def run_latex_two_passes(
|
def _build_cmd(engine: str, tex_filename: str) -> list[str]:
|
||||||
tex_filename: str, workdir: Path, tex_engine: str, timeout_sec: int, logger:str=None
|
base = (engine or "").strip().lower()
|
||||||
) -> Tuple[bool, str, Path]:
|
if base in ("pdflatex", "xelatex", "lualatex"):
|
||||||
cmd = f"{tex_engine} -interaction=nonstopmode -halt-on-error -file-line-error -no-shell-escape {shlex.quote(tex_filename)}"
|
return [
|
||||||
logger = logging.getLogger(logger) if logger else None
|
base,
|
||||||
if logger:
|
"-interaction=nonstopmode",
|
||||||
logger.debug("LaTeX cmd: %s (cwd=%s)", cmd, workdir)
|
"-halt-on-error",
|
||||||
|
"-file-line-error",
|
||||||
|
"-no-shell-escape",
|
||||||
|
tex_filename,
|
||||||
|
]
|
||||||
|
if base == "latexmk":
|
||||||
|
return [
|
||||||
|
"latexmk",
|
||||||
|
"-pdf",
|
||||||
|
"-interaction=nonstopmode",
|
||||||
|
"-halt-on-error",
|
||||||
|
"-file-line-error",
|
||||||
|
tex_filename,
|
||||||
|
]
|
||||||
|
if base == "tectonic":
|
||||||
|
return [
|
||||||
|
"tectonic",
|
||||||
|
"-X",
|
||||||
|
"compile",
|
||||||
|
"--keep-logs",
|
||||||
|
"--outdir",
|
||||||
|
".",
|
||||||
|
tex_filename,
|
||||||
|
]
|
||||||
|
return [
|
||||||
|
"pdflatex",
|
||||||
|
"-interaction=nonstopmode",
|
||||||
|
"-halt-on-error",
|
||||||
|
"-file-line-error",
|
||||||
|
"-no-shell-escape",
|
||||||
|
tex_filename,
|
||||||
|
]
|
||||||
|
|
||||||
async def _run_once() -> Tuple[int, str]:
|
|
||||||
proc = await asyncio.create_subprocess_shell(
|
async def run_latex_two_passes(
|
||||||
cmd,
|
tex_filename: str,
|
||||||
|
workdir: Path,
|
||||||
|
tex_engine: str,
|
||||||
|
timeout_sec: int,
|
||||||
|
logger: str = None,
|
||||||
|
):
|
||||||
|
logger = logging.getLogger(logger) if logger else None
|
||||||
|
|
||||||
|
async def run_once(cmd: list[str]) -> tuple[int, str]:
|
||||||
|
proc = await asyncio.create_subprocess_exec(
|
||||||
|
*cmd,
|
||||||
cwd=str(workdir),
|
cwd=str(workdir),
|
||||||
stdout=asyncio.subprocess.PIPE,
|
stdout=asyncio.subprocess.PIPE,
|
||||||
stderr=asyncio.subprocess.STDOUT,
|
stderr=asyncio.subprocess.STDOUT,
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
out_b, _ = await asyncio.wait_for(proc.communicate(), timeout=timeout_sec)
|
out_b, _ = await asyncio.wait_for(proc.communicate(), timeout=timeout_sec)
|
||||||
out_t = (out_b or b"").decode("utf-8", errors="replace")
|
return (proc.returncode or 0), (out_b or b"").decode(
|
||||||
return proc.returncode or 0, out_t
|
"utf-8", errors="replace"
|
||||||
|
)
|
||||||
except asyncio.TimeoutError:
|
except asyncio.TimeoutError:
|
||||||
try:
|
try:
|
||||||
proc.kill()
|
proc.kill()
|
||||||
@@ -55,27 +96,39 @@ async def run_latex_two_passes(
|
|||||||
raise
|
raise
|
||||||
return 124, f"[Timeout] Compilation exceeded {timeout_sec}s.\n"
|
return 124, f"[Timeout] Compilation exceeded {timeout_sec}s.\n"
|
||||||
|
|
||||||
logs: List[str] = []
|
logs = []
|
||||||
rc1, out1 = await _run_once()
|
cmd = _build_cmd(tex_engine, tex_filename)
|
||||||
|
if logger:
|
||||||
|
logger.debug("LaTeX cmd: %s (cwd=%s)", " ".join(cmd), workdir)
|
||||||
|
|
||||||
|
rc1, out1 = await run_once(cmd)
|
||||||
logs.append(out1)
|
logs.append(out1)
|
||||||
|
second_pass = not ((tex_engine or "").strip().lower() == "tectonic")
|
||||||
|
|
||||||
if rc1 != 0:
|
if rc1 != 0:
|
||||||
log_text = "".join(logs)
|
log_text = "".join(logs)
|
||||||
if logger:
|
pdf = workdir / (Path(tex_filename).stem + ".pdf")
|
||||||
logger.debug("LaTeX pass1 FAIL %s\n%s", tex_filename, log_text[-2000:])
|
return False, log_text, pdf
|
||||||
return False, log_text, workdir / (Path(tex_filename).stem + ".pdf")
|
|
||||||
|
if second_pass:
|
||||||
|
rc2, out2 = await run_once(cmd)
|
||||||
|
logs.append(out2)
|
||||||
|
ok = rc2 == 0
|
||||||
|
else:
|
||||||
|
ok = rc1 == 0
|
||||||
|
|
||||||
rc2, out2 = await _run_once()
|
|
||||||
logs.append(out2)
|
|
||||||
log_text = "".join(logs)
|
log_text = "".join(logs)
|
||||||
pdf = workdir / (Path(tex_filename).stem + ".pdf")
|
pdf = workdir / (Path(tex_filename).stem + ".pdf")
|
||||||
ok = (rc2 == 0) and pdf.exists()
|
ok = ok and pdf.exists()
|
||||||
if logger:
|
|
||||||
logger.debug("LaTeX pass2 %s ok=%s\n%s", tex_filename, ok, log_text[-2000:])
|
|
||||||
return ok, log_text, pdf
|
return ok, log_text, pdf
|
||||||
|
|
||||||
|
|
||||||
async def compile_single_tex_bytes(
|
async def compile_single_tex_bytes(
|
||||||
tex_bytes: bytes, filename: str, tex_engine: str, timeout_sec: int, logger:str=None
|
tex_bytes: bytes,
|
||||||
|
filename: str,
|
||||||
|
tex_engine: str,
|
||||||
|
timeout_sec: int,
|
||||||
|
logger: str = None,
|
||||||
) -> Dict[str, Optional[object]]:
|
) -> Dict[str, Optional[object]]:
|
||||||
if not is_safe_tex_name(filename):
|
if not is_safe_tex_name(filename):
|
||||||
return {
|
return {
|
||||||
@@ -107,7 +160,7 @@ async def compile_single_tex_bytes(
|
|||||||
|
|
||||||
|
|
||||||
async def compile_zip_to_zip(
|
async def compile_zip_to_zip(
|
||||||
zip_bytes: bytes, tex_engine: str, timeout_sec: int, logger:str=None
|
zip_bytes: bytes, tex_engine: str, timeout_sec: int, logger: str = None
|
||||||
) -> Tuple[bytes, List[str]]:
|
) -> Tuple[bytes, List[str]]:
|
||||||
failed: List[str] = []
|
failed: List[str] = []
|
||||||
out_pdf_paths: List[Path] = []
|
out_pdf_paths: List[Path] = []
|
||||||
@@ -143,6 +196,7 @@ async def compile_zip_to_zip(
|
|||||||
failed.append(f"{tex.name} (exception: {e})")
|
failed.append(f"{tex.name} (exception: {e})")
|
||||||
if logger:
|
if logger:
|
||||||
logger.debug("BATCH EXC %s: %s", tex.name, e)
|
logger.debug("BATCH EXC %s: %s", tex.name, e)
|
||||||
|
raise
|
||||||
|
|
||||||
out_zip = td_path / "compiled_pdfs.zip"
|
out_zip = td_path / "compiled_pdfs.zip"
|
||||||
with zipfile.ZipFile(out_zip, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
with zipfile.ZipFile(out_zip, "w", compression=zipfile.ZIP_DEFLATED) as zf:
|
||||||
@@ -153,7 +207,7 @@ async def compile_zip_to_zip(
|
|||||||
|
|
||||||
|
|
||||||
async def ask_openai_diagnosis(
|
async def ask_openai_diagnosis(
|
||||||
log_text: str, tex_excerpt: str, model: str, logger:str = None
|
log_text: str, tex_excerpt: str, model: str, logger: str = None
|
||||||
) -> str:
|
) -> str:
|
||||||
logger = logging.getLogger(logger) if logger else None
|
logger = logging.getLogger(logger) if logger else None
|
||||||
sys = (
|
sys = (
|
||||||
|
|||||||
+28
-14
@@ -8,6 +8,7 @@ from queue import Empty
|
|||||||
|
|
||||||
import discord
|
import discord
|
||||||
import pdf2image
|
import pdf2image
|
||||||
|
import fitz
|
||||||
import PyPDF2
|
import PyPDF2
|
||||||
import requests
|
import requests
|
||||||
from discord.ext import commands, tasks
|
from discord.ext import commands, tasks
|
||||||
@@ -53,20 +54,33 @@ class DataModule(commands.Cog):
|
|||||||
filename = res[random.randrange(0, len(res) - 1)]
|
filename = res[random.randrange(0, len(res) - 1)]
|
||||||
# select random page
|
# select random page
|
||||||
file = open(DIR_PATH_SADOX + filename, "rb")
|
file = open(DIR_PATH_SADOX + filename, "rb")
|
||||||
readpdf = PyPDF2.PdfReader(file)
|
if True:
|
||||||
totalpages = len(readpdf.pages)
|
doc = fitz.open(DIR_PATH_SADOX + filename)
|
||||||
# trunk-ignore(bandit/B311)
|
totalpages = len(doc)
|
||||||
page = random.randrange(1, totalpages)
|
page_index = random.randrange(0, totalpages)
|
||||||
# convert page to image
|
page = doc.load_page(page_index)
|
||||||
image = pdf2image.convert_from_path(
|
mat = fitz.Matrix(2.0, 2.0) # powiększenie
|
||||||
DIR_PATH_SADOX + filename, first_page=page, last_page=page
|
pix = page.get_pixmap(matrix=mat, alpha=False)
|
||||||
)
|
|
||||||
byte_io_stream = io.BytesIO()
|
byte_io_stream = io.BytesIO(pix.tobytes("png"))
|
||||||
image[0].save(byte_io_stream, "JPEG")
|
byte_io_stream.seek(0)
|
||||||
byte_io_stream.seek(0)
|
byte_io_stream.name = "image.png"
|
||||||
byte_io_stream.name = "image.jpg"
|
await ctx.send(file=discord.File(byte_io_stream))
|
||||||
file = discord.File(byte_io_stream)
|
else: #legacy
|
||||||
await ctx.send(file=file)
|
readpdf = PyPDF2.PdfReader(file)
|
||||||
|
totalpages = len(readpdf.pages)
|
||||||
|
# trunk-ignore(bandit/B311)
|
||||||
|
page = random.randrange(1, totalpages)
|
||||||
|
# convert page to image
|
||||||
|
image = pdf2image.convert_from_path(
|
||||||
|
DIR_PATH_SADOX + filename, first_page=page, last_page=page
|
||||||
|
)
|
||||||
|
byte_io_stream = io.BytesIO()
|
||||||
|
image[0].save(byte_io_stream, "JPEG")
|
||||||
|
byte_io_stream.seek(0)
|
||||||
|
byte_io_stream.name = "image.jpg"
|
||||||
|
file = discord.File(byte_io_stream)
|
||||||
|
await ctx.send(file=file)
|
||||||
self.logger.info("Get sadox completed")
|
self.logger.info("Get sadox completed")
|
||||||
|
|
||||||
@tasks.loop(seconds=3)
|
@tasks.loop(seconds=3)
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ spotipy
|
|||||||
tiktoken
|
tiktoken
|
||||||
PyNaCl
|
PyNaCl
|
||||||
flask[async]
|
flask[async]
|
||||||
|
fitz
|
||||||
waitress
|
waitress
|
||||||
clickupython
|
clickupython
|
||||||
assemblyai[extras]
|
assemblyai[extras]
|
||||||
|
|||||||
Reference in New Issue
Block a user