hyperv-host-shutdown-bot/main.py
2026-03-02 17:24:30 -08:00

77 lines
2.7 KiB
Python

import discord, os, asyncio, ctypes
from discord import app_commands
TOKEN = 'qwerty'
ALLOWED = {362372093423255552, 649288650441490433, 288102417386700800}
GRACE_PERIOD = 300
COOLDOWN = 60
bot = discord.Client(intents=discord.Intents.default())
tree = app_commands.CommandTree(bot)
@bot.event
async def on_ready():
print("bot is ready")
await tree.sync()
shutdown_pending = False
@tree.command(name="shutdown", description="shuts down the pc")
@app_commands.checks.cooldown(1, COOLDOWN)
async def shutdown(itn: discord.Interaction):
global shutdown_pending
if itn.user.id not in ALLOWED:
await itn.response.send_message("no")
return
shutdown_pending = True
await itn.response.send_message(f"shutting down in {GRACE_PERIOD // 60} minutes")
class LII(ctypes.Structure):
_fields_ = [("cbSize", ctypes.c_uint), ("dwTime", ctypes.c_uint)]
info = LII(cbSize=ctypes.sizeof(LII))
ctypes.windll.user32.GetLastInputInfo(ctypes.byref(info))
last_input = info.dwTime
i = 0
while i < GRACE_PERIOD:
await asyncio.sleep(1)
ctypes.windll.user32.GetLastInputInfo(ctypes.byref(info))
if not shutdown_pending:
return
if info.dwTime != last_input:
await itn.edit_original_response(content="someone is using the pc, cancelled")
shutdown_pending = False
return
i += 1
await itn.edit_original_response(content="saving vms...")
proc = await asyncio.create_subprocess_shell(
'powershell "Get-VM | Where State -eq Running | Stop-VM -Save"',
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE
)
_, stderr = await proc.communicate()
if proc.returncode != 0:
await itn.edit_original_response(content=f"something went wrong with the vms, cancelled\n```{stderr.decode()}```")
shutdown_pending = False
return
await itn.edit_original_response(content="vms saved, shutting down")
proc = await asyncio.create_subprocess_shell("shutdown /s /t 5")
await proc.communicate()
shutdown_pending = False
@shutdown.error
async def shutdown_error(itn: discord.Interaction, error: app_commands.AppCommandError):
if isinstance(error, app_commands.CommandOnCooldown):
await itn.response.send_message(f"wait {error.retry_after:.0f} seconds")
@tree.command(name="abort", description="cancels the shutdown")
async def abort(itn: discord.Interaction):
global shutdown_pending
if itn.user.id not in ALLOWED:
await itn.response.send_message("no")
return
if not shutdown_pending:
await itn.response.send_message("nothing to cancel")
return
shutdown_pending = False
await itn.response.send_message("cancelled")
bot.run(TOKEN)