Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -655,6 +655,8 @@ print("run[CQ:image,file="+j["img"]+"]")
- [x] 齁语解密 [密文] 或 h解密 [密文]
- [x] fumo加密 [文本]
- [x] fumo解密 [文本]
- [x] qq加密 [文本]
- [x] qq解密 [密文]

</details>
<details>
Expand Down Expand Up @@ -768,7 +770,7 @@ print("run[CQ:image,file="+j["img"]+"]")

- [x] 运势 | 抽签

- [x] 设置底图[车万 DC4 爱因斯坦 星空列车 樱云之恋 富婆妹 李清歌 公主连结 原神 明日方舟 碧蓝航线 碧蓝幻想 战双 阴阳师 赛马娘 东方归言录 奇异恩典 夏日口袋 ASoul Hololive]
- [x] 设置底图[车万 车万2 DC4 爱因斯坦 星空列车 樱云之恋 富婆妹 李清歌 公主连结 原神 明日方舟 碧蓝航线 碧蓝幻想 战双 阴阳师 赛马娘 东方归言录 奇异恩典 夏日口袋 ASoul Hololive]

</details>
<details>
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ require (
github.com/PuerkitoBio/goquery v1.8.0 // indirect
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect
github.com/antchfx/xpath v1.3.5 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
Expand All @@ -76,6 +77,7 @@ require (
github.com/jfreymuth/vorbis v1.0.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.18.4 // indirect
github.com/liuzl/cedar-go v0.0.0-20170805034717-80a9c64b256d // indirect
github.com/liuzl/da v0.0.0-20180704015230-14771aad5b1d // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d h1:ir/IFJU5xbja5Ua
github.com/adamzy/cedar-go v0.0.0-20170805034717-80a9c64b256d/go.mod h1:PRWNwWq0yifz6XDPZu48aSld8BWwBfr2JKB2bGWiEd4=
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca h1:kWzLcty5V2rzOqJM7Tp/MfSX0RMSI1x4IOLApEefYxA=
github.com/ajstarks/svgo v0.0.0-20200320125537-f189e35d30ca/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
Expand Down Expand Up @@ -135,6 +137,8 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5 h1:BXnB1Gz4y/zwQh+ZFNy7rgd+ZfMOrwRr4uZSHEI+ieY=
github.com/kanrichan/resvg-go v0.0.2-0.20231001163256-63db194ca9f5/go.mod h1:c9+VS9GaommgIOzNWb5ze4lYwfT8BZ2UDyGiuQTT7yc=
github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
Expand Down
143 changes: 131 additions & 12 deletions plugin/crypter/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,158 @@
package crypter

import (
"fmt"
"regexp"
"strconv"
"strings"

"github.com/FloatTech/AnimeAPI/airecord"
zero "github.com/wdvxdr1123/ZeroBot"
"github.com/wdvxdr1123/ZeroBot/message"
)

var faceTagRe = regexp.MustCompile(`\{\{face:(\d+)\}\}`)

func parseID(v interface{}) int64 {
n, _ := strconv.ParseInt(fmt.Sprintf("%v", v), 10, 64)
return n
}

func serializeMsg(segs message.Message) string {
var sb strings.Builder
for _, seg := range segs {
switch seg.Type {
case "text":
sb.WriteString(fmt.Sprintf("%v", seg.Data["text"]))
case "face":
fmt.Fprintf(&sb, "{{face:%v}}", seg.Data["id"])
}
}
return sb.String()
}

func deserializeMsg(s string) message.Message {
var msg message.Message
last := 0
for _, loc := range faceTagRe.FindAllStringSubmatchIndex(s, -1) {
if loc[0] > last {
msg = append(msg, message.Text(s[last:loc[0]]))
}
id, _ := strconv.Atoi(s[loc[2]:loc[3]])
msg = append(msg, message.Face(id))
last = loc[1]
}
if last < len(s) {
msg = append(msg, message.Text(s[last:]))
}
return msg
}

func getInput(ctx *zero.Ctx, cmds ...string) string {
full := serializeMsg(ctx.Event.Message)
for _, cmd := range cmds {
if idx := strings.Index(full, cmd); idx >= 0 {
return strings.TrimSpace(full[idx+len(cmd):])
}
}
return ""
}

func getReplyContent(ctx *zero.Ctx) string {
for _, seg := range ctx.Event.Message {
if seg.Type == "reply" {
if msgID := parseID(seg.Data["id"]); msgID > 0 {
if msg := ctx.GetMessage(msgID); msg.Elements != nil {
return serializeMsg(msg.Elements)
}
}
}
}
return ""
}

func getReplyFaceIDs(ctx *zero.Ctx) []int {
for _, seg := range ctx.Event.Message {
if seg.Type == "reply" {
if msgID := parseID(seg.Data["id"]); msgID > 0 {
return extractFaceIDs(ctx.GetMessage(msgID).Elements)
}
}
}
return nil
}

func extractFaceIDs(segs message.Message) []int {
var ids []int
for _, seg := range segs {
if seg.Type == "face" {
if id := int(parseID(seg.Data["id"])); id > 0 {
ids = append(ids, id)
}
}
}
return ids
}

// hou
func houEncryptHandler(ctx *zero.Ctx) {
text := ctx.State["regex_matched"].([]string)[1]
text := getInput(ctx, "h加密", "齁语加密")
result := encodeHou(text)
recCfg := airecord.GetConfig()
record := ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, result)
if record != "" {
if record := ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, result); record != "" {
ctx.SendChain(message.Record(record))
} else {
ctx.SendChain(message.Text(result))
}
}

func houDecryptHandler(ctx *zero.Ctx) {
text := ctx.State["regex_matched"].([]string)[1]
result := decodeHou(text)
ctx.SendChain(message.Text(result))
text := getInput(ctx, "h解密", "齁语解密")
if text == "" {
text = getReplyContent(ctx)
}
if text == "" {
ctx.SendChain(message.Text("请输入密文或回复加密消息"))
return
}
ctx.SendChain(deserializeMsg(decodeHou(text))...)
}

// fumo
func fumoEncryptHandler(ctx *zero.Ctx) {
text := ctx.State["regex_matched"].([]string)[1]
result := encryptFumo(text)
ctx.SendChain(message.Text(result))
ctx.SendChain(message.Text(encryptFumo(getInput(ctx, "fumo加密"))))
}

func fumoDecryptHandler(ctx *zero.Ctx) {
text := ctx.State["regex_matched"].([]string)[1]
result := decryptFumo(text)
ctx.SendChain(message.Text(result))
text := getInput(ctx, "fumo解密")
if text == "" {
text = getReplyContent(ctx)
}
if text == "" {
ctx.SendChain(message.Text("请输入密文或回复加密消息"))
return
}
ctx.SendChain(deserializeMsg(decryptFumo(text))...)
}

// qq表情
func qqEmojiEncryptHandler(ctx *zero.Ctx) {
text := getInput(ctx, "qq加密")
if text == "" {
ctx.SendChain(message.Text("请输入要加密的文本"))
return
}
ctx.SendChain(encodeQQEmoji(text)...)
}

func qqEmojiDecryptHandler(ctx *zero.Ctx) {
faceIDs := extractFaceIDs(ctx.Event.Message)
if len(faceIDs) == 0 {
faceIDs = getReplyFaceIDs(ctx)
}
if len(faceIDs) == 0 {
ctx.SendChain(message.Text("请回复QQ表情加密消息进行解密"))
return
}
ctx.SendChain(deserializeMsg(decodeQQEmoji(faceIDs))...)
}
20 changes: 15 additions & 5 deletions plugin/crypter/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,25 @@ func init() {
"- 齁语解密 [密文] 或 h解密 [密文]\n\n" +
"- Fumo语加解密:\n" +
"- fumo加密 [文本]\n" +
"- fumo解密 [密文]\n\n",
"- fumo解密 [密文]\n\n" +
"- QQ表情加解密:\n" +
"- qq加密 [文本]\n" +
"- qq解密 [密文]\n\n" +
"注意:QQ表情解密建议使用回复,尽量不要复制粘贴\n\n",
PublicDataFolder: "Crypter",
})

