new
commit
71a32d0630
|
@ -0,0 +1 @@
|
||||||
|
.bckr.json
|
|
@ -0,0 +1,281 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"mime/multipart"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"slices"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
logFile, err := os.OpenFile(".bckr.log", os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
log.SetPrefix("bckr | ")
|
||||||
|
log.SetOutput(logFile)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
hideConsoleWindow()
|
||||||
|
|
||||||
|
for {
|
||||||
|
|
||||||
|
// Загрузка конфига
|
||||||
|
config, err := loadConfig(".bckr.json")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
|
// Создание архива
|
||||||
|
archiveName := "backup_" + time.Now().Format("2006-01-02_15-04-05") + ".zip"
|
||||||
|
if err := createArchive(archiveName, config.TargetsFilesOrDirs, config.IgnoreFilesOrDirs); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
goto sleep
|
||||||
|
}
|
||||||
|
log.Println("Archive created successfully:", archiveName)
|
||||||
|
//
|
||||||
|
|
||||||
|
// Отправка архива
|
||||||
|
if err := TGBotSendFile(config.TGYourBotToken, config.TGYourUserId, archiveName, ""); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
goto sleep
|
||||||
|
}
|
||||||
|
log.Println("Archive sent successfully:", archiveName)
|
||||||
|
//
|
||||||
|
|
||||||
|
// Сон
|
||||||
|
sleep:
|
||||||
|
os.Remove(archiveName)
|
||||||
|
time.Sleep(time.Duration(config.IntervalInSeconds) * time.Second)
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type config struct {
|
||||||
|
TGYourBotToken string
|
||||||
|
TGYourUserId string
|
||||||
|
IntervalInSeconds uint64
|
||||||
|
TargetsFilesOrDirs []string
|
||||||
|
IgnoreFilesOrDirs []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadConfig(configPath string) (*config, error) {
|
||||||
|
cfg := &config{}
|
||||||
|
configFile, err := os.ReadFile(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(configFile, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createArchive(archiveName string, targets, ignore []string) error {
|
||||||
|
// Создание файла архива
|
||||||
|
archiveFile, err := os.Create(archiveName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer archiveFile.Close()
|
||||||
|
//
|
||||||
|
|
||||||
|
// Полный путь до архива
|
||||||
|
archiveFileAbsPath, err := filepath.Abs(archiveName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
|
// writer для ZIP-архива
|
||||||
|
zipWriter := zip.NewWriter(archiveFile)
|
||||||
|
defer zipWriter.Close()
|
||||||
|
//
|
||||||
|
|
||||||
|
// Преобразуем ignore-пути в абсолютные для сравнения
|
||||||
|
var ignorePaths []string
|
||||||
|
for _, path := range ignore {
|
||||||
|
absPath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ignorePaths = append(ignorePaths, absPath)
|
||||||
|
}
|
||||||
|
// И не архивируем сами себя
|
||||||
|
ignorePaths = append(ignorePaths, archiveFileAbsPath)
|
||||||
|
//
|
||||||
|
|
||||||
|
// Проверяет, нужно ли игнорировать путь
|
||||||
|
shouldIgnore := func(path string) bool {
|
||||||
|
absPath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(ignorePaths, absPath) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
|
// Обрабатываем каждый целевой путь
|
||||||
|
for _, target := range targets {
|
||||||
|
// Проверяем существование пути
|
||||||
|
if _, err := os.Stat(target); os.IsNotExist(err) {
|
||||||
|
log.Printf("Warning: target path does not exist: %s", target)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Рекурсивно обходим начиная с target
|
||||||
|
err := filepath.Walk(target, func(filePath string, fileInfo os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
// ошибка доступа
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Пропускаем игнорируемые пути
|
||||||
|
if shouldIgnore(filePath) {
|
||||||
|
if fileInfo.IsDir() {
|
||||||
|
return filepath.SkipDir // Пропускаем всю директорию
|
||||||
|
}
|
||||||
|
return nil // Пропускаем файл
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем заголовок файла в архиве
|
||||||
|
header, err := zip.FileInfoHeader(fileInfo)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Устанавливаем правильное имя файла в архиве
|
||||||
|
header.Name, err = filepath.Rel(filepath.Dir(target), filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Для директорий добавляем слеш в конец
|
||||||
|
if fileInfo.IsDir() {
|
||||||
|
header.Name += "/"
|
||||||
|
} else {
|
||||||
|
// Устанавливаем метод сжатия для файликов
|
||||||
|
header.Method = zip.Deflate
|
||||||
|
}
|
||||||
|
|
||||||
|
// Создаем запись в архиве
|
||||||
|
writer, err := zipWriter.CreateHeader(header)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Если это файл - копируем его содержимое
|
||||||
|
if !fileInfo.IsDir() {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(writer, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func TGBotSendFile(botToken, userId, filePath, caption string) error {
|
||||||
|
apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/sendDocument", botToken)
|
||||||
|
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
body := &bytes.Buffer{}
|
||||||
|
|
||||||
|
writer := multipart.NewWriter(body)
|
||||||
|
|
||||||
|
part, err := writer.CreateFormFile("document", filepath.Base(file.Name()))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = io.Copy(part, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.WriteField("chat_id", userId)
|
||||||
|
writer.WriteField("caption", caption)
|
||||||
|
|
||||||
|
err = writer.Close()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("POST", apiURL, body)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("Content-Type", writer.FormDataContentType())
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hideConsoleWindow() {
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
hideConsoleWindowWindows()
|
||||||
|
} else if runtime.GOOS == "linux" || runtime.GOOS == "darwin" {
|
||||||
|
hideConsoleWindowUnix()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func hideConsoleWindowWindows() {
|
||||||
|
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
proc := kernel32.NewProc("FreeConsole")
|
||||||
|
proc.Call()
|
||||||
|
}
|
||||||
|
|
||||||
|
func hideConsoleWindowUnix() {
|
||||||
|
cmd := exec.Command("nohup", os.Args[0])
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.Start()
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
Loading…
Reference in New Issue