make goober export some env variables for cogs to use and add webserver

This commit is contained in:
WhatDidYouExpect 2025-03-30 18:53:57 +02:00
parent c72368010d
commit b5c8895b4d
6 changed files with 429 additions and 8 deletions

2
.gitignore vendored
View file

@ -3,4 +3,4 @@ __pycache__
current_version.txt
MEMORY_LOADED
memory.json
markov_model.pkl
*.pkl

17
bot.py
View file

@ -173,7 +173,9 @@ def generate_sha256_of_current_file():
latest_version = "0.0.0"
local_version = "0.14.8.1"
local_version = "0.14.8.2"
os.environ['gooberlocal_version'] = local_version
os.environ['gooberlatest_version'] = latest_version
def check_for_update():
if ALIVEPING == "false":
@ -327,10 +329,13 @@ def ping_server():
response = requests.post(VERSION_URL+"/ping", json=payload)
if response.status_code == 200:
print(f"{GREEN}{get_translation(LOCALE, 'goober_ping_success').format(NAME=NAME)}{RESET}")
os.environ['gooberauthenticated'] = 'Yes'
else:
print(f"{RED}{get_translation(LOCALE, 'goober_ping_fail')} {response.status_code}{RESET}")
os.environ['gooberauthenticated'] = 'No'
except Exception as e:
print(f"{RED}{get_translation(LOCALE, 'goober_ping_fail2')} {str(e)}{RESET}")
os.environ['gooberauthenticated'] = 'No'
positive_gifs = os.getenv("POSITIVE_GIFS").split(',')
@ -434,6 +439,8 @@ async def talk(ctx, sentence_size: int = 5):
combined_message = f"{coherent_response}\n[jif]({gif_url})"
else:
combined_message = coherent_response
print(combined_message)
os.environ['gooberlatestgen'] = combined_message
await send_message(ctx, combined_message)
else:
await send_message(ctx, f"{get_translation(LOCALE, 'command_talk_generation_fail')}")
@ -511,6 +518,14 @@ async def on_message(message):
# process any commands in the message
await bot.process_commands(message)
@bot.event
async def on_interaction(interaction):
if interaction.type == discord.InteractionType.application_command:
if interaction.user.id in BLACKLISTED_USERS:
return
print(f"{get_translation(LOCALE, 'command_ran_s').format(interaction=interaction)}{interaction.data['name']}")
@bot.hybrid_command(description=f"{get_translation(LOCALE, 'command_desc_ping')}")
async def ping(ctx):
await ctx.defer()

View file