re := `(?:\[CQ:reply,id=-?\d+\])?`

// hou
engine.OnRegex(`^(?:齁语加密|h加密)\s*(.+)$`).SetBlock(true).Handle(houEncryptHandler)
engine.OnRegex(`^(?:齁语解密|h解密)\s*(.+)$`).SetBlock(true).Handle(houDecryptHandler)
engine.OnRegex(re + `^(?:齁语加密|h加密)\s*(.+)$`).SetBlock(true).Handle(houEncryptHandler)
engine.OnRegex(re + `(?:齁语解密|h解密)\s*(.*)$`).SetBlock(true).Handle(houDecryptHandler)

// Fumo
engine.OnRegex(`^fumo加密\s*(.+)$`).SetBlock(true).Handle(fumoEncryptHandler)
engine.OnRegex(`^fumo解密\s*(.+)$`).SetBlock(true).Handle(fumoDecryptHandler)
engine.OnRegex(re + `^fumo加密\s*(.+)$`).SetBlock(true).Handle(fumoEncryptHandler)
engine.OnRegex(re + `fumo解密\s*(.*)$`).SetBlock(true).Handle(fumoDecryptHandler)

// QQ表情
engine.OnRegex(re + `^qq加密\s*(.+)$`).SetBlock(true).Handle(qqEmojiEncryptHandler)
engine.OnRegex(re + `qq解密`).SetBlock(true).Handle(qqEmojiDecryptHandler)
}
131 changes: 131 additions & 0 deletions plugin/crypter/qqemoji.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Package crypter QQ表情加解密
package crypter

