This commit is contained in:
2026-04-23 20:36:37 +03:00
parent 4ecc8973bf
commit 840d7d8e3b
19 changed files with 758 additions and 0 deletions

124
internal/rewrite/html.go Normal file
View File

@@ -0,0 +1,124 @@
package rewrite
import (
"bytes"
"compress/flate"
"compress/gzip"
"fmt"
"io"
"log"
"net/http"
"regexp"
"strings"
"git.gulenok.ru/greenhaze/gaslight/internal/cheating"
"git.gulenok.ru/greenhaze/gaslight/internal/latex"
"git.gulenok.ru/greenhaze/gaslight/internal/openrouter"
)
var questionTestDivRe = regexp.MustCompile(`(?is)<div[^>]*\bid\s*=\s*["'][^"']*\bQuestionTest\b[^"']*["'][^>]*>`)
type HTMLHook func(req *http.Request, html []byte) ([]byte, error)
type Processor struct {
hook HTMLHook
}
func NewProcessor(hook HTMLHook) *Processor {
return &Processor{hook: hook}
}
func DefaultQuestionTestHook(_ *http.Request, html []byte) ([]byte, error) {
switch cheating.DetermineQuestionType(string(html)) {
case cheating.FullTextQuestion:
{
aiResponce, err := openrouter.AskOpenRouter(fmt.Sprintf("РЕШИ ЗАДАНИЕ И ДАЙ МАКСИМАЛЬНО КРАТКИЙ ОТВЕТ: %s", string(html)))
if err != nil {
log.Printf("openrouter failed %s", err)
return html, nil
}
return []byte(strings.ReplaceAll(string(html), "generated", latex.ConvertLaTeXToASCII(aiResponce))), nil
}
case cheating.QuestionWithPicture:
{
}
}
return []byte(strings.ReplaceAll(string(html), "generated", "pwned")), nil
}
func IsHTMLResponse(contentType string) bool {
contentType = strings.ToLower(contentType)
return strings.Contains(contentType, "text/html") || strings.Contains(contentType, "application/xhtml+xml")
}
func (p *Processor) RewriteIfNeeded(req *http.Request, contentEncoding string, body []byte) ([]byte, error) {
decoded, err := decodeBody(contentEncoding, body)
if err != nil {
return nil, err
}
if !questionTestDivRe.Match(decoded) {
return body, nil
}
modifiedDecoded, err := p.hook(req, decoded)
if err != nil {
return nil, err
}
return encodeBody(contentEncoding, modifiedDecoded)
}
func decodeBody(encoding string, body []byte) ([]byte, error) {
switch strings.ToLower(strings.TrimSpace(encoding)) {
case "", "identity":
return body, nil
case "gzip":
r, err := gzip.NewReader(bytes.NewReader(body))
if err != nil {
return nil, err
}
defer r.Close()
return io.ReadAll(r)
case "deflate":
r := flate.NewReader(bytes.NewReader(body))
defer r.Close()
return io.ReadAll(r)
default:
return nil, fmt.Errorf("unsupported content-encoding: %s", encoding)
}
}
func encodeBody(encoding string, decoded []byte) ([]byte, error) {
switch strings.ToLower(strings.TrimSpace(encoding)) {
case "", "identity":
return decoded, nil
case "gzip":
var buf bytes.Buffer
w := gzip.NewWriter(&buf)
if _, err := w.Write(decoded); err != nil {
_ = w.Close()
return nil, err
}
if err := w.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
case "deflate":
var buf bytes.Buffer
w, err := flate.NewWriter(&buf, flate.DefaultCompression)
if err != nil {
return nil, err
}
if _, err := w.Write(decoded); err != nil {
_ = w.Close()
return nil, err
}
if err := w.Close(); err != nil {
return nil, err
}
return buf.Bytes(), nil
default:
return nil, fmt.Errorf("unsupported content-encoding: %s", encoding)
}
}