@ -1,18 +1,24 @@
# goobers custom commands
[Hello World!](https://github.com/WhatDidYouExpect/goobercustomcommands/blob/main/customcommands/hello.py)
[Hello World!](https://github.com/WhatDidYouExpect/goober/blob/main/cogs/hello.py)
by expect
[WhoAmI (lists username and nickname)](https://github.com/WhatDidYouExpect/goober/blob/main/customcommands/whoami.py)
[WhoAmI (lists username and nickname)](https://github.com/WhatDidYouExpect/goober/blob/main/cogs/whoami.py)
by PowerPCFan
[Cog Manager](https://github.com/WhatDidYouExpect/goober/blob/main/customcommands/cogmanager.py)
[Cog Manager](https://github.com/WhatDidYouExpect/goober/blob/main/cogs/cogmanager.py)
by expect
[TensorFlow integration](https://github.com/WhatDidYouExpect/goober/blob/main/customcommands/tf.py)
[TensorFlow integration](https://github.com/WhatDidYouExpect/goober/blob/main/cogs/tf.py)
by SuperSilly2 (requires Python 3.7 - 3.10, tensorflow-metal/tensorflow-gpu and tensorflow/tensorflow-macos)
[Web Scraper](https://raw.githubusercontent.com/WhatDidYouExpect/goober/refs/heads/main/customcommands/webscraper.py)
[Web Scraper](https://raw.githubusercontent.com/WhatDidYouExpect/goober/refs/heads/main/cogs/webscraper.py)
by expect (requires goober version 0.11.7.2 or higher)
[Status Changer](https://raw.githubusercontent.com/WhatDidYouExpect/goober/refs/heads/main/customcommands/songchanger.py)
[Status Changer](https://raw.githubusercontent.com/WhatDidYouExpect/goober/refs/heads/main/cogs/songchanger.py)
by expect (requires goober version 0.11.8 or higher)
[Status Changer](https://raw.githubusercontent.com/WhatDidYouExpect/goober/refs/heads/main/cogs/songchanger.py)
by expect (requires goober version 0.11.8 or higher)
[webUI](https://raw.githubusercontent.com/WhatDidYouExpect/goober/refs/heads/main/cogs/webserver.py)
by expect (requires goober version 0.11.8 or higher)

118
cogs/styles.css Normal file
View file

@ -0,0 +1,118 @@
.topnav {
background-color: rgb(95, 27, 27);
overflow: hidden;
display: flex;
flex-wrap: wrap;
justify-content: center;
padding: 10px;
gap: 20px;
}
.topnav a {
color: #f2f2f2;
text-align: center;
text-decoration: none;
font-size: 17px;
}
.topnav a:hover {
background-color: #ddd;
color: black;
}
.stat-item {
display: flex;
align-items: center;
gap: 5px;
color: white;
font-size: 14px;
}
.stat-title {
font-weight: bold;
color: #ccc;
}
body {
background-color: black;
color: white;
font-family: Arial, sans-serif;
}
a {
color: red;
}
.stats-container {
max-width: 800px;
margin: 20px auto;
padding: 20px;
background-color: #1a1a1a;
border-radius: 8px;
text-align: left;
}
.balls {
text-align: right;
}
.stat-card {
background-color: #2a2a2a;
padding: 15px;
margin: 10px 0;
border-radius: 5px;
border-left: 4px solid rgb(95, 27, 27);
}
.guild-list {
max-height: 300px;
overflow-y: auto;
background-color: #2a2a2a;
padding: 10px;
border-radius: 5px;
}
.guild-item {
padding: 5px 0;
border-bottom: 1px solid #444;
}
.guild-item:last-child {
border-bottom: none;
}
hr {
border-color: rgb(95, 27, 27);
margin: 20px 0;
}
.center {
text-align: center;
}
.stat-container-row {
display: flex;
justify-content: space-between;
gap: 20px;
margin-bottom: 10px;
}
.bot-info {
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.bot-avatar {
width: 50px;
height: 50px;
border-radius: 0%;
}
button {
background-color:black;
color: white;
cursor: pointer;
}

281
cogs/webserver.py Normal file
View file

@ -0,0 +1,281 @@
import discord
from discord.ext import commands, tasks
import asyncio
from aiohttp import web
import psutil
import os
import json
from datetime import datetime
import time
import aiohttp
from aiohttp import WSMsgType
class GooberWeb(commands.Cog):
def __init__(self, bot):
self.bot = bot
self.app = web.Application()
self.runner = None
self.site = None
self.last_command = "No commands executed yet"
self.last_command_time = "Never"
self.start_time = time.time()
self.websockets = set()
self.app.add_routes([
web.get('/', self.handle_index),
web.get('/changesong', self.handle_changesong),
web.get('/stats', self.handle_stats),
web.get('/data', self.handle_json_data),
web.get('/ws', self.handle_websocket),
web.get('/styles.css', self.handle_css),
])
self.bot.loop.create_task(self.start_web_server())
self.update_clients.start()
async def start_web_server(self):
self.runner = web.AppRunner(self.app)
await self.runner.setup()
self.site = web.TCPSite(self.runner, '0.0.0.0', 8080)
await self.site.start()
print("Goober web server started on port 8080")
async def stop_web_server(self):
await self.site.stop()
await self.runner.cleanup()
print("Web server stopped")
def cog_unload(self):
self.update_clients.cancel()
self.bot.loop.create_task(self.stop_web_server())
@tasks.loop(seconds=5)
async def update_clients(self):
if not self.websockets:
return
stats = await self.get_bot_stats()
message = json.dumps(stats)
for ws in set(self.websockets):
try:
await ws.send_str(message)
except ConnectionResetError:
self.websockets.remove(ws)
except Exception as e:
print(f"Error sending to websocket: {e}")
self.websockets.remove(ws)
async def handle_websocket(self, request):
ws = web.WebSocketResponse()
await ws.prepare(request)
self.websockets.add(ws)
try:
async for msg in ws:
if msg.type == WSMsgType.ERROR:
print(f"WebSocket error: {ws.exception()}")
finally:
self.websockets.remove(ws)
return ws
async def handle_css(self, request):
css_path = os.path.join(os.path.dirname(__file__), 'styles.css')
if os.path.exists(css_path):
return web.FileResponse(css_path)
return web.Response(text="CSS file not found", status=404)
@commands.Cog.listener()
async def on_message(self, message):
if message.author.bot:
return
ctx = await self.bot.get_context(message)
if ctx.valid and ctx.command:
self._update_command_stats(ctx.command.name, ctx.author)
@commands.Cog.listener()
async def on_app_command_completion(self, interaction, command):
self._update_command_stats(command.name, interaction.user)
def _update_command_stats(self, command_name, user):
self.last_command = f"{command_name} (by {user.name}#{user.discriminator})"
self.last_command_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
if self.websockets:
asyncio.create_task(self.update_clients())
async def get_bot_stats(self):
process = psutil.Process(os.getpid())
mem_info = process.memory_full_info()
cpu_percent = psutil.cpu_percent()
process_cpu = process.cpu_percent()
memory_json_size = "N/A"
if os.path.exists("memory.json"):
memory_json_size = f"{os.path.getsize('memory.json') / 1024:.2f} KB"
guilds = sorted(self.bot.guilds, key=lambda g: g.member_count, reverse=True)
guild_info = [f"{g.name} ({g.member_count} members)" for g in guilds]
uptime_seconds = int(time.time() - self.start_time)
uptime_str = f"{uptime_seconds // 86400}d {(uptime_seconds % 86400) // 3600}h {(uptime_seconds % 3600) // 60}m {uptime_seconds % 60}s"
return {
"ram_usage": f"{mem_info.rss / 1024 / 1024:.2f} MB",
"cpu_usage": f"{process_cpu}%",
"system_cpu": f"{cpu_percent}%",
"memory_json_size": memory_json_size,
"guild_count": len(guilds),
"guilds": guild_info,
"last_command": self.last_command,
"last_command_time": self.last_command_time,
"bot_uptime": uptime_str,
"latency": f"{self.bot.latency * 1000:.2f} ms",
"bot_name": self.bot.user.name,
"bot_avatar_url": str(self.bot.user.avatar.url) if self.bot.user.avatar else "",
"authenticated": os.getenv("gooberauthenticated"),
"lastmsg": os.getenv("gooberlatestgen")
}
async def handle_update(self, request):
if os.path.exists("goob/update.py"):
return web.FileResponse("goob/update.py")
return web.Response(text="Update file not found", status=404)
async def handle_changesong(self, request):
song = request.query.get('song', '')
if song:
await self.bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=song))
return web.Response(text=f"Changed song to: {song}")
return web.Response(text="Please provide a song parameter", status=400)
async def handle_changes(self, request):
if os.path.exists("goob/changes.txt"):
return web.FileResponse("goob/changes.txt")
return web.Response(text="Changelog not found", status=404)
async def handle_index(self, request):
stats = await self.get_bot_stats()
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>goobs central</title>
<link rel="stylesheet" href="/styles.css">
</head>
<body>
<div class="topnav">
<div class="stat-item" id="ram-usage">
<span class="stat-title">RAM:</span>
<span>{stats['ram_usage']}</span>
</div>
<div class="stat-item" id="cpu-usage">
<span class="stat-title">CPU:</span>
<span>{stats['cpu_usage']}</span>
</div>
<div class="stat-item" id="system-cpu">
<span class="stat-title">System CPU:</span>
<span>{stats['system_cpu']}</span>
</div>
<div class="stat-item" id="latency">
<span class="stat-title">Latency:</span>
<span>{stats['latency']}</span>
</div>
<div class="stat-item" id="json-size">
<span class="stat-title">JSON Size:</span>
<span>{stats['memory_json_size']}</span>
</div>
<div class="stat-item" id="uptime">
<span class="stat-title">Uptime:</span>
<span>{stats['bot_uptime']}</span>
</div>
</div>
<div class="center">
<h2>
<div class="bot-info">
<img src="{stats['bot_avatar_url']}" alt="botvatar" class="bot-avatar" id="bot-avatar">
<span id="bot-name">{stats['bot_name']}</span>
</div>
</h2>
<hr>
<p>your stupid little goober that learns off other people's messages</p>
</div>
<div class="stat-container-row">
<div>
<div class="stat-title">Last Command</div>
<div id="last-command">{stats['last_command']}</div>
<div style="font-size: 0.9em; color: #999;" id="last-command-time">at {stats['last_command_time']}</div>
<br>
<div class="stat-title">Logged into goober central</div>
<div id="last-command">{stats['authenticated']}</div>
<br>
<div class="stat-title">Last generated message</div>
<div id="last-command">{stats['lastmsg']}</div>
<br>
<div class="stat-title">Change song</div>
<form action="/changesong" method="get">
<input type="text" name="song" placeholder="Enter song name..." style="background-color:black; color:white; solid #ccc;">
<button type="submit">
change song
</button>
</form>
</div>
<div class="balls">
<div class="stat-title">Servers (<span id="guild-count">{stats['guild_count']}</span>)</div>
<div id="guild-list">
{"".join(f'<div class="guild-item">{guild}</div>' for guild in stats['guilds'])}
</div>
</div>
</div>
<script>
const ws = new WebSocket('ws://' + window.location.host + '/ws');
ws.onmessage = function(event) {{
const data = JSON.parse(event.data);
document.getElementById('ram-usage').innerHTML = `<span class="stat-title">RAM:</span> <span>${{data.ram_usage}}</span>`;
document.getElementById('cpu-usage').innerHTML = `<span class="stat-title">CPU:</span> <span>${{data.cpu_usage}}</span>`;
document.getElementById('system-cpu').innerHTML = `<span class="stat-title">System CPU:</span> <span>${{data.system_cpu}}</span>`;
document.getElementById('latency').innerHTML = `<span class="stat-title">Latency:</span> <span>${{data.latency}}</span>`;
document.getElementById('json-size').innerHTML = `<span class="stat-title">JSON Size:</span> <span>${{data.memory_json_size}}</span>`;
document.getElementById('uptime').innerHTML = `<span class="stat-title">Uptime:</span> <span>${{data.bot_uptime}}</span>`;
document.getElementById('bot-name').textContent = data.bot_name;
const botAvatar = document.getElementById('bot-avatar');
if (botAvatar.src !== data.bot_avatar_url) {{
botAvatar.src = data.bot_avatar_url;
}}
document.getElementById('last-command').textContent = data.last_command;
document.getElementById('last-command-time').textContent = `at ${{data.last_command_time}}`;
document.getElementById('guild-count').textContent = data.guild_count;
document.getElementById('guild-list').innerHTML =
data.guilds.map(guild => `<div class="guild-item">${{guild}}</div>`).join('');
}};
ws.onclose = function() {{
console.log('WebSocket disconnected');
}};
</script>
</body>
</html>
"""
return web.Response(text=html_content, content_type='text/html')
async def handle_stats(self, request):
return await self.handle_index(request)
async def handle_json_data(self, request):
stats = await self.get_bot_stats()
return web.json_response(stats)
async def setup(bot):
await bot.add_cog(GooberWeb(bot))

View file

@ -57,6 +57,7 @@
"command_help_categories_admin": "Administration",
"command_help_categories_custom": "Custom Commands",
"command_ran": "Info: {message.author.name} ran {message.content}",
"command_ran_s": "Info: {interaction.user} ran ",
"command_desc_ping": "ping",
"command_ping_embed_desc": "Bot Latency:",
"command_ping_footer": "Requested by",