import (
"bytes"
"fmt"
"io"
"strings"
"unicode/utf8"

"github.com/andybalholm/brotli"
"github.com/klauspost/compress/zstd"
"github.com/wdvxdr1123/ZeroBot/message"
)

const (
emojiZeroID = 297
emojiOneID = 424
)

func encodeQQEmoji(text string) message.Message {
if text == "" {
return message.Message{message.Text("请输入要加密的文本")}
}

data := []byte(text)
best, header := data, "0"
if br := tryCompress(func(w io.Writer) io.WriteCloser { return brotli.NewWriterLevel(w, brotli.BestCompression) }, data); len(br) > 0 && len(br) < len(best) {
best, header = br, "10"
}
if zs := tryCompress(func(w io.Writer) io.WriteCloser {
enc, _ := zstd.NewWriter(w, zstd.WithEncoderLevel(zstd.SpeedBestCompression))
return enc
}, data); len(zs) > 0 && len(zs) < len(best) {
best, header = zs, "11"
}

var bin strings.Builder
bin.WriteString(header)
for _, b := range best {
fmt.Fprintf(&bin, "%08b", b)
}

s := bin.String()
msg := make(message.Message, 0, len(s))
for _, bit := range s {
if bit == '0' {
msg = append(msg, message.Face(emojiZeroID))
} else {
msg = append(msg, message.Face(emojiOneID))
}
}
return msg
}

func decodeQQEmoji(faceIDs []int) string {
var bin strings.Builder
for _, id := range faceIDs {
if id == emojiZeroID {
bin.WriteByte('0')
} else if id == emojiOneID {
bin.WriteByte('1')
}
}
binary := bin.String()
if len(binary) < 2 {
return "QQ表情密文格式错误"
}

var header int
switch {
case binary[:2] == "11":
header = 2
case binary[:2] == "10":
header = 2
case binary[0] == '0':
header = 1
default:
return "QQ表情密文格式错误"
}

dataBin := binary[header:]
if len(dataBin)%8 != 0 {
return fmt.Sprintf("QQ表情解密失败:数据长度不正确(%d位)", len(dataBin))
}

data := make([]byte, len(dataBin)/8)
for i := range data {
for j := 0; j < 8; j++ {
if dataBin[i*8+j] == '1' {
data[i] |= 1 << (7 - j)
}
}
}

var out []byte
var err error
switch binary[:header] {
case "0":
out = data
case "10":
r := brotli.NewReader(bytes.NewReader(data))
out, err = io.ReadAll(r)
case "11":
var dec *zstd.Decoder
dec, err = zstd.NewReader(bytes.NewReader(data))
if err == nil {
out, err = io.ReadAll(dec)
dec.Close()
}
}
if err != nil {
return fmt.Sprintf("QQ表情解压失败: %v", err)
}
if !utf8.Valid(out) {
return "QQ表情解密失败:结果不是有效文本"
}
return string(out)
}

func tryCompress(newWriter func(io.Writer) io.WriteCloser, data []byte) []byte {
var buf bytes.Buffer
w := newWriter(&buf)
if _, err := w.Write(data); err != nil {
return nil
}
if err := w.Close(); err != nil {
return nil
}
return buf.Bytes()
}
Loading