feat: 添加帮助功能
This commit is contained in:
parent
ea3ef541e2
commit
48891fbd0a
@ -7,6 +7,7 @@ import (
|
||||
|
||||
func init() {
|
||||
handler.RegisterHandler("echo", echo)
|
||||
handler.RegisterHelpInform("echo", "再说一遍")
|
||||
}
|
||||
|
||||
func echo(msg model.Message) (reply model.Reply) {
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
|
||||
func init() {
|
||||
handler.RegisterHandler("上网", getweb)
|
||||
handler.RegisterHelpInform("上网", "上网 <URL> 截取网页")
|
||||
}
|
||||
|
||||
func getweb(msg model.Message) (reply model.Reply) {
|
||||
@ -15,7 +16,8 @@ func getweb(msg model.Message) (reply model.Reply) {
|
||||
return model.Reply{}
|
||||
}
|
||||
url := msg.RawMsg[len("上网 "):]
|
||||
if err := util.ScreenshotURL(url, "./tmp/getweb/url.png", 0, 0, 0, 0, 0, 0, ""); err != nil {
|
||||
url = formatURL(url)
|
||||
if err := util.ScreenshotURL(url, "./tmp/getweb/url.png", 1620, 1960, 0, 0, 0, 0, ""); err != nil {
|
||||
return model.Reply{
|
||||
ReplyMsg: err.Error(),
|
||||
ReferOriginMsg: true,
|
||||
@ -28,3 +30,10 @@ func getweb(msg model.Message) (reply model.Reply) {
|
||||
FromMsg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
func formatURL(url string) string {
|
||||
if url[:7] != "http://" && url[:8] != "https://" {
|
||||
return "http://" + url
|
||||
}
|
||||
return url
|
||||
}
|
||||
|
@ -8,10 +8,13 @@ import (
|
||||
)
|
||||
|
||||
type Handler func(msg model.Message) (reply model.Reply)
|
||||
type TryCatchHandler func(msg model.Message) (reply model.Reply, catched bool)
|
||||
|
||||
var handlers = make(map[string]Handler)
|
||||
var frontMatchHandlers = make(map[string]Handler)
|
||||
var liveHandlers = make(map[int64]map[int64]Handler)
|
||||
var liveHandlers = make(map[int64]map[int64]TryCatchHandler)
|
||||
var livePrivateHandlers = make(map[int64]TryCatchHandler)
|
||||
var HelpInforms = make(map[string]string)
|
||||
var privateAllHandler Handler
|
||||
|
||||
func RegisterPrivateHandler(handler Handler) {
|
||||
@ -26,9 +29,13 @@ func RegisterFrontMatchHandler(trigger string, handler Handler) {
|
||||
frontMatchHandlers[trigger] = handler
|
||||
}
|
||||
|
||||
func RegisterLiveHandler(groupID int64, userID int64, handler Handler) { //userID=-1 means for all users in groupID
|
||||
func RegisterLiveHandler(groupID int64, userID int64, handler TryCatchHandler) { //userID=-1 means for all users in groupID
|
||||
if groupID == 0 {
|
||||
livePrivateHandlers[userID] = handler
|
||||
return
|
||||
}
|
||||
if _, ok := liveHandlers[groupID]; !ok {
|
||||
liveHandlers[groupID] = make(map[int64]Handler)
|
||||
liveHandlers[groupID] = make(map[int64]TryCatchHandler)
|
||||
}
|
||||
liveHandlers[groupID][userID] = handler
|
||||
}
|
||||
@ -51,8 +58,18 @@ func MsgInHandler(msg model.Message) (reply model.Reply) {
|
||||
}()
|
||||
if handlerUserID, ok := liveHandlers[msg.GroupInfo.GroupId]; ok {
|
||||
if handler, ok := handlerUserID[msg.UserId]; ok {
|
||||
liveHandlers[msg.GroupInfo.GroupId][msg.UserId] = nil
|
||||
return handler(msg)
|
||||
defer func() { liveHandlers[msg.GroupInfo.GroupId][msg.UserId] = nil }()
|
||||
reply, catched := handler(msg)
|
||||
if catched {
|
||||
return reply
|
||||
}
|
||||
}
|
||||
}
|
||||
if handler, ok := livePrivateHandlers[msg.UserId]; ok {
|
||||
defer func() { livePrivateHandlers[msg.UserId] = nil }()
|
||||
reply, catched := handler(msg)
|
||||
if catched {
|
||||
return reply
|
||||
}
|
||||
}
|
||||
for trigger, handler := range frontMatchHandlers {
|
||||
@ -69,3 +86,7 @@ func MsgInHandler(msg model.Message) (reply model.Reply) {
|
||||
}
|
||||
return model.Reply{}
|
||||
}
|
||||
|
||||
func RegisterHelpInform(triggerName string, inform string) {
|
||||
HelpInforms[triggerName] = inform
|
||||
}
|
||||
|
22
handler/help/help.go
Normal file
22
handler/help/help.go
Normal file
@ -0,0 +1,22 @@
|
||||
package help
|
||||
|
||||
import (
|
||||
"git.lxtend.com/qqbot/handler"
|
||||
"git.lxtend.com/qqbot/model"
|
||||
)
|
||||
|
||||
func init() {
|
||||
handler.RegisterHandler("!help", help)
|
||||
}
|
||||
|
||||
func help(msg model.Message) (reply model.Reply) {
|
||||
helpInfo := `以下是支持的功能:`
|
||||
for k, v := range handler.HelpInforms {
|
||||
helpInfo += "\n" + k + " : " + v
|
||||
}
|
||||
return model.Reply{
|
||||
ReplyMsg: helpInfo,
|
||||
ReferOriginMsg: false,
|
||||
FromMsg: msg,
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ func init() {
|
||||
jrrpInstance = jrrp.NewJrrp()
|
||||
handler.RegisterHandler("今日人品", jrrpHandler)
|
||||
handler.RegisterHandler("jrrp", jrrpHandler)
|
||||
handler.RegisterHelpInform("今日人品/jrrp", "今日人品或jrrp 查询今日人品")
|
||||
}
|
||||
|
||||
func jrrpHandler(msg model.Message) (reply model.Reply) {
|
||||
|
85
handler/roll/roll.go
Normal file
85
handler/roll/roll.go
Normal file
@ -0,0 +1,85 @@
|
||||
package roll
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"git.lxtend.com/qqbot/handler"
|
||||
"git.lxtend.com/qqbot/model"
|
||||
"golang.org/x/exp/rand"
|
||||
)
|
||||
|
||||
func init() {
|
||||
handler.RegisterHandler("roll", roll)
|
||||
handler.RegisterHelpInform("roll", "roll点")
|
||||
}
|
||||
|
||||
func roll(msg model.Message) (reply model.Reply) {
|
||||
act, err := ParseRollExpression(msg.RawMsg)
|
||||
if err != nil {
|
||||
return model.Reply{
|
||||
ReplyMsg: "",
|
||||
ReferOriginMsg: false,
|
||||
FromMsg: msg,
|
||||
}
|
||||
}
|
||||
result, results := RollDice(act)
|
||||
return model.Reply{
|
||||
ReplyMsg: "掷骰结果: " + strconv.Itoa(result) + "点 " + fmt.Sprint(results),
|
||||
ReferOriginMsg: true,
|
||||
FromMsg: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// RollExpression 解析结果的结构体
|
||||
type RollExpression struct {
|
||||
Times int // 掷骰次数
|
||||
Sides int // 骰子面数
|
||||
}
|
||||
|
||||
// ParseRollExpression 解析骰子表达式,例如 "3d6" 或 "1d20"
|
||||
func ParseRollExpression(input string) (*RollExpression, error) {
|
||||
// 匹配类似 "3d6" 或 "1d20" 的表达式
|
||||
re := regexp.MustCompile(`(\d*)d(\d+)$`)
|
||||
|
||||
matches := re.FindStringSubmatch(input)
|
||||
if len(matches) != 3 {
|
||||
return nil, fmt.Errorf("invalid roll expression: %s", input)
|
||||
}
|
||||
|
||||
// 如果次数为空,默认为 1
|
||||
times := 1
|
||||
if matches[1] != "" {
|
||||
t, err := strconv.Atoi(matches[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
times = t
|
||||
}
|
||||
|
||||
// 将面数转换为整数
|
||||
sides, err := strconv.Atoi(matches[2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &RollExpression{Times: times, Sides: sides}, nil
|
||||
}
|
||||
|
||||
func RollDice(expr *RollExpression) (int, []int) {
|
||||
rand.Seed(uint64(time.Now().UnixNano())) // 初始化随机种子
|
||||
|
||||
results := make([]int, expr.Times)
|
||||
total := 0
|
||||
|
||||
// 每次掷骰,并累加总和
|
||||
for i := 0; i < expr.Times; i++ {
|
||||
result := rand.Intn(expr.Sides) + 1 // 随机生成 1 到 Sides 的值
|
||||
results[i] = result
|
||||
total += result
|
||||
}
|
||||
|
||||
return total, results
|
||||
}
|
@ -11,11 +11,18 @@ import (
|
||||
|
||||
func init() {
|
||||
handler.RegisterHandler("查ss", getMySS)
|
||||
handler.RegisterHelpInform("查ss", "查看您的最新分数")
|
||||
handler.RegisterHandler("绑定ss", bindSS)
|
||||
handler.RegisterHelpInform("绑定ss", "绑定您的scoresaber账号")
|
||||
handler.RegisterHandler("解绑ss", unbindSS)
|
||||
handler.RegisterHelpInform("解绑ss", "解绑您的scoresaber账号")
|
||||
handler.RegisterHandler("最新ss", getMyRecentScore)
|
||||
handler.RegisterHelpInform("最新ss", "查看您的最新游戏记录")
|
||||
handler.RegisterHandler("最热ss", getRecentScore)
|
||||
handler.RegisterHelpInform("最热ss", "查看全大陆的最新游戏记录")
|
||||
handler.RegisterHandler("截ss", screenshotSS)
|
||||
handler.RegisterHandler("jss", screenshotSS)
|
||||
handler.RegisterHelpInform("截ss/jss", "scoresaber主页截图")
|
||||
}
|
||||
|
||||
func getMySS(msg model.Message) (reply model.Reply) {
|
||||
|
@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
handler.RegisterFrontMatchHandler("[CQ:json,data=", cqJsonUrlParser)
|
||||
handler.RegisterFrontMatchHandler("[CQ:json", cqJsonUrlParser)
|
||||
handler.RegisterFrontMatchHandler("http://", plainTextUrlParser)
|
||||
handler.RegisterFrontMatchHandler("https://", plainTextUrlParser)
|
||||
}
|
||||
@ -50,7 +50,8 @@ func plainTextUrlParser(msg model.Message) (reply model.Reply) {
|
||||
}
|
||||
|
||||
func cqJsonUrlParser(msg model.Message) (reply model.Reply) {
|
||||
qqdocurl, err := extractQQDocURL(msg.RawMsg)
|
||||
newMsg := strings.ReplaceAll(msg.RawMsg, "\n", "")
|
||||
qqdocurl, err := extractQQDocURL(newMsg)
|
||||
if err != nil {
|
||||
return model.Reply{
|
||||
ReplyMsg: "",
|
||||
@ -66,45 +67,67 @@ func cqJsonUrlParser(msg model.Message) (reply model.Reply) {
|
||||
}
|
||||
}
|
||||
|
||||
// extractQQDocURL 从字符串中提取 JSON 数据部分
|
||||
func extractQQDocURL(input string) (string, error) {
|
||||
// 使用正则表达式提取 JSON 数据部分
|
||||
// 使用非贪婪匹配提取 JSON 数据部分
|
||||
re := regexp.MustCompile(`\{.*\}`)
|
||||
jsonPart := re.FindString(input)
|
||||
if jsonPart == "" {
|
||||
return "", fmt.Errorf("无法找到 JSON 数据部分")
|
||||
}
|
||||
|
||||
// 解析 JSON 数据
|
||||
var jsonData map[string]interface{}
|
||||
// 替换 HTML 实体为普通字符
|
||||
jsonPart = strings.ReplaceAll(jsonPart, ",", ",")
|
||||
jsonPart = strings.ReplaceAll(jsonPart, "[", "[")
|
||||
jsonPart = strings.ReplaceAll(jsonPart, "]", "]")
|
||||
jsonPart = strings.ReplaceAll(jsonPart, "&", "&")
|
||||
url, err := parseQQDocURL(jsonPart)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("解析 JSON 失败: %w", err)
|
||||
}
|
||||
return url, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(jsonPart), &jsonData); err != nil {
|
||||
// parseQQDocURL 从 JSON 中提取 qqdocurl 字段
|
||||
func parseQQDocURL(jsonStr string) (string, error) {
|
||||
var jsonData map[string]interface{}
|
||||
|
||||
// 解析 JSON 数据
|
||||
if err := json.Unmarshal([]byte(jsonStr), &jsonData); err != nil {
|
||||
return "", fmt.Errorf("解析 JSON 失败: %w", err)
|
||||
}
|
||||
|
||||
// 定位到 meta -> detail_1 -> qqdocurl
|
||||
meta, ok := jsonData["meta"].(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("找不到 meta 字段")
|
||||
}
|
||||
detail, ok := meta["detail_1"].(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("找不到 detail_1 字段")
|
||||
}
|
||||
qqdocurl, ok := detail["qqdocurl"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("找不到 qqdocurl 字段")
|
||||
url := ""
|
||||
|
||||
if jsonData["app"] == "com.tencent.miniapp_01" { // 定位到 meta -> detail_1 -> qqdocurl
|
||||
meta, ok := jsonData["meta"].(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("找不到 meta 字段")
|
||||
}
|
||||
detail, ok := meta["detail_1"].(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("找不到 detail_1 字段")
|
||||
}
|
||||
url, ok = detail["qqdocurl"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("找不到 qqdocurl 字段")
|
||||
}
|
||||
} else if jsonData["app"] == "com.tencent.structmsg" { // 定位到 meta -> news -> jumpUrl
|
||||
meta, ok := jsonData["meta"].(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("找不到 meta 字段")
|
||||
}
|
||||
news, ok := meta["news"].(map[string]interface{})
|
||||
if !ok {
|
||||
return "", fmt.Errorf("找不到 news 字段")
|
||||
}
|
||||
url, ok = news["jumpUrl"].(string)
|
||||
if !ok {
|
||||
return "", fmt.Errorf("找不到 jumpUrl 字段")
|
||||
}
|
||||
}
|
||||
|
||||
qqdocurl, _ = removeTrackingParams(qqdocurl)
|
||||
qqdocurl, _ = resolveFinalURL(qqdocurl)
|
||||
qqdocurl, _ = removeTrackingParams(qqdocurl)
|
||||
|
||||
return qqdocurl, nil
|
||||
return url, nil
|
||||
}
|
||||
|
||||
func removeTrackingParams(rawURL string) (string, error) {
|
||||
@ -144,6 +167,9 @@ func resolveFinalURL(initialURL string) (string, error) {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("解析重定向地址失败: %w", err)
|
||||
}
|
||||
if redirectURL.String() == initialURL {
|
||||
return initialURL, nil
|
||||
}
|
||||
fmt.Printf("重定向至: %s\n", redirectURL.String())
|
||||
return resolveFinalURL(redirectURL.String())
|
||||
}
|
||||
|
@ -8,7 +8,9 @@ import (
|
||||
|
||||
func init() {
|
||||
handler.RegisterHandler("喜报", xiBao)
|
||||
handler.RegisterHelpInform("喜报", "喜报 [内容] 生成喜报图片,支持换行")
|
||||
handler.RegisterHandler("悲报", beiBao)
|
||||
handler.RegisterHelpInform("悲报", "悲报 [内容] 生成悲报图片,支持换行")
|
||||
}
|
||||
|
||||
func xiBao(msg model.Message) (reply model.Reply) {
|
||||
|
@ -4,8 +4,12 @@ import (
|
||||
_ "git.lxtend.com/qqbot/handler/echo"
|
||||
_ "git.lxtend.com/qqbot/handler/getweb"
|
||||
_ "git.lxtend.com/qqbot/handler/headmaster"
|
||||
_ "git.lxtend.com/qqbot/handler/help"
|
||||
_ "git.lxtend.com/qqbot/handler/jrrp"
|
||||
_ "git.lxtend.com/qqbot/handler/roll"
|
||||
_ "git.lxtend.com/qqbot/handler/scoresaber"
|
||||
_ "git.lxtend.com/qqbot/handler/urlparser"
|
||||
|
||||
// _ "git.lxtend.com/qqbot/handler/wordle"
|
||||
_ "git.lxtend.com/qqbot/handler/xibao"
|
||||
)
|
||||
|
@ -74,12 +74,12 @@ func ScreenshotURL(url, output string, width, height int, marginTop, marginBotto
|
||||
err := chromedp.Run(ctx,
|
||||
chromedp.Navigate(url), // 打开网页
|
||||
ignoreErrors(queryAction), // 等待指定元素
|
||||
chromedp.Sleep(5*time.Second),
|
||||
chromedp.Sleep(10*time.Second),
|
||||
chromedp.ActionFunc(func(ctx context.Context) error { // 自定义截图逻辑
|
||||
layoutViewport, _, _, _, _, _, _ := page.GetLayoutMetrics().Do(ctx)
|
||||
_, _, _, _, cssLayoutViewport, _, _ := page.GetLayoutMetrics().Do(ctx)
|
||||
if width == 0 || height == 0 {
|
||||
width = int(layoutViewport.ClientWidth)
|
||||
height = int(layoutViewport.ClientHeight)
|
||||
width = int(cssLayoutViewport.ClientWidth)
|
||||
height = int(cssLayoutViewport.ClientHeight)
|
||||
}
|
||||
// 计算调整后的截图区域
|
||||
clip := &page.Viewport{
|
||||
@ -125,6 +125,7 @@ func enableRequestInterception() chromedp.Tasks {
|
||||
network.SetBlockedURLS([]string{
|
||||
"pagead2.googlesyndication.com",
|
||||
"optimizationguide-pa.googleapis.com",
|
||||
"static.cloudflareinsights.com",
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -2,9 +2,11 @@ package wsclient
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/url"
|
||||
|
||||
"git.lxtend.com/qqbot/action"
|
||||
"git.lxtend.com/qqbot/constants"
|
||||
"git.lxtend.com/qqbot/handler"
|
||||
"git.lxtend.com/qqbot/model"
|
||||
@ -70,6 +72,9 @@ func (c *WebSocketClient) receiveMessages() {
|
||||
go func() {
|
||||
reply := handler.MsgInHandler(msg)
|
||||
if reply.ReplyMsg != "" {
|
||||
if reply.ReferOriginMsg {
|
||||
reply.ReplyMsg = fmt.Sprintf("%s%s", action.GenReply(reply.FromMsg.OriginMsgId, "", 0, 0, 0), reply.ReplyMsg)
|
||||
}
|
||||
sendPkg := model.GenSendPkg(reply.FromMsg.UserId, reply.FromMsg.GroupInfo.GroupId, reply.ReplyMsg, false)
|
||||
sendPkgJson, err := json.Marshal(sendPkg)
|
||||
if err != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user