Merge pull request #13 from gooberinc/dev

Dev
This commit is contained in:
WhatDidYouExpect 2025-07-09 15:05:36 +02:00 committed by GitHub
commit 57c7196c5c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 156 additions and 103 deletions

1
.gitignore vendored
View file

@ -12,3 +12,4 @@ received_memory.json
translation_report.txt translation_report.txt
translationcompleteness.py translationcompleteness.py
modules/volta modules/volta
log.txt

70
bot.py
View file

@ -11,9 +11,25 @@ import uuid
import asyncio import asyncio
import sys import sys
from typing import List, Dict, Set, Optional, Tuple, Any, Union, Callable, Coroutine, TypeVar, Type from typing import List, Dict, Set, Optional, Tuple, Any, Union, Callable, Coroutine, TypeVar, Type
import logging
from modules.globalvars import * from modules.globalvars import *
from modules.prestartchecks import start_checks from modules.prestartchecks import start_checks
from modules.logger import GooberFormatter
import logging
logger = logging.getLogger("goober")
logger.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)
console_handler.setFormatter(GooberFormatter())
file_handler = logging.FileHandler("log.txt", mode="w+", encoding="UTF-8")
file_handler.setLevel(logging.DEBUG)
file_handler.setFormatter(GooberFormatter(colors=False))
logger.addHandler(console_handler)
logger.addHandler(file_handler)
# Print splash text and check for updates # Print splash text and check for updates
print(splashtext) # Print splash text (from modules/globalvars.py) print(splashtext) # Print splash text (from modules/globalvars.py)
@ -65,7 +81,7 @@ bot: commands.Bot = commands.Bot(
memory: List[str] = load_memory() memory: List[str] = load_memory()
markov_model: Optional[markovify.Text] = load_markov_model() markov_model: Optional[markovify.Text] = load_markov_model()
if not markov_model: if not markov_model:
print(f"{RED}{(_('markov_model_not_found'))}{RESET}") logger.error(_('markov_model_not_found'))
memory = load_memory() memory = load_memory()
markov_model = train_markov_model(memory) markov_model = train_markov_model(memory)
@ -79,9 +95,9 @@ async def load_cogs_from_folder(bot, folder_name="assets/cogs"):
module_path = folder_name.replace("/", ".").replace("\\", ".") + f".{cog_name}" module_path = folder_name.replace("/", ".").replace("\\", ".") + f".{cog_name}"
try: try:
await bot.load_extension(module_path) await bot.load_extension(module_path)
print(f"{GREEN}{(_('loaded_cog'))} {cog_name}{RESET}") logger.info(f"{(_('loaded_cog'))} {cog_name}")
except Exception as e: except Exception as e:
print(f"{RED}{(_('cog_fail'))} {cog_name} {e}{RESET}") logger.error(f"{(_('cog_fail'))} {cog_name} {e}")
traceback.print_exc() traceback.print_exc()
async def fetch_active_users() -> str: async def fetch_active_users() -> str:
@ -92,7 +108,7 @@ async def fetch_active_users() -> str:
else: else:
return "?" return "?"
except Exception as e: except Exception as e:
print(f"{RED}{(_('error_fetching_active_users'))}{RESET} {e}") logger.e(f"{_('error_fetching_active_users')} {RESET} {e}")
return "?" return "?"
async def send_alive_ping_periodically() -> None: async def send_alive_ping_periodically() -> None:
@ -100,7 +116,7 @@ async def send_alive_ping_periodically() -> None:
try: try:
requests.post(f"{VERSION_URL}/aliveping", json={"name": NAME}) requests.post(f"{VERSION_URL}/aliveping", json={"name": NAME})
except Exception as e: except Exception as e:
print(f"{RED}{(_('error_sending_alive_ping'))}{RESET} {e}") logger.error(f"{(_('error_sending_alive_ping'))}{RESET} {e}")
await asyncio.sleep(60) await asyncio.sleep(60)
# Event: Called when the bot is ready # Event: Called when the bot is ready
@ -117,21 +133,21 @@ async def on_ready() -> None:
await load_cogs_from_folder(bot) await load_cogs_from_folder(bot)
try: try:
synced: List[discord.app_commands.AppCommand] = await bot.tree.sync() synced: List[discord.app_commands.AppCommand] = await bot.tree.sync()
print(f"{GREEN}{_('synced_commands')} {len(synced)} {(_('synced_commands2'))} {RESET}") logger.info(f"{_('synced_commands')} {len(synced)} {(_('synced_commands2'))}")
slash_commands_enabled = True slash_commands_enabled = True
ping_server() # ping_server from modules/central.py ping_server() # ping_server from modules/central.py
active_users: str = await fetch_active_users() active_users: str = await fetch_active_users()
print(f"{GREEN}{(_('active_users:'))} {active_users}{RESET}") logger.info(f"{(_('active_users:'))} {active_users}")
print(f"{GREEN}{(_('started')).format(name=NAME)}{RESET}") logger.info(f"{(_('started')).format(name=NAME)}")
bot.loop.create_task(send_alive_ping_periodically()) bot.loop.create_task(send_alive_ping_periodically())
except discord.errors.Forbidden as perm_error: except discord.errors.Forbidden as perm_error:
print(f"{RED}Permission error while syncing commands: {perm_error}{RESET}") logger.error(f"Permission error while syncing commands: {perm_error}")
print(f"{RED}Make sure the bot has the 'applications.commands' scope and is invited with the correct permissions.{RESET}") logger.error("Make sure the bot has the 'applications.commands' scope and is invited with the correct permissions.")
quit() quit()
except Exception as e: except Exception as e:
print(f"{RED}{(_('fail_commands_sync'))} {e}{RESET}") logger.error(f"{_('fail_commands_sync')} {e}")
traceback.print_exc() traceback.print_exc()
quit() quit()
@ -181,13 +197,13 @@ async def retrain(ctx: commands.Context) -> None:
for i, data in enumerate(memory): for i, data in enumerate(memory):
processed_data += 1 processed_data += 1
if processed_data % 1000 == 0 or processed_data == data_size: if processed_data % 1000 == 0 or processed_data == data_size:
await send_message(ctx, f"{(_('command_markov_retraining')).format(processed_data=processed_data, data_size=data_size)}", edit=True, message_reference=processing_message_ref) await send_message(ctx, f"{_('command_markov_retraining').format(processed_data=processed_data, data_size=data_size)}", edit=True, message_reference=processing_message_ref)
global markov_model global markov_model
markov_model = train_markov_model(memory) markov_model = train_markov_model(memory)
save_markov_model(markov_model) save_markov_model(markov_model)
await send_message(ctx, f"{(_('command_markov_retrain_successful')).format(data_size=data_size)}", edit=True, message_reference=processing_message_ref) await send_message(ctx, f"{_('command_markov_retrain_successful').format(data_size=data_size)}", edit=True, message_reference=processing_message_ref)
# Command: Generate a sentence using the Markov model # Command: Generate a sentence using the Markov model
@bot.hybrid_command(description=f"{(_('command_desc_talk'))}") @bot.hybrid_command(description=f"{(_('command_desc_talk'))}")
@ -219,7 +235,7 @@ async def talk(ctx: commands.Context, sentence_size: int = 5) -> None:
combined_message: str = f"{coherent_response}\n[jif]({gif_url})" combined_message: str = f"{coherent_response}\n[jif]({gif_url})"
else: else:
combined_message: str = coherent_response combined_message: str = coherent_response
print(combined_message) logger.info(combined_message)
os.environ['gooberlatestgen'] = combined_message os.environ['gooberlatestgen'] = combined_message
await send_message(ctx, combined_message) await send_message(ctx, combined_message)
else: else:
@ -247,7 +263,7 @@ async def impact(ctx: commands.Context) -> None:
else: else:
fallback_image: Optional[str] = get_random_asset_image() fallback_image: Optional[str] = get_random_asset_image()
if fallback_image is None: if fallback_image is None:
await ctx.reply((_('no_image_available'))) await ctx.reply(_('no_image_available'))
return return
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1]) temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
shutil.copy(fallback_image, temp_input) shutil.copy(fallback_image, temp_input)
@ -255,7 +271,7 @@ async def impact(ctx: commands.Context) -> None:
else: else:
fallback_image = get_random_asset_image() fallback_image = get_random_asset_image()
if fallback_image is None: if fallback_image is None:
await ctx.reply((_('no_image_available'))) await ctx.reply(_('no_image_available'))
return return
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1]) temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
shutil.copy(fallback_image, temp_input) shutil.copy(fallback_image, temp_input)
@ -266,7 +282,7 @@ async def impact(ctx: commands.Context) -> None:
if output_path is None or not os.path.isfile(output_path): if output_path is None or not os.path.isfile(output_path):
if temp_input and os.path.exists(temp_input): if temp_input and os.path.exists(temp_input):
os.remove(temp_input) os.remove(temp_input)
await ctx.reply((_('failed_generate_image'))) await ctx.reply(_('failed_generate_image'))
return return
await ctx.send(file=discord.File(output_path)) await ctx.send(file=discord.File(output_path))
@ -296,7 +312,7 @@ async def demotivator(ctx: commands.Context) -> None:
else: else:
fallback_image: Optional[str] = get_random_asset_image() fallback_image: Optional[str] = get_random_asset_image()
if fallback_image is None: if fallback_image is None:
await ctx.reply((_('no_image_available'))) await ctx.reply(_('no_image_available'))
return return
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1]) temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
shutil.copy(fallback_image, temp_input) shutil.copy(fallback_image, temp_input)
@ -304,7 +320,7 @@ async def demotivator(ctx: commands.Context) -> None:
else: else:
fallback_image = get_random_asset_image() fallback_image = get_random_asset_image()
if fallback_image is None: if fallback_image is None:
await ctx.reply((_('no_image_available'))) await ctx.reply(_('no_image_available'))
return return
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1]) temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
shutil.copy(fallback_image, temp_input) shutil.copy(fallback_image, temp_input)
@ -375,7 +391,7 @@ async def on_message(message: discord.Message) -> None:
return return
if message.content.startswith((f"{PREFIX}talk", f"{PREFIX}mem", f"{PREFIX}help", f"{PREFIX}stats", f"{PREFIX}")): if message.content.startswith((f"{PREFIX}talk", f"{PREFIX}mem", f"{PREFIX}help", f"{PREFIX}stats", f"{PREFIX}")):
print(f"{(_('command_ran')).format(message=message)}") logger.info(f"{(_('command_ran')).format(message=message)}")
await bot.process_commands(message) await bot.process_commands(message)
return return
@ -399,14 +415,14 @@ async def on_message(message: discord.Message) -> None:
try: try:
await message.add_reaction(emoji) await message.add_reaction(emoji)
except Exception as e: except Exception as e:
print(f"Failed to react with emoji: {e}") logger.info(f"Failed to react with emoji: {e}")
await bot.process_commands(message) await bot.process_commands(message)
# Event: Called on every interaction (slash command, etc.) # Event: Called on every interaction (slash command, etc.)
@bot.event @bot.event
async def on_interaction(interaction: discord.Interaction) -> None: async def on_interaction(interaction: discord.Interaction) -> None:
print(f"{(_('command_ran_s')).format(interaction=interaction)}{interaction.data['name']}") logger.info(f"{(_('command_ran_s')).format(interaction=interaction)}{interaction.data['name']}")
# Global check: Block blacklisted users from running commands # Global check: Block blacklisted users from running commands
@bot.check @bot.check
@ -415,11 +431,11 @@ async def block_blacklisted(ctx: commands.Context) -> bool:
try: try:
if isinstance(ctx, discord.Interaction): if isinstance(ctx, discord.Interaction):
if not ctx.response.is_done(): if not ctx.response.is_done():
await ctx.response.send_message((_('blacklisted')), ephemeral=True) await ctx.response.send_message(_('blacklisted'), ephemeral=True)
else: else:
await ctx.followup.send((_('blacklisted')), ephemeral=True) await ctx.followup.send(_('blacklisted'), ephemeral=True)
else: else:
await ctx.send((_('blacklisted_user')), ephemeral=True) await ctx.send(_('blacklisted_user'), ephemeral=True)
except: except:
pass pass
return False return False
@ -483,7 +499,7 @@ async def mem(ctx: commands.Context) -> None:
return return
command: str = """curl -F "reqtype=fileupload" -F "time=1h" -F "fileToUpload=@memory.json" https://litterbox.catbox.moe/resources/internals/api.php""" command: str = """curl -F "reqtype=fileupload" -F "time=1h" -F "fileToUpload=@memory.json" https://litterbox.catbox.moe/resources/internals/api.php"""
memorylitter: subprocess.CompletedProcess = subprocess.run(command, shell=True, capture_output=True, text=True) memorylitter: subprocess.CompletedProcess = subprocess.run(command, shell=True, capture_output=True, text=True)
print(memorylitter) logger.debug(memorylitter)
await send_message(ctx, memorylitter.stdout.strip()) await send_message(ctx, memorylitter.stdout.strip())
# Helper: Improve sentence coherence (simple capitalization fix) # Helper: Improve sentence coherence (simple capitalization fix)

