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