mirror of
https://github.com/krau/SaveAny-Bot.git
synced 2026-06-03 14:40:52 +08:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62cceee592 | ||
|
|
6d315f7af2 | ||
|
|
5352491c76 |
6
.github/ISSUE_TEMPLATE/bug.yml
vendored
6
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -5,6 +5,12 @@ labels:
|
|||||||
assignees:
|
assignees:
|
||||||
- krau
|
- krau
|
||||||
body:
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
# Please Search Before Submitting / 提交前请搜索
|
||||||
|
Please make sure to search existing issues before submitting a new bug report.
|
||||||
|
提交新的 Bug 报告前请务必搜索已有的 issue,避免重复
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "👾 Description"
|
label: "👾 Description"
|
||||||
|
|||||||
64
.github/ISSUE_TEMPLATE/feature.yml
vendored
64
.github/ISSUE_TEMPLATE/feature.yml
vendored
@@ -8,7 +8,69 @@ body:
|
|||||||
- type: markdown
|
- type: markdown
|
||||||
attributes:
|
attributes:
|
||||||
value: |
|
value: |
|
||||||
# Please describe the feature you want in detail
|
Please describe the feature you want in detail.
|
||||||
|
请详细描述你想要的功能。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ IMPORTANT NOTICE / 说明
|
||||||
|
|
||||||
|
Save Any Bot supports multiple storage backends, **including Telegram**.
|
||||||
|
However, **all backends are treated equally**, keep this in mind when submitting feature requests.
|
||||||
|
|
||||||
|
Save Any Bot 支持多种存储后端,**包括 Telegram**。
|
||||||
|
但**所有后端在设计上是平等的**,请在提出功能请求前务必理解这一点。
|
||||||
|
|
||||||
|
### ❌ Out of scope requests / 不在项目范围内的请求
|
||||||
|
The following requests are **out of scope** and will be closed without discussion:
|
||||||
|
|
||||||
|
以下请求**不属于本项目设计范围**,将被直接关闭,不再讨论:
|
||||||
|
|
||||||
|
- Adding **Telegram-specific behaviors or exceptions**
|
||||||
|
添加 **仅针对 Telegram 的特殊行为或例外逻辑**
|
||||||
|
- Treating Telegram as anything other than a **generic file storage backend**
|
||||||
|
将 Telegram 视为非“通用文件存储后端”的特殊存在
|
||||||
|
- Saving or syncing **non-file content** (text messages, chat history, etc.)
|
||||||
|
保存或同步 **非文件内容**(文本消息、聊天记录等)
|
||||||
|
- Preserving or reconstructing original messages (e.g. 1:1 forwarding)
|
||||||
|
保留或还原原始消息形态(例如 1:1 转发)
|
||||||
|
- Perform special reprocessing on files to adapt to specific storage backends
|
||||||
|
(e.g. splitting, re-encoding, transforming, etc.)
|
||||||
|
为适配特定存储后端而对文件进行特殊处理
|
||||||
|
(如分割、转码、重编码、转换格式等)
|
||||||
|
- Any request that requires different logic *only because the backend is Telegram*
|
||||||
|
任何**仅因后端是 Telegram 而需要不同逻辑**的请求
|
||||||
|
|
||||||
|
### ❌ Abuse-leaning or high-risk requests / 滥用倾向的请求
|
||||||
|
Requests that may **enable or encourage** the following will NOT be accepted:
|
||||||
|
|
||||||
|
可能**促成或鼓励**以下行为的请求将不会被接受:
|
||||||
|
|
||||||
|
- Violating Telegram Terms of Service
|
||||||
|
违反 Telegram 服务条款
|
||||||
|
- Building traffic, mirror, or profit-oriented channels using third-party content
|
||||||
|
利用第三方内容构建引流、镜像或牟利用途的频道
|
||||||
|
|
||||||
|
### ⚖️ Design principle / 设计原则
|
||||||
|
Save Any Bot follows a **backend-agnostic design**:
|
||||||
|
|
||||||
|
Save Any Bot 遵循 **后端无关(backend-agnostic)** 的设计原则:
|
||||||
|
|
||||||
|
- If a feature cannot be implemented **uniformly across all backends**, it will not be added.
|
||||||
|
如果某个功能无法在 **所有后端** 中统一实现,则不会被添加。
|
||||||
|
- No backend-specific hacks or special cases will be introduced.
|
||||||
|
不会引入任何后端特有的 hack 或特殊处理逻辑。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
If your request falls into any of the categories above, please do not open an issue.
|
||||||
|
Such issues will be closed.
|
||||||
|
|
||||||
|
如果你的请求符合以上任一情况,请不要提交 issue,
|
||||||
|
相关 issue 将被直接关闭。
|
||||||
|
|
||||||
|
Thank you for respecting the scope and design principles of this project.
|
||||||
|
感谢你的理解与支持。
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "⭐️ Feature description"
|
label: "⭐️ Feature description"
|
||||||
|
|||||||
@@ -7,11 +7,13 @@ import (
|
|||||||
"github.com/celestix/gotgproto/dispatcher"
|
"github.com/celestix/gotgproto/dispatcher"
|
||||||
"github.com/celestix/gotgproto/ext"
|
"github.com/celestix/gotgproto/ext"
|
||||||
"github.com/charmbracelet/log"
|
"github.com/charmbracelet/log"
|
||||||
|
"github.com/duke-git/lancet/v2/validator"
|
||||||
"github.com/gotd/td/tg"
|
"github.com/gotd/td/tg"
|
||||||
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/dirutil"
|
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/dirutil"
|
||||||
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/mediautil"
|
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/mediautil"
|
||||||
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
|
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/msgelem"
|
||||||
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut"
|
"github.com/krau/SaveAny-Bot/client/bot/handlers/utils/shortcut"
|
||||||
|
"github.com/krau/SaveAny-Bot/client/user"
|
||||||
"github.com/krau/SaveAny-Bot/common/i18n"
|
"github.com/krau/SaveAny-Bot/common/i18n"
|
||||||
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
|
"github.com/krau/SaveAny-Bot/common/i18n/i18nk"
|
||||||
"github.com/krau/SaveAny-Bot/common/utils/strutil"
|
"github.com/krau/SaveAny-Bot/common/utils/strutil"
|
||||||
@@ -105,7 +107,12 @@ func handleBatchSave(ctx *ext.Context, update *ext.Update, args []string) error
|
|||||||
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorInvalidMsgIdRange, map[string]any{"Error": err.Error()})), nil)
|
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorInvalidMsgIdRange, map[string]any{"Error": err.Error()})), nil)
|
||||||
return dispatcher.EndGroups
|
return dispatcher.EndGroups
|
||||||
}
|
}
|
||||||
chatID, err := tgutil.ParseChatID(ctx, chatArg)
|
tctx := ctx
|
||||||
|
uctx := user.GetCtx()
|
||||||
|
if uctx != nil && validator.IsIntStr(chatArg) {
|
||||||
|
tctx = uctx
|
||||||
|
}
|
||||||
|
chatID, err := tgutil.ParseChatID(tctx, chatArg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorInvalidIdOrUsername, map[string]any{"Error": err.Error()})), nil)
|
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorInvalidIdOrUsername, map[string]any{"Error": err.Error()})), nil)
|
||||||
return dispatcher.EndGroups
|
return dispatcher.EndGroups
|
||||||
@@ -118,7 +125,7 @@ func handleBatchSave(ctx *ext.Context, update *ext.Update, args []string) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// [TODO]: generator istead of get all messages
|
// [TODO]: generator istead of get all messages
|
||||||
msgs, err := tgutil.GetMessagesRange(ctx, chatID, int(startID), int(endID))
|
msgs, err := tgutil.GetMessagesRange(tctx, chatID, int(startID), int(endID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorGetMessagesFailed, map[string]any{"Error": err.Error()})), nil)
|
ctx.Reply(update, ext.ReplyTextString(i18n.T(i18nk.BotMsgCommonErrorGetMessagesFailed, map[string]any{"Error": err.Error()})), nil)
|
||||||
return dispatcher.EndGroups
|
return dispatcher.EndGroups
|
||||||
@@ -141,7 +148,7 @@ func handleBatchSave(ctx *ext.Context, update *ext.Update, args []string) error
|
|||||||
if !supported {
|
if !supported {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
file, err := tfile.FromMediaMessage(media, ctx.Raw, msg, tfile.WithNameIfEmpty(tgutil.GenFileNameFromMessage(*msg)))
|
file, err := tfile.FromMediaMessage(media, tctx.Raw, msg, tfile.WithNameIfEmpty(tgutil.GenFileNameFromMessage(*msg)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.FromContext(ctx).Errorf("Failed to get file from message: %s", err)
|
log.FromContext(ctx).Errorf("Failed to get file from message: %s", err)
|
||||||
continue
|
continue
|
||||||
@@ -172,14 +179,14 @@ func handleBatchSave(ctx *ext.Context, update *ext.Update, args []string) error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.FromContext(ctx).Errorf("Failed to build storage selection keyboard: %s", err)
|
log.FromContext(ctx).Errorf("Failed to build storage selection keyboard: %s", err)
|
||||||
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
|
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
|
||||||
ID: replied.ID,
|
ID: replied.ID,
|
||||||
Message: i18n.T(i18nk.BotMsgCommonErrorBuildStorageSelectKeyboardFailed, map[string]any{"Error": err.Error()}),
|
Message: i18n.T(i18nk.BotMsgCommonErrorBuildStorageSelectKeyboardFailed, map[string]any{"Error": err.Error()}),
|
||||||
})
|
})
|
||||||
return dispatcher.EndGroups
|
return dispatcher.EndGroups
|
||||||
}
|
}
|
||||||
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
|
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
|
||||||
ID: replied.ID,
|
ID: replied.ID,
|
||||||
Message: i18n.T(i18nk.BotMsgCommonInfoFoundFilesSelectStorage, map[string]any{"Count": len(files)}),
|
Message: i18n.T(i18nk.BotMsgCommonInfoFoundFilesSelectStorage, map[string]any{"Count": len(files)}),
|
||||||
ReplyMarkup: markup,
|
ReplyMarkup: markup,
|
||||||
})
|
})
|
||||||
return dispatcher.EndGroups
|
return dispatcher.EndGroups
|
||||||
|
|||||||
@@ -113,29 +113,28 @@ func InputMessageClassSliceFromInt(ids []int) []tg.InputMessageClass {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMessagesRange(ctx *ext.Context, chatID int64, minId, maxId int) ([]*tg.Message, error) {
|
func GetMessagesRange(ctx *ext.Context, chatID int64, minId, maxId int) (msg []*tg.Message, err error) {
|
||||||
if msg, err := getMessagesRange(ctx, chatID, minId, maxId); err == nil {
|
if msg, err = getMessagesRange(ctx, chatID, minId, maxId); err == nil {
|
||||||
return msg, nil
|
return
|
||||||
}
|
}
|
||||||
in := constant.TDLibPeerID(chatID)
|
in := constant.TDLibPeerID(chatID)
|
||||||
plain := in.ToPlain()
|
plain := in.ToPlain()
|
||||||
|
|
||||||
var channel constant.TDLibPeerID
|
var channel constant.TDLibPeerID
|
||||||
channel.Channel(plain)
|
channel.Channel(plain)
|
||||||
if msg, err := getMessagesRange(ctx, int64(channel), minId, maxId); err == nil {
|
if msg, err = getMessagesRange(ctx, int64(channel), minId, maxId); err == nil {
|
||||||
return msg, nil
|
return
|
||||||
}
|
}
|
||||||
var userID constant.TDLibPeerID
|
var userID constant.TDLibPeerID
|
||||||
userID.User(plain)
|
userID.User(plain)
|
||||||
if msg, err := getMessagesRange(ctx, int64(userID), minId, maxId); err == nil {
|
if msg, err = getMessagesRange(ctx, int64(userID), minId, maxId); err == nil {
|
||||||
return msg, nil
|
return
|
||||||
}
|
}
|
||||||
var chat constant.TDLibPeerID
|
var chat constant.TDLibPeerID
|
||||||
chat.Chat(plain)
|
chat.Chat(plain)
|
||||||
if msg, err := getMessagesRange(ctx, int64(chat), minId, maxId); err == nil {
|
if msg, err = getMessagesRange(ctx, int64(chat), minId, maxId); err == nil {
|
||||||
return msg, nil
|
return
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("failed to get messages range for chatID %d", chatID)
|
return nil, fmt.Errorf("failed to get messages range for chat %d: %w", chatID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getMessagesRange(ctx *ext.Context, chatID int64, minId, maxId int) ([]*tg.Message, error) {
|
func getMessagesRange(ctx *ext.Context, chatID int64, minId, maxId int) ([]*tg.Message, error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user