Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ea5be5b90 | ||
|
|
7f483056e0 | ||
|
|
a6f88d7f75 | ||
|
|
b757df0b5e | ||
|
|
b017046c8b | ||
|
|
a474fdf6ae | ||
|
|
729e688748 | ||
|
|
9ea4857cd9 | ||
|
|
8bf7bc0e85 | ||
|
|
26e344a6f6 | ||
|
|
8f0744077e | ||
|
|
ed99a37831 | ||
|
|
488d709d85 | ||
|
|
66454b082a | ||
|
|
70e83e62d9 | ||
|
|
d2ddb9193a | ||
|
|
5f78db90c7 | ||
|
|
c3a4702e79 |
11
.dockerignore
Normal file
11
.dockerignore
Normal file
@@ -0,0 +1,11 @@
|
||||
*.md
|
||||
.git
|
||||
.github/
|
||||
.gitignore
|
||||
.vscode/
|
||||
downloads/
|
||||
data/
|
||||
cache/
|
||||
docs
|
||||
config.example.toml
|
||||
docker-compose.*
|
||||
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
2
.github/ISSUE_TEMPLATE/bug.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: "👾 报告 bug"
|
||||
description: "报告 bug"
|
||||
labels:
|
||||
- ":space_invader: Bug"
|
||||
- "bug"
|
||||
assignees:
|
||||
- krau
|
||||
body:
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature.yml
vendored
2
.github/ISSUE_TEMPLATE/feature.yml
vendored
@@ -1,7 +1,7 @@
|
||||
name: "⭐️ 功能请求"
|
||||
description: "功能请求"
|
||||
labels:
|
||||
- ":fire: Enhancement"
|
||||
- "enhancement"
|
||||
assignees:
|
||||
- krau
|
||||
body:
|
||||
|
||||
38
.github/workflows/build-docker.yml
vendored
38
.github/workflows/build-docker.yml
vendored
@@ -20,19 +20,6 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
@@ -50,11 +37,36 @@ jobs:
|
||||
org.opencontainers.image.source=https://github.com/krau/SaveAny-Bot
|
||||
org.opencontainers.image.url=https://github.com/krau/SaveAny-Bot
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract version from Git Ref
|
||||
id: extract_version
|
||||
run: |
|
||||
VERSION=$(echo "${{ github.ref }}" | sed 's/refs\/tags\/v//')
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_ENV
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
build-args: |
|
||||
VERSION=${{ steps.meta.outputs.version }}
|
||||
GitCommit=${{ github.sha }}
|
||||
BuildTime=${{ format(github.event.repository.updated_at, 'yyyy-MM-dd HH:mm:ss') }}
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
19
Dockerfile
19
Dockerfile
@@ -1,13 +1,22 @@
|
||||
FROM golang:alpine AS builder
|
||||
|
||||
ARG VERSION="dev"
|
||||
ARG GitCommit="Unknown"
|
||||
ARG BuildTime="Unknown"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY go.* ./
|
||||
RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o saveany-bot .
|
||||
RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||
--mount=type=cache,target=/go/pkg \
|
||||
CGO_ENABLED=0 \
|
||||
go build -trimpath \
|
||||
-ldflags "-s -w \
|
||||
-X github.com/krau/SaveAny-Bot/common.Version=${VERSION} \
|
||||
-X github.com/krau/SaveAny-Bot/common.GitCommit=${GiTCommit} \
|
||||
-X github.com/krau/SaveAny-Bot/common.BuildTime=${BuildTime}" \
|
||||
-o saveany-bot .
|
||||
|
||||
FROM alpine:latest
|
||||
|
||||
@@ -15,4 +24,4 @@ WORKDIR /app
|
||||
|
||||
COPY --from=builder /app/saveany-bot .
|
||||
|
||||
CMD ["./saveany-bot"]
|
||||
ENTRYPOINT ["/app/saveany-bot"]
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
|
||||
## Contributors
|
||||
|
||||
<!-- readme: contributors,AHCorn -start -->
|
||||
<!-- readme: contributors -start -->
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -61,7 +61,7 @@
|
||||
</tr>
|
||||
<tbody>
|
||||
</table>
|
||||
<!-- readme: contributors,AHCorn -end -->
|
||||
<!-- readme: contributors -end -->
|
||||
|
||||
## Thanks
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package bot
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/celestix/gotgproto"
|
||||
@@ -90,12 +89,10 @@ func Init() {
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
common.Log.Fatal("初始化客户端失败: 超时")
|
||||
os.Exit(1)
|
||||
common.Log.Panic("初始化客户端失败: 超时")
|
||||
case result := <-resultChan:
|
||||
if result.err != nil {
|
||||
common.Log.Fatalf("初始化客户端失败: %s", result.err)
|
||||
os.Exit(1)
|
||||
common.Log.Panicf("初始化客户端失败: %s", result.err)
|
||||
}
|
||||
Client = result.client
|
||||
RegisterHandlers(Client.Dispatcher)
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
)
|
||||
|
||||
func AddToQueue(ctx *ext.Context, update *ext.Update) error {
|
||||
// TODO: 回调数据用户独立鉴权 (处理 bot 在群聊中的情况)
|
||||
if !slice.Contain(config.Cfg.GetUsersID(), update.CallbackQuery.UserID) {
|
||||
ctx.AnswerCallback(&tg.MessagesSetBotCallbackAnswerRequest{
|
||||
QueryID: update.CallbackQuery.QueryID,
|
||||
@@ -117,7 +116,7 @@ func AddToQueue(ctx *ext.Context, update *ext.Update) error {
|
||||
if update.CallbackQuery.MsgID != record.ReplyMessageID {
|
||||
record.ReplyMessageID = update.CallbackQuery.MsgID
|
||||
if err := dao.SaveReceivedFile(record); err != nil {
|
||||
common.Log.Errorf("更新接收的文件失败: %s", err)
|
||||
common.Log.Errorf("更新记录失败: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +168,7 @@ func AddToQueue(ctx *ext.Context, update *ext.Update) error {
|
||||
task = types.Task{
|
||||
Ctx: ctx,
|
||||
Status: types.Pending,
|
||||
FileDBID: record.ID,
|
||||
File: file,
|
||||
StorageName: storageName,
|
||||
FileChatID: record.ChatID,
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
type ConversationType string
|
||||
|
||||
type ConversationState struct {
|
||||
sync.Mutex
|
||||
conversationType ConversationType
|
||||
InConversation bool
|
||||
data map[ConversationType]map[string]interface{}
|
||||
}
|
||||
|
||||
func (c *ConversationState) Reset() {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.InConversation = false
|
||||
c.conversationType = ""
|
||||
c.data = make(map[ConversationType]map[string]interface{})
|
||||
}
|
||||
|
||||
func (c *ConversationState) SetConversationType(t ConversationType) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
c.conversationType = t
|
||||
}
|
||||
|
||||
func (c *ConversationState) GetData(key string) interface{} {
|
||||
if c.data == nil || c.data[c.conversationType] == nil {
|
||||
return nil
|
||||
}
|
||||
return c.data[c.conversationType][key]
|
||||
}
|
||||
|
||||
func (c *ConversationState) SetData(key string, value interface{}) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
if c.data == nil {
|
||||
c.data = make(map[ConversationType]map[string]interface{})
|
||||
}
|
||||
if c.data[c.conversationType] == nil {
|
||||
c.data[c.conversationType] = make(map[string]interface{})
|
||||
}
|
||||
c.data[c.conversationType][key] = value
|
||||
}
|
||||
|
||||
// TODO: Implement conversation handling
|
||||
// var userConversationState = make(map[int64]*ConversationState)
|
||||
|
||||
// func handleConversation(ctx *ext.Context, update *ext.Update) error {
|
||||
// userID := update.EffectiveUser().GetID()
|
||||
// state, ok := userConversationState[userID]
|
||||
// if !ok {
|
||||
// return dispatcher.ContinueGroups
|
||||
// }
|
||||
// if update.EffectiveMessage.Text == "/cancel" {
|
||||
// state.Reset()
|
||||
// ctx.Reply(update, ext.ReplyTextString("已取消"), nil)
|
||||
// return dispatcher.EndGroups
|
||||
// }
|
||||
// if !state.InConversation {
|
||||
// return dispatcher.ContinueGroups
|
||||
// }
|
||||
// return handleConversationState(ctx, update, state)
|
||||
// }
|
||||
|
||||
// func handleConversationState(ctx *ext.Context, update *ext.Update, state *ConversationState) error {
|
||||
// switch state.conversationType {
|
||||
// default:
|
||||
// common.Log.Errorf("Unknown conversation type: %s", state.conversationType)
|
||||
// }
|
||||
// return dispatcher.EndGroups
|
||||
// }
|
||||
@@ -77,7 +77,7 @@ func dirCmd(ctx *ext.Context, update *ext.Update) error {
|
||||
ctx.Reply(update, ext.ReplyTextString("路径ID无效"), nil)
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
return delDir(ctx, update, user, dirID)
|
||||
return delDir(ctx, update, dirID)
|
||||
default:
|
||||
ctx.Reply(update, ext.ReplyTextString("未知操作"), nil)
|
||||
return dispatcher.EndGroups
|
||||
@@ -99,7 +99,7 @@ func addDir(ctx *ext.Context, update *ext.Update, user *dao.User, storageName, p
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
|
||||
func delDir(ctx *ext.Context, update *ext.Update, user *dao.User, dirID int) error {
|
||||
func delDir(ctx *ext.Context, update *ext.Update, dirID int) error {
|
||||
if err := dao.DeleteDirByID(uint(dirID)); err != nil {
|
||||
common.Log.Errorf("删除路径失败: %s", err)
|
||||
ctx.Reply(update, ext.ReplyTextString("删除路径失败"), nil)
|
||||
|
||||
@@ -256,7 +256,7 @@ func handleBatchSave(ctx *ext.Context, update *ext.Update, args []string) error
|
||||
successadd++
|
||||
}
|
||||
ctx.EditMessage(update.EffectiveChat().GetID(), &tg.MessagesEditMessageRequest{
|
||||
Message: fmt.Sprintf("批量保存完成\n成功添加: %d/%d\n获取文件失败: %d\n获取消息失败: %d\n保存数据库失败: %d", successadd, total, failedGetFile, failedGetMsg, failedSaveDB),
|
||||
Message: fmt.Sprintf("批量添加任务完成\n成功添加: %d/%d\n获取文件失败: %d\n获取消息失败: %d\n保存数据库失败: %d", successadd, total, failedGetFile, failedGetMsg, failedSaveDB),
|
||||
ID: replied.ID,
|
||||
})
|
||||
return dispatcher.EndGroups
|
||||
|
||||
95
bot/handle_send.go
Normal file
95
bot/handle_send.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package bot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/celestix/gotgproto/dispatcher"
|
||||
"github.com/celestix/gotgproto/ext"
|
||||
tgtypes "github.com/celestix/gotgproto/types"
|
||||
"github.com/gotd/td/tg"
|
||||
)
|
||||
|
||||
func copyMediaToChat(ctx *ext.Context, msg *tg.Message, chatID int64) (*tgtypes.Message, error) {
|
||||
media, ok := msg.GetMedia()
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("获取媒体失败")
|
||||
}
|
||||
|
||||
req := &tg.MessagesSendMediaRequest{
|
||||
InvertMedia: msg.InvertMedia,
|
||||
Message: msg.Message,
|
||||
}
|
||||
|
||||
switch m := media.(type) {
|
||||
case *tg.MessageMediaDocument:
|
||||
document, ok := m.Document.AsNotEmpty()
|
||||
if !ok {
|
||||
return nil, ErrEmptyDocument
|
||||
}
|
||||
inputMedia := &tg.InputMediaDocument{
|
||||
ID: document.AsInput(),
|
||||
}
|
||||
inputMedia.SetFlags()
|
||||
req.Media = inputMedia
|
||||
|
||||
case *tg.MessageMediaPhoto:
|
||||
photo, ok := m.Photo.AsNotEmpty()
|
||||
if !ok {
|
||||
return nil, ErrEmptyPhoto
|
||||
}
|
||||
inputMedia := &tg.InputMediaPhoto{
|
||||
ID: photo.AsInput(),
|
||||
}
|
||||
inputMedia.SetFlags()
|
||||
req.Media = inputMedia
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("不支持的媒体类型: %T", media)
|
||||
}
|
||||
|
||||
req.SetEntities(msg.Entities)
|
||||
req.SetFlags()
|
||||
|
||||
return ctx.SendMedia(chatID, req)
|
||||
}
|
||||
|
||||
func sendFileToTelegram(ctx *ext.Context, update *ext.Update) error {
|
||||
args := strings.Split(string(update.CallbackQuery.Data), " ")
|
||||
if len(args) < 3 {
|
||||
ctx.AnswerCallback(&tg.MessagesSetBotCallbackAnswerRequest{
|
||||
QueryID: update.CallbackQuery.QueryID,
|
||||
Alert: true,
|
||||
Message: "参数错误",
|
||||
CacheTime: 5,
|
||||
})
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
fileChatID, _ := strconv.Atoi(args[1])
|
||||
fileMessageID, _ := strconv.Atoi(args[2])
|
||||
fileMessage, err := GetTGMessage(ctx, int64(fileChatID), fileMessageID)
|
||||
if err != nil {
|
||||
ctx.AnswerCallback(&tg.MessagesSetBotCallbackAnswerRequest{
|
||||
QueryID: update.CallbackQuery.QueryID,
|
||||
Alert: true,
|
||||
Message: "无法获取文件消息",
|
||||
CacheTime: 5,
|
||||
})
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
_, err = copyMediaToChat(ctx, fileMessage, update.EffectiveChat().GetID())
|
||||
if err != nil {
|
||||
ctx.AnswerCallback(&tg.MessagesSetBotCallbackAnswerRequest{
|
||||
QueryID: update.CallbackQuery.QueryID,
|
||||
Alert: true,
|
||||
Message: fmt.Sprintf("发送文件失败: %s", err),
|
||||
CacheTime: 5,
|
||||
})
|
||||
} else {
|
||||
ctx.AnswerCallback(&tg.MessagesSetBotCallbackAnswerRequest{
|
||||
QueryID: update.CallbackQuery.QueryID,
|
||||
})
|
||||
}
|
||||
return dispatcher.EndGroups
|
||||
}
|
||||
@@ -29,5 +29,6 @@ func RegisterHandlers(dispatcher dispatcher.Dispatcher) {
|
||||
dispatcher.AddHandler(handlers.NewCallbackQuery(filters.CallbackQuery.Prefix("add"), AddToQueue))
|
||||
dispatcher.AddHandler(handlers.NewCallbackQuery(filters.CallbackQuery.Prefix("set_default"), setDefaultStorage))
|
||||
dispatcher.AddHandler(handlers.NewCallbackQuery(filters.CallbackQuery.Prefix("cancel"), cancelTask))
|
||||
dispatcher.AddHandler(handlers.NewCallbackQuery(filters.CallbackQuery.Prefix("send_here"), sendFileToTelegram))
|
||||
dispatcher.AddHandler(handlers.NewMessage(filters.Message.Media, handleFileMessage))
|
||||
}
|
||||
|
||||
11
bot/utils.go
11
bot/utils.go
@@ -71,6 +71,14 @@ func getSelectStorageMarkup(userChatID int64, fileChatID, fileMessageID int) (*t
|
||||
row.Buttons = buttons[i:min(i+3, len(buttons))]
|
||||
markup.Rows = append(markup.Rows, row)
|
||||
}
|
||||
markup.Rows = append(markup.Rows, tg.KeyboardButtonRow{
|
||||
Buttons: []tg.KeyboardButtonClass{
|
||||
&tg.KeyboardButtonCallback{
|
||||
Text: "发送到当前聊天",
|
||||
Data: []byte(fmt.Sprintf("send_here %d %d", fileChatID, fileMessageID)),
|
||||
},
|
||||
},
|
||||
})
|
||||
return markup, nil
|
||||
}
|
||||
|
||||
@@ -182,6 +190,9 @@ func FileFromMessage(ctx *ext.Context, chatID int64, messageID int, customFileNa
|
||||
key := fmt.Sprintf("file:%d:%d", chatID, messageID)
|
||||
cachedFile, err := common.CacheGet[*types.File](ctx, key)
|
||||
if err == nil {
|
||||
if customFileName != "" {
|
||||
cachedFile.FileName = customFileName
|
||||
}
|
||||
return cachedFile, nil
|
||||
}
|
||||
common.Log.Debugf("Getting file: %s", key)
|
||||
|
||||
@@ -47,7 +47,7 @@ func Run(_ *cobra.Command, _ []string) {
|
||||
return
|
||||
}
|
||||
common.Log.Info("正在清理缓存文件夹: ", cachePath)
|
||||
if err := os.RemoveAll(cachePath); err != nil {
|
||||
if err := common.RemoveAllInDir(cachePath); err != nil {
|
||||
common.Log.Error("清理缓存失败: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,15 +25,16 @@ func InitLogger() {
|
||||
}
|
||||
}
|
||||
consoleH := handler.NewConsoleHandler(logLevels)
|
||||
fileH, err := handler.NewTimeRotateFile(
|
||||
logFilePath,
|
||||
rotatefile.EveryDay,
|
||||
handler.WithLogLevels(slog.AllLevels),
|
||||
handler.WithBackupNum(logBackupNum),
|
||||
handler.WithBuffSize(0),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
Log.AddHandler(consoleH)
|
||||
if logFilePath != "" && logBackupNum > 0 {
|
||||
fileH, err := handler.NewTimeRotateFile(
|
||||
logFilePath,
|
||||
rotatefile.EveryDay,
|
||||
handler.WithLogLevels(slog.AllLevels),
|
||||
handler.WithBackupNum(logBackupNum))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
Log.AddHandler(fileH)
|
||||
}
|
||||
Log.AddHandlers(consoleH, fileH)
|
||||
}
|
||||
|
||||
35
common/os.go
35
common/os.go
@@ -1,31 +1,11 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 创建文件, 自动创建目录
|
||||
func MkFile(path string, data []byte) error {
|
||||
err := os.MkdirAll(filepath.Dir(path), os.ModePerm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, data, os.ModePerm)
|
||||
}
|
||||
|
||||
// 删除文件, 并清理空目录. 如果文件不存在则返回 nil
|
||||
func PurgeFile(path string) error {
|
||||
if err := os.Remove(path); err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return RemoveEmptyDirectories(filepath.Dir(path))
|
||||
}
|
||||
|
||||
func RmFileAfter(path string, td time.Duration) {
|
||||
_, err := os.Stat(path)
|
||||
if err != nil {
|
||||
@@ -34,22 +14,23 @@ func RmFileAfter(path string, td time.Duration) {
|
||||
}
|
||||
Log.Debugf("Remove file after %s: %s", td, path)
|
||||
time.AfterFunc(td, func() {
|
||||
PurgeFile(path)
|
||||
if err := os.Remove(path); err != nil {
|
||||
Log.Errorf("Failed to remove file %s: %s", path, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 递归删除空目录
|
||||
func RemoveEmptyDirectories(dirPath string) error {
|
||||
// 删除目录下的所有内容, 但不删除目录本身
|
||||
func RemoveAllInDir(dirPath string) error {
|
||||
entries, err := os.ReadDir(dirPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
err := os.Remove(dirPath)
|
||||
if err != nil {
|
||||
for _, entry := range entries {
|
||||
entryPath := filepath.Join(dirPath, entry.Name())
|
||||
if err := os.RemoveAll(entryPath); err != nil {
|
||||
return err
|
||||
}
|
||||
return RemoveEmptyDirectories(filepath.Dir(dirPath))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -90,11 +90,9 @@ func Init() error {
|
||||
viper.SetDefault("telegram.rpc_retry", 5)
|
||||
|
||||
viper.SetDefault("temp.base_path", "cache/")
|
||||
viper.SetDefault("temp.cache_ttl", 3600)
|
||||
viper.SetDefault("temp.cache_ttl", 30)
|
||||
|
||||
viper.SetDefault("log.level", "INFO")
|
||||
viper.SetDefault("log.file", "logs/saveany.log")
|
||||
viper.SetDefault("log.backup_count", 7)
|
||||
|
||||
viper.SetDefault("db.path", "data/saveany.db")
|
||||
viper.SetDefault("db.session", "data/session.db")
|
||||
|
||||
@@ -27,6 +27,13 @@ import (
|
||||
|
||||
func processPendingTask(task *types.Task) error {
|
||||
common.Log.Debugf("Start processing task: %s", task.String())
|
||||
|
||||
if task.FileName() != "" && !task.IsTelegraph && task.File.FileSize != 0 && task.FileDBID != 0 {
|
||||
ext := path.Ext(task.FileName())
|
||||
name := task.FileName()[:len(task.FileName())-len(ext)]
|
||||
task.File.FileName = fmt.Sprintf("%s_%d%s", name, task.FileDBID, ext)
|
||||
}
|
||||
|
||||
if task.FileName() == "" {
|
||||
task.File.FileName = fmt.Sprintf("%d_%d_%s", task.FileChatID, task.FileMessageID, task.File.Hash())
|
||||
}
|
||||
@@ -60,6 +67,7 @@ func processPendingTask(task *types.Task) error {
|
||||
|
||||
notsupportStreamStorage, notsupportStream := taskStorage.(storage.StorageNotSupportStream)
|
||||
cancelMarkUp := getCancelTaskMarkup(task)
|
||||
|
||||
if config.Cfg.Stream {
|
||||
if !notsupportStream {
|
||||
text, entities := buildProgressMessageEntity(task, 0, task.StartTime, 0)
|
||||
|
||||
13
docker-compose.local.yml
Normal file
13
docker-compose.local.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
services:
|
||||
saveany-bot:
|
||||
build: .
|
||||
container_name: saveany-bot
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./data:/app/data
|
||||
- ./config.toml:/app/config.toml
|
||||
- ./downloads:/app/downloads
|
||||
- ./cache:/app/cache
|
||||
# 使用 host 模式以便访问宿主机服务 (如代理)
|
||||
# 如果你对 Docker 网络模式熟悉, 可以自行修改
|
||||
network_mode: host
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/krau/SaveAny-Bot/types"
|
||||
@@ -113,8 +115,13 @@ func (c *Client) MkDir(ctx context.Context, dirPath string) error {
|
||||
}
|
||||
|
||||
func (c *Client) WriteFile(ctx context.Context, remotePath string, content io.Reader) error {
|
||||
url := c.BaseURL + remotePath
|
||||
resp, err := c.doRequest(ctx, WebdavMethodPut, url, content)
|
||||
u, err := url.Parse(c.BaseURL)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parts := strings.Split(strings.Trim(remotePath, "/"), "/")
|
||||
u.Path = path.Join(u.Path, strings.Join(parts, "/"))
|
||||
resp, err := c.doRequest(ctx, WebdavMethodPut, u.String(), content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -124,4 +131,5 @@ func (c *Client) WriteFile(ctx context.Context, remotePath string, content io.Re
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("PUT: %s", resp.Status)
|
||||
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ type Task struct {
|
||||
StorageName string
|
||||
StoragePath string
|
||||
StartTime time.Time
|
||||
FileDBID uint
|
||||
|
||||
File *File
|
||||
FileMessageID int
|
||||
|
||||
Reference in New Issue
Block a user