diff --git a/.env.example b/.env.example deleted file mode 100644 index 7baa6f9..0000000 --- a/.env.example +++ /dev/null @@ -1 +0,0 @@ -DISCORD_BOT_TOKEN="" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 422c1ad..704d466 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,4 @@ received_memory.json translation_report.txt translationcompleteness.py modules/volta -log.txt -settings/admin_logs.json -settings/settings.json -assets/images/cached/* \ No newline at end of file +log.txt \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index fc06f56..ca39f8a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "modules/volta"] path = modules/volta - url = https://github.com/gooberinc/volta + url = https://forgejo.expect.ovh/gooberinc/volta diff --git a/README.md b/README.md index bb820eb..c1e9957 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ -Real repo: https://forgejo.expect.ovh/gooberinc/goober -This is just a fork that was made the upstream +knockoff of genai basically :p + + +Special thanks to [Charlie's Computers](https://github.com/PowerPCFan) for being the only one I know of that's hosting Goober 24/7 + +[Goober Central](https://github.com/whatdidyouexpect/goober-central) + +[Another mirror](https://forgejo.expect.ovh/gooberinc/goober) +no promises that it'll be stable diff --git a/assets/cogs/README.md b/assets/cogs/README.md index 7d2ddee..8680835 100644 --- a/assets/cogs/README.md +++ b/assets/cogs/README.md @@ -8,9 +8,6 @@ by PowerPCFan [Cog Manager](https://github.com/WhatDidYouExpect/goober/blob/main/cogs/cogmanager.py) by expect -[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/cogs/webscraper.py) by expect (requires goober version 0.11.7.2 or higher) diff --git a/assets/cogs/breaking_news.py b/assets/cogs/breaking_news.py deleted file mode 100644 index eee3f82..0000000 --- a/assets/cogs/breaking_news.py +++ /dev/null @@ -1,153 +0,0 @@ -from typing import List -import discord -from discord.ext import commands -import markovify -from PIL import Image, ImageDraw, ImageFont -import os -from modules.markovmemory import load_markov_model -from textwrap import wrap -import logging -from modules.settings import instance as settings_manager -import re -import time -from modules.sync_conenctor import instance as sync_hub - -logger = logging.getLogger("goober") - -settings = settings_manager.settings - - -class BreakingNews(commands.Cog): - def __init__(self, bot: commands.Bot): - self.bot: commands.Bot = bot - self.font_size = 90 - self.image_margin = -25 - self.font: ImageFont.FreeTypeFont = ImageFont.truetype( - os.path.join("assets", "fonts", "SpecialGothic.ttf"), self.font_size - ) - - self.model: markovify.NewlineText | None = load_markov_model() - - @commands.command() - async def auto_create(self, ctx: commands.Context, enabled: str | None): - if enabled not in ["yes", "no"]: - await ctx.send( - f'Please use {settings["bot"]["prefix"]}auto_create ' - ) - return False - - mode: bool = enabled == "yes" - - settings_manager.set_plugin_setting( - "breaking_news", {"create_from_message_content": mode} - ) - - await ctx.send("Changed setting!") - - @commands.Cog.listener() - async def on_message(self, message: discord.Message): - if not settings_manager.get_plugin_settings( - "breaking_news", {"create_from_message_content": False} - ).get("create_from_message_content"): - logger.debug("Ignoring message - create_from_message_content not enabled") - return - - if not message.content.lower().startswith("breaking news:"): - logger.debug("Ignoring message - doesnt start with breaking news:") - return - - if not sync_hub.can_breaking_news(message.id): - logger.debug("Sync hub denied breaking news request") - return - - - texts = re.split("breaking news:", message.content, flags=re.IGNORECASE) - - logger.debug(texts) - try: - text = texts[1].strip() - if not self.model: - await message.reply("No news specified and model not found!") - return False - - text = text or self.model.make_sentence(max_chars=50, tries=50) - path = self.__insert_text(text) - except IndexError: - if self.model is None: - await message.reply("No model loaded and no breaking news specified") - return False - - path = self.__insert_text( - self.model.make_sentence(max_chars=50, tries=50) or "" - ) - await message.reply("You didn't specify any breaking news!") - - with open(path, "rb") as f: - await message.reply(file=discord.File(f)) - - @commands.command() - async def breaking_news(self, ctx: commands.Context, *args): - if not self.model: - await ctx.send("Please supply a message!") - return False - - message = " ".join(args) or self.model.make_sentence(max_chars=50, tries=50) - - if not message: - await ctx.send("Please supply a message!") - return False - - with open(self.__insert_text(message), "rb") as f: - await ctx.send(content="Breaking news!", file=discord.File(f)) - - def __insert_text(self, text): - start = time.time() - base_image_data: Image.ImageFile.ImageFile = Image.open( - os.path.join("assets", "images", "breaking_news.png") - ) - - base_image: ImageDraw.ImageDraw = ImageDraw.Draw(base_image_data) - - MAX_IMAGE_WIDTH = base_image_data.width - self.image_margin - - if len(text) * self.font_size > MAX_IMAGE_WIDTH: - parts = wrap(text, MAX_IMAGE_WIDTH // self.font_size) - logger.debug(parts) - for index, part in enumerate(parts): - text_size = base_image.textlength(part, self.font) - - base_image.text( - ( - self.image_margin / 2 + ((MAX_IMAGE_WIDTH - text_size) / 2), - (base_image_data.height * 0.2) + index * self.font_size, - ), - part, - font=self.font, - ) - else: - text_size = base_image.textlength(text, self.font) - - base_image.text( - ( - self.image_margin / 2 + ((MAX_IMAGE_WIDTH - text_size) / 2), - (base_image_data.height * 0.2), - ), - text, - font=self.font, - ) - - path_folders = os.path.join("assets", "images", "cache") - os.makedirs(path_folders, exist_ok=True) - - path = os.path.join(path_folders, "breaking_news.png") - - with open(path, "wb") as f: - base_image_data.save(f) - - logger.info(f"Generation took {time.time() - start}s") - - return path - - -async def setup(bot: commands.Bot): - await bot.add_cog(BreakingNews(bot)) diff --git a/assets/cogs/cogmanager.py b/assets/cogs/cogmanager.py new file mode 100644 index 0000000..017f021 --- /dev/null +++ b/assets/cogs/cogmanager.py @@ -0,0 +1,67 @@ +import discord +from discord.ext import commands +from modules.globalvars import ownerid + +COG_PREFIX = "assets.cogs." + +class CogManager(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @commands.command() + async def load(self, ctx, cog_name: str = None): + if ctx.author.id != ownerid: + await ctx.send("You do not have permission to use this command.") + return + if cog_name is None: + await ctx.send("Please provide the cog name to load.") + return + try: + await self.bot.load_extension(COG_PREFIX + cog_name) + await ctx.send(f"Loaded cog `{cog_name}` successfully.") + except Exception as e: + await ctx.send(f"Error loading cog `{cog_name}`: {e}") + + @commands.command() + async def unload(self, ctx, cog_name: str = None): + if ctx.author.id != ownerid: + await ctx.send("You do not have permission to use this command.") + return + if cog_name is None: + await ctx.send("Please provide the cog name to unload.") + return + try: + await self.bot.unload_extension(COG_PREFIX + cog_name) + await ctx.send(f"Unloaded cog `{cog_name}` successfully.") + except Exception as e: + await ctx.send(f"Error unloading cog `{cog_name}`: {e}") + + @commands.command() + async def reload(self, ctx, cog_name: str = None): + if ctx.author.id != ownerid: + await ctx.send("You do not have permission to use this command.") + return + if cog_name is None: + await ctx.send("Please provide the cog name to reload.") + return + try: + await self.bot.unload_extension(COG_PREFIX + cog_name) + await self.bot.load_extension(COG_PREFIX + cog_name) + await ctx.send(f"Reloaded cog `{cog_name}` successfully.") + except Exception as e: + await ctx.send(f"Error reloading cog `{cog_name}`: {e}") + + @commands.command() + async def listcogs(self, ctx): + """Lists all currently loaded cogs in an embed.""" + cogs = list(self.bot.cogs.keys()) + if not cogs: + await ctx.send("No cogs are currently loaded.") + return + + embed = discord.Embed(title="Loaded Cogs", description="Here is a list of all currently loaded cogs:") + embed.add_field(name="Cogs", value="\n".join(cogs), inline=False) + await ctx.send(embed=embed) + +async def setup(bot): + await bot.add_cog(CogManager(bot)) diff --git a/assets/cogs/eightball.py b/assets/cogs/eightball.py index 39eb42c..ec6c955 100644 --- a/assets/cogs/eightball.py +++ b/assets/cogs/eightball.py @@ -2,40 +2,56 @@ import random import discord from discord.ext import commands - class eightball(commands.Cog): def __init__(self, bot): self.bot = bot @commands.command() async def eightball(self, ctx): - answer = random.choice( - [ - "It is certain.", - "It is decidedly so.", - "Without a doubt.", - "Yes definitely.", - "You may rely on it.", - "As I see it, yes.", - "Most likely.", - "Outlook good.", - "Yes.", - "Signs point to yes.", - "Reply hazy, try again.", - "Ask again later.", - "Better not tell you now.", - "Cannot predict now.", - "Concentrate and ask again.", - "Don't count on it.", - "My reply is no.", - "My sources say no.", - "Outlook not so good.", - "Very doubtful.", - ] - ) - - await ctx.send(answer) + answer = random.randint(1, 20) + text = "Nothing" + if answer==1: + text = "It is certain." + elif answer==2: + text = "It is decidedly so." + elif answer==3: + text = "Without a doubt." + elif answer==4: + text = "Yes definitely." + elif answer==5: + text = "You may rely on it." + elif answer==6: + text = "As I see it, yes." + elif answer==7: + text = "Most likely." + elif answer==8: + text = "Outlook good." + elif answer==9: + text = "Yes." + elif answer==10: + text = "Signs point to yes." + elif answer==11: + text = "Reply hazy, try again." + elif answer==12: + text = "Ask again later." + elif answer==13: + text = "Better not tell you now." + elif answer==14: + text = "Cannot predict now." + elif answer==15: + text = "Concentrate and ask again." + elif answer==16: + text = "Don't count on it." + elif answer==17: + text = "My reply is no." + elif answer==18: + text = "My sources say no." + elif answer==19: + text = "Outlook not so good." + elif answer==20: + text = "Very doubtful." + await ctx.send(text) async def setup(bot): await bot.add_cog(eightball(bot)) diff --git a/assets/cogs/example.py b/assets/cogs/example.py deleted file mode 100644 index 5db4016..0000000 --- a/assets/cogs/example.py +++ /dev/null @@ -1,131 +0,0 @@ -import discord -from discord.ext import commands -from discord import app_commands - -import discord.ext -import discord.ext.commands - -import random - -from modules.permission import requires_admin -from modules.sentenceprocessing import send_message -from modules.settings import instance as settings_manager -from typing import TypedDict - -# Name according to your cog (e.g a random number generator -> RandomNumber) -class Example(commands.Cog): - # __init__ method is required with these exact parameters - def __init__(self, bot: discord.ext.commands.Bot): # type hinting (aka : discord.ext.commands.Bot) isn't necessary, but provides better intellisense in code editors - self.bot: discord.ext.commands.Bot = bot - - - # a basic ping slash command which utilizes embeds - @app_commands.command(name="ping", description="A command that sends a ping!") - async def ping(self, interaction: discord.Interaction): - await interaction.response.defer() - - example_embed = discord.Embed( - title="Pong!!", - description="The Beretta fires fast and won't make you feel any better!", - color=discord.Color.blue(), - ) - example_embed.set_footer( - text=f"Requested by {interaction.user.name}", - icon_url=interaction.user.display_avatar, - ) - - await interaction.followup.send(embed=example_embed) - - # a basic command (aka prefix.random_number) - # Shows how to get parameters, and how to send messages using goobers message thing - @commands.command() - async def random_number(self, ctx: commands.Context, minimum: int | None, maximum: int | None): # every argument after ctx is a part of the command, aka "g.random_number 0 5" would set minimum as 0 and maximum as 5 - # We should always assume that command parameters are None, since someone can gall g.randon_number. - - if minimum is None: - await send_message(ctx, message="Please specify the minimum number!") - return # make sure we dont continue - - if maximum is None: - await send_message(ctx, message="Please specify the maximum number!") - return # make sure we dont continue - - - number = random.randint(minimum, maximum) - - example_embed = discord.Embed( - title="Random number generator", - description=f"Random number: {number}", - color=discord.Color.blue(), - ) - example_embed.set_footer( - text=f"Requested by {ctx.author.name}", - icon_url=ctx.author.display_avatar, - ) - - await send_message(ctx, embed=example_embed) - - - # A command which requires the executor to be an admin, and takes a discord user as an argument - @requires_admin() # from modules.permission import requires_admin - @commands.command() - async def ban_user(self, ctx: commands.Context, target: discord.Member | None, reason: str | None): - if target is None: - await send_message(ctx, "Please specify a user by pinging them!") - return - - await target.ban(reason=reason) - await send_message(ctx, message=f"Banned user {target.name}!") - - - # Changing and getting plugin settings, defining a settings schmea - @commands.command() - async def change_hello_message(self, ctx: commands.Context, new_message: str | None): - COG_NAME = "example" # change this to whatever you want, but keep it the same accross your cog - - if new_message is None: - await send_message(ctx, "Please specify a new message!") - return - - # Generating a settings schema (optional) - # from typing import TypedDict - class IntroSettings(TypedDict): - message: str - - class SettingsType(TypedDict): - intro: IntroSettings - leave_message: str - - # End of optional typing - # Note: if you decide to do this, please place these at the top of the file! (but after imports) - - default_settings: SettingsType = { # use default_settings = { if you didnt define the types - "intro": { - "message": "Hello user!" - }, - "leave_message": "Goodbye user!" - } - - - # from modules.settings import instance as settings_manager - # get current plugin settings - # change "example" to your cog name - settings: SettingsType = settings_manager.get_plugin_settings(COG_NAME, default=default_settings) #type: ignore[assignment] - - # Now you can use settings easily! - - current_message = settings["intro"]["message"] - await send_message(ctx, message=f"Current message: {current_message}") - - # Changing plugin settings - settings["intro"]["message"] = "brand new message!" - - settings_manager.set_plugin_setting(COG_NAME, settings) - - new_message = settings["intro"]["message"] - await send_message(ctx, message=f"New message: {new_message}") - - - -async def setup(bot): - await bot.add_cog(Example(bot)) diff --git a/assets/cogs/filesharing.py b/assets/cogs/filesharing.py index 441375c..ad6ac98 100644 --- a/assets/cogs/filesharing.py +++ b/assets/cogs/filesharing.py @@ -1,70 +1,48 @@ import discord -import discord.context_managers from discord.ext import commands -import logging -from typing import Literal, get_args, cast -from modules.permission import requires_admin -from modules.settings import instance as settings_manager - -settings = settings_manager.settings - - -logger = logging.getLogger("goober") - -AvailableModes = Literal["r", "s"] - - +from modules.globalvars import ownerid class FileSync(commands.Cog): def __init__(self, bot): - self.bot: discord.Client = bot - self.mode: AvailableModes | None = None + self.bot = bot + self.mode = None self.peer_id = None self.awaiting_file = False - @requires_admin() @commands.command() - async def syncfile(self, ctx: commands.Context, mode: str, peer: discord.User): - if self.mode not in get_args(AvailableModes): - await ctx.send("Invalid mode, use 's' or 'r'.") - return - - self.mode = cast(AvailableModes, mode.lower()) + async def syncfile(self, ctx, mode: str, peer: discord.User): + self.mode = mode.lower() self.peer_id = peer.id - + if ctx.author.id != ownerid: + await ctx.send("You don't have permission to execute this command.") + return if self.mode == "s": await ctx.send(f"<@{self.peer_id}> FILE_TRANSFER_REQUEST") - await ctx.send(file=discord.File(settings["bot"]["active_memory"])) + await ctx.send(file=discord.File("memory.json")) await ctx.send("File sent in this channel.") - elif self.mode == "r": await ctx.send("Waiting for incoming file...") self.awaiting_file = True + else: + await ctx.send("Invalid mode, use 's' or 'r'.") + @commands.Cog.listener() - async def on_message(self, message: discord.Message): + async def on_message(self, message): if message.author == self.bot.user or not self.awaiting_file: return - if message.author.id != self.peer_id: return if message.content == "FILE_TRANSFER_REQUEST": - logger.info("Ping received. Awaiting file...") - if not message.attachments: - return - - for attachment in message.attachments: - if not attachment.filename.endswith(".json"): - continue - - filename = "received_memory.json" - with open(filename, "wb") as f: - await attachment.save(f) - - logger.info(f"File saved as {filename}") - await message.channel.send("File received and saved.") - self.awaiting_file = False - + print("Ping received. Awaiting file...") + if message.attachments: + for attachment in message.attachments: + if attachment.filename.endswith(".json"): + filename = "received_memory.json" + await attachment.save(filename) + print(f"File saved as {filename}") + await message.channel.send("File received and saved.") + self.awaiting_file = False async def setup(bot): await bot.add_cog(FileSync(bot)) diff --git a/assets/cogs/fuckup.py b/assets/cogs/fuckup.py index 89a3867..1dbc4d0 100644 --- a/assets/cogs/fuckup.py +++ b/assets/cogs/fuckup.py @@ -1,10 +1,9 @@ import discord from discord.ext import commands from modules.image import * +from modules.volta.main import _ from PIL import Image, ImageEnhance, ImageFilter, ImageOps, ImageChops, ImageColor import os, random, shutil, tempfile -import modules.keys as k - async def deepfryimage(path): with Image.open(path).convert("RGB") as im: @@ -45,17 +44,14 @@ class whami(commands.Cog): def __init__(self, bot): self.bot = bot + @commands.command() async def fuckup(self, ctx): assets_folder = "assets/images" temp_input = None def get_random_asset_image(): - files = [ - f - for f in os.listdir(assets_folder) - if f.lower().endswith((".png", ".jpg", ".jpeg", ".webp")) - ] + files = [f for f in os.listdir(assets_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))] if not files: return None return os.path.join(assets_folder, random.choice(files)) @@ -70,7 +66,7 @@ class whami(commands.Cog): else: fallback_image = get_random_asset_image() if fallback_image is None: - await ctx.reply(k.no_image_available()) + await ctx.reply(_('no_image_available')) return temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1]) shutil.copy(fallback_image, temp_input) @@ -78,7 +74,7 @@ class whami(commands.Cog): else: fallback_image = get_random_asset_image() if fallback_image is None: - await ctx.reply(k.no_image_available()) + await ctx.reply(_('no_image_available')) return temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1]) shutil.copy(fallback_image, temp_input) @@ -89,7 +85,7 @@ class whami(commands.Cog): if output_path is None or not os.path.isfile(output_path): if temp_input and os.path.exists(temp_input): os.remove(temp_input) - await ctx.reply(k.failed_generate_image()) + await ctx.reply(_('failed_generate_image')) return deepfried_path = await deepfryimage(output_path) @@ -98,6 +94,5 @@ class whami(commands.Cog): if temp_input and os.path.exists(temp_input): os.remove(temp_input) - async def setup(bot): await bot.add_cog(whami(bot)) diff --git a/assets/cogs/internal/base_commands.py b/assets/cogs/internal/base_commands.py deleted file mode 100644 index d1ac354..0000000 --- a/assets/cogs/internal/base_commands.py +++ /dev/null @@ -1,231 +0,0 @@ -import os -from typing import Dict, List -import discord -from discord.ext import commands -import discord.ext -import discord.ext.commands -import modules.keys as k -from modules.permission import requires_admin -from modules.sentenceprocessing import send_message -from modules.settings import instance as settings_manager -import requests -import psutil -import cpuinfo -import sys -import subprocess -import updater -from modules.sync_conenctor import instance as sync_connector - -settings = settings_manager.settings - - -class BaseCommands(commands.Cog): - def __init__(self, bot): - self.bot: discord.ext.commands.Bot = bot - - @commands.command() - async def help(self, ctx: commands.Context) -> None: - embed: discord.Embed = discord.Embed( - title=f"{k.command_help_embed_title()}", - description=f"{k.command_help_embed_desc()}", - color=discord.Colour(0x000000), - ) - - command_categories = { - f"{k.command_help_categories_general()}": [ - "mem", - "talk", - "about", - "ping", - "impact", - "demotivator", - "help", - ], - f"{k.command_help_categories_admin()}": ["stats", "retrain", "setlanguage"], - } - - custom_commands: List[str] = [] - for cog_name, cog in self.bot.cogs.items(): - for command in cog.get_commands(): - if ( - command.name - not in command_categories[f"{k.command_help_categories_general()}"] - and command.name - not in command_categories[f"{k.command_help_categories_admin()}"] - ): - custom_commands.append(command.name) - - if custom_commands: - embed.add_field( - name=f"{k.command_help_categories_custom()}", - value="\n".join( - [ - f"{settings['bot']['prefix']}{command}" - for command in custom_commands - ] - ), - inline=False, - ) - - for category, commands_list in command_categories.items(): - commands_in_category: str = "\n".join( - [f"{settings['bot']['prefix']}{command}" for command in commands_list] - ) - embed.add_field(name=category, value=commands_in_category, inline=False) - - await send_message(ctx, embed=embed) - - @requires_admin() - @commands.command() - async def setlanguage(self, ctx: commands.Context, locale: str) -> None: - await ctx.defer() - k.change_language(locale) - - settings["locale"] = locale # type: ignore - settings_manager.commit() - - await ctx.send(":thumbsup:") - - @commands.command() - async def ping(self, ctx: commands.Context) -> None: - await ctx.defer() - latency: int = round(self.bot.latency * 1000) - - embed: discord.Embed = discord.Embed( - title="Pong!!", - description=( - settings["bot"]["misc"]["ping_line"], - f"`{k.command_ping_embed_desc()}: {latency}ms`\n", - ), - color=discord.Colour(0x000000), - ) - embed.set_footer( - text=f"{k.command_ping_footer()} {ctx.author.name}", - icon_url=ctx.author.display_avatar.url, - ) - - await ctx.send(embed=embed) - - @commands.command() - async def about(self, ctx: commands.Context) -> None: - embed: discord.Embed = discord.Embed( - title=k.command_about_embed_title(), - description="", - color=discord.Colour(0x000000), - ) - - embed.add_field( - name=k.command_about_embed_field1(), - value=settings['name'], - inline=False, - ) - - embed.add_field(name="Github", value=f"https://github.com/gooberinc/goober") - await send_message(ctx, embed=embed) - - @commands.command() - async def stats(self, ctx: commands.Context) -> None: - memory_file: str = settings["bot"]["active_memory"] - file_size: int = os.path.getsize(memory_file) - - memory_info = psutil.virtual_memory() # type: ignore - total_memory = memory_info.total / (1024**3) - used_memory = memory_info.used / (1024**3) - - - cpu_name = cpuinfo.get_cpu_info()["brand_raw"] - - - with open(memory_file, "r") as file: - line_count: int = sum(1 for _ in file) - - embed: discord.Embed = discord.Embed( - title=f"{k.command_stats_embed_title()}", - description=f"{k.command_stats_embed_desc()}", - color=discord.Colour(0x000000), - ) - embed.add_field( - name=f"{k.command_stats_embed_field1name()}", - value=f"{k.command_stats_embed_field1value(file_size=file_size, line_count=line_count)}", - inline=False, - ) - - embed.add_field( - name=k.system_info(), - value=f""" - {k.memory_usage(used=round(used_memory,2), total=round(total_memory,2), percent=round(used_memory/total_memory * 100))} - {k.cpu_info(cpu_name)} - """ - ) - - with open(settings["splash_text_loc"], "r") as f: - splash_text = "".join(f.readlines()) - - embed.add_field( - name=f"{k.command_stats_embed_field3name()}", - value=f"""{k.command_stats_embed_field3value( - NAME=settings["name"], PREFIX=settings["bot"]["prefix"], ownerid=settings["bot"]["owner_ids"][0], - PING_LINE=settings["bot"]["misc"]["ping_line"], showmemenabled=settings["bot"]["allow_show_mem_command"], - USERTRAIN_ENABLED=settings["bot"]["user_training"], song=settings["bot"]["misc"]["activity"]["content"], - splashtext=splash_text - )}""", - inline=False, - ) - - await send_message(ctx, embed=embed) - - @requires_admin() - @commands.command() - async def restart(self, ctx: commands.Context): - await ctx.send("Restarting...") - os.execv(sys.executable, [sys.executable] + sys.argv) - - @requires_admin() - @commands.command() - async def force_update(self, ctx: commands.Context): - await ctx.send("Forcefully updating...") - updater.force_update() - os.execv(sys.executable, [sys.executable] + sys.argv) - - - @requires_admin() - @commands.command() - async def mem(self, ctx: commands.Context) -> None: - if not settings["bot"]["allow_show_mem_command"]: - return - - with open(settings["bot"]["active_memory"], "rb") as f: - data: bytes = f.read() - - response = requests.post( - "https://litterbox.catbox.moe/resources/internals/api.php", - data={"reqtype": "fileupload", "time": "1h"}, - files={"fileToUpload": data}, - ) - - await send_message(ctx, response.text) - - @requires_admin() - @commands.command() - async def test_synchub(self, ctx: commands.Context, message_id: str | None) -> None: - message_id = message_id or "0" - status = sync_connector.can_react(int(message_id)) - - await send_message(ctx, f"Is allowed to react to message id {message_id}? {status} (connection active? {sync_connector.connected})") - - - @requires_admin() - @commands.command() - async def connect_synchub(self, ctx: commands.Context) -> None: - await send_message(ctx, "Trying to connect...") - - connected = sync_connector.try_to_connect() - if connected: - await send_message(ctx, "Succesfully connected to sync hub!") - else: - await send_message(ctx, "Failed to connect to sync hub") - -async def setup(bot: discord.ext.commands.Bot): - print("Setting up base_commands") - bot.remove_command("help") - await bot.add_cog(BaseCommands(bot)) diff --git a/assets/cogs/internal/cogmanager.py b/assets/cogs/internal/cogmanager.py deleted file mode 100644 index 3387b7a..0000000 --- a/assets/cogs/internal/cogmanager.py +++ /dev/null @@ -1,129 +0,0 @@ -import discord -from discord.ext import commands -import discord.ext -import discord.ext.commands -from modules.permission import requires_admin -from modules.settings import instance as settings_manager -from modules.globalvars import available_cogs - -settings = settings_manager.settings - - -COG_PREFIX = "assets.cogs." - - -class CogManager(commands.Cog): - def __init__(self, bot): - self.bot = bot - - @requires_admin() - @commands.command() - async def enable(self, ctx, cog_name: str): - try: - await self.bot.load_extension(COG_PREFIX + cog_name) - await ctx.send(f"Loaded cog `{cog_name}` successfully.") - settings["bot"]["enabled_cogs"].append(cog_name) - settings_manager.add_admin_log_event( - { - "action": "add", - "author": ctx.author.id, - "change": "enabled_cogs", - "messageId": ctx.message.id, - "target": cog_name, - } - ) - settings_manager.commit() - - except Exception as e: - await ctx.send(f"Error enabling cog `{cog_name}`: {e}") - - @requires_admin() - @commands.command() - async def load(self, ctx, cog_name: str | None = None): - if cog_name is None: - await ctx.send("Give cog_name") - return - - if cog_name[:-3] not in settings["bot"]["enabled_cogs"]: - await ctx.send("Please enable the cog first!") - return - if cog_name is None: - await ctx.send("Please provide the cog name to load.") - return - try: - await self.bot.load_extension(COG_PREFIX + cog_name) - await ctx.send(f"Loaded cog `{cog_name}` successfully.") - except Exception as e: - await ctx.send(f"Error loading cog `{cog_name}`: {e}") - - @requires_admin() - @commands.command() - async def unload(self, ctx, cog_name: str | None = None): - if cog_name is None: - await ctx.send("Please provide the cog name to unload.") - return - try: - await self.bot.unload_extension(COG_PREFIX + cog_name) - await ctx.send(f"Unloaded cog `{cog_name}` successfully.") - except Exception as e: - await ctx.send(f"Error unloading cog `{cog_name}`: {e}") - - @requires_admin() - @commands.command() - async def disable(self, ctx, cog_name: str | None = None): - if cog_name is None: - await ctx.send("Please provide the cog name to disable.") - return - try: - await self.bot.unload_extension(COG_PREFIX + cog_name) - await ctx.send(f"Unloaded cog `{cog_name}` successfully.") - settings["bot"]["enabled_cogs"].remove(cog_name) - settings_manager.add_admin_log_event( - { - "action": "del", - "author": ctx.author.id, - "change": "enabled_cogs", - "messageId": ctx.message.id, - "target": cog_name, - } - ) - settings_manager.commit() - except Exception as e: - await ctx.send(f"Error unloading cog `{cog_name}`: {e}") - - @requires_admin() - @commands.command() - async def reload(self, ctx, cog_name: str | None = None): - if cog_name is None: - await ctx.send("Please provide the cog name to reload.") - return - - if cog_name[:-3] not in settings["bot"]["enabled_cogs"]: - await ctx.send("Please enable the cog first!") - return - try: - await self.bot.unload_extension(COG_PREFIX + cog_name) - await self.bot.load_extension(COG_PREFIX + cog_name) - await ctx.send(f"Reloaded cog `{cog_name}` successfully.") - except Exception as e: - await ctx.send(f"Error reloading cog `{cog_name}`: {e}") - - @commands.command() - async def listcogs(self, ctx): - """Lists all currently loaded cogs in an embed.""" - cogs = list(self.bot.cogs.keys()) - if not cogs: - await ctx.send("No cogs are currently loaded.") - return - - embed = discord.Embed( - title="Loaded Cogs", - description="Here is a list of all currently loaded cogs:", - ) - embed.add_field(name="Loaded cogs", value="\n".join(cogs), inline=False) - embed.add_field(name="Available cogs", value="\n".join(available_cogs())) - await ctx.send(embed=embed) - - -async def setup(bot): - await bot.add_cog(CogManager(bot)) diff --git a/assets/cogs/internal/markov.py b/assets/cogs/internal/markov.py deleted file mode 100644 index ab8e9da..0000000 --- a/assets/cogs/internal/markov.py +++ /dev/null @@ -1,123 +0,0 @@ -import os -import random -import re -import discord -from discord.ext import commands - -import discord.ext -import discord.ext.commands - -from modules.markovmemory import ( - load_markov_model, - save_markov_model, - train_markov_model, -) -from modules.permission import requires_admin -from modules.sentenceprocessing import ( - improve_sentence_coherence, - is_positive, - rephrase_for_coherence, - send_message, -) -import modules.keys as k -import logging -from typing import List, Optional, Set -import json -import time -import markovify - - -logger = logging.getLogger("goober") -from modules.settings import instance as settings_manager - -settings = settings_manager.settings - - -class Markov(commands.Cog): - def __init__(self, bot): - self.bot: discord.ext.commands.Bot = bot - - self.model: markovify.NewlineText | None = load_markov_model() - - @requires_admin() - @commands.command() - async def retrain(self, ctx: discord.ext.commands.Context): - message_ref: discord.Message | None = await send_message( - ctx, f"{k.command_markov_retrain()}" - ) - - if message_ref is None: - logger.error("Failed to send message!") - return - - try: - with open(settings["bot"]["active_memory"], "r") as f: - memory: List[str] = json.load(f) - except FileNotFoundError: - await send_message(ctx, f"{k.command_markov_memory_not_found()}") - return - except json.JSONDecodeError: - await send_message(ctx, f"{k.command_markov_memory_is_corrupt()}") - return - - data_size: int = len(memory) - - processing_message_ref: discord.Message | None = await send_message( - ctx, f"{k.command_markov_retraining(data_size)}" - ) - if processing_message_ref is None: - logger.error("Couldnt find message processing message!") - - start_time: float = time.time() - - model = train_markov_model(memory) - if not model: - logger.error("Failed to train markov model") - await ctx.send("Failed to retrain!") - return False - - self.model = model - save_markov_model(self.model) - - logger.debug(f"Completed retraining in {round(time.time() - start_time,3)}s") - - await send_message( - ctx, - f"{k.command_markov_retrain_successful(data_size)}", - edit=True, - message_reference=processing_message_ref, - ) - - @commands.command() - async def talk(self, ctx: commands.Context, sentence_size: int = 5) -> None: - if not self.model: - await send_message(ctx, f"{k.command_talk_insufficent_text()}") - return - - response: str = "" - if sentence_size == 1: - response = ( - self.model.make_short_sentence(max_chars=200, tries=700) - or k.command_talk_generation_fail() - ) - - else: - response = improve_sentence_coherence( - self.model.make_sentence(tries=100, max_words=sentence_size) - or k.command_talk_generation_fail() - ) - - cleaned_response: str = re.sub(r"[^\w\s]", "", response).lower() - coherent_response: str = rephrase_for_coherence(cleaned_response) - - if random.random() < 0.9 and is_positive(coherent_response): - gif_url: str = random.choice(settings["bot"]["misc"]["positive_gifs"]) - - coherent_response = f"{coherent_response}\n[jif]({gif_url})" - - os.environ["gooberlatestgen"] = coherent_response - await send_message(ctx, coherent_response) - - -async def setup(bot): - await bot.add_cog(Markov(bot)) diff --git a/assets/cogs/internal/permissions.py b/assets/cogs/internal/permissions.py deleted file mode 100644 index e12c7f6..0000000 --- a/assets/cogs/internal/permissions.py +++ /dev/null @@ -1,118 +0,0 @@ -import discord -from discord.ext import commands - -from modules.permission import requires_admin -from modules.settings import instance as settings_manager - -settings = settings_manager.settings - - -class PermissionManager(commands.Cog): - def __init__(self, bot): - self.bot = bot - - @requires_admin() - @commands.command() - async def add_owner(self, ctx: commands.Context, member: discord.Member): - settings["bot"]["owner_ids"].append(member.id) - settings_manager.add_admin_log_event( - { - "action": "add", - "author": ctx.author.id, - "change": "owner_ids", - "messageId": ctx.message.id, - "target": member.id, - } - ) - - settings_manager.commit() - - embed = discord.Embed( - title="Permissions", - description=f"Set {member.name} as an owner", - color=discord.Color.blue(), - ) - - await ctx.send(embed=embed) - - @requires_admin() - @commands.command() - async def remove_owner(self, ctx: commands.Context, member: discord.Member): - try: - settings["bot"]["owner_ids"].remove(member.id) - settings_manager.add_admin_log_event( - { - "action": "del", - "author": ctx.author.id, - "change": "owner_ids", - "messageId": ctx.message.id, - "target": member.id, - } - ) - settings_manager.commit() - except ValueError: - await ctx.send("User is not an owner!") - return - - embed = discord.Embed( - title="Permissions", - description=f"Removed {member.name} from being an owner", - color=discord.Color.blue(), - ) - - await ctx.send(embed=embed) - - @requires_admin() - @commands.command() - async def blacklist_user(self, ctx: commands.Context, member: discord.Member): - settings["bot"]["blacklisted_users"].append(member.id) - settings_manager.add_admin_log_event( - { - "action": "add", - "author": ctx.author.id, - "change": "blacklisted_users", - "messageId": ctx.message.id, - "target": member.id, - } - ) - settings_manager.commit() - - embed = discord.Embed( - title="Blacklist", - description=f"Added {member.name} to the blacklist", - color=discord.Color.blue(), - ) - - await ctx.send(embed=embed) - - @requires_admin() - @commands.command() - async def unblacklist_user(self, ctx: commands.Context, member: discord.Member): - try: - settings["bot"]["blacklisted_users"].remove(member.id) - settings_manager.add_admin_log_event( - { - "action": "del", - "author": ctx.author.id, - "change": "blacklisted_users", - "messageId": ctx.message.id, - "target": member.id, - } - ) - settings_manager.commit() - - except ValueError: - await ctx.send("User is not on the blacklist!") - return - - embed = discord.Embed( - title="Blacklist", - description=f"Removed {member.name} from blacklist", - color=discord.Color.blue(), - ) - - await ctx.send(embed=embed) - - -async def setup(bot): - await bot.add_cog(PermissionManager(bot)) diff --git a/assets/cogs/lastfm.py b/assets/cogs/lastfm.py.disabled similarity index 78% rename from assets/cogs/lastfm.py rename to assets/cogs/lastfm.py.disabled index 2ae3290..822305a 100644 --- a/assets/cogs/lastfm.py +++ b/assets/cogs/lastfm.py.disabled @@ -6,12 +6,11 @@ from dotenv import load_dotenv load_dotenv() -# stole most of this code from my old expect bot so dont be suprised if its poorly made +#stole most of this code from my old expect bot so dont be suprised if its poorly made LASTFM_API_KEY = os.getenv("LASTFM_API_KEY") LASTFM_USERNAME = os.getenv("LASTFM_USERNAME") - class LastFmCog(commands.Cog): def __init__(self, bot): self.bot = bot @@ -35,11 +34,7 @@ class LastFmCog(commands.Cog): self.current_track = track artist, song = track activity_name = f"{artist} - {song}" - await self.bot.change_presence( - activity=discord.Activity( - type=discord.ActivityType.listening, name=activity_name - ) - ) + await self.bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=activity_name)) print(f"Updated song to {artist} - {song}") else: print("LastFM gave me the same track! not updating...") @@ -57,11 +52,7 @@ class LastFmCog(commands.Cog): self.current_track = track artist, song = track activity_name = f"{artist} - {song}" - await self.bot.change_presence( - activity=discord.Activity( - type=discord.ActivityType.listening, name=activity_name - ) - ) + await self.bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=activity_name)) await ctx.send(f"Updated presence to: Listening to {activity_name}") async def fetch_current_track(self): @@ -80,13 +71,12 @@ class LastFmCog(commands.Cog): return None track = recenttracks[0] - if "@attr" in track and track["@attr"].get("nowplaying") == "true": - artist = track.get("artist", {}).get("#text", "Unknown Artist") - song = track.get("name", "Unknown Song") + if '@attr' in track and track['@attr'].get('nowplaying') == 'true': + artist = track.get('artist', {}).get('#text', 'Unknown Artist') + song = track.get('name', 'Unknown Song') return artist, song return None - async def setup(bot): if not LASTFM_API_KEY or not LASTFM_USERNAME: return diff --git a/assets/cogs/lyrics.py b/assets/cogs/lyrics.py new file mode 100644 index 0000000..db999b4 --- /dev/null +++ b/assets/cogs/lyrics.py @@ -0,0 +1,123 @@ +import discord +from discord.ext import commands +from discord import app_commands +import aiohttp +import re + +class Lyrics(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="lyrics", description="Get lyrics for a song") + @app_commands.describe( + artist="Name of the artist", + song="Title of the song", + language="Target language code (optional)" + ) + @app_commands.choices(language=[ + app_commands.Choice(name="Bulgarian", value="bg"), + app_commands.Choice(name="Czech", value="cs"), + app_commands.Choice(name="Danish", value="da"), + app_commands.Choice(name="German", value="de"), + app_commands.Choice(name="Greek", value="el"), + app_commands.Choice(name="English", value="en"), + app_commands.Choice(name="Spanish", value="es"), + app_commands.Choice(name="Estonian", value="et"), + app_commands.Choice(name="Finnish", value="fi"), + app_commands.Choice(name="French", value="fr"), + app_commands.Choice(name="Irish", value="ga"), + app_commands.Choice(name="Croatian", value="hr"), + app_commands.Choice(name="Hungarian", value="hu"), + app_commands.Choice(name="Italian", value="it"), + app_commands.Choice(name="Lithuanian", value="lt"), + app_commands.Choice(name="Latvian", value="lv"), + app_commands.Choice(name="Maltese", value="mt"), + app_commands.Choice(name="Dutch", value="nl"), + app_commands.Choice(name="Polish", value="pl"), + app_commands.Choice(name="Portuguese", value="pt"), + app_commands.Choice(name="Romanian", value="ro"), + app_commands.Choice(name="Slovak", value="sk"), + app_commands.Choice(name="Slovene", value="sl"), + app_commands.Choice(name="Swedish", value="sv"), + ]) + async def lyrics(self, interaction: discord.Interaction, artist: str = None, song: str = None, language: app_commands.Choice[str] = None): + await interaction.response.defer() + if not artist or not song: + member = interaction.guild.get_member(interaction.user.id) + if not member: + member = await interaction.guild.fetch_member(interaction.user.id) + act_artist, act_song = await self.get_artist_song_from_presence(member) + if act_artist and act_song: + artist = artist or act_artist + song = song or act_song + else: + await interaction.followup.send("No artist or song provided and couldn't find it from your current activity.") + return + + lyrics = await self.fetch_lyrics(artist, song) + if not lyrics: + await interaction.followup.send(f"Could not find lyrics for **{artist} - {song}**") + return + + if language: + translated = await self.translate_text(lyrics, language.value) + if translated: + lyrics = translated + + if len(lyrics) > 1900: + lyrics = lyrics[:1900] + "\n\n[...lyrics truncated...]" + + embed = discord.Embed( + title=f"{artist} - {song}", + description=lyrics, + color=discord.Color.blue() + ) + embed.set_footer(text=f"Requested by {interaction.user}", icon_url=interaction.user.display_avatar.url) + await interaction.followup.send(embed=embed) + + async def get_artist_song_from_presence(self, member: discord.Member): + for activity in member.activities: + if isinstance(activity, discord.Spotify): + return activity.artist, activity.title + return None, None + + + async def fetch_lyrics(self, artist, song): + artist_q = artist.replace(' ', '+').lower() + song_q = song.replace(' ', '+').lower() + + url = f"https://lrclib.net/api/get?artist_name={artist_q}&track_name={song_q}" + print(url) + + async with aiohttp.ClientSession() as session: + try: + async with session.get(url) as resp: + if resp.status != 200: + return None + data = await resp.json() + return data.get('plainLyrics') + except Exception: + return None + + async def translate_text(self, text: str, target_lang: str) -> str | None: + translate_url = "https://translate.googleapis.com/translate_a/single" + params = { + "client": "gtx", + "sl": "auto", + "tl": target_lang, + "dt": "t", + "q": text + } + async with aiohttp.ClientSession() as session: + try: + async with session.get(translate_url, params=params) as resp: + if resp.status != 200: + return None + result = await resp.json() + translated_chunks = [item[0] for item in result[0] if item[0]] + return ''.join(translated_chunks) + except Exception: + return None + +async def setup(bot): + await bot.add_cog(Lyrics(bot)) diff --git a/assets/cogs/pulse.py b/assets/cogs/pulse.py index ba031b5..f717221 100644 --- a/assets/cogs/pulse.py +++ b/assets/cogs/pulse.py @@ -2,12 +2,7 @@ from discord.ext import commands import discord from collections import defaultdict, Counter import datetime -from modules.permission import requires_admin -from modules.settings import instance as settings_manager - -settings = settings_manager.settings - - +from modules.globalvars import ownerid class StatsCog(commands.Cog): def __init__(self, bot): self.bot = bot @@ -34,54 +29,38 @@ class StatsCog(commands.Cog): async def on_command(self, ctx): self.command_usage[ctx.command.qualified_name] += 1 - @requires_admin() @commands.command() async def spyware(self, ctx): + if ctx.author.id != ownerid: + return uptime = datetime.datetime.utcnow() - self.start_time hours_elapsed = max((uptime.total_seconds() / 3600), 1) avg_per_hour = self.total_messages / hours_elapsed if self.messages_per_hour: - peak_hour, peak_count = max( - self.messages_per_hour.items(), key=lambda x: x[1] - ) + peak_hour, peak_count = max(self.messages_per_hour.items(), key=lambda x: x[1]) else: peak_hour, peak_count = "N/A", 0 top_users = self.user_message_counts.most_common(5) embed = discord.Embed(title="Community Stats", color=discord.Color.blue()) - embed.add_field(name="Uptime", value=str(uptime).split(".")[0], inline=False) - embed.add_field( - name="Total Messages", value=str(self.total_messages), inline=True - ) - embed.add_field( - name="Active Users", value=str(len(self.active_users)), inline=True - ) - embed.add_field( - name="Avg Messages/Hour", value=f"{avg_per_hour:.2f}", inline=True - ) - embed.add_field( - name="Peak Hour (UTC)", - value=f"{peak_hour}: {peak_count} messages", - inline=True, - ) + embed.add_field(name="Uptime", value=str(uptime).split('.')[0], inline=False) + embed.add_field(name="Total Messages", value=str(self.total_messages), inline=True) + embed.add_field(name="Active Users", value=str(len(self.active_users)), inline=True) + embed.add_field(name="Avg Messages/Hour", value=f"{avg_per_hour:.2f}", inline=True) + embed.add_field(name="Peak Hour (UTC)", value=f"{peak_hour}: {peak_count} messages", inline=True) - top_str = ( - "\n".join(f"<@{user_id}>: {count} messages" for user_id, count in top_users) - or "No data" - ) + top_str = "\n".join( + f"<@{user_id}>: {count} messages" for user_id, count in top_users + ) or "No data" embed.add_field(name="Top Chatters", value=top_str, inline=False) - cmd_str = ( - "\n".join( - f"{cmd}: {count}" for cmd, count in self.command_usage.most_common(5) - ) - or "No commands used yet" - ) + cmd_str = "\n".join( + f"{cmd}: {count}" for cmd, count in self.command_usage.most_common(5) + ) or "No commands used yet" embed.add_field(name="Top Commands", value=cmd_str, inline=False) await ctx.send(embed=embed) - async def setup(bot): await bot.add_cog(StatsCog(bot)) diff --git a/assets/cogs/slashcomandexample.py b/assets/cogs/slashcomandexample.py new file mode 100644 index 0000000..63b45f1 --- /dev/null +++ b/assets/cogs/slashcomandexample.py @@ -0,0 +1,22 @@ +import discord +from discord.ext import commands +from discord import app_commands + +class Ping(commands.Cog): + def __init__(self, bot): + self.bot = bot + + @app_commands.command(name="slashcommand", description="slashcommandexample") + async def ping(self, interaction: discord.Interaction): + await interaction.response.defer() + exampleembed = discord.Embed( + title="Pong!!", + description="The Beretta fires fast and won't make you feel any better!", + color=discord.Color.blue() + ) + exampleembed.set_footer(text=f"Requested by {interaction.user.name}", icon_url=interaction.user.avatar.url) + + await interaction.followup.send(embed=exampleembed) + +async def setup(bot): + await bot.add_cog(Ping(bot)) diff --git a/assets/cogs/songchanger.py b/assets/cogs/songchanger.py index 55efacc..36fde47 100644 --- a/assets/cogs/songchanger.py +++ b/assets/cogs/songchanger.py @@ -3,9 +3,6 @@ from discord.ext import commands from modules.globalvars import RED, GREEN, RESET, LOCAL_VERSION_FILE import os -from modules.permission import requires_admin - - class songchange(commands.Cog): def __init__(self, bot): self.bot = bot @@ -19,20 +16,18 @@ class songchange(commands.Cog): global local_version local_version = get_local_version() - @requires_admin() @commands.command() - async def changesong(self, ctx, song: str): - await ctx.send(f"Changed song to {song}") + async def changesong(self, ctx): + if LOCAL_VERSION_FILE > "0.11.8": + await ctx.send(f"Goober is too old! you must have version 0.11.8 you have {local_version}") + return + await ctx.send("Check the terminal! (this does not persist across restarts)") + song = input("\nEnter a song:\n") try: - await self.bot.change_presence( - activity=discord.Activity( - type=discord.ActivityType.listening, name=f"{song}" - ) - ) + await self.bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=f"{song}")) print(f"{GREEN}Changed song to {song}{RESET}") except Exception as e: print(f"{RED}An error occurred while changing songs..: {str(e)}{RESET}") - async def setup(bot): await bot.add_cog(songchange(bot)) diff --git a/assets/cogs/tf.py b/assets/cogs/tf.py deleted file mode 100644 index 0b31a91..0000000 --- a/assets/cogs/tf.py +++ /dev/null @@ -1,191 +0,0 @@ -import discord -from discord.ext import commands -import os -import numpy as np -import json -import pickle -import functools -import re -import time -import asyncio - -ready = True -MODEL_MATCH_STRING = r"[0-9]{2}_[0-9]{2}_[0-9]{4}-[0-9]{2}_[0-9]{2}" - -try: - import tensorflow as tf - import keras - from keras.preprocessing.text import Tokenizer - from keras.preprocessing.sequence import pad_sequences - from keras.models import Sequential, load_model - from keras.layers import Embedding, LSTM, Dense - from keras.backend import clear_session - - if tf.config.list_physical_devices("GPU"): - print("Using GPU acceleration") - elif tf.config.list_physical_devices("Metal"): - print("Using Metal for macOS acceleration") -except ImportError: - print( - "ERROR: Failed to import TensorFlow. Ensure you have the correct dependencies:" - ) - print("tensorflow>=2.15.0") - print("For macOS (Apple Silicon): tensorflow-metal") - ready = False - - -class TFCallback(keras.callbacks.Callback): - def __init__(self, bot, progress_embed: discord.Embed, message): - self.embed = progress_embed - self.bot = bot - self.message = message - self.times = [time.time()] - - async def send_message(self, message: str, description: str, **kwargs): - if "epoch" in kwargs: - self.times.append(time.time()) - avg_epoch_time = np.mean(np.diff(self.times)) - description = f"ETA: {round(avg_epoch_time)}s" - self.embed.add_field( - name=f" - {message}", - value=description, - inline=False, - ) - await self.message.edit(embed=self.embed) - - def on_train_end(self, logs=None): - self.bot.loop.create_task( - self.send_message("Training stopped", "Training has been stopped.") - ) - - def on_epoch_begin(self, epoch, logs=None): - self.bot.loop.create_task( - self.send_message( - f"Starting epoch {epoch}", "This might take a while", epoch=True - ) - ) - - def on_epoch_end(self, epoch, logs=None): - self.bot.loop.create_task( - self.send_message( - f"Epoch {epoch} ended", - f"Accuracy: {round(logs.get('accuracy', 0.0), 4)}", - ) - ) - - -class Ai: - def __init__(self): - model_path = settings.get("model_path") - if model_path: - self.__load_model(model_path) - self.is_loaded = model_path is not None - self.batch_size = 64 - - def generate_model_name(self): - return time.strftime("%d_%m_%Y-%H_%M", time.localtime()) - - def __load_model(self, model_path): - clear_session() - self.model = load_model(os.path.join(model_path, "model.h5")) - model_name = os.path.basename(model_path) - try: - with open(os.path.join(model_path, "tokenizer.pkl"), "rb") as f: - self.tokenizer = pickle.load(f) - except FileNotFoundError: - print("Failed to load tokenizer, using default.") - self.tokenizer = Tokenizer() - with open("memory.json", "r") as f: - self.tokenizer.fit_on_texts(json.load(f)) - self.is_loaded = True - - def reload_model(self): - clear_session() - model_path = settings.get("model_path") - if model_path: - self.__load_model(model_path) - self.is_loaded = True - - async def run_async(self, func, bot, *args, **kwargs): - return await bot.loop.run_in_executor( - None, functools.partial(func, *args, **kwargs) - ) - - -class Learning(Ai): - def create_model(self, memory, epochs=2): - memory = memory[:2000] - tokenizer = Tokenizer() - tokenizer.fit_on_texts(memory) - sequences = tokenizer.texts_to_sequences(memory) - X, y = [], [] - for seq in sequences: - for i in range(1, len(seq)): - X.append(seq[:i]) - y.append(seq[i]) - maxlen = max(map(len, X)) - X = pad_sequences(X, maxlen=maxlen, padding="pre") - y = np.array(y) - - model = Sequential( - [ - Embedding(input_dim=VOCAB_SIZE, output_dim=128, input_length=maxlen), - LSTM(64), - Dense(VOCAB_SIZE, activation="softmax"), - ] - ) - - model.compile( - optimizer="adam", - loss="sparse_categorical_crossentropy", - metrics=["accuracy"], - ) - history = model.fit(X, y, epochs=epochs, batch_size=64, callbacks=[tf_callback]) - self.save_model(model, tokenizer, history) - - def save_model(self, model, tokenizer, history, name=None): - name = name or self.generate_model_name() - model_dir = os.path.join("models", name) - os.makedirs(model_dir, exist_ok=True) - - with open(os.path.join(model_dir, "info.json"), "w") as f: - json.dump(history.history, f) - with open(os.path.join(model_dir, "tokenizer.pkl"), "wb") as f: - pickle.dump(tokenizer, f) - model.save(os.path.join(model_dir, "model.h5")) - - -class Generation(Ai): - def generate_sentence(self, word_amount, seed): - if not self.is_loaded: - return False - for _ in range(word_amount): - token_list = self.tokenizer.texts_to_sequences([seed])[0] - token_list = pad_sequences( - [token_list], maxlen=self.model.input_shape[1], padding="pre" - ) - predicted_word_index = np.argmax( - self.model.predict(token_list, verbose=0), axis=-1 - )[0] - output_word = next( - ( - w - for w, i in self.tokenizer.word_index.items() - if i == predicted_word_index - ), - "", - ) - seed += " " + output_word - return seed - - -VOCAB_SIZE = 100_000 -settings = {} -learning = Learning() -generation = Generation() - -tf_callback = None - - -async def setup(bot): - await bot.add_cog(Tf(bot)) diff --git a/assets/cogs/webscraper.py b/assets/cogs/webscraper.py.disabled similarity index 89% rename from assets/cogs/webscraper.py rename to assets/cogs/webscraper.py.disabled index b2fe177..351e2e8 100644 --- a/assets/cogs/webscraper.py +++ b/assets/cogs/webscraper.py.disabled @@ -5,12 +5,7 @@ from bs4 import BeautifulSoup import json import asyncio from urllib.parse import urljoin -from modules.permission import requires_admin -from modules.settings import instance as settings_manager - -settings = settings_manager.settings - - +from modules.globalvars import ownerid class WebScraper(commands.Cog): def __init__(self, bot): self.bot = bot @@ -27,7 +22,7 @@ class WebScraper(commands.Cog): def extract_sentences(self, text): """Extract sentences from text.""" - sentences = text.split(".") + sentences = text.split('.') return [sentence.strip() for sentence in sentences if sentence.strip()] def save_to_json(self, sentences): @@ -54,6 +49,7 @@ class WebScraper(commands.Cog): print("No data to undo.") return False + data = data[:-1] with open("memory.json", "w") as file: @@ -77,14 +73,18 @@ class WebScraper(commands.Cog): soup = BeautifulSoup(html, "html.parser") - for paragraph in soup.find_all("p"): + for paragraph in soup.find_all('p'): sentences = self.extract_sentences(paragraph.get_text()) self.save_to_json(sentences) - @requires_admin() + @commands.command() async def start_scrape(self, ctx, start_url: str): """Command to start the scraping process.""" + if ctx.author.id != ownerid: + await ctx.send("You do not have permission to use this command.") + return + if not start_url.startswith("http"): await ctx.send("Please provide a valid URL.") return @@ -96,16 +96,18 @@ class WebScraper(commands.Cog): await ctx.send("Scraping complete! Sentences saved to memory.json.") - @requires_admin() @commands.command() async def undo_scrape(self, ctx): """Command to undo the last scrape.""" + if ctx.author.id != ownerid: + await ctx.send("You do not have permission to use this command.") + return + success = self.undo_last_scrape() if success: await ctx.send("Last scrape undone successfully.") else: await ctx.send("No data to undo or an error occurred.") - async def setup(bot): await bot.add_cog(WebScraper(bot)) diff --git a/assets/cogs/webserver.py b/assets/cogs/webserver.py index d367d75..110c20d 100644 --- a/assets/cogs/webserver.py +++ b/assets/cogs/webserver.py @@ -14,7 +14,6 @@ from modules.globalvars import VERSION_URL import sys import subprocess - class GooberWeb(commands.Cog): def __init__(self, bot): self.bot = bot @@ -25,20 +24,18 @@ class GooberWeb(commands.Cog): 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), - web.get("/settings", self.handle_settings), - web.post("/update_settings", self.handle_update_settings), - web.post("/restart_bot", self.handle_restart_bot), - ] - ) + + 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), + web.get('/settings', self.handle_settings), + web.post('/update_settings', self.handle_update_settings), + web.post('/restart_bot', self.handle_restart_bot), + ]) self.bot.loop.create_task(self.start_web_server()) self.update_clients.start() @@ -55,82 +52,69 @@ class GooberWeb(commands.Cog): async def get_blacklisted_users(self): blacklisted_ids = os.getenv("BLACKLISTED_USERS", "").split(",") blacklisted_users = [] - + for user_id in blacklisted_ids: if not user_id.strip(): continue - + try: user = await self.bot.fetch_user(int(user_id)) - blacklisted_users.append( - { - "name": f"{user.name}", - "avatar_url": ( - str(user.avatar.url) - if user.avatar - else str(user.default_avatar.url) - ), - "id": user.id, - } - ) + blacklisted_users.append({ + "name": f"{user.name}", + "avatar_url": str(user.avatar.url) if user.avatar else str(user.default_avatar.url), + "id": user.id + }) except discord.NotFound: - blacklisted_users.append( - { - "name": f"Unknown User ({user_id})", - "avatar_url": "", - "id": user_id, - } - ) + blacklisted_users.append({ + "name": f"Unknown User ({user_id})", + "avatar_url": "", + "id": user_id + }) except discord.HTTPException as e: print(f"Error fetching user {user_id}: {e}") continue - + return blacklisted_users - + async def get_enhanced_guild_info(self): guilds = sorted(self.bot.guilds, key=lambda g: g.member_count, reverse=True) guild_info = [] - + for guild in guilds: icon_url = str(guild.icon.url) if guild.icon else "" - guild_info.append( - { - "name": guild.name, - "member_count": guild.member_count, - "icon_url": icon_url, - "id": guild.id, - } - ) - + guild_info.append({ + "name": guild.name, + "member_count": guild.member_count, + "icon_url": icon_url, + "id": guild.id + }) + return guild_info - + 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) + 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): - if self.site is None or self.runner is None: - return - 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) @@ -139,62 +123,62 @@ class GooberWeb(commands.Cog): 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") + 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})" 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" - + guild_info = await self.get_enhanced_guild_info() blacklisted_users = await self.get_blacklisted_users() - + 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}%", @@ -209,29 +193,23 @@ class GooberWeb(commands.Cog): "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 "" - ), + "bot_avatar_url": str(self.bot.user.avatar.url) if self.bot.user.avatar else "", "authenticated": os.getenv("gooberauthenticated"), "lastmsg": os.getenv("gooberlatestgen"), "localversion": os.getenv("gooberlocal_version"), "latestversion": os.getenv("gooberlatest_version"), - "owner": os.getenv("ownerid"), + "owner": os.getenv("ownerid") } - + 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", "") + song = request.query.get('song', '') if song: - await self.bot.change_presence( - activity=discord.Activity( - type=discord.ActivityType.listening, name=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) @@ -243,37 +221,36 @@ class GooberWeb(commands.Cog): async def read_env_file(self): env_vars = {} try: - with open(".env", "r") as f: + with open('.env', 'r') as f: for line in f: line = line.strip() - if not line or line.startswith("#") or "=" not in line: + if not line or line.startswith('#') or '=' not in line: continue - key, value = line.split("=", 1) + key, value = line.split('=', 1) key = key.strip() - if key in ["splashtext", "DISCORD_BOT_TOKEN"]: + if key in ['splashtext', 'DISCORD_BOT_TOKEN']: continue - env_vars[key] = value.strip("\"'") + env_vars[key] = value.strip('"\'') except FileNotFoundError: print(".env file not found") return env_vars + async def handle_settings(self, request): env_vars = await self.read_env_file() - + # Get config.py variables config_vars = {} try: - with open("config.py", "r") as f: + with open('config.py', 'r') as f: for line in f: - if line.startswith("VERSION_URL"): - config_vars["VERSION_URL"] = ( - line.split("=", 1)[1].strip().strip('"') - ) + if line.startswith('VERSION_URL'): + config_vars['VERSION_URL'] = line.split('=', 1)[1].strip().strip('"') except FileNotFoundError: pass - + settings_html = """ @@ -295,7 +272,7 @@ class GooberWeb(commands.Cog):

Goober Settings

""" - + for key, value in env_vars.items(): settings_html += f"""
@@ -303,7 +280,7 @@ class GooberWeb(commands.Cog):
""" - + for key, value in config_vars.items(): settings_html += f"""
@@ -311,7 +288,7 @@ class GooberWeb(commands.Cog):
""" - + settings_html += """
@@ -322,15 +299,15 @@ class GooberWeb(commands.Cog): """ - - return web.Response(text=settings_html, content_type="text/html") - + + return web.Response(text=settings_html, content_type='text/html') + async def handle_update_settings(self, request): data = await request.post() env_text = "" try: - with open(".env", "r") as f: + with open('.env', 'r') as f: env_text = f.read() except FileNotFoundError: pass @@ -338,42 +315,32 @@ class GooberWeb(commands.Cog): def replace_match(match): key = match.group(1) value = match.group(2) - if key in ["splashtext", "DISCORD_BOT_TOKEN"]: + if key in ['splashtext', 'DISCORD_BOT_TOKEN']: return match.group(0) if key in data: new_value = data[key] if not (new_value.startswith('"') and new_value.endswith('"')): new_value = f'"{new_value}"' - return f"{key}={new_value}" + return f'{key}={new_value}' return match.group(0) - env_text = re.sub( - r"^(\w+)=([\s\S]+?)(?=\n\w+=|\Z)", - replace_match, - env_text, - flags=re.MULTILINE, - ) + env_text = re.sub(r'^(\w+)=([\s\S]+?)(?=\n\w+=|\Z)', replace_match, env_text, flags=re.MULTILINE) - with open(".env", "w") as f: - f.write(env_text.strip() + "\n") + with open('.env', 'w') as f: + f.write(env_text.strip() + '\n') - if "VERSION_URL" in data: + if 'VERSION_URL' in data: config_text = "" try: - with open("config.py", "r") as f: + with open('config.py', 'r') as f: config_text = f.read() except FileNotFoundError: pass - config_text = re.sub( - r'^(VERSION_URL\s*=\s*").+?"', - f'\\1{data["VERSION_URL"]}"', - config_text, - flags=re.MULTILINE, - ) + config_text = re.sub(r'^(VERSION_URL\s*=\s*").+?"', f'\\1{data["VERSION_URL"]}"', config_text, flags=re.MULTILINE) - with open("config.py", "w") as f: - f.write(config_text.strip() + "\n") + with open('config.py', 'w') as f: + f.write(config_text.strip() + '\n') return aiohttp.web.Response(text="Settings updated successfully!") @@ -381,12 +348,8 @@ class GooberWeb(commands.Cog): stats = await self.get_bot_stats() guild_list_html = "" - for guild in stats["guilds"]: - icon_html = ( - f'guild icon' - if guild["icon_url"] - else '
' - ) + for guild in stats['guilds']: + icon_html = f'guild icon' if guild["icon_url"] else '
' guild_list_html += f"""
{icon_html} @@ -397,12 +360,8 @@ class GooberWeb(commands.Cog):
""" blacklisted_users_html = "" - for user in stats["blacklisted_users"]: - avatar_html = ( - f'user avatar' - if user["avatar_url"] - else '
' - ) + for user in stats['blacklisted_users']: + avatar_html = f'user avatar' if user["avatar_url"] else '
' blacklisted_users_html += f"""
{avatar_html} @@ -413,11 +372,11 @@ class GooberWeb(commands.Cog):
""" - owner_id = stats.get("owner") + owner_id = stats.get('owner') owner = None owner_username = "Owner" owner_pfp = "" - + if owner_id: try: owner = await self.bot.fetch_user(int(owner_id)) @@ -426,6 +385,7 @@ class GooberWeb(commands.Cog): except: pass + html_content = f""" @@ -906,16 +866,15 @@ class GooberWeb(commands.Cog): """ - - return web.Response(text=html_content, content_type="text/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)) diff --git a/assets/cogs/whoami.py b/assets/cogs/whoami.py index 55379ec..a60633f 100644 --- a/assets/cogs/whoami.py +++ b/assets/cogs/whoami.py @@ -1,7 +1,6 @@ import discord from discord.ext import commands - class whoami(commands.Cog): def __init__(self, bot): self.bot = bot @@ -14,13 +13,12 @@ class whoami(commands.Cog): embed = discord.Embed( title="User Information", description=f"Your User ID is: {user_id}\n" - f"Your username is: {username}\n" - f"Your nickname in this server is: <@{user_id}>", - color=discord.Color.blue(), + f"Your username is: {username}\n" + f"Your nickname in this server is: <@{user_id}>", + color=discord.Color.blue() ) await ctx.send(embed=embed) - async def setup(bot): await bot.add_cog(whoami(bot)) diff --git a/assets/fonts/SpecialGothic.ttf b/assets/fonts/SpecialGothic.ttf deleted file mode 100644 index 1da7c19..0000000 Binary files a/assets/fonts/SpecialGothic.ttf and /dev/null differ diff --git a/assets/images/breaking_news.png b/assets/images/breaking_news.png deleted file mode 100644 index 039f6f0..0000000 Binary files a/assets/images/breaking_news.png and /dev/null differ diff --git a/assets/images/cache/breaking_news.png b/assets/images/cache/breaking_news.png deleted file mode 100644 index 326b20d..0000000 Binary files a/assets/images/cache/breaking_news.png and /dev/null differ diff --git a/assets/locales/en.json b/assets/locales/en.json index 71e19eb..53e9dd5 100644 --- a/assets/locales/en.json +++ b/assets/locales/en.json @@ -1,8 +1,23 @@ { + "minigames_hangman_game": "Word: {display_word()}\nWrong guesses: {wrong_guesses}/{max_wrong}", + "minigames_hangman_lost": "You lost! The word was:", + "minigames_hangman_won": "You won! The word was:", + "minigames_hangman_already_guessed": "You already guessed", + "minigames_hangman_user_letter_guess": "Your letter guess", + "minigames_hangman_guess": "Guess a Letter", + "minigames_hangman_api_failed": "Failed to get a random word.", + "minigames_hangman": "Play Hangman with a random word", + "minigames_click_to_guess": "Click to guess a number from 1 to 10", + "minigames_guess_button": "Guess", + "minigames_wrong_number": "Wrong! The number was", + "minigames_correct": "Correct!", + "minigames_invalid_number": "Invalid number!", + "minigames_guess_the_number": "Guess the number", + "minigames_your_guess": "Your guess (1-10)", "memory_file_valid": "The memory.json file is valid!", "file_aint_uft8": "File is not valid UTF-8 text. Might be binary or corrupted.", "psutil_not_installed": "Memory check skipped.", - "not_cloned": "Goober is not cloned! Please clone it from GitHub.", + "not_cloned": "Goober is not cloned! Please clone it from Git.", "checks_disabled": "Checks are disabled!", "unhandled_exception": "An unhandled exception occurred. Please report this issue on GitHub.", "active_users:": "Active users:", @@ -131,14 +146,6 @@ "command_stats_embed_field2name": "Version", "command_stats_embed_field2value": "Local: {local_version} \nLatest: {latest_version}", "command_stats_embed_field3name": "Variable Info", - "command_stats_embed_field3value": "Name: {NAME} \nPrefix: {PREFIX} \nOwner ID: {ownerid}\nPing line: {PING_LINE} \nMemory Sharing Enabled: {showmemenabled} \nUser Training Enabled: {USERTRAIN_ENABLED}\nSong: {song} \nSplashtext: ```{splashtext}```", - "no_image_available": "No images available!", - "failed_generate_image": "Failed to generate an image", - "markov_model_not_found": "Markov model not found!", - "blacklisted": "blacklisted", - "blacklisted_user": "Blacklisted user", - "edit_fail": "Failed to edit message", - "system_info": "System information", - "cpu_info": "CPU: {cpu}" + "command_stats_embed_field3value": "Name: {NAME} \nPrefix: {PREFIX} \nOwner ID: {ownerid}\nPing line: {PING_LINE} \nMemory Sharing Enabled: {showmemenabled} \nUser Training Enabled: {USERTRAIN_ENABLED}\nSong: {song} \nSplashtext: ```{splashtext}```" } diff --git a/assets/locales/es.json b/assets/locales/es.json index c2aa734..4444a9c 100644 --- a/assets/locales/es.json +++ b/assets/locales/es.json @@ -73,5 +73,5 @@ "command_stats_embed_field2name": "Version", "command_stats_embed_field2value": "Version local: {local_version} \nUltima version: {latest_version}", "command_stats_embed_field3name": "informacion sobre las variables", - "command_stats_embed_field3value": "Nombre: {NAME} \nPrefijo: {PREFIX} \nID del propietario: {ownerid}\nLinea de ping: {PING_LINE} \nCompartir memoria habilitada: {showmemenabled} \nEntrenamiento de usuario habilitado: {USERTRAIN_ENABLED} \nCancion: {song} \nTexto de bienvenida: ```{splashtext}```" + "command_stats_embed_field3value": "Nombre: {NAME} \nPrefijo: {PREFIX} \nID del propietario: {ownerid}\nLinea de ping: {PING_LINE} \nCompartir memoria habilitada: {showmemenabled} \nEntrenamiento de usuario habilitado: {USERTRAIN_ENABLED} \nCancion: {song} \nTexto de bienvenida: ```{splashtext}```" } \ No newline at end of file diff --git a/assets/locales/fi.json b/assets/locales/fi.json index 7b16005..93141cc 100644 --- a/assets/locales/fi.json +++ b/assets/locales/fi.json @@ -102,7 +102,7 @@ "command_markov_retrain": "Uudelleenkoulutetaan markov-mallia... Odota.", "command_markov_memory_not_found": "Virhe: muistitiedostoa ei löytynyt!", "command_markov_memory_is_corrupt": "Virhe: muistitiedosto on korruptoitu!", - "command_markov_retraining": "Käsitellään {processed_data}/{data_size} datapistettä...", + "command_markov_retraining": "Käsitellään {processed_data}/{data_size} datapisteestä...", "command_markov_retrain_successful": "Markov-malli koulutettiin uudestaan {data_size} datapisteellä!", "command_desc_talk":"puhuu ja sillei", "command_talk_insufficent_text": "Minun pitää oppia lisää viesteistä ennen kun puhun.", @@ -131,6 +131,5 @@ "command_stats_embed_field2value": "Paikallinen: {local_version} \nUusin: {latest_version}", "command_stats_embed_field3name": "Muuttajainformaatio", "command_stats_embed_field3value": "Nimi: {NAME} \nEtuliite: {PREFIX} \nOmistajan ID: {ownerid}\nPing-linja: {PING_LINE} \nMuistin jako päällä: {showmemenabled} \nOppiminen käyttäjistä: {USERTRAIN_ENABLED}\nLaulu: {song} \nRoisketeksti: ```{splashtext}```" - } diff --git a/assets/locales/fr.json b/assets/locales/fr.json deleted file mode 100644 index 572d2f4..0000000 --- a/assets/locales/fr.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "checks_disabled": "Les vérifications sont désactivées !", - "unhandled_exception": "Une exception non gérée est survenue. Merci de rapporter ce problème sur GitHub.", - "active_users:": "Utilisateurs actifs :", - "spacy_initialized": "spaCy et spacytextblob sont prêts.", - "spacy_model_not_found": "Le modèle spaCy est introuvable ! Téléchargement en cours...", - "env_file_not_found": "Le fichier .env est introuvable ! Créez-en un avec les variables nécessaires.", - "error_fetching_active_users": "Erreur lors de la récupération des utilisateurs actifs : {error}", - "error_sending_alive_ping": "Erreur lors de l’envoi du ping actif : {error}", - "already_started": "J’ai déjà démarré ! Je ne me mets pas à jour...", - "please_restart": "Redémarre, stp !", - "local_ahead": "Local {remote}/{branch} est en avance ou à jour. Pas de mise à jour...", - "remote_ahead": "Remote {remote}/{branch} est en avance. Mise à jour en cours...", - "cant_find_local_version": "Je ne trouve pas la variable local_version ! Ou elle a été modifiée et ce n’est pas un entier !", - "running_prestart_checks": "Exécution des vérifications préalables au démarrage...", - "continuing_in_seconds": "Reprise dans {seconds} secondes... Appuie sur une touche pour passer.", - "missing_requests_psutil": "requests et psutil manquants ! Installe-les avec pip : `pip install requests psutil`", - "requirements_not_found": "requirements.txt introuvable à {path}, a-t-il été modifié ?", - "warning_failed_parse_imports": "Avertissement : Échec du parsing des imports depuis {filename} : {error}", - "cogs_dir_not_found": "Répertoire des cogs introuvable à {path}, scan ignoré.", - "std_lib_local_skipped": "LIB STD / LOCAL {package} (vérification sautée)", - "ok_installed": "OK", - "missing_package": "MANQUANT", - "missing_package2": "n’est pas installé", - "missing_packages_detected": "Packages manquants détectés :", - "telling_goober_central": "Envoi à goober central à {url}", - "failed_to_contact": "Impossible de contacter {url} : {error}", - "all_requirements_satisfied": "Toutes les dépendances sont satisfaites.", - "ping_to": "Ping vers {host} : {latency} ms", - "high_latency": "Latence élevée détectée ! Tu pourrais avoir des délais de réponse.", - "could_not_parse_latency": "Impossible d’analyser la latence.", - "ping_failed": "Ping vers {host} échoué.", - "error_running_ping": "Erreur lors du ping : {error}", - "memory_usage": "Utilisation mémoire : {used} Go / {total} Go ({percent}%)", - "memory_above_90": "Usage mémoire au-dessus de 90% ({percent}%). Pense à libérer de la mémoire.", - "total_memory": "Mémoire totale : {total} Go", - "used_memory": "Mémoire utilisée : {used} Go", - "low_free_memory": "Mémoire libre faible détectée ! Seulement {free} Go disponibles.", - "measuring_cpu": "Mesure de l’usage CPU par cœur...", - "core_usage": "Cœur {idx} : [{bar}] {usage}%", - "total_cpu_usage": "Usage total CPU : {usage}%", - "high_avg_cpu": "Moyenne CPU élevée : {usage}%", - "really_high_cpu": "Charge CPU vraiment élevée ! Le système pourrait ralentir ou planter.", - "memory_file": "Fichier mémoire : {size} Mo", - "memory_file_large": "Fichier mémoire de 1 Go ou plus, pense à le nettoyer pour libérer de l’espace.", - "memory_file_corrupted": "Fichier mémoire corrompu ! Erreur JSON : {error}", - "consider_backup_memory": "Pense à sauvegarder et recréer le fichier mémoire.", - "memory_file_encoding": "Problèmes d’encodage du fichier mémoire : {error}", - "error_reading_memory": "Erreur lecture fichier mémoire : {error}", - "memory_file_not_found": "Fichier mémoire introuvable.", - "modification_warning": "Goober a été modifié ! Toutes les modifications seront perdues lors d'une mise à jour !", - "reported_version": "Version rapportée :", - "current_hash": "Hachage actuel :", - "not_found": "n'est pas trouvé !", - "version_error": "Impossible de récupérer les informations de version. Code d'état", - "loaded_cog": "Cog chargé :", - "loaded_cog2": "Module chargé :", - "cog_fail": "Échec du chargement du cog :", - "cog_fail2": "Échec du chargement du module :", - "no_model": "Aucun modèle Markov sauvegardé trouvé. Démarrage à partir de zéro.", - "folder_created": "Dossier '{folder_name}' créé.", - "folder_exists": "Le dossier '{folder_name}' existe déjà. Ignorons...", - "logged_in": "Connecté en tant que", - "synced_commands": "Synchronisé", - "synced_commands2": "commandes !", - "fail_commands_sync": "Échec de la synchronisation des commandes :", - "started": "{name} a démarré !", - "name_check": "Erreur lors de la vérification de la disponibilité du nom :", - "name_taken": "Le nom est déjà pris. Veuillez choisir un autre nom.", - "name_check2": "Erreur lors de la vérification de la disponibilité du nom :", - "add_token": "Token : {token}\nVeuillez ajouter ce token à votre fichier .env comme", - "token_exists": "Le token existe déjà dans .env. Utilisation du token existant.", - "registration_error": "Erreur lors de l'enregistrement :", - "version_backup": "Sauvegarde créée :", - "backup_error": "Erreur : {LOCAL_VERSION_FILE} introuvable pour la sauvegarde.", - "model_loaded": "Modèle Markov chargé depuis", - "fetch_update_fail": "Impossible de récupérer les informations de mise à jour.", - "invalid_server": "Erreur : Informations de version invalides reçues du serveur.", - "goober_server_alert": "Alerte du serveur Goober central !\n", - "new_version": "Nouvelle version disponible : {latest_version} (Actuelle : {local_version})", - "changelog": "Consultez {VERSION_URL}/goob/changes.txt pour voir les modifications\n\n", - "invalid_version": "La version : {local_version} n'est pas valide !", - "invalid_version2": "Si c'est intentionnel, ignorez ce message. Sinon, appuyez sur Y pour récupérer une version valide depuis le serveur, quelle que soit la version actuelle de Goober.", - "invalid_version3": "La version actuelle sera sauvegardée dans current_version.bak..", - "input": "(Y ou toute autre touche pour ignorer...)", - "modification_ignored": "Vous avez modifié", - "modification_ignored2": "IGNOREWARNING est désactivé..", - "latest_version": "Vous utilisez la dernière version :", - "latest_version2": "Consultez {VERSION_URL}/goob/changes.txt pour voir les modifications", - "pinging_disabled": "Le ping est désactivé ! Je ne préviens pas le serveur que je suis en ligne...", - "goober_ping_success": "Connecté à Goober central en tant que {NAME}", - "goober_ping_fail": "Échec de l'envoi des données. Le serveur a retourné le code d'état :", - "goober_ping_fail2": "Une erreur est survenue lors de l'envoi des données :", - "sentence_positivity": "La positivité de la phrase est :", - "command_edit_fail": "Échec de la modification du message :", - "command_desc_retrain": "Réentraîne manuellement le modèle Markov.", - "command_markov_retrain": "Réentraînement du modèle Markov... Veuillez patienter.", - "command_markov_memory_not_found": "Erreur : fichier de mémoire introuvable !", - "command_markov_memory_is_corrupt": "Erreur : le fichier de mémoire est corrompu !", - "command_markov_retraining": "Traitement de {processed_data}/{data_size} points de données...", - "command_markov_retrain_successful": "Modèle Markov réentraîné avec succès en utilisant {data_size} points de données !", - "command_desc_talk": "parle et tout ça", - "command_talk_insufficent_text": "Je dois apprendre plus de messages avant de pouvoir parler.", - "command_talk_generation_fail": "Je n'ai rien à dire pour le moment !", - "command_desc_help": "aide", - "command_help_embed_title": "Aide du bot", - "command_help_embed_desc": "Liste des commandes regroupées par catégorie.", - "command_help_categories_general": "Général", - "command_help_categories_admin": "Administration", - "command_help_categories_custom": "Commandes personnalisées", - "command_ran": "Info : {message.author.name} a exécuté {message.content}", - "command_ran_s": "Info : {interaction.user} a exécuté ", - "command_desc_ping": "ping", - "command_ping_embed_desc": "Latence du bot :", - "command_ping_footer": "Demandé par", - "command_about_desc": "à propos", - "command_about_embed_title": "À propos de moi", - "command_about_embed_field1": "Nom", - "command_about_embed_field2name": "Version", - "command_about_embed_field2value": "Locale : {local_version} \nDernière : {latest_version}", - "command_desc_stats": "statistiques", - "command_stats_embed_title": "Statistiques du bot", - "command_stats_embed_desc": "Données sur la mémoire du bot.", - "command_stats_embed_field1name": "Statistiques du fichier", - "command_stats_embed_field1value": "Taille : {file_size} octets\nLignes : {line_count}", - "command_stats_embed_field2name": "Version", - "command_stats_embed_field2value": "Locale : {local_version} \nDernière : {latest_version}", - "command_stats_embed_field3name": "Informations variables", - "command_stats_embed_field3value": "Nom : {NAME} \nPréfixe : {PREFIX} \nID du propriétaire : {ownerid}\nLigne de ping : {PING_LINE} \nPartage de mémoire activé : {showmemenabled} \nEntraînement utilisateur activé : {USERTRAIN_ENABLED} \nChanson : {song} \nTexte de démarrage : ```{splashtext}```" -} \ No newline at end of file diff --git a/assets/locales/fr_ca.json b/assets/locales/fr_ca.json new file mode 100644 index 0000000..782b255 --- /dev/null +++ b/assets/locales/fr_ca.json @@ -0,0 +1,149 @@ +{ + "minigames_hangman_game": "Mot à deviner : {display_word()}\nMauvaises guesses : {wrong_guesses}/{max_wrong}", + "minigames_hangman_lost": "T'es échoué solide! Le mot était :", + "minigames_hangman_won": "T'as gagné en masse! Le mot était :", + "minigames_hangman_already_guessed": "T'as déjà essayé ça mon chum", + "minigames_hangman_user_letter_guess": "Ta guess de lettre", + "minigames_hangman_guess": "Devine une lettre", + "minigames_hangman_api_failed": "Ça a chié en essayant d'avoir un mot aléatoire.", + "minigames_hangman": "Jouer au Pendu avec un mot pogné au hasard", + "minigames_click_to_guess": "Clique pour deviner un chiffre entre 1 pis 10", + "minigames_guess_button": "Devine", + "minigames_wrong_number": "Nope! C'était", + "minigames_correct": "Bonne guess!", + "minigames_invalid_number": "Chiffre pas valide!", + "minigames_guess_the_number": "Devine le chiffre", + "minigames_your_guess": "Ta guess (1-10)", + "memory_file_valid": "Le fichier memory.json est correct!", + "file_aint_uft8": "Le fichier est pas du bon UTF-8. Ça doit être binaire ou scrap.", + "psutil_not_installed": "Vérification de mémoire skipée.", + "not_cloned": "Goober est pas cloné! Va donc le cloner depuis Git.", + "checks_disabled": "Les checks sont désactivées!", + "unhandled_exception": "Y'a eu une erreur pas prévue. Rapporte ça sur GitHub mon gars.", + "active_users:": "Monde actif :", + "spacy_initialized": "spaCy pis spacytextblob sont prêts.", + "spacy_model_not_found": "Le modèle spaCy est introuvable! On le télécharge...", + "env_file_not_found": "Le fichier .env est pas là! Fais-en un avec les variables nécessaires.", + "error_fetching_active_users": "Ça a chié en essayant de pogner les utilisateurs actifs : {error}", + "error_sending_alive_ping": "Ça a chié en envoyant le ping : {error}", + "already_started": "J'suis déjà parti! J'me mets pas à jour...", + "please_restart": "Redémarre-moi donc!", + "local_ahead": "La version locale {remote}/{branch} est à jour. Pas besoin d'update...", + "remote_ahead": "La version remote {remote}/{branch} est en avance. On update...", + "cant_find_local_version": "J'arrive pas à trouver la variable local_version! Ou ben elle a été modifiée pis c'est pas un chiffre!", + "running_prestart_checks": "On fait les checks avant de partir...", + "continuing_in_seconds": "On continue dans {seconds} secondes... Appuie sur une touche pour skip.", + "missing_requests_psutil": "Y manque requests pis psutil! Installe-les avec pip : `pip install requests psutil`", + "requirements_not_found": "requirements.txt introuvable à {path}, est-ce qu'il a été modifié?", + "warning_failed_parse_imports": "Attention : Ça a chié en lisant les imports de {filename} : {error}", + "cogs_dir_not_found": "Le dossier des cogs est pas à {path}, on skip le scan.", + "std_lib_local_skipped": "LIB STD / LOCAL {package} (check skipé)", + "ok_installed": "OK", + "missing_package": "MANQUANT", + "missing_package2": "est pas installé", + "missing_packages_detected": "Y'a des affaires qui manquent :", + "telling_goober_central": "J'envoie ça à goober central à {url}", + "failed_to_contact": "J'ai pas réussi à contacter {url} : {error}", + "all_requirements_satisfied": "Tout ce qu'il faut est installé.", + "ping_to": "Ping à {host} : {latency} ms", + "high_latency": "Latence élevée! Ça pourrait être lent.", + "could_not_parse_latency": "J'ai pas pu comprendre la latence.", + "ping_failed": "Le ping à {host} a chié.", + "error_running_ping": "Ça a chié en faisant le ping : {error}", + "memory_usage": "Mémoire utilisée : {used} Go / {total} Go ({percent}%)", + "memory_above_90": "La mémoire est à plus de 90% ({percent}%). Libère de la mémoire.", + "total_memory": "Mémoire totale : {total} Go", + "used_memory": "Mémoire utilisée : {used} Go", + "low_free_memory": "Y'a presque plus de mémoire! Juste {free} Go de libre.", + "measuring_cpu": "On check l'usage CPU par coeur...", + "core_usage": "Coeur {idx} : [{bar}] {usage}%", + "total_cpu_usage": "Usage total CPU : {usage}%", + "high_avg_cpu": "CPU trop élevé : {usage}%", + "really_high_cpu": "Le CPU est en tabarnak! Ça pourrait crasher.", + "memory_file": "Fichier mémoire : {size} Mo", + "memory_file_large": "Fichier mémoire de 1 Go ou plus, nettoie ça pour faire de la place.", + "memory_file_corrupted": "Fichier mémoire scrap! Erreur JSON : {error}", + "consider_backup_memory": "Pense à faire un backup pis recréer le fichier mémoire.", + "memory_file_encoding": "Problème d'encodage du fichier mémoire : {error}", + "error_reading_memory": "Ça a chié en lisant le fichier mémoire : {error}", + "memory_file_not_found": "Fichier mémoire pas trouvé.", + "modification_warning": "Goober a été modifié! Tes modifications vont être perdues à l'update!", + "reported_version": "Version rapportée :", + "current_hash": "Hash actuel :", + "not_found": "est pas trouvé!", + "version_error": "J'ai pas pu avoir les infos de version. Code d'état", + "loaded_cog": "Cog chargé :", + "loaded_cog2": "Module chargé :", + "cog_fail": "Ça a chié en chargeant le cog :", + "cog_fail2": "Ça a chié en chargeant le module :", + "no_model": "Y'a pas de modèle Markov de sauvegardé. On part de zéro.", + "folder_created": "Dossier '{folder_name}' créé.", + "folder_exists": "Le dossier '{folder_name}' existe déjà. On skip...", + "logged_in": "Connecté en tant que", + "synced_commands": "Synchronisé", + "synced_commands2": "commandes!", + "fail_commands_sync": "Ça a chié en synchronisant les commandes :", + "started": "{name} est parti!", + "name_check": "Ça a chié en checkant si le nom est libre :", + "name_taken": "Le nom est déjà pris. Choisis-en un autre.", + "name_check2": "Ça a chié en checkant si le nom est libre :", + "add_token": "Token : {token}\nAjoute ce token dans ton .env comme", + "token_exists": "Le token existe déjà dans .env. On utilise celui-là.", + "registration_error": "Ça a chié en s'enregistrant :", + "version_backup": "Backup créé :", + "backup_error": "Erreur : {LOCAL_VERSION_FILE} pas trouvé pour le backup.", + "model_loaded": "Modèle Markov chargé depuis", + "fetch_update_fail": "J'ai pas pu avoir les infos d'update.", + "invalid_server": "Erreur : Infos de version invalides du serveur.", + "goober_server_alert": "Alerte du serveur Goober central!\n", + "new_version": "Nouvelle version disponible : {latest_version} (Actuelle : {local_version})", + "changelog": "Va voir {VERSION_URL}/goob/changes.txt pour les changements\n\n", + "invalid_version": "La version : {local_version} est pas valide!", + "invalid_version2": "Si c'est fait exprès, ignore ça. Sinon, appuie sur Y pour avoir une version valide du serveur, peu importe ta version actuelle de Goober.", + "invalid_version3": "La version actuelle va être backupée dans current_version.bak..", + "input": "(Y ou n'importe quelle touche pour skip...)", + "modification_ignored": "T'as modifié", + "modification_ignored2": "IGNOREWARNING est désactivé..", + "latest_version": "T'as la dernière version :", + "latest_version2": "Va voir {VERSION_URL}/goob/changes.txt pour les changements", + "pinging_disabled": "Le ping est désactivé! J'dis pas au serveur que j'suis en ligne...", + "goober_ping_success": "Connecté à Goober central en tant que {NAME}", + "goober_ping_fail": "Ça a chié en envoyant les données. Le serveur a retourné :", + "goober_ping_fail2": "Ça a chié en envoyant les données :", + "sentence_positivity": "La phrase est positive à :", + "command_edit_fail": "Ça a chié en éditant le message :", + "command_desc_retrain": "Réentraîne le modèle Markov à la main.", + "command_markov_retrain": "Réentraînement du modèle Markov... Attend un peu.", + "command_markov_memory_not_found": "Erreur : fichier mémoire pas trouvé!", + "command_markov_memory_is_corrupt": "Erreur : fichier mémoire scrap!", + "command_markov_retraining": "Traitement de {processed_data}/{data_size} points de données...", + "command_markov_retrain_successful": "Modèle Markov réentraîné avec succès avec {data_size} points de données!", + "command_desc_talk": "parle pis toute", + "command_talk_insufficent_text": "J'ai pas assez appris pour pouvoir parler.", + "command_talk_generation_fail": "J'ai rien à dire pour l'instant!", + "command_desc_help": "aide", + "command_help_embed_title": "Aide du bot", + "command_help_embed_desc": "Liste des commandes par catégorie.", + "command_help_categories_general": "Général", + "command_help_categories_admin": "Admin", + "command_help_categories_custom": "Commandes perso", + "command_ran": "Info : {message.author.name} a fait {message.content}", + "command_ran_s": "Info : {interaction.user} a fait ", + "command_desc_ping": "ping", + "command_ping_embed_desc": "Latence du bot :", + "command_ping_footer": "Demandé par", + "command_about_desc": "à propos", + "command_about_embed_title": "À propos de moi", + "command_about_embed_field1": "Nom", + "command_about_embed_field2name": "Version", + "command_about_embed_field2value": "Locale : {local_version} \nDernière : {latest_version}", + "command_desc_stats": "stats", + "command_stats_embed_title": "Stats du bot", + "command_stats_embed_desc": "Infos sur la mémoire du bot.", + "command_stats_embed_field1name": "Stats du fichier", + "command_stats_embed_field1value": "Taille : {file_size} octets\nLignes : {line_count}", + "command_stats_embed_field2name": "Version", + "command_stats_embed_field2value": "Locale : {local_version} \nDernière : {latest_version}", + "command_stats_embed_field3name": "Infos variables", + "command_stats_embed_field3value": "Nom : {NAME} \nPréfixe : {PREFIX} \nID du proprio : {ownerid}\nLigne de ping : {PING_LINE} \nPartage de mémoire activé : {showmemenabled} \nEntraînement utilisateur activé : {USERTRAIN_ENABLED} \nChanson : {song} \nTexte de démarrage : ```{splashtext}```" +} \ No newline at end of file diff --git a/assets/locales/it.json b/assets/locales/it.json index 954b0ce..4f97806 100644 --- a/assets/locales/it.json +++ b/assets/locales/it.json @@ -1,8 +1,23 @@ { + "minigames_hangman_game": "Parola: {display_word()}\nErrori: {wrong_guesses}/{max_wrong}", + "minigames_hangman_lost": "Hai perso! La parola era:", + "minigames_hangman_won": "Hai vinto! La parola era:", + "minigames_hangman_already_guessed": "Hai già indovinato", + "minigames_hangman_user_letter_guess": "La tua lettera", + "minigames_hangman_guess": "Indovina una lettera", + "minigames_hangman_api_failed": "Impossibile ottenere una parola casuale.", + "minigames_hangman": "Gioca all'impiccato con una parola casuale", + "minigames_click_to_guess": "Clicca per indovinare un numero da 1 a 10", + "minigames_guess_button": "Indovina", + "minigames_wrong_number": "Sbagliato! Il numero era", + "minigames_correct": "Corretto!", + "minigames_invalid_number": "Numero non valido!", + "minigames_guess_the_number": "Indovina il numero", + "minigames_your_guess": "Il tuo numero (1-10)", "memory_file_valid": "Il file JSON è valido!", "file_aint_utf8": "Il file non è un UTF-8 valido. Forse è binario?", "psutil_not_installed": "Controllo memoria saltato.", - "not_cloned": "Goober non è stato clonato! Clonalo da GitHub.", + "not_cloned": "Goober non è stato clonato! Clonalo da Git.", "checks_disabled": "I controlli sono disabilitati!", "unhandled_exception": "Si è verificata un'eccezione non gestita. Segnala questo problema su GitHub, per favore.", "active_users:": "Utenti attivi:", diff --git a/bot.py b/bot.py index 6c5a30a..bbee94d 100644 --- a/bot.py +++ b/bot.py @@ -1,5 +1,22 @@ +import os +import re +import json +import time +import random +import traceback +import subprocess +import tempfile +import shutil +import psutil +import asyncio +import platform +import sys +from typing import List, Dict, Set, Optional, Tuple, Any, Union, Callable, Coroutine, TypeVar, Type import logging +from modules.globalvars import * +from modules.prestartchecks import start_checks from modules.logger import GooberFormatter +import logging logger = logging.getLogger("goober") logger.setLevel(logging.DEBUG) @@ -15,238 +32,205 @@ file_handler.setFormatter(GooberFormatter(colors=False)) logger.addHandler(console_handler) logger.addHandler(file_handler) -import os -import re -import json -import time -import random -import traceback -import subprocess -import tempfile -import shutil -import sys -from typing import ( - List, - Dict, - Literal, - Set, - Optional, - Tuple, - Any, - TypedDict, - Union, - Callable, - Coroutine, - TypeVar, - Type, -) -import logging -from modules.prestartchecks import start_checks -import modules.keys as k -from modules import key_compiler -import logging -from watchdog.observers import Observer -from watchdog.events import FileSystemEventHandler -from modules.settings import instance as settings_manager, ActivityType -from modules.permission import requires_admin -from modules.sync_conenctor import instance as sync_connector - -import threading - - -def build_keys(): - key_compiler.build_result( - "en", - "assets/locales", - types=True, - output_path="modules/keys.py", - generate_comments=True, - ) - - -build_keys() - - -settings = settings_manager.settings - -splash_text: str = "" - -k.change_language(settings["locale"]) - - -with open(settings["splash_text_loc"], "r", encoding="UTF-8") as f: - splash_text = "".join(f.readlines()) - print(splash_text) - +# Print splash text and check for updates +print(splashtext) # Print splash text (from modules/globalvars.py) start_checks() +import requests import discord from discord.ext import commands from discord import app_commands -from discord import Colour, Message - -from better_profanity import profanity +from discord import Colour, Embed, File, Interaction, Message +from discord.abc import Messageable from discord.ext import commands +from modules.volta.main import _, set_language from modules.markovmemory import * +from modules.version import * from modules.sentenceprocessing import * from modules.unhandledexception import handle_exception -from modules.image import gen_demotivator - +from modules.image import gen_meme, gen_demotivator +from modules.minigames import guessthenumber, hangman sys.excepthook = handle_exception - - -class MessageMetadata(TypedDict): - user_id: str - user_name: str - guild_id: str | Literal["DM"] - guild_name: str | Literal["DM"] - channel_id: str - channel_name: str - message: str - timestamp: float - +check_for_update() # Check for updates (from modules/version.py) +# Type aliases +T = TypeVar('T') +MessageContext = Union[commands.Context, discord.Interaction] +MessageReference = Union[Message, discord.WebhookMessage] # Constants with type hints -positive_gifs: List[str] = settings["bot"]["misc"]["positive_gifs"] +positive_gifs: List[str] = os.getenv("POSITIVE_GIFS", "").split(',') currenthash: str = "" launched: bool = False slash_commands_enabled: bool = False -# Set up Discord bot intents and create bot instance -intents: discord.Intents = discord.Intents.default() -intents.messages = True -intents.message_content = True - -bot: commands.Bot = commands.Bot( - command_prefix=settings["bot"]["prefix"], - intents=intents, - allowed_mentions=discord.AllowedMentions( - everyone=False, roles=False, users=False, replied_user=True - ), -) - # Load memory and Markov model for text generation -memory: List[str | Dict[Literal["_meta"], MessageMetadata]] = load_memory() +memory: List[str] = load_memory() markov_model: Optional[markovify.Text] = load_markov_model() if not markov_model: - logger.error(k.markov_model_not_found()) + logger.error(_('markov_model_not_found')) memory = load_memory() markov_model = train_markov_model(memory) generated_sentences: Set[str] = set() used_words: Set[str] = set() +async def load_cogs_from_folder(bot, folder_name="assets/cogs"): + for filename in os.listdir(folder_name): + if filename.endswith(".py") and not filename.startswith("_"): + cog_name = filename[:-3] + module_path = folder_name.replace("/", ".").replace("\\", ".") + f".{cog_name}" + try: + await bot.load_extension(module_path) + logger.info(f"{(_('loaded_cog'))} {cog_name}") + except Exception as e: + logger.error(f"{(_('cog_fail'))} {cog_name} {e}") + traceback.print_exc() -async def load_cogs_from_folder(bot: commands.Bot, folder_name="assets/cogs"): - for filename in [file for file in os.listdir(folder_name) if file.endswith(".py")]: - cog_name: str = filename[:-3] - - if ( - "internal" not in folder_name - and cog_name not in settings["bot"]["enabled_cogs"] - ): - logger.debug(f"Skipping cog {cog_name} (not in enabled cogs)") - continue - - module_path = folder_name.replace("/", ".").replace("\\", ".") + f".{cog_name}" - +async def send_alive_ping_periodically() -> None: + while True: try: - await bot.load_extension(module_path) - logger.info(f"{k.loaded_cog()} {cog_name}") + requests.post(f"{VERSION_URL}/aliveping", json={"name": NAME}) except Exception as e: - logger.error(f"{k.cog_fail()} {cog_name} {e}") - traceback.print_exc() - + logger.error(f"{(_('error_sending_alive_ping'))}{RESET} {e}") + await asyncio.sleep(60) # Event: Called when the bot is ready @bot.event async def on_ready() -> None: global launched - + global slash_commands_enabled + global NAME + global status + folder_name: str = "cogs" if launched: return - - await load_cogs_from_folder(bot, "assets/cogs/internal") + await load_cogs_from_folder(bot) try: synced: List[discord.app_commands.AppCommand] = await bot.tree.sync() - - logger.info(f"{k.synced_commands()} {len(synced)} {k.synced_commands2()}") - logger.info(k.started(settings["name"])) - + logger.info(f"{_('synced_commands')} {len(synced)} {(_('synced_commands2'))}") + slash_commands_enabled = True + logger.info(f"{(_('started')).format(name=NAME)}") + bot.loop.create_task(send_alive_ping_periodically()) except discord.errors.Forbidden as perm_error: - logger.error(f"Permission error while syncing commands: {perm_error}") - logger.error( - "Make sure the bot has the 'applications.commands' scope and is invited with the correct permissions." - ) + logger.error(f"Permission error while syncing commands: {perm_error}") + logger.error("Make sure the bot has the 'applications.commands' scope and is invited with the correct permissions.") quit() except Exception as e: - logger.error(f"{k.fail_commands_sync()} {e}") + logger.error(f"{_('fail_commands_sync')} {e}") traceback.print_exc() quit() - - if not settings["bot"]["misc"]["activity"]["content"]: + + if not song: return - - activity_type = discord.ActivityType.unknown - - settings_activity = settings["bot"]["misc"]["activity"]["type"] - - activities: Dict[ActivityType, discord.ActivityType] = { - "listening": discord.ActivityType.listening, - "playing": discord.ActivityType.playing, - "streaming": discord.ActivityType.streaming, - "competing": discord.ActivityType.competing, - "watching": discord.ActivityType.watching, - } - - await bot.change_presence( - activity=discord.Activity( - type=activities.get( - settings["bot"]["misc"]["activity"]["type"], - discord.ActivityType.unknown, - ), - name=settings["bot"]["misc"]["activity"]["content"], - ) - ) + + status = { + "idle": discord.Status.idle, + "dnd": discord.Status.dnd, + "invisible": discord.Status.invisible, + "online": discord.Status.online + }.get(status.lower(), discord.Status.online) + await bot.change_presence(status=status, activity=discord.Activity(type=discord.ActivityType.listening, name=f"{song}")) launched = True - - @bot.event async def on_command_error(ctx: commands.Context, error: commands.CommandError) -> None: from modules.unhandledexception import handle_exception - + if isinstance(error, commands.CommandInvokeError): original: Exception = error.original handle_exception( - type(original), - original, - original.__traceback__, - context=f"Command: {ctx.command} | User: {ctx.author}", + type(original), original, original.__traceback__, + context=f"Command: {ctx.command} | User: {ctx.author}" ) else: handle_exception( - type(error), - error, - error.__traceback__, - context=f"Command: {ctx.command} | User: {ctx.author}", + type(error), error, error.__traceback__, + context=f"Command: {ctx.command} | User: {ctx.author}" ) +# Command: Retrain the Markov model from memory +@bot.hybrid_command(description=f"{(_('command_desc_retrain'))}") +async def retrain(ctx: commands.Context) -> None: + if ctx.author.id != ownerid: + return -# New demotivator command -@bot.hybrid_command(description="Generate a demotivator poster with two lines of text") -async def demotivator(ctx: commands.Context) -> None: + message_ref: MessageReference = await send_message(ctx, f"{(_('command_markov_retrain'))}") + try: + with open(MEMORY_FILE, 'r') as f: + memory: List[str] = json.load(f) + except FileNotFoundError: + await send_message(ctx, f"{(_('command_markov_memory_not_found'))}") + return + except json.JSONDecodeError: + await send_message(ctx, f"{(_('command_markov_memory_is_corrupt'))}") + return + + data_size: int = len(memory) + processed_data: int = 0 + processing_message_ref: MessageReference = await send_message(ctx, f"{(_('command_markov_retraining')).format(processed_data=processed_data, data_size=data_size)}") + start_time: float = time.time() + + for i, data in enumerate(memory): + processed_data += 1 + + global markov_model + markov_model = train_markov_model(memory) + 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) + +# Command: Generate a sentence using the Markov model +@bot.hybrid_command(description=f"{(_('command_desc_talk'))}") +async def talk(ctx: commands.Context, sentence_size: int = 5) -> None: + if not markov_model: + await send_message(ctx, f"{(_('command_talk_insufficent_text'))}") + return + + response: Optional[str] = None + for _ in range(20): + if sentence_size == 1: + response = markov_model.make_short_sentence(max_chars=100, tries=100) + if response: + response = response.split()[0] + else: + response = markov_model.make_sentence(tries=100, max_words=sentence_size) + + if response and response not in generated_sentences: + if sentence_size > 1: + response = improve_sentence_coherence(response) + generated_sentences.add(response) + break + + if response: + cleaned_response: str = re.sub(r'[^\w\s]', '', response).lower() + coherent_response: str = rephrase_for_coherence(cleaned_response) + if random.random() < 0.9 and is_positive(coherent_response): + gif_url: str = random.choice(positive_gifs) + combined_message: str = f"{coherent_response}\n[jif]({gif_url})" + else: + combined_message: str = coherent_response + logger.info(combined_message) + os.environ['gooberlatestgen'] = combined_message + await send_message(ctx, combined_message) + else: + await send_message(ctx, f"{(_('command_talk_generation_fail'))}") + +@bot.hybrid_command(description=f"RAM") +async def ramusage(ctx): + process = psutil.Process(os.getpid()) + mem = process.memory_info().rss + await send_message(ctx, f"Total memory used: {mem / 1024 / 1024:.2f} MB") + +# Command: Generate an image +@bot.hybrid_command(description=f"{(_('command_desc_help'))}") +async def impact(ctx: commands.Context, text: Optional[str] = None) -> None: assets_folder: str = "assets/images" - temp_input: str | None = None + temp_input: Optional[str] = None def get_random_asset_image() -> Optional[str]: - files: List[str] = [ - f - for f in os.listdir(assets_folder) - if f.lower().endswith((".png", ".jpg", ".jpeg", ".webp")) - ] + files: List[str] = [f for f in os.listdir(assets_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))] if not files: return None return os.path.join(assets_folder, random.choice(files)) @@ -256,13 +240,12 @@ async def demotivator(ctx: commands.Context) -> None: if attachment.content_type and attachment.content_type.startswith("image/"): ext: str = os.path.splitext(attachment.filename)[1] temp_input = f"tempy{ext}" - with open(temp_input, "wb") as f: - await attachment.save(f) + await attachment.save(temp_input) input_path: str = temp_input else: fallback_image: Optional[str] = get_random_asset_image() if fallback_image is None: - await ctx.reply(k.no_image_available()) + await ctx.reply(_('no_image_available')) return temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1]) shutil.copy(fallback_image, temp_input) @@ -270,13 +253,63 @@ async def demotivator(ctx: commands.Context) -> None: else: fallback_image = get_random_asset_image() if fallback_image is None: - await ctx.reply(k.no_image_available()) + await ctx.reply(_('no_image_available')) return temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1]) shutil.copy(fallback_image, temp_input) input_path = temp_input - output_path: Optional[str] = await gen_demotivator(input_path) # type: ignore + output_path: Optional[str] = await gen_meme(input_path, custom_text=text) + + + if output_path is None or not os.path.isfile(output_path): + if temp_input and os.path.exists(temp_input): + os.remove(temp_input) + await ctx.reply(_('failed_generate_image')) + return + + await ctx.send(file=discord.File(output_path)) + + if temp_input and os.path.exists(temp_input): + os.remove(temp_input) + +# New demotivator command +@bot.hybrid_command(description="Generate a demotivator poster with two lines of text") +async def demotivator(ctx: commands.Context) -> None: + assets_folder: str = "assets/images" + temp_input: Optional[str] = None + + def get_random_asset_image() -> Optional[str]: + files: List[str] = [f for f in os.listdir(assets_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))] + if not files: + return None + return os.path.join(assets_folder, random.choice(files)) + + if ctx.message.attachments: + attachment: discord.Attachment = ctx.message.attachments[0] + if attachment.content_type and attachment.content_type.startswith("image/"): + ext: str = os.path.splitext(attachment.filename)[1] + temp_input = f"tempy{ext}" + await attachment.save(temp_input) + input_path: str = temp_input + else: + fallback_image: Optional[str] = get_random_asset_image() + if fallback_image is None: + await ctx.reply(_('no_image_available')) + return + temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1]) + shutil.copy(fallback_image, temp_input) + input_path = temp_input + else: + fallback_image = get_random_asset_image() + if fallback_image is None: + await ctx.reply(_('no_image_available')) + return + temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1]) + shutil.copy(fallback_image, temp_input) + input_path = temp_input + + output_path: Optional[str] = await gen_demotivator(input_path) if output_path is None or not os.path.isfile(output_path): if temp_input and os.path.exists(temp_input): @@ -289,50 +322,70 @@ async def demotivator(ctx: commands.Context) -> None: if temp_input and os.path.exists(temp_input): os.remove(temp_input) +bot.remove_command('help') +# Command: Show help information +@bot.hybrid_command(description=f"{(_('command_desc_help'))}") +async def help(ctx: commands.Context) -> None: + embed: discord.Embed = discord.Embed( + title=f"{(_('command_help_embed_title'))}", + description=f"{(_('command_help_embed_desc'))}", + color=Colour(0x000000) + ) + + command_categories: Dict[str, List[str]] = { + f"{(_('command_help_categories_general'))}": ["mem", "talk", "about", "ping", "impact", "demotivator", "help"], + f"{(_('command_help_categories_admin'))}": ["stats", "retrain", "setlanguage"] + } + + custom_commands: List[str] = [] + for cog_name, cog in bot.cogs.items(): + for command in cog.get_commands(): + if command.name not in command_categories[f"{(_('command_help_categories_general'))}"] and command.name not in command_categories[f"{(_('command_help_categories_admin'))}"]: + custom_commands.append(command.name) + + if custom_commands: + embed.add_field(name=f"{(_('command_help_categories_custom'))}", value="\n".join([f"{PREFIX}{command}" for command in custom_commands]), inline=False) + + for category, commands_list in command_categories.items(): + commands_in_category: str = "\n".join([f"{PREFIX}{command}" for command in commands_list]) + embed.add_field(name=category, value=commands_in_category, inline=False) + + await send_message(ctx, embed=embed) + +@bot.hybrid_command(description=f"{(_('command_desc_setlang'))}") +@app_commands.describe(locale="Choose your language") +async def setlanguage(ctx: commands.Context, locale: str) -> None: + if ctx.author.id != ownerid: + await ctx.send(":thumbsdown:") + return + await ctx.defer() + set_language(locale) + await ctx.send(":thumbsup:") # Event: Called on every message @bot.event async def on_message(message: discord.Message) -> None: global memory, markov_model - EMOJIS = [ - "\U0001f604", - "\U0001f44d", - "\U0001f525", - "\U0001f4af", - "\U0001f389", - "\U0001f60e", - ] # originally was emojis but it would probably shit itself on systems without unicode so.... + EMOJIS = ["\U0001F604", "\U0001F44D", "\U0001F525", "\U0001F4AF", "\U0001F389", "\U0001F60E"] # originally was emojis but it would probably shit itself on systems without unicode so.... if message.author.bot: return - if message.author.id in settings["bot"]["blacklisted_users"]: + if str(message.author.id) in BLACKLISTED_USERS: return - - commands = [ - settings["bot"]["prefix"] + command.name for command in bot.tree.get_commands() - ] - - if message.content.startswith(tuple(commands)): - logger.info(f"{k.command_ran(message.author.name, message.content)}") + + if message.content.startswith((f"{PREFIX}talk", f"{PREFIX}mem", f"{PREFIX}help", f"{PREFIX}stats", f"{PREFIX}")): + logger.info(f"{(_('command_ran')).format(message=message)}") await bot.process_commands(message) return - if ( - profanity.contains_profanity(message.content) - and settings["bot"]["misc"]["block_profanity"] - ): - return - if message.content: - if not settings["bot"]["user_training"]: + if not USERTRAIN_ENABLED: return - - formatted_message: str = append_mentions_to_18digit_integer(message.content) - cleaned_message: str = preprocess_message(formatted_message) + formatted_message: str = message.content + cleaned_message: str = formatted_message if cleaned_message: memory.append(cleaned_message) - - message_metadata: MessageMetadata = { + message_metadata = { "user_id": str(message.author.id), "user_name": str(message.author), "guild_id": str(message.guild.id) if message.guild else "DM", @@ -340,7 +393,7 @@ async def on_message(message: discord.Message) -> None: "channel_id": str(message.channel.id), "channel_name": str(message.channel), "message": message.content, - "timestamp": time.time(), + "timestamp": time.time() } try: if isinstance(memory, list): @@ -352,17 +405,10 @@ async def on_message(message: discord.Message) -> None: save_memory(memory) - sentiment_score = is_positive( - message.content - ) # doesnt work but im scared to change the logic now please ignore + sentiment_score = is_positive(message.content) # doesnt work but im scared to change the logic now please ignore if sentiment_score > 0.8: - if not settings["bot"]["react_to_messages"]: + if REACT != "True": return - if not sync_connector.can_react(message.id): - logger.info("Sync hub determined that this instance cannot react") - return - - emoji = random.choice(EMOJIS) try: await message.add_reaction(emoji) @@ -371,32 +417,106 @@ async def on_message(message: discord.Message) -> None: await bot.process_commands(message) - -# Event: Called on every interaction (slash command, etc.) @bot.event async def on_interaction(interaction: discord.Interaction) -> None: - logger.info(f"{k.command_ran_s(interaction.user.name)} {interaction.user.name}") - + name = None + if interaction.data.get('name') is None: + name = "Unknown" + else: + name = interaction.data['name'] + logger.info(f"{(_('command_ran_s')).format(interaction=interaction)}{name}") # Global check: Block blacklisted users from running commands @bot.check async def block_blacklisted(ctx: commands.Context) -> bool: - if ctx.author.id not in settings["bot"]["blacklisted_users"]: - return True - - try: - if isinstance(ctx, discord.Interaction): - if not ctx.response.is_done(): - await ctx.response.send_message(k.blacklisted(), ephemeral=True) + if str(ctx.author.id) in BLACKLISTED_USERS: + try: + if isinstance(ctx, discord.Interaction): + if not ctx.response.is_done(): + await ctx.response.send_message(_('blacklisted'), ephemeral=True) + else: + await ctx.followup.send(_('blacklisted'), ephemeral=True) else: - await ctx.followup.send(k.blacklisted(), ephemeral=True) - else: - await ctx.send(k.blacklisted_user(), ephemeral=True) - except: + await ctx.send(_('blacklisted_user'), ephemeral=True) + except: + pass return False - return True +# Command: Show bot latency +@bot.hybrid_command(description=f"{(_('command_desc_ping'))}") +async def ping(ctx: commands.Context) -> None: + await ctx.defer() + latency: int = round(bot.latency * 1000) + + LOLembed: discord.Embed = discord.Embed( + title="Pong!!", + description=( + f"{PING_LINE}\n" + f"`{(_('command_ping_embed_desc'))}: {latency}ms`\n" + ), + color=Colour(0x000000) + ) + LOLembed.set_footer(text=f"{(_('command_ping_footer'))} {ctx.author.name}", icon_url=ctx.author.avatar.url) + + await ctx.send(embed=LOLembed) + +def get_git_remote_url(): + try: + url = subprocess.check_output( + ["git", "config", "--get", "remote.origin.url"], + text=True, + stderr=subprocess.DEVNULL, + ).strip() + return url + except subprocess.CalledProcessError: + return "Unknown" + +# Command: Show about information +@bot.hybrid_command(description=f"{(_('command_about_desc'))}") +async def about(ctx: commands.Context) -> None: + print("-----------------------------------\n\n") + latest_version: str = check_for_update() + print("-----------------------------------") + embed: discord.Embed = discord.Embed(title=f"{(_('command_about_embed_title'))}", description="", color=Colour(0x000000)) + embed.add_field(name=f"{(_('command_about_embed_field1'))}", value=f"{NAME}", inline=False) + embed.add_field(name=f"{(_('command_about_embed_field2name'))}", value=f"{(_('command_about_embed_field2value')).format(local_version=local_version, latest_version=latest_version)}", inline=False) + embed.add_field(name=f"Git", value=get_git_remote_url()) + embed.add_field(name=f"OS", value=platform.platform()) + + await send_message(ctx, embed=embed) + +# Command: Show bot statistics (admin only) +@bot.hybrid_command(description="stats") +async def stats(ctx: commands.Context) -> None: + if ctx.author.id != ownerid: + return + print("-----------------------------------\n\n") + latest_version: str = check_for_update() + print("-----------------------------------") + memory_file: str = 'memory.json' + file_size: int = os.path.getsize(memory_file) + + with open(memory_file, 'r') as file: + line_count: int = sum(1 for _ in file) + + embed: discord.Embed = discord.Embed(title=f"{(_('command_stats_embed_title'))}", description=f"{(_('command_stats_embed_desc'))}", color=Colour(0x000000)) + embed.add_field(name=f"{(_('command_stats_embed_field1name'))}", value=f"{(_('command_stats_embed_field1value')).format(file_size=file_size, line_count=line_count)}", inline=False) + embed.add_field(name=f"{(_('command_stats_embed_field2name'))}", value=f"{(_('command_stats_embed_field2value')).format(local_version=local_version, latest_version=latest_version)}", inline=False) + embed.add_field(name=f"{(_('command_stats_embed_field3name'))}", value=f"{(_('command_stats_embed_field3value')).format(NAME=NAME, PREFIX=PREFIX, ownerid=ownerid, PING_LINE=PING_LINE, showmemenabled=showmemenabled, USERTRAIN_ENABLED=USERTRAIN_ENABLED, song=song, splashtext=splashtext)}", inline=False) + embed.add_field(name=f"OS", value=platform.platform()) + embed.add_field(name="Python Version", value=platform.python_version()) + await send_message(ctx, embed=embed) + +# Command: Upload memory.json to litterbox.catbox.moe and return the link +@bot.hybrid_command() +async def mem(ctx: commands.Context) -> None: + if showmemenabled != "true": + return + 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) + logger.debug(memorylitter) + await send_message(ctx, memorylitter.stdout.strip()) # Helper: Improve sentence coherence (simple capitalization fix) def improve_sentence_coherence(sentence: str) -> str: @@ -404,40 +524,5 @@ def improve_sentence_coherence(sentence: str) -> str: sentence = sentence.replace(" i ", " I ") return sentence - -class OnMyWatch: - watchDirectory = "assets/locales" - - def __init__(self): - self.observer = Observer() - - def run(self): - event_handler = Handler() - self.observer.schedule(event_handler, self.watchDirectory, recursive=True) - self.observer.start() - try: - while True: - time.sleep(5) - except: - self.observer.stop() - print("Observer Stopped") - - self.observer.join() - - -class Handler(FileSystemEventHandler): - def on_any_event(self, event): - if event.is_directory: - return None - - elif event.event_type == "modified": - build_keys() - - -observer = Observer() -observer.schedule(Handler(), "assets/locales") -observer.start() - # Start the bot -if __name__ == "__main__": - bot.run(os.environ.get("DISCORD_BOT_TOKEN", "")) +bot.run(TOKEN) \ No newline at end of file diff --git a/botminimal.py b/botminimal.py new file mode 100644 index 0000000..859662f --- /dev/null +++ b/botminimal.py @@ -0,0 +1,245 @@ +import discord +from discord.ext import commands, tasks +import json +import markovify +import nltk +from nltk.tokenize import word_tokenize +import random +import os +import time +import re +from dotenv import load_dotenv +load_dotenv() +# download NLTK data files +nltk.download('punkt') +MEMORY_FILE = "memory.json" +MEMORY_LOADED_FILE = "MEMORY_LOADED" + +def load_memory(): + data = [] + + # Try to load data from MEMORY_FILE + try: + with open(MEMORY_FILE, "r") as f: + data = json.load(f) + except FileNotFoundError: + pass + + return data + +# Save memory data to MEMORY_FILE +def save_memory(memory): + with open(MEMORY_FILE, "w") as f: + json.dump(memory, f, indent=4) + +def train_markov_model(memory, additional_data=None): + if not memory: + return None + filtered_memory = [line for line in memory if isinstance(line, str)] + if additional_data: + filtered_memory.extend(line for line in additional_data if isinstance(line, str)) + if not filtered_memory: + return None + text = "\n".join(filtered_memory) + model = markovify.NewlineText(text, state_size=2) + return model + +#this doesnt work and im extremely pissed and mad +def append_mentions_to_18digit_integer(message): + pattern = r'\b\d{18}\b' + return re.sub(pattern, lambda match: f"<@{match.group(0)}>", message) + +def preprocess_message(message): + message = append_mentions_to_18digit_integer(message) + tokens = word_tokenize(message) + tokens = [token for token in tokens if token.isalnum()] + return " ".join(tokens) + + +intents = discord.Intents.default() +intents.messages = True +intents.message_content = True +bot = commands.Bot(command_prefix="g!", intents=intents) +memory = load_memory() +markov_model = train_markov_model(memory) + +generated_sentences = set() +used_words = set() + +@bot.event +async def on_ready(): + print(f"Logged in as {bot.user}") + post_message.start() + +positive_keywords = ["happy", "good", "great", "amazing", "awesome", "joy", "love", "fantastic", "positive", "cheerful", "victory", "favorite", "lmao", "lol", "xd", "XD", "xD", "Xd"] + +positive_gifs = [ + "https://tenor.com/view/chill-guy-my-new-character-gif-2777893510283028272", + "https://tenor.com/view/goodnight-goodnight-friends-weezer-weezer-goodnight-gif-7322052181075806988" +] + +def is_positive(sentence): + sentence_lower = sentence.lower() + return any(keyword in sentence_lower for keyword in positive_keywords) + +@bot.command() +async def ask(ctx): + await ctx.send("Command undergoing fixes!") + #not really lol + +@bot.command() +async def talk(ctx): + if markov_model: + response = None + for _ in range(10): # im going to shit my pants 10 times to get a coherent sentence + response = markov_model.make_sentence(tries=100) + if response and response not in generated_sentences: + # preprocess shit for grammer + response = improve_sentence_coherence(response) + generated_sentences.add(response) + break + + if response: + async with ctx.typing(): + cleaned_response = re.sub(r'[^\w\s]', '', response) + cleaned_response = cleaned_response.lower() + coherent_response = rephrase_for_coherence(cleaned_response) + if random.random() < 0.9: + if is_positive(coherent_response): + gif_url = random.choice(positive_gifs) + combined_message = f"{coherent_response}\n[jif]({gif_url})" + await ctx.send(combined_message) + else: + await ctx.send(coherent_response) + else: + await ctx.send(coherent_response) + else: + await ctx.send("I have nothing to say right now!") + else: + await ctx.send("I need to learn more from messages before I can talk.") + +def improve_sentence_coherence(sentence): + + sentence = sentence.replace(" i ", " I ") + return sentence + +def rephrase_for_coherence(sentence): + + words = sentence.split() + + coherent_sentence = " ".join(words) + return coherent_sentence + +bot.help_command = None + + +@bot.command() +async def help(ctx, *args): + + if args: + command_name = args[0] + command = bot.get_command(command_name) + + if command: + embed = discord.Embed( + title=f"Help: g!{command_name}", + description=f"**Description:** {command.help}", + color=discord.Color.blue() + ) + await ctx.send(embed=embed) + else: + await ctx.send(f"Command `{command_name}` not found.") + else: + + embed = discord.Embed( + title="Bot Help", + description="List of commands grouped by category.", + color=discord.Color.blue() + ) + + command_categories = { + "General": ["show_memory", "talk", "ask", "ping"], + "Debug": ["word_usage"] + } + + for category, commands_list in command_categories.items(): + commands_in_category = "\n".join([f"g!{command}" for command in commands_list]) + embed.add_field(name=category, value=commands_in_category, inline=False) + + await ctx.send(embed=embed) + +@bot.event +async def on_message(message): + global memory, markov_model, last_random_talk_time + + if message.author.bot: + return + + + if message.content.startswith(("g!talk", "g!show_memory", "g!help", "g!")): + await bot.process_commands(message) + return + + if message.content: + formatted_message = append_mentions_to_18digit_integer(message.content) + cleaned_message = preprocess_message(formatted_message) + if cleaned_message: + memory.append(cleaned_message) + save_memory(memory) + markov_model = train_markov_model(memory) + + # process any commands in the message + await bot.process_commands(message) + +@bot.command() +async def ping(ctx): + await ctx.defer() + #stolen from my expect bot very proud + latency = round(bot.latency * 1000) + + LOLembed = discord.Embed( + title="Pong!!", + description=( + f"The Beretta fires fast and won't make you feel any better!\n" + f"`Bot Latency: {latency}ms`\n" + ), + color=discord.Color.blue() + ) + LOLembed.set_footer(text=f"Requested by {ctx.author.name}", icon_url=ctx.author.avatar.url) + + await ctx.send(embed=LOLembed) # use ctx.send instead of respond because it has nothing to respond to and its not a slash command + +@bot.command() +async def show_memory(ctx): + memory = load_memory() + memory_text = json.dumps(memory, indent=4) + if len(memory_text) > 1024: + with open(MEMORY_FILE, "r") as f: + await ctx.send(" ", file=discord.File(f, MEMORY_FILE)) + else: + embed = discord.Embed(title="Memory Contents", description="The bot's memory.", color=discord.Color.blue()) + embed.add_field(name="Memory Data", value=f"```json\n{memory_text}\n```", inline=False) + await ctx.send(embed=embed) + +def improve_sentence_coherence(sentence): + sentence = sentence.replace(" i ", " I ") + return sentence + +@tasks.loop(minutes=60) +async def post_message(): + channel_id = 1296141985253691433 + channel = bot.get_channel(channel_id) + if channel and markov_model: + response = None + for _ in range(10): + response = markov_model.make_sentence(tries=100) + if response and response not in generated_sentences: + generated_sentences.add(response) + break + + if response: + await channel.send(response) + +# run the bot +TOKEN = os.getenv("DISCORDBOTTOKEN", "0") +bot.run(TOKEN) diff --git a/example.env b/example.env new file mode 100644 index 0000000..45cfd9b --- /dev/null +++ b/example.env @@ -0,0 +1,29 @@ +DISCORDBOTTOKEN= +BOTPREFIX="g." +PINGLINE="The Beretta fires fast and won't make you feel any better!" +BLACKLISTEDUSERS= +OWNERID= +USERTRAINENABLED="true" +SHOWMEMENABLED="true" +LOCALE=fi +NAME=goober +AUTOUPDATE="True" +SONG="Basket Case - Green Day" +CHECKSDISABLED="Frue" +REACT="True" +STATUS="idle" +POSITIVEGIFS="https://media.discordapp.net/attachments/821047460151427135/1181371808566493184/jjpQGeno.gif, https://tenor.com/view/chill-guy-my-new-character-gif-2777893510283028272,https://tenor.com/view/goodnight-goodnight-friends-weezer-weezer-goodnight-gif-7322052181075806988" +SPLASHTEXT=" + + SS\ + SS | + SSSSSS\ SSSSSS\ SSSSSS\ SSSSSSS\ SSSSSS\ SSSSSS\ +SS __SS\ SS __SS\ SS __SS\ SS __SS\ SS __SS\ SS __SS\ +SS / SS |SS / SS |SS / SS |SS | SS |SSSSSSSS |SS | \__| +SS | SS |SS | SS |SS | SS |SS | SS |SS ____|SS | +\SSSSSSS |\SSSSSS |\SSSSSS |SSSSSSS |\SSSSSSS\ SS | + \____SS | \______/ \______/ \_______/ \_______|\__| +SS\ SS | +\SSSSSS | + \______/ +" \ No newline at end of file diff --git a/modules/globalvars.py b/modules/globalvars.py index 7a70f9c..ac37ed4 100644 --- a/modules/globalvars.py +++ b/modules/globalvars.py @@ -1,71 +1,72 @@ import os import platform -from typing import Callable, List from dotenv import load_dotenv import pathlib +import discord +from discord.ext import commands +from discord import app_commands +from discord import Colour, Embed, File, Interaction, Message +from discord.abc import Messageable +from discord.ext import commands import subprocess - - def get_git_branch(): try: - branch = ( - subprocess.check_output( - ["git", "rev-parse", "--abbrev-ref", "HEAD"], stderr=subprocess.DEVNULL - ) - .decode("utf-8") - .strip() - ) + branch = subprocess.check_output( + ["git", "rev-parse", "--abbrev-ref", "HEAD"], + stderr=subprocess.DEVNULL + ).decode('utf-8').strip() return branch except subprocess.CalledProcessError: return None - -env_path = pathlib.Path(__file__).parent.parent / ".env" +env_path = pathlib.Path(__file__).parent.parent / '.env' load_dotenv(dotenv_path=env_path) - -available_cogs: Callable[[], List[str]] = lambda: [ - file[:-3] for file in os.listdir("assets/cogs") if file.endswith(".py") -] - ANSI = "\033[" RED = f"{ANSI}31m" GREEN = f"{ANSI}32m" YELLOW = f"{ANSI}33m" PURPLE = f"{ANSI}35m" -DEBUG = f"{ANSI}90m" +DEBUG = f"{ANSI}1;30m" RESET = f"{ANSI}0m" - VERSION_URL = "https://raw.githubusercontent.com/gooberinc/version/main" -UPDATE_URL = VERSION_URL + "/latest_version.json" +UPDATE_URL = VERSION_URL+"/latest_version.json" print(UPDATE_URL) -LOCAL_VERSION_FILE = "current_version.txt" - -# TOKEN = os.getenv("DISCORDBOTTOKEN", "0") -# PREFIX = os.getenv("BOTPREFIX", "g.") -# PING_LINE = os.getenv("PINGLINE") -# CHECKS_DISABLED = os.getenv("CHECKSDISABLED") -# LOCALE = os.getenv("LOCALE", "en") -# BLACKLISTED_USERS = os.getenv("BLACKLISTEDUSERS", "").split(",") -# USERTRAIN_ENABLED = os.getenv("USERTRAINENABLED", "true").lower() == "true" -# NAME = os.getenv("NAME") -# MEMORY_FILE = "memory.json" -# MEMORY_LOADED_FILE = "MEMORY_LOADED" # is this still even used?? okay just checked its used in the markov module -# ALIVEPING = os.getenv("ALIVEPING") -# AUTOUPDATE = os.getenv("AUTOUPDATE") -# REACT = os.getenv("REACT") - -# gooberTOKEN = os.getenv("GOOBERTOKEN") -# splashtext = os.getenv("SPLASHTEXT") -# ownerid = int(os.getenv("OWNERID", "0")) -# showmemenabled = os.getenv("SHOWMEMENABLED") - - -# IGNOREWARNING = False # is this either??? i don't think so? -# song = os.getenv("song") +LOCAL_VERSION_FILE = "current_version.txt" +TOKEN = os.getenv("DISCORDBOTTOKEN", "0") +PREFIX = os.getenv("BOTPREFIX", "g.") +PING_LINE = os.getenv("PINGLINE") +CHECKS_DISABLED = os.getenv("CHECKSDISABLED") +LOCALE = os.getenv("LOCALE", "en") +gooberTOKEN = os.getenv("GOOBERTOKEN") +splashtext = os.getenv("SPLASHTEXT") +ownerid = int(os.getenv("OWNERID", "0")) +status = os.getenv("STATUS") +showmemenabled = os.getenv("SHOWMEMENABLED") +BLACKLISTED_USERS = os.getenv("BLACKLISTEDUSERS", "").split(",") +USERTRAIN_ENABLED = os.getenv("USERTRAINENABLED", "true").lower() == "true" +NAME = os.getenv("NAME") +MEMORY_FILE = "memory.json" +MEMORY_LOADED_FILE = "MEMORY_LOADED" # is this still even used?? okay just checked its used in the markov module +ALIVEPING = os.getenv("ALIVEPING") +AUTOUPDATE = os.getenv("AUTOUPDATE") +song = os.getenv("SONG") arch = platform.machine() -slash_commands_enabled = True # 100% broken, its a newer enough version so its probably enabled by default.... fix this at somepoint or hard code it in goober central code launched = False latest_version = "0.0.0" -local_version = "2.3.3" -os.environ["gooberlocal_version"] = local_version -beta = get_git_branch() == "dev" +local_version = "2.3.5" +os.environ['gooberlocal_version'] = local_version +REACT = os.getenv("REACT") +if get_git_branch() == "dev": + 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 +else: + beta = False + + +# Set up Discord bot intents and create bot instance +intents: discord.Intents = discord.Intents.default() +intents.messages = True +intents.presences = True +intents.members = True +intents.message_content = True +bot: commands.Bot = commands.Bot(command_prefix=PREFIX, intents=intents, allowed_mentions=discord.AllowedMentions(everyone=False, roles=False, users=False, replied_user=True)) \ No newline at end of file diff --git a/modules/image.py b/modules/image.py index e5a8e2a..d3807df 100644 --- a/modules/image.py +++ b/modules/image.py @@ -6,58 +6,38 @@ import tempfile from typing import Optional, List from PIL import Image, ImageDraw, ImageFont, ImageOps from modules.markovmemory import load_markov_model -from modules.sentenceprocessing import ( - improve_sentence_coherence, - rephrase_for_coherence, -) +from modules.sentenceprocessing import improve_sentence_coherence, rephrase_for_coherence generated_sentences = set() - def load_font(size): return ImageFont.truetype("assets/fonts/Impact.ttf", size=size) - def load_tnr(size): return ImageFont.truetype("assets/fonts/TNR.ttf", size=size) - def draw_text_with_outline(draw, text, x, y, font): - outline_offsets = [ - (-2, -2), - (-2, 2), - (2, -2), - (2, 2), - (0, -2), - (0, 2), - (-2, 0), - (2, 0), - ] + outline_offsets = [(-2, -2), (-2, 2), (2, -2), (2, 2), (0, -2), (0, 2), (-2, 0), (2, 0)] for ox, oy in outline_offsets: draw.text((x + ox, y + oy), text, font=font, fill="black") draw.text((x, y), text, font=font, fill="white") - def fits_in_width(text, font, max_width, draw): bbox = draw.textbbox((0, 0), text, font=font) text_width = bbox[2] - bbox[0] return text_width <= max_width - def split_text_to_fit(text, font, max_width, draw): words = text.split() for i in range(len(words), 0, -1): top_text = " ".join(words[:i]) bottom_text = " ".join(words[i:]) - if fits_in_width(top_text, font, max_width, draw) and fits_in_width( - bottom_text, font, max_width, draw - ): + if fits_in_width(top_text, font, max_width, draw) and fits_in_width(bottom_text, font, max_width, draw): return top_text, bottom_text midpoint = len(words) // 2 return " ".join(words[:midpoint]), " ".join(words[midpoint:]) - -async def gen_meme(input_image_path, sentence_size=5, max_attempts=10): +async def gen_meme(input_image_path, sentence_size=5, max_attempts=10, custom_text=None): markov_model = load_markov_model() if not markov_model or not os.path.isfile(input_image_path): return None @@ -72,29 +52,28 @@ async def gen_meme(input_image_path, sentence_size=5, max_attempts=10): font = load_font(font_size) response = None - for _ in range(20): - if sentence_size == 1: - candidate = markov_model.make_short_sentence( - max_chars=100, tries=100 - ) - if candidate: - candidate = candidate.split()[0] - else: - candidate = markov_model.make_sentence( - tries=100, max_words=sentence_size - ) + if custom_text: + response = custom_text + else: + for _ in range(20): + if sentence_size == 1: + candidate = markov_model.make_short_sentence(max_chars=100, tries=100) + if candidate: + candidate = candidate.split()[0] + else: + candidate = markov_model.make_sentence(tries=100, max_words=sentence_size) - if candidate and candidate not in generated_sentences: - if sentence_size > 1: - candidate = improve_sentence_coherence(candidate) - generated_sentences.add(candidate) - response = candidate + if candidate and candidate not in generated_sentences: + if sentence_size > 1: + candidate = improve_sentence_coherence(candidate) + generated_sentences.add(candidate) + response = candidate break if not response: response = "NO TEXT GENERATED" - cleaned_response = re.sub(r"[^\w\s]", "", response).lower() + cleaned_response = re.sub(r'[^\w\s]', '', response).lower() coherent_response = rephrase_for_coherence(cleaned_response).upper() bbox = draw.textbbox((0, 0), coherent_response, font=font) @@ -103,15 +82,11 @@ async def gen_meme(input_image_path, sentence_size=5, max_attempts=10): max_text_height = height // 4 if text_width <= width and text_height_px <= max_text_height: - draw_text_with_outline( - draw, coherent_response, (width - text_width) / 2, 0, font - ) + draw_text_with_outline(draw, coherent_response, (width - text_width) / 2, 0, font) img.save(input_image_path) return input_image_path else: - top_text, bottom_text = split_text_to_fit( - coherent_response, font, width, draw - ) + top_text, bottom_text = split_text_to_fit(coherent_response, font, width, draw) top_bbox = draw.textbbox((0, 0), top_text, font=font) bottom_bbox = draw.textbbox((0, 0), bottom_text, font=font) @@ -120,21 +95,9 @@ async def gen_meme(input_image_path, sentence_size=5, max_attempts=10): bottom_height = bottom_bbox[3] - bottom_bbox[1] if top_height <= max_text_height and bottom_height <= max_text_height: - draw_text_with_outline( - draw, - top_text, - (width - (top_bbox[2] - top_bbox[0])) / 2, - 0, - font, - ) + draw_text_with_outline(draw, top_text, (width - (top_bbox[2] - top_bbox[0])) / 2, 0, font) y_bottom = height - bottom_height - int(height * 0.04) - draw_text_with_outline( - draw, - bottom_text, - (width - (bottom_bbox[2] - bottom_bbox[0])) / 2, - y_bottom, - font, - ) + draw_text_with_outline(draw, bottom_text, (width - (bottom_bbox[2] - bottom_bbox[0])) / 2, y_bottom, font) img.save(input_image_path) return input_image_path @@ -153,7 +116,6 @@ async def gen_meme(input_image_path, sentence_size=5, max_attempts=10): img.save(input_image_path) return input_image_path - async def gen_demotivator(input_image_path, max_attempts=5): markov_model = load_markov_model() if not markov_model or not os.path.isfile(input_image_path): @@ -165,7 +127,7 @@ async def gen_demotivator(input_image_path, max_attempts=5): size = max(img.width, img.height) frame_thick = int(size * 0.0054) inner_size = size - 2 * frame_thick - resized_img = img.resize((inner_size, inner_size), Image.LANCZOS) + resized_img = img.resize((inner_size, inner_size), Image.LANCZOS) framed = Image.new("RGB", (size, size), "white") framed.paste(resized_img, (frame_thick, frame_thick)) landscape_w = int(size * 1.5) @@ -186,10 +148,8 @@ async def gen_demotivator(input_image_path, max_attempts=5): title = t.upper() subtitle = s.capitalize() break - if not title: - title = "DEMOTIVATOR" - if not subtitle: - subtitle = "no text generated" + if not title: title = "DEMOTIVATOR" + if not subtitle: subtitle = "no text generated" title_sz = int(caption_h * 0.4) sub_sz = int(caption_h * 0.25) diff --git a/modules/key_compiler.py b/modules/key_compiler.py deleted file mode 100644 index 7385dbd..0000000 --- a/modules/key_compiler.py +++ /dev/null @@ -1,220 +0,0 @@ -# The MIT License (MIT) - -# Copyright (c) 2025 ctih1 - -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in -# all copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -# THE SOFTWARE. - -import os -from typing import Dict, List, Literal -import json -import sys -import logging -import time - -NOTICE = """ -# This file was automatically created from localization JSON files. -# DO NOT EDIT THIS FILE DIRECTLY. If you want to edit a translation, please use the language's JSON file. - -#fmt: off -""" - - -logger = logging.getLogger("kaannos") - - -class LanguageCollector: - def __init__(self, language_dir: str) -> None: - self.path: str = language_dir - self.languages: Dict[str, Dict[str, str]] = {} - - for file in os.listdir(self.path): - if not file.endswith(".json") or len(file) > 7: - logger.debug(f"Skipping {file}") - continue - - locale: str = file.split(".json")[0] - logger.info(f"Discovered {file}") - with open(os.path.join(self.path, file), "r", encoding="UTF-8") as f: - keys: Dict[str, str] = json.load(f) - self.languages[locale] = keys - - self.find_missing_keys() - - def find_missing_keys(self) -> None: - primary_language_keys: Dict[str, str] = self.languages["en"] - - for key in primary_language_keys: - for language in self.languages: - if key not in self.languages[language]: - logger.warning(f"Key {key} missing from {language}") - - for language in self.languages: - for key in self.languages[language]: - if key not in primary_language_keys: - logger.warning(f"Leftover key {key} found from {language}") - - -class Script: - def __init__(self) -> None: - self.script: str = "" - - def add_line(self, content, indent: int = 0, newline: bool = True) -> None: - tabs = "\t" * indent - newline_content = "\n" if newline else "" - - self.script += f"{tabs}{content}{newline_content}" - - -def process_name(key: str) -> str: - return key.replace(" ", "_").replace(":", "").lower() - - -def find_args(string: str) -> List[str]: - variable_open: bool = False - temp_content: str = "" - - variables: List[str] = [] - for char in string: - if variable_open: - if char == "}": - variable_open = False - variables.append(temp_content) - temp_content = "" - continue - - if char == "{": - raise SyntaxError("Variable already open!") - - temp_content += char - - else: - if char == "}": - raise SyntaxError("Trying to close a nonexistant variable") - - if char == "{": - variable_open = True - - return variables - - -def convert_args( - inp: str, vars: List[str], mode: Literal["brackets", "none"] = "brackets" -) -> str: - replacements = {".": "_", ",": "_"} - - for var in vars: - cleaned_var = var - for key, val in replacements.items(): - cleaned_var = cleaned_var.replace(key, val) - - if mode == "none": - inp = inp.replace(f"{var}", f"{cleaned_var}") - else: - inp = inp.replace(f"{{{var}}}", f"{{{cleaned_var}}}") - - return inp - - -class GenerateScript: - def __init__( - self, - primary_lang: str, - language_data: Dict[str, Dict[str, str]], - use_typing: bool = True, - output_path: str = "out.py", - generate_comments: bool = True, - ): - self.data = language_data - self.primary = primary_lang - self.script = Script() - self.uses_typing: bool = use_typing - self.output = output_path - self.generate_comments = generate_comments - - def create(self): - # I really don't like this implementation but also it works - self.script.add_line(NOTICE) - if self.uses_typing: - self.script.add_line("from typing import Literal, List") - self.script.add_line(f"Language=Literal{list(self.data.keys())}") - self.script.add_line( - f"languages: List[Language] = {list(self.data.keys())}" - ) - self.script.add_line(f"default_lang: Language | str='{self.primary}'") - self.script.add_line( - "def change_language(new_lang: Language | str) -> None: global default_lang; default_lang = new_lang" - ) - else: - self.script.add_line(f"languages = {list(self.data.keys())}") - self.script.add_line(f"default_lang='{self.primary}'") - self.script.add_line( - "def change_language(new_lang): global default_lang; default_lang = new_lang" - ) - - self.primary_data = self.data[self.primary] - - for key in self.primary_data: - args = find_args(self.primary_data[key]) - - self.script.add_line( - f"def {process_name(key)}({convert_args(','.join([*args, 'lang:str|None=None' if self.uses_typing else 'lang']), args, 'none')}):" - ) - if self.generate_comments: - self.script.add_line('"""', 1) - self.script.add_line("### Locales", 1) - for language in self.data: - self.script.add_line( - f"- {language.capitalize()}: **{self.data[language].get(key, self.primary_data[key])}**", - 1, - ) - self.script.add_line('"""', 1) - self.script.add_line("if not lang: lang=default_lang", 1) - for language in self.data: - formatted_map = "{" - for arg in args: - formatted_map += f'"{convert_args(arg, args, "none")}": {convert_args(arg, args, "none")},' - formatted_map = formatted_map[:-1] + "}" - self.script.add_line( - f"""if lang == '{language}': return {convert_args(json.dumps( - self.data[language].get(key,self.primary_data[key]), - ensure_ascii=False - ), args)}{f'.format_map({formatted_map})' if len(args) > 0 else ''}""", - 1, - ) - - self.script.add_line( - "else: raise ValueError(f'Invalid language {lang}')", 1 - ) - with open(self.output, "w", encoding="UTF-8") as f: - f.write(self.script.script) - - -def build_result( - primary_lang: str, - locale_dir: str, - types: bool, - output_path: str, - generate_comments: bool = True, -): - start = time.time() - lc = LanguageCollector(locale_dir) - GenerateScript( - primary_lang, lc.languages, types, output_path, generate_comments - ).create() - logger.info(f"Done in {time.time() - start}s") diff --git a/modules/keys.py b/modules/keys.py deleted file mode 100644 index 3000f50..0000000 --- a/modules/keys.py +++ /dev/null @@ -1,2340 +0,0 @@ - -# This file was automatically created from localization JSON files. -# DO NOT EDIT THIS FILE DIRECTLY. If you want to edit a translation, please use the language's JSON file. - -#fmt: off - -from typing import Literal, List -Language=Literal['en', 'es', 'fi', 'fr', 'it'] -languages: List[Language] = ['en', 'es', 'fi', 'fr', 'it'] -default_lang: Language | str='en' -def change_language(new_lang: Language | str) -> None: global default_lang; default_lang = new_lang -def memory_file_valid(lang:str|None=None): - """ - ### Locales - - En: **The memory.json file is valid!** - - Es: **The memory.json file is valid!** - - Fi: **memory.json on toimiva!** - - Fr: **The memory.json file is valid!** - - It: **Il file JSON è valido!** - """ - if not lang: lang=default_lang - if lang == 'en': return "The memory.json file is valid!" - if lang == 'es': return "The memory.json file is valid!" - if lang == 'fi': return "memory.json on toimiva!" - if lang == 'fr': return "The memory.json file is valid!" - if lang == 'it': return "Il file JSON è valido!" - else: raise ValueError(f'Invalid language {lang}') -def file_aint_uft8(lang:str|None=None): - """ - ### Locales - - En: **File is not valid UTF-8 text. Might be binary or corrupted.** - - Es: **File is not valid UTF-8 text. Might be binary or corrupted.** - - Fi: **Tiedosto ei ole UTF-8 tekstiä. Saattaa olla binääriä tai korruptoitunut.** - - Fr: **File is not valid UTF-8 text. Might be binary or corrupted.** - - It: **File is not valid UTF-8 text. Might be binary or corrupted.** - """ - if not lang: lang=default_lang - if lang == 'en': return "File is not valid UTF-8 text. Might be binary or corrupted." - if lang == 'es': return "File is not valid UTF-8 text. Might be binary or corrupted." - if lang == 'fi': return "Tiedosto ei ole UTF-8 tekstiä. Saattaa olla binääriä tai korruptoitunut." - if lang == 'fr': return "File is not valid UTF-8 text. Might be binary or corrupted." - if lang == 'it': return "File is not valid UTF-8 text. Might be binary or corrupted." - else: raise ValueError(f'Invalid language {lang}') -def psutil_not_installed(lang:str|None=None): - """ - ### Locales - - En: **Memory check skipped.** - - Es: **Memory check skipped.** - - Fi: **Memory check skipped.** - - Fr: **Memory check skipped.** - - It: **Controllo memoria saltato.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Memory check skipped." - if lang == 'es': return "Memory check skipped." - if lang == 'fi': return "Memory check skipped." - if lang == 'fr': return "Memory check skipped." - if lang == 'it': return "Controllo memoria saltato." - else: raise ValueError(f'Invalid language {lang}') -def not_cloned(lang:str|None=None): - """ - ### Locales - - En: **Goober is not cloned! Please clone it from GitHub.** - - Es: **Goober is not cloned! Please clone it from GitHub.** - - Fi: **Goober is not cloned! Please clone it from GitHub.** - - Fr: **Goober is not cloned! Please clone it from GitHub.** - - It: **Goober non è stato clonato! Clonalo da GitHub.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Goober is not cloned! Please clone it from GitHub." - if lang == 'es': return "Goober is not cloned! Please clone it from GitHub." - if lang == 'fi': return "Goober is not cloned! Please clone it from GitHub." - if lang == 'fr': return "Goober is not cloned! Please clone it from GitHub." - if lang == 'it': return "Goober non è stato clonato! Clonalo da GitHub." - else: raise ValueError(f'Invalid language {lang}') -def checks_disabled(lang:str|None=None): - """ - ### Locales - - En: **Checks are disabled!** - - Es: **Checks are disabled!** - - Fi: **Tarkistukset on poistettu käytöstä!** - - Fr: **Les vérifications sont désactivées !** - - It: **I controlli sono disabilitati!** - """ - if not lang: lang=default_lang - if lang == 'en': return "Checks are disabled!" - if lang == 'es': return "Checks are disabled!" - if lang == 'fi': return "Tarkistukset on poistettu käytöstä!" - if lang == 'fr': return "Les vérifications sont désactivées !" - if lang == 'it': return "I controlli sono disabilitati!" - else: raise ValueError(f'Invalid language {lang}') -def unhandled_exception(lang:str|None=None): - """ - ### Locales - - En: **An unhandled exception occurred. Please report this issue on GitHub.** - - Es: **An unhandled exception occurred. Please report this issue on GitHub.** - - Fi: **Käsittelemätön virhe tapahtui. Ilmoita tästä GitHubissa.** - - Fr: **Une exception non gérée est survenue. Merci de rapporter ce problème sur GitHub.** - - It: **Si è verificata un'eccezione non gestita. Segnala questo problema su GitHub, per favore.** - """ - if not lang: lang=default_lang - if lang == 'en': return "An unhandled exception occurred. Please report this issue on GitHub." - if lang == 'es': return "An unhandled exception occurred. Please report this issue on GitHub." - if lang == 'fi': return "Käsittelemätön virhe tapahtui. Ilmoita tästä GitHubissa." - if lang == 'fr': return "Une exception non gérée est survenue. Merci de rapporter ce problème sur GitHub." - if lang == 'it': return "Si è verificata un'eccezione non gestita. Segnala questo problema su GitHub, per favore." - else: raise ValueError(f'Invalid language {lang}') -def active_users(lang:str|None=None): - """ - ### Locales - - En: **Active users:** - - Es: **Active users:** - - Fi: **Aktiiviset käyttäjät:** - - Fr: **Utilisateurs actifs :** - - It: **Utenti attivi:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Active users:" - if lang == 'es': return "Active users:" - if lang == 'fi': return "Aktiiviset käyttäjät:" - if lang == 'fr': return "Utilisateurs actifs :" - if lang == 'it': return "Utenti attivi:" - else: raise ValueError(f'Invalid language {lang}') -def spacy_initialized(lang:str|None=None): - """ - ### Locales - - En: **spaCy and spacytextblob are ready.** - - Es: **spaCy and spacytextblob are ready.** - - Fi: **spaCy ja spacytextblob ovat valmiita.** - - Fr: **spaCy et spacytextblob sont prêts.** - - It: **spaCy e spacytextblob sono pronti.** - """ - if not lang: lang=default_lang - if lang == 'en': return "spaCy and spacytextblob are ready." - if lang == 'es': return "spaCy and spacytextblob are ready." - if lang == 'fi': return "spaCy ja spacytextblob ovat valmiita." - if lang == 'fr': return "spaCy et spacytextblob sont prêts." - if lang == 'it': return "spaCy e spacytextblob sono pronti." - else: raise ValueError(f'Invalid language {lang}') -def spacy_model_not_found(lang:str|None=None): - """ - ### Locales - - En: **The spaCy model was not found! Downloading it....`** - - Es: **The spaCy model was not found! Downloading it....`** - - Fi: **spaCy mallia ei löytynyt! Ladataan se....`** - - Fr: **Le modèle spaCy est introuvable ! Téléchargement en cours...** - - It: **Il modello spaCy non è stato trovato! Lo sto scaricando...** - """ - if not lang: lang=default_lang - if lang == 'en': return "The spaCy model was not found! Downloading it....`" - if lang == 'es': return "The spaCy model was not found! Downloading it....`" - if lang == 'fi': return "spaCy mallia ei löytynyt! Ladataan se....`" - if lang == 'fr': return "Le modèle spaCy est introuvable ! Téléchargement en cours..." - if lang == 'it': return "Il modello spaCy non è stato trovato! Lo sto scaricando..." - else: raise ValueError(f'Invalid language {lang}') -def env_file_not_found(lang:str|None=None): - """ - ### Locales - - En: **The .env file was not found! Please create one with the required variables.** - - Es: **The .env file was not found! Please create one with the required variables.** - - Fi: **.env-tiedostoa ei löytnyt! Luo tiedosto jossa on tarvittavat muuttujat** - - Fr: **Le fichier .env est introuvable ! Créez-en un avec les variables nécessaires.** - - It: **Il file .env non è stato trovato! Crea un file con le variabili richieste.** - """ - if not lang: lang=default_lang - if lang == 'en': return "The .env file was not found! Please create one with the required variables." - if lang == 'es': return "The .env file was not found! Please create one with the required variables." - if lang == 'fi': return ".env-tiedostoa ei löytnyt! Luo tiedosto jossa on tarvittavat muuttujat" - if lang == 'fr': return "Le fichier .env est introuvable ! Créez-en un avec les variables nécessaires." - if lang == 'it': return "Il file .env non è stato trovato! Crea un file con le variabili richieste." - else: raise ValueError(f'Invalid language {lang}') -def error_fetching_active_users(error,lang:str|None=None): - """ - ### Locales - - En: **Error fetching active users: {error}** - - Es: **Error fetching active users: {error}** - - Fi: **Aktiivisten käyttäjien hankkimisessa tapahtui ongelma: {error}** - - Fr: **Erreur lors de la récupération des utilisateurs actifs : {error}** - - It: **Errore nel recupero degli utenti attivi: {error}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error fetching active users: {error}".format_map({"error": error}) - if lang == 'es': return "Error fetching active users: {error}".format_map({"error": error}) - if lang == 'fi': return "Aktiivisten käyttäjien hankkimisessa tapahtui ongelma: {error}".format_map({"error": error}) - if lang == 'fr': return "Erreur lors de la récupération des utilisateurs actifs : {error}".format_map({"error": error}) - if lang == 'it': return "Errore nel recupero degli utenti attivi: {error}".format_map({"error": error}) - else: raise ValueError(f'Invalid language {lang}') -def error_sending_alive_ping(error,lang:str|None=None): - """ - ### Locales - - En: **Error sending alive ping: {error}** - - Es: **Error sending alive ping: {error}** - - Fi: **Pingin lähettäminen goober centraliin epäonnistui: {error}** - - Fr: **Erreur lors de l’envoi du ping actif : {error}** - - It: **Errore nell'invio di aliveping:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error sending alive ping: {error}".format_map({"error": error}) - if lang == 'es': return "Error sending alive ping: {error}".format_map({"error": error}) - if lang == 'fi': return "Pingin lähettäminen goober centraliin epäonnistui: {error}".format_map({"error": error}) - if lang == 'fr': return "Erreur lors de l’envoi du ping actif : {error}".format_map({"error": error}) - if lang == 'it': return "Errore nell'invio di aliveping:".format_map({"error": error}) - else: raise ValueError(f'Invalid language {lang}') -def already_started(lang:str|None=None): - """ - ### Locales - - En: **I've already started! I'm not updating...** - - Es: **I've already started! I'm not updating...** - - Fi: **Olen jo käynnistynyt! Ei päivitetä...** - - Fr: **J’ai déjà démarré ! Je ne me mets pas à jour...** - - It: **Sono già avviato! Non aggiorno...** - """ - if not lang: lang=default_lang - if lang == 'en': return "I've already started! I'm not updating..." - if lang == 'es': return "I've already started! I'm not updating..." - if lang == 'fi': return "Olen jo käynnistynyt! Ei päivitetä..." - if lang == 'fr': return "J’ai déjà démarré ! Je ne me mets pas à jour..." - if lang == 'it': return "Sono già avviato! Non aggiorno..." - else: raise ValueError(f'Invalid language {lang}') -def please_restart(lang:str|None=None): - """ - ### Locales - - En: **Please Restart goober!** - - Es: **Please Restart goober!** - - Fi: **Käynnistä uudelleen, hölmö!** - - Fr: **Redémarre, stp !** - - It: **Riavvia goober!** - """ - if not lang: lang=default_lang - if lang == 'en': return "Please Restart goober!" - if lang == 'es': return "Please Restart goober!" - if lang == 'fi': return "Käynnistä uudelleen, hölmö!" - if lang == 'fr': return "Redémarre, stp !" - if lang == 'it': return "Riavvia goober!" - else: raise ValueError(f'Invalid language {lang}') -def local_ahead(remote,branch,lang:str|None=None): - """ - ### Locales - - En: **Local {remote}/{branch} is ahead and/or up to par. Not Updating...** - - Es: **Local {remote}/{branch} is ahead and/or up to par. Not Updating...** - - Fi: **Paikallinen {remote}/{branch} on edellä ja/tai ajan tasalla. Ohitetaan päivitys...** - - Fr: **Local {remote}/{branch} est en avance ou à jour. Pas de mise à jour...** - - It: **Il ramo locale {remote}/{branch} è aggiornato o avanti. Nessun aggiornamento...** - """ - if not lang: lang=default_lang - if lang == 'en': return "Local {remote}/{branch} is ahead and/or up to par. Not Updating...".format_map({"remote": remote,"branch": branch}) - if lang == 'es': return "Local {remote}/{branch} is ahead and/or up to par. Not Updating...".format_map({"remote": remote,"branch": branch}) - if lang == 'fi': return "Paikallinen {remote}/{branch} on edellä ja/tai ajan tasalla. Ohitetaan päivitys...".format_map({"remote": remote,"branch": branch}) - if lang == 'fr': return "Local {remote}/{branch} est en avance ou à jour. Pas de mise à jour...".format_map({"remote": remote,"branch": branch}) - if lang == 'it': return "Il ramo locale {remote}/{branch} è aggiornato o avanti. Nessun aggiornamento...".format_map({"remote": remote,"branch": branch}) - else: raise ValueError(f'Invalid language {lang}') -def remote_ahead(remote,branch,lang:str|None=None): - """ - ### Locales - - En: **Remote {remote}/{branch} is ahead. Updating...** - - Es: **Remote {remote}/{branch} is ahead. Updating...** - - Fi: **Etärepositorio {remote}/{branch} on edellä. Päivitetään...** - - Fr: **Remote {remote}/{branch} est en avance. Mise à jour en cours...** - - It: **Il ramo remoto {remote}/{branch} è avanti. Aggiornamento in corso...** - """ - if not lang: lang=default_lang - if lang == 'en': return "Remote {remote}/{branch} is ahead. Updating...".format_map({"remote": remote,"branch": branch}) - if lang == 'es': return "Remote {remote}/{branch} is ahead. Updating...".format_map({"remote": remote,"branch": branch}) - if lang == 'fi': return "Etärepositorio {remote}/{branch} on edellä. Päivitetään...".format_map({"remote": remote,"branch": branch}) - if lang == 'fr': return "Remote {remote}/{branch} est en avance. Mise à jour en cours...".format_map({"remote": remote,"branch": branch}) - if lang == 'it': return "Il ramo remoto {remote}/{branch} è avanti. Aggiornamento in corso...".format_map({"remote": remote,"branch": branch}) - else: raise ValueError(f'Invalid language {lang}') -def cant_find_local_version(lang:str|None=None): - """ - ### Locales - - En: **I can't find the local_version variable! Or it's been tampered with and it's not an integer!** - - Es: **I can't find the local_version variable! Or it's been tampered with and it's not an integer!** - - Fi: **Muuttujaa local_version ei löytynyt, tai sitä on muokattu eikä ole kokonaisluku!** - - Fr: **Je ne trouve pas la variable local_version ! Ou elle a été modifiée et ce n’est pas un entier !** - - It: **Impossibile trovare la variabile local_version! O è stata manomessa e non è un intero!** - """ - if not lang: lang=default_lang - if lang == 'en': return "I can't find the local_version variable! Or it's been tampered with and it's not an integer!" - if lang == 'es': return "I can't find the local_version variable! Or it's been tampered with and it's not an integer!" - if lang == 'fi': return "Muuttujaa local_version ei löytynyt, tai sitä on muokattu eikä ole kokonaisluku!" - if lang == 'fr': return "Je ne trouve pas la variable local_version ! Ou elle a été modifiée et ce n’est pas un entier !" - if lang == 'it': return "Impossibile trovare la variabile local_version! O è stata manomessa e non è un intero!" - else: raise ValueError(f'Invalid language {lang}') -def running_prestart_checks(lang:str|None=None): - """ - ### Locales - - En: **Running pre-start checks...** - - Es: **Running pre-start checks...** - - Fi: **Suoritetaan esikäynnistystarkistuksia...** - - Fr: **Exécution des vérifications préalables au démarrage...** - - It: **Esecuzione dei controlli pre-avvio...** - """ - if not lang: lang=default_lang - if lang == 'en': return "Running pre-start checks..." - if lang == 'es': return "Running pre-start checks..." - if lang == 'fi': return "Suoritetaan esikäynnistystarkistuksia..." - if lang == 'fr': return "Exécution des vérifications préalables au démarrage..." - if lang == 'it': return "Esecuzione dei controlli pre-avvio..." - else: raise ValueError(f'Invalid language {lang}') -def continuing_in_seconds(seconds,lang:str|None=None): - """ - ### Locales - - En: **Continuing in {seconds} seconds... Press any key to skip.** - - Es: **Continuing in {seconds} seconds... Press any key to skip.** - - Fi: **Jatketaan {seconds} sekunnin kuluttua... Paina mitä tahansa näppäintä ohittaaksesi.** - - Fr: **Reprise dans {seconds} secondes... Appuie sur une touche pour passer.** - - It: **Continuo tra {seconds} secondi... Premi un tasto per saltare.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Continuing in {seconds} seconds... Press any key to skip.".format_map({"seconds": seconds}) - if lang == 'es': return "Continuing in {seconds} seconds... Press any key to skip.".format_map({"seconds": seconds}) - if lang == 'fi': return "Jatketaan {seconds} sekunnin kuluttua... Paina mitä tahansa näppäintä ohittaaksesi.".format_map({"seconds": seconds}) - if lang == 'fr': return "Reprise dans {seconds} secondes... Appuie sur une touche pour passer.".format_map({"seconds": seconds}) - if lang == 'it': return "Continuo tra {seconds} secondi... Premi un tasto per saltare.".format_map({"seconds": seconds}) - else: raise ValueError(f'Invalid language {lang}') -def missing_requests_psutil(lang:str|None=None): - """ - ### Locales - - En: **Missing requests and psutil! Please install them using pip: `pip install requests psutil`** - - Es: **Missing requests and psutil! Please install them using pip: `pip install requests psutil`** - - Fi: **Kirjastot requests ja psutil puuttuvat! Asenna ne komennolla: `pip install requests psutil`** - - Fr: **requests et psutil manquants ! Installe-les avec pip : `pip install requests psutil`** - - It: **Mancano requests e psutil! Installali con pip: `pip install requests psutil`** - """ - if not lang: lang=default_lang - if lang == 'en': return "Missing requests and psutil! Please install them using pip: `pip install requests psutil`" - if lang == 'es': return "Missing requests and psutil! Please install them using pip: `pip install requests psutil`" - if lang == 'fi': return "Kirjastot requests ja psutil puuttuvat! Asenna ne komennolla: `pip install requests psutil`" - if lang == 'fr': return "requests et psutil manquants ! Installe-les avec pip : `pip install requests psutil`" - if lang == 'it': return "Mancano requests e psutil! Installali con pip: `pip install requests psutil`" - else: raise ValueError(f'Invalid language {lang}') -def requirements_not_found(path,lang:str|None=None): - """ - ### Locales - - En: **requirements.txt not found at {path} was it tampered with?** - - Es: **requirements.txt not found at {path} was it tampered with?** - - Fi: **Tiedostoa requirements.txt ei löytynyt polusta {path} – onko sitä muokattu?** - - Fr: **requirements.txt introuvable à {path}, a-t-il été modifié ?** - - It: **requirements.txt non trovato in {path} è stato manomesso?** - """ - if not lang: lang=default_lang - if lang == 'en': return "requirements.txt not found at {path} was it tampered with?".format_map({"path": path}) - if lang == 'es': return "requirements.txt not found at {path} was it tampered with?".format_map({"path": path}) - if lang == 'fi': return "Tiedostoa requirements.txt ei löytynyt polusta {path} – onko sitä muokattu?".format_map({"path": path}) - if lang == 'fr': return "requirements.txt introuvable à {path}, a-t-il été modifié ?".format_map({"path": path}) - if lang == 'it': return "requirements.txt non trovato in {path} è stato manomesso?".format_map({"path": path}) - else: raise ValueError(f'Invalid language {lang}') -def warning_failed_parse_imports(filename,error,lang:str|None=None): - """ - ### Locales - - En: **Warning: Failed to parse imports from {filename}: {error}** - - Es: **Warning: Failed to parse imports from {filename}: {error}** - - Fi: **Varoitus: tuontien jäsentäminen epäonnistui tiedostossa {filename}: {error}** - - Fr: **Avertissement : Échec du parsing des imports depuis {filename} : {error}** - - It: **Attenzione: impossibile analizzare le importazioni da {filename}: {error}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Warning: Failed to parse imports from {filename}: {error}".format_map({"filename": filename,"error": error}) - if lang == 'es': return "Warning: Failed to parse imports from {filename}: {error}".format_map({"filename": filename,"error": error}) - if lang == 'fi': return "Varoitus: tuontien jäsentäminen epäonnistui tiedostossa {filename}: {error}".format_map({"filename": filename,"error": error}) - if lang == 'fr': return "Avertissement : Échec du parsing des imports depuis {filename} : {error}".format_map({"filename": filename,"error": error}) - if lang == 'it': return "Attenzione: impossibile analizzare le importazioni da {filename}: {error}".format_map({"filename": filename,"error": error}) - else: raise ValueError(f'Invalid language {lang}') -def cogs_dir_not_found(path,lang:str|None=None): - """ - ### Locales - - En: **Cogs directory not found at {path}, skipping scan.** - - Es: **Cogs directory not found at {path}, skipping scan.** - - Fi: **Cogs-kansiota ei löytynyt polusta {path}, ohitetaan tarkistus.** - - Fr: **Répertoire des cogs introuvable à {path}, scan ignoré.** - - It: **Cartella cogs non trovata in {path}, scansione saltata.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Cogs directory not found at {path}, skipping scan.".format_map({"path": path}) - if lang == 'es': return "Cogs directory not found at {path}, skipping scan.".format_map({"path": path}) - if lang == 'fi': return "Cogs-kansiota ei löytynyt polusta {path}, ohitetaan tarkistus.".format_map({"path": path}) - if lang == 'fr': return "Répertoire des cogs introuvable à {path}, scan ignoré.".format_map({"path": path}) - if lang == 'it': return "Cartella cogs non trovata in {path}, scansione saltata.".format_map({"path": path}) - else: raise ValueError(f'Invalid language {lang}') -def std_lib_local_skipped(package,lang:str|None=None): - """ - ### Locales - - En: **STD LIB / LOCAL {package} (skipped check)** - - Es: **STD LIB / LOCAL {package} (skipped check)** - - Fi: **STD LIB / PAIKALLINEN {package} (tarkistus ohitettu)** - - Fr: **LIB STD / LOCAL {package} (vérification sautée)** - - It: **LIB STD / LOCALE {package} (controllo saltato)** - """ - if not lang: lang=default_lang - if lang == 'en': return "STD LIB / LOCAL {package} (skipped check)".format_map({"package": package}) - if lang == 'es': return "STD LIB / LOCAL {package} (skipped check)".format_map({"package": package}) - if lang == 'fi': return "STD LIB / PAIKALLINEN {package} (tarkistus ohitettu)".format_map({"package": package}) - if lang == 'fr': return "LIB STD / LOCAL {package} (vérification sautée)".format_map({"package": package}) - if lang == 'it': return "LIB STD / LOCALE {package} (controllo saltato)".format_map({"package": package}) - else: raise ValueError(f'Invalid language {lang}') -def ok_installed(lang:str|None=None): - """ - ### Locales - - En: **OK** - - Es: **OK** - - Fi: **OK** - - Fr: **OK** - - It: **OK** - """ - if not lang: lang=default_lang - if lang == 'en': return "OK" - if lang == 'es': return "OK" - if lang == 'fi': return "OK" - if lang == 'fr': return "OK" - if lang == 'it': return "OK" - else: raise ValueError(f'Invalid language {lang}') -def missing_package(lang:str|None=None): - """ - ### Locales - - En: **MISSING** - - Es: **MISSING** - - Fi: **PUUTTUU** - - Fr: **MANQUANT** - - It: **REQUISITO MANCANTE** - """ - if not lang: lang=default_lang - if lang == 'en': return "MISSING" - if lang == 'es': return "MISSING" - if lang == 'fi': return "PUUTTUU" - if lang == 'fr': return "MANQUANT" - if lang == 'it': return "REQUISITO MANCANTE" - else: raise ValueError(f'Invalid language {lang}') -def missing_package2(lang:str|None=None): - """ - ### Locales - - En: **is not installed** - - Es: **is not installed** - - Fi: **ei ole asennettu** - - Fr: **n’est pas installé** - - It: **non è installato** - """ - if not lang: lang=default_lang - if lang == 'en': return "is not installed" - if lang == 'es': return "is not installed" - if lang == 'fi': return "ei ole asennettu" - if lang == 'fr': return "n’est pas installé" - if lang == 'it': return "non è installato" - else: raise ValueError(f'Invalid language {lang}') -def missing_packages_detected(lang:str|None=None): - """ - ### Locales - - En: **Missing packages detected:** - - Es: **Missing packages detected:** - - Fi: **Puuttuvia kirjastoja havaittu:** - - Fr: **Packages manquants détectés :** - - It: **Pacchetti mancanti rilevati:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Missing packages detected:" - if lang == 'es': return "Missing packages detected:" - if lang == 'fi': return "Puuttuvia kirjastoja havaittu:" - if lang == 'fr': return "Packages manquants détectés :" - if lang == 'it': return "Pacchetti mancanti rilevati:" - else: raise ValueError(f'Invalid language {lang}') -def telling_goober_central(url,lang:str|None=None): - """ - ### Locales - - En: **Telling goober central at {url}** - - Es: **Telling goober central at {url}** - - Fi: **Ilmoitetaan goober-centralille osoitteessa {url}** - - Fr: **Envoi à goober central à {url}** - - It: **Segnalazione a goober central su {url}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Telling goober central at {url}".format_map({"url": url}) - if lang == 'es': return "Telling goober central at {url}".format_map({"url": url}) - if lang == 'fi': return "Ilmoitetaan goober-centralille osoitteessa {url}".format_map({"url": url}) - if lang == 'fr': return "Envoi à goober central à {url}".format_map({"url": url}) - if lang == 'it': return "Segnalazione a goober central su {url}".format_map({"url": url}) - else: raise ValueError(f'Invalid language {lang}') -def failed_to_contact(url,error,lang:str|None=None): - """ - ### Locales - - En: **Failed to contact {url}: {error}** - - Es: **Failed to contact {url}: {error}** - - Fi: **Yhteyden muodostus epäonnistui osoitteeseen {url}: {error}** - - Fr: **Impossible de contacter {url} : {error}** - - It: **Impossibile contattare {url}: {error}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Failed to contact {url}: {error}".format_map({"url": url,"error": error}) - if lang == 'es': return "Failed to contact {url}: {error}".format_map({"url": url,"error": error}) - if lang == 'fi': return "Yhteyden muodostus epäonnistui osoitteeseen {url}: {error}".format_map({"url": url,"error": error}) - if lang == 'fr': return "Impossible de contacter {url} : {error}".format_map({"url": url,"error": error}) - if lang == 'it': return "Impossibile contattare {url}: {error}".format_map({"url": url,"error": error}) - else: raise ValueError(f'Invalid language {lang}') -def all_requirements_satisfied(lang:str|None=None): - """ - ### Locales - - En: **All requirements are satisfied.** - - Es: **All requirements are satisfied.** - - Fi: **Kaikki vaatimukset täyttyvät.** - - Fr: **Toutes les dépendances sont satisfaites.** - - It: **Tutti i requisiti sono soddisfatti.** - """ - if not lang: lang=default_lang - if lang == 'en': return "All requirements are satisfied." - if lang == 'es': return "All requirements are satisfied." - if lang == 'fi': return "Kaikki vaatimukset täyttyvät." - if lang == 'fr': return "Toutes les dépendances sont satisfaites." - if lang == 'it': return "Tutti i requisiti sono soddisfatti." - else: raise ValueError(f'Invalid language {lang}') -def ping_to(host,latency,lang:str|None=None): - """ - ### Locales - - En: **Ping to {host}: {latency} ms** - - Es: **Ping to {host}: {latency} ms** - - Fi: **Ping osoitteeseen {host}: {latency} ms** - - Fr: **Ping vers {host} : {latency} ms** - - It: **Ping a {host}: {latency} ms** - """ - if not lang: lang=default_lang - if lang == 'en': return "Ping to {host}: {latency} ms".format_map({"host": host,"latency": latency}) - if lang == 'es': return "Ping to {host}: {latency} ms".format_map({"host": host,"latency": latency}) - if lang == 'fi': return "Ping osoitteeseen {host}: {latency} ms".format_map({"host": host,"latency": latency}) - if lang == 'fr': return "Ping vers {host} : {latency} ms".format_map({"host": host,"latency": latency}) - if lang == 'it': return "Ping a {host}: {latency} ms".format_map({"host": host,"latency": latency}) - else: raise ValueError(f'Invalid language {lang}') -def high_latency(lang:str|None=None): - """ - ### Locales - - En: **High latency detected! You may experience delays in response times.** - - Es: **High latency detected! You may experience delays in response times.** - - Fi: **Korkea viive havaittu! Vastaukset saattavat hidastua.** - - Fr: **Latence élevée détectée ! Tu pourrais avoir des délais de réponse.** - - It: **Latenza elevata rilevata! Potresti riscontrare ritardi nelle risposte.** - """ - if not lang: lang=default_lang - if lang == 'en': return "High latency detected! You may experience delays in response times." - if lang == 'es': return "High latency detected! You may experience delays in response times." - if lang == 'fi': return "Korkea viive havaittu! Vastaukset saattavat hidastua." - if lang == 'fr': return "Latence élevée détectée ! Tu pourrais avoir des délais de réponse." - if lang == 'it': return "Latenza elevata rilevata! Potresti riscontrare ritardi nelle risposte." - else: raise ValueError(f'Invalid language {lang}') -def could_not_parse_latency(lang:str|None=None): - """ - ### Locales - - En: **Could not parse latency.** - - Es: **Could not parse latency.** - - Fi: **Viivettä ei voitu tulkita.** - - Fr: **Impossible d’analyser la latence.** - - It: **Impossibile analizzare la latenza.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Could not parse latency." - if lang == 'es': return "Could not parse latency." - if lang == 'fi': return "Viivettä ei voitu tulkita." - if lang == 'fr': return "Impossible d’analyser la latence." - if lang == 'it': return "Impossibile analizzare la latenza." - else: raise ValueError(f'Invalid language {lang}') -def ping_failed(host,lang:str|None=None): - """ - ### Locales - - En: **Ping to {host} failed.** - - Es: **Ping to {host} failed.** - - Fi: **Ping osoitteeseen {host} epäonnistui.** - - Fr: **Ping vers {host} échoué.** - - It: **Ping a {host} fallito.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Ping to {host} failed.".format_map({"host": host}) - if lang == 'es': return "Ping to {host} failed.".format_map({"host": host}) - if lang == 'fi': return "Ping osoitteeseen {host} epäonnistui.".format_map({"host": host}) - if lang == 'fr': return "Ping vers {host} échoué.".format_map({"host": host}) - if lang == 'it': return "Ping a {host} fallito.".format_map({"host": host}) - else: raise ValueError(f'Invalid language {lang}') -def error_running_ping(error,lang:str|None=None): - """ - ### Locales - - En: **Error running ping: {error}** - - Es: **Error running ping: {error}** - - Fi: **Virhe ping-komennon suorittamisessa: {error}** - - Fr: **Erreur lors du ping : {error}** - - It: **Errore durante l'esecuzione del ping: {error}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error running ping: {error}".format_map({"error": error}) - if lang == 'es': return "Error running ping: {error}".format_map({"error": error}) - if lang == 'fi': return "Virhe ping-komennon suorittamisessa: {error}".format_map({"error": error}) - if lang == 'fr': return "Erreur lors du ping : {error}".format_map({"error": error}) - if lang == 'it': return "Errore durante l'esecuzione del ping: {error}".format_map({"error": error}) - else: raise ValueError(f'Invalid language {lang}') -def memory_usage(used,total,percent,lang:str|None=None): - """ - ### Locales - - En: **Memory Usage: {used} GB / {total} GB ({percent}%)** - - Es: **Memory Usage: {used} GB / {total} GB ({percent}%)** - - Fi: **Muistin käyttö: {used} Gt / {total} Gt ({percent}%)** - - Fr: **Utilisation mémoire : {used} Go / {total} Go ({percent}%)** - - It: **Utilizzo memoria: {used} GB / {total} GB ({percent}%)** - """ - if not lang: lang=default_lang - if lang == 'en': return "Memory Usage: {used} GB / {total} GB ({percent}%)".format_map({"used": used,"total": total,"percent": percent}) - if lang == 'es': return "Memory Usage: {used} GB / {total} GB ({percent}%)".format_map({"used": used,"total": total,"percent": percent}) - if lang == 'fi': return "Muistin käyttö: {used} Gt / {total} Gt ({percent}%)".format_map({"used": used,"total": total,"percent": percent}) - if lang == 'fr': return "Utilisation mémoire : {used} Go / {total} Go ({percent}%)".format_map({"used": used,"total": total,"percent": percent}) - if lang == 'it': return "Utilizzo memoria: {used} GB / {total} GB ({percent}%)".format_map({"used": used,"total": total,"percent": percent}) - else: raise ValueError(f'Invalid language {lang}') -def memory_above_90(percent,lang:str|None=None): - """ - ### Locales - - En: **Memory usage is above 90% ({percent}%). Consider freeing up memory.** - - Es: **Memory usage is above 90% ({percent}%). Consider freeing up memory.** - - Fi: **Muistin käyttö ylittää 90 % ({percent}%). Harkitse muistin vapauttamista.** - - Fr: **Usage mémoire au-dessus de 90% ({percent}%). Pense à libérer de la mémoire.** - - It: **Utilizzo memoria sopra il 90% ({percent}%). Considera di liberare memoria.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Memory usage is above 90% ({percent}%). Consider freeing up memory.".format_map({"percent": percent}) - if lang == 'es': return "Memory usage is above 90% ({percent}%). Consider freeing up memory.".format_map({"percent": percent}) - if lang == 'fi': return "Muistin käyttö ylittää 90 % ({percent}%). Harkitse muistin vapauttamista.".format_map({"percent": percent}) - if lang == 'fr': return "Usage mémoire au-dessus de 90% ({percent}%). Pense à libérer de la mémoire.".format_map({"percent": percent}) - if lang == 'it': return "Utilizzo memoria sopra il 90% ({percent}%). Considera di liberare memoria.".format_map({"percent": percent}) - else: raise ValueError(f'Invalid language {lang}') -def total_memory(total,lang:str|None=None): - """ - ### Locales - - En: **Total Memory: {total} GB** - - Es: **Total Memory: {total} GB** - - Fi: **Kokonaismuisti: {total} Gt** - - Fr: **Mémoire totale : {total} Go** - - It: **Memoria totale: {total} GB** - """ - if not lang: lang=default_lang - if lang == 'en': return "Total Memory: {total} GB".format_map({"total": total}) - if lang == 'es': return "Total Memory: {total} GB".format_map({"total": total}) - if lang == 'fi': return "Kokonaismuisti: {total} Gt".format_map({"total": total}) - if lang == 'fr': return "Mémoire totale : {total} Go".format_map({"total": total}) - if lang == 'it': return "Memoria totale: {total} GB".format_map({"total": total}) - else: raise ValueError(f'Invalid language {lang}') -def used_memory(used,lang:str|None=None): - """ - ### Locales - - En: **Used Memory: {used} GB** - - Es: **Used Memory: {used} GB** - - Fi: **Käytetty muisti: {used} Gt** - - Fr: **Mémoire utilisée : {used} Go** - - It: **Memoria usata: {used} GB** - """ - if not lang: lang=default_lang - if lang == 'en': return "Used Memory: {used} GB".format_map({"used": used}) - if lang == 'es': return "Used Memory: {used} GB".format_map({"used": used}) - if lang == 'fi': return "Käytetty muisti: {used} Gt".format_map({"used": used}) - if lang == 'fr': return "Mémoire utilisée : {used} Go".format_map({"used": used}) - if lang == 'it': return "Memoria usata: {used} GB".format_map({"used": used}) - else: raise ValueError(f'Invalid language {lang}') -def low_free_memory(free,lang:str|None=None): - """ - ### Locales - - En: **Low free memory detected! Only {free} GB available.** - - Es: **Low free memory detected! Only {free} GB available.** - - Fi: **Vapaa muisti vähissä! Vain {free} Gt jäljellä.** - - Fr: **Mémoire libre faible détectée ! Seulement {free} Go disponibles.** - - It: **Poca memoria libera! Solo {free} GB disponibili.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Low free memory detected! Only {free} GB available.".format_map({"free": free}) - if lang == 'es': return "Low free memory detected! Only {free} GB available.".format_map({"free": free}) - if lang == 'fi': return "Vapaa muisti vähissä! Vain {free} Gt jäljellä.".format_map({"free": free}) - if lang == 'fr': return "Mémoire libre faible détectée ! Seulement {free} Go disponibles.".format_map({"free": free}) - if lang == 'it': return "Poca memoria libera! Solo {free} GB disponibili.".format_map({"free": free}) - else: raise ValueError(f'Invalid language {lang}') -def measuring_cpu(lang:str|None=None): - """ - ### Locales - - En: **Measuring CPU usage per core...** - - Es: **Measuring CPU usage per core...** - - Fi: **Mitataan suorittimen käyttöä ytimittäin...** - - Fr: **Mesure de l’usage CPU par cœur...** - - It: **Misurazione utilizzo CPU per core...** - """ - if not lang: lang=default_lang - if lang == 'en': return "Measuring CPU usage per core..." - if lang == 'es': return "Measuring CPU usage per core..." - if lang == 'fi': return "Mitataan suorittimen käyttöä ytimittäin..." - if lang == 'fr': return "Mesure de l’usage CPU par cœur..." - if lang == 'it': return "Misurazione utilizzo CPU per core..." - else: raise ValueError(f'Invalid language {lang}') -def core_usage(idx,bar,usage,lang:str|None=None): - """ - ### Locales - - En: **Core {idx}: [{bar}] {usage}%** - - Es: **Core {idx}: [{bar}] {usage}%** - - Fi: **Ydin {idx}: [{bar}] {usage}%** - - Fr: **Cœur {idx} : [{bar}] {usage}%** - - It: **Core {idx}: [{bar}] {usage}%** - """ - if not lang: lang=default_lang - if lang == 'en': return "Core {idx}: [{bar}] {usage}%".format_map({"idx": idx,"bar": bar,"usage": usage}) - if lang == 'es': return "Core {idx}: [{bar}] {usage}%".format_map({"idx": idx,"bar": bar,"usage": usage}) - if lang == 'fi': return "Ydin {idx}: [{bar}] {usage}%".format_map({"idx": idx,"bar": bar,"usage": usage}) - if lang == 'fr': return "Cœur {idx} : [{bar}] {usage}%".format_map({"idx": idx,"bar": bar,"usage": usage}) - if lang == 'it': return "Core {idx}: [{bar}] {usage}%".format_map({"idx": idx,"bar": bar,"usage": usage}) - else: raise ValueError(f'Invalid language {lang}') -def total_cpu_usage(usage,lang:str|None=None): - """ - ### Locales - - En: **Total CPU Usage: {usage}%** - - Es: **Total CPU Usage: {usage}%** - - Fi: **Kokonaisprosessorin käyttö: {usage}%** - - Fr: **Usage total CPU : {usage}%** - - It: **Utilizzo totale CPU: {usage}%** - """ - if not lang: lang=default_lang - if lang == 'en': return "Total CPU Usage: {usage}%".format_map({"usage": usage}) - if lang == 'es': return "Total CPU Usage: {usage}%".format_map({"usage": usage}) - if lang == 'fi': return "Kokonaisprosessorin käyttö: {usage}%".format_map({"usage": usage}) - if lang == 'fr': return "Usage total CPU : {usage}%".format_map({"usage": usage}) - if lang == 'it': return "Utilizzo totale CPU: {usage}%".format_map({"usage": usage}) - else: raise ValueError(f'Invalid language {lang}') -def high_avg_cpu(usage,lang:str|None=None): - """ - ### Locales - - En: **High average CPU usage: {usage}%** - - Es: **High average CPU usage: {usage}%** - - Fi: **Korkea keskimääräinen prosessorin käyttö: {usage}%** - - Fr: **Moyenne CPU élevée : {usage}%** - - It: **Utilizzo medio CPU elevato: {usage}%** - """ - if not lang: lang=default_lang - if lang == 'en': return "High average CPU usage: {usage}%".format_map({"usage": usage}) - if lang == 'es': return "High average CPU usage: {usage}%".format_map({"usage": usage}) - if lang == 'fi': return "Korkea keskimääräinen prosessorin käyttö: {usage}%".format_map({"usage": usage}) - if lang == 'fr': return "Moyenne CPU élevée : {usage}%".format_map({"usage": usage}) - if lang == 'it': return "Utilizzo medio CPU elevato: {usage}%".format_map({"usage": usage}) - else: raise ValueError(f'Invalid language {lang}') -def really_high_cpu(lang:str|None=None): - """ - ### Locales - - En: **Really high CPU load! System may throttle or hang.** - - Es: **Really high CPU load! System may throttle or hang.** - - Fi: **Erittäin korkea prosessorikuorma! Järjestelmä saattaa hidastua tai jumittua.** - - Fr: **Charge CPU vraiment élevée ! Le système pourrait ralentir ou planter.** - - It: **Carico CPU molto alto! Il sistema potrebbe rallentare o bloccarsi.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Really high CPU load! System may throttle or hang." - if lang == 'es': return "Really high CPU load! System may throttle or hang." - if lang == 'fi': return "Erittäin korkea prosessorikuorma! Järjestelmä saattaa hidastua tai jumittua." - if lang == 'fr': return "Charge CPU vraiment élevée ! Le système pourrait ralentir ou planter." - if lang == 'it': return "Carico CPU molto alto! Il sistema potrebbe rallentare o bloccarsi." - else: raise ValueError(f'Invalid language {lang}') -def memory_file(size,lang:str|None=None): - """ - ### Locales - - En: **Memory file: {size} MB** - - Es: **Memory file: {size} MB** - - Fi: **Muistitiedosto: {size} Mt** - - Fr: **Fichier mémoire : {size} Mo** - - It: **File memoria: {size} MB** - """ - if not lang: lang=default_lang - if lang == 'en': return "Memory file: {size} MB".format_map({"size": size}) - if lang == 'es': return "Memory file: {size} MB".format_map({"size": size}) - if lang == 'fi': return "Muistitiedosto: {size} Mt".format_map({"size": size}) - if lang == 'fr': return "Fichier mémoire : {size} Mo".format_map({"size": size}) - if lang == 'it': return "File memoria: {size} MB".format_map({"size": size}) - else: raise ValueError(f'Invalid language {lang}') -def memory_file_large(lang:str|None=None): - """ - ### Locales - - En: **Memory file is 1GB or higher, consider clearing it to free up space.** - - Es: **Memory file is 1GB or higher, consider clearing it to free up space.** - - Fi: **Muistitiedosto on enemmän kuin 1 Gt – harkitse sen tyhjentämistä tilan vapauttamiseksi.** - - Fr: **Fichier mémoire de 1 Go ou plus, pense à le nettoyer pour libérer de l’espace.** - - It: **Il file di memoria è 1GB o più, valuta di svuotarlo.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Memory file is 1GB or higher, consider clearing it to free up space." - if lang == 'es': return "Memory file is 1GB or higher, consider clearing it to free up space." - if lang == 'fi': return "Muistitiedosto on enemmän kuin 1 Gt – harkitse sen tyhjentämistä tilan vapauttamiseksi." - if lang == 'fr': return "Fichier mémoire de 1 Go ou plus, pense à le nettoyer pour libérer de l’espace." - if lang == 'it': return "Il file di memoria è 1GB o più, valuta di svuotarlo." - else: raise ValueError(f'Invalid language {lang}') -def memory_file_corrupted(error,lang:str|None=None): - """ - ### Locales - - En: **Memory file is corrupted! JSON decode error: {error}** - - Es: **Memory file is corrupted! JSON decode error: {error}** - - Fi: **Muistitiedosto on vioittunut! JSON purkuvirhe: {error}** - - Fr: **Fichier mémoire corrompu ! Erreur JSON : {error}** - - It: **File memoria corrotto! Errore JSON decode: {error}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Memory file is corrupted! JSON decode error: {error}".format_map({"error": error}) - if lang == 'es': return "Memory file is corrupted! JSON decode error: {error}".format_map({"error": error}) - if lang == 'fi': return "Muistitiedosto on vioittunut! JSON purkuvirhe: {error}".format_map({"error": error}) - if lang == 'fr': return "Fichier mémoire corrompu ! Erreur JSON : {error}".format_map({"error": error}) - if lang == 'it': return "File memoria corrotto! Errore JSON decode: {error}".format_map({"error": error}) - else: raise ValueError(f'Invalid language {lang}') -def consider_backup_memory(lang:str|None=None): - """ - ### Locales - - En: **Consider backing up and recreating the memory file.** - - Es: **Consider backing up and recreating the memory file.** - - Fi: **Harkitse muistitiedoston varmuuskopioimista ja uudelleenluontia.** - - Fr: **Pense à sauvegarder et recréer le fichier mémoire.** - - It: **Valuta di fare un backup e ricreare il file di memoria.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Consider backing up and recreating the memory file." - if lang == 'es': return "Consider backing up and recreating the memory file." - if lang == 'fi': return "Harkitse muistitiedoston varmuuskopioimista ja uudelleenluontia." - if lang == 'fr': return "Pense à sauvegarder et recréer le fichier mémoire." - if lang == 'it': return "Valuta di fare un backup e ricreare il file di memoria." - else: raise ValueError(f'Invalid language {lang}') -def memory_file_encoding(error,lang:str|None=None): - """ - ### Locales - - En: **Memory file has encoding issues: {error}** - - Es: **Memory file has encoding issues: {error}** - - Fi: **Muistitiedostossa on koodausongelmia: {error}** - - Fr: **Problèmes d’encodage du fichier mémoire : {error}** - - It: **Problemi di codifica nel file memoria: {error}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Memory file has encoding issues: {error}".format_map({"error": error}) - if lang == 'es': return "Memory file has encoding issues: {error}".format_map({"error": error}) - if lang == 'fi': return "Muistitiedostossa on koodausongelmia: {error}".format_map({"error": error}) - if lang == 'fr': return "Problèmes d’encodage du fichier mémoire : {error}".format_map({"error": error}) - if lang == 'it': return "Problemi di codifica nel file memoria: {error}".format_map({"error": error}) - else: raise ValueError(f'Invalid language {lang}') -def error_reading_memory(error,lang:str|None=None): - """ - ### Locales - - En: **Error reading memory file: {error}** - - Es: **Error reading memory file: {error}** - - Fi: **Virhe muistitiedoston lukemisessa: {error}** - - Fr: **Erreur lecture fichier mémoire : {error}** - - It: **Errore nella lettura del file memoria: {error}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error reading memory file: {error}".format_map({"error": error}) - if lang == 'es': return "Error reading memory file: {error}".format_map({"error": error}) - if lang == 'fi': return "Virhe muistitiedoston lukemisessa: {error}".format_map({"error": error}) - if lang == 'fr': return "Erreur lecture fichier mémoire : {error}".format_map({"error": error}) - if lang == 'it': return "Errore nella lettura del file memoria: {error}".format_map({"error": error}) - else: raise ValueError(f'Invalid language {lang}') -def memory_file_not_found(lang:str|None=None): - """ - ### Locales - - En: **Memory file not found.** - - Es: **Memory file not found.** - - Fi: **Muistitiedostoa ei löytynyt.** - - Fr: **Fichier mémoire introuvable.** - - It: **File memoria non trovato.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Memory file not found." - if lang == 'es': return "Memory file not found." - if lang == 'fi': return "Muistitiedostoa ei löytynyt." - if lang == 'fr': return "Fichier mémoire introuvable." - if lang == 'it': return "File memoria non trovato." - else: raise ValueError(f'Invalid language {lang}') -def modification_warning(lang:str|None=None): - """ - ### Locales - - En: **Goober has been modified! Any changes will be lost in an update!** - - Es: **Goober ha sido modificado! Se omiten las comprobaciones del servidor por completo...** - - Fi: **Gooberia on muokattu! Ohitetaan palvelimen tarkistus kokonaan...** - - Fr: **Goober a été modifié ! Toutes les modifications seront perdues lors d'une mise à jour !** - - It: **Goober è stato modificato! Verifiche del server saltate completamente...** - """ - if not lang: lang=default_lang - if lang == 'en': return "Goober has been modified! Any changes will be lost in an update!" - if lang == 'es': return "Goober ha sido modificado! Se omiten las comprobaciones del servidor por completo..." - if lang == 'fi': return "Gooberia on muokattu! Ohitetaan palvelimen tarkistus kokonaan..." - if lang == 'fr': return "Goober a été modifié ! Toutes les modifications seront perdues lors d'une mise à jour !" - if lang == 'it': return "Goober è stato modificato! Verifiche del server saltate completamente..." - else: raise ValueError(f'Invalid language {lang}') -def reported_version(lang:str|None=None): - """ - ### Locales - - En: **Reported Version:** - - Es: **Version reportada:** - - Fi: **Ilmoitettu versio:** - - Fr: **Version rapportée :** - - It: **Versione segnalata:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Reported Version:" - if lang == 'es': return "Version reportada:" - if lang == 'fi': return "Ilmoitettu versio:" - if lang == 'fr': return "Version rapportée :" - if lang == 'it': return "Versione segnalata:" - else: raise ValueError(f'Invalid language {lang}') -def current_hash(lang:str|None=None): - """ - ### Locales - - En: **Current Hash:** - - Es: **Hash actual:** - - Fi: **Tämänhetkinen hash:** - - Fr: **Hachage actuel :** - - It: **Hash attuale:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Current Hash:" - if lang == 'es': return "Hash actual:" - if lang == 'fi': return "Tämänhetkinen hash:" - if lang == 'fr': return "Hachage actuel :" - if lang == 'it': return "Hash attuale:" - else: raise ValueError(f'Invalid language {lang}') -def not_found(lang:str|None=None): - """ - ### Locales - - En: **is not found!** - - Es: **no existe!** - - Fi: **ei löytynyt!** - - Fr: **n'est pas trouvé !** - - It: **non trovato!** - """ - if not lang: lang=default_lang - if lang == 'en': return "is not found!" - if lang == 'es': return "no existe!" - if lang == 'fi': return "ei löytynyt!" - if lang == 'fr': return "n'est pas trouvé !" - if lang == 'it': return "non trovato!" - else: raise ValueError(f'Invalid language {lang}') -def version_error(lang:str|None=None): - """ - ### Locales - - En: **Unable to fetch version info. Status code** - - Es: **No se puede obtener la informacion de la version. Codigo de estado** - - Fi: **Versiotietojen saanti epäonnistui.. Tilakoodi:** - - Fr: **Impossible de récupérer les informations de version. Code d'état** - - It: **Impossibile recuperare le informazioni sulla versione. Codice di stato** - """ - if not lang: lang=default_lang - if lang == 'en': return "Unable to fetch version info. Status code" - if lang == 'es': return "No se puede obtener la informacion de la version. Codigo de estado" - if lang == 'fi': return "Versiotietojen saanti epäonnistui.. Tilakoodi:" - if lang == 'fr': return "Impossible de récupérer les informations de version. Code d'état" - if lang == 'it': return "Impossibile recuperare le informazioni sulla versione. Codice di stato" - else: raise ValueError(f'Invalid language {lang}') -def loaded_cog(lang:str|None=None): - """ - ### Locales - - En: **Loaded cog:** - - Es: **Engranaje cog:** - - Fi: **Ladatut cogit:** - - Fr: **Cog chargé :** - - It: **Cog caricato:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Loaded cog:" - if lang == 'es': return "Engranaje cog:" - if lang == 'fi': return "Ladatut cogit:" - if lang == 'fr': return "Cog chargé :" - if lang == 'it': return "Cog caricato:" - else: raise ValueError(f'Invalid language {lang}') -def loaded_cog2(lang:str|None=None): - """ - ### Locales - - En: **Loaded module:** - - Es: **Loaded module:** - - Fi: **Ladattiin moduuli:** - - Fr: **Module chargé :** - - It: **Module caricato:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Loaded module:" - if lang == 'es': return "Loaded module:" - if lang == 'fi': return "Ladattiin moduuli:" - if lang == 'fr': return "Module chargé :" - if lang == 'it': return "Module caricato:" - else: raise ValueError(f'Invalid language {lang}') -def cog_fail(lang:str|None=None): - """ - ### Locales - - En: **Failed to load cog:** - - Es: **No se pudo cargar el cog:** - - Fi: **Cogin lataus epäonnistui kohteelle:** - - Fr: **Échec du chargement du cog :** - - It: **Impossibile caricare il cog:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Failed to load cog:" - if lang == 'es': return "No se pudo cargar el cog:" - if lang == 'fi': return "Cogin lataus epäonnistui kohteelle:" - if lang == 'fr': return "Échec du chargement du cog :" - if lang == 'it': return "Impossibile caricare il cog:" - else: raise ValueError(f'Invalid language {lang}') -def cog_fail2(lang:str|None=None): - """ - ### Locales - - En: **Failed to load module:** - - Es: **Failed to load module:** - - Fi: **Moduulin lataaminen epäonnistui:** - - Fr: **Échec du chargement du module :** - - It: **Impossibile caricare il module:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Failed to load module:" - if lang == 'es': return "Failed to load module:" - if lang == 'fi': return "Moduulin lataaminen epäonnistui:" - if lang == 'fr': return "Échec du chargement du module :" - if lang == 'it': return "Impossibile caricare il module:" - else: raise ValueError(f'Invalid language {lang}') -def no_model(lang:str|None=None): - """ - ### Locales - - En: **No saved Markov model found. Starting from scratch.** - - Es: **No se encontro ningún modelo de Markov guardado. Empezando desde cero.** - - Fi: **Olemassaolevaa markov-mallia ei löydetty. Aloitetaan alusta.** - - Fr: **Aucun modèle Markov sauvegardé trouvé. Démarrage à partir de zéro.** - - It: **Nessun modello Markov salvato trovato. Iniziamo da zero.** - """ - if not lang: lang=default_lang - if lang == 'en': return "No saved Markov model found. Starting from scratch." - if lang == 'es': return "No se encontro ningún modelo de Markov guardado. Empezando desde cero." - if lang == 'fi': return "Olemassaolevaa markov-mallia ei löydetty. Aloitetaan alusta." - if lang == 'fr': return "Aucun modèle Markov sauvegardé trouvé. Démarrage à partir de zéro." - if lang == 'it': return "Nessun modello Markov salvato trovato. Iniziamo da zero." - else: raise ValueError(f'Invalid language {lang}') -def folder_created(folder_name,lang:str|None=None): - """ - ### Locales - - En: **Folder '{folder_name}' created.** - - Es: **Directorio '{folder_name}' creado.** - - Fi: **Kansio '{folder_name}' luotu.** - - Fr: **Dossier '{folder_name}' créé.** - - It: **Cartella '{folder_name}' creata.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Folder '{folder_name}' created.".format_map({"folder_name": folder_name}) - if lang == 'es': return "Directorio '{folder_name}' creado.".format_map({"folder_name": folder_name}) - if lang == 'fi': return "Kansio '{folder_name}' luotu.".format_map({"folder_name": folder_name}) - if lang == 'fr': return "Dossier '{folder_name}' créé.".format_map({"folder_name": folder_name}) - if lang == 'it': return "Cartella '{folder_name}' creata.".format_map({"folder_name": folder_name}) - else: raise ValueError(f'Invalid language {lang}') -def folder_exists(folder_name,lang:str|None=None): - """ - ### Locales - - En: **Folder '{folder_name}' already exists. skipping...** - - Es: **El directorio '{folder_name}' ya existe. Se omite...** - - Fi: **Kansio '{folder_name}' on jo olemassa...** - - Fr: **Le dossier '{folder_name}' existe déjà. Ignorons...** - - It: **La cartella '{folder_name}' esiste già. Saltando...** - """ - if not lang: lang=default_lang - if lang == 'en': return "Folder '{folder_name}' already exists. skipping...".format_map({"folder_name": folder_name}) - if lang == 'es': return "El directorio '{folder_name}' ya existe. Se omite...".format_map({"folder_name": folder_name}) - if lang == 'fi': return "Kansio '{folder_name}' on jo olemassa...".format_map({"folder_name": folder_name}) - if lang == 'fr': return "Le dossier '{folder_name}' existe déjà. Ignorons...".format_map({"folder_name": folder_name}) - if lang == 'it': return "La cartella '{folder_name}' esiste già. Saltando...".format_map({"folder_name": folder_name}) - else: raise ValueError(f'Invalid language {lang}') -def logged_in(lang:str|None=None): - """ - ### Locales - - En: **Logged in as** - - Es: **Inicio sesion como** - - Fi: **Kirjauduttiin sisään käyttäjänä** - - Fr: **Connecté en tant que** - - It: **Accesso effettuato come** - """ - if not lang: lang=default_lang - if lang == 'en': return "Logged in as" - if lang == 'es': return "Inicio sesion como" - if lang == 'fi': return "Kirjauduttiin sisään käyttäjänä" - if lang == 'fr': return "Connecté en tant que" - if lang == 'it': return "Accesso effettuato come" - else: raise ValueError(f'Invalid language {lang}') -def synced_commands(lang:str|None=None): - """ - ### Locales - - En: **Synced** - - Es: **Sincronizado** - - Fi: **Synkronoitiin** - - Fr: **Synchronisé** - - It: **Sincronizzati** - """ - if not lang: lang=default_lang - if lang == 'en': return "Synced" - if lang == 'es': return "Sincronizado" - if lang == 'fi': return "Synkronoitiin" - if lang == 'fr': return "Synchronisé" - if lang == 'it': return "Sincronizzati" - else: raise ValueError(f'Invalid language {lang}') -def synced_commands2(lang:str|None=None): - """ - ### Locales - - En: **commands!** - - Es: **comandos!** - - Fi: **komennot!** - - Fr: **commandes !** - - It: **comandi!** - """ - if not lang: lang=default_lang - if lang == 'en': return "commands!" - if lang == 'es': return "comandos!" - if lang == 'fi': return "komennot!" - if lang == 'fr': return "commandes !" - if lang == 'it': return "comandi!" - else: raise ValueError(f'Invalid language {lang}') -def fail_commands_sync(lang:str|None=None): - """ - ### Locales - - En: **Failed to sync commands:** - - Es: **Error al sincronizar comandos:** - - Fi: **Komentojen synkronointi epäonnistui:** - - Fr: **Échec de la synchronisation des commandes :** - - It: **Impossibile sincronizzare i comandi:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Failed to sync commands:" - if lang == 'es': return "Error al sincronizar comandos:" - if lang == 'fi': return "Komentojen synkronointi epäonnistui:" - if lang == 'fr': return "Échec de la synchronisation des commandes :" - if lang == 'it': return "Impossibile sincronizzare i comandi:" - else: raise ValueError(f'Invalid language {lang}') -def started(name,lang:str|None=None): - """ - ### Locales - - En: **{name} has started! -You're the star of the show now baby!** - - Es: **{name} ha empezado!** - - Fi: **{name} on käynnistynyt! -Olet nyt sarjan tähti, beibi!** - - Fr: **{name} a démarré !** - - It: **{name} è stato avviato! -Il palco è tuo!** - """ - if not lang: lang=default_lang - if lang == 'en': return "{name} has started!\nYou're the star of the show now baby!".format_map({"name": name}) - if lang == 'es': return "{name} ha empezado!".format_map({"name": name}) - if lang == 'fi': return "{name} on käynnistynyt!\nOlet nyt sarjan tähti, beibi!".format_map({"name": name}) - if lang == 'fr': return "{name} a démarré !".format_map({"name": name}) - if lang == 'it': return "{name} è stato avviato!\nIl palco è tuo!".format_map({"name": name}) - else: raise ValueError(f'Invalid language {lang}') -def name_check(lang:str|None=None): - """ - ### Locales - - En: **Error checking name availability:** - - Es: **Error al comprobar la disponibilidad del nombre:** - - Fi: **Nimen saatavuuden tarkistus epäonnistui:** - - Fr: **Erreur lors de la vérification de la disponibilité du nom :** - - It: **Errore nel controllo disponibilità del nome:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error checking name availability:" - if lang == 'es': return "Error al comprobar la disponibilidad del nombre:" - if lang == 'fi': return "Nimen saatavuuden tarkistus epäonnistui:" - if lang == 'fr': return "Erreur lors de la vérification de la disponibilité du nom :" - if lang == 'it': return "Errore nel controllo disponibilità del nome:" - else: raise ValueError(f'Invalid language {lang}') -def name_taken(lang:str|None=None): - """ - ### Locales - - En: **Name is already taken. Please choose a different name.** - - Es: **El nombre ya está en uso. Elija otro.** - - Fi: **Nimi on jo käytössä. Valitse toinen nimi.** - - Fr: **Le nom est déjà pris. Veuillez choisir un autre nom.** - - It: **Il nome è già preso. Scegli un nome diverso.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Name is already taken. Please choose a different name." - if lang == 'es': return "El nombre ya está en uso. Elija otro." - if lang == 'fi': return "Nimi on jo käytössä. Valitse toinen nimi." - if lang == 'fr': return "Le nom est déjà pris. Veuillez choisir un autre nom." - if lang == 'it': return "Il nome è già preso. Scegli un nome diverso." - else: raise ValueError(f'Invalid language {lang}') -def name_check2(lang:str|None=None): - """ - ### Locales - - En: **Error during name availability check:** - - Es: **Error durante la comprobacion de disponibilidad del nombre:** - - Fi: **Virhe tapahtui nimen saatavuuden tarkistamisessa:** - - Fr: **Erreur lors de la vérification de la disponibilité du nom :** - - It: **Errore durante il controllo della disponibilità del nome:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error during name availability check:" - if lang == 'es': return "Error durante la comprobacion de disponibilidad del nombre:" - if lang == 'fi': return "Virhe tapahtui nimen saatavuuden tarkistamisessa:" - if lang == 'fr': return "Erreur lors de la vérification de la disponibilité du nom :" - if lang == 'it': return "Errore durante il controllo della disponibilità del nome:" - else: raise ValueError(f'Invalid language {lang}') -def add_token(token,lang:str|None=None): - """ - ### Locales - - En: **Token: {token} -Please add this token to your .env file as** - - Es: **Token: {token} -Agregue este token a su archivo .env como** - - Fi: **Token: {token} -Lisää tämä .env-tiedostoosi nimellä** - - Fr: **Token : {token} -Veuillez ajouter ce token à votre fichier .env comme** - - It: **Token: {token} -Aggiungi questo token al tuo file .env come** - """ - if not lang: lang=default_lang - if lang == 'en': return "Token: {token}\nPlease add this token to your .env file as".format_map({"token": token}) - if lang == 'es': return "Token: {token}\nAgregue este token a su archivo .env como".format_map({"token": token}) - if lang == 'fi': return "Token: {token}\nLisää tämä .env-tiedostoosi nimellä".format_map({"token": token}) - if lang == 'fr': return "Token : {token}\nVeuillez ajouter ce token à votre fichier .env comme".format_map({"token": token}) - if lang == 'it': return "Token: {token}\nAggiungi questo token al tuo file .env come".format_map({"token": token}) - else: raise ValueError(f'Invalid language {lang}') -def token_exists(lang:str|None=None): - """ - ### Locales - - En: **Token already exists in .env. Continuing with the existing token.** - - Es: **Hay un token en el archivo .env. Continue con el token existente.** - - Fi: **Token on jo olemassa .env-tiedostossa. Jatketaan määritetyllä tokenilla.** - - Fr: **Le token existe déjà dans .env. Utilisation du token existant.** - - It: **Il token esiste già in .env. Continuando con il token esistente.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Token already exists in .env. Continuing with the existing token." - if lang == 'es': return "Hay un token en el archivo .env. Continue con el token existente." - if lang == 'fi': return "Token on jo olemassa .env-tiedostossa. Jatketaan määritetyllä tokenilla." - if lang == 'fr': return "Le token existe déjà dans .env. Utilisation du token existant." - if lang == 'it': return "Il token esiste già in .env. Continuando con il token esistente." - else: raise ValueError(f'Invalid language {lang}') -def registration_error(lang:str|None=None): - """ - ### Locales - - En: **Error during registration:** - - Es: **Error durante el registro:** - - Fi: **Virhe rekisteröinnissä:** - - Fr: **Erreur lors de l'enregistrement :** - - It: **Errore durante la registrazione:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error during registration:" - if lang == 'es': return "Error durante el registro:" - if lang == 'fi': return "Virhe rekisteröinnissä:" - if lang == 'fr': return "Erreur lors de l'enregistrement :" - if lang == 'it': return "Errore durante la registrazione:" - else: raise ValueError(f'Invalid language {lang}') -def version_backup(lang:str|None=None): - """ - ### Locales - - En: **Backup created:** - - Es: **Copia de seguridad creada:** - - Fi: **Varmuuskopio luotu:** - - Fr: **Sauvegarde créée :** - - It: **Backup creato:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Backup created:" - if lang == 'es': return "Copia de seguridad creada:" - if lang == 'fi': return "Varmuuskopio luotu:" - if lang == 'fr': return "Sauvegarde créée :" - if lang == 'it': return "Backup creato:" - else: raise ValueError(f'Invalid language {lang}') -def backup_error(LOCAL_VERSION_FILE,lang:str|None=None): - """ - ### Locales - - En: **Error: {LOCAL_VERSION_FILE} not found for backup.** - - Es: **Error: {LOCAL_VERSION_FILE} no encontrado para la copia de seguridad.** - - Fi: **Virhe: {LOCAL_VERSION_FILE}-tiedostoa ei löytynyt varmuuskopiota varten.** - - Fr: **Erreur : {LOCAL_VERSION_FILE} introuvable pour la sauvegarde.** - - It: **Errore: {LOCAL_VERSION_FILE} non trovato per il backup.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error: {LOCAL_VERSION_FILE} not found for backup.".format_map({"LOCAL_VERSION_FILE": LOCAL_VERSION_FILE}) - if lang == 'es': return "Error: {LOCAL_VERSION_FILE} no encontrado para la copia de seguridad.".format_map({"LOCAL_VERSION_FILE": LOCAL_VERSION_FILE}) - if lang == 'fi': return "Virhe: {LOCAL_VERSION_FILE}-tiedostoa ei löytynyt varmuuskopiota varten.".format_map({"LOCAL_VERSION_FILE": LOCAL_VERSION_FILE}) - if lang == 'fr': return "Erreur : {LOCAL_VERSION_FILE} introuvable pour la sauvegarde.".format_map({"LOCAL_VERSION_FILE": LOCAL_VERSION_FILE}) - if lang == 'it': return "Errore: {LOCAL_VERSION_FILE} non trovato per il backup.".format_map({"LOCAL_VERSION_FILE": LOCAL_VERSION_FILE}) - else: raise ValueError(f'Invalid language {lang}') -def model_loaded(lang:str|None=None): - """ - ### Locales - - En: **Markov model loaded from** - - Es: **Modelo de Markov cargado desde** - - Fi: **Markov-malli ladattu** - - Fr: **Modèle Markov chargé depuis** - - It: **Modello Markov caricato da** - """ - if not lang: lang=default_lang - if lang == 'en': return "Markov model loaded from" - if lang == 'es': return "Modelo de Markov cargado desde" - if lang == 'fi': return "Markov-malli ladattu" - if lang == 'fr': return "Modèle Markov chargé depuis" - if lang == 'it': return "Modello Markov caricato da" - else: raise ValueError(f'Invalid language {lang}') -def fetch_update_fail(lang:str|None=None): - """ - ### Locales - - En: **Could not fetch update information.** - - Es: **No se pudo obtener la informacion de actualizacion.** - - Fi: **Päivitystietojen hankkiminen epäonnistui.** - - Fr: **Impossible de récupérer les informations de mise à jour.** - - It: **Impossibile recuperare le informazioni sull'aggiornamento.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Could not fetch update information." - if lang == 'es': return "No se pudo obtener la informacion de actualizacion." - if lang == 'fi': return "Päivitystietojen hankkiminen epäonnistui." - if lang == 'fr': return "Impossible de récupérer les informations de mise à jour." - if lang == 'it': return "Impossibile recuperare le informazioni sull'aggiornamento." - else: raise ValueError(f'Invalid language {lang}') -def invalid_server(lang:str|None=None): - """ - ### Locales - - En: **Error: Invalid version information received from server.** - - Es: **Error: Se recibio informacion de version no valida del servidor.** - - Fi: **Virhe: Palvelin antoi virheellisen versiotietoraportin.** - - Fr: **Erreur : Informations de version invalides reçues du serveur.** - - It: **Errore: informazioni sulla versione non valide ricevute dal server.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error: Invalid version information received from server." - if lang == 'es': return "Error: Se recibio informacion de version no valida del servidor." - if lang == 'fi': return "Virhe: Palvelin antoi virheellisen versiotietoraportin." - if lang == 'fr': return "Erreur : Informations de version invalides reçues du serveur." - if lang == 'it': return "Errore: informazioni sulla versione non valide ricevute dal server." - else: raise ValueError(f'Invalid language {lang}') -def goober_server_alert(lang:str|None=None): - """ - ### Locales - - En: **Alert from goober central! -** - - Es: **Alert from goober central! -** - - Fi: **Viesti goober centralista! -** - - Fr: **Alerte du serveur Goober central ! -** - - It: **Avviso da goober central! -** - """ - if not lang: lang=default_lang - if lang == 'en': return "Alert from goober central!\n" - if lang == 'es': return "Alert from goober central!\n" - if lang == 'fi': return "Viesti goober centralista!\n" - if lang == 'fr': return "Alerte du serveur Goober central !\n" - if lang == 'it': return "Avviso da goober central!\n" - else: raise ValueError(f'Invalid language {lang}') -def new_version(latest_version,local_version,lang:str|None=None): - """ - ### Locales - - En: **New version available: {latest_version} (Current: {local_version})** - - Es: **Nueva version disponible: {latest_version} (Version actual: {local_version})** - - Fi: **Uusi versio saatavilla: {latest_version} (Tämänhetkinen: {local_version})** - - Fr: **Nouvelle version disponible : {latest_version} (Actuelle : {local_version})** - - It: **Nuova versione disponibile: {latest_version} (Attuale: {local_version})** - """ - if not lang: lang=default_lang - if lang == 'en': return "New version available: {latest_version} (Current: {local_version})".format_map({"latest_version": latest_version,"local_version": local_version}) - if lang == 'es': return "Nueva version disponible: {latest_version} (Version actual: {local_version})".format_map({"latest_version": latest_version,"local_version": local_version}) - if lang == 'fi': return "Uusi versio saatavilla: {latest_version} (Tämänhetkinen: {local_version})".format_map({"latest_version": latest_version,"local_version": local_version}) - if lang == 'fr': return "Nouvelle version disponible : {latest_version} (Actuelle : {local_version})".format_map({"latest_version": latest_version,"local_version": local_version}) - if lang == 'it': return "Nuova versione disponibile: {latest_version} (Attuale: {local_version})".format_map({"latest_version": latest_version,"local_version": local_version}) - else: raise ValueError(f'Invalid language {lang}') -def changelog(VERSION_URL,lang:str|None=None): - """ - ### Locales - - En: **Check {VERSION_URL}/goob/changes.txt to check out the changelog - -** - - Es: **Consulte {VERSION_URL}/goob/changes.txt para ver el registro de cambios - -** - - Fi: **Mene osoitteeseen {VERSION_URL}/goob/changes.txt katsotakseen muutoslokin - -** - - Fr: **Consultez {VERSION_URL}/goob/changes.txt pour voir les modifications - -** - - It: **Controlla {VERSION_URL}/goob/changes.txt per vedere il changelog - -** - """ - if not lang: lang=default_lang - if lang == 'en': return "Check {VERSION_URL}/goob/changes.txt to check out the changelog\n\n".format_map({"VERSION_URL": VERSION_URL}) - if lang == 'es': return "Consulte {VERSION_URL}/goob/changes.txt para ver el registro de cambios\n\n".format_map({"VERSION_URL": VERSION_URL}) - if lang == 'fi': return "Mene osoitteeseen {VERSION_URL}/goob/changes.txt katsotakseen muutoslokin\n\n".format_map({"VERSION_URL": VERSION_URL}) - if lang == 'fr': return "Consultez {VERSION_URL}/goob/changes.txt pour voir les modifications\n\n".format_map({"VERSION_URL": VERSION_URL}) - if lang == 'it': return "Controlla {VERSION_URL}/goob/changes.txt per vedere il changelog\n\n".format_map({"VERSION_URL": VERSION_URL}) - else: raise ValueError(f'Invalid language {lang}') -def invalid_version(local_version,lang:str|None=None): - """ - ### Locales - - En: **The version: {local_version} isnt valid!** - - Es: **La version: {local_version} no es valida!** - - Fi: **Versio: {local_version} on virheellinen!** - - Fr: **La version : {local_version} n'est pas valide !** - - It: **La versione: {local_version} non è valida!** - """ - if not lang: lang=default_lang - if lang == 'en': return "The version: {local_version} isnt valid!".format_map({"local_version": local_version}) - if lang == 'es': return "La version: {local_version} no es valida!".format_map({"local_version": local_version}) - if lang == 'fi': return "Versio: {local_version} on virheellinen!".format_map({"local_version": local_version}) - if lang == 'fr': return "La version : {local_version} n'est pas valide !".format_map({"local_version": local_version}) - if lang == 'it': return "La versione: {local_version} non è valida!".format_map({"local_version": local_version}) - else: raise ValueError(f'Invalid language {lang}') -def invalid_version2(lang:str|None=None): - """ - ### Locales - - En: **If this is intended then ignore this message, else press Y to pull a valid version from the server regardless of the version of goober currently running** - - Es: **Si esto es lo que pretende, ignore este mensaje; si no, haga clic en Y en su teclado para descargar una version valida del servidor, independientemente de la version que se este ejecutando actualmente.** - - Fi: **Jos tämä on tahallista, voit jättää tämän viestin huomiotta. Jos tämä ei ole tahallista, paina Y-näppäintä hankkiaksesi kelvollisen version, riippumatta Gooberin tämänhetkisestä versiosta.** - - Fr: **Si c'est intentionnel, ignorez ce message. Sinon, appuyez sur Y pour récupérer une version valide depuis le serveur, quelle que soit la version actuelle de Goober.** - - It: **Se è intenzionale ignora questo messaggio, altrimenti premi Y per scaricare una versione valida dal server indipendentemente dalla versione attuale di goober** - """ - if not lang: lang=default_lang - if lang == 'en': return "If this is intended then ignore this message, else press Y to pull a valid version from the server regardless of the version of goober currently running" - if lang == 'es': return "Si esto es lo que pretende, ignore este mensaje; si no, haga clic en Y en su teclado para descargar una version valida del servidor, independientemente de la version que se este ejecutando actualmente." - if lang == 'fi': return "Jos tämä on tahallista, voit jättää tämän viestin huomiotta. Jos tämä ei ole tahallista, paina Y-näppäintä hankkiaksesi kelvollisen version, riippumatta Gooberin tämänhetkisestä versiosta." - if lang == 'fr': return "Si c'est intentionnel, ignorez ce message. Sinon, appuyez sur Y pour récupérer une version valide depuis le serveur, quelle que soit la version actuelle de Goober." - if lang == 'it': return "Se è intenzionale ignora questo messaggio, altrimenti premi Y per scaricare una versione valida dal server indipendentemente dalla versione attuale di goober" - else: raise ValueError(f'Invalid language {lang}') -def invalid_version3(lang:str|None=None): - """ - ### Locales - - En: **The current version will be backed up to current_version.bak..** - - Es: **La version actual se copiara a current_version.bak..** - - Fi: **Tämänhetkinen versio varmuuskopioidaan kohteeseen current_version.bak..** - - Fr: **La version actuelle sera sauvegardée dans current_version.bak..** - - It: **La versione attuale sarà salvata come current_version.bak..** - """ - if not lang: lang=default_lang - if lang == 'en': return "The current version will be backed up to current_version.bak.." - if lang == 'es': return "La version actual se copiara a current_version.bak.." - if lang == 'fi': return "Tämänhetkinen versio varmuuskopioidaan kohteeseen current_version.bak.." - if lang == 'fr': return "La version actuelle sera sauvegardée dans current_version.bak.." - if lang == 'it': return "La versione attuale sarà salvata come current_version.bak.." - else: raise ValueError(f'Invalid language {lang}') -def input(lang:str|None=None): - """ - ### Locales - - En: **(Y or any other key to ignore....)** - - Es: **(Y o cualquier otra tecla para ignorar....)** - - Fi: **(Y:tä tai mitä vaan muuta näppäintä jättää tämän huomioimatta....)** - - Fr: **(Y ou toute autre touche pour ignorer...)** - - It: **(Y o qualsiasi altro tasto per ignorare....)** - """ - if not lang: lang=default_lang - if lang == 'en': return "(Y or any other key to ignore....)" - if lang == 'es': return "(Y o cualquier otra tecla para ignorar....)" - if lang == 'fi': return "(Y:tä tai mitä vaan muuta näppäintä jättää tämän huomioimatta....)" - if lang == 'fr': return "(Y ou toute autre touche pour ignorer...)" - if lang == 'it': return "(Y o qualsiasi altro tasto per ignorare....)" - else: raise ValueError(f'Invalid language {lang}') -def modification_ignored(lang:str|None=None): - """ - ### Locales - - En: **You've modified** - - Es: **Has modificado** - - Fi: **Olet muokannut** - - Fr: **Vous avez modifié** - - It: **Hai modificato** - """ - if not lang: lang=default_lang - if lang == 'en': return "You've modified" - if lang == 'es': return "Has modificado" - if lang == 'fi': return "Olet muokannut" - if lang == 'fr': return "Vous avez modifié" - if lang == 'it': return "Hai modificato" - else: raise ValueError(f'Invalid language {lang}') -def modification_ignored2(lang:str|None=None): - """ - ### Locales - - En: **IGNOREWARNING is set to false..** - - Es: **IGNOREWARNING es falso** - - Fi: **IGNOREWARNING on asetettu false:ksi..** - - Fr: **IGNOREWARNING est désactivé..** - - It: **IGNOREWARNING è impostato su false..** - """ - if not lang: lang=default_lang - if lang == 'en': return "IGNOREWARNING is set to false.." - if lang == 'es': return "IGNOREWARNING es falso" - if lang == 'fi': return "IGNOREWARNING on asetettu false:ksi.." - if lang == 'fr': return "IGNOREWARNING est désactivé.." - if lang == 'it': return "IGNOREWARNING è impostato su false.." - else: raise ValueError(f'Invalid language {lang}') -def latest_version(lang:str|None=None): - """ - ### Locales - - En: **You're using the latest version:** - - Es: **Usando la ultima version:** - - Fi: **Käytät uusinta versiota:** - - Fr: **Vous utilisez la dernière version :** - - It: **Stai utilizzando l'ultima versione:** - """ - if not lang: lang=default_lang - if lang == 'en': return "You're using the latest version:" - if lang == 'es': return "Usando la ultima version:" - if lang == 'fi': return "Käytät uusinta versiota:" - if lang == 'fr': return "Vous utilisez la dernière version :" - if lang == 'it': return "Stai utilizzando l'ultima versione:" - else: raise ValueError(f'Invalid language {lang}') -def latest_version2(VERSION_URL,lang:str|None=None): - """ - ### Locales - - En: **Check {VERSION_URL}/goob/changes.txt to check out the changelog** - - Es: **Consulte {VERSION_URL}/goob/changes.txt para ver el registro de cambios** - - Fi: **Tarkista {VERSION_URL}/goob/changes.txt katsotakseen muutosloki** - - Fr: **Consultez {VERSION_URL}/goob/changes.txt pour voir les modifications** - - It: **Controlla {VERSION_URL}/goob/changes.txt per vedere il changelog** - """ - if not lang: lang=default_lang - if lang == 'en': return "Check {VERSION_URL}/goob/changes.txt to check out the changelog".format_map({"VERSION_URL": VERSION_URL}) - if lang == 'es': return "Consulte {VERSION_URL}/goob/changes.txt para ver el registro de cambios".format_map({"VERSION_URL": VERSION_URL}) - if lang == 'fi': return "Tarkista {VERSION_URL}/goob/changes.txt katsotakseen muutosloki".format_map({"VERSION_URL": VERSION_URL}) - if lang == 'fr': return "Consultez {VERSION_URL}/goob/changes.txt pour voir les modifications".format_map({"VERSION_URL": VERSION_URL}) - if lang == 'it': return "Controlla {VERSION_URL}/goob/changes.txt per vedere il changelog".format_map({"VERSION_URL": VERSION_URL}) - else: raise ValueError(f'Invalid language {lang}') -def pinging_disabled(lang:str|None=None): - """ - ### Locales - - En: **Pinging is disabled! Not telling the server im on...** - - Es: **El ping esta deshabilitado** - - Fi: **Pingaus on poistettu käytöstä! En kerro palvelimelle, että olen päällä...** - - Fr: **Le ping est désactivé ! Je ne préviens pas le serveur que je suis en ligne...** - - It: **Il ping è disabilitato! Non dico al server che sono online...** - """ - if not lang: lang=default_lang - if lang == 'en': return "Pinging is disabled! Not telling the server im on..." - if lang == 'es': return "El ping esta deshabilitado" - if lang == 'fi': return "Pingaus on poistettu käytöstä! En kerro palvelimelle, että olen päällä..." - if lang == 'fr': return "Le ping est désactivé ! Je ne préviens pas le serveur que je suis en ligne..." - if lang == 'it': return "Il ping è disabilitato! Non dico al server che sono online..." - else: raise ValueError(f'Invalid language {lang}') -def goober_ping_success(NAME,lang:str|None=None): - """ - ### Locales - - En: **Logged into goober central as {NAME}** - - Es: **Envie ping a Goober Central!** - - Fi: **Lähetettiin olemassaolo ping goober centraliin!** - - Fr: **Connecté à Goober central en tant que {NAME}** - - It: **Accesso a goober central come {NAME}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Logged into goober central as {NAME}".format_map({"NAME": NAME}) - if lang == 'es': return "Envie ping a Goober Central!".format_map({"NAME": NAME}) - if lang == 'fi': return "Lähetettiin olemassaolo ping goober centraliin!".format_map({"NAME": NAME}) - if lang == 'fr': return "Connecté à Goober central en tant que {NAME}".format_map({"NAME": NAME}) - if lang == 'it': return "Accesso a goober central come {NAME}".format_map({"NAME": NAME}) - else: raise ValueError(f'Invalid language {lang}') -def goober_ping_fail(lang:str|None=None): - """ - ### Locales - - En: **Failed to send data. Server returned status code:** - - Es: **Error al enviar datos. El servidor devolvio el codigo de estado:** - - Fi: **Tiedon lähetys epäonnistui. Palvelin antoi tilakoodin:** - - Fr: **Échec de l'envoi des données. Le serveur a retourné le code d'état :** - - It: **Impossibile inviare i dati. Il server ha restituito il codice di stato:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Failed to send data. Server returned status code:" - if lang == 'es': return "Error al enviar datos. El servidor devolvio el codigo de estado:" - if lang == 'fi': return "Tiedon lähetys epäonnistui. Palvelin antoi tilakoodin:" - if lang == 'fr': return "Échec de l'envoi des données. Le serveur a retourné le code d'état :" - if lang == 'it': return "Impossibile inviare i dati. Il server ha restituito il codice di stato:" - else: raise ValueError(f'Invalid language {lang}') -def goober_ping_fail2(lang:str|None=None): - """ - ### Locales - - En: **An error occurred while sending data:** - - Es: **Se produjo un error al enviar los datos:** - - Fi: **Tiedon lähettämisen aikana tapahtui virhe:** - - Fr: **Une erreur est survenue lors de l'envoi des données :** - - It: **Si è verificato un errore durante l'invio dei dati:** - """ - if not lang: lang=default_lang - if lang == 'en': return "An error occurred while sending data:" - if lang == 'es': return "Se produjo un error al enviar los datos:" - if lang == 'fi': return "Tiedon lähettämisen aikana tapahtui virhe:" - if lang == 'fr': return "Une erreur est survenue lors de l'envoi des données :" - if lang == 'it': return "Si è verificato un errore durante l'invio dei dati:" - else: raise ValueError(f'Invalid language {lang}') -def sentence_positivity(lang:str|None=None): - """ - ### Locales - - En: **Positivity of sentence is:** - - Es: **La positividad de la sentencia es:** - - Fi: **Lauseen positiivisuus on:** - - Fr: **La positivité de la phrase est :** - - It: **La positività della frase è:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Positivity of sentence is:" - if lang == 'es': return "La positividad de la sentencia es:" - if lang == 'fi': return "Lauseen positiivisuus on:" - if lang == 'fr': return "La positivité de la phrase est :" - if lang == 'it': return "La positività della frase è:" - else: raise ValueError(f'Invalid language {lang}') -def command_edit_fail(lang:str|None=None): - """ - ### Locales - - En: **Failed to edit message:** - - Es: **No se pudo editar el mensaje:** - - Fi: **Viestin muokkaus epäonnistui:** - - Fr: **Échec de la modification du message :** - - It: **Impossibile modificare il messaggio:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Failed to edit message:" - if lang == 'es': return "No se pudo editar el mensaje:" - if lang == 'fi': return "Viestin muokkaus epäonnistui:" - if lang == 'fr': return "Échec de la modification du message :" - if lang == 'it': return "Impossibile modificare il messaggio:" - else: raise ValueError(f'Invalid language {lang}') -def command_desc_retrain(lang:str|None=None): - """ - ### Locales - - En: **Retrains the Markov model manually.** - - Es: **Vuelve a entrenar el modelo de Markov manualmente.** - - Fi: **Uudelleenkouluttaa markov-mallin manuaalisesti.** - - Fr: **Réentraîne manuellement le modèle Markov.** - - It: **Rafforza manualmente il modello Markov.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Retrains the Markov model manually." - if lang == 'es': return "Vuelve a entrenar el modelo de Markov manualmente." - if lang == 'fi': return "Uudelleenkouluttaa markov-mallin manuaalisesti." - if lang == 'fr': return "Réentraîne manuellement le modèle Markov." - if lang == 'it': return "Rafforza manualmente il modello Markov." - else: raise ValueError(f'Invalid language {lang}') -def command_markov_retrain(lang:str|None=None): - """ - ### Locales - - En: **Retraining the Markov model... Please wait.** - - Es: **Reentrenando el modelo de Markov... Por favor espere** - - Fi: **Uudelleenkoulutetaan markov-mallia... Odota.** - - Fr: **Réentraînement du modèle Markov... Veuillez patienter.** - - It: **Rafforzamento del modello Markov in corso... Attendere.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Retraining the Markov model... Please wait." - if lang == 'es': return "Reentrenando el modelo de Markov... Por favor espere" - if lang == 'fi': return "Uudelleenkoulutetaan markov-mallia... Odota." - if lang == 'fr': return "Réentraînement du modèle Markov... Veuillez patienter." - if lang == 'it': return "Rafforzamento del modello Markov in corso... Attendere." - else: raise ValueError(f'Invalid language {lang}') -def command_markov_memory_not_found(lang:str|None=None): - """ - ### Locales - - En: **Error: memory file not found!** - - Es: **Error: no hay archivo de memoria!** - - Fi: **Virhe: muistitiedostoa ei löytynyt!** - - Fr: **Erreur : fichier de mémoire introuvable !** - - It: **Errore: file di memoria non trovato!** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error: memory file not found!" - if lang == 'es': return "Error: no hay archivo de memoria!" - if lang == 'fi': return "Virhe: muistitiedostoa ei löytynyt!" - if lang == 'fr': return "Erreur : fichier de mémoire introuvable !" - if lang == 'it': return "Errore: file di memoria non trovato!" - else: raise ValueError(f'Invalid language {lang}') -def command_markov_memory_is_corrupt(lang:str|None=None): - """ - ### Locales - - En: **Error: memory file is corrupt!** - - Es: **Error: el archivo de memoria esta danado!** - - Fi: **Virhe: muistitiedosto on korruptoitu!** - - Fr: **Erreur : le fichier de mémoire est corrompu !** - - It: **Errore: file di memoria corrotto!** - """ - if not lang: lang=default_lang - if lang == 'en': return "Error: memory file is corrupt!" - if lang == 'es': return "Error: el archivo de memoria esta danado!" - if lang == 'fi': return "Virhe: muistitiedosto on korruptoitu!" - if lang == 'fr': return "Erreur : le fichier de mémoire est corrompu !" - if lang == 'it': return "Errore: file di memoria corrotto!" - else: raise ValueError(f'Invalid language {lang}') -def command_markov_retraining(data_size,lang:str|None=None): - """ - ### Locales - - En: **Processing {data_size} data points...** - - Es: **Procesando {processed_data}/{data_size} puntos de datos...** - - Fi: **Käsitellään {processed_data}/{data_size} datapistettä...** - - Fr: **Traitement de {processed_data}/{data_size} points de données...** - - It: **Elaborazione di {data_size} punti dati...** - """ - if not lang: lang=default_lang - if lang == 'en': return "Processing {data_size} data points...".format_map({"data_size": data_size}) - if lang == 'es': return "Procesando {processed_data}/{data_size} puntos de datos...".format_map({"data_size": data_size}) - if lang == 'fi': return "Käsitellään {processed_data}/{data_size} datapistettä...".format_map({"data_size": data_size}) - if lang == 'fr': return "Traitement de {processed_data}/{data_size} points de données...".format_map({"data_size": data_size}) - if lang == 'it': return "Elaborazione di {data_size} punti dati...".format_map({"data_size": data_size}) - else: raise ValueError(f'Invalid language {lang}') -def command_markov_retrain_successful(data_size,lang:str|None=None): - """ - ### Locales - - En: **Markov model retrained successfully using {data_size} data points!** - - Es: **Modelo de Markov reentrenado exitosamente usando {data_size} puntos de datos!** - - Fi: **Markov-malli koulutettiin uudestaan {data_size} datapisteellä!** - - Fr: **Modèle Markov réentraîné avec succès en utilisant {data_size} points de données !** - - It: **Modello Markov rafforzato con successo utilizzando {data_size} punti dati!** - """ - if not lang: lang=default_lang - if lang == 'en': return "Markov model retrained successfully using {data_size} data points!".format_map({"data_size": data_size}) - if lang == 'es': return "Modelo de Markov reentrenado exitosamente usando {data_size} puntos de datos!".format_map({"data_size": data_size}) - if lang == 'fi': return "Markov-malli koulutettiin uudestaan {data_size} datapisteellä!".format_map({"data_size": data_size}) - if lang == 'fr': return "Modèle Markov réentraîné avec succès en utilisant {data_size} points de données !".format_map({"data_size": data_size}) - if lang == 'it': return "Modello Markov rafforzato con successo utilizzando {data_size} punti dati!".format_map({"data_size": data_size}) - else: raise ValueError(f'Invalid language {lang}') -def command_desc_talk(lang:str|None=None): - """ - ### Locales - - En: **talks n like stuf** - - Es: **hace que el bot hable** - - Fi: **puhuu ja sillei** - - Fr: **parle et tout ça** - - It: **parla n come stuf** - """ - if not lang: lang=default_lang - if lang == 'en': return "talks n like stuf" - if lang == 'es': return "hace que el bot hable" - if lang == 'fi': return "puhuu ja sillei" - if lang == 'fr': return "parle et tout ça" - if lang == 'it': return "parla n come stuf" - else: raise ValueError(f'Invalid language {lang}') -def command_talk_insufficent_text(lang:str|None=None): - """ - ### Locales - - En: **I need to learn more from messages before I can talk.** - - Es: **Necesito aprender más sobre los mensajes antes de hablar.** - - Fi: **Minun pitää oppia lisää viesteistä ennen kun puhun.** - - Fr: **Je dois apprendre plus de messages avant de pouvoir parler.** - - It: **Ho bisogno di imparare di più dai messaggi prima di poter parlare.** - """ - if not lang: lang=default_lang - if lang == 'en': return "I need to learn more from messages before I can talk." - if lang == 'es': return "Necesito aprender más sobre los mensajes antes de hablar." - if lang == 'fi': return "Minun pitää oppia lisää viesteistä ennen kun puhun." - if lang == 'fr': return "Je dois apprendre plus de messages avant de pouvoir parler." - if lang == 'it': return "Ho bisogno di imparare di più dai messaggi prima di poter parlare." - else: raise ValueError(f'Invalid language {lang}') -def command_talk_generation_fail(lang:str|None=None): - """ - ### Locales - - En: **I have nothing to say right now!** - - Es: **No tengo nada que decir ahora!** - - Fi: **Minulla ei ole mitään sanottavaa!** - - Fr: **Je n'ai rien à dire pour le moment !** - - It: **Non ho nulla da dire in questo momento!** - """ - if not lang: lang=default_lang - if lang == 'en': return "I have nothing to say right now!" - if lang == 'es': return "No tengo nada que decir ahora!" - if lang == 'fi': return "Minulla ei ole mitään sanottavaa!" - if lang == 'fr': return "Je n'ai rien à dire pour le moment !" - if lang == 'it': return "Non ho nulla da dire in questo momento!" - else: raise ValueError(f'Invalid language {lang}') -def command_desc_help(lang:str|None=None): - """ - ### Locales - - En: **help** - - Es: **Ayuda** - - Fi: **auta** - - Fr: **aide** - - It: **aiuto** - """ - if not lang: lang=default_lang - if lang == 'en': return "help" - if lang == 'es': return "Ayuda" - if lang == 'fi': return "auta" - if lang == 'fr': return "aide" - if lang == 'it': return "aiuto" - else: raise ValueError(f'Invalid language {lang}') -def command_help_embed_title(lang:str|None=None): - """ - ### Locales - - En: **Bot Help** - - Es: **Ayuda del bot** - - Fi: **Botin apu** - - Fr: **Aide du bot** - - It: **Aiuto Bot** - """ - if not lang: lang=default_lang - if lang == 'en': return "Bot Help" - if lang == 'es': return "Ayuda del bot" - if lang == 'fi': return "Botin apu" - if lang == 'fr': return "Aide du bot" - if lang == 'it': return "Aiuto Bot" - else: raise ValueError(f'Invalid language {lang}') -def command_help_embed_desc(lang:str|None=None): - """ - ### Locales - - En: **List of commands grouped by category.** - - Es: **Lista de comandos agrupados por categoria** - - Fi: **Komennot ryhmitelty kategorioilla** - - Fr: **Liste des commandes regroupées par catégorie.** - - It: **Elenco dei comandi raggruppati per categoria.** - """ - if not lang: lang=default_lang - if lang == 'en': return "List of commands grouped by category." - if lang == 'es': return "Lista de comandos agrupados por categoria" - if lang == 'fi': return "Komennot ryhmitelty kategorioilla" - if lang == 'fr': return "Liste des commandes regroupées par catégorie." - if lang == 'it': return "Elenco dei comandi raggruppati per categoria." - else: raise ValueError(f'Invalid language {lang}') -def command_help_categories_general(lang:str|None=None): - """ - ### Locales - - En: **General** - - Es: **General** - - Fi: **Yleiset** - - Fr: **Général** - - It: **Generale** - """ - if not lang: lang=default_lang - if lang == 'en': return "General" - if lang == 'es': return "General" - if lang == 'fi': return "Yleiset" - if lang == 'fr': return "Général" - if lang == 'it': return "Generale" - else: raise ValueError(f'Invalid language {lang}') -def command_help_categories_admin(lang:str|None=None): - """ - ### Locales - - En: **Administration** - - Es: **Administracion** - - Fi: **Ylläpito** - - Fr: **Administration** - - It: **Amministrazione** - """ - if not lang: lang=default_lang - if lang == 'en': return "Administration" - if lang == 'es': return "Administracion" - if lang == 'fi': return "Ylläpito" - if lang == 'fr': return "Administration" - if lang == 'it': return "Amministrazione" - else: raise ValueError(f'Invalid language {lang}') -def command_help_categories_custom(lang:str|None=None): - """ - ### Locales - - En: **Custom Commands** - - Es: **Comandos personalizados** - - Fi: **Mukautetut komennot** - - Fr: **Commandes personnalisées** - - It: **Comandi personalizzati** - """ - if not lang: lang=default_lang - if lang == 'en': return "Custom Commands" - if lang == 'es': return "Comandos personalizados" - if lang == 'fi': return "Mukautetut komennot" - if lang == 'fr': return "Commandes personnalisées" - if lang == 'it': return "Comandi personalizzati" - else: raise ValueError(f'Invalid language {lang}') -def command_ran(message_author_name,message_content,lang:str|None=None): - """ - ### Locales - - En: **Info: {message.author.name} ran {message.content}** - - Es: **Informacion: {message.author.name} ejecuto {message.content}** - - Fi: **Tietoa: {message.author.name} suoritti {message.content}** - - Fr: **Info : {message.author.name} a exécuté {message.content}** - - It: **Info: {message.author.name} ha eseguito {message.content}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Info: {message_author_name} ran {message_content}".format_map({"message_author_name": message_author_name,"message_content": message_content}) - if lang == 'es': return "Informacion: {message_author_name} ejecuto {message_content}".format_map({"message_author_name": message_author_name,"message_content": message_content}) - if lang == 'fi': return "Tietoa: {message_author_name} suoritti {message_content}".format_map({"message_author_name": message_author_name,"message_content": message_content}) - if lang == 'fr': return "Info : {message_author_name} a exécuté {message_content}".format_map({"message_author_name": message_author_name,"message_content": message_content}) - if lang == 'it': return "Info: {message_author_name} ha eseguito {message_content}".format_map({"message_author_name": message_author_name,"message_content": message_content}) - else: raise ValueError(f'Invalid language {lang}') -def command_ran_s(interaction_user,lang:str|None=None): - """ - ### Locales - - En: **Info: {interaction.user} ran ** - - Es: **Info: {interaction.user} ran ** - - Fi: **Info: {interaction.user} suoritti** - - Fr: **Info : {interaction.user} a exécuté ** - - It: **Info: {interaction.user} ha eseguito ** - """ - if not lang: lang=default_lang - if lang == 'en': return "Info: {interaction_user} ran ".format_map({"interaction_user": interaction_user}) - if lang == 'es': return "Info: {interaction_user} ran ".format_map({"interaction_user": interaction_user}) - if lang == 'fi': return "Info: {interaction_user} suoritti".format_map({"interaction_user": interaction_user}) - if lang == 'fr': return "Info : {interaction_user} a exécuté ".format_map({"interaction_user": interaction_user}) - if lang == 'it': return "Info: {interaction_user} ha eseguito ".format_map({"interaction_user": interaction_user}) - else: raise ValueError(f'Invalid language {lang}') -def command_desc_ping(lang:str|None=None): - """ - ### Locales - - En: **ping** - - Es: **ping** - - Fi: **ping** - - Fr: **ping** - - It: **ping** - """ - if not lang: lang=default_lang - if lang == 'en': return "ping" - if lang == 'es': return "ping" - if lang == 'fi': return "ping" - if lang == 'fr': return "ping" - if lang == 'it': return "ping" - else: raise ValueError(f'Invalid language {lang}') -def command_desc_setlang(lang:str|None=None): - """ - ### Locales - - En: **Set a new language for the bot (temporarily)** - - Es: **Set a new language for the bot (temporarily)** - - Fi: **Set a new language for the bot (temporarily)** - - Fr: **Set a new language for the bot (temporarily)** - - It: **Imposta una nuova lingua per il bot (temporaneamente)** - """ - if not lang: lang=default_lang - if lang == 'en': return "Set a new language for the bot (temporarily)" - if lang == 'es': return "Set a new language for the bot (temporarily)" - if lang == 'fi': return "Set a new language for the bot (temporarily)" - if lang == 'fr': return "Set a new language for the bot (temporarily)" - if lang == 'it': return "Imposta una nuova lingua per il bot (temporaneamente)" - else: raise ValueError(f'Invalid language {lang}') -def command_ping_embed_desc(lang:str|None=None): - """ - ### Locales - - En: **Bot Latency:** - - Es: **Latencia del bot:** - - Fi: **Botin viive:** - - Fr: **Latence du bot :** - - It: **Latenza del bot:** - """ - if not lang: lang=default_lang - if lang == 'en': return "Bot Latency:" - if lang == 'es': return "Latencia del bot:" - if lang == 'fi': return "Botin viive:" - if lang == 'fr': return "Latence du bot :" - if lang == 'it': return "Latenza del bot:" - else: raise ValueError(f'Invalid language {lang}') -def command_ping_footer(lang:str|None=None): - """ - ### Locales - - En: **Requested by** - - Es: **Solicitado por** - - Fi: **Pyytäjä: ** - - Fr: **Demandé par** - - It: **Richiesto da** - """ - if not lang: lang=default_lang - if lang == 'en': return "Requested by" - if lang == 'es': return "Solicitado por" - if lang == 'fi': return "Pyytäjä: " - if lang == 'fr': return "Demandé par" - if lang == 'it': return "Richiesto da" - else: raise ValueError(f'Invalid language {lang}') -def command_about_desc(lang:str|None=None): - """ - ### Locales - - En: **about** - - Es: **Acerca** - - Fi: **tietoa** - - Fr: **à propos** - - It: **informazioni** - """ - if not lang: lang=default_lang - if lang == 'en': return "about" - if lang == 'es': return "Acerca" - if lang == 'fi': return "tietoa" - if lang == 'fr': return "à propos" - if lang == 'it': return "informazioni" - else: raise ValueError(f'Invalid language {lang}') -def command_about_embed_title(lang:str|None=None): - """ - ### Locales - - En: **About me** - - Es: **Acerca de mi** - - Fi: **Tietoa minusta** - - Fr: **À propos de moi** - - It: **Informazioni su di me** - """ - if not lang: lang=default_lang - if lang == 'en': return "About me" - if lang == 'es': return "Acerca de mi" - if lang == 'fi': return "Tietoa minusta" - if lang == 'fr': return "À propos de moi" - if lang == 'it': return "Informazioni su di me" - else: raise ValueError(f'Invalid language {lang}') -def command_about_embed_field1(lang:str|None=None): - """ - ### Locales - - En: **Name** - - Es: **Nombre** - - Fi: **Nimi** - - Fr: **Nom** - - It: **Nome** - """ - if not lang: lang=default_lang - if lang == 'en': return "Name" - if lang == 'es': return "Nombre" - if lang == 'fi': return "Nimi" - if lang == 'fr': return "Nom" - if lang == 'it': return "Nome" - else: raise ValueError(f'Invalid language {lang}') -def command_about_embed_field2name(lang:str|None=None): - """ - ### Locales - - En: **Version** - - Es: **Version** - - Fi: **Versio** - - Fr: **Version** - - It: **Versione** - """ - if not lang: lang=default_lang - if lang == 'en': return "Version" - if lang == 'es': return "Version" - if lang == 'fi': return "Versio" - if lang == 'fr': return "Version" - if lang == 'it': return "Versione" - else: raise ValueError(f'Invalid language {lang}') -def command_about_embed_field2value(local_version,latest_version,lang:str|None=None): - """ - ### Locales - - En: **Local: {local_version} -Latest: {latest_version}** - - Es: **Version local: {local_version} -Ultima version: {latest_version}** - - Fi: **Paikallinen: {local_version} -Uusin: {latest_version}** - - Fr: **Locale : {local_version} -Dernière : {latest_version}** - - It: **Locale: {local_version} -Ultima: {latest_version}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Local: {local_version} \nLatest: {latest_version}".format_map({"local_version": local_version,"latest_version": latest_version}) - if lang == 'es': return "Version local: {local_version} \nUltima version: {latest_version}".format_map({"local_version": local_version,"latest_version": latest_version}) - if lang == 'fi': return "Paikallinen: {local_version} \nUusin: {latest_version}".format_map({"local_version": local_version,"latest_version": latest_version}) - if lang == 'fr': return "Locale : {local_version} \nDernière : {latest_version}".format_map({"local_version": local_version,"latest_version": latest_version}) - if lang == 'it': return "Locale: {local_version} \nUltima: {latest_version}".format_map({"local_version": local_version,"latest_version": latest_version}) - else: raise ValueError(f'Invalid language {lang}') -def command_desc_stats(lang:str|None=None): - """ - ### Locales - - En: **stats** - - Es: **Estadistica** - - Fi: **statistiikat** - - Fr: **statistiques** - - It: **statistiche** - """ - if not lang: lang=default_lang - if lang == 'en': return "stats" - if lang == 'es': return "Estadistica" - if lang == 'fi': return "statistiikat" - if lang == 'fr': return "statistiques" - if lang == 'it': return "statistiche" - else: raise ValueError(f'Invalid language {lang}') -def command_stats_embed_title(lang:str|None=None): - """ - ### Locales - - En: **Bot stats** - - Es: **Estadisticas de bot** - - Fi: **Botin statistiikat** - - Fr: **Statistiques du bot** - - It: **Statistiche del bot** - """ - if not lang: lang=default_lang - if lang == 'en': return "Bot stats" - if lang == 'es': return "Estadisticas de bot" - if lang == 'fi': return "Botin statistiikat" - if lang == 'fr': return "Statistiques du bot" - if lang == 'it': return "Statistiche del bot" - else: raise ValueError(f'Invalid language {lang}') -def command_stats_embed_desc(lang:str|None=None): - """ - ### Locales - - En: **Data about the the bot's memory.** - - Es: **Datos sobre la memoria del bot** - - Fi: **Tietoa botin muistista.** - - Fr: **Données sur la mémoire du bot.** - - It: **Dati sulla memoria del bot.** - """ - if not lang: lang=default_lang - if lang == 'en': return "Data about the the bot's memory." - if lang == 'es': return "Datos sobre la memoria del bot" - if lang == 'fi': return "Tietoa botin muistista." - if lang == 'fr': return "Données sur la mémoire du bot." - if lang == 'it': return "Dati sulla memoria del bot." - else: raise ValueError(f'Invalid language {lang}') -def command_stats_embed_field1name(lang:str|None=None): - """ - ### Locales - - En: **File Stats** - - Es: **Estadisticas** - - Fi: **Tiedostostatistiikat** - - Fr: **Statistiques du fichier** - - It: **Statistiche del file** - """ - if not lang: lang=default_lang - if lang == 'en': return "File Stats" - if lang == 'es': return "Estadisticas" - if lang == 'fi': return "Tiedostostatistiikat" - if lang == 'fr': return "Statistiques du fichier" - if lang == 'it': return "Statistiche del file" - else: raise ValueError(f'Invalid language {lang}') -def command_stats_embed_field1value(file_size,line_count,lang:str|None=None): - """ - ### Locales - - En: **Size: {file_size} bytes -Lines: {line_count}** - - Es: **Tamano: {file_size} bytes -Lineas: {line_count}** - - Fi: **Koko: {file_size} tavua -Linjoja: {line_count}** - - Fr: **Taille : {file_size} octets -Lignes : {line_count}** - - It: **Dimensione: {file_size} byte -Linee: {line_count}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Size: {file_size} bytes\nLines: {line_count}".format_map({"file_size": file_size,"line_count": line_count}) - if lang == 'es': return "Tamano: {file_size} bytes\nLineas: {line_count}".format_map({"file_size": file_size,"line_count": line_count}) - if lang == 'fi': return "Koko: {file_size} tavua\nLinjoja: {line_count}".format_map({"file_size": file_size,"line_count": line_count}) - if lang == 'fr': return "Taille : {file_size} octets\nLignes : {line_count}".format_map({"file_size": file_size,"line_count": line_count}) - if lang == 'it': return "Dimensione: {file_size} byte\nLinee: {line_count}".format_map({"file_size": file_size,"line_count": line_count}) - else: raise ValueError(f'Invalid language {lang}') -def command_stats_embed_field2name(lang:str|None=None): - """ - ### Locales - - En: **Version** - - Es: **Version** - - Fi: **Versio** - - Fr: **Version** - - It: **Versione** - """ - if not lang: lang=default_lang - if lang == 'en': return "Version" - if lang == 'es': return "Version" - if lang == 'fi': return "Versio" - if lang == 'fr': return "Version" - if lang == 'it': return "Versione" - else: raise ValueError(f'Invalid language {lang}') -def command_stats_embed_field2value(local_version,latest_version,lang:str|None=None): - """ - ### Locales - - En: **Local: {local_version} -Latest: {latest_version}** - - Es: **Version local: {local_version} -Ultima version: {latest_version}** - - Fi: **Paikallinen: {local_version} -Uusin: {latest_version}** - - Fr: **Locale : {local_version} -Dernière : {latest_version}** - - It: **Locale: {local_version} -Ultima: {latest_version}** - """ - if not lang: lang=default_lang - if lang == 'en': return "Local: {local_version} \nLatest: {latest_version}".format_map({"local_version": local_version,"latest_version": latest_version}) - if lang == 'es': return "Version local: {local_version} \nUltima version: {latest_version}".format_map({"local_version": local_version,"latest_version": latest_version}) - if lang == 'fi': return "Paikallinen: {local_version} \nUusin: {latest_version}".format_map({"local_version": local_version,"latest_version": latest_version}) - if lang == 'fr': return "Locale : {local_version} \nDernière : {latest_version}".format_map({"local_version": local_version,"latest_version": latest_version}) - if lang == 'it': return "Locale: {local_version} \nUltima: {latest_version}".format_map({"local_version": local_version,"latest_version": latest_version}) - else: raise ValueError(f'Invalid language {lang}') -def command_stats_embed_field3name(lang:str|None=None): - """ - ### Locales - - En: **Variable Info** - - Es: **informacion sobre las variables** - - Fi: **Muuttajainformaatio** - - Fr: **Informations variables** - - It: **Informazioni sulle variabili** - """ - if not lang: lang=default_lang - if lang == 'en': return "Variable Info" - if lang == 'es': return "informacion sobre las variables" - if lang == 'fi': return "Muuttajainformaatio" - if lang == 'fr': return "Informations variables" - if lang == 'it': return "Informazioni sulle variabili" - else: raise ValueError(f'Invalid language {lang}') -def command_stats_embed_field3value(NAME,PREFIX,ownerid,PING_LINE,showmemenabled,USERTRAIN_ENABLED,song,splashtext,lang:str|None=None): - """ - ### Locales - - En: **Name: {NAME} -Prefix: {PREFIX} -Owner ID: {ownerid} -Ping line: {PING_LINE} -Memory Sharing Enabled: {showmemenabled} -User Training Enabled: {USERTRAIN_ENABLED} -Song: {song} -Splashtext: ```{splashtext}```** - - Es: **Nombre: {NAME} -Prefijo: {PREFIX} -ID del propietario: {ownerid} -Linea de ping: {PING_LINE} -Compartir memoria habilitada: {showmemenabled} -Entrenamiento de usuario habilitado: {USERTRAIN_ENABLED} -Cancion: {song} -Texto de bienvenida: ```{splashtext}```** - - Fi: **Nimi: {NAME} -Etuliite: {PREFIX} -Omistajan ID: {ownerid} -Ping-linja: {PING_LINE} -Muistin jako päällä: {showmemenabled} -Oppiminen käyttäjistä: {USERTRAIN_ENABLED} -Laulu: {song} -Roisketeksti: ```{splashtext}```** - - Fr: **Nom : {NAME} -Préfixe : {PREFIX} -ID du propriétaire : {ownerid} -Ligne de ping : {PING_LINE} -Partage de mémoire activé : {showmemenabled} -Entraînement utilisateur activé : {USERTRAIN_ENABLED} -Chanson : {song} -Texte de démarrage : ```{splashtext}```** - - It: **Nome: {NAME} -Prefisso: {PREFIX} -ID Proprietario: {ownerid} -Linea ping: {PING_LINE} -Memoria Condivisa Abilitata: {showmemenabled} -Addestramento Utente Abilitato: {USERTRAIN_ENABLED} -Canzone: {song} -Splashtext: ```{splashtext}```** - """ - if not lang: lang=default_lang - if lang == 'en': return "Name: {NAME} \nPrefix: {PREFIX} \nOwner ID: {ownerid}\nPing line: {PING_LINE} \nMemory Sharing Enabled: {showmemenabled} \nUser Training Enabled: {USERTRAIN_ENABLED}\nSong: {song} \nSplashtext: ```{splashtext}```".format_map({"NAME": NAME,"PREFIX": PREFIX,"ownerid": ownerid,"PING_LINE": PING_LINE,"showmemenabled": showmemenabled,"USERTRAIN_ENABLED": USERTRAIN_ENABLED,"song": song,"splashtext": splashtext}) - if lang == 'es': return "Nombre: {NAME} \nPrefijo: {PREFIX} \nID del propietario: {ownerid}\nLinea de ping: {PING_LINE} \nCompartir memoria habilitada: {showmemenabled} \nEntrenamiento de usuario habilitado: {USERTRAIN_ENABLED} \nCancion: {song} \nTexto de bienvenida: ```{splashtext}```".format_map({"NAME": NAME,"PREFIX": PREFIX,"ownerid": ownerid,"PING_LINE": PING_LINE,"showmemenabled": showmemenabled,"USERTRAIN_ENABLED": USERTRAIN_ENABLED,"song": song,"splashtext": splashtext}) - if lang == 'fi': return "Nimi: {NAME} \nEtuliite: {PREFIX} \nOmistajan ID: {ownerid}\nPing-linja: {PING_LINE} \nMuistin jako päällä: {showmemenabled} \nOppiminen käyttäjistä: {USERTRAIN_ENABLED}\nLaulu: {song} \nRoisketeksti: ```{splashtext}```".format_map({"NAME": NAME,"PREFIX": PREFIX,"ownerid": ownerid,"PING_LINE": PING_LINE,"showmemenabled": showmemenabled,"USERTRAIN_ENABLED": USERTRAIN_ENABLED,"song": song,"splashtext": splashtext}) - if lang == 'fr': return "Nom : {NAME} \nPréfixe : {PREFIX} \nID du propriétaire : {ownerid}\nLigne de ping : {PING_LINE} \nPartage de mémoire activé : {showmemenabled} \nEntraînement utilisateur activé : {USERTRAIN_ENABLED} \nChanson : {song} \nTexte de démarrage : ```{splashtext}```".format_map({"NAME": NAME,"PREFIX": PREFIX,"ownerid": ownerid,"PING_LINE": PING_LINE,"showmemenabled": showmemenabled,"USERTRAIN_ENABLED": USERTRAIN_ENABLED,"song": song,"splashtext": splashtext}) - if lang == 'it': return "Nome: {NAME} \nPrefisso: {PREFIX} \nID Proprietario: {ownerid}\nLinea ping: {PING_LINE} \nMemoria Condivisa Abilitata: {showmemenabled} \nAddestramento Utente Abilitato: {USERTRAIN_ENABLED}\nCanzone: {song} \nSplashtext: ```{splashtext}```".format_map({"NAME": NAME,"PREFIX": PREFIX,"ownerid": ownerid,"PING_LINE": PING_LINE,"showmemenabled": showmemenabled,"USERTRAIN_ENABLED": USERTRAIN_ENABLED,"song": song,"splashtext": splashtext}) - else: raise ValueError(f'Invalid language {lang}') -def no_image_available(lang:str|None=None): - """ - ### Locales - - En: **No images available!** - - Es: **No images available!** - - Fi: **No images available!** - - Fr: **No images available!** - - It: **No images available!** - """ - if not lang: lang=default_lang - if lang == 'en': return "No images available!" - if lang == 'es': return "No images available!" - if lang == 'fi': return "No images available!" - if lang == 'fr': return "No images available!" - if lang == 'it': return "No images available!" - else: raise ValueError(f'Invalid language {lang}') -def failed_generate_image(lang:str|None=None): - """ - ### Locales - - En: **Failed to generate an image** - - Es: **Failed to generate an image** - - Fi: **Failed to generate an image** - - Fr: **Failed to generate an image** - - It: **Failed to generate an image** - """ - if not lang: lang=default_lang - if lang == 'en': return "Failed to generate an image" - if lang == 'es': return "Failed to generate an image" - if lang == 'fi': return "Failed to generate an image" - if lang == 'fr': return "Failed to generate an image" - if lang == 'it': return "Failed to generate an image" - else: raise ValueError(f'Invalid language {lang}') -def markov_model_not_found(lang:str|None=None): - """ - ### Locales - - En: **Markov model not found!** - - Es: **Markov model not found!** - - Fi: **Markov model not found!** - - Fr: **Markov model not found!** - - It: **Markov model not found!** - """ - if not lang: lang=default_lang - if lang == 'en': return "Markov model not found!" - if lang == 'es': return "Markov model not found!" - if lang == 'fi': return "Markov model not found!" - if lang == 'fr': return "Markov model not found!" - if lang == 'it': return "Markov model not found!" - else: raise ValueError(f'Invalid language {lang}') -def blacklisted(lang:str|None=None): - """ - ### Locales - - En: **blacklisted** - - Es: **blacklisted** - - Fi: **blacklisted** - - Fr: **blacklisted** - - It: **blacklisted** - """ - if not lang: lang=default_lang - if lang == 'en': return "blacklisted" - if lang == 'es': return "blacklisted" - if lang == 'fi': return "blacklisted" - if lang == 'fr': return "blacklisted" - if lang == 'it': return "blacklisted" - else: raise ValueError(f'Invalid language {lang}') -def blacklisted_user(lang:str|None=None): - """ - ### Locales - - En: **Blacklisted user** - - Es: **Blacklisted user** - - Fi: **Blacklisted user** - - Fr: **Blacklisted user** - - It: **Blacklisted user** - """ - if not lang: lang=default_lang - if lang == 'en': return "Blacklisted user" - if lang == 'es': return "Blacklisted user" - if lang == 'fi': return "Blacklisted user" - if lang == 'fr': return "Blacklisted user" - if lang == 'it': return "Blacklisted user" - else: raise ValueError(f'Invalid language {lang}') -def edit_fail(lang:str|None=None): - """ - ### Locales - - En: **Failed to edit message** - - Es: **Failed to edit message** - - Fi: **Failed to edit message** - - Fr: **Failed to edit message** - - It: **Failed to edit message** - """ - if not lang: lang=default_lang - if lang == 'en': return "Failed to edit message" - if lang == 'es': return "Failed to edit message" - if lang == 'fi': return "Failed to edit message" - if lang == 'fr': return "Failed to edit message" - if lang == 'it': return "Failed to edit message" - else: raise ValueError(f'Invalid language {lang}') -def system_info(lang:str|None=None): - """ - ### Locales - - En: **System information** - - Es: **System information** - - Fi: **System information** - - Fr: **System information** - - It: **System information** - """ - if not lang: lang=default_lang - if lang == 'en': return "System information" - if lang == 'es': return "System information" - if lang == 'fi': return "System information" - if lang == 'fr': return "System information" - if lang == 'it': return "System information" - else: raise ValueError(f'Invalid language {lang}') -def cpu_info(cpu,lang:str|None=None): - """ - ### Locales - - En: **CPU: {cpu}** - - Es: **CPU: {cpu}** - - Fi: **CPU: {cpu}** - - Fr: **CPU: {cpu}** - - It: **CPU: {cpu}** - """ - if not lang: lang=default_lang - if lang == 'en': return "CPU: {cpu}".format_map({"cpu": cpu}) - if lang == 'es': return "CPU: {cpu}".format_map({"cpu": cpu}) - if lang == 'fi': return "CPU: {cpu}".format_map({"cpu": cpu}) - if lang == 'fr': return "CPU: {cpu}".format_map({"cpu": cpu}) - if lang == 'it': return "CPU: {cpu}".format_map({"cpu": cpu}) - else: raise ValueError(f'Invalid language {lang}') diff --git a/modules/logger.py b/modules/logger.py index 3b9d361..79d9029 100644 --- a/modules/logger.py +++ b/modules/logger.py @@ -1,28 +1,30 @@ import logging +import re from modules.globalvars import * - class GooberFormatter(logging.Formatter): - def __init__(self, colors: bool = True): # Disable colors for TXT output + 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.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, + logging.CRITICAL: PURPLE + self._format + RESET } def format(self, record: logging.LogRecord): + ansiescape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') if self.colors: - log_fmt = self.FORMATS.get(record.levelno) # Add colors + log_fmt = self.FORMATS.get(record.levelno) # Add colors else: - log_fmt = self._format # Just use the default format - + 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) + formatted = formatter.format(record) + if not self.colors: + formatted = ansiescape.sub('', formatted) + return formatted diff --git a/modules/markovmemory.py b/modules/markovmemory.py index cce5cf2..3235035 100644 --- a/modules/markovmemory.py +++ b/modules/markovmemory.py @@ -3,16 +3,9 @@ import json import markovify import pickle from modules.globalvars import * +from modules.volta.main import _ import logging -import modules.keys as k -from modules.settings import instance as settings_manager - -settings = settings_manager.settings - - logger = logging.getLogger("goober") - - # Get file size and line count for a given file path def get_file_info(file_path): try: @@ -23,59 +16,49 @@ def get_file_info(file_path): except Exception as e: return {"error": str(e)} - # Load memory data from file, or use default dataset if not loaded yet def load_memory(): data = [] # Try to load data from MEMORY_FILE try: - with open(settings["bot"]["active_memory"], "r") as f: + with open(MEMORY_FILE, "r") as f: data = json.load(f) except FileNotFoundError: pass return data - # Save memory data to MEMORY_FILE def save_memory(memory): - with open(settings["bot"]["active_memory"], "w") as f: + with open(MEMORY_FILE, "w") as f: json.dump(memory, f, indent=4) - -def train_markov_model(memory, additional_data=None) -> markovify.NewlineText | None: +def train_markov_model(memory, additional_data=None): if not memory: return None - filtered_memory = [line for line in memory if isinstance(line, str)] if additional_data: - filtered_memory.extend( - line for line in additional_data if isinstance(line, str) - ) - + filtered_memory.extend(line for line in additional_data if isinstance(line, str)) if not filtered_memory: return None - text = "\n".join(filtered_memory) model = markovify.NewlineText(text, state_size=2) return model - # Save the Markov model to a pickle file -def save_markov_model(model, filename="markov_model.pkl"): - with open(filename, "wb") as f: +def save_markov_model(model, filename='markov_model.pkl'): + with open(filename, 'wb') as f: pickle.dump(model, f) logger.info(f"Markov model saved to {filename}.") - # 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: - with open(filename, "rb") as f: + with open(filename, 'rb') as f: model = pickle.load(f) - logger.info(f"{k.model_loaded()} {filename}.{RESET}") + logger.info(f"{_('model_loaded')} {filename}.{RESET}") return model except FileNotFoundError: - logger.error(f"{filename} {k.not_found()}{RESET}") - return None + logger.error(f"{filename} {_('not_found')}{RESET}") + return None \ No newline at end of file diff --git a/modules/minigames.py b/modules/minigames.py new file mode 100644 index 0000000..0a74a82 --- /dev/null +++ b/modules/minigames.py @@ -0,0 +1,71 @@ +import random +import discord +from discord import ui, Interaction, TextStyle +from discord.ext import commands +import aiohttp +import asyncio +from modules.globalvars import bot +from modules.volta.main import _ + +# @bot.hybrid_command(description=_('minigames_guess_the_number')) +async def guessthenumber(ctx: commands.Context): + number = random.randint(1, 10) + class GuessModal(ui.Modal, title=_('minigames_guess_the_number')): + guess = ui.TextInput(label=_('minigames_your_guess'), style=TextStyle.short) + async def on_submit(self, interaction: Interaction): + try: + user_guess = int(self.guess.value) + except: + await interaction.response.send_message(_('minigames_invalid_number'), ephemeral=True) + return + if user_guess == number: + await interaction.response.send_message(_('minigames_correct'), ephemeral=True) + else: + await interaction.response.send_message(f"{_('minigames_wrong_number')} {number}.", ephemeral=True) + async def button_callback(interaction: Interaction): + await interaction.response.send_modal(GuessModal()) + button = ui.Button(label=_('minigames_guess_button'), style=discord.ButtonStyle.primary) + button.callback = button_callback + view = ui.View() + view.add_item(button) + await ctx.send(_('minigames_click_to_guess'), view=view) + +# @bot.hybrid_command(description=_('minigames_hangman')) nope nope nope fuck no nope no thanks no nuh uh not today nope +async def hangman(ctx: commands.Context): + async with aiohttp.ClientSession() as session: + async with session.get("https://random-word-api.herokuapp.com/word?number=1") as resp: + if resp.status != 200: + await ctx.send("Failed to get a random word.") + return + data = await resp.json() + word = data[0].lower() + print(word) + guessed_letters = set() + wrong_guesses = 0 + max_wrong = 6 + def display_word(): + return " ".join([c if c in guessed_letters else "_" for c in word]) + class GuessModal(ui.Modal, title=_('minigames_hangman_guess')): + letter = ui.TextInput(label=_('minigames_hangman_user_letter_guess'), style=TextStyle.short, max_length=1) + async def on_submit(self, interaction: Interaction): + nonlocal guessed_letters, wrong_guesses + guess = self.letter.value.lower() + if guess in guessed_letters: + await interaction.response.send_message(f"{_('minigames_hangman_already_guessed')}'{guess}'!", ephemeral=True) + return + guessed_letters.add(guess) + if guess not in word: + wrong_guesses += 1 + if all(c in guessed_letters for c in word): + await interaction.response.edit_message(content=f"{_('minigames_hangman_won')} **{word}**", view=None) + elif wrong_guesses >= max_wrong: + await interaction.response.edit_message(content=f"{_('minigames_hangman_lost')} **{word}**", view=None) + else: + await interaction.response.edit_message(content=_('minigames_hangman_game').format(display_word=display_word(),wrong_guesses=wrong_guesses,max_wrong=max_wrong), view=view) + async def button_callback(interaction: Interaction): + await interaction.response.send_modal(GuessModal()) + button = ui.Button(label=_('minigames_click_to_guess'), style=discord.ButtonStyle.primary) + button.callback = button_callback + view = ui.View() + view.add_item(button) + await ctx.send(_('minigames_hangman_game').format(display_word=display_word,wrong_guesses=wrong_guesses,max_wrong=max_wrong), view=view) \ No newline at end of file diff --git a/modules/permission.py b/modules/permission.py deleted file mode 100644 index 86b1dd4..0000000 --- a/modules/permission.py +++ /dev/null @@ -1,36 +0,0 @@ -from functools import wraps -import discord - -import discord.ext -import discord.ext.commands - -from modules.settings import instance as settings_manager -import logging - -logger = logging.getLogger("goober") - -settings = settings_manager.settings - - -class PermissionError(Exception): - pass - - -def requires_admin(): - async def wrapper(ctx: discord.ext.commands.Context): - if ctx.author.id not in settings["bot"]["owner_ids"]: - await ctx.send( - "You don't have the necessary permissions to run this command!" - ) - return False - - command = ctx.command - if not command: - logger.info(f"Unknown command ran {ctx.message}") - else: - logger.info( - f'Command {settings["bot"]["prefix"]}{command.name} @{ctx.author.name}' - ) - return True - - return discord.ext.commands.check(wrapper) diff --git a/modules/prestartchecks.py b/modules/prestartchecks.py index f4f4898..bb952f4 100644 --- a/modules/prestartchecks.py +++ b/modules/prestartchecks.py @@ -1,4 +1,5 @@ from modules.globalvars import * +from modules.volta.main import _, check_missing_translations import time import os import sys @@ -10,12 +11,6 @@ import re from spacy.util import is_package import importlib.metadata import logging -import modules.keys as k -from modules.settings import instance as settings_manager -from modules.sync_conenctor import instance as sync_hub - -settings = settings_manager.settings - logger = logging.getLogger("goober") @@ -26,8 +21,7 @@ try: import psutil except ImportError: psutilavaliable = False - logger.error(k.missing_requests_psutil()) - + logger.error(_('missing_requests_psutil')) def check_for_model(): if is_package("en_core_web_sm"): @@ -35,88 +29,80 @@ def check_for_model(): else: logger.info("Model is not installed.") - + def iscloned(): if os.path.exists(".git"): return True else: - logger.error(f"{k.not_cloned()}") + logger.error(f"{_('not_cloned')}") sys.exit(1) - def get_stdlib_modules(): - stdlib_path = pathlib.Path(sysconfig.get_paths()["stdlib"]) + stdlib_path = pathlib.Path(sysconfig.get_paths()['stdlib']) modules = set() - if hasattr(sys, "builtin_module_names"): + if hasattr(sys, 'builtin_module_names'): modules.update(sys.builtin_module_names) - for file in stdlib_path.glob("*.py"): - if file.stem != "__init__": + for file in stdlib_path.glob('*.py'): + if file.stem != '__init__': modules.add(file.stem) for folder in stdlib_path.iterdir(): - if folder.is_dir() and (folder / "__init__.py").exists(): + if folder.is_dir() and (folder / '__init__.py').exists(): modules.add(folder.name) - for file in stdlib_path.glob("*.*"): - if file.suffix in (".so", ".pyd"): + for file in stdlib_path.glob('*.*'): + if file.suffix in ('.so', '.pyd'): modules.add(file.stem) return modules - def check_requirements(): STD_LIB_MODULES = get_stdlib_modules() PACKAGE_ALIASES = { "discord": "discord.py", "better_profanity": "better-profanity", "dotenv": "python-dotenv", - "pil": "pillow", - "websocket": "websocket-client" + "pil": "pillow" } parent_dir = os.path.dirname(os.path.abspath(__file__)) - 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): - logger.error(f"{k.requirements_not_found(path=requirements_path)}") + logger.error(f"{(_('requirements_not_found')).format(path=requirements_path)}") return - with open(requirements_path, "r") as f: + with open(requirements_path, 'r') as f: lines = f.readlines() requirements = set() for line in lines: line = line.strip() - if line and not line.startswith("#"): - base_pkg = line.split("==")[0].lower() + if line and not line.startswith('#'): + base_pkg = line.split('==')[0].lower() aliased_pkg = PACKAGE_ALIASES.get(base_pkg, base_pkg) requirements.add(aliased_pkg) - 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 = [] for req in sorted(requirements): - if req in STD_LIB_MODULES or req == "modules": - print(k.std_lib_local_skipped(package=req)) + if req in STD_LIB_MODULES or req == 'modules': + print((_('std_lib_local_skipped')).format(package=req)) continue check_name = req.lower() if check_name in installed_packages: - logger.info(f"{k.ok_installed()} {check_name}") + logger.info(f"{_('ok_installed').format(package=check_name)} {check_name}") else: - logger.error(f"{k.missing_package()} {check_name} {k.missing_package2()}") + logger.error(f"{(_('missing_package')).format(package=check_name)} {check_name} {(_('missing_package2'))}") missing.append(check_name) if missing: - logger.error(k.missing_packages_detected()) + logger.error(_('missing_packages_detected')) for pkg in missing: print(f" - {pkg}") sys.exit(1) else: - logger.info(k.all_requirements_satisfied()) - + logger.info(_('all_requirements_satisfied')) def check_latency(): host = "1.1.1.1" @@ -136,103 +122,82 @@ def check_latency(): try: result = subprocess.run( - cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True ) if result.returncode == 0: match = re.search(latency_pattern, result.stdout) if match: latency_ms = float(match.group(1)) - logger.info(k.ping_to(host=host, latency=latency_ms)) + logger.info((_('ping_to')).format(host=host, latency=latency_ms)) if latency_ms > 300: - logger.warning(f"{k.high_latency()}") + logger.warning(f"{(_('high_latency'))}") else: - logger.warning(k.could_not_parse_latency()) + logger.warning((_('could_not_parse_latency'))) else: print(result.stderr) - logger.error(f"{k.ping_failed(host=host)}{RESET}") + logger.error(f"{(_('ping_failed')).format(host=host)}{RESET}") except Exception as e: - logger.error(k.error_running_ping(error=e)) - + logger.error((_('error_running_ping')).format(error=e)) def check_memory(): if psutilavaliable == False: return try: memory_info = psutil.virtual_memory() # type: ignore - total_memory = memory_info.total / (1024**3) - used_memory = memory_info.used / (1024**3) - free_memory = memory_info.available / (1024**3) + total_memory = memory_info.total / (1024 ** 3) + used_memory = memory_info.used / (1024 ** 3) + free_memory = memory_info.available / (1024 ** 3) - logger.info( - k.memory_usage( - 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: - print( - f"{YELLOW}{k.memory_above_90(percent=(used_memory / total_memory) * 100)}{RESET}" - ) - logger.info(k.total_memory(total=total_memory)) - logger.info(k.used_memory(used=used_memory)) + print(f"{YELLOW}{(_('memory_above_90')).format(percent=(used_memory / total_memory) * 100)}{RESET}") + logger.info((_('total_memory')).format(total=total_memory)) + logger.info((_('used_memory')).format(used=used_memory)) if free_memory < 1: - logger.warning(f"{k.low_free_memory(free=free_memory)}") - + logger.warning(f"{(_('low_free_memory')).format(free=free_memory)}") + sys.exit(1) except ImportError: - logger.error( - k.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(): if psutilavaliable == False: return - logger.info(k.measuring_cpu()) + logger.info((_('measuring_cpu'))) cpu_per_core = psutil.cpu_percent(interval=1, percpu=True) # type: ignore total_cpu = sum(cpu_per_core) / len(cpu_per_core) - logger.info(k.total_cpu_usage(usage=total_cpu)) - + logger.info((_('total_cpu_usage')).format(usage=total_cpu)) if total_cpu > 85: - logger.warning(f"{k.high_avg_cpu(usage=total_cpu)}") - + logger.warning(f"{(_('high_avg_cpu')).format(usage=total_cpu)}") if total_cpu > 95: - logger.error(k.really_high_cpu()) + logger.error(_('really_high_cpu')) sys.exit(1) - def check_memoryjson(): try: - logger.info( - k.memory_file( - size=os.path.getsize(settings["bot"]["active_memory"]) / (1024**2) - ) - ) - if os.path.getsize(settings["bot"]["active_memory"]) > 1_073_741_824: - logger.warning(f"{k.memory_file_large()}") + logger.info((_('memory_file')).format(size=os.path.getsize(MEMORY_FILE) / (1024 ** 2))) + if os.path.getsize(MEMORY_FILE) > 1_073_741_824: + logger.warning(f"{(_('memory_file_large'))}") try: - with open(settings["bot"]["active_memory"], "r", encoding="utf-8") as f: + with open(MEMORY_FILE, 'r', encoding='utf-8') as f: json.load(f) - except json.JSONDecodeError as e: - logger.error(f"{k.memory_file_corrupted(error=e)}") - logger.warning(f"{k.consider_backup_memory()}") - + logger.error(f"{(_('memory_file_corrupted')).format(error=e)}") + logger.warning(f"{(_('consider_backup_memory'))}") except UnicodeDecodeError as e: - logger.error(f"{k.memory_file_encoding(error=e)}") - logger.warning(f"{k.consider_backup_memory()}") - + logger.error(f"{(_('memory_file_encoding')).format(error=e)}") + logger.warning(f"{(_('consider_backup_memory'))}") except Exception as e: - logger.error(f"{k.error_reading_memory(error=e)}") + logger.error(f"{(_('error_reading_memory')).format(error=e)}") except FileNotFoundError: - logger.info(f"{k.memory_file_not_found()}") - + logger(f"{(_('memory_file_not_found'))}") def presskey2skip(timeout): - if os.name == "nt": + if os.name == 'nt': import msvcrt - start_time = time.time() while True: if msvcrt.kbhit(): @@ -261,44 +226,30 @@ def presskey2skip(timeout): time.sleep(0.1) finally: termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) - -def check_synchub(): - if not sync_hub.connected: - logger.warning("Sync hub not connected properly! The bot will not be able to react to messages, or create breaking news unless you disable synchub in settings") - else: - logger.info("Sync hub is conencted") - beta = beta - - def start_checks(): - if settings["disable_checks"]: - logger.warning(f"{k.checks_disabled()}") + if CHECKS_DISABLED == "True": + logger.warning(f"{(_('checks_disabled'))}") return - - logger.info(k.running_prestart_checks()) + logger.info(_('running_prestart_checks')) check_for_model() iscloned() + check_missing_translations() check_requirements() check_latency() check_memory() check_memoryjson() check_cpu() - check_synchub() if os.path.exists(".env"): pass else: - logger.warning(f"{k.env_file_not_found()}") + logger.warning(f"{(_('env_file_not_found'))}") sys.exit(1) if beta == True: - logger.warning( - f"this build isnt finished yet, some things might not work as expected" - ) + logger.warning(f"this build isnt finished yet, some things might not work as expected") else: pass - logger.info(k.continuing_in_seconds(seconds=5)) + logger.info(_('continuing_in_seconds').format(seconds=5)) presskey2skip(timeout=5) - os.system("cls" if os.name == "nt" else "clear") - - with open(settings["splash_text_loc"], "r") as f: - print("".join(f.readlines())) + os.system('cls' if os.name == 'nt' else 'clear') + print(splashtext) \ No newline at end of file diff --git a/modules/sentenceprocessing.py b/modules/sentenceprocessing.py index 839c36a..95a703b 100644 --- a/modules/sentenceprocessing.py +++ b/modules/sentenceprocessing.py @@ -1,15 +1,12 @@ import re -import discord.ext -import discord.ext.commands from modules.globalvars import * +from modules.volta.main import _ + import spacy from spacy.tokens import Doc from spacytextblob.spacytextblob import SpacyTextBlob -import discord -import modules.keys as k import logging - logger = logging.getLogger("goober") @@ -17,13 +14,12 @@ def check_resources(): try: nlp = spacy.load("en_core_web_sm") except OSError: - logging.critical(k.spacy_model_not_found()) - spacy.cli.download("en_core_web_sm") # type: ignore + logging.critical((_('spacy_model_not_found'))) + spacy.cli.download("en_core_web_sm") nlp = spacy.load("en_core_web_sm") if "spacytextblob" not in nlp.pipe_names: nlp.add_pipe("spacytextblob") - logger.info(k.spacy_initialized()) - + logger.info((_('spacy_initialized'))) check_resources() @@ -31,65 +27,49 @@ nlp = spacy.load("en_core_web_sm") nlp.add_pipe("spacytextblob") Doc.set_extension("polarity", getter=lambda doc: doc._.blob.polarity) - def is_positive(sentence): doc = nlp(sentence) sentiment_score = doc._.polarity # from spacytextblob - debug_message = f"{k.sentence_positivity()} {sentiment_score}{RESET}" + debug_message = f"{(_('sentence_positivity'))} {sentiment_score}{RESET}" 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 - - -async def send_message( - ctx: discord.ext.commands.Context, - message: str | None = None, - embed: discord.Embed | None = None, - file: discord.File | None = None, - edit: bool = False, - message_reference: discord.Message | None = None, -) -> discord.Message | None: - - sent_message: discord.Message | None = None + 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 +async def send_message(ctx, message=None, embed=None, file=None, edit=False, message_reference=None): if edit and message_reference: try: await message_reference.edit(content=message, embed=embed) - return message_reference except Exception as e: - await ctx.send(f"{k.edit_fail()} {e}") - return None - - if embed: - sent_message = await ctx.send(embed=embed, content=message) - elif file: - sent_message = await ctx.send(file=file, content=message) + await ctx.send(f"{RED}{(_('edit_fail'))} {e}{RESET}") else: - sent_message = await ctx.send(content=message) - - return sent_message - - -def append_mentions_to_18digit_integer(message): - pattern = r"\b\d{18}\b" - return re.sub(pattern, lambda match: "", message) - + if hasattr(ctx, "respond"): + sent_message = None + if embed: + sent_message = await ctx.respond(embed=embed, ephemeral=False) + elif message: + sent_message = await ctx.respond(message, ephemeral=False) + if file: + sent_message = await ctx.respond(file=file, ephemeral=False) + else: + sent_message = None + if embed: + sent_message = await ctx.send(embed=embed) + elif message: + sent_message = await ctx.send(message) + if file: + sent_message = await ctx.send(file=file) + return sent_message def preprocess_message(message): - message = append_mentions_to_18digit_integer(message) + message = message doc = nlp(message) tokens = [token.text for token in doc if token.is_alpha or token.is_digit] return " ".join(tokens) - def improve_sentence_coherence(sentence): - return re.sub(r"\bi\b", "I", sentence) - + return "" def rephrase_for_coherence(sentence): - words = sentence.split() - coherent_sentence = " ".join(words) + coherent_sentence = sentence return coherent_sentence diff --git a/modules/settings.py b/modules/settings.py deleted file mode 100644 index 8943453..0000000 --- a/modules/settings.py +++ /dev/null @@ -1,155 +0,0 @@ -import json -import os -from typing import Dict, List, Literal, Mapping, Any, TypedDict -from modules.keys import Language -import logging -import copy - -logger = logging.getLogger("goober") - -ActivityType = Literal["listening", "playing", "streaming", "competing", "watching"] - -class SyncHub(TypedDict): - url: str - enabled: bool - -class Activity(TypedDict): - content: str - type: ActivityType - - -class MiscBotOptions(TypedDict): - ping_line: str - activity: Activity - positive_gifs: List[str] - block_profanity: bool - - -class BotSettings(TypedDict): - prefix: str - owner_ids: List[int] - blacklisted_users: List[int] - user_training: bool - allow_show_mem_command: bool - react_to_messages: bool - misc: MiscBotOptions - enabled_cogs: List[str] - active_memory: str - sync_hub: SyncHub - - -class SettingsType(TypedDict): - bot: BotSettings - locale: Language - name: str - auto_update: bool - disable_checks: bool - splash_text_loc: str - cog_settings: Dict[str, Mapping[Any, Any]] - - -class AdminLogEvent(TypedDict): - messageId: int - author: int - target: str | int - action: Literal["del", "add", "set"] - change: Literal["owner_ids", "blacklisted_users", "enabled_cogs"] - - -class Settings: - def __init__(self) -> None: - global instance - instance = self - - self.path: str = os.path.join(".", "settings", "settings.json") - - if not os.path.exists(self.path): - logger.critical( - f"Missing settings file from {self.path}! Did you forget to copy settings.example.json?" - ) - raise ValueError("settings.json file does not exist!") - - self.settings: SettingsType - self.original_settings: SettingsType - - with open(self.path, "r") as f: - self.__kv_store: dict = json.load(f) - - self.settings = SettingsType(self.__kv_store) # type: ignore - self.original_settings = copy.deepcopy(self.settings) - - self.log_path: str = os.path.join(".", "settings", "admin_logs.json") - - self.migrate() - - def migrate(self): - active_song: str | None = ( - self.settings.get("bot", {}).get("misc", {}).get("active_song") - ) - - if active_song: - logger.warning("Found deprecated active_song, migrating") - - self.settings["bot"]["misc"]["activity"] = { - "content": active_song, - "type": "listening", - } - - del self.settings["bot"]["misc"]["active_song"] # type: ignore - - sync_hub: SyncHub | None = self.settings.get("bot", {}).get("sync_hub") - - if not sync_hub: - logger.warning("Adding sync hub settings") - self.settings["bot"]["sync_hub"] = { - "enabled": True, - "url": "ws://goober.frii.site" - } - - self.commit() - - def reload_settings(self) -> None: - with open(self.path, "r") as f: - self.__kv_store: dict = json.load(f) - - self.settings = SettingsType(self.__kv_store) # type: ignore - self.original_settings = copy.deepcopy(self.settings) - - def commit(self) -> None: - with open(self.path, "w") as f: - json.dump(self.settings, f, ensure_ascii=False, indent=4) - - self.original_settings = self.settings - - def discard(self) -> None: - self.settings = self.original_settings - - def get_plugin_settings( - self, plugin_name: str, default: Mapping[Any, Any] - ) -> Mapping[Any, Any]: - return self.settings["cog_settings"].get(plugin_name, default) - - def set_plugin_setting( - self, plugin_name: str, new_settings: Mapping[Any, Any] - ) -> None: - """Changes a plugin setting. Commits changes""" - self.settings["cog_settings"][plugin_name] = new_settings - - self.commit() - - def add_admin_log_event(self, event: AdminLogEvent): - if not os.path.exists(self.log_path): - logger.warning("Admin log doesn't exist!") - with open(self.log_path, "w") as f: - json.dump([], f) - - with open(self.log_path, "r") as f: - logs: List[AdminLogEvent] = json.load(f) - - logs.append(event) - - with open(self.log_path, "w") as f: - json.dump(logs, f, ensure_ascii=False, indent=4) - - -instance: Settings = Settings() diff --git a/modules/sync_conenctor.py b/modules/sync_conenctor.py deleted file mode 100644 index 588c772..0000000 --- a/modules/sync_conenctor.py +++ /dev/null @@ -1,94 +0,0 @@ -import websocket -from modules.settings import instance as settings_manager -import logging - - -logger = logging.getLogger("goober") -settings = settings_manager.settings - -class SyncConnector: - def __init__(self, url: str): - self.connected: bool = True - self.url = url - self.client: websocket.WebSocket | None = None - - self.try_to_connect() - - def __connect(self) -> bool: - try: - self.client = websocket.create_connection(self.url) - except OSError as e: - logger.debug(e) - logger.debug(e.strerror) - return False - - return True - - def try_to_connect(self) -> bool: - if self.__connect(): - logger.info("Connected to sync hub!") - self.connected = True - else: - logger.error("Failed to connect to sync hub.. Disabling for the time being") - self.connected = False - - return self.connected - - - def can_react(self, message_id: int) -> bool: - """ - Checks if goober can react to a messsage - """ - - return self.can_event(message_id, "react") - - - def can_breaking_news(self, message_id: int) -> bool: - """ - Checks if goober can send a breaking news alert - """ - - return self.can_event(message_id, "breaking_news") - - - def can_event(self, message_id: int, event: str, retry_depth: int = 0) -> bool: - """ - Checks if goober can send a breaking news alert - """ - - logger.debug(f"Checking {event} for message {message_id}") - - if not settings["bot"]["sync_hub"]["enabled"]: - logger.info("Skipping sync hub check") - return True - - if retry_depth > 2: - logger.error("Too many retries. Returning false") - return False - - if not self.client: - logger.error("Client no connected") - return False - - if not self.connected: - logger.warning("Not connected to sync hub.. Returning False to avoid conflicts") - return False - - try: - self.client.send(f"event={event};ref={message_id}") - return self.client.recv() == "unhandled" - except ConnectionResetError: - logger.error("Connection to sync hub reset! Retrying...") - - if not self.__connect(): - logger.error("Failed to reconnect to sync hub... Disabling") - self.connected = False - return False - - logger.info("Managed to reconnect to sync hub! Retrying requests") - self.connected = True - return self.can_event(message_id, event, retry_depth+1) - - - -instance = SyncConnector(settings["bot"]["sync_hub"]["url"]) \ No newline at end of file diff --git a/modules/unhandledexception.py b/modules/unhandledexception.py index 1118329..9521e72 100644 --- a/modules/unhandledexception.py +++ b/modules/unhandledexception.py @@ -1,29 +1,25 @@ import sys import traceback import os -from modules.settings import instance as settings_manager -import logging -from modules.globalvars import RED, RESET -import modules.keys as k - -settings = settings_manager.settings -logger = logging.getLogger("goober") - +from modules.globalvars import RED, RESET, splashtext +from modules.volta.main import _ def handle_exception(exc_type, exc_value, exc_traceback, *, context=None): - os.system("cls" if os.name == "nt" else "clear") - + os.system('cls' if os.name == 'nt' else 'clear') + if issubclass(exc_type, KeyboardInterrupt): sys.__excepthook__(exc_type, exc_value, exc_traceback) return - with open(settings["splash_text_loc"], "r") as f: - print("".join(f.readlines())) - + print(splashtext) print(f"{RED}=====BEGINNING OF TRACEBACK====={RESET}") traceback.print_exception(exc_type, exc_value, exc_traceback) print(f"{RED}========END OF TRACEBACK========{RESET}") - print(f"{RED}{k.unhandled_exception()}{RESET}") + print(f"{RED}{_('unhandled_exception')}{RESET}") + if context: print(f"{RED}Context: {context}{RESET}") + + + diff --git a/modules/version.py b/modules/version.py new file mode 100644 index 0000000..a5c1132 --- /dev/null +++ b/modules/version.py @@ -0,0 +1,105 @@ +from modules.volta.main import _ +from modules.globalvars import * +import requests +import subprocess +import sys +import logging +import json +import time +import random +logger = logging.getLogger("goober") +launched = False + +# Run a shell command and return its output +def run_cmd(cmd): + result = subprocess.run(cmd, shell=True, capture_output=True, text=True) + return result.stdout.strip() + +# Check if the remote branch is ahead of the local branch +def is_remote_ahead(branch='main', remote='origin'): + run_cmd(f'git fetch {remote}') + count = run_cmd(f'git rev-list --count HEAD..{remote}/{branch}') + return int(count) > 0 + +# Automatically update the local repository if the remote is ahead +def auto_update(branch='main', remote='origin'): + if launched == True: + print(_("already_started")) + return + if AUTOUPDATE != "True": + pass # Auto-update is disabled + if is_remote_ahead(branch, remote): + print(_( "remote_ahead").format(remote=remote, branch=branch)) + pull_result = run_cmd(f'git pull {remote} {branch}') + logger.info(pull_result) + logger.info(_( "please_restart")) + sys.exit(0) + else: + logger.info(_( "local_ahead").format(remote=remote, branch=branch)) + +def get_latest_version_info(): + try: + unique_suffix = f"{int(time.time())}_{random.randint(0, 9999)}" + url = f"{UPDATE_URL}?_={unique_suffix}" + + curl_cmd = [ + "curl", + "-s", + "-H", "Cache-Control: no-cache", + "-H", "Pragma: no-cache", + url + ] + + result = subprocess.run(curl_cmd, capture_output=True, text=True, timeout=5) + content = result.stdout + + if result.returncode != 0: + logger.error(f"curl failed with return code {result.returncode}") + return None + + try: + data = json.loads(content) + return data + except json.JSONDecodeError: + logger.error("JSON decode failed") + logger.error(content[:500]) + return None + + except Exception as e: + logger.error(f"Exception in get_latest_version_info: {e}") + return None + +# Check if an update is available and perform update if needed +def check_for_update(): + global latest_version, local_version, launched + + latest_version_info = get_latest_version_info() + if not latest_version_info: + logger.error(f"{_('fetch_update_fail')}") + return None, None + + latest_version = latest_version_info.get("version") + os.environ['gooberlatest_version'] = latest_version + download_url = latest_version_info.get("download_url") + + if not latest_version or not download_url: + logger.error(f"{RED}{_('invalid_server')}{RESET}") + return None, None + # Check if local_version is valid + if local_version == "0.0.0" or None: + logger.error(f"{RED}{_('cant_find_local_version')}{RESET}") + return + # Compare local and latest versions + if local_version < latest_version: + logger.info(f"{YELLOW}{_('new_version').format(latest_version=latest_version, local_version=local_version)}{RESET}") + logger.info(f"{YELLOW}{_('changelog').format(VERSION_URL=VERSION_URL)}{RESET}") + auto_update() + elif beta == True: + logger.warning(f"You are running an \"unstable\" version of Goober, do not expect it to work properly.\nVersion {local_version}\nServer: {latest_version}{RESET}") + elif local_version > latest_version: + logger.warning(f"{_('modification_warning')}") + elif local_version == latest_version: + logger.info(f"{_('latest_version')} {local_version}") + logger.info(f"{_('latest_version2').format(VERSION_URL=VERSION_URL)}\n\n") + launched = True + return latest_version \ No newline at end of file diff --git a/modules/volta/main.py b/modules/volta/main.py new file mode 100644 index 0000000..4b62144 --- /dev/null +++ b/modules/volta/main.py @@ -0,0 +1,220 @@ +# If you're seeing this after cloning the Goober repo, note that this is a standalone module for translations. +# While it's used by Goober Core, it lives in its own repository and should not be modified here. +# For updates or contributions, visit: https://github.com/gooberinc/volta +# Also, Note to self: Add more comments it needs more love +import os +import locale +import json +import pathlib +import threading +import platform +import sys +import time +from dotenv import load_dotenv +from functools import lru_cache + +ANSI = "\033[" +RED = f"{ANSI}31m" +GREEN = f"{ANSI}32m" +YELLOW = f"{ANSI}33m" +DEBUG = f"{ANSI}1;30m" +RESET = f"{ANSI}0m" + +LOCALE = os.getenv("LOCALE") +module_dir = pathlib.Path(__file__).parent.parent +working_dir = pathlib.Path.cwd() +EXCLUDE_DIRS = {'.git', '__pycache__'} + +locales_dirs = [] +ENGLISH_MISSING = False +FALLBACK_LOCALE = "en" +if os.getenv("fallback_locale"): + FALLBACK_LOCALE = os.getenv("fallback_locale") +def find_locales_dirs(base_path): + found = [] + for root, dirs, files in os.walk(base_path): + dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS] + + if 'locales' in dirs: + locales_path = pathlib.Path(root) / 'locales' + found.append(locales_path) + dirs.remove('locales') + return found + +def find_dotenv(start_path: pathlib.Path) -> pathlib.Path | None: + current = start_path.resolve() + while current != current.parent: + candidate = current / ".env" + if candidate.exists(): + return candidate + current = current.parent + return None + +env_path = find_dotenv(pathlib.Path(__file__).parent) +if env_path: + load_dotenv(dotenv_path=env_path) + print(f"[VOLTA] {GREEN}Loaded .env from {env_path}{RESET}") +else: + print(f"[VOLTA] {YELLOW}No .env file found from {__file__} upwards.{RESET}") + +locales_dirs.extend(find_locales_dirs(module_dir)) +if working_dir != module_dir: + locales_dirs.extend(find_locales_dirs(working_dir)) + +translations = {} +_file_mod_times = {} + + +def get_system_locale(): + system = platform.system() # fallback incase locale isnt set + if system == "Windows": + lang, _ = locale.getdefaultlocale() + return lang or os.getenv("LANG") + elif system == "Darwin": + try: + import subprocess + result = subprocess.run( + ["defaults", "read", "-g", "AppleLocale"], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True + ) + return result.stdout.strip() or locale.getdefaultlocale()[0] + except Exception: + return locale.getdefaultlocale()[0] + elif system == "Linux": + return ( + os.getenv("LC_ALL") or + os.getenv("LANG") or + locale.getdefaultlocale()[0] + ) + return locale.getdefaultlocale()[0] + + +def load_translations(): + global translations, _file_mod_times + translations.clear() + _file_mod_times.clear() + + for locales_dir in locales_dirs: + for filename in os.listdir(locales_dir): + if filename.endswith(".json"): + lang_code = filename[:-5] + file_path = locales_dir / filename + try: + with open(file_path, "r", encoding="utf-8") as f: + data = json.load(f) + if lang_code not in translations: + translations[lang_code] = {} + translations[lang_code].update(data) + _file_mod_times[(lang_code, file_path)] = file_path.stat().st_mtime + except Exception as e: + print(f"[VOLTA] {RED}Failed loading {file_path}: {e}{RESET}") + +def reload_if_changed(): + while True: + for (lang_code, file_path), last_mtime in list(_file_mod_times.items()): + try: + current_mtime = file_path.stat().st_mtime + if current_mtime != last_mtime: + print(f"[VOLTA] {RED}Translation file changed: {file_path}, reloading...{RESET}") + _lookup_translation.cache_clear() + load_translations() + break + except FileNotFoundError: + print(f"[VOLTA] {RED}Translation file removed: {file_path}{RESET}") + _file_mod_times.pop((lang_code, file_path), None) + if lang_code in translations: + translations.pop(lang_code, None) + +def set_language(lang: str): + global LOCALE, ENGLISH_MISSING + if not LOCALE: + LOCALE = get_system_locale() + elif lang in translations: + LOCALE = lang + else: + print(f"[VOLTA] {RED}Language '{lang}' not found, defaulting to 'en'{RESET}") + if FALLBACK_LOCALE in translations: + LOCALE = FALLBACK_LOCALE + else: + print(f"[VOLTA] {RED}The fallback translations cannot be found! No fallback available.{RESET}") + ENGLISH_MISSING = True + _lookup_translation.cache_clear() + +def check_missing_translations(LOCALE=LOCALE): + global ENGLISH_MISSING + load_translations() + if FALLBACK_LOCALE not in translations: + print(f"[VOLTA] {RED}Fallback translations ({FALLBACK_LOCALE}.json) missing from assets/locales.{RESET}") + ENGLISH_MISSING = True + return + if LOCALE == "en": + print("[VOLTA] Locale is English, skipping missing key check.") + return + + + en_keys = set(translations.get("en", {}).keys()) + locale_keys = set(translations.get(LOCALE, {}).keys()) + + missing_keys = en_keys - locale_keys + total_keys = len(en_keys) + missing_count = len(missing_keys) + + if missing_count > 0: + percent_missing = (missing_count / total_keys) * 100 + if percent_missing == 100: + print(f"[VOLTA] {YELLOW}Warning: All keys are missing in locale '{LOCALE}'! Defaulting back to {FALLBACK_LOCALE}{RESET}") + set_language(FALLBACK_LOCALE) + elif percent_missing > 0: + print(f"[VOLTA] {YELLOW}Warning: {missing_count}/{total_keys} keys missing in locale '{LOCALE}' ({percent_missing:.1f}%)!{RESET}") + for key in sorted(missing_keys): + print(f" - {key}") + time.sleep(2) + else: + print(f"[VOLTA] All translation keys present for locale: {LOCALE}") + +printedsystemfallback = False + +@lru_cache(maxsize=600) +def _lookup_translation(lang: str, key: str): + return translations.get(lang, {}).get(key) + +def get_translation(lang: str, key: str): + global printedsystemfallback + if ENGLISH_MISSING: + return f"[VOLTA] {RED}No fallback available!{RESET}" + + val = _lookup_translation(lang, key) + if val: + return val + sys_lang = get_system_locale().split("_")[0] if get_system_locale() else None + if sys_lang and sys_lang != lang: + sys_val = _lookup_translation(sys_lang, key) + if sys_val: + if not printedsystemfallback: + print(f"[VOLTA] {YELLOW}Falling back to system language {sys_lang}!{RESET}") + printedsystemfallback = True + return sys_val + fallback_val = _lookup_translation(FALLBACK_LOCALE, key) + if fallback_val: + print(f"[VOLTA] {YELLOW}Missing key: '{key}' in '{lang}', falling back to fallback locale '{FALLBACK_LOCALE}'{RESET}") + return fallback_val + + return f"[VOLTA] {YELLOW}Missing key: '{key}' in all locales!{RESET}" + +def _(key: str) -> str: + return get_translation(LOCALE, key) + +load_translations() + +watchdog_thread = threading.Thread(target=reload_if_changed, daemon=True) +watchdog_thread.start() + +if __name__ == '__main__': + import argparse + parser = argparse.ArgumentParser() + parser.add_argument("LOCALE", help="Locale to validate") + args = parser.parse_args() + print("[VOLTA] Validating all locales....") + check_missing_translations(LOCALE=f"{args.LOCALE}") diff --git a/replace_volta.py b/replace_volta.py deleted file mode 100644 index 3c7b86f..0000000 --- a/replace_volta.py +++ /dev/null @@ -1,51 +0,0 @@ -import os -import re - -folder_path = "." - -# Real trap regex 😮‍💨 — group(1)=key, group(2)=format args (optional) -pattern = re.compile( - r""" - (? None: - logger.info("Forcefully updating...") - stash = subprocess.run(["git", "stash"], capture_output=True) - logger.info(stash) - pull = subprocess.run(["git", "pull", "origin", "main"], check=True, capture_output=True) - logger.info(pull) -