2024-10-13 14:50:53 +08:00

121 lines
3.1 KiB
Go

package urlparser
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"regexp"
"strings"
"git.lxtend.com/qqbot/handler"
"git.lxtend.com/qqbot/model"
)
func init() {
handler.RegisterFrontMatchHandler("[CQ:json,data=", urlParser)
}
func urlParser(msg model.Message) (reply model.Reply) {
qqdocurl, err := extractQQDocURL(msg.RawMsg)
if err != nil {
return model.Reply{
ReplyMsg: fmt.Sprintf("解析失败: %v", err),
ReferOriginMsg: true,
FromMsg: msg,
}
}
return model.Reply{
ReplyMsg: qqdocurl,
ReferOriginMsg: true,
FromMsg: msg,
}
}
func extractQQDocURL(input string) (string, error) {
// 使用正则表达式提取 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, "&", "&")
if err := json.Unmarshal([]byte(jsonPart), &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 字段")
}
qqdocurl, _ = removeTrackingParams(qqdocurl)
qqdocurl, _ = resolveFinalURL(qqdocurl)
qqdocurl, _ = removeTrackingParams(qqdocurl)
return qqdocurl, nil
}
func removeTrackingParams(rawURL string) (string, error) {
parsedURL, err := url.Parse(rawURL)
if err != nil {
return "", err
}
// 仅保留 URL 的 Scheme 和 Host + Path 部分
return fmt.Sprintf("%s://%s%s", parsedURL.Scheme, parsedURL.Host, parsedURL.Path), nil
}
func resolveFinalURL(initialURL string) (string, error) {
// 解析 URL 确保其格式正确
parsedURL, err := url.Parse(initialURL)
if err != nil {
return "", fmt.Errorf("URL 解析失败: %w", err)
}
// 创建一个 HTTP 客户端
client := &http.Client{
// 禁用自动重定向,以便手动处理 302
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
// 发起 GET 请求
resp, err := client.Get(parsedURL.String())
if err != nil {
return "", fmt.Errorf("请求失败: %w", err)
}
defer resp.Body.Close()
// 如果是 302 重定向,则递归访问新链接
if resp.StatusCode == http.StatusFound || resp.StatusCode == http.StatusMovedPermanently {
redirectURL, err := resp.Location()
if err != nil {
return "", fmt.Errorf("解析重定向地址失败: %w", err)
}
fmt.Printf("重定向至: %s\n", redirectURL.String())
return resolveFinalURL(redirectURL.String())
}
// 返回最终的非 302 链接
return initialURL, nil
}