From c565f962c5c610b33aabe4dd09e4188c050e0681 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Wed, 16 Jul 2025 20:11:08 +0200 Subject: [PATCH 01/29] sigh --- .../{webserver.py.disabled => webserver.py} | 0 bot.py | 5 ++-- modules/image.py | 29 ++++++++++--------- 3 files changed, 19 insertions(+), 15 deletions(-) rename assets/cogs/{webserver.py.disabled => webserver.py} (100%) diff --git a/assets/cogs/webserver.py.disabled b/assets/cogs/webserver.py similarity index 100% rename from assets/cogs/webserver.py.disabled rename to assets/cogs/webserver.py diff --git a/bot.py b/bot.py index 74cf0b7..0c40592 100644 --- a/bot.py +++ b/bot.py @@ -225,7 +225,7 @@ async def talk(ctx: commands.Context, sentence_size: int = 5) -> None: # Command: Generate an image @bot.hybrid_command(description=f"{(_('command_desc_help'))}") -async def impact(ctx: commands.Context) -> None: +async def impact(ctx: commands.Context, text: Optional[str] = None) -> None: assets_folder: str = "assets/images" temp_input: Optional[str] = None @@ -259,7 +259,8 @@ async def impact(ctx: commands.Context) -> None: shutil.copy(fallback_image, temp_input) input_path = temp_input - output_path: Optional[str] = await gen_meme(input_path) + 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): diff --git a/modules/image.py b/modules/image.py index 58180d5..d3807df 100644 --- a/modules/image.py +++ b/modules/image.py @@ -37,7 +37,7 @@ def split_text_to_fit(text, font, max_width, draw): 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 @@ -52,19 +52,22 @@ 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: From 7c6be59dc96a1987bb2c74fb25ef474fcc58c089 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Thu, 17 Jul 2025 15:22:39 +0200 Subject: [PATCH 02/29] yeah no lastfm gets the status change so fix later i guess --- assets/cogs/{lastfm.py => lastfm.py.disabled} | 0 bot.py | 14 +++++++++++--- example.env | 1 + modules/globalvars.py | 3 ++- 4 files changed, 14 insertions(+), 4 deletions(-) rename assets/cogs/{lastfm.py => lastfm.py.disabled} (100%) diff --git a/assets/cogs/lastfm.py b/assets/cogs/lastfm.py.disabled similarity index 100% rename from assets/cogs/lastfm.py rename to assets/cogs/lastfm.py.disabled diff --git a/bot.py b/bot.py index 0c40592..4c7f817 100644 --- a/bot.py +++ b/bot.py @@ -113,6 +113,7 @@ async def on_ready() -> None: global launched global slash_commands_enabled global NAME + global status folder_name: str = "cogs" if launched: @@ -136,8 +137,15 @@ async def on_ready() -> None: quit() if not song: - return - await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=f"{song}")) + return + + 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 @@ -372,7 +380,7 @@ async def on_message(message: discord.Message) -> None: if str(message.author.id) in BLACKLISTED_USERS: return - + 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) diff --git a/example.env b/example.env index fe40023..45cfd9b 100644 --- a/example.env +++ b/example.env @@ -11,6 +11,7 @@ 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=" diff --git a/modules/globalvars.py b/modules/globalvars.py index 2281388..5241cca 100644 --- a/modules/globalvars.py +++ b/modules/globalvars.py @@ -35,6 +35,7 @@ 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" @@ -44,7 +45,7 @@ MEMORY_LOADED_FILE = "MEMORY_LOADED" # is this still even used?? okay just check ALIVEPING = os.getenv("ALIVEPING") AUTOUPDATE = os.getenv("AUTOUPDATE") # IGNOREWARNING = False # is this either??? i don't think so? -song = os.getenv("song") +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 From cb45a9fffc94946246220f057df12f3bfa32c5fb Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Thu, 17 Jul 2025 15:33:32 +0200 Subject: [PATCH 03/29] Update todo.txt --- todo.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/todo.txt b/todo.txt index 428c6c8..9a1a0f3 100644 --- a/todo.txt +++ b/todo.txt @@ -1,4 +1,2 @@ -- fix missing translations in some cases - revamp wiki -- clean the rest - alot From 016e907d39c2f77b3b8afa76001b2d05b69dab22 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Thu, 17 Jul 2025 17:42:21 +0200 Subject: [PATCH 04/29] probably wont break anything (foreshadowing) --- modules/globalvars.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/globalvars.py b/modules/globalvars.py index 5241cca..8ff634e 100644 --- a/modules/globalvars.py +++ b/modules/globalvars.py @@ -44,10 +44,8 @@ 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") -# IGNOREWARNING = False # is this either??? i don't think so? 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" From 53e0eb5289bd2102d745150c6d8bf3e1069e9565 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Thu, 17 Jul 2025 23:35:59 +0200 Subject: [PATCH 05/29] bump it to 2.3.4 --- modules/globalvars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/globalvars.py b/modules/globalvars.py index 8ff634e..0a5a57b 100644 --- a/modules/globalvars.py +++ b/modules/globalvars.py @@ -48,7 +48,7 @@ song = os.getenv("SONG") arch = platform.machine() launched = False latest_version = "0.0.0" -local_version = "2.3.3" +local_version = "2.3.4" os.environ['gooberlocal_version'] = local_version REACT = os.getenv("REACT") if get_git_branch() == "dev": From 54b8bf4c5937619e2bfc494347b22aeead76521c Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Thu, 17 Jul 2025 23:48:41 +0200 Subject: [PATCH 06/29] change submodule path --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From dec83f1513de220eb0193e02a1f7a92aa986c3ea Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 00:49:04 +0200 Subject: [PATCH 07/29] why did they call the song rule #34 if it has nothing to do with rule34 --- assets/cogs/README.md | 3 - assets/cogs/tf.py.disabled | 155 ---------------------------------- bot.py | 9 +- modules/sentenceprocessing.py | 11 +-- 4 files changed, 5 insertions(+), 173 deletions(-) delete mode 100644 assets/cogs/tf.py.disabled 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/tf.py.disabled b/assets/cogs/tf.py.disabled deleted file mode 100644 index 28609e6..0000000 --- a/assets/cogs/tf.py.disabled +++ /dev/null @@ -1,155 +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 - from tensorflow import keras - from tensorflow.keras.preprocessing.text import Tokenizer - from tensorflow.keras.preprocessing.sequence import pad_sequences - from tensorflow.keras.models import Sequential, load_model - from tensorflow.keras.layers import Embedding, LSTM, Dense - from tensorflow.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)) \ No newline at end of file diff --git a/bot.py b/bot.py index 4c7f817..8af816b 100644 --- a/bot.py +++ b/bot.py @@ -41,8 +41,6 @@ from discord.ext import commands from discord import app_commands from discord import Colour, Embed, File, Interaction, Message from discord.abc import Messageable - -from better_profanity import profanity from discord.ext import commands from modules.volta.main import _, set_language @@ -386,14 +384,11 @@ async def on_message(message: discord.Message) -> None: await bot.process_commands(message) return - if profanity.contains_profanity(message.content): - return - if message.content: 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 = { diff --git a/modules/sentenceprocessing.py b/modules/sentenceprocessing.py index 993ba90..95a703b 100644 --- a/modules/sentenceprocessing.py +++ b/modules/sentenceprocessing.py @@ -61,20 +61,15 @@ async def send_message(ctx, message=None, embed=None, file=None, edit=False, mes sent_message = await ctx.send(file=file) return sent_message -def append_mentions_to_18digit_integer(message): - pattern = r'\b\d{18}\b' - return re.sub(pattern, lambda match: "", 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 From 280df4f5e012a94d7bf656c3bb2c242a10dbd477 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 01:26:55 +0200 Subject: [PATCH 08/29] fuck it one liner --- bot.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/bot.py b/bot.py index 8af816b..6833874 100644 --- a/bot.py +++ b/bot.py @@ -68,11 +68,7 @@ slash_commands_enabled: bool = False intents: discord.Intents = discord.Intents.default() intents.messages = 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) -) +bot: commands.Bot = commands.Bot(command_prefix=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] = load_memory() From ae2d565004b7f32314f1af605be77ebd0300d1d1 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 01:29:31 +0200 Subject: [PATCH 09/29] dynamic git URL --- bot.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 6833874..9315b3a 100644 --- a/bot.py +++ b/bot.py @@ -459,6 +459,17 @@ async def ping(ctx: commands.Context) -> None: 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: @@ -468,7 +479,7 @@ async def about(ctx: commands.Context) -> None: 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"Github", value=f"https://github.com/gooberinc/goober") + embed.add_field(name=f"Github", value=get_git_remote_url()) await send_message(ctx, embed=embed) From 4a695a7baca6c4c5d99d743a68032ef2a1371108 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 01:30:57 +0200 Subject: [PATCH 10/29] added os why not fuck it amirite --- bot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 9315b3a..45da38d 100644 --- a/bot.py +++ b/bot.py @@ -7,8 +7,8 @@ import traceback import subprocess import tempfile import shutil -import uuid import asyncio +import platform import sys from typing import List, Dict, Set, Optional, Tuple, Any, Union, Callable, Coroutine, TypeVar, Type import logging @@ -480,6 +480,7 @@ async def about(ctx: commands.Context) -> None: 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"Github", value=get_git_remote_url()) + embed.add_field(name=f"OS", value=platform.platform) await send_message(ctx, embed=embed) From cbe7fe201fb4f18e3826cbf65e807e6f11a9bd60 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 01:31:46 +0200 Subject: [PATCH 11/29] dumbass forgot to add () --- bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 45da38d..4178468 100644 --- a/bot.py +++ b/bot.py @@ -480,7 +480,7 @@ async def about(ctx: commands.Context) -> None: 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"Github", value=get_git_remote_url()) - embed.add_field(name=f"OS", value=platform.platform) + embed.add_field(name=f"OS", value=platform.platform()) await send_message(ctx, embed=embed) From 01b40f8b58091507f50ecea9b4ab42b8f6edd41e Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 01:33:01 +0200 Subject: [PATCH 12/29] added python version jst incase --- bot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 4178468..5d11e6b 100644 --- a/bot.py +++ b/bot.py @@ -502,7 +502,8 @@ async def stats(ctx: commands.Context) -> None: 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 From f5d7121dfb315cc3ebf902a15a149a80109284bf Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 01:36:16 +0200 Subject: [PATCH 13/29] changed from github to git cause i self host it now (i use arch btw) --- bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.py b/bot.py index 5d11e6b..38a01b4 100644 --- a/bot.py +++ b/bot.py @@ -479,7 +479,7 @@ async def about(ctx: commands.Context) -> None: 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"Github", value=get_git_remote_url()) + 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) From 9a4d486e2587170f9d7a1e3a8b690ae569d3c4aa Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 01:40:40 +0200 Subject: [PATCH 14/29] Please enter the commit message for your changes. Lines starting --- modules/globalvars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/globalvars.py b/modules/globalvars.py index 0a5a57b..37b22e9 100644 --- a/modules/globalvars.py +++ b/modules/globalvars.py @@ -48,7 +48,7 @@ song = os.getenv("SONG") arch = platform.machine() launched = False latest_version = "0.0.0" -local_version = "2.3.4" +local_version = "2.3.5" os.environ['gooberlocal_version'] = local_version REACT = os.getenv("REACT") if get_git_branch() == "dev": From 8666c835656b92703a866c7e53e5523c20849842 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 13:57:58 +0200 Subject: [PATCH 15/29] prolly gonna move command to their own modules soon soo --- bot.py | 16 +++++----- modules/globalvars.py | 16 ++++++++-- modules/minigames.py | 70 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 11 deletions(-) create mode 100644 modules/minigames.py diff --git a/bot.py b/bot.py index 38a01b4..3af7e5e 100644 --- a/bot.py +++ b/bot.py @@ -49,7 +49,7 @@ from modules.version import * from modules.sentenceprocessing import * from modules.unhandledexception import handle_exception from modules.image import gen_meme, gen_demotivator - +from modules.minigames import guessthenumber, hangman sys.excepthook = handle_exception check_for_update() # Check for updates (from modules/version.py) @@ -64,12 +64,6 @@ 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=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] = load_memory() markov_model: Optional[markovify.Text] = load_markov_model() @@ -419,10 +413,14 @@ 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"{(_('command_ran_s')).format(interaction=interaction)}{interaction.data['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 diff --git a/modules/globalvars.py b/modules/globalvars.py index 37b22e9..5c20f1d 100644 --- a/modules/globalvars.py +++ b/modules/globalvars.py @@ -2,6 +2,12 @@ import os import platform 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: @@ -15,7 +21,6 @@ def get_git_branch(): env_path = pathlib.Path(__file__).parent.parent / '.env' load_dotenv(dotenv_path=env_path) - ANSI = "\033[" RED = f"{ANSI}31m" GREEN = f"{ANSI}32m" @@ -55,4 +60,11 @@ 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 \ No newline at end of file + beta = 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=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/minigames.py b/modules/minigames.py new file mode 100644 index 0000000..d7ef821 --- /dev/null +++ b/modules/minigames.py @@ -0,0 +1,70 @@ +import random +import discord +from discord import ui, Interaction, TextStyle +from discord.ext import commands +import aiohttp +import asyncio +from modules.globalvars import bot + +@bot.hybrid_command(description="Guess the number game") +async def guessthenumber(ctx: commands.Context): + number = random.randint(1, 10) + class GuessModal(ui.Modal, title="Guess the Number"): + guess = ui.TextInput(label="Your guess (1-10)", style=TextStyle.short) + async def on_submit(self, interaction: Interaction): + try: + user_guess = int(self.guess.value) + except: + await interaction.response.send_message("Invalid number!", ephemeral=True) + return + if user_guess == number: + await interaction.response.send_message("Correct!", ephemeral=True) + else: + await interaction.response.send_message(f"Wrong! The number was {number}.", ephemeral=True) + async def button_callback(interaction: Interaction): + await interaction.response.send_modal(GuessModal()) + button = ui.Button(label="Guess", style=discord.ButtonStyle.primary) + button.callback = button_callback + view = ui.View() + view.add_item(button) + await ctx.send("Click to guess a number from 1 to 10", view=view) + +@bot.hybrid_command(description="Play Hangman with a random word") +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="Guess a Letter"): + letter = ui.TextInput(label="Your 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"You 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"You won! The word was: **{word}**", view=None) + elif wrong_guesses >= max_wrong: + await interaction.response.edit_message(content=f"You lost! The word was: **{word}**", view=None) + else: + await interaction.response.edit_message(content=f"Word: {display_word()}\nWrong guesses: {wrong_guesses}/{max_wrong}", view=view) + async def button_callback(interaction: Interaction): + await interaction.response.send_modal(GuessModal()) + button = ui.Button(label="Guess a letter", style=discord.ButtonStyle.primary) + button.callback = button_callback + view = ui.View() + view.add_item(button) + await ctx.send(f"Word: {display_word()}\nWrong guesses: {wrong_guesses}/{max_wrong}\nClick the button to guess a letter.", view=view) \ No newline at end of file From 52d9b058cf4437059887d7c1b2cc1324f41d6c2c Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:02:57 +0200 Subject: [PATCH 16/29] half assed translations not doing this today --- assets/locales/en.json | 4 +- modules/commands.py | 207 +++++++++++++++++++++++++++++++++++++++++ modules/minigames.py | 7 +- 3 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 modules/commands.py diff --git a/assets/locales/en.json b/assets/locales/en.json index 61f4778..4465658 100644 --- a/assets/locales/en.json +++ b/assets/locales/en.json @@ -1,5 +1,7 @@ { - "memory_file_valid": "The memory.json file is valid!", + "guess_the_number": "Guess the number", + "your_guess": "Your guess (1-10)", + "memosry_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.", diff --git a/modules/commands.py b/modules/commands.py new file mode 100644 index 0000000..573e27f --- /dev/null +++ b/modules/commands.py @@ -0,0 +1,207 @@ + +from modules.globalvars import * +# 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 + + 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'))}") + +# 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: 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_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): + os.remove(temp_input) + await ctx.reply("Failed to generate demotivator.") + return + + await ctx.send(file=discord.File(output_path)) + + 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:") \ No newline at end of file diff --git a/modules/minigames.py b/modules/minigames.py index d7ef821..9cf8b17 100644 --- a/modules/minigames.py +++ b/modules/minigames.py @@ -5,12 +5,13 @@ from discord.ext import commands import aiohttp import asyncio from modules.globalvars import bot +from modules.volta.main import _ -@bot.hybrid_command(description="Guess the number game") +@bot.hybrid_command(description=_('guess_the_number')) async def guessthenumber(ctx: commands.Context): number = random.randint(1, 10) - class GuessModal(ui.Modal, title="Guess the Number"): - guess = ui.TextInput(label="Your guess (1-10)", style=TextStyle.short) + class GuessModal(ui.Modal, title=_('guess_the_number')): + guess = ui.TextInput(label=_('your_guess'), style=TextStyle.short) async def on_submit(self, interaction: Interaction): try: user_guess = int(self.guess.value) From 954014d1a4513b2da399213f20225e37ae3f96bc Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:03:35 +0200 Subject: [PATCH 17/29] fuck --- modules/commands.py | 207 -------------------------------------------- 1 file changed, 207 deletions(-) delete mode 100644 modules/commands.py diff --git a/modules/commands.py b/modules/commands.py deleted file mode 100644 index 573e27f..0000000 --- a/modules/commands.py +++ /dev/null @@ -1,207 +0,0 @@ - -from modules.globalvars import * -# 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 - - 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'))}") - -# 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: 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_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): - os.remove(temp_input) - await ctx.reply("Failed to generate demotivator.") - return - - await ctx.send(file=discord.File(output_path)) - - 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:") \ No newline at end of file From 39f2c26fecce88e47600520ac479e873be43a903 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:08:36 +0200 Subject: [PATCH 18/29] updated volta --- modules/volta/main.py | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/modules/volta/main.py b/modules/volta/main.py index fb6a080..1f05473 100644 --- a/modules/volta/main.py +++ b/modules/volta/main.py @@ -9,6 +9,7 @@ import pathlib import threading import time from dotenv import load_dotenv +from functools import lru_cache ANSI = "\033[" RED = f"{ANSI}31m" @@ -140,6 +141,7 @@ def set_language(lang: str): 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(): global LOCALE, ENGLISH_MISSING @@ -175,26 +177,33 @@ def check_missing_translations(): 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}" - fallback_translations = translations.get(FALLBACK_LOCALE, {}) - sys_lang = get_system_locale().split("_")[0] if get_system_locale() else None - sys_translations = translations.get(sys_lang, {}) if sys_lang else {} - lang_translations = translations.get(lang, {}) - if key in lang_translations: - return lang_translations[key] - if sys_lang and sys_lang != lang and key in sys_translations: - if not printedsystemfallback: - print(f"[VOLTA] {YELLOW}Falling back to system language {sys_lang}!{RESET}") - printedsystemfallback = True - return sys_translations[key] - if key in fallback_translations: - print(f"[VOLTA] {YELLOW}Missing key: '{key}' in '{lang}', falling back to fallback locale '{FALLBACK_LOCALE}'{RESET}") - return fallback_translations[key] - return f"[VOLTA] {YELLOW}Missing key: '{key}' in all locales!{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) From a92439b3522b0c0df331de84dfb25693a5104d3d Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 14:20:24 +0200 Subject: [PATCH 19/29] up 2 date w/volta --- modules/volta/main.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/modules/volta/main.py b/modules/volta/main.py index 1f05473..aae76fa 100644 --- a/modules/volta/main.py +++ b/modules/volta/main.py @@ -120,6 +120,7 @@ def reload_if_changed(): 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: @@ -143,8 +144,8 @@ def set_language(lang: str): ENGLISH_MISSING = True _lookup_translation.cache_clear() -def check_missing_translations(): - global LOCALE, ENGLISH_MISSING +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}") @@ -213,4 +214,9 @@ watchdog_thread = threading.Thread(target=reload_if_changed, daemon=True) watchdog_thread.start() if __name__ == '__main__': - print("Volta should not be run directly! Please use it as a module..") + 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}") From 2e4576ba4b772afd78398a30128ad5683e593d6e Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 16:09:14 +0200 Subject: [PATCH 20/29] =?UTF-8?q?do=20NOT=20clone=20this=20=E2=9C=8C?= =?UTF-8?q?=EF=B8=8F=F0=9F=98=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/locales/en.json | 19 ++++++++++++++++--- assets/locales/it.json | 15 +++++++++++++++ modules/minigames.py | 30 +++++++++++++++--------------- modules/volta/main.py | 6 ++---- 4 files changed, 48 insertions(+), 22 deletions(-) diff --git a/assets/locales/en.json b/assets/locales/en.json index 4465658..a91a1e1 100644 --- a/assets/locales/en.json +++ b/assets/locales/en.json @@ -1,7 +1,20 @@ { - "guess_the_number": "Guess the number", - "your_guess": "Your guess (1-10)", - "memosry_file_valid": "The memory.json file is valid!", + "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.", diff --git a/assets/locales/it.json b/assets/locales/it.json index 954b0ce..45cfa41 100644 --- a/assets/locales/it.json +++ b/assets/locales/it.json @@ -1,4 +1,19 @@ { + "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.", diff --git a/modules/minigames.py b/modules/minigames.py index 9cf8b17..363c1e9 100644 --- a/modules/minigames.py +++ b/modules/minigames.py @@ -7,30 +7,30 @@ import asyncio from modules.globalvars import bot from modules.volta.main import _ -@bot.hybrid_command(description=_('guess_the_number')) +@bot.hybrid_command(description=_('minigames_guess_the_number')) async def guessthenumber(ctx: commands.Context): number = random.randint(1, 10) - class GuessModal(ui.Modal, title=_('guess_the_number')): - guess = ui.TextInput(label=_('your_guess'), style=TextStyle.short) + 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("Invalid number!", ephemeral=True) + await interaction.response.send_message(_('minigames_invalid_number'), ephemeral=True) return if user_guess == number: - await interaction.response.send_message("Correct!", ephemeral=True) + await interaction.response.send_message(_('minigames_correct'), ephemeral=True) else: - await interaction.response.send_message(f"Wrong! The number was {number}.", ephemeral=True) + 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="Guess", style=discord.ButtonStyle.primary) + 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("Click to guess a number from 1 to 10", view=view) + await ctx.send(_('minigames_click_to_guess'), view=view) -@bot.hybrid_command(description="Play Hangman with a random word") +@bot.hybrid_command(description=_('minigames_hangman')) 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: @@ -45,23 +45,23 @@ async def hangman(ctx: commands.Context): max_wrong = 6 def display_word(): return " ".join([c if c in guessed_letters else "_" for c in word]) - class GuessModal(ui.Modal, title="Guess a Letter"): - letter = ui.TextInput(label="Your letter guess", style=TextStyle.short, max_length=1) + 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"You already guessed '{guess}'!", ephemeral=True) + 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"You won! The word was: **{word}**", view=None) + 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"You lost! The word was: **{word}**", view=None) + await interaction.response.edit_message(content=f"{_('minigames_hangman_lost')} **{word}**", view=None) else: - await interaction.response.edit_message(content=f"Word: {display_word()}\nWrong guesses: {wrong_guesses}/{max_wrong}", view=view) + 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="Guess a letter", style=discord.ButtonStyle.primary) diff --git a/modules/volta/main.py b/modules/volta/main.py index aae76fa..4b62144 100644 --- a/modules/volta/main.py +++ b/modules/volta/main.py @@ -7,6 +7,8 @@ import locale import json import pathlib import threading +import platform +import sys import time from dotenv import load_dotenv from functools import lru_cache @@ -62,10 +64,6 @@ if working_dir != module_dir: translations = {} _file_mod_times = {} -import locale -import platform -import os -import sys def get_system_locale(): system = platform.system() # fallback incase locale isnt set From 59ce219183d0a1019bbf5afc4039c17f3456d5bb Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Fri, 18 Jul 2025 16:15:34 +0200 Subject: [PATCH 21/29] fuckass interactions.... --- assets/locales/it.json | 2 +- bot.py | 2 +- modules/minigames.py | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assets/locales/it.json b/assets/locales/it.json index 45cfa41..0ae6413 100644 --- a/assets/locales/it.json +++ b/assets/locales/it.json @@ -1,5 +1,5 @@ { - "minigames_hangman_game": "Parola: {display_word()}\nErrori: {wrong_guesses}/{max_wrong}", + "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", diff --git a/bot.py b/bot.py index 3af7e5e..dcd777f 100644 --- a/bot.py +++ b/bot.py @@ -49,7 +49,7 @@ from modules.version import * from modules.sentenceprocessing import * from modules.unhandledexception import handle_exception from modules.image import gen_meme, gen_demotivator -from modules.minigames import guessthenumber, hangman +# from modules.minigames import guessthenumber, hangman sys.excepthook = handle_exception check_for_update() # Check for updates (from modules/version.py) diff --git a/modules/minigames.py b/modules/minigames.py index 363c1e9..0a74a82 100644 --- a/modules/minigames.py +++ b/modules/minigames.py @@ -7,7 +7,7 @@ import asyncio from modules.globalvars import bot from modules.volta.main import _ -@bot.hybrid_command(description=_('minigames_guess_the_number')) +# @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')): @@ -30,7 +30,7 @@ async def guessthenumber(ctx: commands.Context): view.add_item(button) await ctx.send(_('minigames_click_to_guess'), view=view) -@bot.hybrid_command(description=_('minigames_hangman')) +# @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: @@ -61,11 +61,11 @@ async def hangman(ctx: commands.Context): 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) + 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="Guess a letter", style=discord.ButtonStyle.primary) + 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(f"Word: {display_word()}\nWrong guesses: {wrong_guesses}/{max_wrong}\nClick the button to guess a letter.", view=view) \ No newline at end of file + 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 From fcb1a9782ad2a4e415bcf866b5288875ed861d69 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Sat, 19 Jul 2025 00:30:57 +0200 Subject: [PATCH 22/29] Linux openmediavault 6.12.32+bpo-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.32-1~bpo12+1 (2025-06-21) x86_64 GNU/Linux --- bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot.py b/bot.py index dcd777f..a774aa0 100644 --- a/bot.py +++ b/bot.py @@ -352,7 +352,7 @@ async def help(ctx: commands.Context) -> None: @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:") + await ctx.send(":thumbsdown:") return await ctx.defer() set_language(locale) From bf421e4fd0a79a6fbba46d38f880499c8b457bdc Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Sat, 19 Jul 2025 00:34:38 +0200 Subject: [PATCH 23/29] swear to your hearts content --- requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c53d758..7a9e5af 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,6 @@ spacy spacytextblob requests psutil -better_profanity python-dotenv dotenv pillow \ No newline at end of file From 3c71e9f54e53bcfc12d54690b5c86bba02300935 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Mon, 21 Jul 2025 16:58:35 +0200 Subject: [PATCH 24/29] bundled goober 0.0.5 in with default goober incase i dunno they want a small version of it --- botminimal.py | 245 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 245 insertions(+) create mode 100644 botminimal.py 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) From 221f2370e42c56a7809407cbb660dd9d753cce6a Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Mon, 21 Jul 2025 21:12:31 +0200 Subject: [PATCH 25/29] oh yeah --- assets/locales/en.json | 2 +- assets/locales/fr.json | 130 ----------------------------------------- assets/locales/it.json | 2 +- bot.py | 11 ++++ modules/logger.py | 7 ++- 5 files changed, 19 insertions(+), 133 deletions(-) delete mode 100644 assets/locales/fr.json diff --git a/assets/locales/en.json b/assets/locales/en.json index a91a1e1..53e9dd5 100644 --- a/assets/locales/en.json +++ b/assets/locales/en.json @@ -17,7 +17,7 @@ "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:", 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/it.json b/assets/locales/it.json index 0ae6413..972088d 100644 --- a/assets/locales/it.json +++ b/assets/locales/it.json @@ -17,7 +17,7 @@ "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 a774aa0..ac5897d 100644 --- a/bot.py +++ b/bot.py @@ -481,6 +481,17 @@ async def about(ctx: commands.Context) -> None: embed.add_field(name=f"OS", value=platform.platform()) await send_message(ctx, embed=embed) +@bot.hybrid_command(description="balls") +async def countUser(ctx, user: discord.User = None): + user = user or ctx.author + user_id = str(user.id) + + with open(MEMORY_FILE, 'r') as f: + data = json.load(f) + + count = sum(1 for entry in data if '_meta' in entry and entry['_meta'].get('user_id') == user_id) + + await ctx.send(f"User {user.display_name}'s ID appears {count} times in the memory file.") # Command: Show bot statistics (admin only) @bot.hybrid_command(description="stats") diff --git a/modules/logger.py b/modules/logger.py index 76f5f10..79d9029 100644 --- a/modules/logger.py +++ b/modules/logger.py @@ -1,4 +1,5 @@ import logging +import re from modules.globalvars import * class GooberFormatter(logging.Formatter): @@ -16,10 +17,14 @@ class GooberFormatter(logging.Formatter): } def format(self, record: logging.LogRecord): + ansiescape = re.compile(r'\x1B[@-_][0-?]*[ -/]*[@-~]') if self.colors: log_fmt = self.FORMATS.get(record.levelno) # Add colors else: log_fmt = self._format # Just use the default format formatter = logging.Formatter(log_fmt, datefmt="%m/%d/%y %H:%M:%S") - return formatter.format(record) + formatted = formatter.format(record) + if not self.colors: + formatted = ansiescape.sub('', formatted) + return formatted From ed53089d4a1c30db96ecd1abee6a7b08d5354950 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Mon, 21 Jul 2025 22:10:00 +0200 Subject: [PATCH 26/29] :p --- README.md | 5 ++--- bot.py | 18 +++++++----------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index dc1f094..ae744a6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ knockoff of genai basically :p - +not that hard to setup just modify the .env and install packages and youre rollin +maybe also create custom commands (you put them in the cog folder its just drap and drop) 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) - ![the goober](https://goober.whatdidyouexpect.eu/imgs/goobs/goobs.png) diff --git a/bot.py b/bot.py index ac5897d..12d3d87 100644 --- a/bot.py +++ b/bot.py @@ -7,6 +7,7 @@ import traceback import subprocess import tempfile import shutil +import psutil import asyncio import platform import sys @@ -219,6 +220,12 @@ async def talk(ctx: commands.Context, sentence_size: int = 5) -> None: 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: @@ -481,17 +488,6 @@ async def about(ctx: commands.Context) -> None: embed.add_field(name=f"OS", value=platform.platform()) await send_message(ctx, embed=embed) -@bot.hybrid_command(description="balls") -async def countUser(ctx, user: discord.User = None): - user = user or ctx.author - user_id = str(user.id) - - with open(MEMORY_FILE, 'r') as f: - data = json.load(f) - - count = sum(1 for entry in data if '_meta' in entry and entry['_meta'].get('user_id') == user_id) - - await ctx.send(f"User {user.display_name}'s ID appears {count} times in the memory file.") # Command: Show bot statistics (admin only) @bot.hybrid_command(description="stats") From f17db0d22c74efa10e019916eafa2a69c4f55906 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Mon, 21 Jul 2025 22:13:42 +0200 Subject: [PATCH 27/29] canadian french added i dont know any normal french and the guy i know is canadian --- assets/locales/fr_ca.json | 149 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 assets/locales/fr_ca.json 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 From 7d6c5aae513fd3d1955ceae4207c9718aa25ac18 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Tue, 22 Jul 2025 00:19:55 +0200 Subject: [PATCH 28/29] ts fuckass minigames file will be the death of me --- assets/cogs/lyrics.py | 123 +++++++++++++++++++++++++++++++++++++++++ assets/locales/it.json | 2 +- bot.py | 5 +- modules/globalvars.py | 2 + 4 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 assets/cogs/lyrics.py 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/locales/it.json b/assets/locales/it.json index 972088d..4f97806 100644 --- a/assets/locales/it.json +++ b/assets/locales/it.json @@ -1,5 +1,5 @@ { - "minigames_hangman_game": "Parola: {display_word}\nErrori: {wrong_guesses}/{max_wrong}", + "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", diff --git a/bot.py b/bot.py index 12d3d87..bbee94d 100644 --- a/bot.py +++ b/bot.py @@ -50,10 +50,9 @@ from modules.version import * from modules.sentenceprocessing import * from modules.unhandledexception import handle_exception from modules.image import gen_meme, gen_demotivator -# from modules.minigames import guessthenumber, hangman +from modules.minigames import guessthenumber, hangman sys.excepthook = handle_exception check_for_update() # Check for updates (from modules/version.py) - # Type aliases T = TypeVar('T') MessageContext = Union[commands.Context, discord.Interaction] @@ -114,7 +113,6 @@ async def on_ready() -> None: 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}") @@ -136,7 +134,6 @@ async def on_ready() -> None: }.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 diff --git a/modules/globalvars.py b/modules/globalvars.py index 5c20f1d..ac37ed4 100644 --- a/modules/globalvars.py +++ b/modules/globalvars.py @@ -66,5 +66,7 @@ else: # 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 From b860d0e2714e77e018e77bd522866dd6de3d79c9 Mon Sep 17 00:00:00 2001 From: WhatDidYouExpect <89535984+WhatDidYouExpect@users.noreply.github.com> Date: Tue, 22 Jul 2025 00:25:56 +0200 Subject: [PATCH 29/29] a --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ae744a6..c1e9957 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ knockoff of genai basically :p -not that hard to setup just modify the .env and install packages and youre rollin -maybe also create custom commands (you put them in the cog folder its just drap and drop) + Special thanks to [Charlie's Computers](https://github.com/PowerPCFan) for being the only one I know of that's hosting Goober 24/7 -![the goober](https://goober.whatdidyouexpect.eu/imgs/goobs/goobs.png) +[Goober Central](https://github.com/whatdidyouexpect/goober-central) + +[Another mirror](https://forgejo.expect.ovh/gooberinc/goober) +no promises that it'll be stable