125 lines
3.1 KiB
Go
125 lines
3.1 KiB
Go
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)
|
||
}
|
||
}
|