commit
754d760252
8 changed files with 205 additions and 61 deletions
|
@ -1,8 +1,9 @@
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import os
|
|
||||||
from modules.globalvars import ownerid
|
from modules.globalvars import ownerid
|
||||||
|
|
||||||
|
COG_PREFIX = "assets.cogs."
|
||||||
|
|
||||||
class CogManager(commands.Cog):
|
class CogManager(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
@ -16,7 +17,7 @@ class CogManager(commands.Cog):
|
||||||
await ctx.send("Please provide the cog name to load.")
|
await ctx.send("Please provide the cog name to load.")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
await self.bot.load_extension(f"cogs.{cog_name}")
|
await self.bot.load_extension(COG_PREFIX + cog_name)
|
||||||
await ctx.send(f"Loaded cog `{cog_name}` successfully.")
|
await ctx.send(f"Loaded cog `{cog_name}` successfully.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await ctx.send(f"Error loading cog `{cog_name}`: {e}")
|
await ctx.send(f"Error loading cog `{cog_name}`: {e}")
|
||||||
|
@ -30,7 +31,7 @@ class CogManager(commands.Cog):
|
||||||
await ctx.send("Please provide the cog name to unload.")
|
await ctx.send("Please provide the cog name to unload.")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
await self.bot.unload_extension(f"cogs.{cog_name}")
|
await self.bot.unload_extension(COG_PREFIX + cog_name)
|
||||||
await ctx.send(f"Unloaded cog `{cog_name}` successfully.")
|
await ctx.send(f"Unloaded cog `{cog_name}` successfully.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await ctx.send(f"Error unloading cog `{cog_name}`: {e}")
|
await ctx.send(f"Error unloading cog `{cog_name}`: {e}")
|
||||||
|
@ -44,8 +45,8 @@ class CogManager(commands.Cog):
|
||||||
await ctx.send("Please provide the cog name to reload.")
|
await ctx.send("Please provide the cog name to reload.")
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
await self.bot.unload_extension(f"cogs.{cog_name}")
|
await self.bot.unload_extension(COG_PREFIX + cog_name)
|
||||||
await self.bot.load_extension(f"cogs.{cog_name}")
|
await self.bot.load_extension(COG_PREFIX + cog_name)
|
||||||
await ctx.send(f"Reloaded cog `{cog_name}` successfully.")
|
await ctx.send(f"Reloaded cog `{cog_name}` successfully.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await ctx.send(f"Error reloading cog `{cog_name}`: {e}")
|
await ctx.send(f"Error reloading cog `{cog_name}`: {e}")
|
||||||
|
|
98
assets/cogs/fuckup.py
Normal file
98
assets/cogs/fuckup.py
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
import discord
|
||||||
|
from discord.ext import commands
|
||||||
|
from modules.image import *
|
||||||
|
from modules.volta.main import _
|
||||||
|
from PIL import Image, ImageEnhance, ImageFilter, ImageOps, ImageChops, ImageColor
|
||||||
|
import os, random, shutil, tempfile
|
||||||
|
|
||||||
|
async def deepfryimage(path):
|
||||||
|
with Image.open(path).convert("RGB") as im:
|
||||||
|
# make it burn
|
||||||
|
for _ in range(3):
|
||||||
|
im = im.resize((int(im.width * 0.7), int(im.height * 0.7)))
|
||||||
|
im = im.resize((int(im.width * 1.5), int(im.height * 1.5)))
|
||||||
|
im = ImageEnhance.Contrast(im).enhance(random.uniform(5, 10))
|
||||||
|
im = ImageEnhance.Sharpness(im).enhance(random.uniform(10, 50))
|
||||||
|
im = ImageEnhance.Brightness(im).enhance(random.uniform(1.5, 3))
|
||||||
|
r, g, b = im.split()
|
||||||
|
r = r.point(lambda i: min(255, i * random.uniform(1.2, 2.0)))
|
||||||
|
g = g.point(lambda i: min(255, i * random.uniform(0.5, 1.5)))
|
||||||
|
b = b.point(lambda i: min(255, i * random.uniform(0.5, 2.0)))
|
||||||
|
channels = [r, g, b]
|
||||||
|
random.shuffle(channels)
|
||||||
|
im = Image.merge("RGB", tuple(channels))
|
||||||
|
overlay_color = tuple(random.randint(0, 255) for _ in range(3))
|
||||||
|
overlay = Image.new("RGB", im.size, overlay_color)
|
||||||
|
im = ImageChops.add(im, overlay, scale=2.0, offset=random.randint(-64, 64))
|
||||||
|
|
||||||
|
im = im.filter(ImageFilter.EDGE_ENHANCE_MORE)
|
||||||
|
im = im.filter(ImageFilter.GaussianBlur(radius=random.uniform(0.5, 2)))
|
||||||
|
for _ in range(3):
|
||||||
|
tmp_path = tempfile.mktemp(suffix=".jpg")
|
||||||
|
im.save(tmp_path, format="JPEG", quality=random.randint(5, 15))
|
||||||
|
im = Image.open(tmp_path)
|
||||||
|
if random.random() < 0.3:
|
||||||
|
im = ImageOps.posterize(im, bits=random.choice([2, 3, 4]))
|
||||||
|
if random.random() < 0.2:
|
||||||
|
im = ImageOps.invert(im)
|
||||||
|
out_path = tempfile.mktemp(suffix=".jpg")
|
||||||
|
im.save(out_path, format="JPEG", quality=5)
|
||||||
|
return out_path
|
||||||
|
|
||||||
|
|
||||||
|
class whami(commands.Cog):
|
||||||
|
def __init__(self, bot):
|
||||||
|
self.bot = bot
|
||||||
|
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def fuckup(self, ctx):
|
||||||
|
assets_folder = "assets/images"
|
||||||
|
temp_input = None
|
||||||
|
|
||||||
|
def get_random_asset_image():
|
||||||
|
files = [f for f in os.listdir(assets_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
|
||||||
|
if not files:
|
||||||
|
return None
|
||||||
|
return os.path.join(assets_folder, random.choice(files))
|
||||||
|
|
||||||
|
if ctx.message.attachments:
|
||||||
|
attachment = ctx.message.attachments[0]
|
||||||
|
if attachment.content_type and attachment.content_type.startswith("image/"):
|
||||||
|
ext = os.path.splitext(attachment.filename)[1]
|
||||||
|
temp_input = f"tempy{ext}"
|
||||||
|
await attachment.save(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
|
||||||
|
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 = await gen_meme(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_generate_image'))
|
||||||
|
return
|
||||||
|
|
||||||
|
deepfried_path = await deepfryimage(output_path)
|
||||||
|
await ctx.send(file=discord.File(deepfried_path))
|
||||||
|
|
||||||
|
if temp_input and os.path.exists(temp_input):
|
||||||
|
os.remove(temp_input)
|
||||||
|
|
||||||
|
async def setup(bot):
|
||||||
|
await bot.add_cog(whami(bot))
|
|
@ -1,29 +0,0 @@
|
||||||
import discord
|
|
||||||
from discord.ext import commands
|
|
||||||
import os
|
|
||||||
import requests
|
|
||||||
import ast
|
|
||||||
from modules.globalvars import VERSION_URL
|
|
||||||
|
|
||||||
|
|
||||||
class grabTemplate(commands.Cog):
|
|
||||||
def __init__(self, bot):
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
def download_json():
|
|
||||||
response = requests.get(f"{VERSION_URL}/goob/template.json")
|
|
||||||
if response.status_code == 200:
|
|
||||||
if os.path.exists("memory.json"):
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
userinput = input("Do you want to download the template json instead of starting from scratch?\n(Y/N)\n")
|
|
||||||
if userinput.lower() == "y":
|
|
||||||
with open("memory.json", "w", encoding="utf-8") as file:
|
|
||||||
file.write(response.text)
|
|
||||||
else:
|
|
||||||
print("Starting from scratch...")
|
|
||||||
elif response.status_code == 404:
|
|
||||||
print("File not found on goober central!!")
|
|
||||||
download_json()
|
|
||||||
async def setup(bot):
|
|
||||||
await bot.add_cog(grabTemplate(bot))
|
|
14
bot.py
14
bot.py
|
@ -99,17 +99,6 @@ async def load_cogs_from_folder(bot, folder_name="assets/cogs"):
|
||||||
logger.error(f"{(_('cog_fail'))} {cog_name} {e}")
|
logger.error(f"{(_('cog_fail'))} {cog_name} {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
async def fetch_active_users() -> str:
|
|
||||||
try:
|
|
||||||
response: requests.Response = requests.get(f"{VERSION_URL}/active-users")
|
|
||||||
if response.status_code == 200:
|
|
||||||
return response.text.strip()
|
|
||||||
else:
|
|
||||||
return "?"
|
|
||||||
except Exception as e:
|
|
||||||
logger.e(f"{_('error_fetching_active_users')} {RESET} {e}")
|
|
||||||
return "?"
|
|
||||||
|
|
||||||
async def send_alive_ping_periodically() -> None:
|
async def send_alive_ping_periodically() -> None:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
|
@ -134,9 +123,6 @@ async def on_ready() -> None:
|
||||||
synced: List[discord.app_commands.AppCommand] = await bot.tree.sync()
|
synced: List[discord.app_commands.AppCommand] = await bot.tree.sync()
|
||||||
logger.info(f"{_('synced_commands')} {len(synced)} {(_('synced_commands2'))}")
|
logger.info(f"{_('synced_commands')} {len(synced)} {(_('synced_commands2'))}")
|
||||||
slash_commands_enabled = True
|
slash_commands_enabled = True
|
||||||
|
|
||||||
active_users: str = await fetch_active_users()
|
|
||||||
logger.info(f"{(_('active_users:'))} {active_users}")
|
|
||||||
logger.info(f"{(_('started')).format(name=NAME)}")
|
logger.info(f"{(_('started')).format(name=NAME)}")
|
||||||
|
|
||||||
bot.loop.create_task(send_alive_ping_periodically())
|
bot.loop.create_task(send_alive_ping_periodically())
|
||||||
|
|
|
@ -5,11 +5,8 @@ BLACKLISTEDUSERS=
|
||||||
OWNERID=
|
OWNERID=
|
||||||
USERTRAINENABLED="true"
|
USERTRAINENABLED="true"
|
||||||
SHOWMEMENABLED="true"
|
SHOWMEMENABLED="true"
|
||||||
NAME="gooberino goobs"
|
|
||||||
LOCALE=fi
|
LOCALE=fi
|
||||||
ALIVEPING="True"
|
|
||||||
AUTOUPDATE="True"
|
AUTOUPDATE="True"
|
||||||
GOOBERTOKEN=
|
|
||||||
SONG="Basket Case - Green Day"
|
SONG="Basket Case - Green Day"
|
||||||
CHECKSDISABLED="Frue"
|
CHECKSDISABLED="Frue"
|
||||||
REACT="True"
|
REACT="True"
|
||||||
|
|
51
modules/coghooks.py
Normal file
51
modules/coghooks.py
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
import os
|
||||||
|
import importlib
|
||||||
|
import inspect
|
||||||
|
import sys
|
||||||
|
|
||||||
|
_hooks = {}
|
||||||
|
|
||||||
|
def register_hook(name, func):
|
||||||
|
if name not in _hooks:
|
||||||
|
_hooks[name] = []
|
||||||
|
_hooks[name].append(func)
|
||||||
|
|
||||||
|
async def call_hook(name, *args, **kwargs):
|
||||||
|
if name not in _hooks:
|
||||||
|
return
|
||||||
|
for func in _hooks[name]:
|
||||||
|
if callable(func):
|
||||||
|
if inspect.iscoroutinefunction(func):
|
||||||
|
await func(*args, **kwargs)
|
||||||
|
else:
|
||||||
|
func(*args, **kwargs)
|
||||||
|
|
||||||
|
def register_all_functions_as_hooks(module):
|
||||||
|
"""Register every function/coroutine function in the given module as a hook under its function name."""
|
||||||
|
for name, obj in inspect.getmembers(module):
|
||||||
|
if inspect.isfunction(obj) or inspect.iscoroutinefunction(obj):
|
||||||
|
register_hook(name, obj)
|
||||||
|
|
||||||
|
def _register_all_modules_functions():
|
||||||
|
"""Scan all python modules in this 'modules' folder (excluding this file) and register their functions."""
|
||||||
|
# Calculate the path to the modules directory relative to this file
|
||||||
|
modules_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
|
||||||
|
# Current file name so we skip it
|
||||||
|
current_file = os.path.basename(__file__)
|
||||||
|
|
||||||
|
# Ensure 'modules' is in sys.path for importlib to work
|
||||||
|
if modules_dir not in sys.path:
|
||||||
|
sys.path.insert(0, modules_dir)
|
||||||
|
|
||||||
|
# List all python files except dunder files and this file
|
||||||
|
for filename in os.listdir(modules_dir):
|
||||||
|
if filename.endswith(".py") and not filename.startswith("__") and filename != current_file:
|
||||||
|
module_name = filename[:-3] # strip .py extension
|
||||||
|
try:
|
||||||
|
module = importlib.import_module(module_name)
|
||||||
|
register_all_functions_as_hooks(module)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[hooks] Failed to import {module_name}: {e}")
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ 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
|
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
|
launched = False
|
||||||
latest_version = "0.0.0"
|
latest_version = "0.0.0"
|
||||||
local_version = "2.3.1"
|
local_version = "2.3.2"
|
||||||
os.environ['gooberlocal_version'] = local_version
|
os.environ['gooberlocal_version'] = local_version
|
||||||
REACT = os.getenv("REACT")
|
REACT = os.getenv("REACT")
|
||||||
if get_git_branch() == "dev":
|
if get_git_branch() == "dev":
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
# For updates or contributions, visit: https://github.com/gooberinc/volta
|
# For updates or contributions, visit: https://github.com/gooberinc/volta
|
||||||
# Also, Note to self: Add more comments it needs more love
|
# Also, Note to self: Add more comments it needs more love
|
||||||
import os
|
import os
|
||||||
|
import locale
|
||||||
import json
|
import json
|
||||||
import pathlib
|
import pathlib
|
||||||
import threading
|
import threading
|
||||||
|
@ -16,8 +17,6 @@ YELLOW = f"{ANSI}33m"
|
||||||
DEBUG = f"{ANSI}1;30m"
|
DEBUG = f"{ANSI}1;30m"
|
||||||
RESET = f"{ANSI}0m"
|
RESET = f"{ANSI}0m"
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
LOCALE = os.getenv("LOCALE")
|
LOCALE = os.getenv("LOCALE")
|
||||||
module_dir = pathlib.Path(__file__).parent.parent
|
module_dir = pathlib.Path(__file__).parent.parent
|
||||||
working_dir = pathlib.Path.cwd()
|
working_dir = pathlib.Path.cwd()
|
||||||
|
@ -62,6 +61,37 @@ if working_dir != module_dir:
|
||||||
translations = {}
|
translations = {}
|
||||||
_file_mod_times = {}
|
_file_mod_times = {}
|
||||||
|
|
||||||
|
import locale
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def get_system_locale():
|
||||||
|
system = platform.system() # fallback incase locale isnt set
|
||||||
|
if system == "Windows":
|
||||||
|
lang, _ = locale.getdefaultlocale()
|
||||||
|
return lang or os.getenv("LANG")
|
||||||
|
elif system == "Darwin":
|
||||||
|
try:
|
||||||
|
import subprocess
|
||||||
|
result = subprocess.run(
|
||||||
|
["defaults", "read", "-g", "AppleLocale"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
text=True
|
||||||
|
)
|
||||||
|
return result.stdout.strip() or locale.getdefaultlocale()[0]
|
||||||
|
except Exception:
|
||||||
|
return locale.getdefaultlocale()[0]
|
||||||
|
elif system == "Linux":
|
||||||
|
return (
|
||||||
|
os.getenv("LC_ALL") or
|
||||||
|
os.getenv("LANG") or
|
||||||
|
locale.getdefaultlocale()[0]
|
||||||
|
)
|
||||||
|
return locale.getdefaultlocale()[0]
|
||||||
|
|
||||||
|
|
||||||
def load_translations():
|
def load_translations():
|
||||||
global translations, _file_mod_times
|
global translations, _file_mod_times
|
||||||
translations.clear()
|
translations.clear()
|
||||||
|
@ -99,7 +129,9 @@ def reload_if_changed():
|
||||||
|
|
||||||
def set_language(lang: str):
|
def set_language(lang: str):
|
||||||
global LOCALE, ENGLISH_MISSING
|
global LOCALE, ENGLISH_MISSING
|
||||||
if lang in translations:
|
if not LOCALE:
|
||||||
|
LOCALE = get_system_locale()
|
||||||
|
elif lang in translations:
|
||||||
LOCALE = lang
|
LOCALE = lang
|
||||||
else:
|
else:
|
||||||
print(f"[VOLTA] {RED}Language '{lang}' not found, defaulting to 'en'{RESET}")
|
print(f"[VOLTA] {RED}Language '{lang}' not found, defaulting to 'en'{RESET}")
|
||||||
|
@ -141,19 +173,27 @@ def check_missing_translations():
|
||||||
else:
|
else:
|
||||||
print(f"[VOLTA] All translation keys present for locale: {LOCALE}")
|
print(f"[VOLTA] All translation keys present for locale: {LOCALE}")
|
||||||
|
|
||||||
|
printedsystemfallback = False
|
||||||
|
|
||||||
def get_translation(lang: str, key: str):
|
def get_translation(lang: str, key: str):
|
||||||
|
global printedsystemfallback
|
||||||
if ENGLISH_MISSING:
|
if ENGLISH_MISSING:
|
||||||
return f"[VOLTA] {RED}No fallback available!{RESET}"
|
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, {})
|
lang_translations = translations.get(lang, {})
|
||||||
if key in lang_translations:
|
if key in lang_translations:
|
||||||
return lang_translations[key]
|
return lang_translations[key]
|
||||||
else:
|
if sys_lang and sys_lang != lang and key in sys_translations:
|
||||||
if key not in translations.get(FALLBACK_LOCALE, {}):
|
if not printedsystemfallback:
|
||||||
return f"[VOLTA] {YELLOW}Missing key: '{key}' in {FALLBACK_LOCALE}.json!{RESET}"
|
print(f"[VOLTA] {YELLOW}Falling back to system language {sys_lang}!{RESET}")
|
||||||
fallback = translations.get(FALLBACK_LOCALE, {}).get(key, key)
|
printedsystemfallback = True
|
||||||
print(f"[VOLTA] {YELLOW}Missing key: '{key}' in language '{lang}', falling back to: '{fallback}' using {FALLBACK_LOCALE}.json{RESET}") # yeah probably print this
|
return sys_translations[key]
|
||||||
return fallback
|
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}"
|
||||||
|
|
||||||
def _(key: str) -> str:
|
def _(key: str) -> str:
|
||||||
return get_translation(LOCALE, key)
|
return get_translation(LOCALE, key)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue