gacha-bot2 / cogs /backup.py
Amit
Clean Cloud Deployment
732ca45
Raw
History Blame Contribute Delete
4.29 kB
# cogs/backup.py
import discord
from discord.ext import commands, tasks
from discord import app_commands
import asyncio
import os
import datetime
import zipfile
import logging
from config import DB_DSN, BACKUP_CHANNEL_ID
logger = logging.getLogger(__name__)
class BackupCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.auto_backup.start()
def cog_unload(self):
self.auto_backup.cancel()
@tasks.loop(hours=12)
async def auto_backup(self):
"""Automatically runs every 12 hours."""
await self.bot.wait_until_ready()
await self._execute_backup("πŸ•’ Automated 12-Hour Backup")
async def _execute_backup(self, title_prefix: str, interaction: discord.Interaction = None):
"""The core logic for dumping, zipping, and uploading the database."""
# Ensure we have a valid destination
channel = self.bot.get_channel(BACKUP_CHANNEL_ID)
if not channel:
error_msg = "❌ `BACKUP_CHANNEL_ID` is missing or invalid in your .env file! Cannot upload database backup."
logger.error(error_msg)
if interaction:
await interaction.followup.send(error_msg, ephemeral=True)
return
now = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
sql_filename = f"db_dump_{now}.sql"
zip_filename = f"Gacha_Backup_{now}.zip"
logger.info(f"Starting database backup: {sql_filename}")
try:
# 1. Run the native PostgreSQL dump command securely
process = await asyncio.create_subprocess_exec(
'pg_dump', '--dbname', DB_DSN, '-f', sql_filename,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await process.communicate()
if process.returncode != 0:
error_msg = f"❌ pg_dump failed: {stderr.decode()}"
logger.error(error_msg)
if interaction:
await interaction.followup.send(error_msg, ephemeral=True)
return
# 2. Compress it heavily so we don't hit Discord's 25MB upload limit
def zip_file():
with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:
zipf.write(sql_filename)
await asyncio.to_thread(zip_file)
# 3. Format and Upload to Discord
file = discord.File(fp=zip_filename, filename=zip_filename)
embed = discord.Embed(
title=f"πŸ“¦ {title_prefix}",
description=f"Secure database snapshot generated at `<t:{int(datetime.datetime.now().timestamp())}:f>`.",
color=discord.Color.green()
)
embed.set_footer(text="To restore: Extract the .zip and run `psql -d gacha_bot_db -f <filename.sql>` in Termux.")
await channel.send(embed=embed, file=file)
logger.info("βœ… Backup successfully uploaded to Discord Cloud.")
if interaction:
await interaction.followup.send("βœ… Backup successfully forced and uploaded to the private vault channel!", ephemeral=True)
except Exception as e:
logger.error(f"Backup Error: {e}", exc_info=True)
if interaction:
await interaction.followup.send("❌ A critical error occurred while generating the backup.", ephemeral=True)
finally:
# 4. TRASH THE LOCAL FILES: Prevents your Android storage from filling up!
if os.path.exists(sql_filename):
os.remove(sql_filename)
if os.path.exists(zip_filename):
os.remove(zip_filename)
# Allow admins to manually force a backup at any time
@app_commands.command(name="force_backup", description="[ADMIN] Force an immediate database backup to the cloud vault")
@app_commands.default_permissions(administrator=True)
async def force_backup(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral=True)
await self._execute_backup("βš™οΈ Manual Admin Backup", interaction)
async def setup(bot):
await bot.add_cog(BackupCog(bot))