21 | 一个尝试在保护用户隐私的前提下,提供各类功能的类命令行Telegram机器人。
22 |
23 | 帮助文档 »
24 |
25 |
26 | 查看演示
27 | ·
28 | 报告错误/提出更多内容
29 | ·
30 | 用户交流群组
31 |
%s
\nRGB: (%d, %d, %d)
\n十六进制 (Hex): %s
\nRGB 百分比 (RGB Percent): %s
\n\n推荐颜色:\n%s", name, r, g, b, hex, rgbPercent, recommendedColors)
100 |
101 | imagePath := fmt.Sprintf("/tmp/color_%d_%d_%d.png", r, g, b)
102 | err := createColorImage(r, g, b, imagePath)
103 | if err != nil {
104 | log.Printf("Error creating color image: %v", err)
105 | utils.SendMessage(chatID, "生成颜色图片时出错。", messageID, bot)
106 | return
107 | }
108 |
109 | err = utils.SendPhotoWithCaption(chatID, messageID, imagePath, text, bot)
110 | if err != nil {
111 | log.Printf("Error sending photo: %v", err)
112 | utils.SendMessage(chatID, "发送颜色图片时出错。", messageID, bot)
113 | return
114 | }
115 |
116 | // 删除临时图片文件
117 | err = os.Remove(imagePath)
118 | if err != nil {
119 | log.Printf("Error removing temp image file: %v", err)
120 | }
121 | }
122 |
123 | func getRecommendedColors(r, g, b int) string {
124 | var recommendations string
125 | // 生成不同亮度的推荐颜色
126 | for i := 1; i <= 3; i++ {
127 | factor := float64(i) / 4.0
128 | newR := int(float64(r) * factor)
129 | newG := int(float64(g) * factor)
130 | newB := int(float64(b) * factor)
131 | hex := fmt.Sprintf("#%02x%02x%02x", newR, newG, newB)
132 | recommendations += fmt.Sprintf("%s
\n", hex)
133 | }
134 | return recommendations
135 | }
136 |
137 | func createColorImage(r, g, b int, path string) error {
138 | img := image.NewRGBA(image.Rect(0, 0, 100, 100))
139 | fillColor := color.RGBA{uint8(r), uint8(g), uint8(b), 255}
140 | draw.Draw(img, img.Bounds(), &image.Uniform{fillColor}, image.Point{}, draw.Src)
141 |
142 | file, err := os.Create(path)
143 | if err != nil {
144 | return err
145 | }
146 | defer file.Close()
147 |
148 | return png.Encode(file, img)
149 | }
150 |
--------------------------------------------------------------------------------
/functions/curconv/curconv.go:
--------------------------------------------------------------------------------
1 | package curconv
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "log"
7 | "net/http"
8 | "regexp"
9 | "strconv"
10 | "strings"
11 |
12 | tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
13 | )
14 |
15 | // API URL 模板
16 | const apiURL = "https://www.mastercard.com/settlement/currencyrate/conversion-rate?fxDate=0000-00-00&transCurr=%s&crdhldBillCurr=%s&bankFee=0&transAmt=%s"
17 |
18 | // HandleCurconvCommand 处理汇率转换命令
19 | func HandleCurconvCommand(message *tgbotapi.Message, bot *tgbotapi.BotAPI) {
20 | args := strings.Fields(message.CommandArguments())
21 |
22 | if len(args) < 2 {
23 | reply := "用法: /curconv 货币源 货币目标 [金额]"
24 | SendMessage(message.Chat.ID, reply, message.MessageID, bot)
25 | return
26 | }
27 |
28 | // 校验输入,只允许英文字母、数字、空格和小数点
29 | validInput := regexp.MustCompile(`^[a-zA-Z0-9\s.]+$`).MatchString
30 | for _, arg := range args {
31 | if !validInput(arg) {
32 | reply := "无效的输入,请使用: /curconv 货币源 货币目标 [金额]"
33 | SendMessage(message.Chat.ID, reply, message.MessageID, bot)
34 | return
35 | }
36 | }
37 |
38 | sourceCurrency := args[0]
39 | targetCurrency := args[1]
40 | amount := "100" // 默认金额
41 |
42 | if len(args) == 3 {
43 | // 尝试解析 amount 参数为浮点数
44 | if _, err := strconv.ParseFloat(args[2], 64); err != nil {
45 | reply := "无效的金额,请输入一个有效的数字。用法: /curconv 货币源 货币目标 [金额]"
46 | SendMessage(message.Chat.ID, reply, message.MessageID, bot)
47 | return
48 | }
49 | amount = args[2]
50 | }
51 |
52 | log.Printf("Fetching conversion rate for %s to %s with amount %s", sourceCurrency, targetCurrency, amount)
53 | url := fmt.Sprintf(apiURL, sourceCurrency, targetCurrency, amount)
54 |
55 | resp, err := http.Get(url)
56 | if err != nil {
57 | log.Printf("Error fetching conversion rate: %v", err)
58 | SendMessage(message.Chat.ID, "获取汇率失败,请稍后再试。", message.MessageID, bot)
59 | return
60 | }
61 | defer resp.Body.Close()
62 |
63 | if resp.StatusCode != http.StatusOK {
64 | log.Printf("API request failed with status code: %d", resp.StatusCode)
65 | SendMessage(message.Chat.ID, "API请求失败,请稍后再试。", message.MessageID, bot)
66 | return
67 | }
68 |
69 | var result struct {
70 | Name string `json:"name"`
71 | Description string `json:"description"`
72 | Date string `json:"date"`
73 | Type string `json:"type"`
74 | Data struct {
75 | ConversionRate float64 `json:"conversionRate"`
76 | CrdhldBillAmt float64 `json:"crdhldBillAmt"`
77 | FxDate string `json:"fxDate"`
78 | TransCurr string `json:"transCurr"`
79 | CrdhldBillCurr string `json:"crdhldBillCurr"`
80 | TransAmt float64 `json:"transAmt"`
81 | ErrorCode string `json:"errorCode"`
82 | ErrorMessage string `json:"errorMessage"`
83 | } `json:"data"`
84 | }
85 |
86 | if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
87 | log.Printf("Error decoding API response: %v", err)
88 | SendMessage(message.Chat.ID, "解析汇率数据失败,请稍后再试。", message.MessageID, bot)
89 | return
90 | }
91 |
92 | // 处理API错误响应
93 | if result.Type == "error" {
94 | log.Printf("API error: %s", result.Data.ErrorMessage)
95 | reply := "货币不可用,请在 这里 选择有效的货币"
96 | SendMessage(message.Chat.ID, reply, message.MessageID, bot)
97 | return
98 | }
99 |
100 | var reply string
101 | if len(args) == 3 {
102 | reply = fmt.Sprintf(
103 | "%s (UTC)\n%s - %s 的汇率为 %.6f\n若您的金额为 %.2f %s,那么你可以兑换 %.2f %s",
104 | result.Date, result.Data.TransCurr, result.Data.CrdhldBillCurr,
105 | result.Data.ConversionRate, result.Data.TransAmt, result.Data.TransCurr,
106 | result.Data.CrdhldBillAmt, result.Data.CrdhldBillCurr,
107 | )
108 | } else {
109 | reply = fmt.Sprintf(
110 | "%s (UTC)\n%s - %s 的汇率为 %.6f",
111 | result.Date, result.Data.TransCurr, result.Data.CrdhldBillCurr,
112 | result.Data.ConversionRate,
113 | )
114 | }
115 |
116 | log.Printf("Sending response: %s", reply)
117 | SendMessage(message.Chat.ID, reply, message.MessageID, bot)
118 | }
119 |
120 | // SendMessage 发送文本消息
121 | func SendMessage(chatID int64, text string, messageID int, bot *tgbotapi.BotAPI) {
122 | msg := tgbotapi.NewMessage(chatID, text)
123 | msg.ParseMode = "HTML"
124 | msg.ReplyToMessageID = messageID // 设置回复消息ID
125 | _, err := bot.Send(msg)
126 | if err != nil {
127 | log.Printf("Error sending message: %v", err)
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/functions/getid/getid.go:
--------------------------------------------------------------------------------
1 | package getid
2 |
3 | import (
4 | "fmt"
5 |
6 | "AIOPrivacyBot/utils"
7 |
8 | tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
9 | )
10 |
11 | // HandleGetIDCommand handles the /getid command.
12 | func HandleGetIDCommand(message *tgbotapi.Message, bot *tgbotapi.BotAPI, configSuperAdmins []string) {
13 | var response string
14 | user := message.From
15 |
16 | isSuperAdmin := "否"
17 | for _, admin := range configSuperAdmins {
18 | if admin == fmt.Sprintf("%d", user.ID) {
19 | isSuperAdmin = "是"
20 | break
21 | }
22 | }
23 |
24 | if message.Chat.IsPrivate() {
25 | response = fmt.Sprintf(`个人ID信息:
26 | 个人用户名: %s
27 | 个人昵称: %s %s
28 | 个人ID: %d
29 | 超级管理员: %s`,
30 | user.UserName, user.FirstName, user.LastName, user.ID, isSuperAdmin)
31 | } else {
32 | chat := message.Chat
33 | response = fmt.Sprintf(`群聊ID信息:
34 | 群组名称: %s
35 | 群组类型: %s
36 | 群组ID: %d
37 |
38 | 个人ID信息:
39 | 个人用户名: %s
40 | 个人昵称: %s %s
41 | 个人ID: %d
42 | 超级管理员: %s`,
43 | chat.Title, chat.Type, chat.ID,
44 | user.UserName, user.FirstName, user.LastName, user.ID, isSuperAdmin)
45 | }
46 |
47 | utils.SendMessage(message.Chat.ID, response, message.MessageID, bot) // 传递 messageID
48 | }
49 |
--------------------------------------------------------------------------------
/functions/help/help.go:
--------------------------------------------------------------------------------
1 | package help
2 |
3 | import (
4 | "log"
5 |
6 | "AIOPrivacyBot/utils"
7 |
8 | tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
9 | )
10 |
11 | func SendHelpMessage(message *tgbotapi.Message, bot *tgbotapi.BotAPI) {
12 | chatID := message.Chat.ID
13 | messageID := message.MessageID // 获取原始消息ID
14 | photoPath := "functions/help/help_image.png"
15 | photoCaption := `欢迎使用 @AIOPrivacyBot !一个尝试在保护用户隐私的前提下,提供各类功能的类命令行Telegram机器人。
16 |
17 | 用法
18 |
19 | 您可以向机器人发送 /help
来获取有关帮助文档。
20 |
21 | 请通过:https://github.com/AIOPrivacy/AIOPrivacyBot?tab=readme-ov-file#%E7%94%A8%E6%B3%95访问完整用法支持文档
22 |
23 | 24 | 聊天触发类 25 | /play 动作触发功能 26 | /ask AI提问学术问题 27 | /getid 用户查看ID信息功能 28 | /status 查看系统信息 29 | /admins 召唤所有管理员 30 | /string 字符串编码 31 | /num 数字进制转换 32 | /curconv 货币转换,汇率查询 33 | /color 颜色转换&色卡推荐 34 | 支持51 | ↑ 点击展开详细命令说明 52 | 53 | ⚠ 机器人正在测试中,如果遇到bug请及时提出! 54 | 55 | 欢迎加入用户交流群:https://t.me/AIOPrivacy` 56 | 57 | // 使用 utils.SendPhotoWithCaption 发送带有文字的图片并回复到用户 58 | err := utils.SendPhotoWithCaption(chatID, messageID, photoPath, photoCaption, bot) 59 | if err != nil { 60 | log.Printf("Error sending help image: %v", err) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /functions/help/help_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AIOPrivacy/AIOPrivacyBot/1f4d659016e5587459dbb515fb110dcf31dd2644/functions/help/help_image.png -------------------------------------------------------------------------------- /functions/num/num.go: -------------------------------------------------------------------------------- 1 | package num 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/big" 7 | 8 | "AIOPrivacyBot/utils" 9 | 10 | tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5" 11 | ) 12 | 13 | // HandleNumCommand 处理 /num 命令 14 | func HandleNumCommand(message *tgbotapi.Message, bot *tgbotapi.BotAPI) { 15 | input := message.CommandArguments() 16 | var decimalValue = new(big.Int) 17 | 18 | // 自动识别进制并转换为十进制 19 | decimalValue, success := decimalValue.SetString(input, 0) 20 | 21 | if !success { 22 | log.Printf("Error parsing number: %s", input) 23 | utils.SendMessage(message.Chat.ID, "输入的数字格式不正确,请检查后重新输入。", message.MessageID, bot) 24 | return 25 | } 26 | 27 | // 转换为其他进制表示 28 | binaryValue := decimalValue.Text(2) 29 | octalValue := decimalValue.Text(8) 30 | hexValue := decimalValue.Text(16) 31 | 32 | // 构建回复消息 33 | replyText := fmt.Sprintf( 34 | "输入 (Input):RGB
、16进制
、颜色名称
,用法举例: 35 |/color@AIOPrivacyBot #ffffff
36 |/color@AIOPrivacyBot 255 255 255
37 |/color@AIOPrivacyBot Blue
38 | 39 | Inline 模式触发类 40 | 各类网址的安全过滤/检测 41 | 机器人 Inline 模式下运作,您可以这样调用: 42 |@AIOPrivacyBot -check https://www.amazon.com/dp/exampleProduct/ref=sxin_0_pb
43 | 内容网站的内容下载存储到 telegraph 44 | 机器人 Inline 模式下运作,您可以这样调用: 45 |@AIOPrivacyBot -view https://www.52pojie.cn/thread-143136-1-1.html
46 | 47 | 其他触发类 48 | 回复机器人随机触发 AI聊天触发功能 49 | 70% 概率的出现 笨笨的猫娘 AI 玩耍! 50 |
%s
\n十进制 (Decimal): %s
\n二进制 (Binary): %s
\n八进制 (Octal): %s
\n十六进制 (Hex): %s
",
35 | input, decimalValue.String(), binaryValue, octalValue, hexValue,
36 | )
37 |
38 | utils.SendMessage(message.Chat.ID, replyText, message.MessageID, bot)
39 | }
40 |
--------------------------------------------------------------------------------
/functions/play/play.go:
--------------------------------------------------------------------------------
1 | package play
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "regexp"
7 | "strings"
8 |
9 | "AIOPrivacyBot/utils"
10 |
11 | tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
12 | )
13 |
14 | func HandlePlayCommand(message *tgbotapi.Message, bot *tgbotapi.BotAPI) {
15 | if message.Chat.IsPrivate() {
16 | err := utils.SendMessage(message.Chat.ID, "此命令只可用于群组", message.MessageID, bot)
17 | if err != nil {
18 | log.Printf("Error sending message: %v", err)
19 | }
20 | return
21 | }
22 |
23 | if message.CommandArguments() == "" {
24 | err := utils.SendMessage(message.Chat.ID,
25 | `需要使用参数
26 | 以下演示都以A回复B模拟!
27 |
28 | 主动模式
29 | /play@AIOPrivacyBot -t xxxxx
可以成功触发 A xxxxx了 B!
30 | /play@AIOPrivacyBot -t xxxxx yyyyy
可以成功触发 A xxxxx B yyyyy
31 |
32 | 被动模式
33 | /play@AIOPrivacyBot -p xxxxx
可以成功触发 A 被 B xxxxx了!
34 | /play@AIOPrivacyBot -p xxxxx yyyyy
可以成功触发 B xxxxx A yyyyy
35 |
36 | 注意:可以使用英文 ' 或 " 包括发送内容来高于空格优先级,例如 /play@AIOPrivacyBot -p "xx xxx" "yy yy y"
`,
37 | message.MessageID, bot)
38 | if err != nil {
39 | log.Printf("Error sending message: %v", err)
40 | }
41 | return
42 | }
43 |
44 | args := parseArguments(message.CommandArguments())
45 |
46 | if len(args) < 2 {
47 | err := utils.SendMessage(message.Chat.ID,
48 | `需要更多参数
49 | 以下演示都以A回复B模拟!
50 |
51 | 主动模式
52 | /play@AIOPrivacyBot -t xxxxx
可以成功触发 A xxxxx了 B!
53 | /play@AIOPrivacyBot -t xxxxx yyyyy
可以成功触发 A xxxxx B yyyyy
54 |
55 | 被动模式
56 | /play@AIOPrivacyBot -p xxxxx
可以成功触发 A 被 B xxxxx了!
57 | /play@AIOPrivacyBot -p xxxxx yyyyy
可以成功触发 B xxxxx A yyyyy
58 |
59 | 注意:可以使用英文 ' 或 " 包括发送内容来高于空格优先级,例如 /play@AIOPrivacyBot -p "xx xxx" "yy yy y"
`,
60 | message.MessageID, bot)
61 |
62 | if err != nil {
63 | log.Printf("Error sending message: %v", err)
64 | }
65 | return
66 | }
67 |
68 | senderMention := fmt.Sprintf("%s %s", message.From.ID, message.From.FirstName, message.From.LastName)
69 | targetMention := senderMention
70 | if message.ReplyToMessage != nil {
71 | targetMention = fmt.Sprintf("%s %s", message.ReplyToMessage.From.ID, message.ReplyToMessage.From.FirstName, message.ReplyToMessage.From.LastName)
72 | }
73 |
74 | commandType := args[0]
75 | action := args[1]
76 | extra := ""
77 | if len(args) > 2 {
78 | extra = args[2]
79 | }
80 |
81 | var response string
82 | if commandType == "-t" {
83 | if extra == "" {
84 | response = fmt.Sprintf("%s %s了 %s!", senderMention, action, targetMention)
85 | } else {
86 | response = fmt.Sprintf("%s %s %s %s", senderMention, action, targetMention, extra)
87 | }
88 | } else if commandType == "-p" {
89 | if extra == "" {
90 | response = fmt.Sprintf("%s 被 %s %s了!", senderMention, targetMention, action)
91 | } else {
92 | response = fmt.Sprintf("%s %s %s %s", targetMention, action, senderMention, extra)
93 | }
94 | }
95 |
96 | err := utils.SendMessage(message.Chat.ID, response, message.MessageID, bot)
97 | if err != nil {
98 | log.Printf("Error sending message: %v", err)
99 | }
100 | }
101 |
102 | func parseArguments(command string) []string {
103 | re := regexp.MustCompile(`'[^']*'|"[^"]*"|\S+`)
104 | matches := re.FindAllString(command, -1)
105 | for i := range matches {
106 | matches[i] = strings.Trim(matches[i], `"'`)
107 | }
108 | return matches
109 | }
110 |
--------------------------------------------------------------------------------
/functions/setting/setting.go:
--------------------------------------------------------------------------------
1 | package setting
2 |
3 | import (
4 | "database/sql"
5 | "fmt"
6 | "log"
7 | "regexp"
8 | "strconv"
9 | "strings"
10 |
11 | tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
12 | )
13 |
14 | var availableFeatures = []string{
15 | "help", "play", "ask", "getid", "status", "admins",
16 | "num", "string", "curconv", "color", "setting", "ai_chat",
17 | }
18 |
19 | // Features that cannot be disabled
20 | var nonDisablableFeatures = []string{"setting"}
21 |
22 | func IsFeatureEnabled(db *sql.DB, groupID int64, featureName string) bool {
23 | for _, feature := range nonDisablableFeatures {
24 | if feature == featureName {
25 | return true
26 | }
27 | }
28 |
29 | var featureOffList string
30 | err := db.QueryRow("SELECT feature_off FROM group_setting WHERE groupid = ?", groupID).Scan(&featureOffList)
31 | if err != nil && err != sql.ErrNoRows {
32 | log.Printf("Error querying feature status: %v", err)
33 | return true
34 | }
35 |
36 | if featureOffList == "" {
37 | return true
38 | }
39 |
40 | disabledFeatures := strings.Split(featureOffList, ",")
41 | for _, feature := range disabledFeatures {
42 | if feature == featureName {
43 | return false
44 | }
45 | }
46 |
47 | return true
48 | }
49 |
50 | func HandleSettingCommand(db *sql.DB, message *tgbotapi.Message, bot *tgbotapi.BotAPI, superAdmins []string) {
51 | if !isAdmin(bot, message, superAdmins) {
52 | msg := tgbotapi.NewMessage(message.Chat.ID, "你没有权限修改设置")
53 | bot.Send(msg)
54 | return
55 | }
56 |
57 | args := strings.Fields(message.CommandArguments())
58 | if len(args) < 2 || (len(args) == 3 && args[0] != "enable") {
59 | msg := tgbotapi.NewMessage(message.Chat.ID, "用法: /setting %s
\nPunycode:%s
", encoded, punycode)
66 | }
67 |
68 | func handleCRC(str string) string {
69 | crc32q := crc32.MakeTable(0xD5828281)
70 | crc := crc32.Checksum([]byte(str), crc32q)
71 | return fmt.Sprintf("CRC:%d
", crc)
72 | }
73 |
74 | func handleBase(str string) string {
75 | base64Encoded := base64.StdEncoding.EncodeToString([]byte(str))
76 | base32Encoded := base32.StdEncoding.EncodeToString([]byte(str))
77 | base16Encoded := hex.EncodeToString([]byte(str))
78 |
79 | return fmt.Sprintf("Base:\nBase64:%s
\nBase32:%s
\nBase16:%s
", base64Encoded, base32Encoded, base16Encoded)
80 | }
81 |
82 | func handleUnicode(str string) string {
83 | utf8Encoded := toUTF8Hex(str)
84 | utf16Encoded := toUTF16Hex(str)
85 | utf32Encoded := toUTF32Hex(str)
86 |
87 | return fmt.Sprintf("Unicode:\nUTF-8:%s
\nUTF-16:%s
\nUTF-32:%s
", utf8Encoded, utf16Encoded, utf32Encoded)
88 | }
89 |
90 | func handleASCII(str string) string {
91 | var asciiCodes []string
92 | for _, r := range str {
93 | asciiCodes = append(asciiCodes, fmt.Sprintf("%d", r))
94 | }
95 | return fmt.Sprintf("ASCII十进制:%s
", strings.Join(asciiCodes, " "))
96 | }
97 |
98 | func handleMD5(str string) string {
99 | hash := md5.Sum([]byte(str))
100 | return fmt.Sprintf("MD5:%s
", hex.EncodeToString(hash[:]))
101 | }
102 |
103 | func handleSHA(str string) string {
104 | sha1Hash := sha1.Sum([]byte(str))
105 | sha256Hash := sha256.Sum256([]byte(str))
106 | sha512Hash := sha512.Sum512([]byte(str))
107 |
108 | return fmt.Sprintf("SHA:\nSHA1:%s
\nSHA256:%s
\nSHA512:%s
", hex.EncodeToString(sha1Hash[:]), hex.EncodeToString(sha256Hash[:]), hex.EncodeToString(sha512Hash[:]))
109 | }
110 |
111 | func toUTF8Hex(s string) string {
112 | var result strings.Builder
113 | for _, r := range s {
114 | result.WriteString(fmt.Sprintf("%02X", r))
115 | }
116 | return result.String()
117 | }
118 |
119 | func toUTF16Hex(s string) string {
120 | var result strings.Builder
121 | for _, r := range s {
122 | result.WriteString(fmt.Sprintf("%04X", r))
123 | }
124 | return result.String()
125 | }
126 |
127 | func toUTF32Hex(s string) string {
128 | var result strings.Builder
129 | for _, r := range s {
130 | result.WriteString(fmt.Sprintf("%08X", r))
131 | }
132 | return result.String()
133 | }
134 |
--------------------------------------------------------------------------------
/functions/view/export.js:
--------------------------------------------------------------------------------
1 | const webUrl = window.location.href;
2 | const headline = document.title;
3 | const host = location.host;
4 |
5 | const sites = [
6 | { "host": "blog.csdn.net", "el": "article.baidu_pl", "cut_str": "_" },
7 | { "host": "www.jianshu.com", "el": "article._2rhmJa", "cut_str": " - " },
8 | { "host": "juejin.cn", "el": ".article-viewer.markdown-body.result", "cut_str": " - " },
9 | { "host": "zhuanlan.zhihu.com", "el": ".Post-RichTextContainer", "cut_str": " - " },
10 | { "host": "www.cnblogs.com", "el": "#cnblogs_post_body", "cut_str": " - " },
11 | { "host": "www.jb51.net", "el": "#content", "cut_str": "_" },
12 | { "host": "blog.51cto.com", "el": "#result", "cut_str": "_" },
13 | { "host": "www.pianshen.com", "el": ".blogpost-body", "cut_str": " - " },
14 | { "host": "www.360doc.com", "el": "#artContent", "cut_str": "" },
15 | { "host": "baijiahao.baidu.com", "el": "div[data-testid='article']", "cut_str": "" },
16 | { "host": "jingyan.baidu.com", "el": ".exp-content-outer", "cut_str": "-" },
17 | { "host": "www.52pojie.cn", "el": ".t_f", "cut_str": " - " },
18 | { "host": "cloud.tencent.com", "el": ".mod-content__markdown", "cut_str": "-" },
19 | { "host": "developer.aliyun.com", "el": ".content-wrapper", "cut_str": "-" },
20 | { "host": "huaweicloud.csdn.net", "el": ".main-content", "cut_str": "_" },
21 | { "host": "www.bilibili.com", "el": "#read-article-holder", "cut_str": " - " },
22 | { "host": "weibo.com", "el": ".main_editor", "cut_str": "" },
23 | { "host": "www.weibo.com", "el": ".main_editor", "cut_str": "" },
24 | { "host": "mp.weixin.qq.com", "el": "#js_content", "cut_str": "" },
25 | { "host": "segmentfault.com", "el": ".article.fmt.article-content", "cut_str": "- SegmentFault 思否" },
26 | { "host": "www.qinglite.cn", "el": ".markdown-body", "cut_str": "-" },
27 | { "host": "www.manongjc.com", "el": "#code_example", "cut_str": " - " }
28 |
29 | ]
30 |
31 | const cutTitle = (title, cut_str) => {
32 | try {
33 | const newTitle = title.split(cut_str)[0];
34 | return newTitle;
35 | }
36 | catch (e) {
37 | console.log(e);
38 | return title;
39 | }
40 | }
41 |
42 | const domToNode = (domNode) => {
43 | if (domNode.nodeType == domNode.TEXT_NODE) {
44 | return domNode.data;
45 | }
46 | if (domNode.nodeType != domNode.ELEMENT_NODE) {
47 | return false;
48 | }
49 | let nodeElement = {};
50 | nodeElement.tag = domNode.tagName.toLowerCase();
51 | for (const attr of domNode.attributes) {
52 | if (attr.name == 'href' || attr.name == 'src') {
53 | if (!nodeElement.attrs) {
54 | nodeElement.attrs = {};
55 | }
56 | nodeElement.attrs[attr.name] = attr.value;
57 | }
58 | }
59 | if (domNode.childNodes.length > 0) {
60 | nodeElement.children = [];
61 | for (const child of domNode.childNodes) {
62 | nodeElement.children.push(domToNode(child));
63 | }
64 | }
65 | return nodeElement;
66 | }
67 |
68 | const getData = () => {
69 | let new_headline;
70 |
71 | for (const site of sites) {
72 | if (!host.endsWith(site.host)) continue;
73 | const cut = site.cut_str;
74 |
75 | if (cut != '') {
76 | new_headline = cutTitle(headline, cut);
77 | } else {
78 | new_headline = document.title;
79 | }
80 |
81 | const ele = document.querySelector(site.el)
82 | const rootNode = domToNode(ele);
83 | rootNode.children.push(`\n\n本文转自 ${webUrl} ,如有侵权,请联系删除。`)
84 | rootNode.children.push(`\n本文接到 @AIOPrivacyBot 用户的Inline请求,用户要求保护其隐私,因此将其转换为Telegraph文档发送。`)
85 | const data = {
86 | title: new_headline,
87 | node: JSON.stringify(rootNode.children),
88 | };
89 |
90 | return data;
91 | }
92 |
93 | return null;
94 | }
95 |
96 | getData();
97 |
--------------------------------------------------------------------------------
/functions/view/view.go:
--------------------------------------------------------------------------------
1 | package view
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | "fmt"
7 | "log"
8 | "net/http"
9 | "os"
10 | "strings"
11 |
12 | tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
13 | "github.com/playwright-community/playwright-go"
14 | )
15 |
16 | const telegraphAPI = "https://api.telegra.ph/createPage"
17 |
18 | var accessToken string
19 |
20 | type CreatePageResponse struct {
21 | OK bool `json:"ok"`
22 | Result Result `json:"result"`
23 | }
24 |
25 | type Result struct {
26 | URL string `json:"url"`
27 | }
28 |
29 | type PageData struct {
30 | Title string `json:"title"`
31 | Node string `json:"node"`
32 | }
33 |
34 | func Init(tgToken string) {
35 | accessToken = tgToken
36 | }
37 |
38 | func HandleViewCommand(inlineQuery *tgbotapi.InlineQuery, bot *tgbotapi.BotAPI) {
39 | query := strings.TrimSpace(inlineQuery.Query)
40 | if !strings.HasPrefix(query, "-view") {
41 | return
42 | }
43 |
44 | url := strings.TrimPrefix(query, "-view")
45 | url = strings.TrimSpace(url)
46 | if url == "" {
47 | return
48 | }
49 |
50 | pageData, err := fetchPageData(url)
51 | if err != nil {
52 | log.Printf("Error fetching page data: %v", err)
53 | return
54 | }
55 |
56 | if pageData.Title == "" || pageData.Node == "" {
57 | results := []interface{}{
58 | tgbotapi.NewInlineQueryResultArticleHTML(
59 | inlineQuery.ID,
60 | "链接不支持",
61 | "不支持此链接,请使用 -check功能",
62 | ),
63 | }
64 | inlineConf := tgbotapi.InlineConfig{
65 | InlineQueryID: inlineQuery.ID,
66 | Results: results,
67 | IsPersonal: true,
68 | }
69 | if _, err := bot.Request(inlineConf); err != nil {
70 | log.Printf("Error sending inline query response: %v", err)
71 | }
72 | return
73 | }
74 |
75 | telegraphURL, err := postToTelegraph(pageData)
76 | if err != nil {
77 | log.Printf("Error posting to Telegraph: %v", err)
78 | return
79 | }
80 |
81 | results := []interface{}{
82 | tgbotapi.NewInlineQueryResultArticleHTML(
83 | inlineQuery.ID,
84 | "URL Clean & View",
85 | fmt.Sprintf("我通过 @AIOPrivacyBot 分享了文章:《%s》\n\n查看完整内容请点击:这里", pageData.Title, telegraphURL),
86 | ),
87 | }
88 |
89 | inlineConf := tgbotapi.InlineConfig{
90 | InlineQueryID: inlineQuery.ID,
91 | Results: results,
92 | IsPersonal: true,
93 | }
94 |
95 | if _, err := bot.Request(inlineConf); err != nil {
96 | log.Printf("Error sending inline query response: %v", err)
97 | }
98 | }
99 |
100 | func fetchPageData(targetURL string) (PageData, error) {
101 | // 自动安装浏览器依赖
102 | if err := playwright.Install(); err != nil {
103 | return PageData{}, fmt.Errorf("could not install browsers: %w", err)
104 | }
105 |
106 | pw, err := playwright.Run()
107 | if err != nil {
108 | return PageData{}, fmt.Errorf("could not start playwright: %w", err)
109 | }
110 | defer pw.Stop()
111 |
112 | browser, err := pw.Chromium.Launch()
113 | if err != nil {
114 | return PageData{}, fmt.Errorf("could not launch browser: %w", err)
115 | }
116 | defer browser.Close()
117 |
118 | page, err := browser.NewPage()
119 | if err != nil {
120 | return PageData{}, fmt.Errorf("could not create page: %w", err)
121 | }
122 |
123 | if _, err = page.Goto(targetURL); err != nil {
124 | return PageData{}, fmt.Errorf("could not goto: %w", err)
125 | }
126 |
127 | jsFilePath := "./functions/view/export.js"
128 | jsCode, err := os.ReadFile(jsFilePath)
129 | if err != nil {
130 | return PageData{}, fmt.Errorf("could not read JavaScript file: %w", err)
131 | }
132 |
133 | result, err := page.Evaluate(string(jsCode))
134 | if err != nil {
135 | return PageData{}, fmt.Errorf("could not execute JavaScript: %w", err)
136 | }
137 |
138 | if result == nil {
139 | return PageData{}, nil // 返回空的 PageData 以便上层处理
140 | }
141 |
142 | data := result.(map[string]interface{})
143 | title, titleOk := data["title"].(string)
144 | node, nodeOk := data["node"].(string)
145 | if !titleOk || !nodeOk {
146 | return PageData{}, nil // 返回空的 PageData 以便上层处理
147 | }
148 |
149 | return PageData{
150 | Title: title,
151 | Node: node,
152 | }, nil
153 | }
154 |
155 | func postToTelegraph(pageData PageData) (string, error) {
156 | data := map[string]interface{}{
157 | "title": pageData.Title,
158 | "author_name": "AIOPrivacyBot",
159 | "content": pageData.Node,
160 | "access_token": accessToken,
161 | }
162 |
163 | jsonData, err := json.Marshal(data)
164 | if err != nil {
165 | return "", fmt.Errorf("error marshalling JSON: %w", err)
166 | }
167 |
168 | resp, err := http.Post(telegraphAPI, "application/json", bytes.NewBuffer(jsonData))
169 | if err != nil {
170 | return "", fmt.Errorf("error sending request: %w", err)
171 | }
172 | defer resp.Body.Close()
173 |
174 | var response CreatePageResponse
175 | if err := json.NewDecoder(resp.Body).Decode(&response); err != nil {
176 | return "", fmt.Errorf("error decoding response: %w", err)
177 | }
178 |
179 | if response.OK {
180 | return response.Result.URL, nil
181 | } else {
182 | return "", fmt.Errorf("failed to create page")
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module AIOPrivacyBot
2 |
3 | go 1.22.4
4 |
5 | require (
6 | github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1
7 | golang.org/x/net v0.17.0
8 | )
9 |
10 | require (
11 | github.com/deckarep/golang-set/v2 v2.6.0 // indirect
12 | github.com/go-jose/go-jose/v3 v3.0.3 // indirect
13 | github.com/go-ole/go-ole v1.2.6 // indirect
14 | github.com/go-stack/stack v1.8.1 // indirect
15 | github.com/mattn/go-sqlite3 v1.14.22 // indirect
16 | github.com/playwright-community/playwright-go v0.4501.1 // indirect
17 | github.com/russross/blackfriday/v2 v2.1.0 // indirect
18 | github.com/shirou/gopsutil v3.21.11+incompatible // indirect
19 | github.com/tklauser/go-sysconf v0.3.14 // indirect
20 | github.com/tklauser/numcpus v0.8.0 // indirect
21 | github.com/yusufpapurcu/wmi v1.2.4 // indirect
22 | go.uber.org/multierr v1.11.0 // indirect
23 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
24 | golang.org/x/image v0.19.0 // indirect
25 | golang.org/x/sys v0.22.0 // indirect
26 | golang.org/x/text v0.17.0 // indirect
27 | )
28 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2 | github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
3 | github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
4 | github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
5 | github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
6 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
7 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
8 | github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
9 | github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
10 | github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1 h1:wG8n/XJQ07TmjbITcGiUaOtXxdrINDz1b0J1w0SzqDc=
11 | github.com/go-telegram-bot-api/telegram-bot-api/v5 v5.5.1/go.mod h1:A2S0CWkNylc2phvKXWBBdD3K0iGnDBGbzRpISP2zBl8=
12 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
13 | github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
14 | github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
15 | github.com/playwright-community/playwright-go v0.4501.1 h1:kz8SIfR6nEI8blk77nTVD0K5/i37QP5rY/o8a1fG+4c=
16 | github.com/playwright-community/playwright-go v0.4501.1/go.mod h1:bpArn5TqNzmP0jroCgw4poSOG9gSeQg490iLqWAaa7w=
17 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
18 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
19 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
20 | github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
21 | github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
22 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
23 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
24 | github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
25 | github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
26 | github.com/tklauser/numcpus v0.8.0 h1:Mx4Wwe/FjZLeQsK/6kt2EOepwwSl7SmJrK5bV/dXYgY=
27 | github.com/tklauser/numcpus v0.8.0/go.mod h1:ZJZlAY+dmR4eut8epnzf0u/VwodKmryxR8txiloSqBE=
28 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
29 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
30 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
31 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
32 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
33 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
34 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
35 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
36 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
37 | golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
38 | golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ=
39 | golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys=
40 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
41 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
42 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
43 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
44 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
45 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
46 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
47 | golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
48 | golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
49 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
50 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
51 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
52 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
53 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3 h1:7TYNF4UdlohbFwpNH04CoPMp1cHUZgO1Ebq5r2hIjfo=
54 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
55 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
56 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
57 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
58 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
59 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
60 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
61 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
62 | golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
63 | golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
64 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
65 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
66 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
67 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
68 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
69 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
70 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
71 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
72 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
73 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
74 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
75 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
76 | golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
77 | golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
78 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
79 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
80 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
81 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
82 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
83 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
84 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
85 |
--------------------------------------------------------------------------------
/images/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AIOPrivacy/AIOPrivacyBot/1f4d659016e5587459dbb515fb110dcf31dd2644/images/logo.png
--------------------------------------------------------------------------------
/images/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AIOPrivacy/AIOPrivacyBot/1f4d659016e5587459dbb515fb110dcf31dd2644/images/screenshot.png
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "database/sql"
5 | "encoding/json"
6 | "fmt"
7 | "log"
8 | "math/rand"
9 | "os"
10 | "strings"
11 | "time"
12 |
13 | tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
14 | _ "github.com/mattn/go-sqlite3"
15 |
16 | "AIOPrivacyBot/functions/admins"
17 | "AIOPrivacyBot/functions/ai_chat"
18 | "AIOPrivacyBot/functions/ask"
19 | "AIOPrivacyBot/functions/check"
20 | "AIOPrivacyBot/functions/color"
21 | "AIOPrivacyBot/functions/curconv"
22 | "AIOPrivacyBot/functions/getid"
23 | "AIOPrivacyBot/functions/help"
24 | "AIOPrivacyBot/functions/num"
25 | "AIOPrivacyBot/functions/play"
26 | "AIOPrivacyBot/functions/setting"
27 | "AIOPrivacyBot/functions/status"
28 | "AIOPrivacyBot/functions/stringcalc"
29 | "AIOPrivacyBot/functions/view"
30 | )
31 |
32 | type Config struct {
33 | Token string `json:"token"`
34 | SuperAdmins []string `json:"super_admins"`
35 | SafeBrowsingAPIKey string `json:"safe_browsing_api_key"`
36 | TelegraphAccessToken string `json:"telegraph_access_token"`
37 | }
38 |
39 | var (
40 | botUsername string
41 | config Config
42 | db *sql.DB
43 | )
44 |
45 | func main() {
46 |
47 | // 确保 /tmp 目录存在
48 | err := os.MkdirAll("/tmp", os.ModePerm)
49 | if err != nil {
50 | log.Fatalf("Error creating /tmp directory: %v", err)
51 | }
52 |
53 | file, err := os.Open("config.json")
54 | if err != nil {
55 | log.Fatalf("Error opening config file: %v", err)
56 | }
57 | defer file.Close()
58 |
59 | decoder := json.NewDecoder(file)
60 | err = decoder.Decode(&config)
61 | if err != nil {
62 | log.Fatalf("Error decoding config file: %v", err)
63 | }
64 |
65 | bot, err := tgbotapi.NewBotAPI(config.Token)
66 | if err != nil {
67 | log.Fatalf("Error creating new bot: %v", err)
68 | }
69 |
70 | botUsername = bot.Self.UserName
71 | log.Printf("Authorized on account %s", botUsername)
72 |
73 | // Initialize SQLite database
74 | initDatabase()
75 |
76 | // Initialize check package with SafeBrowsingAPIKey
77 | check.Init(config.SafeBrowsingAPIKey)
78 |
79 | // Initialize view package with Telegraph access token
80 | view.Init(config.TelegraphAccessToken)
81 |
82 | // 设置命令
83 | setBotCommands(bot)
84 |
85 | u := tgbotapi.NewUpdate(0)
86 | u.Timeout = 60
87 |
88 | updates := bot.GetUpdatesChan(u)
89 |
90 | for update := range updates {
91 | go handleUpdate(update, bot)
92 | }
93 | }
94 |
95 | func initDatabase() {
96 | var err error
97 | db, err = sql.Open("sqlite3", "./bot.db")
98 | if err != nil {
99 | log.Fatalf("Error opening database: %v", err)
100 | }
101 |
102 | _, err = db.Exec(`CREATE TABLE IF NOT EXISTS group_setting (
103 | groupid INTEGER PRIMARY KEY,
104 | feature_off TEXT,
105 | optional_features TEXT,
106 | value_ai_chat INTEGER
107 | )`)
108 | if err != nil {
109 | log.Fatalf("Error creating table: %v", err)
110 | }
111 |
112 | log.Println("Database initialized successfully")
113 | }
114 |
115 | func handleUpdate(update tgbotapi.Update, bot *tgbotapi.BotAPI) {
116 | if update.Message != nil {
117 | log.Printf("Received message from %s: %s", update.Message.From.UserName, update.Message.Text)
118 | processMessage(update.Message, bot)
119 | } else if update.InlineQuery != nil {
120 | processInlineQuery(update.InlineQuery, bot)
121 | } else if update.CallbackQuery != nil {
122 | admins.HandleCallbackQuery(update.CallbackQuery, bot)
123 | }
124 | }
125 |
126 | func processMessage(message *tgbotapi.Message, bot *tgbotapi.BotAPI) {
127 | log.Printf("Processing message from %s: %s", message.From.UserName, message.Text)
128 |
129 | if message.IsCommand() {
130 | // 判断是否启用了该命令功能
131 | if !setting.IsFeatureEnabled(db, message.Chat.ID, message.Command()) {
132 | // msg := tgbotapi.NewMessage(message.Chat.ID, "该功能已被管理员关闭")
133 | // sentMsg, _ := bot.Send(msg)
134 | time.AfterFunc(5*time.Second, func() {
135 | // bot.DeleteMessage(tgbotapi.NewDeleteMessage(message.Chat.ID, sentMsg.MessageID))
136 | })
137 | return
138 | }
139 |
140 | command := message.Command()
141 | if command == "help" && (message.Chat.IsPrivate() || strings.Contains(message.Text, fmt.Sprintf("@%s", botUsername))) {
142 | help.SendHelpMessage(message, bot)
143 | } else if command == "play" && strings.Contains(message.Text, fmt.Sprintf("@%s", botUsername)) {
144 | play.HandlePlayCommand(message, bot)
145 | } else if command == "ask" && (message.Chat.IsPrivate() || strings.Contains(message.Text, fmt.Sprintf("@%s", botUsername))) {
146 | ask.HandleAskCommand(message, bot)
147 | } else if command == "getid" && (message.Chat.IsPrivate() || strings.Contains(message.Text, fmt.Sprintf("@%s", botUsername))) {
148 | getid.HandleGetIDCommand(message, bot, config.SuperAdmins)
149 | } else if command == "status" && (message.Chat.IsPrivate() || strings.Contains(message.Text, fmt.Sprintf("@%s", botUsername))) {
150 | status.HandleStatusCommand(message, bot)
151 | } else if command == "admins" && (message.Chat.IsGroup() || message.Chat.IsSuperGroup()) {
152 | admins.HandleAdminsCommand(message, bot)
153 | } else if command == "num" && (message.Chat.IsPrivate() || strings.Contains(message.Text, fmt.Sprintf("@%s", botUsername))) {
154 | num.HandleNumCommand(message, bot)
155 | } else if command == "string" && (message.Chat.IsPrivate() || strings.Contains(message.Text, fmt.Sprintf("@%s", botUsername))) {
156 | stringcalc.HandleStringCommand(message, bot)
157 | } else if command == "curconv" && (message.Chat.IsPrivate() || strings.Contains(message.Text, fmt.Sprintf("@%s", botUsername))) {
158 | curconv.HandleCurconvCommand(message, bot)
159 | } else if command == "color" && (message.Chat.IsPrivate() || strings.Contains(message.Text, fmt.Sprintf("@%s", botUsername))) {
160 | color.HandleColorCommand(message, bot)
161 | } else if command == "setting" {
162 | setting.HandleSettingCommand(db, message, bot, config.SuperAdmins)
163 | }
164 | } else if (message.Chat.IsGroup() || message.Chat.IsSuperGroup()) && isReplyToBot(message) && shouldTriggerResponse(message.Chat.ID) {
165 | ai_chat.HandleAIChat(message, bot)
166 | }
167 | }
168 |
169 | func processInlineQuery(inlineQuery *tgbotapi.InlineQuery, bot *tgbotapi.BotAPI) {
170 | if strings.HasPrefix(inlineQuery.Query, "-view") {
171 | view.HandleViewCommand(inlineQuery, bot)
172 | } else if strings.HasPrefix(inlineQuery.Query, "-check") {
173 | check.HandleInlineQuery(inlineQuery, bot)
174 | }
175 | }
176 |
177 | func isReplyToBot(message *tgbotapi.Message) bool {
178 | if message.ReplyToMessage != nil && message.ReplyToMessage.From.UserName == botUsername {
179 | return true
180 | }
181 | return false
182 | }
183 |
184 | func shouldTriggerResponse(groupID int64) bool {
185 | var triggerValue int
186 | err := db.QueryRow("SELECT value_ai_chat FROM group_setting WHERE groupid = ?", groupID).Scan(&triggerValue)
187 | if err != nil {
188 | log.Printf("Error fetching AI chat trigger value: %v", err)
189 | triggerValue = 0 // Default value
190 | }
191 |
192 | log.Printf("AI Chat Trigger Value: %d", triggerValue)
193 | rand.Seed(time.Now().UnixNano())
194 | randomValue := rand.Intn(100) + 1
195 | log.Printf("Random value generated: %d", randomValue)
196 |
197 | // Adjust this logic based on your expectation
198 | return randomValue <= triggerValue
199 | }
200 |
201 | func setBotCommands(bot *tgbotapi.BotAPI) {
202 | commands := []tgbotapi.BotCommand{
203 | {Command: "help", Description: "获取帮助信息"},
204 | {Command: "play", Description: "互动游玩"},
205 | {Command: "ask", Description: "提问AI"},
206 | {Command: "getid", Description: "获取ID"},
207 | {Command: "status", Description: "获取机器人状态"},
208 | {Command: "admins", Description: "召唤管理员"},
209 | {Command: "num", Description: "数字进制转换"},
210 | {Command: "string", Description: "字符串编码"},
211 | {Command: "curconv", Description: "货币汇率计算"},
212 | {Command: "color", Description: "颜色转换&色卡推荐"},
213 | {Command: "setting", Description: "管理群组设置"},
214 | }
215 |
216 | config := tgbotapi.NewSetMyCommands(commands...)
217 |
218 | _, err := bot.Request(config)
219 | if err != nil {
220 | log.Fatalf("Error setting bot commands: %v", err)
221 | }
222 |
223 | log.Println("Bot commands set successfully")
224 | }
225 |
--------------------------------------------------------------------------------
/utils/response.go:
--------------------------------------------------------------------------------
1 | package utils
2 |
3 | import (
4 | "log"
5 | "os"
6 |
7 | tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
8 | )
9 |
10 | // SendPhotoWithCaption 发送带有文字的图片
11 | func SendPhotoWithCaption(chatID int64, messageID int, photoPath, caption string, bot *tgbotapi.BotAPI) error {
12 | log.Printf("Sending photo with caption to chat ID %d: %s", chatID, caption)
13 |
14 | photoFile, err := os.Open(photoPath)
15 | if err != nil {
16 | log.Printf("Error opening photo: %v", err)
17 | return err
18 | }
19 | defer photoFile.Close()
20 |
21 | photo := tgbotapi.NewPhoto(chatID, tgbotapi.FileReader{
22 | Name: photoPath,
23 | Reader: photoFile,
24 | })
25 | photo.Caption = caption
26 | photo.ParseMode = "HTML" // 设置为 HTML
27 | photo.ReplyToMessageID = messageID // 设置回复消息ID
28 | _, err = bot.Send(photo)
29 | if err != nil {
30 | log.Printf("Error sending photo: %v", err)
31 | return err
32 | }
33 |
34 | log.Printf("Photo sent successfully to chat ID %d", chatID)
35 | return nil
36 | }
37 |
38 | // SendMessage 发送文本消息
39 | func SendMessage(chatID int64, text string, messageID int, bot *tgbotapi.BotAPI) error {
40 | log.Printf("Sending message to chat ID %d: %s", chatID, text)
41 | msg := tgbotapi.NewMessage(chatID, text)
42 | msg.ParseMode = "HTML"
43 | msg.ReplyToMessageID = messageID // 设置回复消息ID
44 | _, err := bot.Send(msg)
45 | if err != nil {
46 | log.Printf("Error sending message: %v", err)
47 | return err
48 | }
49 | log.Printf("Message sent successfully to chat ID %d", chatID)
50 | return nil
51 | }
52 |
53 | // SendMarkdownMessage 发送 Markdown 格式的文本消息,并回复到用户
54 | func SendMarkdownMessage(chatID int64, messageID int, text string, bot *tgbotapi.BotAPI) error {
55 | log.Printf("Sending Markdown message to chat ID %d: %s", chatID, text)
56 | msg := tgbotapi.NewMessage(chatID, text)
57 | msg.ParseMode = "Markdown" // 使用 Markdown 解析模式
58 | msg.ReplyToMessageID = messageID // 回复到原始消息
59 |
60 | _, err := bot.Send(msg)
61 | if err != nil {
62 | log.Printf("Error sending Markdown message: %v", err)
63 | return err
64 | }
65 | log.Printf("Markdown message sent successfully to chat ID %d", chatID)
66 | return nil
67 | }
68 |
69 | // SendMarkdownMessageWithInlineKeyboard 发送带有内联键盘的 Markdown 格式的消息
70 | func SendMarkdownMessageWithInlineKeyboard(chatID int64, messageID int, text string, buttons []tgbotapi.InlineKeyboardButton, bot *tgbotapi.BotAPI) error {
71 | log.Printf("Sending Markdown message with inline keyboard to chat ID %d: %s", chatID, text)
72 | msg := tgbotapi.NewMessage(chatID, text)
73 | msg.ParseMode = "Markdown"
74 | msg.ReplyToMessageID = messageID // 回复到原始消息
75 |
76 | keyboard := tgbotapi.NewInlineKeyboardMarkup(
77 | tgbotapi.NewInlineKeyboardRow(buttons...),
78 | )
79 | msg.ReplyMarkup = keyboard
80 |
81 | _, err := bot.Send(msg)
82 | if err != nil {
83 | log.Printf("Error sending Markdown message with inline keyboard: %v", err)
84 | return err
85 | }
86 | log.Printf("Markdown message with inline keyboard sent successfully to chat ID %d", chatID)
87 | return nil
88 | }
89 |
90 | // SendPlainTextMessage 发送纯文本消息
91 | func SendPlainTextMessage(chatID int64, text string, messageID int, bot *tgbotapi.BotAPI) error {
92 | log.Printf("Sending plain text message to chat ID %d: %s", chatID, text)
93 | msg := tgbotapi.NewMessage(chatID, text)
94 | msg.ParseMode = "" // 不使用任何解析模式
95 | msg.ReplyToMessageID = messageID // 回复到原始消息
96 | _, err := bot.Send(msg)
97 | if err != nil {
98 | log.Printf("Error sending plain text message: %v", err)
99 | return err
100 | }
101 | log.Printf("Plain text message sent successfully to chat ID %d", chatID)
102 | return nil
103 | }
104 |
--------------------------------------------------------------------------------