Skip to content

Commit e0fa3ba

Browse files
authored
feat(crypter): 添加qq表情加密并支持回应密文 (#1287)
1 parent 82dc709 commit e0fa3ba

4 files changed

Lines changed: 213 additions & 17 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,8 @@ print("run[CQ:image,file="+j["img"]+"]")
655655
- [x] 齁语解密 [密文] 或 h解密 [密文]
656656
- [x] fumo加密 [文本]
657657
- [x] fumo解密 [文本]
658+
- [x] qq加密 [文本]
659+
- [x] qq解密 [密文]
658660

659661
</details>
660662
<details>

plugin/crypter/handlers.go

Lines changed: 130 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,157 @@
22
package crypter
33

44
import (
5+
"fmt"
6+
"regexp"
7+
"strconv"
8+
"strings"
9+
510
"github.com/FloatTech/AnimeAPI/airecord"
611
zero "github.com/wdvxdr1123/ZeroBot"
712
"github.com/wdvxdr1123/ZeroBot/message"
813
)
914

15+
var faceTagRe = regexp.MustCompile(`\{\{face:(\d+)\}\}`)
16+
17+
func parseID(v interface{}) int64 {
18+
n, _ := strconv.ParseInt(fmt.Sprint(v), 10, 64)
19+
return n
20+
}
21+
22+
func serializeMsg(segs message.Message) string {
23+
var sb strings.Builder
24+
for _, seg := range segs {
25+
switch seg.Type {
26+
case "text":
27+
sb.WriteString(seg.Data["text"])
28+
case "face":
29+
fmt.Fprintf(&sb, "{{face:%v}}", seg.Data["id"])
30+
}
31+
}
32+
return sb.String()
33+
}
34+
35+
func deserializeMsg(s string) message.Message {
36+
var msg message.Message
37+
parts := faceTagRe.Split(s, -1)
38+
matches := faceTagRe.FindAllStringSubmatch(s, -1)
39+
for i, part := range parts {
40+
if part != "" {
41+
msg = append(msg, message.Text(part))
42+
}
43+
if i < len(matches) {
44+
id, _ := strconv.Atoi(matches[i][1])
45+
msg = append(msg, message.Face(id))
46+
}
47+
}
48+
return msg
49+
}
50+
51+
func getInput(ctx *zero.Ctx, cmds ...string) string {
52+
full := serializeMsg(ctx.Event.Message)
53+
for _, cmd := range cmds {
54+
if idx := strings.Index(full, cmd); idx >= 0 {
55+
return strings.TrimSpace(full[idx+len(cmd):])
56+
}
57+
}
58+
return ""
59+
}
60+
61+
func getReplyContent(ctx *zero.Ctx) string {
62+
for _, seg := range ctx.Event.Message {
63+
if seg.Type == "reply" {
64+
if msgID := parseID(seg.Data["id"]); msgID > 0 {
65+
if msg := ctx.GetMessage(msgID); msg.Elements != nil {
66+
return serializeMsg(msg.Elements)
67+
}
68+
}
69+
}
70+
}
71+
return ""
72+
}
73+
74+
func getReplyFaceIDs(ctx *zero.Ctx) []int {
75+
for _, seg := range ctx.Event.Message {
76+
if seg.Type == "reply" {
77+
if msgID := parseID(seg.Data["id"]); msgID > 0 {
78+
return extractFaceIDs(ctx.GetMessage(msgID).Elements)
79+
}
80+
}
81+
}
82+
return nil
83+
}
84+
85+
func extractFaceIDs(segs message.Message) []int {
86+
var ids []int
87+
for _, seg := range segs {
88+
if seg.Type == "face" {
89+
if id := int(parseID(seg.Data["id"])); id > 0 {
90+
ids = append(ids, id)
91+
}
92+
}
93+
}
94+
return ids
95+
}
96+
1097
// hou
1198
func houEncryptHandler(ctx *zero.Ctx) {
12-
text := ctx.State["regex_matched"].([]string)[1]
99+
text := getInput(ctx, "h加密", "齁语加密")
13100
result := encodeHou(text)
14101
recCfg := airecord.GetConfig()
15-
record := ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, result)
16-
if record != "" {
102+
if record := ctx.GetAIRecord(recCfg.ModelID, recCfg.Customgid, result); record != "" {
17103
ctx.SendChain(message.Record(record))
18104
} else {
19105
ctx.SendChain(message.Text(result))
20106
}
21107
}
22108

23109
func houDecryptHandler(ctx *zero.Ctx) {
24-
text := ctx.State["regex_matched"].([]string)[1]
25-
result := decodeHou(text)
26-
ctx.SendChain(message.Text(result))
110+
text := getInput(ctx, "h解密", "齁语解密")
111+
if text == "" {
112+
text = getReplyContent(ctx)
113+
}
114+
if text == "" {
115+
ctx.SendChain(message.Text("请输入密文或回复加密消息"))
116+
return
117+
}
118+
ctx.SendChain(deserializeMsg(decodeHou(text))...)
27119
}
28120

29121
// fumo
30122
func fumoEncryptHandler(ctx *zero.Ctx) {
31-
text := ctx.State["regex_matched"].([]string)[1]
32-
result := encryptFumo(text)
33-
ctx.SendChain(message.Text(result))
123+
ctx.SendChain(message.Text(encryptFumo(getInput(ctx, "fumo加密"))))
34124
}
35125

36126
func fumoDecryptHandler(ctx *zero.Ctx) {
37-
text := ctx.State["regex_matched"].([]string)[1]
38-
result := decryptFumo(text)
39-
ctx.SendChain(message.Text(result))
127+
text := getInput(ctx, "fumo解密")
128+
if text == "" {
129+
text = getReplyContent(ctx)
130+
}
131+
if text == "" {
132+
ctx.SendChain(message.Text("请输入密文或回复加密消息"))
133+
return
134+
}
135+
ctx.SendChain(deserializeMsg(decryptFumo(text))...)
136+
}
137+
138+
// qq表情
139+
func qqEmojiEncryptHandler(ctx *zero.Ctx) {
140+
text := getInput(ctx, "qq加密")
141+
if text == "" {
142+
ctx.SendChain(message.Text("请输入要加密的文本"))
143+
return
144+
}
145+
ctx.SendChain(encodeQQEmoji(text)...)
146+
}
147+
148+
func qqEmojiDecryptHandler(ctx *zero.Ctx) {
149+
faceIDs := extractFaceIDs(ctx.Event.Message)
150+
if len(faceIDs) == 0 {
151+
faceIDs = getReplyFaceIDs(ctx)
152+
}
153+
if len(faceIDs) == 0 {
154+
ctx.SendChain(message.Text("请回复QQ表情加密消息进行解密"))
155+
return
156+
}
157+
ctx.SendChain(deserializeMsg(decodeQQEmoji(faceIDs))...)
40158
}

plugin/crypter/main.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,25 @@ func init() {
1717
"- 齁语解密 [密文] 或 h解密 [密文]\n\n" +
1818
"- Fumo语加解密:\n" +
1919
"- fumo加密 [文本]\n" +
20-
"- fumo解密 [密文]\n\n",
20+
"- fumo解密 [密文]\n\n" +
21+
"- QQ表情加解密:\n" +
22+
"- qq加密 [文本]\n" +
23+
"- qq解密 [密文]\n\n" +
24+
"注意:QQ表情解密建议使用回复,尽量不要复制粘贴\n\n",
2125
PublicDataFolder: "Crypter",
2226
})
2327