View file

@ -3,17 +3,19 @@ import os
import modules.globalvars as gv import modules.globalvars as gv
from modules.volta.main import _ from modules.volta.main import _
from modules.markovmemory import get_file_info from modules.markovmemory import get_file_info
import logging
logger = logging.getLogger("goober")
# Ping the server to check if it's alive and send some info # Ping the server to check if it's alive and send some info
def ping_server(): def ping_server():
if gv.ALIVEPING == "false": if gv.ALIVEPING == "false":
# If pinging is disabled, print message and set environment variable # If pinging is disabled, print message and set environment variable
print(f"{gv.YELLOW}{(_('pinging_disabled'))}{gv.RESET}") print(f"{gv.YELLOW}{(_('pinging_disabled'))}")
os.environ['gooberauthenticated'] = 'No' os.environ['gooberauthenticated'] = 'No'
return return
# Get server alert message # Get server alert message
goobres = requests.get(f"{gv.VERSION_URL}/alert") goobres = requests.get(f"{gv.VERSION_URL}/alert")
print(f"{(_('goober_server_alert'))}{goobres.text}") logger.info(f"{(_('goober_server_alert'))}{goobres.text}")
# Gather file info for payload # Gather file info for payload
file_info = get_file_info(gv.MEMORY_FILE) file_info = get_file_info(gv.MEMORY_FILE)
payload = { payload = {
@ -28,15 +30,15 @@ def ping_server():
response = requests.post(gv.VERSION_URL+"/ping", json=payload) response = requests.post(gv.VERSION_URL+"/ping", json=payload)
if response.status_code == 200: if response.status_code == 200:
# Success: print message and set environment variable # Success: print message and set environment variable
print(f"{gv.GREEN}{(_('goober_ping_success')).format(NAME=gv.NAME)}{gv.RESET}") logger.info(f"{(_('goober_ping_success')).format(NAME=gv.NAME)}")
os.environ['gooberauthenticated'] = 'Yes' os.environ['gooberauthenticated'] = 'Yes'
else: else:
# Failure: print error and set environment variable # Failure: print error and set environment variable
print(f"{gv.RED}{(_('goober_ping_fail'))} {response.status_code}{gv.RESET}") logger.error(f"{(_('goober_ping_fail'))} {response.status_code}")
os.environ['gooberauthenticated'] = 'No' os.environ['gooberauthenticated'] = 'No'
except Exception as e: except Exception as e:
# Exception: print error and set environment variable # Exception: print error and set environment variable
print(f"{gv.RED}{(_('goober_ping_fail2'))} {str(e)}{gv.RESET}") logger.error(f"{(_('goober_ping_fail2'))} {str(e)}")
os.environ['gooberauthenticated'] = 'No' os.environ['gooberauthenticated'] = 'No'
# Check if a given name is available for registration # Check if a given name is available for registration
@ -52,11 +54,11 @@ def is_name_available(NAME):
return data.get("available", False) return data.get("available", False)
else: else:
# Print error if request failed # Print error if request failed
print(f"{(_('name_check'))}", response.json()) logger.e(f"{(_('name_check'))}", response.json())
return False return False
except Exception as e: except Exception as e:
# Print exception if request failed # Print exception if request failed
print(f"{(_('name_check2'))}", e) logger.error(f"{(_('name_check2'))}", e)
return False return False
# Register a new name with the server # Register a new name with the server
@ -70,7 +72,7 @@ def register_name(NAME):
if os.getenv("gooberTOKEN"): if os.getenv("gooberTOKEN"):
return return
# Name taken: print error and exit # Name taken: print error and exit
print(f"{gv.RED}{(_('name_taken'))}{gv.RESET}") logger.critical(f"{(_('name_taken'))}")
quit() quit()
# Register the name # Register the name
response = requests.post(f"{gv.VERSION_URL}/register", json={"name": NAME}, headers={"Content-Type": "application/json"}) response = requests.post(f"{gv.VERSION_URL}/register", json={"name": NAME}, headers={"Content-Type": "application/json"})
@ -79,18 +81,18 @@ def register_name(NAME):
token = data.get("token") token = data.get("token")
if not os.getenv("gooberTOKEN"): if not os.getenv("gooberTOKEN"):
# Print instructions to add token and exit # Print instructions to add token and exit
print(f"{gv.GREEN}{(_('add_token')).format(token=token)} gooberTOKEN=<token>.{gv.gv.RESET}") logger.info(f"{(_('add_token')).format(token=token)} gooberTOKEN=<token>.")
quit() quit()
else: else:
print(f"{gv.GREEN}{gv.gv.RESET}") print(f"{gv.GREEN}{gv.gv.RESET}")
return token return token
else: else:
# Print error if registration failed # Print error if registration failed
print(f"{gv.RED}{(_('token_exists')).format()}{gv.RESET}", response.json()) logger.critical(f"{gv.RED}{(_('token_exists')).format()}", response.json())
return None return None
except Exception as e: except Exception as e:
# Print exception if registration failed # Print exception if registration failed
print(f"{gv.RED}{(_('registration_error')).format()}{gv.RESET}", e) logger.critical(f"{gv.RED}{(_('registration_error')).format()}", e)
return None return None
# Attempt to register the name at module load # Attempt to register the name at module load

View file

@ -10,6 +10,7 @@ ANSI = "\033["
RED = f"{ANSI}31m" RED = f"{ANSI}31m"
GREEN = f"{ANSI}32m" GREEN = f"{ANSI}32m"
YELLOW = f"{ANSI}33m" YELLOW = f"{ANSI}33m"
PURPLE = f"{ANSI}35m"
DEBUG = f"{ANSI}1;30m" DEBUG = f"{ANSI}1;30m"
RESET = f"{ANSI}0m" RESET = f"{ANSI}0m"
VERSION_URL = "https://goober.expect.ovh" VERSION_URL = "https://goober.expect.ovh"
@ -40,5 +41,5 @@ latest_version = "0.0.0"
local_version = "2.1.3" local_version = "2.1.3"
os.environ['gooberlocal_version'] = local_version os.environ['gooberlocal_version'] = local_version
REACT = os.getenv("REACT") REACT = os.getenv("REACT")
beta = False # this makes goober think its a beta version, so it will not update to the latest stable version or run any version checks beta = True # this makes goober think its a beta version, so it will not update to the latest stable version or run any version checks

25
modules/logger.py Normal file
View file

@ -0,0 +1,25 @@
import logging
from modules.globalvars import *
class GooberFormatter(logging.Formatter):
def __init__(self, colors: bool = True): # Disable colors for TXT output
self.colors = colors
self._format = f"[ %(levelname)-8s ]: %(message)s {DEBUG} [%(asctime)s.%(msecs)03d] (%(filename)s:%(funcName)s) {RESET}"
self.FORMATS = {
logging.DEBUG: DEBUG + self._format + RESET,
logging.INFO: self._format.replace("%(levelname)-8s", f"{GREEN}%(levelname)-8s{RESET}"),
logging.WARNING: YELLOW + self._format + RESET,
logging.ERROR: RED + self._format + RESET,
logging.CRITICAL: PURPLE + self._format + RESET
}
def format(self, record: logging.LogRecord):
if self.colors:
log_fmt = self.FORMATS.get(record.levelno) # Add colors
else:
log_fmt = self._format # Just use the default format
formatter = logging.Formatter(log_fmt, datefmt="%m/%d/%y %H:%M:%S")
return formatter.format(record)

View file

@ -4,7 +4,8 @@ import markovify
import pickle import pickle
from modules.globalvars import * from modules.globalvars import *
from modules.volta.main import _ from modules.volta.main import _
import logging
logger = logging.getLogger("goober")
# Get file size and line count for a given file path # Get file size and line count for a given file path
def get_file_info(file_path): def get_file_info(file_path):
try: try:
@ -47,15 +48,15 @@ def train_markov_model(memory, additional_data=None):
def save_markov_model(model, filename='markov_model.pkl'): def save_markov_model(model, filename='markov_model.pkl'):
with open(filename, 'wb') as f: with open(filename, 'wb') as f:
pickle.dump(model, f) pickle.dump(model, f)
print(f"Markov model saved to {filename}.") logger.info(f"Markov model saved to {filename}.")
# Load the Markov model from a pickle file # Load the Markov model from a pickle file
def load_markov_model(filename='markov_model.pkl'): def load_markov_model(filename='markov_model.pkl'):
try: try:
with open(filename, 'rb') as f: with open(filename, 'rb') as f:
model = pickle.load(f) model = pickle.load(f)
print(f"{GREEN}{_('model_loaded')} {filename}.{RESET}") logger.info(f"{_('model_loaded')} {filename}.{RESET}")
return model return model
except FileNotFoundError: except FileNotFoundError:
print(f"{RED}{filename} {_('not_found')}{RESET}") logger.error(f"{filename} {_('not_found')}{RESET}")
return None return None

View file

@ -10,6 +10,9 @@ import json
import re import re
from spacy.util import is_package from spacy.util import is_package
import importlib.metadata import importlib.metadata
import logging
logger = logging.getLogger("goober")
# import shutil # import shutil
psutilavaliable = True psutilavaliable = True
@ -18,20 +21,20 @@ try:
import psutil import psutil
except ImportError: except ImportError:
psutilavaliable = False psutilavaliable = False
print(RED, _('missing_requests_psutil'), RESET) logger.error(_('missing_requests_psutil'))
def check_for_model(): def check_for_model():
if is_package("en_core_web_sm"): if is_package("en_core_web_sm"):
print("Model is installed.") logger.info("Model is installed.")
else: else:
print("Model is not installed.") logger.info("Model is not installed.")
def iscloned(): def iscloned():
if os.path.exists(".git"): if os.path.exists(".git"):
return True return True
else: else:
print(f"{RED}{(_('not_cloned'))}{RESET}") logger.error(f"{_('not_cloned')}")
sys.exit(1) sys.exit(1)
def get_stdlib_modules(): def get_stdlib_modules():
@ -63,7 +66,7 @@ def check_requirements():
requirements_path = os.path.abspath(os.path.join(parent_dir, '..', 'requirements.txt')) requirements_path = os.path.abspath(os.path.join(parent_dir, '..', 'requirements.txt'))
if not os.path.exists(requirements_path): if not os.path.exists(requirements_path):
print(f"{RED}{(_('requirements_not_found')).format(path=requirements_path)}{RESET}") logger.error(f"{(_('requirements_not_found')).format(path=requirements_path)}")
return return
with open(requirements_path, 'r') as f: with open(requirements_path, 'r') as f:
@ -95,9 +98,9 @@ def check_requirements():
continue continue
requirements.add(pkg) requirements.add(pkg)
except Exception as e: except Exception as e:
print(f"{YELLOW}{(_('warning_failed_parse_imports')).format(filename=filename, error=e)}{RESET}") logger.warning(f"{(_('warning_failed_parse_imports')).format(filename=filename, error=e)}")
else: else:
print(f"{YELLOW}{(_('cogs_dir_not_found')).format(path=cogs_dir)}{RESET}") logger.warning(f"{(_('cogs_dir_not_found')).format(path=cogs_dir)}")
installed_packages = {dist.metadata['Name'].lower() for dist in importlib.metadata.distributions()} installed_packages = {dist.metadata['Name'].lower() for dist in importlib.metadata.distributions()}
missing = [] missing = []
@ -110,16 +113,16 @@ def check_requirements():
check_name = PACKAGE_ALIASES.get(req, req).lower() check_name = PACKAGE_ALIASES.get(req, req).lower()
if check_name in installed_packages: if check_name in installed_packages:
print(f"[ {GREEN}{(_('ok_installed')).format(package=check_name)}{RESET} ] {check_name}") logger.info(f"{_('ok_installed').format(package=check_name)} {check_name}")
else: else:
print(f"[ {RED}{(_('missing_package')).format(package=check_name)}{RESET} ] {check_name} {(_('missing_package2'))}") logger.error(f"{(_('missing_package')).format(package=check_name)} {check_name} {(_('missing_package2'))}")
missing.append(check_name) missing.append(check_name)
if missing: if missing:
print(RED, _('missing_packages_detected'), RESET) logger.error(_('missing_packages_detected'))
for pkg in missing: for pkg in missing:
print(f" - {pkg}") print(f" - {pkg}")
print((_('telling_goober_central')).format(url=VERSION_URL)) logger.info((_('telling_goober_central')).format(url=VERSION_URL))
payload = { payload = {
"name": NAME, "name": NAME,
"version": local_version, "version": local_version,
@ -129,10 +132,10 @@ def check_requirements():
try: try:
requests.post(VERSION_URL + "/ping", json=payload) # type: ignore requests.post(VERSION_URL + "/ping", json=payload) # type: ignore
except Exception as e: except Exception as e:
print(f"{RED}{(_('failed_to_contact')).format(url=VERSION_URL, error=e)}{RESET}") logger.error(f"{(_('failed_to_contact')).format(url=VERSION_URL, error=e)}")
sys.exit(1) sys.exit(1)
else: else:
print(_('all_requirements_satisfied')) logger.info(_('all_requirements_satisfied'))
def check_latency(): def check_latency():
host = "1.1.1.1" host = "1.1.1.1"
@ -158,16 +161,16 @@ def check_latency():
match = re.search(latency_pattern, result.stdout) match = re.search(latency_pattern, result.stdout)
if match: if match:
latency_ms = float(match.group(1)) latency_ms = float(match.group(1))
print((_('ping_to')).format(host=host, latency=latency_ms)) logger.info((_('ping_to')).format(host=host, latency=latency_ms))
if latency_ms > 300: if latency_ms > 300:
print(f"{YELLOW}{(_('high_latency'))}{RESET}") logger.warning(f"{(_('high_latency'))}")
else: else:
print(f"{YELLOW}{(_('could_not_parse_latency'))}{RESET}") logger.warning((_('could_not_parse_latency')))
else: else:
print(result.stderr) print(result.stderr)
print(f"{RED}{(_('ping_failed')).format(host=host)}{RESET}") logger.error(f"{(_('ping_failed')).format(host=host)}{RESET}")
except Exception as e: except Exception as e:
print(f"{RED}{(_('error_running_ping')).format(error=e)}{RESET}") logger.error((_('error_running_ping')).format(error=e))
def check_memory(): def check_memory():
if psutilavaliable == False: if psutilavaliable == False:
@ -178,21 +181,21 @@ def check_memory():
used_memory = memory_info.used / (1024 ** 3) used_memory = memory_info.used / (1024 ** 3)
free_memory = memory_info.available / (1024 ** 3) free_memory = memory_info.available / (1024 ** 3)
print((_('memory_usage')).format(used=used_memory, total=total_memory, percent=(used_memory / total_memory) * 100)) logger.info((_('memory_usage')).format(used=used_memory, total=total_memory, percent=(used_memory / total_memory) * 100))
if used_memory > total_memory * 0.9: if used_memory > total_memory * 0.9:
print(f"{YELLOW}{(_('memory_above_90')).format(percent=(used_memory / total_memory) * 100)}{RESET}") print(f"{YELLOW}{(_('memory_above_90')).format(percent=(used_memory / total_memory) * 100)}{RESET}")
print((_('total_memory')).format(total=total_memory)) logger.info((_('total_memory')).format(total=total_memory))
print((_('used_memory')).format(used=used_memory)) logger.info((_('used_memory')).format(used=used_memory))
if free_memory < 1: if free_memory < 1:
print(f"{RED}{(_('low_free_memory')).format(free=free_memory)}{RESET}") logger.warning(f"{(_('low_free_memory')).format(free=free_memory)}")
sys.exit(1) sys.exit(1)
except ImportError: except ImportError:
print(_('psutil_not_installed')) # todo: translate this into italian and put it in the translations "psutil is not installed. Memory check skipped." logger.error(_('psutil_not_installed')) # todo: translate this into italian and put it in the translations "psutil is not installed. Memory check skipped."
def check_cpu(): def check_cpu():
if psutilavaliable == False: if psutilavaliable == False:
return return
print((_('measuring_cpu'))) logger.info((_('measuring_cpu')))
cpu_per_core = psutil.cpu_percent(interval=1, percpu=True) # type: ignore cpu_per_core = psutil.cpu_percent(interval=1, percpu=True) # type: ignore
for idx, core_usage in enumerate(cpu_per_core): for idx, core_usage in enumerate(cpu_per_core):
bar_length = int(core_usage / 5) bar_length = int(core_usage / 5)
@ -203,33 +206,33 @@ def check_cpu():
color = YELLOW color = YELLOW
else: else:
color = GREEN color = GREEN
print((_('core_usage')).format(idx=idx, bar=bar, usage=core_usage)) logger.info((_('core_usage')).format(idx=idx, bar=bar, usage=core_usage))
total_cpu = sum(cpu_per_core) / len(cpu_per_core) total_cpu = sum(cpu_per_core) / len(cpu_per_core)
print((_('total_cpu_usage')).format(usage=total_cpu)) logger.info((_('total_cpu_usage')).format(usage=total_cpu))
if total_cpu > 85: if total_cpu > 85:
print(f"{YELLOW}{(_('high_avg_cpu')).format(usage=total_cpu)}{RESET}") logger.warning(f"{(_('high_avg_cpu')).format(usage=total_cpu)}")
if total_cpu > 95: if total_cpu > 95:
print(f"{RED}{(_('really_high_cpu'))}{RESET}") logger.error(_('really_high_cpu'))
sys.exit(1) sys.exit(1)
def check_memoryjson(): def check_memoryjson():
try: try:
print((_('memory_file')).format(size=os.path.getsize(MEMORY_FILE) / (1024 ** 2))) logger.info((_('memory_file')).format(size=os.path.getsize(MEMORY_FILE) / (1024 ** 2)))
if os.path.getsize(MEMORY_FILE) > 1_073_741_824: if os.path.getsize(MEMORY_FILE) > 1_073_741_824:
print(f"{YELLOW}{(_('memory_file_large'))}{RESET}") logger.warning(f"{(_('memory_file_large'))}")
try: try:
with open(MEMORY_FILE, 'r', encoding='utf-8') as f: with open(MEMORY_FILE, 'r', encoding='utf-8') as f:
json.load(f) json.load(f)
except json.JSONDecodeError as e: except json.JSONDecodeError as e:
print(f"{RED}{(_('memory_file_corrupted')).format(error=e)}{RESET}") logger.error(f"{(_('memory_file_corrupted')).format(error=e)}")
print(f"{YELLOW}{(_('consider_backup_memory'))}{RESET}") logger.warning(f"{(_('consider_backup_memory'))}")
except UnicodeDecodeError as e: except UnicodeDecodeError as e:
print(f"{RED}{(_('memory_file_encoding')).format(error=e)}{RESET}") logger.error(f"{(_('memory_file_encoding')).format(error=e)}")
print(f"{YELLOW}{(_('consider_backup_memory'))}{RESET}") logger.warning(f"{(_('consider_backup_memory'))}")
except Exception as e: except Exception as e:
print(f"{RED}{(_('error_reading_memory')).format(error=e)}{RESET}") logger.error(f"{(_('error_reading_memory')).format(error=e)}")
except FileNotFoundError: except FileNotFoundError:
print(f"{YELLOW}{(_('memory_file_not_found'))}{RESET}") logger(f"{(_('memory_file_not_found'))}")
def presskey2skip(timeout): def presskey2skip(timeout):
if os.name == 'nt': if os.name == 'nt':
@ -265,9 +268,9 @@ def presskey2skip(timeout):
beta = beta beta = beta
def start_checks(): def start_checks():
if CHECKS_DISABLED == "True": if CHECKS_DISABLED == "True":
print(f"{YELLOW}{(_('checks_disabled'))}{RESET}") logger.warning(f"{(_('checks_disabled'))}")
return return
print(_('running_prestart_checks')) logger.info(_('running_prestart_checks'))
check_for_model() check_for_model()
iscloned() iscloned()
check_missing_translations() check_missing_translations()
@ -279,13 +282,13 @@ def start_checks():
if os.path.exists(".env"): if os.path.exists(".env"):
pass pass
else: else:
print(f"{YELLOW}{(_('env_file_not_found'))}{RESET}") logger.warning(f"{(_('env_file_not_found'))}")
sys.exit(1) sys.exit(1)
if beta == True: if beta == True:
print(f"{YELLOW}this build isnt finished yet, some things might not work as expected{RESET}") logger.warning(f"this build isnt finished yet, some things might not work as expected")
else: else:
pass pass
print(_('continuing_in_seconds').format(seconds=5)) logger.info(_('continuing_in_seconds').format(seconds=5))
presskey2skip(timeout=5) presskey2skip(timeout=5)
os.system('cls' if os.name == 'nt' else 'clear') os.system('cls' if os.name == 'nt' else 'clear')
print(splashtext) print(splashtext)

View file

@ -6,17 +6,20 @@ import spacy
from spacy.tokens import Doc from spacy.tokens import Doc
from spacytextblob.spacytextblob import SpacyTextBlob from spacytextblob.spacytextblob import SpacyTextBlob
import logging
logger = logging.getLogger("goober")
def check_resources(): def check_resources():
try: try:
nlp = spacy.load("en_core_web_sm") nlp = spacy.load("en_core_web_sm")
except OSError: except OSError:
print((_('spacy_model_not_found'))) logging.critical((_('spacy_model_not_found')))
spacy.cli.download("en_core_web_sm") spacy.cli.download("en_core_web_sm")
nlp = spacy.load("en_core_web_sm") nlp = spacy.load("en_core_web_sm")
if "spacytextblob" not in nlp.pipe_names: if "spacytextblob" not in nlp.pipe_names:
nlp.add_pipe("spacytextblob") nlp.add_pipe("spacytextblob")
print((_('spacy_initialized'))) logger.info((_('spacy_initialized')))
check_resources() check_resources()
@ -28,8 +31,8 @@ def is_positive(sentence):
doc = nlp(sentence) doc = nlp(sentence)
sentiment_score = doc._.polarity # from spacytextblob sentiment_score = doc._.polarity # from spacytextblob
debug_message = f"{DEBUG}{(_('sentence_positivity'))} {sentiment_score}{RESET}" debug_message = f"{(_('sentence_positivity'))} {sentiment_score}{RESET}"
print(debug_message) logger.debug(debug_message)
return sentiment_score > 0.6 # had to raise the bar because it kept saying "death to jews" was fine and it kept reacting to them return sentiment_score > 0.6 # had to raise the bar because it kept saying "death to jews" was fine and it kept reacting to them

View file

@ -3,7 +3,8 @@ from modules.globalvars import *
import requests import requests
import subprocess import subprocess
import sys import sys
import logging
logger = logging.getLogger("goober")
launched = False launched = False
# Run a shell command and return its output # Run a shell command and return its output
@ -27,11 +28,11 @@ def auto_update(branch='main', remote='origin'):
if is_remote_ahead(branch, remote): if is_remote_ahead(branch, remote):
print(_( "remote_ahead").format(remote=remote, branch=branch)) print(_( "remote_ahead").format(remote=remote, branch=branch))
pull_result = run_cmd(f'git pull {remote} {branch}') pull_result = run_cmd(f'git pull {remote} {branch}')
print(pull_result) logger.info(pull_result)
print(_( "please_restart")) logger.info(_( "please_restart"))
sys.exit(0) sys.exit(0)
else: else:
print(_( "local_ahead").format(remote=remote, branch=branch)) logger.info(_( "local_ahead").format(remote=remote, branch=branch))
# Fetch the latest version info from the update server # Fetch the latest version info from the update server
def get_latest_version_info(): def get_latest_version_info():
@ -40,10 +41,10 @@ def get_latest_version_info():
if response.status_code == 200: if response.status_code == 200:
return response.json() return response.json()
else: else:
print(f"{RED}{_( 'version_error')} {response.status_code}{RESET}") logger.error(f"{RED}{_( 'version_error')} {response.status_code}{RESET}")
return None return None
except requests.RequestException as e: except requests.RequestException as e:
print(f"{RED}{_( 'version_error')} {e}{RESET}") logger.error(f"{RED}{_( 'version_error')} {e}{RESET}")
return None return None
# Check if an update is available and perform update if needed # Check if an update is available and perform update if needed
@ -54,7 +55,7 @@ def check_for_update():
latest_version_info = get_latest_version_info() latest_version_info = get_latest_version_info()
if not latest_version_info: if not latest_version_info:
print(f"{_('fetch_update_fail')}") logger.error(f"{_('fetch_update_fail')}")
return None, None return None, None
latest_version = latest_version_info.get("version") latest_version = latest_version_info.get("version")
@ -62,25 +63,25 @@ def check_for_update():
download_url = latest_version_info.get("download_url") download_url = latest_version_info.get("download_url")
if not latest_version or not download_url: if not latest_version or not download_url:
print(f"{RED}{_(LOCALE, 'invalid_server')}{RESET}") logger.error(f"{RED}{_(LOCALE, 'invalid_server')}{RESET}")
return None, None return None, None
# Check if local_version is valid # Check if local_version is valid
if local_version == "0.0.0" or None: if local_version == "0.0.0" or None:
print(f"{RED}{_('cant_find_local_version')}{RESET}") logger.error(f"{RED}{_('cant_find_local_version')}{RESET}")
return return
# Compare local and latest versions # Compare local and latest versions
if local_version < latest_version: if local_version < latest_version:
print(f"{YELLOW}{_('new_version').format(latest_version=latest_version, local_version=local_version)}{RESET}") logger.info(f"{YELLOW}{_('new_version').format(latest_version=latest_version, local_version=local_version)}{RESET}")
print(f"{YELLOW}{_('changelog').format(VERSION_URL=VERSION_URL)}{RESET}") logger.info(f"{YELLOW}{_('changelog').format(VERSION_URL=VERSION_URL)}{RESET}")
auto_update() auto_update()
elif beta == True: elif beta == True:
print(f"{YELLOW}You are running an \"unstable\" version of Goober, do not expect it to work properly.\nVersion {local_version}{RESET}") logger.warning(f"You are running an \"unstable\" version of Goober, do not expect it to work properly.\nVersion {local_version}{RESET}")
elif local_version > latest_version: elif local_version > latest_version:
print(f"{YELLOW}{_('modification_warning')}{RESET}") logger.warning(f"{_('modification_warning')}")
elif local_version == latest_version: elif local_version == latest_version:
print(f"{GREEN}{_('latest_version')} {local_version}{RESET}") logger.info(f"{_('latest_version')} {local_version}")
print(f"{_('latest_version2').format(VERSION_URL=VERSION_URL)}\n\n") logger.info(f"{_('latest_version2').format(VERSION_URL=VERSION_URL)}\n\n")
launched = True launched = True
return latest_version return latest_version