Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
71844deab1 | ||
|
|
55fed6389e | ||
|
|
8ce5c2e007 | ||
|
|
6ecfbd8385 | ||
|
|
6c2bfd72cd |
@@ -8,7 +8,6 @@ import (
|
||||
"github.com/gotd/td/tg"
|
||||
"github.com/krau/SaveAny-Bot/common"
|
||||
"github.com/krau/SaveAny-Bot/dao"
|
||||
"github.com/krau/SaveAny-Bot/storage"
|
||||
"github.com/krau/SaveAny-Bot/types"
|
||||
)
|
||||
|
||||
@@ -28,11 +27,11 @@ func handleFileMessage(ctx *ext.Context, update *ext.Update) error {
|
||||
ctx.Reply(update, ext.ReplyTextString("获取用户失败"), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
storages := storage.GetUserStorages(user.ChatID)
|
||||
if len(storages) == 0 {
|
||||
ctx.Reply(update, ext.ReplyTextString("无可用的存储"), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
// storages := storage.GetUserStorages(user.ChatID)
|
||||
// if len(storages) == 0 {
|
||||
// ctx.Reply(update, ext.ReplyTextString("无可用的存储"), nil)
|
||||
// return dispatcher.EndGroups
|
||||
// }
|
||||
|
||||
msg, err := ctx.Reply(update, ext.ReplyTextString("正在获取文件信息..."), nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -10,7 +11,6 @@ import (
|
||||
"github.com/gotd/td/tg"
|
||||
"github.com/krau/SaveAny-Bot/common"
|
||||
"github.com/krau/SaveAny-Bot/dao"
|
||||
"github.com/krau/SaveAny-Bot/storage"
|
||||
"github.com/krau/SaveAny-Bot/types"
|
||||
)
|
||||
|
||||
@@ -19,47 +19,47 @@ var (
|
||||
linkRegex = regexp.MustCompile(linkRegexString)
|
||||
)
|
||||
|
||||
func parseLink(ctx *ext.Context, link string) (chatID int64, messageID int, err error) {
|
||||
strSlice := strings.Split(link, "/")
|
||||
if len(strSlice) < 3 {
|
||||
return 0, 0, fmt.Errorf("链接格式错误: %s", link)
|
||||
}
|
||||
messageID, err = strconv.Atoi(strSlice[len(strSlice)-1])
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("无法解析消息 ID: %s", err)
|
||||
}
|
||||
if len(strSlice) == 3 {
|
||||
chatUsername := strSlice[1]
|
||||
linkChat, err := ctx.ResolveUsername(chatUsername)
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("解析用户名失败: %s", err)
|
||||
}
|
||||
if linkChat == nil {
|
||||
return 0, 0, fmt.Errorf("找不到该聊天: %s", chatUsername)
|
||||
}
|
||||
chatID = linkChat.GetID()
|
||||
} else if len(strSlice) == 4 {
|
||||
chatIDInt, err := strconv.Atoi(strSlice[2])
|
||||
if err != nil {
|
||||
return 0, 0, fmt.Errorf("无法解析 Chat ID: %s", err)
|
||||
}
|
||||
chatID = int64(chatIDInt)
|
||||
} else {
|
||||
return 0, 0, fmt.Errorf("无效的链接: %s", link)
|
||||
}
|
||||
return chatID, messageID, nil
|
||||
}
|
||||
|
||||
func handleLinkMessage(ctx *ext.Context, update *ext.Update) error {
|
||||
common.Log.Trace("Got link message")
|
||||
link := linkRegex.FindString(update.EffectiveMessage.Text)
|
||||
if link == "" {
|
||||
return dispatcher.ContinueGroups
|
||||
}
|
||||
strSlice := strings.Split(link, "/")
|
||||
if len(strSlice) < 3 {
|
||||
return dispatcher.ContinueGroups
|
||||
}
|
||||
messageID, err := strconv.Atoi(strSlice[len(strSlice)-1])
|
||||
linkChatID, messageID, err := parseLink(ctx, link)
|
||||
if err != nil {
|
||||
common.Log.Errorf("解析消息 ID 失败: %s", err)
|
||||
ctx.Reply(update, ext.ReplyTextString("无法解析消息 ID"), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
var linkChatID int64
|
||||
if len(strSlice) == 3 {
|
||||
chatUsername := strSlice[1]
|
||||
linkChat, err := ctx.ResolveUsername(chatUsername)
|
||||
if err != nil {
|
||||
common.Log.Errorf("解析用户名失败: %s", err)
|
||||
ctx.Reply(update, ext.ReplyTextString("解析用户名失败"), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
if linkChat == nil {
|
||||
common.Log.Errorf("无法找到聊天: %s", chatUsername)
|
||||
ctx.Reply(update, ext.ReplyTextString("无法找到聊天"), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
linkChatID = linkChat.GetID()
|
||||
} else if len(strSlice) == 4 {
|
||||
chatID, err := strconv.Atoi(strSlice[2])
|
||||
if err != nil {
|
||||
common.Log.Errorf("解析 Chat ID 失败: %s", err)
|
||||
ctx.Reply(update, ext.ReplyTextString("解析 Chat ID 失败"), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
linkChatID = int64(chatID)
|
||||
} else {
|
||||
ctx.Reply(update, ext.ReplyTextString("无法解析链接"), nil)
|
||||
common.Log.Errorf("解析链接失败: %s", err)
|
||||
ctx.Reply(update, ext.ReplyTextString("解析链接失败: "+err.Error()), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
|
||||
@@ -69,12 +69,13 @@ func handleLinkMessage(ctx *ext.Context, update *ext.Update) error {
|
||||
ctx.Reply(update, ext.ReplyTextString("获取用户失败"), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
storages := storage.GetUserStorages(user.ChatID)
|
||||
|
||||
if len(storages) == 0 {
|
||||
ctx.Reply(update, ext.ReplyTextString("无可用的存储"), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
// storages := storage.GetUserStorages(user.ChatID)
|
||||
// if len(storages) == 0 {
|
||||
// ctx.Reply(update, ext.ReplyTextString("无可用的存储"), nil)
|
||||
// return dispatcher.EndGroups
|
||||
// }
|
||||
|
||||
replied, err := ctx.Reply(update, ext.ReplyTextString("正在获取文件..."), nil)
|
||||
if err != nil {
|
||||
common.Log.Errorf("回复失败: %s", err)
|
||||
|
||||
@@ -63,12 +63,11 @@ func saveCmd(ctx *ext.Context, update *ext.Update) error {
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
|
||||
storages := storage.GetUserStorages(user.ChatID)
|
||||
|
||||
if len(storages) == 0 {
|
||||
ctx.Reply(update, ext.ReplyTextString("无可用的存储"), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
// storages := storage.GetUserStorages(user.ChatID)
|
||||
// if len(storages) == 0 {
|
||||
// ctx.Reply(update, ext.ReplyTextString("无可用的存储"), nil)
|
||||
// return dispatcher.EndGroups
|
||||
// }
|
||||
|
||||
msg, err := GetTGMessage(ctx, update.EffectiveChat().GetID(), replyToMsgID)
|
||||
if err != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/gotd/contrib/middleware/floodwait"
|
||||
"github.com/gotd/contrib/middleware/ratelimit"
|
||||
"github.com/gotd/td/telegram"
|
||||
"github.com/krau/SaveAny-Bot/common"
|
||||
"github.com/krau/SaveAny-Bot/config"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
@@ -30,8 +31,40 @@ const noPermissionText string = `
|
||||
func checkPermission(ctx *ext.Context, update *ext.Update) error {
|
||||
userID := update.GetUserChat().GetID()
|
||||
if !slice.Contain(config.Cfg.GetUsersID(), userID) {
|
||||
if config.Cfg.AsPublicCopyMediaBot {
|
||||
tryCopyMedia(ctx, update)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
ctx.Reply(update, ext.ReplyTextString(noPermissionText), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
return dispatcher.ContinueGroups
|
||||
}
|
||||
|
||||
func tryCopyMedia(ctx *ext.Context, update *ext.Update) {
|
||||
if !config.Cfg.AsPublicCopyMediaBot {
|
||||
return
|
||||
}
|
||||
if update.EffectiveMessage == nil || update.EffectiveMessage.Message == nil {
|
||||
return
|
||||
}
|
||||
common.Log.Tracef("Got copy media request from %d", update.EffectiveChat().GetID())
|
||||
msg := update.EffectiveMessage.Message
|
||||
if link := linkRegex.FindString(update.EffectiveMessage.Text); link != "" {
|
||||
linkChatID, messageID, err := parseLink(ctx, link)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
fileMessage, err := GetTGMessage(ctx, linkChatID, messageID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if fileMessage == nil || fileMessage.Media == nil {
|
||||
return
|
||||
}
|
||||
msg = fileMessage
|
||||
}
|
||||
if _, err := copyMediaToChat(ctx, msg, update.EffectiveChat().GetID()); err != nil {
|
||||
common.Log.Errorf("Failed to copy media: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,9 +49,9 @@ func getSelectStorageMarkup(userChatID int64, fileChatID, fileMessageID int) (*t
|
||||
return nil, fmt.Errorf("failed to get user by chat ID: %d, error: %w", userChatID, err)
|
||||
}
|
||||
storages := storage.GetUserStorages(user.ChatID)
|
||||
if len(storages) == 0 {
|
||||
return nil, ErrNoStorages
|
||||
}
|
||||
// if len(storages) == 0 {
|
||||
// return nil, ErrNoStorages
|
||||
// }
|
||||
|
||||
buttons := make([]tg.KeyboardButtonClass, 0)
|
||||
for _, storage := range storages {
|
||||
@@ -216,7 +216,7 @@ func GetTGMessage(ctx *ext.Context, chatId int64, messageID int) (*tg.Message, e
|
||||
if err == nil {
|
||||
return cacheMessage, nil
|
||||
}
|
||||
common.Log.Debugf("Fetching message: %d", messageID)
|
||||
common.Log.Debugf("Fetching message: %d:%d", chatId, messageID)
|
||||
messages, err := ctx.GetMessages(chatId, []tg.InputMessageClass{&tg.InputMessageID{ID: messageID}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -17,6 +17,9 @@ type Config struct {
|
||||
Threads int `toml:"threads" mapstructure:"threads" json:"threads"`
|
||||
Stream bool `toml:"stream" mapstructure:"stream" json:"stream"`
|
||||
|
||||
// Experimental: 将拷贝媒体文件的功能设为公开可用
|
||||
AsPublicCopyMediaBot bool `toml:"as_public_copy_media_bot" mapstructure:"as_public_copy_media_bot" json:"as_public_copy_media_bot"`
|
||||
|
||||
Users []userConfig `toml:"users" mapstructure:"users" json:"users"`
|
||||
|
||||
Temp tempConfig `toml:"temp" mapstructure:"temp"`
|
||||
|
||||
46
docs/docs/experimental.md
Normal file
46
docs/docs/experimental.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 实验性功能
|
||||
|
||||
这里的功能不太稳定, 且未来可能会被删除或修改。
|
||||
|
||||
## 存储规则
|
||||
|
||||
允许你为 Bot 在上传文件到存储时设置一些重定向规则, 用于自动整理所保存的文件.
|
||||
|
||||
见: https://github.com/krau/SaveAny-Bot/issues/28
|
||||
|
||||
目前支持的规则类型:
|
||||
|
||||
1. FILENAME-REGEX
|
||||
2. MESSAGE-REGEX
|
||||
|
||||
添加规则的基本语法:
|
||||
|
||||
"规则类型 规则内容 存储名 路径"
|
||||
|
||||
注意空格的使用, 语法正确 bot 才能解析, 以下是一条合法的添加规则命令:
|
||||
|
||||
```
|
||||
/rule add FILENAME-REGEX (?i)\.(mp4|mkv|ts|avi|flv)$ MyAlist /视频
|
||||
```
|
||||
|
||||
此外, 规则中的存储名若使用 "CHOSEN" , 则表示存储到点击按钮选择的存储端的路径下
|
||||
|
||||
规则介绍:
|
||||
|
||||
### FILENAME-REGEX
|
||||
|
||||
根据文件名正则匹配, 规则内容要求为一个合法的正则表达式, 如
|
||||
|
||||
```
|
||||
FILENAME-REGEX (?i)\.(mp4|mkv|ts|avi|flv)$ MyAlist /视频
|
||||
```
|
||||
|
||||
表示将文件名后缀为 mp4,mkv,ts,avi,flv 的文件放到名为 MyAlist 存储下的 /视频 目录内 (同时受配置文件中的 `base_path` 影响)
|
||||
|
||||
### MESSAGE-REGEX
|
||||
|
||||
同上, 根据消息文本内容正则匹配
|
||||
|
||||
## 复制并发送媒体消息
|
||||
|
||||
将接收到的文件(媒体)消息, 或链接对应的消息原样发送到当前聊天, 点击选择存储按钮中的 "发送到当前聊天" 即可.
|
||||
@@ -35,4 +35,4 @@ Bot 接受两种消息: 文件和链接.
|
||||
|
||||
**不支持** Stream 模式的存储端:
|
||||
|
||||
- alist
|
||||
- alist
|
||||
@@ -30,5 +30,6 @@ nav:
|
||||
- index.md
|
||||
- deploy.md
|
||||
- help.md
|
||||
- experimental.md
|
||||
- faq.md
|
||||
- contribute.md
|
||||
|
||||
Reference in New Issue
Block a user