28+
re := `(?:\[CQ:reply,id=-?\d+\])?`
29+
2430
// hou
25-
engine.OnRegex(`^(?:齁语加密|h加密)\s*(.+)$`).SetBlock(true).Handle(houEncryptHandler)
26-
engine.OnRegex(`^(?:齁语解密|h解密)\s*(.+)$`).SetBlock(true).Handle(houDecryptHandler)
31+
engine.OnRegex(re + `^(?:齁语加密|h加密)\s*(.+)$`).SetBlock(true).Handle(houEncryptHandler)
32+
engine.OnRegex(re + `(?:齁语解密|h解密)\s*(.*)$`).SetBlock(true).Handle(houDecryptHandler)
2733

2834
// Fumo
29-
engine.OnRegex(`^fumo加密\s*(.+)$`).SetBlock(true).Handle(fumoEncryptHandler)
30-
engine.OnRegex(`^fumo解密\s*(.+)$`).SetBlock(true).Handle(fumoDecryptHandler)
35+
engine.OnRegex(re + `^fumo加密\s*(.+)$`).SetBlock(true).Handle(fumoEncryptHandler)
36+
engine.OnRegex(re + `fumo解密\s*(.*)$`).SetBlock(true).Handle(fumoDecryptHandler)
37+
38+
// QQ表情
39+
engine.OnRegex(re + `^qq加密\s*(.+)$`).SetBlock(true).Handle(qqEmojiEncryptHandler)
40+
engine.OnRegex(re + `qq解密`).SetBlock(true).Handle(qqEmojiDecryptHandler)
3141
}

plugin/crypter/qqemoji.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Package crypter QQ表情加解密
2+
package crypter
3+
4+
import (
5+
"fmt"
6+
"strings"
7+
"unicode/utf8"
8+
9+
"github.com/wdvxdr1123/ZeroBot/message"
10+
)
11+
12+
const (
13+
emojiZeroID = 297
14+
emojiOneID = 424
15+
)
16+
17+
func encodeQQEmoji(text string) message.Message {
18+
if text == "" {
19+
return message.Message{message.Text("请输入要加密的文本")}
20+
}
21+
22+
var bin strings.Builder
23+
for _, b := range []byte(text) {
24+
fmt.Fprintf(&bin, "%08b", b)
25+
}
26+
27+
s := bin.String()
28+
msg := make(message.Message, 0, len(s))
29+
for _, bit := range s {
30+
if bit == '0' {
31+
msg = append(msg, message.Face(emojiZeroID))
32+
} else {
33+
msg = append(msg, message.Face(emojiOneID))
34+
}
35+
}
36+
return msg
37+
}
38+
39+
func decodeQQEmoji(faceIDs []int) string {
40+
var bin strings.Builder
41+
for _, id := range faceIDs {
42+
if id == emojiZeroID {
43+
bin.WriteByte('0')
44+
} else if id == emojiOneID {
45+
bin.WriteByte('1')
46+
}
47+
}
48+
binary := bin.String()
49+
if len(binary) == 0 || len(binary)%8 != 0 {
50+
return "QQ表情密文格式错误"
51+
}
52+
53+
data := make([]byte, len(binary)/8)
54+
for i := range data {
55+
for j := 0; j < 8; j++ {
56+
if binary[i*8+j] == '1' {
57+
data[i] |= 1 << (7 - j)
58+
}
59+
}
60+
}
61+
62+
if !utf8.Valid(data) {
63+
return "QQ表情解密失败:结果不是有效文本"
64+
}
65+
return string(data)
66+
}

0 commit comments

Comments
 (0)