Skip to content

Instantly share code, notes, and snippets.

@Delnegend
Last active November 11, 2023 09:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Delnegend/1e91bd5640b5d3703599e30b2916a31e to your computer and use it in GitHub Desktop.
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`.
#!/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