feat: 添加帮助功能

This commit is contained in:
lixiangwuxian 2024-10-15 23:07:49 +08:00
parent ea3ef541e2
commit 48891fbd0a
12 changed files with 217 additions and 33 deletions

View File

@ -7,6 +7,7 @@ import (
func init() {
handler.RegisterHandler("echo", echo)
handler.RegisterHelpInform("echo", "再说一遍")
}
func echo(msg model.Message) (reply model.Reply) {

View File

@ -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
}

View File

@ -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
View 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,
}
}

View File

@ -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
View 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
}

View File

@ -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) {

View File

@ -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, "&#44;", ",")
jsonPart = strings.ReplaceAll(jsonPart, "&#91;", "[")
jsonPart = strings.ReplaceAll(jsonPart, "&#93;", "]")
jsonPart = strings.ReplaceAll(jsonPart, "&amp;", "&")
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())
}

View File

@ -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) {

View File

@ -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"
)

View File

@ -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",
}),
}
}

View File

@ -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 {