Last active
November 11, 2023 09:48
-
-
Save Delnegend/1e91bd5640b5d3703599e30b2916a31e to your computer and use it in GitHub Desktop.
Backup any file/folder from your Linux server to a Discord channel using webhook. Requires `pip install requests`.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python3 | |
import sys | |
import os | |
import json | |
import shutil | |
import requests | |
import subprocess as sp | |
from datetime import datetime | |
from dataclasses import dataclass, field | |
from concurrent.futures import ThreadPoolExecutor, as_completed | |
TMP_DIR = "/tmp" | |
WEBHOOK = "https://discord.com/api/webhooks/<CHANGE_ME>" | |
EXEC = "/bin/sh" | |
PASS_7Z = "<CHANGE_ME>" | |
# To extract: 7z x file.7z -p'your_password' | |
@dataclass | |
class AppData: | |
name: str | |
parent_dir: str | |
exclude: list[str] = field(default_factory=list) | |
input_path: str = field(default_factory=str) # can be file or directory | |
stop_container: bool = field(default=True) | |
# Add/remove/modify accordingly to your needs. | |
CONTAINERS = [ | |
AppData( | |
parent_dir="/mnt/DockerData", | |
name="adguard", | |
exclude=["*.json", "*.txt"], | |
input_path="./AdguardHome", | |
stop_container=False, | |
), | |
AppData(parent_dir="/mnt/DockerData", name="fail2ban", exclude=["log"], input_path="./fail2ban"), | |
AppData( | |
parent_dir="/mnt/DockerData", | |
name="portainer", | |
exclude=["cli-plugins", "common", "bin", "certs", "tls", "export-*.json"], | |
input_path="./portainer", | |
), | |
] | |
def run(cmd: str) -> None: | |
sp.run(cmd, shell=True, executable=EXEC, stdout=sp.DEVNULL, stderr=sp.DEVNULL) | |
def compress(app: AppData) -> None: | |
now = datetime.now().strftime("%Y-%m-%d-%H-%M-%S") | |
out_7z_file = f"{app.name}.{now}.7z" | |
out_7z_path = os.path.join(TMP_DIR, out_7z_file) | |
_7z_cmd = ["7z", "a", "-t7z", "-m0=lzma2", "-mx=9", "-mfb=64", "-md=32m", "-ms=on", "-mhe=on", f"-p{PASS_7Z}"] | |
_7z_cmd.extend([f"-xr!'{x}'" for x in app.exclude]) | |
_7z_cmd.extend([out_7z_path, os.path.join(app.parent_dir, app.input_path)]) | |
if app.stop_container: | |
run("docker stop " + app.name) | |
run(" ".join(_7z_cmd)) | |
print(" " + app.name) | |
if app.stop_container: | |
run("docker start " + app.name) | |
def send_file() -> None: | |
files = [f for f in os.listdir(TMP_DIR) if os.path.isfile(os.path.join(TMP_DIR, f))] | |
files_req = {f"file{index}": open(os.path.join(TMP_DIR, file), "rb") for index, file in enumerate(files)} | |
requests.post(WEBHOOK, files=files_req) | |
def main() -> None: | |
if shutil.which("7z") is None: | |
requests.post( | |
WEBHOOK, data=json.dumps({"content": ":x: `7z` not found"}), headers={"Content-Type": "application/json"} | |
) | |
print("7z not found") | |
sys.exit(1) | |
if os.geteuid() != 0: | |
requests.post( | |
WEBHOOK, data=json.dumps({"content": ":x: `sudo` required"}), headers={"Content-Type": "application/json"} | |
) | |
print("sudo required") | |
sys.exit(1) | |
shutil.rmtree(TMP_DIR, ignore_errors=True) | |
os.mkdir(TMP_DIR) | |
print("Compressing containers") | |
with ThreadPoolExecutor(max_workers=10) as executor: | |
futures = [executor.submit(compress, x) for x in CONTAINERS] | |
for future in as_completed(futures): | |
future.result() | |
print("Sending files") | |
send_file() | |
shutil.rmtree(TMP_DIR, ignore_errors=True) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment