forked from gooberinc/goober
rewrote env parts and fixed some frigging issues
This commit is contained in:
parent
99ab5d334e
commit
f7042ed8a7
23 changed files with 3070 additions and 280 deletions
1
.env.example
Normal file
1
.env.example
Normal file
|
@ -0,0 +1 @@
|
||||||
|
DISCORD_BOT_TOKEN=""
|
|
@ -1,6 +1,11 @@
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from modules.globalvars import ownerid
|
import discord.ext
|
||||||
|
import discord.ext.commands
|
||||||
|
from modules.settings import Settings as SettingsManager
|
||||||
|
settings_manager = SettingsManager()
|
||||||
|
settings = settings_manager.settings
|
||||||
|
|
||||||
|
|
||||||
COG_PREFIX = "assets.cogs."
|
COG_PREFIX = "assets.cogs."
|
||||||
|
|
||||||
|
@ -9,10 +14,34 @@ class CogManager(commands.Cog):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def load(self, ctx, cog_name: str = None):
|
async def enable(self, ctx, cog_name: str):
|
||||||
if ctx.author.id != ownerid:
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
await ctx.send("You do not have permission to use this command.")
|
await ctx.send("You do not have permission to use this command.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
await self.bot.load_extension(COG_PREFIX + cog_name)
|
||||||
|
await ctx.send(f"Loaded cog `{cog_name}` successfully.")
|
||||||
|
settings["bot"]["enabled_cogs"].append(cog_name)
|
||||||
|
settings_manager.commit()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
await ctx.send(f"Error enabling cog `{cog_name}`: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def load(self, ctx, cog_name: str | None = None):
|
||||||
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
|
await ctx.send("You do not have permission to use this command.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if cog_name is None:
|
||||||
|
await ctx.send("Give cog_name")
|
||||||
|
return
|
||||||
|
|
||||||
|
if cog_name[:-3] not in settings["bot"]["enabled_cogs"]:
|
||||||
|
await ctx.send("Please enable the cog first!")
|
||||||
|
return
|
||||||
if cog_name is None:
|
if cog_name is None:
|
||||||
await ctx.send("Please provide the cog name to load.")
|
await ctx.send("Please provide the cog name to load.")
|
||||||
return
|
return
|
||||||
|
@ -23,8 +52,8 @@ class CogManager(commands.Cog):
|
||||||
await ctx.send(f"Error loading cog `{cog_name}`: {e}")
|
await ctx.send(f"Error loading cog `{cog_name}`: {e}")
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def unload(self, ctx, cog_name: str = None):
|
async def unload(self, ctx, cog_name: str | None = None):
|
||||||
if ctx.author.id != ownerid:
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
await ctx.send("You do not have permission to use this command.")
|
await ctx.send("You do not have permission to use this command.")
|
||||||
return
|
return
|
||||||
if cog_name is None:
|
if cog_name is None:
|
||||||
|
@ -37,13 +66,34 @@ class CogManager(commands.Cog):
|
||||||
await ctx.send(f"Error unloading cog `{cog_name}`: {e}")
|
await ctx.send(f"Error unloading cog `{cog_name}`: {e}")
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def reload(self, ctx, cog_name: str = None):
|
async def disable(self, ctx, cog_name: str | None = None):
|
||||||
if ctx.author.id != ownerid:
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
|
await ctx.send("You do not have permission to use this command.")
|
||||||
|
return
|
||||||
|
if cog_name is None:
|
||||||
|
await ctx.send("Please provide the cog name to disable.")
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
await self.bot.unload_extension(COG_PREFIX + cog_name)
|
||||||
|
await ctx.send(f"Unloaded cog `{cog_name}` successfully.")
|
||||||
|
settings["bot"]["enabled_cogs"].remove(cog_name)
|
||||||
|
settings_manager.commit()
|
||||||
|
except Exception as e:
|
||||||
|
await ctx.send(f"Error unloading cog `{cog_name}`: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
@commands.command()
|
||||||
|
async def reload(self, ctx, cog_name: str | None = None):
|
||||||
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
await ctx.send("You do not have permission to use this command.")
|
await ctx.send("You do not have permission to use this command.")
|
||||||
return
|
return
|
||||||
if cog_name is None:
|
if cog_name is None:
|
||||||
await ctx.send("Please provide the cog name to reload.")
|
await ctx.send("Please provide the cog name to reload.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if cog_name[:-3] not in settings["bot"]["enabled_cogs"]:
|
||||||
|
await ctx.send("Please enable the cog first!")
|
||||||
|
return
|
||||||
try:
|
try:
|
||||||
await self.bot.unload_extension(COG_PREFIX + cog_name)
|
await self.bot.unload_extension(COG_PREFIX + cog_name)
|
||||||
await self.bot.load_extension(COG_PREFIX + cog_name)
|
await self.bot.load_extension(COG_PREFIX + cog_name)
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
import discord
|
import discord
|
||||||
import discord.context_managers
|
import discord.context_managers
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from modules.globalvars import ownerid
|
|
||||||
import logging
|
import logging
|
||||||
from typing import Literal, get_args, cast
|
from typing import Literal, get_args, cast
|
||||||
|
from modules.settings import Settings as SettingsManager
|
||||||
|
settings_manager = SettingsManager()
|
||||||
|
settings = settings_manager.settings
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("goober")
|
logger = logging.getLogger("goober")
|
||||||
|
|
||||||
|
@ -22,10 +25,10 @@ class FileSync(commands.Cog):
|
||||||
await ctx.send("Invalid mode, use 's' or 'r'.")
|
await ctx.send("Invalid mode, use 's' or 'r'.")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.mode: AvailableModes = cast(AvailableModes, mode.lower())
|
self.mode = cast(AvailableModes, mode.lower())
|
||||||
self.peer_id = peer.id
|
self.peer_id = peer.id
|
||||||
|
|
||||||
if ctx.author.id != ownerid:
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
await ctx.send("You don't have permission to execute this command.")
|
await ctx.send("You don't have permission to execute this command.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import discord
|
import discord
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
from modules.image import *
|
from modules.image import *
|
||||||
from modules.volta.main import _
|
|
||||||
from PIL import Image, ImageEnhance, ImageFilter, ImageOps, ImageChops, ImageColor
|
from PIL import Image, ImageEnhance, ImageFilter, ImageOps, ImageChops, ImageColor
|
||||||
import os, random, shutil, tempfile
|
import os, random, shutil, tempfile
|
||||||
|
import modules.keys as k
|
||||||
|
|
||||||
async def deepfryimage(path):
|
async def deepfryimage(path):
|
||||||
with Image.open(path).convert("RGB") as im:
|
with Image.open(path).convert("RGB") as im:
|
||||||
|
@ -66,7 +66,7 @@ class whami(commands.Cog):
|
||||||
else:
|
else:
|
||||||
fallback_image = get_random_asset_image()
|
fallback_image = get_random_asset_image()
|
||||||
if fallback_image is None:
|
if fallback_image is None:
|
||||||
await ctx.reply(_('no_image_available'))
|
await ctx.reply(k.no_image_available())
|
||||||
return
|
return
|
||||||
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
|
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
|
||||||
shutil.copy(fallback_image, temp_input)
|
shutil.copy(fallback_image, temp_input)
|
||||||
|
@ -74,7 +74,7 @@ class whami(commands.Cog):
|
||||||
else:
|
else:
|
||||||
fallback_image = get_random_asset_image()
|
fallback_image = get_random_asset_image()
|
||||||
if fallback_image is None:
|
if fallback_image is None:
|
||||||
await ctx.reply(_('no_image_available'))
|
await ctx.reply(k.no_image_available())
|
||||||
return
|
return
|
||||||
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
|
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
|
||||||
shutil.copy(fallback_image, temp_input)
|
shutil.copy(fallback_image, temp_input)
|
||||||
|
@ -85,7 +85,7 @@ class whami(commands.Cog):
|
||||||
if output_path is None or not os.path.isfile(output_path):
|
if output_path is None or not os.path.isfile(output_path):
|
||||||
if temp_input and os.path.exists(temp_input):
|
if temp_input and os.path.exists(temp_input):
|
||||||
os.remove(temp_input)
|
os.remove(temp_input)
|
||||||
await ctx.reply(_('failed_generate_image'))
|
await ctx.reply(k.failed_generate_image())
|
||||||
return
|
return
|
||||||
|
|
||||||
deepfried_path = await deepfryimage(output_path)
|
deepfried_path = await deepfryimage(output_path)
|
||||||
|
|
|
@ -2,7 +2,10 @@ from discord.ext import commands
|
||||||
import discord
|
import discord
|
||||||
from collections import defaultdict, Counter
|
from collections import defaultdict, Counter
|
||||||
import datetime
|
import datetime
|
||||||
from modules.globalvars import ownerid
|
from modules.settings import Settings as SettingsManager
|
||||||
|
settings_manager = SettingsManager()
|
||||||
|
settings = settings_manager.settings
|
||||||
|
|
||||||
class StatsCog(commands.Cog):
|
class StatsCog(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
@ -31,7 +34,7 @@ class StatsCog(commands.Cog):
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def spyware(self, ctx):
|
async def spyware(self, ctx):
|
||||||
if ctx.author.id != ownerid:
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
return
|
return
|
||||||
uptime = datetime.datetime.utcnow() - self.start_time
|
uptime = datetime.datetime.utcnow() - self.start_time
|
||||||
hours_elapsed = max((uptime.total_seconds() / 3600), 1)
|
hours_elapsed = max((uptime.total_seconds() / 3600), 1)
|
||||||
|
|
|
@ -14,12 +14,12 @@ MODEL_MATCH_STRING = r"[0-9]{2}_[0-9]{2}_[0-9]{4}-[0-9]{2}_[0-9]{2}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import tensorflow as tf
|
import tensorflow as tf
|
||||||
from tensorflow import keras
|
import keras
|
||||||
from tensorflow.keras.preprocessing.text import Tokenizer
|
from keras.preprocessing.text import Tokenizer
|
||||||
from tensorflow.keras.preprocessing.sequence import pad_sequences
|
from keras.preprocessing.sequence import pad_sequences
|
||||||
from tensorflow.keras.models import Sequential, load_model
|
from keras.models import Sequential, load_model
|
||||||
from tensorflow.keras.layers import Embedding, LSTM, Dense
|
from keras.layers import Embedding, LSTM, Dense
|
||||||
from tensorflow.keras.backend import clear_session
|
from keras.backend import clear_session
|
||||||
|
|
||||||
if tf.config.list_physical_devices('GPU'):
|
if tf.config.list_physical_devices('GPU'):
|
||||||
print("Using GPU acceleration")
|
print("Using GPU acceleration")
|
|
@ -5,7 +5,10 @@ from bs4 import BeautifulSoup
|
||||||
import json
|
import json
|
||||||
import asyncio
|
import asyncio
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
from modules.globalvars import ownerid
|
from modules.settings import Settings as SettingsManager
|
||||||
|
settings_manager = SettingsManager()
|
||||||
|
settings = settings_manager.settings
|
||||||
|
|
||||||
class WebScraper(commands.Cog):
|
class WebScraper(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
@ -81,7 +84,7 @@ class WebScraper(commands.Cog):
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def start_scrape(self, ctx, start_url: str):
|
async def start_scrape(self, ctx, start_url: str):
|
||||||
"""Command to start the scraping process."""
|
"""Command to start the scraping process."""
|
||||||
if ctx.author.id != ownerid:
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
await ctx.send("You do not have permission to use this command.")
|
await ctx.send("You do not have permission to use this command.")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -99,7 +102,7 @@ class WebScraper(commands.Cog):
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def undo_scrape(self, ctx):
|
async def undo_scrape(self, ctx):
|
||||||
"""Command to undo the last scrape."""
|
"""Command to undo the last scrape."""
|
||||||
if ctx.author.id != ownerid:
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
await ctx.send("You do not have permission to use this command.")
|
await ctx.send("You do not have permission to use this command.")
|
||||||
return
|
return
|
||||||
|
|
|
@ -99,6 +99,9 @@ class GooberWeb(commands.Cog):
|
||||||
print("Goober web server started on port 8080")
|
print("Goober web server started on port 8080")
|
||||||
|
|
||||||
async def stop_web_server(self):
|
async def stop_web_server(self):
|
||||||
|
if self.site is None or self.runner is None:
|
||||||
|
return
|
||||||
|
|
||||||
await self.site.stop()
|
await self.site.stop()
|
||||||
await self.runner.cleanup()
|
await self.runner.cleanup()
|
||||||
print("Web server stopped")
|
print("Web server stopped")
|
|
@ -131,6 +131,12 @@
|
||||||
"command_stats_embed_field2name": "Version",
|
"command_stats_embed_field2name": "Version",
|
||||||
"command_stats_embed_field2value": "Local: {local_version} \nLatest: {latest_version}",
|
"command_stats_embed_field2value": "Local: {local_version} \nLatest: {latest_version}",
|
||||||
"command_stats_embed_field3name": "Variable Info",
|
"command_stats_embed_field3name": "Variable Info",
|
||||||
"command_stats_embed_field3value": "Name: {NAME} \nPrefix: {PREFIX} \nOwner ID: {ownerid}\nPing line: {PING_LINE} \nMemory Sharing Enabled: {showmemenabled} \nUser Training Enabled: {USERTRAIN_ENABLED}\nSong: {song} \nSplashtext: ```{splashtext}```"
|
"command_stats_embed_field3value": "Name: {NAME} \nPrefix: {PREFIX} \nOwner ID: {ownerid}\nPing line: {PING_LINE} \nMemory Sharing Enabled: {showmemenabled} \nUser Training Enabled: {USERTRAIN_ENABLED}\nSong: {song} \nSplashtext: ```{splashtext}```",
|
||||||
|
"no_image_available": "No images available!",
|
||||||
|
"failed_generate_image": "Failed to generate an image",
|
||||||
|
"markov_model_not_found": "Markov model not found!",
|
||||||
|
"blacklisted": "blacklisted",
|
||||||
|
"blacklisted_user": "Blacklisted user",
|
||||||
|
"edit_fail": "Failed to edit message"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -102,7 +102,7 @@
|
||||||
"command_markov_retrain": "Uudelleenkoulutetaan markov-mallia... Odota.",
|
"command_markov_retrain": "Uudelleenkoulutetaan markov-mallia... Odota.",
|
||||||
"command_markov_memory_not_found": "Virhe: muistitiedostoa ei löytynyt!",
|
"command_markov_memory_not_found": "Virhe: muistitiedostoa ei löytynyt!",
|
||||||
"command_markov_memory_is_corrupt": "Virhe: muistitiedosto on korruptoitu!",
|
"command_markov_memory_is_corrupt": "Virhe: muistitiedosto on korruptoitu!",
|
||||||
"command_markov_retraining": "Käsitellään {processed_data}/{data_size} datapisteestä...",
|
"command_markov_retraining": "Käsitellään {processed_data}/{data_size} datapistettä...",
|
||||||
"command_markov_retrain_successful": "Markov-malli koulutettiin uudestaan {data_size} datapisteellä!",
|
"command_markov_retrain_successful": "Markov-malli koulutettiin uudestaan {data_size} datapisteellä!",
|
||||||
"command_desc_talk":"puhuu ja sillei",
|
"command_desc_talk":"puhuu ja sillei",
|
||||||
"command_talk_insufficent_text": "Minun pitää oppia lisää viesteistä ennen kun puhun.",
|
"command_talk_insufficent_text": "Minun pitää oppia lisää viesteistä ennen kun puhun.",
|
||||||
|
@ -131,5 +131,6 @@
|
||||||
"command_stats_embed_field2value": "Paikallinen: {local_version} \nUusin: {latest_version}",
|
"command_stats_embed_field2value": "Paikallinen: {local_version} \nUusin: {latest_version}",
|
||||||
"command_stats_embed_field3name": "Muuttajainformaatio",
|
"command_stats_embed_field3name": "Muuttajainformaatio",
|
||||||
"command_stats_embed_field3value": "Nimi: {NAME} \nEtuliite: {PREFIX} \nOmistajan ID: {ownerid}\nPing-linja: {PING_LINE} \nMuistin jako päällä: {showmemenabled} \nOppiminen käyttäjistä: {USERTRAIN_ENABLED}\nLaulu: {song} \nRoisketeksti: ```{splashtext}```"
|
"command_stats_embed_field3value": "Nimi: {NAME} \nEtuliite: {PREFIX} \nOmistajan ID: {ownerid}\nPing-linja: {PING_LINE} \nMuistin jako päällä: {showmemenabled} \nOppiminen käyttäjistä: {USERTRAIN_ENABLED}\nLaulu: {song} \nRoisketeksti: ```{splashtext}```"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
308
bot.py
308
bot.py
|
@ -10,12 +10,22 @@ import shutil
|
||||||
import uuid
|
import uuid
|
||||||
import asyncio
|
import asyncio
|
||||||
import sys
|
import sys
|
||||||
from typing import List, Dict, Set, Optional, Tuple, Any, Union, Callable, Coroutine, TypeVar, Type
|
from typing import List, Dict, Literal, Set, Optional, Tuple, Any, TypedDict, Union, Callable, Coroutine, TypeVar, Type
|
||||||
import logging
|
import logging
|
||||||
from modules.globalvars import *
|
from modules.globalvars import *
|
||||||
from modules.prestartchecks import start_checks
|
from modules.prestartchecks import start_checks
|
||||||
from modules.logger import GooberFormatter
|
from modules.logger import GooberFormatter
|
||||||
|
import modules.keys as k
|
||||||
|
from modules import key_compiler
|
||||||
import logging
|
import logging
|
||||||
|
from watchdog.observers import Observer
|
||||||
|
from watchdog.events import FileSystemEventHandler
|
||||||
|
from modules.settings import Settings as SettingsManager
|
||||||
|
|
||||||
|
def build_keys():
|
||||||
|
key_compiler.build_result("en", "assets/locales", types=True, output_path="modules/keys.py", generate_comments=True)
|
||||||
|
|
||||||
|
build_keys()
|
||||||
|
|
||||||
logger = logging.getLogger("goober")
|
logger = logging.getLogger("goober")
|
||||||
logger.setLevel(logging.DEBUG)
|
logger.setLevel(logging.DEBUG)
|
||||||
|
@ -31,8 +41,15 @@ file_handler.setFormatter(GooberFormatter(colors=False))
|
||||||
logger.addHandler(console_handler)
|
logger.addHandler(console_handler)
|
||||||
logger.addHandler(file_handler)
|
logger.addHandler(file_handler)
|
||||||
|
|
||||||
# Print splash text and check for updates
|
settings_manager = SettingsManager()
|
||||||
print(splashtext) # Print splash text (from modules/globalvars.py)
|
settings = settings_manager.settings
|
||||||
|
|
||||||
|
splash_text: str = ""
|
||||||
|
|
||||||
|
with open(settings["splash_text_loc"], "r", encoding="UTF-8") as f:
|
||||||
|
splash_text = "".join(f.readlines())
|
||||||
|
print(splash_text)
|
||||||
|
|
||||||
start_checks()
|
start_checks()
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
@ -45,7 +62,6 @@ from discord.abc import Messageable
|
||||||
from better_profanity import profanity
|
from better_profanity import profanity
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
from modules.volta.main import _, set_language
|
|
||||||
from modules.markovmemory import *
|
from modules.markovmemory import *
|
||||||
from modules.version import *
|
from modules.version import *
|
||||||
from modules.sentenceprocessing import *
|
from modules.sentenceprocessing import *
|
||||||
|
@ -60,8 +76,18 @@ T = TypeVar('T')
|
||||||
MessageContext = Union[commands.Context, discord.Interaction]
|
MessageContext = Union[commands.Context, discord.Interaction]
|
||||||
MessageReference = Union[Message, discord.WebhookMessage]
|
MessageReference = Union[Message, discord.WebhookMessage]
|
||||||
|
|
||||||
|
class MessageMetadata(TypedDict):
|
||||||
|
user_id: str
|
||||||
|
user_name: str
|
||||||
|
guild_id: str | Literal["DM"]
|
||||||
|
guild_name: str | Literal["DM"]
|
||||||
|
channel_id: str
|
||||||
|
channel_name: str
|
||||||
|
message: str
|
||||||
|
timestamp: float
|
||||||
|
|
||||||
# Constants with type hints
|
# Constants with type hints
|
||||||
positive_gifs: List[str] = os.getenv("POSITIVE_GIFS", "").split(',')
|
positive_gifs: List[str] = settings["bot"]["misc"]["positive_gifs"]
|
||||||
currenthash: str = ""
|
currenthash: str = ""
|
||||||
launched: bool = False
|
launched: bool = False
|
||||||
slash_commands_enabled: bool = False
|
slash_commands_enabled: bool = False
|
||||||
|
@ -70,41 +96,48 @@ slash_commands_enabled: bool = False
|
||||||
intents: discord.Intents = discord.Intents.default()
|
intents: discord.Intents = discord.Intents.default()
|
||||||
intents.messages = True
|
intents.messages = True
|
||||||
intents.message_content = True
|
intents.message_content = True
|
||||||
|
|
||||||
bot: commands.Bot = commands.Bot(
|
bot: commands.Bot = commands.Bot(
|
||||||
command_prefix=PREFIX,
|
command_prefix=settings["bot"]["prefix"],
|
||||||
intents=intents,
|
intents=intents,
|
||||||
allowed_mentions=discord.AllowedMentions(everyone=False, roles=False, users=False, replied_user=True)
|
allowed_mentions=discord.AllowedMentions(everyone=False, roles=False, users=False, replied_user=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Load memory and Markov model for text generation
|
# Load memory and Markov model for text generation
|
||||||
memory: List[str] = load_memory()
|
memory: List[str | Dict[Literal["_meta"], MessageMetadata]] = load_memory()
|
||||||
markov_model: Optional[markovify.Text] = load_markov_model()
|
markov_model: Optional[markovify.Text] = load_markov_model()
|
||||||
if not markov_model:
|
if not markov_model:
|
||||||
logger.error(_('markov_model_not_found'))
|
logger.error(k.markov_model_not_found())
|
||||||
memory = load_memory()
|
memory = load_memory()
|
||||||
markov_model = train_markov_model(memory)
|
markov_model = train_markov_model(memory)
|
||||||
|
|
||||||
generated_sentences: Set[str] = set()
|
generated_sentences: Set[str] = set()
|
||||||
used_words: Set[str] = set()
|
used_words: Set[str] = set()
|
||||||
|
|
||||||
async def load_cogs_from_folder(bot, folder_name="assets/cogs"):
|
async def load_cogs_from_folder(bot: commands.Bot, folder_name="assets/cogs"):
|
||||||
for filename in os.listdir(folder_name):
|
for filename in [file for file in os.listdir(folder_name) if file.endswith(".py")]:
|
||||||
if filename.endswith(".py") and not filename.startswith("_"):
|
cog_name:str = filename[:-3]
|
||||||
cog_name = filename[:-3]
|
print(cog_name)
|
||||||
|
|
||||||
|
if cog_name not in settings["bot"]["enabled_cogs"]:
|
||||||
|
logger.debug(f"Skipping cog {cog_name} (not in enabled cogs)")
|
||||||
|
continue
|
||||||
|
|
||||||
module_path = folder_name.replace("/", ".").replace("\\", ".") + f".{cog_name}"
|
module_path = folder_name.replace("/", ".").replace("\\", ".") + f".{cog_name}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await bot.load_extension(module_path)
|
await bot.load_extension(module_path)
|
||||||
logger.info(f"{(_('loaded_cog'))} {cog_name}")
|
logger.info(f"{k.loaded_cog()} {cog_name}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{(_('cog_fail'))} {cog_name} {e}")
|
logger.error(f"{k.cog_fail()} {cog_name} {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
async def send_alive_ping_periodically() -> None:
|
async def send_alive_ping_periodically() -> None:
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
requests.post(f"{VERSION_URL}/aliveping", json={"name": NAME})
|
requests.post(f"{VERSION_URL}/aliveping", json={"name": settings["name"]})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{(_('error_sending_alive_ping'))}{RESET} {e}")
|
logger.error(f"{k.error_sending_alive_ping(e)}{RESET} {e}")
|
||||||
await asyncio.sleep(60)
|
await asyncio.sleep(60)
|
||||||
|
|
||||||
# Event: Called when the bot is ready
|
# Event: Called when the bot is ready
|
||||||
|
@ -121,9 +154,9 @@ async def on_ready() -> None:
|
||||||
await load_cogs_from_folder(bot)
|
await load_cogs_from_folder(bot)
|
||||||
try:
|
try:
|
||||||
synced: List[discord.app_commands.AppCommand] = await bot.tree.sync()
|
synced: List[discord.app_commands.AppCommand] = await bot.tree.sync()
|
||||||
logger.info(f"{_('synced_commands')} {len(synced)} {(_('synced_commands2'))}")
|
logger.info(f"{k.synced_commands()} {len(synced)} {k.synced_commands2()}")
|
||||||
slash_commands_enabled = True
|
slash_commands_enabled = True
|
||||||
logger.info(f"{(_('started')).format(name=NAME)}")
|
logger.info(k.started(settings["name"]))
|
||||||
|
|
||||||
bot.loop.create_task(send_alive_ping_periodically())
|
bot.loop.create_task(send_alive_ping_periodically())
|
||||||
except discord.errors.Forbidden as perm_error:
|
except discord.errors.Forbidden as perm_error:
|
||||||
|
@ -131,13 +164,13 @@ async def on_ready() -> None:
|
||||||
logger.error("Make sure the bot has the 'applications.commands' scope and is invited with the correct permissions.")
|
logger.error("Make sure the bot has the 'applications.commands' scope and is invited with the correct permissions.")
|
||||||
quit()
|
quit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{_('fail_commands_sync')} {e}")
|
logger.error(f"{k.fail_commands_sync()} {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
quit()
|
quit()
|
||||||
|
|
||||||
if not song:
|
if not settings["bot"]["misc"]["active_song"]:
|
||||||
return
|
return
|
||||||
await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=f"{song}"))
|
await bot.change_presence(activity=discord.Activity(type=discord.ActivityType.listening, name=settings["bot"]["misc"]["active_song"]))
|
||||||
launched = True
|
launched = True
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
|
@ -157,41 +190,48 @@ async def on_command_error(ctx: commands.Context, error: commands.CommandError)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Command: Retrain the Markov model from memory
|
# Command: Retrain the Markov model from memory
|
||||||
@bot.hybrid_command(description=f"{(_('command_desc_retrain'))}")
|
@bot.hybrid_command(description=f"{k.command_desc_retrain()}")
|
||||||
async def retrain(ctx: commands.Context) -> None:
|
async def retrain(ctx: commands.Context) -> None:
|
||||||
if ctx.author.id != ownerid:
|
global markov_model
|
||||||
|
|
||||||
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
|
return
|
||||||
|
|
||||||
|
message_ref: discord.Message | None = await send_message(ctx, f"{k.command_markov_retrain()}")
|
||||||
|
if message_ref is None:
|
||||||
|
logger.error("Failed to send message!")
|
||||||
return
|
return
|
||||||
|
|
||||||
message_ref: MessageReference = await send_message(ctx, f"{(_('command_markov_retrain'))}")
|
|
||||||
try:
|
try:
|
||||||
with open(MEMORY_FILE, 'r') as f:
|
with open(settings["bot"]["active_memory"], 'r') as f:
|
||||||
memory: List[str] = json.load(f)
|
memory: List[str] = json.load(f)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
await send_message(ctx, f"{(_('command_markov_memory_not_found'))}")
|
await send_message(ctx, f"{k.command_markov_memory_not_found()}")
|
||||||
return
|
return
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
await send_message(ctx, f"{(_('command_markov_memory_is_corrupt'))}")
|
await send_message(ctx, f"{k.command_markov_memory_is_corrupt()}")
|
||||||
return
|
return
|
||||||
|
|
||||||
data_size: int = len(memory)
|
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)}")
|
processing_message_ref: discord.Message | None = await send_message(ctx, f"{k.command_markov_retraining(data_size)}")
|
||||||
|
if processing_message_ref is None:
|
||||||
|
logger.error("Couldnt find message processing message!")
|
||||||
|
|
||||||
start_time: float = time.time()
|
start_time: float = time.time()
|
||||||
|
|
||||||
for i, data in enumerate(memory):
|
|
||||||
processed_data += 1
|
|
||||||
|
|
||||||
global markov_model
|
|
||||||
markov_model = train_markov_model(memory)
|
markov_model = train_markov_model(memory)
|
||||||
save_markov_model(markov_model)
|
save_markov_model(markov_model)
|
||||||
|
|
||||||
await send_message(ctx, f"{_('command_markov_retrain_successful').format(data_size=data_size)}", edit=True, message_reference=processing_message_ref)
|
logger.debug(f"Completed retraining in {round(time.time() - start_time,3)}s")
|
||||||
|
|
||||||
|
await send_message(ctx, f"{k.command_markov_retrain_successful(data_size)}", edit=True, message_reference=processing_message_ref)
|
||||||
|
|
||||||
# Command: Generate a sentence using the Markov model
|
# Command: Generate a sentence using the Markov model
|
||||||
@bot.hybrid_command(description=f"{(_('command_desc_talk'))}")
|
@bot.hybrid_command(description=f"{k.command_desc_talk()}")
|
||||||
async def talk(ctx: commands.Context, sentence_size: int = 5) -> None:
|
async def talk(ctx: commands.Context, sentence_size: int = 5) -> None:
|
||||||
if not markov_model:
|
if not markov_model:
|
||||||
await send_message(ctx, f"{(_('command_talk_insufficent_text'))}")
|
await send_message(ctx, f"{k.command_talk_insufficent_text()}")
|
||||||
return
|
return
|
||||||
|
|
||||||
response: Optional[str] = None
|
response: Optional[str] = None
|
||||||
|
@ -218,65 +258,18 @@ async def talk(ctx: commands.Context, sentence_size: int = 5) -> None:
|
||||||
else:
|
else:
|
||||||
combined_message: str = coherent_response
|
combined_message: str = coherent_response
|
||||||
logger.info(combined_message)
|
logger.info(combined_message)
|
||||||
|
|
||||||
os.environ['gooberlatestgen'] = combined_message
|
os.environ['gooberlatestgen'] = combined_message
|
||||||
await send_message(ctx, combined_message)
|
await send_message(ctx, combined_message)
|
||||||
else:
|
else:
|
||||||
await send_message(ctx, f"{(_('command_talk_generation_fail'))}")
|
await send_message(ctx, f"{k.command_talk_generation_fail()}")
|
||||||
|
|
||||||
# Command: Generate an image
|
|
||||||
@bot.hybrid_command(description=f"{(_('command_desc_help'))}")
|
|
||||||
async def impact(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_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
|
|
||||||
|
|
||||||
await ctx.send(file=discord.File(output_path))
|
|
||||||
|
|
||||||
if temp_input and os.path.exists(temp_input):
|
|
||||||
os.remove(temp_input)
|
|
||||||
|
|
||||||
# New demotivator command
|
# New demotivator command
|
||||||
@bot.hybrid_command(description="Generate a demotivator poster with two lines of text")
|
@bot.hybrid_command(description="Generate a demotivator poster with two lines of text")
|
||||||
async def demotivator(ctx: commands.Context) -> None:
|
async def demotivator(ctx: commands.Context) -> None:
|
||||||
assets_folder: str = "assets/images"
|
assets_folder: str = "assets/images"
|
||||||
temp_input: Optional[str] = None
|
temp_input: str | None = None
|
||||||
|
|
||||||
def get_random_asset_image() -> Optional[str]:
|
def get_random_asset_image() -> Optional[str]:
|
||||||
files: List[str] = [f for f in os.listdir(assets_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
|
files: List[str] = [f for f in os.listdir(assets_folder) if f.lower().endswith(('.png', '.jpg', '.jpeg', '.webp'))]
|
||||||
|
@ -289,12 +282,13 @@ async def demotivator(ctx: commands.Context) -> None:
|
||||||
if attachment.content_type and attachment.content_type.startswith("image/"):
|
if attachment.content_type and attachment.content_type.startswith("image/"):
|
||||||
ext: str = os.path.splitext(attachment.filename)[1]
|
ext: str = os.path.splitext(attachment.filename)[1]
|
||||||
temp_input = f"tempy{ext}"
|
temp_input = f"tempy{ext}"
|
||||||
await attachment.save(temp_input)
|
with open(temp_input, "wb") as f:
|
||||||
|
await attachment.save(f)
|
||||||
input_path: str = temp_input
|
input_path: str = temp_input
|
||||||
else:
|
else:
|
||||||
fallback_image: Optional[str] = get_random_asset_image()
|
fallback_image: Optional[str] = get_random_asset_image()
|
||||||
if fallback_image is None:
|
if fallback_image is None:
|
||||||
await ctx.reply(_('no_image_available'))
|
await ctx.reply(k.no_image_available())
|
||||||
return
|
return
|
||||||
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
|
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
|
||||||
shutil.copy(fallback_image, temp_input)
|
shutil.copy(fallback_image, temp_input)
|
||||||
|
@ -302,7 +296,7 @@ async def demotivator(ctx: commands.Context) -> None:
|
||||||
else:
|
else:
|
||||||
fallback_image = get_random_asset_image()
|
fallback_image = get_random_asset_image()
|
||||||
if fallback_image is None:
|
if fallback_image is None:
|
||||||
await ctx.reply(_('no_image_available'))
|
await ctx.reply(k.no_image_available())
|
||||||
return
|
return
|
||||||
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
|
temp_input = tempfile.mktemp(suffix=os.path.splitext(fallback_image)[1])
|
||||||
shutil.copy(fallback_image, temp_input)
|
shutil.copy(fallback_image, temp_input)
|
||||||
|
@ -323,42 +317,43 @@ async def demotivator(ctx: commands.Context) -> None:
|
||||||
|
|
||||||
bot.remove_command('help')
|
bot.remove_command('help')
|
||||||
# Command: Show help information
|
# Command: Show help information
|
||||||
@bot.hybrid_command(description=f"{(_('command_desc_help'))}")
|
@bot.hybrid_command(description=f"{k.command_desc_help()}")
|
||||||
async def help(ctx: commands.Context) -> None:
|
async def help(ctx: commands.Context) -> None:
|
||||||
embed: discord.Embed = discord.Embed(
|
embed: discord.Embed = discord.Embed(
|
||||||
title=f"{(_('command_help_embed_title'))}",
|
title=f"{k.command_help_embed_title()}",
|
||||||
description=f"{(_('command_help_embed_desc'))}",
|
description=f"{k.command_help_embed_desc()}",
|
||||||
color=Colour(0x000000)
|
color=Colour(0x000000)
|
||||||
)
|
)
|
||||||
|
|
||||||
command_categories: Dict[str, List[str]] = {
|
command_categories: Dict[str, List[str]] = {
|
||||||
f"{(_('command_help_categories_general'))}": ["mem", "talk", "about", "ping", "impact", "demotivator", "help"],
|
f"{k.command_help_categories_general()}": ["mem", "talk", "about", "ping", "impact", "demotivator", "help"],
|
||||||
f"{(_('command_help_categories_admin'))}": ["stats", "retrain", "setlanguage"]
|
f"{k.command_help_categories_admin()}": ["stats", "retrain", "setlanguage"]
|
||||||
}
|
}
|
||||||
|
|
||||||
custom_commands: List[str] = []
|
custom_commands: List[str] = []
|
||||||
for cog_name, cog in bot.cogs.items():
|
for cog_name, cog in bot.cogs.items():
|
||||||
for command in cog.get_commands():
|
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'))}"]:
|
if command.name not in command_categories[f"{k.command_help_categories_general()}"] and command.name not in command_categories[f"{k.command_help_categories_admin()}"]:
|
||||||
custom_commands.append(command.name)
|
custom_commands.append(command.name)
|
||||||
|
|
||||||
if custom_commands:
|
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)
|
embed.add_field(name=f"{k.command_help_categories_custom()}", value="\n".join([f"{settings["bot"]["prefix"]}{command}" for command in custom_commands]), inline=False)
|
||||||
|
|
||||||
for category, commands_list in command_categories.items():
|
for category, commands_list in command_categories.items():
|
||||||
commands_in_category: str = "\n".join([f"{PREFIX}{command}" for command in commands_list])
|
commands_in_category: str = "\n".join([f"{settings["bot"]["prefix"]}{command}" for command in commands_list])
|
||||||
embed.add_field(name=category, value=commands_in_category, inline=False)
|
embed.add_field(name=category, value=commands_in_category, inline=False)
|
||||||
|
|
||||||
await send_message(ctx, embed=embed)
|
await send_message(ctx, embed=embed)
|
||||||
|
|
||||||
@bot.hybrid_command(description=f"{(_('command_desc_setlang'))}")
|
@bot.hybrid_command(description=f"{k.command_desc_setlang()}")
|
||||||
@app_commands.describe(locale="Choose your language")
|
@app_commands.describe(locale="Choose your language")
|
||||||
async def setlanguage(ctx: commands.Context, locale: str) -> None:
|
async def setlanguage(ctx: commands.Context, locale: str) -> None:
|
||||||
if ctx.author.id != ownerid:
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
await ctx.send(":thumbsdown:")
|
await ctx.send(":thumbsdown:")
|
||||||
return
|
return
|
||||||
|
|
||||||
await ctx.defer()
|
await ctx.defer()
|
||||||
set_language(locale)
|
k.change_language(locale)
|
||||||
await ctx.send(":thumbsup:")
|
await ctx.send(":thumbsup:")
|
||||||
|
|
||||||
# Event: Called on every message
|
# Event: Called on every message
|
||||||
|
@ -369,25 +364,32 @@ async def on_message(message: discord.Message) -> None:
|
||||||
if message.author.bot:
|
if message.author.bot:
|
||||||
return
|
return
|
||||||
|
|
||||||
if str(message.author.id) in BLACKLISTED_USERS:
|
if str(message.author.id) in settings["bot"]["blacklisted_users"]:
|
||||||
return
|
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)}")
|
commands = [settings["bot"]["prefix"] + command.name for command in bot.tree.get_commands()]
|
||||||
|
|
||||||
|
if message.content.startswith(tuple(commands)):
|
||||||
|
logger.info(f"{k.command_ran(message.author.name, message.content)}")
|
||||||
await bot.process_commands(message)
|
await bot.process_commands(message)
|
||||||
return
|
return
|
||||||
|
|
||||||
if profanity.contains_profanity(message.content):
|
if profanity.contains_profanity(message.content) and settings["bot"]["misc"]["block_profanity"]:
|
||||||
return
|
return
|
||||||
|
|
||||||
if message.content:
|
if message.content:
|
||||||
if not USERTRAIN_ENABLED:
|
if not settings["bot"]["user_training"]:
|
||||||
return
|
return
|
||||||
|
|
||||||
formatted_message: str = append_mentions_to_18digit_integer(message.content)
|
formatted_message: str = append_mentions_to_18digit_integer(message.content)
|
||||||
cleaned_message: str = preprocess_message(formatted_message)
|
cleaned_message: str = preprocess_message(formatted_message)
|
||||||
if cleaned_message:
|
if cleaned_message:
|
||||||
memory.append(cleaned_message)
|
memory.append(cleaned_message)
|
||||||
message_metadata = {
|
|
||||||
|
|
||||||
|
|
||||||
|
message_metadata: MessageMetadata = {
|
||||||
"user_id": str(message.author.id),
|
"user_id": str(message.author.id),
|
||||||
"user_name": str(message.author),
|
"user_name": str(message.author),
|
||||||
"guild_id": str(message.guild.id) if message.guild else "DM",
|
"guild_id": str(message.guild.id) if message.guild else "DM",
|
||||||
|
@ -409,7 +411,7 @@ async def on_message(message: discord.Message) -> None:
|
||||||
|
|
||||||
sentiment_score = is_positive(message.content) # doesnt work but im scared to change the logic now please ignore
|
sentiment_score = is_positive(message.content) # doesnt work but im scared to change the logic now please ignore
|
||||||
if sentiment_score > 0.8:
|
if sentiment_score > 0.8:
|
||||||
if REACT != "True":
|
if not settings["bot"]["react_to_messages"]:
|
||||||
return
|
return
|
||||||
emoji = random.choice(EMOJIS)
|
emoji = random.choice(EMOJIS)
|
||||||
try:
|
try:
|
||||||
|
@ -422,27 +424,27 @@ async def on_message(message: discord.Message) -> None:
|
||||||
# Event: Called on every interaction (slash command, etc.)
|
# Event: Called on every interaction (slash command, etc.)
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_interaction(interaction: discord.Interaction) -> None:
|
async def on_interaction(interaction: discord.Interaction) -> None:
|
||||||
logger.info(f"{(_('command_ran_s')).format(interaction=interaction)}{interaction.data['name']}")
|
logger.info(f"{k.command_ran_s(interaction.user.name)} {interaction.user.name}")
|
||||||
|
|
||||||
# Global check: Block blacklisted users from running commands
|
# Global check: Block blacklisted users from running commands
|
||||||
@bot.check
|
@bot.check
|
||||||
async def block_blacklisted(ctx: commands.Context) -> bool:
|
async def block_blacklisted(ctx: commands.Context) -> bool:
|
||||||
if str(ctx.author.id) in BLACKLISTED_USERS:
|
if str(ctx.author.id) in settings["bot"]["blacklisted_users"]:
|
||||||
try:
|
try:
|
||||||
if isinstance(ctx, discord.Interaction):
|
if isinstance(ctx, discord.Interaction):
|
||||||
if not ctx.response.is_done():
|
if not ctx.response.is_done():
|
||||||
await ctx.response.send_message(_('blacklisted'), ephemeral=True)
|
await ctx.response.send_message(k.blacklisted(), ephemeral=True)
|
||||||
else:
|
else:
|
||||||
await ctx.followup.send(_('blacklisted'), ephemeral=True)
|
await ctx.followup.send(k.blacklisted(), ephemeral=True)
|
||||||
else:
|
else:
|
||||||
await ctx.send(_('blacklisted_user'), ephemeral=True)
|
await ctx.send(k.blacklisted_user(), ephemeral=True)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# Command: Show bot latency
|
# Command: Show bot latency
|
||||||
@bot.hybrid_command(description=f"{(_('command_desc_ping'))}")
|
@bot.hybrid_command(description=f"{k.command_desc_ping()}")
|
||||||
async def ping(ctx: commands.Context) -> None:
|
async def ping(ctx: commands.Context) -> None:
|
||||||
await ctx.defer()
|
await ctx.defer()
|
||||||
latency: int = round(bot.latency * 1000)
|
latency: int = round(bot.latency * 1000)
|
||||||
|
@ -450,35 +452,44 @@ async def ping(ctx: commands.Context) -> None:
|
||||||
LOLembed: discord.Embed = discord.Embed(
|
LOLembed: discord.Embed = discord.Embed(
|
||||||
title="Pong!!",
|
title="Pong!!",
|
||||||
description=(
|
description=(
|
||||||
f"{PING_LINE}\n"
|
settings["bot"]["misc"]["ping_line"],
|
||||||
f"`{(_('command_ping_embed_desc'))}: {latency}ms`\n"
|
f"`{k.command_ping_embed_desc()}: {latency}ms`\n"
|
||||||
),
|
),
|
||||||
color=Colour(0x000000)
|
color=Colour(0x000000)
|
||||||
)
|
)
|
||||||
LOLembed.set_footer(text=f"{(_('command_ping_footer'))} {ctx.author.name}", icon_url=ctx.author.avatar.url)
|
LOLembed.set_footer(text=f"{k.command_ping_footer()} {ctx.author.name}", icon_url=ctx.author.display_avatar.url)
|
||||||
|
|
||||||
await ctx.send(embed=LOLembed)
|
await ctx.send(embed=LOLembed)
|
||||||
|
|
||||||
# Command: Show about information
|
# Command: Show about information
|
||||||
@bot.hybrid_command(description=f"{(_('command_about_desc'))}")
|
@bot.hybrid_command(description=f"{k.command_about_desc()}")
|
||||||
async def about(ctx: commands.Context) -> None:
|
async def about(ctx: commands.Context) -> None:
|
||||||
print("-----------------------------------\n\n")
|
print("-----------------------------------\n\n")
|
||||||
latest_version: str = check_for_update()
|
latest_version: str = str(check_for_update())
|
||||||
print("-----------------------------------")
|
print("-----------------------------------")
|
||||||
embed: discord.Embed = discord.Embed(title=f"{(_('command_about_embed_title'))}", description="", color=Colour(0x000000))
|
embed: discord.Embed = discord.Embed(title=f"{k.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=k.command_about_embed_field1(), value=f"{settings["name"]}", inline=False)
|
||||||
embed.add_field(name=f"Github", value=f"https://github.com/gooberinc/goober")
|
|
||||||
|
embed.add_field(
|
||||||
|
name=k.command_about_embed_field2name(),
|
||||||
|
value=k.command_about_embed_field2value(
|
||||||
|
local_version=local_version, latest_version=latest_version
|
||||||
|
),
|
||||||
|
inline=False
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.add_field(name="Github", value=f"https://github.com/gooberinc/goober")
|
||||||
|
|
||||||
await send_message(ctx, embed=embed)
|
await send_message(ctx, embed=embed)
|
||||||
|
|
||||||
# Command: Show bot statistics (admin only)
|
# Command: Show bot statistics (admin only)
|
||||||
@bot.hybrid_command(description="stats")
|
@bot.hybrid_command(description="stats")
|
||||||
async def stats(ctx: commands.Context) -> None:
|
async def stats(ctx: commands.Context) -> None:
|
||||||
if ctx.author.id != ownerid:
|
if ctx.author.id not in settings["bot"]["owner_ids"]:
|
||||||
return
|
return
|
||||||
print("-----------------------------------\n\n")
|
print("-----------------------------------\n\n")
|
||||||
latest_version: str = check_for_update()
|
latest_version: str = str(check_for_update())
|
||||||
print("-----------------------------------")
|
print("-----------------------------------")
|
||||||
memory_file: str = 'memory.json'
|
memory_file: str = 'memory.json'
|
||||||
file_size: int = os.path.getsize(memory_file)
|
file_size: int = os.path.getsize(memory_file)
|
||||||
|
@ -486,18 +497,24 @@ async def stats(ctx: commands.Context) -> None:
|
||||||
with open(memory_file, 'r') as file:
|
with open(memory_file, 'r') as file:
|
||||||
line_count: int = sum(1 for _ in file)
|
line_count: int = sum(1 for _ in file)
|
||||||
|
|
||||||
embed: discord.Embed = discord.Embed(title=f"{(_('command_stats_embed_title'))}", description=f"{(_('command_stats_embed_desc'))}", color=Colour(0x000000))
|
embed: discord.Embed = discord.Embed(title=f"{k.command_stats_embed_title()}", description=f"{k.command_stats_embed_desc()}", color=Colour(0x000000))
|
||||||
embed.add_field(name=f"{(_('command_stats_embed_field1name'))}", value=f"{(_('command_stats_embed_field1value')).format(file_size=file_size, line_count=line_count)}", inline=False)
|
embed.add_field(name=f"{k.command_stats_embed_field1name()}", value=f"{k.command_stats_embed_field1value(file_size=file_size, line_count=line_count)}", inline=False)
|
||||||
embed.add_field(name=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"{k.command_stats_embed_field2name()}", value=f"{k.command_stats_embed_field2value(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"{k.command_stats_embed_field3name()}", value=f"{k.command_stats_embed_field3value(
|
||||||
|
NAME=settings["name"], PREFIX=settings["bot"]["prefix"], ownerid=settings["bot"]["owner_ids"][0],
|
||||||
|
PING_LINE=settings["bot"]["misc"]["ping_line"], showmemenabled=settings["bot"]["allow_show_mem_command"],
|
||||||
|
USERTRAIN_ENABLED=settings["bot"]["user_training"], song=settings["bot"]["misc"]["active_song"],
|
||||||
|
splashtext=splash_text
|
||||||
|
)}", inline=False)
|
||||||
|
|
||||||
await send_message(ctx, embed=embed)
|
await send_message(ctx, embed=embed)
|
||||||
|
|
||||||
# Command: Upload memory.json to litterbox.catbox.moe and return the link
|
# Command: Upload memory.json to litterbox.catbox.moe and return the link
|
||||||
@bot.hybrid_command()
|
@bot.hybrid_command()
|
||||||
async def mem(ctx: commands.Context) -> None:
|
async def mem(ctx: commands.Context) -> None:
|
||||||
if showmemenabled != "true":
|
if not settings["bot"]["allow_show_mem_command"]:
|
||||||
return
|
return
|
||||||
|
|
||||||
command: str = """curl -F "reqtype=fileupload" -F "time=1h" -F "fileToUpload=@memory.json" https://litterbox.catbox.moe/resources/internals/api.php"""
|
command: str = """curl -F "reqtype=fileupload" -F "time=1h" -F "fileToUpload=@memory.json" https://litterbox.catbox.moe/resources/internals/api.php"""
|
||||||
memorylitter: subprocess.CompletedProcess = subprocess.run(command, shell=True, capture_output=True, text=True)
|
memorylitter: subprocess.CompletedProcess = subprocess.run(command, shell=True, capture_output=True, text=True)
|
||||||
logger.debug(memorylitter)
|
logger.debug(memorylitter)
|
||||||
|
@ -509,5 +526,36 @@ def improve_sentence_coherence(sentence: str) -> str:
|
||||||
sentence = sentence.replace(" i ", " I ")
|
sentence = sentence.replace(" i ", " I ")
|
||||||
return sentence
|
return sentence
|
||||||
|
|
||||||
|
|
||||||
|
class OnMyWatch:
|
||||||
|
watchDirectory = "assets/locales"
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.observer = Observer()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
event_handler = Handler()
|
||||||
|
self.observer.schedule(event_handler, self.watchDirectory, recursive = True)
|
||||||
|
self.observer.start()
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
time.sleep(5)
|
||||||
|
except:
|
||||||
|
self.observer.stop()
|
||||||
|
print("Observer Stopped")
|
||||||
|
|
||||||
|
self.observer.join()
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(FileSystemEventHandler):
|
||||||
|
def on_any_event(self, event):
|
||||||
|
if event.is_directory:
|
||||||
|
return None
|
||||||
|
|
||||||
|
elif event.event_type == 'modified':
|
||||||
|
build_keys()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Start the bot
|
# Start the bot
|
||||||
bot.run(TOKEN)
|
bot.run(os.environ.get("DISCORD_BOT_TOKEN", ""))
|
|
@ -23,37 +23,38 @@ YELLOW = f"{ANSI}33m"
|
||||||
PURPLE = f"{ANSI}35m"
|
PURPLE = f"{ANSI}35m"
|
||||||
DEBUG = f"{ANSI}1;30m"
|
DEBUG = f"{ANSI}1;30m"
|
||||||
RESET = f"{ANSI}0m"
|
RESET = f"{ANSI}0m"
|
||||||
|
|
||||||
VERSION_URL = "https://raw.githubusercontent.com/gooberinc/version/main"
|
VERSION_URL = "https://raw.githubusercontent.com/gooberinc/version/main"
|
||||||
UPDATE_URL = VERSION_URL+"/latest_version.json"
|
UPDATE_URL = VERSION_URL+"/latest_version.json"
|
||||||
print(UPDATE_URL)
|
print(UPDATE_URL)
|
||||||
LOCAL_VERSION_FILE = "current_version.txt"
|
LOCAL_VERSION_FILE = "current_version.txt"
|
||||||
TOKEN = os.getenv("DISCORDBOTTOKEN", "0")
|
|
||||||
PREFIX = os.getenv("BOTPREFIX", "g.")
|
# TOKEN = os.getenv("DISCORDBOTTOKEN", "0")
|
||||||
PING_LINE = os.getenv("PINGLINE")
|
# PREFIX = os.getenv("BOTPREFIX", "g.")
|
||||||
CHECKS_DISABLED = os.getenv("CHECKSDISABLED")
|
# PING_LINE = os.getenv("PINGLINE")
|
||||||
LOCALE = os.getenv("LOCALE", "en")
|
# CHECKS_DISABLED = os.getenv("CHECKSDISABLED")
|
||||||
gooberTOKEN = os.getenv("GOOBERTOKEN")
|
# LOCALE = os.getenv("LOCALE", "en")
|
||||||
splashtext = os.getenv("SPLASHTEXT")
|
# BLACKLISTED_USERS = os.getenv("BLACKLISTEDUSERS", "").split(",")
|
||||||
ownerid = int(os.getenv("OWNERID", "0"))
|
# USERTRAIN_ENABLED = os.getenv("USERTRAINENABLED", "true").lower() == "true"
|
||||||
showmemenabled = os.getenv("SHOWMEMENABLED")
|
# NAME = os.getenv("NAME")
|
||||||
BLACKLISTED_USERS = os.getenv("BLACKLISTEDUSERS", "").split(",")
|
# MEMORY_FILE = "memory.json"
|
||||||
USERTRAIN_ENABLED = os.getenv("USERTRAINENABLED", "true").lower() == "true"
|
# MEMORY_LOADED_FILE = "MEMORY_LOADED" # is this still even used?? okay just checked its used in the markov module
|
||||||
NAME = os.getenv("NAME")
|
# ALIVEPING = os.getenv("ALIVEPING")
|
||||||
MEMORY_FILE = "memory.json"
|
# AUTOUPDATE = os.getenv("AUTOUPDATE")
|
||||||
MEMORY_LOADED_FILE = "MEMORY_LOADED" # is this still even used?? okay just checked its used in the markov module
|
# REACT = os.getenv("REACT")
|
||||||
ALIVEPING = os.getenv("ALIVEPING")
|
|
||||||
AUTOUPDATE = os.getenv("AUTOUPDATE")
|
# gooberTOKEN = os.getenv("GOOBERTOKEN")
|
||||||
|
# splashtext = os.getenv("SPLASHTEXT")
|
||||||
|
# ownerid = int(os.getenv("OWNERID", "0"))
|
||||||
|
# showmemenabled = os.getenv("SHOWMEMENABLED")
|
||||||
|
|
||||||
|
|
||||||
# IGNOREWARNING = False # is this either??? i don't think so?
|
# IGNOREWARNING = False # is this either??? i don't think so?
|
||||||
song = os.getenv("song")
|
# song = os.getenv("song")
|
||||||
arch = platform.machine()
|
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.3"
|
local_version = "2.3.3"
|
||||||
os.environ['gooberlocal_version'] = local_version
|
os.environ['gooberlocal_version'] = local_version
|
||||||
REACT = os.getenv("REACT")
|
beta = get_git_branch() == "dev"
|
||||||
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
|
|
||||||
|
|
191
modules/key_compiler.py
Normal file
191
modules/key_compiler.py
Normal file
|
@ -0,0 +1,191 @@
|
||||||
|
# The MIT License (MIT)
|
||||||
|
|
||||||
|
# Copyright (c) 2025 ctih1
|
||||||
|
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
import os
|
||||||
|
from typing import Dict, List, Literal
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
import time
|
||||||
|
|
||||||
|
NOTICE = """
|
||||||
|
# This file was automatically created from localization JSON files.
|
||||||
|
# DO NOT EDIT THIS FILE DIRECTLY. If you want to edit a translation, please use the language's JSON file.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format="%(levelname)s: [%(filename)s:%(funcName)s] %(message)s",
|
||||||
|
datefmt="%d/%m/%Y %H.%M.%S",
|
||||||
|
stream=sys.stdout,
|
||||||
|
)
|
||||||
|
|
||||||
|
logger = logging.getLogger("kaannos")
|
||||||
|
|
||||||
|
class LanguageCollector:
|
||||||
|
def __init__(self, language_dir: str) -> None:
|
||||||
|
self.path: str = language_dir
|
||||||
|
self.languages: Dict[str, Dict[str,str]] = {}
|
||||||
|
|
||||||
|
for file in os.listdir(self.path):
|
||||||
|
if not file.endswith(".json") or len(file) > 7:
|
||||||
|
logger.debug(f"Skipping {file}")
|
||||||
|
continue
|
||||||
|
|
||||||
|
locale: str = file.split(".json")[0]
|
||||||
|
logger.info(f"Discovered {file}")
|
||||||
|
with open(os.path.join(self.path, file), "r", encoding="UTF-8") as f:
|
||||||
|
keys: Dict[str,str] = json.load(f)
|
||||||
|
self.languages[locale] = keys
|
||||||
|
|
||||||
|
print(self.languages)
|
||||||
|
self.find_missing_keys()
|
||||||
|
|
||||||
|
|
||||||
|
def find_missing_keys(self) -> None:
|
||||||
|
primary_language_keys: Dict[str, str] = self.languages["en"]
|
||||||
|
|
||||||
|
for key in primary_language_keys:
|
||||||
|
for language in self.languages:
|
||||||
|
if key not in self.languages[language]:
|
||||||
|
logger.warning(f"Key {key} missing from {language}")
|
||||||
|
|
||||||
|
for language in self.languages:
|
||||||
|
for key in self.languages[language]:
|
||||||
|
if key not in primary_language_keys:
|
||||||
|
logger.warning(f"Leftover key {key} found from {language}")
|
||||||
|
|
||||||
|
class Script:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.script: str = ""
|
||||||
|
|
||||||
|
def add_line(self, content, indent: int=0, newline: bool = True) -> None:
|
||||||
|
self.script += f"{'\t' * indent}{content}{'\n' if newline else ''}"
|
||||||
|
|
||||||
|
def process_name(key: str) -> str:
|
||||||
|
return key.replace(" ", "_").replace(":","").lower()
|
||||||
|
|
||||||
|
def find_args(string: str) -> List[str]:
|
||||||
|
variable_open: bool = False
|
||||||
|
temp_content: str = ""
|
||||||
|
|
||||||
|
variables: List[str] = []
|
||||||
|
for char in string:
|
||||||
|
if variable_open:
|
||||||
|
if char == "}":
|
||||||
|
variable_open = False
|
||||||
|
variables.append(temp_content)
|
||||||
|
temp_content = ""
|
||||||
|
continue
|
||||||
|
|
||||||
|
if char == "{":
|
||||||
|
raise SyntaxError("Variable already open!")
|
||||||
|
|
||||||
|
temp_content += char
|
||||||
|
|
||||||
|
else:
|
||||||
|
if char == "}":
|
||||||
|
raise SyntaxError("Trying to close a nonexistant variable")
|
||||||
|
|
||||||
|
if char == "{":
|
||||||
|
variable_open = True
|
||||||
|
|
||||||
|
return variables
|
||||||
|
|
||||||
|
def convert_args(inp: str, vars: List[str], mode: Literal["brackets", "none"] = "brackets") -> str:
|
||||||
|
replacements = {
|
||||||
|
".": "_",
|
||||||
|
",": "_"
|
||||||
|
}
|
||||||
|
|
||||||
|
for var in vars:
|
||||||
|
cleaned_var = var
|
||||||
|
for key, val in replacements.items():
|
||||||
|
cleaned_var = cleaned_var.replace(key, val)
|
||||||
|
|
||||||
|
if mode == "none":
|
||||||
|
inp = inp.replace(f"{var}", f"{cleaned_var}")
|
||||||
|
else:
|
||||||
|
inp = inp.replace(f"{{{var}}}", f"{{{cleaned_var}}}")
|
||||||
|
|
||||||
|
return inp
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class GenerateScript:
|
||||||
|
def __init__(self, primary_lang:str, language_data: Dict[str, Dict[str,str]], use_typing: bool = True, output_path: str = "out.py", generate_comments: bool = True):
|
||||||
|
self.data = language_data
|
||||||
|
self.primary = primary_lang
|
||||||
|
self.script = Script()
|
||||||
|
self.uses_typing: bool = use_typing
|
||||||
|
self.output = output_path
|
||||||
|
self.generate_comments = generate_comments
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
# I really don't like this implementation but also it works
|
||||||
|
self.script.add_line(NOTICE)
|
||||||
|
if self.uses_typing:
|
||||||
|
self.script.add_line("from typing import Literal, List")
|
||||||
|
self.script.add_line(f"Language=Literal{list(self.data.keys())}")
|
||||||
|
self.script.add_line(f"languages: List[Language] = {list(self.data.keys())}")
|
||||||
|
self.script.add_line(f"default_lang: Language | str='{self.primary}'")
|
||||||
|
self.script.add_line("def change_language(new_lang: Language | str) -> None: global default_lang; default_lang = new_lang")
|
||||||
|
else:
|
||||||
|
self.script.add_line(f"languages = {list(self.data.keys())}")
|
||||||
|
self.script.add_line(f"default_lang='{self.primary}'")
|
||||||
|
self.script.add_line("def change_language(new_lang): global default_lang; default_lang = new_lang")
|
||||||
|
|
||||||
|
|
||||||
|
self.primary_data = self.data[self.primary]
|
||||||
|
|
||||||
|
for key in self.primary_data:
|
||||||
|
args = find_args(self.primary_data[key])
|
||||||
|
|
||||||
|
self.script.add_line(f"def {process_name(key)}({convert_args(','.join([*args, "lang:str|None=None" if self.uses_typing else "lang"]), args, "none")}):")
|
||||||
|
if self.generate_comments:
|
||||||
|
self.script.add_line('"""', 1)
|
||||||
|
self.script.add_line("### Locales", 1)
|
||||||
|
for language in self.data:
|
||||||
|
self.script.add_line(f'- {language.capitalize()}: **{self.data[language].get(key, self.primary_data[key])}**', 1)
|
||||||
|
self.script.add_line('"""', 1)
|
||||||
|
self.script.add_line("if not lang: lang=default_lang", 1)
|
||||||
|
for language in self.data:
|
||||||
|
formatted_map = "{"
|
||||||
|
for arg in args:
|
||||||
|
formatted_map += f'"{convert_args(arg, args, "none")}": {convert_args(arg, args, "none")},'
|
||||||
|
formatted_map = formatted_map[:-1] + "}"
|
||||||
|
self.script.add_line(f"""if lang == '{language}': return {convert_args(json.dumps(
|
||||||
|
self.data[language].get(key,self.primary_data[key]),
|
||||||
|
ensure_ascii=False
|
||||||
|
), args)}{f'.format_map({formatted_map})' if len(args) > 0 else ''}""", 1)
|
||||||
|
|
||||||
|
self.script.add_line("else: raise ValueError(f'Invalid language {lang}')", 1)
|
||||||
|
with open(self.output, "w", encoding="UTF-8") as f:
|
||||||
|
f.write(self.script.script)
|
||||||
|
|
||||||
|
|
||||||
|
def build_result(primary_lang: str, locale_dir: str, types: bool, output_path: str, generate_comments: bool = True):
|
||||||
|
start = time.time()
|
||||||
|
lc = LanguageCollector(locale_dir)
|
||||||
|
GenerateScript(primary_lang, lc.languages, types, output_path, generate_comments).create()
|
||||||
|
logger.info(f"Done in {time.time() - start}s")
|
2306
modules/keys.py
Normal file
2306
modules/keys.py
Normal file
File diff suppressed because it is too large
Load diff
|
@ -3,8 +3,13 @@ import json
|
||||||
import markovify
|
import markovify
|
||||||
import pickle
|
import pickle
|
||||||
from modules.globalvars import *
|
from modules.globalvars import *
|
||||||
from modules.volta.main import _
|
|
||||||
import logging
|
import logging
|
||||||
|
import modules.keys as k
|
||||||
|
from modules.settings import Settings as SettingsManager
|
||||||
|
settings_manager = SettingsManager()
|
||||||
|
settings = settings_manager.settings
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("goober")
|
logger = logging.getLogger("goober")
|
||||||
# Get file size and line count for a given file path
|
# Get file size and line count for a given file path
|
||||||
def get_file_info(file_path):
|
def get_file_info(file_path):
|
||||||
|
@ -22,7 +27,7 @@ def load_memory():
|
||||||
|
|
||||||
# Try to load data from MEMORY_FILE
|
# Try to load data from MEMORY_FILE
|
||||||
try:
|
try:
|
||||||
with open(MEMORY_FILE, "r") as f:
|
with open(settings["bot"]["active_memory"], "r") as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
@ -31,7 +36,7 @@ def load_memory():
|
||||||
|
|
||||||
# Save memory data to MEMORY_FILE
|
# Save memory data to MEMORY_FILE
|
||||||
def save_memory(memory):
|
def save_memory(memory):
|
||||||
with open(MEMORY_FILE, "w") as f:
|
with open(settings["bot"]["active_memory"], "w") as f:
|
||||||
json.dump(memory, f, indent=4)
|
json.dump(memory, f, indent=4)
|
||||||
|
|
||||||
def train_markov_model(memory, additional_data=None):
|
def train_markov_model(memory, additional_data=None):
|
||||||
|
@ -57,8 +62,8 @@ def load_markov_model(filename='markov_model.pkl'):
|
||||||
try:
|
try:
|
||||||
with open(filename, 'rb') as f:
|
with open(filename, 'rb') as f:
|
||||||
model = pickle.load(f)
|
model = pickle.load(f)
|
||||||
logger.info(f"{_('model_loaded')} {filename}.{RESET}")
|
logger.info(f"{k.model_loaded()} {filename}.{RESET}")
|
||||||
return model
|
return model
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logger.error(f"{filename} {_('not_found')}{RESET}")
|
logger.error(f"{filename} {k.not_found()}{RESET}")
|
||||||
return None
|
return None
|
|
@ -1,5 +1,4 @@
|
||||||
from modules.globalvars import *
|
from modules.globalvars import *
|
||||||
from modules.volta.main import _, check_missing_translations
|
|
||||||
import time
|
import time
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
@ -11,6 +10,11 @@ import re
|
||||||
from spacy.util import is_package
|
from spacy.util import is_package
|
||||||
import importlib.metadata
|
import importlib.metadata
|
||||||
import logging
|
import logging
|
||||||
|
import modules.keys as k
|
||||||
|
from modules.settings import Settings as SettingsManager
|
||||||
|
settings_manager = SettingsManager()
|
||||||
|
settings = settings_manager.settings
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger("goober")
|
logger = logging.getLogger("goober")
|
||||||
|
|
||||||
|
@ -21,7 +25,7 @@ try:
|
||||||
import psutil
|
import psutil
|
||||||
except ImportError:
|
except ImportError:
|
||||||
psutilavaliable = False
|
psutilavaliable = False
|
||||||
logger.error(_('missing_requests_psutil'))
|
logger.error(k.missing_requests_psutil())
|
||||||
|
|
||||||
def check_for_model():
|
def check_for_model():
|
||||||
if is_package("en_core_web_sm"):
|
if is_package("en_core_web_sm"):
|
||||||
|
@ -34,7 +38,7 @@ def iscloned():
|
||||||
if os.path.exists(".git"):
|
if os.path.exists(".git"):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
logger.error(f"{_('not_cloned')}")
|
logger.error(f"{k.not_cloned()}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def get_stdlib_modules():
|
def get_stdlib_modules():
|
||||||
|
@ -67,7 +71,7 @@ def check_requirements():
|
||||||
requirements_path = os.path.abspath(os.path.join(parent_dir, '..', 'requirements.txt'))
|
requirements_path = os.path.abspath(os.path.join(parent_dir, '..', 'requirements.txt'))
|
||||||
|
|
||||||
if not os.path.exists(requirements_path):
|
if not os.path.exists(requirements_path):
|
||||||
logger.error(f"{(_('requirements_not_found')).format(path=requirements_path)}")
|
logger.error(f"{k.requirements_not_found(path=requirements_path)}")
|
||||||
return
|
return
|
||||||
|
|
||||||
with open(requirements_path, 'r') as f:
|
with open(requirements_path, 'r') as f:
|
||||||
|
@ -85,24 +89,24 @@ def check_requirements():
|
||||||
|
|
||||||
for req in sorted(requirements):
|
for req in sorted(requirements):
|
||||||
if req in STD_LIB_MODULES or req == 'modules':
|
if req in STD_LIB_MODULES or req == 'modules':
|
||||||
print((_('std_lib_local_skipped')).format(package=req))
|
print(k.std_lib_local_skipped(package=req))
|
||||||
continue
|
continue
|
||||||
|
|
||||||
check_name = req.lower()
|
check_name = req.lower()
|
||||||
|
|
||||||
if check_name in installed_packages:
|
if check_name in installed_packages:
|
||||||
logger.info(f"{_('ok_installed').format(package=check_name)} {check_name}")
|
logger.info(f"{k.ok_installed()} {check_name}")
|
||||||
else:
|
else:
|
||||||
logger.error(f"{(_('missing_package')).format(package=check_name)} {check_name} {(_('missing_package2'))}")
|
logger.error(f"{k.missing_package()} {check_name} {k.missing_package2()}")
|
||||||
missing.append(check_name)
|
missing.append(check_name)
|
||||||
|
|
||||||
if missing:
|
if missing:
|
||||||
logger.error(_('missing_packages_detected'))
|
logger.error(k.missing_packages_detected())
|
||||||
for pkg in missing:
|
for pkg in missing:
|
||||||
print(f" - {pkg}")
|
print(f" - {pkg}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
logger.info(_('all_requirements_satisfied'))
|
logger.info(k.all_requirements_satisfied())
|
||||||
|
|
||||||
def check_latency():
|
def check_latency():
|
||||||
host = "1.1.1.1"
|
host = "1.1.1.1"
|
||||||
|
@ -132,16 +136,16 @@ def check_latency():
|
||||||
match = re.search(latency_pattern, result.stdout)
|
match = re.search(latency_pattern, result.stdout)
|
||||||
if match:
|
if match:
|
||||||
latency_ms = float(match.group(1))
|
latency_ms = float(match.group(1))
|
||||||
logger.info((_('ping_to')).format(host=host, latency=latency_ms))
|
logger.info(k.ping_to(host=host, latency=latency_ms))
|
||||||
if latency_ms > 300:
|
if latency_ms > 300:
|
||||||
logger.warning(f"{(_('high_latency'))}")
|
logger.warning(f"{k.high_latency()}")
|
||||||
else:
|
else:
|
||||||
logger.warning((_('could_not_parse_latency')))
|
logger.warning(k.could_not_parse_latency())
|
||||||
else:
|
else:
|
||||||
print(result.stderr)
|
print(result.stderr)
|
||||||
logger.error(f"{(_('ping_failed')).format(host=host)}{RESET}")
|
logger.error(f"{k.ping_failed(host=host)}{RESET}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error((_('error_running_ping')).format(error=e))
|
logger.error(k.error_running_ping(error=e))
|
||||||
|
|
||||||
def check_memory():
|
def check_memory():
|
||||||
if psutilavaliable == False:
|
if psutilavaliable == False:
|
||||||
|
@ -152,48 +156,48 @@ def check_memory():
|
||||||
used_memory = memory_info.used / (1024 ** 3)
|
used_memory = memory_info.used / (1024 ** 3)
|
||||||
free_memory = memory_info.available / (1024 ** 3)
|
free_memory = memory_info.available / (1024 ** 3)
|
||||||
|
|
||||||
logger.info((_('memory_usage')).format(used=used_memory, total=total_memory, percent=(used_memory / total_memory) * 100))
|
logger.info(k.memory_usage(used=used_memory, total=total_memory, percent=(used_memory / total_memory) * 100))
|
||||||
if used_memory > total_memory * 0.9:
|
if used_memory > total_memory * 0.9:
|
||||||
print(f"{YELLOW}{(_('memory_above_90')).format(percent=(used_memory / total_memory) * 100)}{RESET}")
|
print(f"{YELLOW}{k.memory_above_90(percent=(used_memory / total_memory) * 100)}{RESET}")
|
||||||
logger.info((_('total_memory')).format(total=total_memory))
|
logger.info(k.total_memory(total=total_memory))
|
||||||
logger.info((_('used_memory')).format(used=used_memory))
|
logger.info(k.used_memory(used=used_memory))
|
||||||
if free_memory < 1:
|
if free_memory < 1:
|
||||||
logger.warning(f"{(_('low_free_memory')).format(free=free_memory)}")
|
logger.warning(f"{k.low_free_memory(free=free_memory)}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logger.error(_('psutil_not_installed')) # todo: translate this into italian and put it in the translations "psutil is not installed. Memory check skipped."
|
logger.error(k.psutil_not_installed()) # todo: translate this into italian and put it in the translations "psutil is not installed. Memory check skipped."
|
||||||
|
|
||||||
def check_cpu():
|
def check_cpu():
|
||||||
if psutilavaliable == False:
|
if psutilavaliable == False:
|
||||||
return
|
return
|
||||||
logger.info((_('measuring_cpu')))
|
logger.info(k.measuring_cpu())
|
||||||
cpu_per_core = psutil.cpu_percent(interval=1, percpu=True) # type: ignore
|
cpu_per_core = psutil.cpu_percent(interval=1, percpu=True) # type: ignore
|
||||||
total_cpu = sum(cpu_per_core) / len(cpu_per_core)
|
total_cpu = sum(cpu_per_core) / len(cpu_per_core)
|
||||||
logger.info((_('total_cpu_usage')).format(usage=total_cpu))
|
logger.info(k.total_cpu_usage(usage=total_cpu))
|
||||||
if total_cpu > 85:
|
if total_cpu > 85:
|
||||||
logger.warning(f"{(_('high_avg_cpu')).format(usage=total_cpu)}")
|
logger.warning(f"{k.high_avg_cpu(usage=total_cpu)}")
|
||||||
if total_cpu > 95:
|
if total_cpu > 95:
|
||||||
logger.error(_('really_high_cpu'))
|
logger.error(k.really_high_cpu())
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def check_memoryjson():
|
def check_memoryjson():
|
||||||
try:
|
try:
|
||||||
logger.info((_('memory_file')).format(size=os.path.getsize(MEMORY_FILE) / (1024 ** 2)))
|
logger.info(k.memory_file(size=os.path.getsize(settings["bot"]["active_memory"]) / (1024 ** 2)))
|
||||||
if os.path.getsize(MEMORY_FILE) > 1_073_741_824:
|
if os.path.getsize(settings["bot"]["active_memory"]) > 1_073_741_824:
|
||||||
logger.warning(f"{(_('memory_file_large'))}")
|
logger.warning(f"{k.memory_file_large()}")
|
||||||
try:
|
try:
|
||||||
with open(MEMORY_FILE, 'r', encoding='utf-8') as f:
|
with open(settings["bot"]["active_memory"], 'r', encoding='utf-8') as f:
|
||||||
json.load(f)
|
json.load(f)
|
||||||
except json.JSONDecodeError as e:
|
except json.JSONDecodeError as e:
|
||||||
logger.error(f"{(_('memory_file_corrupted')).format(error=e)}")
|
logger.error(f"{k.memory_file_corrupted(error=e)}")
|
||||||
logger.warning(f"{(_('consider_backup_memory'))}")
|
logger.warning(f"{k.consider_backup_memory()}")
|
||||||
except UnicodeDecodeError as e:
|
except UnicodeDecodeError as e:
|
||||||
logger.error(f"{(_('memory_file_encoding')).format(error=e)}")
|
logger.error(f"{k.memory_file_encoding(error=e)}")
|
||||||
logger.warning(f"{(_('consider_backup_memory'))}")
|
logger.warning(f"{k.consider_backup_memory()}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"{(_('error_reading_memory')).format(error=e)}")
|
logger.error(f"{k.error_reading_memory(error=e)}")
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logger(f"{(_('memory_file_not_found'))}")
|
logger.info(f"{k.memory_file_not_found()}")
|
||||||
|
|
||||||
def presskey2skip(timeout):
|
def presskey2skip(timeout):
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
|
@ -228,13 +232,13 @@ def presskey2skip(timeout):
|
||||||
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
||||||
beta = beta
|
beta = beta
|
||||||
def start_checks():
|
def start_checks():
|
||||||
if CHECKS_DISABLED == "True":
|
if settings["disable_checks"]:
|
||||||
logger.warning(f"{(_('checks_disabled'))}")
|
logger.warning(f"{k.checks_disabled()}")
|
||||||
return
|
return
|
||||||
logger.info(_('running_prestart_checks'))
|
|
||||||
|
logger.info(k.running_prestart_checks())
|
||||||
check_for_model()
|
check_for_model()
|
||||||
iscloned()
|
iscloned()
|
||||||
check_missing_translations()
|
|
||||||
check_requirements()
|
check_requirements()
|
||||||
check_latency()
|
check_latency()
|
||||||
check_memory()
|
check_memory()
|
||||||
|
@ -243,13 +247,15 @@ def start_checks():
|
||||||
if os.path.exists(".env"):
|
if os.path.exists(".env"):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
logger.warning(f"{(_('env_file_not_found'))}")
|
logger.warning(f"{k.env_file_not_found()}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if beta == True:
|
if beta == True:
|
||||||
logger.warning(f"this build isnt finished yet, some things might not work as expected")
|
logger.warning(f"this build isnt finished yet, some things might not work as expected")
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
logger.info(_('continuing_in_seconds').format(seconds=5))
|
logger.info(k.continuing_in_seconds(seconds=5))
|
||||||
presskey2skip(timeout=5)
|
presskey2skip(timeout=5)
|
||||||
os.system('cls' if os.name == 'nt' else 'clear')
|
os.system('cls' if os.name == 'nt' else 'clear')
|
||||||
print(splashtext)
|
|
||||||
|
with open(settings ["splash_text_loc"], "r") as f:
|
||||||
|
print("".join(f.readlines()))
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
import re
|
import re
|
||||||
|
import discord.ext
|
||||||
|
import discord.ext.commands
|
||||||
from modules.globalvars import *
|
from modules.globalvars import *
|
||||||
from modules.volta.main import _
|
|
||||||
|
|
||||||
import spacy
|
import spacy
|
||||||
from spacy.tokens import Doc
|
from spacy.tokens import Doc
|
||||||
from spacytextblob.spacytextblob import SpacyTextBlob
|
from spacytextblob.spacytextblob import SpacyTextBlob
|
||||||
|
import discord
|
||||||
|
import modules.keys as k
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger("goober")
|
logger = logging.getLogger("goober")
|
||||||
|
@ -14,12 +16,12 @@ def check_resources():
|
||||||
try:
|
try:
|
||||||
nlp = spacy.load("en_core_web_sm")
|
nlp = spacy.load("en_core_web_sm")
|
||||||
except OSError:
|
except OSError:
|
||||||
logging.critical((_('spacy_model_not_found')))
|
logging.critical(k.spacy_model_not_found())
|
||||||
spacy.cli.download("en_core_web_sm")
|
spacy.cli.download("en_core_web_sm") # type: ignore
|
||||||
nlp = spacy.load("en_core_web_sm")
|
nlp = spacy.load("en_core_web_sm")
|
||||||
if "spacytextblob" not in nlp.pipe_names:
|
if "spacytextblob" not in nlp.pipe_names:
|
||||||
nlp.add_pipe("spacytextblob")
|
nlp.add_pipe("spacytextblob")
|
||||||
logger.info((_('spacy_initialized')))
|
logger.info(k.spacy_initialized())
|
||||||
|
|
||||||
check_resources()
|
check_resources()
|
||||||
|
|
||||||
|
@ -31,34 +33,36 @@ def is_positive(sentence):
|
||||||
doc = nlp(sentence)
|
doc = nlp(sentence)
|
||||||
sentiment_score = doc._.polarity # from spacytextblob
|
sentiment_score = doc._.polarity # from spacytextblob
|
||||||
|
|
||||||
debug_message = f"{(_('sentence_positivity'))} {sentiment_score}{RESET}"
|
debug_message = f"{k.sentence_positivity()} {sentiment_score}{RESET}"
|
||||||
logger.debug(debug_message)
|
logger.debug(debug_message)
|
||||||
|
|
||||||
return sentiment_score > 0.6 # had to raise the bar because it kept saying "death to jews" was fine and it kept reacting to them
|
return sentiment_score > 0.6 # had to raise the bar because it kept saying "death to jews" was fine and it kept reacting to them
|
||||||
|
|
||||||
async def send_message(ctx, message=None, embed=None, file=None, edit=False, message_reference=None):
|
async def send_message(ctx: discord.ext.commands.Context,
|
||||||
|
message: str | None = None,
|
||||||
|
embed: discord.Embed | None = None,
|
||||||
|
file: discord.File | None = None,
|
||||||
|
edit: bool = False,
|
||||||
|
message_reference: discord.Message | None = None
|
||||||
|
) -> discord.Message | None:
|
||||||
|
|
||||||
|
sent_message: discord.Message | None = None
|
||||||
|
|
||||||
if edit and message_reference:
|
if edit and message_reference:
|
||||||
try:
|
try:
|
||||||
await message_reference.edit(content=message, embed=embed)
|
await message_reference.edit(content=message, embed=embed)
|
||||||
|
return message_reference
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await ctx.send(f"{RED}{(_('edit_fail'))} {e}{RESET}")
|
await ctx.send(f"{k.edit_fail()} {e}")
|
||||||
else:
|
return None
|
||||||
if hasattr(ctx, "respond"):
|
|
||||||
sent_message = None
|
|
||||||
if embed:
|
if embed:
|
||||||
sent_message = await ctx.respond(embed=embed, ephemeral=False)
|
sent_message = await ctx.send(embed=embed, content=message)
|
||||||
elif message:
|
elif file:
|
||||||
sent_message = await ctx.respond(message, ephemeral=False)
|
sent_message = await ctx.send(file=file, content=message)
|
||||||
if file:
|
|
||||||
sent_message = await ctx.respond(file=file, ephemeral=False)
|
|
||||||
else:
|
else:
|
||||||
sent_message = None
|
sent_message = await ctx.send(content=message)
|
||||||
if embed:
|
|
||||||
sent_message = await ctx.send(embed=embed)
|
|
||||||
elif message:
|
|
||||||
sent_message = await ctx.send(message)
|
|
||||||
if file:
|
|
||||||
sent_message = await ctx.send(file=file)
|
|
||||||
return sent_message
|
return sent_message
|
||||||
|
|
||||||
def append_mentions_to_18digit_integer(message):
|
def append_mentions_to_18digit_integer(message):
|
||||||
|
|
58
modules/settings.py
Normal file
58
modules/settings.py
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from typing import List, Mapping, Any, TypedDict
|
||||||
|
from modules.keys import Language
|
||||||
|
import logging
|
||||||
|
import copy
|
||||||
|
|
||||||
|
logger = logging.getLogger("goober")
|
||||||
|
|
||||||
|
class MiscBotOptions(TypedDict):
|
||||||
|
ping_line: str
|
||||||
|
active_song: str
|
||||||
|
positive_gifs: List[str]
|
||||||
|
block_profanity: bool
|
||||||
|
|
||||||
|
class BotSettings(TypedDict):
|
||||||
|
prefix: str
|
||||||
|
owner_ids: List[int]
|
||||||
|
blacklisted_users: List[int]
|
||||||
|
user_training: bool
|
||||||
|
allow_show_mem_command: bool
|
||||||
|
react_to_messages: bool
|
||||||
|
misc: MiscBotOptions
|
||||||
|
enabled_cogs: List[str]
|
||||||
|
active_memory: str
|
||||||
|
|
||||||
|
class SettingsType(TypedDict):
|
||||||
|
bot: BotSettings
|
||||||
|
locale: Language
|
||||||
|
name: str
|
||||||
|
auto_update: bool
|
||||||
|
disable_checks: bool
|
||||||
|
splash_text_loc: str
|
||||||
|
|
||||||
|
class Settings:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.path: str = os.path.join(".", "settings", "settings.json")
|
||||||
|
|
||||||
|
if not os.path.exists(self.path):
|
||||||
|
raise ValueError("settings.json file does not exist!")
|
||||||
|
|
||||||
|
self.settings: SettingsType
|
||||||
|
self.original_settings: SettingsType
|
||||||
|
|
||||||
|
with open(self.path, "r") as f:
|
||||||
|
self.__kv_store: dict = json.load(f)
|
||||||
|
|
||||||
|
self.settings = SettingsType(self.__kv_store) # type: ignore
|
||||||
|
self.original_settings = copy.deepcopy(self.settings)
|
||||||
|
|
||||||
|
def commit(self) -> None:
|
||||||
|
with open(self.path, "w") as f:
|
||||||
|
json.dump(self.settings, f, indent=4)
|
||||||
|
|
||||||
|
self.original_settings = self.settings
|
||||||
|
|
||||||
|
def discard(self) -> None:
|
||||||
|
self.settings = self.original_settings
|
|
@ -1,8 +1,14 @@
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import os
|
import os
|
||||||
from modules.globalvars import RED, RESET, splashtext
|
from modules.settings import Settings as SettingsManager
|
||||||
from modules.volta.main import _
|
import logging
|
||||||
|
from modules.globalvars import RED, RESET
|
||||||
|
import modules.keys as k
|
||||||
|
|
||||||
|
settings_manager = SettingsManager()
|
||||||
|
settings = settings_manager.settings
|
||||||
|
logger = logging.getLogger("goober")
|
||||||
|
|
||||||
def handle_exception(exc_type, exc_value, exc_traceback, *, context=None):
|
def handle_exception(exc_type, exc_value, exc_traceback, *, context=None):
|
||||||
os.system('cls' if os.name == 'nt' else 'clear')
|
os.system('cls' if os.name == 'nt' else 'clear')
|
||||||
|
@ -11,11 +17,13 @@ def handle_exception(exc_type, exc_value, exc_traceback, *, context=None):
|
||||||
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
sys.__excepthook__(exc_type, exc_value, exc_traceback)
|
||||||
return
|
return
|
||||||
|
|
||||||
print(splashtext)
|
with open(settings['splash_text_loc'], "r") as f:
|
||||||
|
print("".join(f.readlines()))
|
||||||
|
|
||||||
print(f"{RED}=====BEGINNING OF TRACEBACK====={RESET}")
|
print(f"{RED}=====BEGINNING OF TRACEBACK====={RESET}")
|
||||||
traceback.print_exception(exc_type, exc_value, exc_traceback)
|
traceback.print_exception(exc_type, exc_value, exc_traceback)
|
||||||
print(f"{RED}========END OF TRACEBACK========{RESET}")
|
print(f"{RED}========END OF TRACEBACK========{RESET}")
|
||||||
print(f"{RED}{_('unhandled_exception')}{RESET}")
|
print(f"{RED}{k.unhandled_exception()}{RESET}")
|
||||||
|
|
||||||
|
|
||||||
if context:
|
if context:
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
from modules.volta.main import _
|
|
||||||
from modules.globalvars import *
|
|
||||||
import requests
|
import requests
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
@ -7,6 +5,13 @@ import logging
|
||||||
import json
|
import json
|
||||||
import time
|
import time
|
||||||
import random
|
import random
|
||||||
|
import modules.keys as k
|
||||||
|
from modules.globalvars import *
|
||||||
|
from modules.settings import Settings as SettingsManager
|
||||||
|
|
||||||
|
settings_manager = SettingsManager()
|
||||||
|
settings = settings_manager.settings
|
||||||
|
|
||||||
logger = logging.getLogger("goober")
|
logger = logging.getLogger("goober")
|
||||||
launched = False
|
launched = False
|
||||||
|
|
||||||
|
@ -24,18 +29,18 @@ def is_remote_ahead(branch='main', remote='origin'):
|
||||||
# Automatically update the local repository if the remote is ahead
|
# Automatically update the local repository if the remote is ahead
|
||||||
def auto_update(branch='main', remote='origin'):
|
def auto_update(branch='main', remote='origin'):
|
||||||
if launched == True:
|
if launched == True:
|
||||||
print(_("already_started"))
|
print(k.already_started())
|
||||||
return
|
return
|
||||||
if AUTOUPDATE != "True":
|
if settings["auto_update"] != "True":
|
||||||
pass # Auto-update is disabled
|
pass # Auto-update is disabled
|
||||||
if is_remote_ahead(branch, remote):
|
if is_remote_ahead(branch, remote):
|
||||||
print(_( "remote_ahead").format(remote=remote, branch=branch))
|
logger.info(k.remote_ahead(remote, branch))
|
||||||
pull_result = run_cmd(f'git pull {remote} {branch}')
|
pull_result = run_cmd(f'git pull {remote} {branch}')
|
||||||
logger.info(pull_result)
|
logger.info(pull_result)
|
||||||
logger.info(_( "please_restart"))
|
logger.info(k.please_restart())
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
else:
|
else:
|
||||||
logger.info(_( "local_ahead").format(remote=remote, branch=branch))
|
logger.info(k.local_ahead(remote, branch))
|
||||||
|
|
||||||
def get_latest_version_info():
|
def get_latest_version_info():
|
||||||
try:
|
try:
|
||||||
|
@ -75,31 +80,34 @@ def check_for_update():
|
||||||
|
|
||||||
latest_version_info = get_latest_version_info()
|
latest_version_info = get_latest_version_info()
|
||||||
if not latest_version_info:
|
if not latest_version_info:
|
||||||
logger.error(f"{_('fetch_update_fail')}")
|
logger.error(f"{k.fetch_update_fail()}")
|
||||||
return None, None
|
return None
|
||||||
|
|
||||||
latest_version = latest_version_info.get("version")
|
latest_version = latest_version_info.get("version")
|
||||||
os.environ['gooberlatest_version'] = latest_version
|
os.environ['gooberlatest_version'] = latest_version
|
||||||
download_url = latest_version_info.get("download_url")
|
download_url = latest_version_info.get("download_url")
|
||||||
|
|
||||||
if not latest_version or not download_url:
|
if not latest_version or not download_url:
|
||||||
logger.error(f"{RED}{_('invalid_server')}{RESET}")
|
logger.error(k.invalid_server())
|
||||||
return None, None
|
return None
|
||||||
|
|
||||||
# Check if local_version is valid
|
# Check if local_version is valid
|
||||||
if local_version == "0.0.0" or None:
|
if local_version == "0.0.0" or None:
|
||||||
logger.error(f"{RED}{_('cant_find_local_version')}{RESET}")
|
logger.error(k.cant_find_local_version())
|
||||||
return
|
return
|
||||||
# Compare local and latest versions
|
# Compare local and latest versions
|
||||||
|
|
||||||
if local_version < latest_version:
|
if local_version < latest_version:
|
||||||
logger.info(f"{YELLOW}{_('new_version').format(latest_version=latest_version, local_version=local_version)}{RESET}")
|
logger.warning(k.new_version(latest_version=latest_version, local_version=local_version))
|
||||||
logger.info(f"{YELLOW}{_('changelog').format(VERSION_URL=VERSION_URL)}{RESET}")
|
logger.warning(k.changelog(VERSION_URL=VERSION_URL))
|
||||||
auto_update()
|
auto_update()
|
||||||
|
|
||||||
elif beta == True:
|
elif beta == True:
|
||||||
logger.warning(f"You are running an \"unstable\" version of Goober, do not expect it to work properly.\nVersion {local_version}\nServer: {latest_version}{RESET}")
|
logger.warning(f"You are running an \"unstable\" version of Goober, do not expect it to work properly.\nVersion {local_version}\nServer: {latest_version}{RESET}")
|
||||||
elif local_version > latest_version:
|
elif local_version > latest_version:
|
||||||
logger.warning(f"{_('modification_warning')}")
|
logger.warning(f"{k.modification_warning()}")
|
||||||
elif local_version == latest_version:
|
elif local_version == latest_version:
|
||||||
logger.info(f"{_('latest_version')} {local_version}")
|
logger.info(f"{k.latest_version()} {local_version}")
|
||||||
logger.info(f"{_('latest_version2').format(VERSION_URL=VERSION_URL)}\n\n")
|
logger.info(f"{k.latest_version2(VERSION_URL=VERSION_URL)}\n\n")
|
||||||
launched = True
|
launched = True
|
||||||
return latest_version
|
return latest_version
|
47
replace_volta.py
Normal file
47
replace_volta.py
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
folder_path = "."
|
||||||
|
|
||||||
|
# Real trap regex 😮💨 — group(1)=key, group(2)=format args (optional)
|
||||||
|
pattern = re.compile(
|
||||||
|
r"""
|
||||||
|
(?<!\w) # not part of a variable name
|
||||||
|
\(? # optional opening (
|
||||||
|
_\(\s*'([a-zA-Z0-9_]+)'\s*\) # k.key()
|
||||||
|
\)? # optional closing )
|
||||||
|
(?:\.format\((.*?)\))? # optional .format(...)
|
||||||
|
""",
|
||||||
|
re.VERBOSE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def fix_content(content):
|
||||||
|
def repl(match):
|
||||||
|
key = match.group(1)
|
||||||
|
args = match.group(2)
|
||||||
|
if args:
|
||||||
|
return f"k.{key}({args})"
|
||||||
|
else:
|
||||||
|
return f"k.{key}()"
|
||||||
|
|
||||||
|
return pattern.sub(repl, content)
|
||||||
|
|
||||||
|
# File types we sweepin 🧹
|
||||||
|
file_exts = [".py", ".html", ".txt", ".js"]
|
||||||
|
|
||||||
|
for subdir, _, files in os.walk(folder_path):
|
||||||
|
for file in files:
|
||||||
|
if any(file.endswith(ext) for ext in file_exts):
|
||||||
|
path = os.path.join(subdir, file)
|
||||||
|
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
original = f.read()
|
||||||
|
|
||||||
|
updated = fix_content(original)
|
||||||
|
|
||||||
|
if original != updated:
|
||||||
|
print(f"🛠️ Fixed: {path}")
|
||||||
|
with open(path, "w", encoding="utf-8") as f:
|
||||||
|
f.write(updated)
|
||||||
|
|
||||||
|
print("🚀💥 ALL cleaned. No `_('...')` left on road — now it’s k.dot or nothin fam 😎🔫")
|
27
settings/settings.json
Normal file
27
settings/settings.json
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"bot": {
|
||||||
|
"prefix": "\u00e4.",
|
||||||
|
"owner_ids": [
|
||||||
|
642441889181728810
|
||||||
|
],
|
||||||
|
"blacklisted_users": [],
|
||||||
|
"user_training": true,
|
||||||
|
"allow_show_mem_command": true,
|
||||||
|
"react_to_messages": true,
|
||||||
|
"misc": {
|
||||||
|
"ping_line": "The Beretta fires fast and won't make you feel any better!",
|
||||||
|
"active_song": "Basket Case - Green Day",
|
||||||
|
"positive_gifs": [],
|
||||||
|
"block_profanity": false
|
||||||
|
},
|
||||||
|
"active_memory": "memory.json",
|
||||||
|
"enabled_cogs": [
|
||||||
|
"cogmanager"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locale": "fi",
|
||||||
|
"name": "gubert",
|
||||||
|
"auto_update": true,
|
||||||
|
"disable_checks": false,
|
||||||
|
"splash_text_loc": "settings/splash.txt"
|
||||||
|
}
|
11
settings/splash.txt
Normal file
11
settings/splash.txt
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
SS\
|
||||||
|
SS |
|
||||||
|
SSSSSS\ SSSSSS\ SSSSSS\ SSSSSSS\ SSSSSS\ SSSSSS\
|
||||||
|
SS __SS\ SS __SS\ SS __SS\ SS __SS\ SS __SS\ SS __SS\
|
||||||
|
SS / SS |SS / SS |SS / SS |SS | SS |SSSSSSSS |SS | \__|
|
||||||
|
SS | SS |SS | SS |SS | SS |SS | SS |SS ____|SS |
|
||||||
|
\SSSSSSS |\SSSSSS |\SSSSSS |SSSSSSS |\SSSSSSS\ SS |
|
||||||
|
\____SS | \______/ \______/ \_______/ \_______|\__|
|
||||||
|
SS\ SS |
|
||||||
|
\SSSSSS |
|
||||||
|
\______/
|
Loading…
Add table
Add a link
Reference in a new issue