serr 2025-05-02 20:37:24 +03:00
commit 71a32d0630
5 changed files with 285 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.bckr.json

BIN
bckr.exe Normal file

Binary file not shown.

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module main
go 1.23.2

0
go.sum Normal file
View File

281
main.go Normal file
View File

@ -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)
}