update polish again
This commit is contained in:
1 parent
10f372173f
commit
f88fd7989a
1 file changed
+40
-81
+40
-81
@@ -1,6 +1,5 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import os
|
import os
|
||||||
from datetime import datetime, timezone
|
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
@@ -14,11 +13,9 @@ SERVICE_NAME = "yuuka"
|
|||||||
GIT_TIMEOUT = 30
|
GIT_TIMEOUT = 30
|
||||||
SYSTEMD_TIMEOUT = 15
|
SYSTEMD_TIMEOUT = 15
|
||||||
|
|
||||||
# ── colors ───────────────────────────────────────────────────────────────────
|
|
||||||
C_OK = 0x50FA7B
|
C_OK = 0x50FA7B
|
||||||
C_ERR = 0xFF5555
|
C_ERR = 0xFF5555
|
||||||
C_WARN = 0xFFB86C
|
C_WARN = 0xFFB86C
|
||||||
C_INFO = 0x8BE9FD
|
|
||||||
|
|
||||||
|
|
||||||
# ── subprocess helpers ────────────────────────────────────────────────────────
|
# ── subprocess helpers ────────────────────────────────────────────────────────
|
||||||
@@ -52,10 +49,7 @@ async def run_git_pull() -> tuple[int, str]:
|
|||||||
|
|
||||||
|
|
||||||
async def run_git_log(n: int = 5) -> tuple[int, str]:
|
async def run_git_log(n: int = 5) -> tuple[int, str]:
|
||||||
return await _run(
|
return await _run("git", "log", "--oneline", f"-{n}", cwd=BOT_DIR, timeout=GIT_TIMEOUT)
|
||||||
"git", "log", f"--oneline", f"-{n}",
|
|
||||||
cwd=BOT_DIR, timeout=GIT_TIMEOUT,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
async def run_sudo_systemctl(action: str) -> tuple[int, str]:
|
async def run_sudo_systemctl(action: str) -> tuple[int, str]:
|
||||||
@@ -68,8 +62,7 @@ async def run_sudo_systemctl(action: str) -> tuple[int, str]:
|
|||||||
|
|
||||||
async def run_journalctl(n: int = 50) -> tuple[int, str]:
|
async def run_journalctl(n: int = 50) -> tuple[int, str]:
|
||||||
return await _run(
|
return await _run(
|
||||||
"sudo", "-S", "journalctl", "-u", SERVICE_NAME,
|
"sudo", "-S", "journalctl", "-u", SERVICE_NAME, "-n", str(n), "--no-pager",
|
||||||
"-n", str(n), "--no-pager",
|
|
||||||
input_data=f"{SUDO_PASSWORD}\n".encode() if SUDO_PASSWORD else None,
|
input_data=f"{SUDO_PASSWORD}\n".encode() if SUDO_PASSWORD else None,
|
||||||
timeout=SYSTEMD_TIMEOUT,
|
timeout=SYSTEMD_TIMEOUT,
|
||||||
)
|
)
|
||||||
@@ -77,19 +70,10 @@ async def run_journalctl(n: int = 50) -> tuple[int, str]:
|
|||||||
|
|
||||||
def truncate(text: str, limit: int = 1900) -> str:
|
def truncate(text: str, limit: int = 1900) -> str:
|
||||||
if len(text) > limit:
|
if len(text) > limit:
|
||||||
return text[:limit] + "\n… (truncated)"
|
return text[:limit] + "\n... (truncated)"
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def _embed(title: str, description: str, color: int) -> discord.Embed:
|
|
||||||
return discord.Embed(
|
|
||||||
title=title,
|
|
||||||
description=description,
|
|
||||||
color=color,
|
|
||||||
timestamp=datetime.now(timezone.utc),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# ── cog ──────────────────────────────────────────────────────────────────────
|
# ── cog ──────────────────────────────────────────────────────────────────────
|
||||||
class GitPull(commands.Cog):
|
class GitPull(commands.Cog):
|
||||||
def __init__(self, bot: commands.Bot):
|
def __init__(self, bot: commands.Bot):
|
||||||
@@ -99,11 +83,7 @@ class GitPull(commands.Cog):
|
|||||||
return ctx.author.id == OWNER_ID
|
return ctx.author.id == OWNER_ID
|
||||||
|
|
||||||
async def _do_restart(self, ctx: commands.Context):
|
async def _do_restart(self, ctx: commands.Context):
|
||||||
await ctx.reply(embed=_embed(
|
await ctx.reply(embed=discord.Embed(description="Restarting service...", color=C_WARN))
|
||||||
"⟳ Restarting",
|
|
||||||
f"Restarting `{SERVICE_NAME}` — see you on the other side.",
|
|
||||||
C_WARN,
|
|
||||||
))
|
|
||||||
await asyncio.sleep(0.5)
|
await asyncio.sleep(0.5)
|
||||||
proc = await asyncio.create_subprocess_exec(
|
proc = await asyncio.create_subprocess_exec(
|
||||||
"sudo", "-S", "systemctl", "restart", SERVICE_NAME,
|
"sudo", "-S", "systemctl", "restart", SERVICE_NAME,
|
||||||
@@ -119,11 +99,8 @@ class GitPull(commands.Cog):
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# ── !gitpull ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@commands.command(name="gitpull")
|
@commands.command(name="gitpull")
|
||||||
async def gitpull(self, ctx: commands.Context):
|
async def gitpull(self, ctx: commands.Context):
|
||||||
"""Pull latest changes from git without restarting."""
|
|
||||||
if not self._is_owner(ctx):
|
if not self._is_owner(ctx):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -131,30 +108,27 @@ class GitPull(commands.Cog):
|
|||||||
code, output = await run_git_pull()
|
code, output = await run_git_pull()
|
||||||
|
|
||||||
if code != 0:
|
if code != 0:
|
||||||
return await ctx.reply(embed=_embed(
|
return await ctx.reply(embed=discord.Embed(
|
||||||
"✗ Pull Failed",
|
title="Git Pull",
|
||||||
f"```\n{truncate(output)}\n```",
|
description=f"```\n{truncate(output)}\n```",
|
||||||
C_ERR,
|
color=C_ERR,
|
||||||
))
|
))
|
||||||
|
|
||||||
if "Already up to date" in output:
|
if "Already up to date" in output:
|
||||||
return await ctx.reply(embed=_embed(
|
return await ctx.reply(embed=discord.Embed(
|
||||||
"✓ Already Up to Date",
|
title="Git Pull",
|
||||||
"Nothing new to pull.",
|
description="Already up to date.",
|
||||||
C_WARN,
|
color=C_WARN,
|
||||||
))
|
))
|
||||||
|
|
||||||
await ctx.reply(embed=_embed(
|
await ctx.reply(embed=discord.Embed(
|
||||||
"✓ Pulled",
|
title="Git Pull",
|
||||||
f"```\n{truncate(output)}\n```",
|
description=f"```\n{truncate(output)}\n```",
|
||||||
C_OK,
|
color=C_OK,
|
||||||
))
|
))
|
||||||
|
|
||||||
# ── !gitlog ───────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@commands.command(name="gitlog")
|
@commands.command(name="gitlog")
|
||||||
async def gitlog(self, ctx: commands.Context, n: int = 5):
|
async def gitlog(self, ctx: commands.Context, n: int = 5):
|
||||||
"""Show the last N commits. Default: 5."""
|
|
||||||
if not self._is_owner(ctx):
|
if not self._is_owner(ctx):
|
||||||
return
|
return
|
||||||
n = max(1, min(n, 20))
|
n = max(1, min(n, 20))
|
||||||
@@ -162,18 +136,14 @@ class GitPull(commands.Cog):
|
|||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
code, output = await run_git_log(n)
|
code, output = await run_git_log(n)
|
||||||
|
|
||||||
color = C_OK if code == 0 else C_ERR
|
await ctx.reply(embed=discord.Embed(
|
||||||
await ctx.reply(embed=_embed(
|
title=f"Last {n} commits",
|
||||||
f"📋 Last {n} Commits",
|
description=f"```\n{truncate(output)}\n```",
|
||||||
f"```\n{truncate(output)}\n```",
|
color=C_OK if code == 0 else C_ERR,
|
||||||
color,
|
|
||||||
))
|
))
|
||||||
|
|
||||||
# ── !logs ─────────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@commands.command(name="logs")
|
@commands.command(name="logs")
|
||||||
async def logs(self, ctx: commands.Context, n: int = 40):
|
async def logs(self, ctx: commands.Context, n: int = 40):
|
||||||
"""Show the last N lines of the service journal. Default: 40."""
|
|
||||||
if not self._is_owner(ctx):
|
if not self._is_owner(ctx):
|
||||||
return
|
return
|
||||||
n = max(1, min(n, 100))
|
n = max(1, min(n, 100))
|
||||||
@@ -181,45 +151,34 @@ class GitPull(commands.Cog):
|
|||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
code, output = await run_journalctl(n)
|
code, output = await run_journalctl(n)
|
||||||
|
|
||||||
color = C_OK if code == 0 else C_ERR
|
await ctx.reply(embed=discord.Embed(
|
||||||
await ctx.reply(embed=_embed(
|
title=f"Logs ({n} lines)",
|
||||||
f"📄 Logs — {SERVICE_NAME} (last {n} lines)",
|
description=f"```\n{truncate(output)}\n```",
|
||||||
f"```\n{truncate(output)}\n```",
|
color=C_OK if code == 0 else C_ERR,
|
||||||
color,
|
|
||||||
))
|
))
|
||||||
|
|
||||||
# ── !status ───────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@commands.command(name="status")
|
@commands.command(name="status")
|
||||||
async def status(self, ctx: commands.Context):
|
async def status(self, ctx: commands.Context):
|
||||||
"""Show systemd service status."""
|
|
||||||
if not self._is_owner(ctx):
|
if not self._is_owner(ctx):
|
||||||
return
|
return
|
||||||
|
|
||||||
async with ctx.typing():
|
async with ctx.typing():
|
||||||
code, output = await run_sudo_systemctl("status")
|
code, output = await run_sudo_systemctl("status")
|
||||||
|
|
||||||
color = C_OK if code == 0 else C_ERR
|
await ctx.reply(embed=discord.Embed(
|
||||||
await ctx.reply(embed=_embed(
|
title=f"Service Status: {SERVICE_NAME}",
|
||||||
f"{'✓' if code == 0 else '✗'} Status — {SERVICE_NAME}",
|
description=f"```\n{truncate(output)}\n```",
|
||||||
f"```\n{truncate(output)}\n```",
|
color=C_OK if code == 0 else C_ERR,
|
||||||
color,
|
|
||||||
))
|
))
|
||||||
|
|
||||||
# ── !restart ──────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@commands.command(name="restart")
|
@commands.command(name="restart")
|
||||||
async def restart(self, ctx: commands.Context):
|
async def restart(self, ctx: commands.Context):
|
||||||
"""Restart the bot service."""
|
|
||||||
if not self._is_owner(ctx):
|
if not self._is_owner(ctx):
|
||||||
return
|
return
|
||||||
await self._do_restart(ctx)
|
await self._do_restart(ctx)
|
||||||
|
|
||||||
# ── !update ───────────────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
@commands.command(name="update")
|
@commands.command(name="update")
|
||||||
async def update(self, ctx: commands.Context):
|
async def update(self, ctx: commands.Context):
|
||||||
"""Pull latest changes and restart if anything changed."""
|
|
||||||
if not self._is_owner(ctx):
|
if not self._is_owner(ctx):
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -227,23 +186,23 @@ class GitPull(commands.Cog):
|
|||||||
code, output = await run_git_pull()
|
code, output = await run_git_pull()
|
||||||
|
|
||||||
if code != 0:
|
if code != 0:
|
||||||
return await ctx.reply(embed=_embed(
|
return await ctx.reply(embed=discord.Embed(
|
||||||
"✗ Update Failed",
|
title="Update Failed",
|
||||||
f"Pull failed — restart skipped.\n```\n{truncate(output)}\n```",
|
description=f"git pull failed, restart skipped.\n```\n{truncate(output)}\n```",
|
||||||
C_ERR,
|
color=C_ERR,
|
||||||
))
|
))
|
||||||
|
|
||||||
if "Already up to date" in output:
|
if "Already up to date" in output:
|
||||||
return await ctx.reply(embed=_embed(
|
return await ctx.reply(embed=discord.Embed(
|
||||||
"✓ Already Up to Date",
|
title="Update",
|
||||||
"Nothing new to pull. Restart skipped.",
|
description="No changes to pull. Restart skipped.",
|
||||||
C_WARN,
|
color=C_WARN,
|
||||||
))
|
))
|
||||||
|
|
||||||
await ctx.reply(embed=_embed(
|
await ctx.reply(embed=discord.Embed(
|
||||||
"✓ Updated",
|
title="Update",
|
||||||
f"```\n{truncate(output)}\n```",
|
description=f"Pulled changes:\n```\n{truncate(output)}\n```",
|
||||||
C_OK,
|
color=C_OK,
|
||||||
))
|
))
|
||||||
await self._do_restart(ctx)
|
await self._do_restart(ctx)
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user