2025-01-05 21:03:49 +01:00
import os
import re
2025-06-22 19:07:41 +02:00
import json
import time
import random
import traceback
2025-01-05 21:03:49 +01:00
import subprocess
2025-06-29 19:44:56 +02:00
import tempfile
import shutil
2025-07-01 15:38:03 +02:00
import uuid
2025-07-01 20:26:48 +02:00
import asyncio
2025-07-05 20:15:54 +02:00
import sys
2025-07-22 19:32:19 +03:00
from typing import List , Dict , Literal , Set , Optional , Tuple , Any , TypedDict , Union , Callable , Coroutine , TypeVar , Type
2025-07-08 17:02:10 +03:00
import logging
2025-06-27 19:45:44 +02:00
from modules . globalvars import *
from modules . prestartchecks import start_checks
2025-07-08 17:02:10 +03:00
from modules . logger import GooberFormatter
2025-07-22 19:32:19 +03:00
import modules . keys as k
from modules import key_compiler
2025-07-08 17:02:10 +03:00
import logging
2025-07-22 19:32:19 +03:00
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 ( )
2025-07-08 17:02:10 +03:00
logger = logging . getLogger ( " goober " )
logger . setLevel ( logging . DEBUG )
console_handler = logging . StreamHandler ( )
console_handler . setLevel ( logging . DEBUG )
console_handler . setFormatter ( GooberFormatter ( ) )
file_handler = logging . FileHandler ( " log.txt " , mode = " w+ " , encoding = " UTF-8 " )
file_handler . setLevel ( logging . DEBUG )
file_handler . setFormatter ( GooberFormatter ( colors = False ) )
logger . addHandler ( console_handler )
logger . addHandler ( file_handler )
2025-07-22 19:32:19 +03:00
settings_manager = SettingsManager ( )
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 )
2025-06-27 19:45:44 +02:00
start_checks ( )
2025-06-22 19:07:41 +02:00
import requests
import discord
2025-06-28 16:56:05 -04:00
from discord . ext import commands
2025-07-07 13:05:50 +02:00
from discord import app_commands
2025-07-05 20:15:54 +02:00
from discord import Colour , Embed , File , Interaction , Message
from discord . abc import Messageable
2025-06-22 19:07:41 +02:00
from better_profanity import profanity
2025-06-28 16:56:05 -04:00
from discord . ext import commands
2025-06-27 19:45:44 +02:00
2025-06-20 23:48:10 +02:00
from modules . markovmemory import *
from modules . version import *
2025-06-21 12:00:49 +02:00
from modules . sentenceprocessing import *
2025-06-22 20:49:07 +02:00
from modules . unhandledexception import handle_exception
2025-07-05 20:15:54 +02:00
from modules . image import gen_meme , gen_demotivator
2025-03-16 16:39:26 +01:00
2025-06-27 19:45:44 +02:00
sys . excepthook = handle_exception
2025-06-22 19:07:41 +02:00
check_for_update ( ) # Check for updates (from modules/version.py)
2025-03-16 16:39:26 +01:00
2025-07-05 20:15:54 +02:00
# Type aliases
T = TypeVar ( ' T ' )
MessageContext = Union [ commands . Context , discord . Interaction ]
MessageReference = Union [ Message , discord . WebhookMessage ]
2025-03-16 16:39:26 +01:00
2025-07-22 19:32:19 +03:00
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
2025-07-05 20:15:54 +02:00
# Constants with type hints
2025-07-22 19:32:19 +03:00
positive_gifs : List [ str ] = settings [ " bot " ] [ " misc " ] [ " positive_gifs " ]
2025-07-05 20:15:54 +02:00
currenthash : str = " "
launched : bool = False
slash_commands_enabled : bool = False
2025-01-05 21:03:49 +01:00
2025-06-22 19:07:41 +02:00
# Set up Discord bot intents and create bot instance
2025-07-05 20:15:54 +02:00
intents : discord . Intents = discord . Intents . default ( )
2025-01-05 21:03:49 +01:00
intents . messages = True
intents . message_content = True
2025-07-22 19:32:19 +03:00
2025-07-05 20:15:54 +02:00
bot : commands . Bot = commands . Bot (
2025-07-22 19:32:19 +03:00
command_prefix = settings [ " bot " ] [ " prefix " ] ,
2025-06-22 19:07:41 +02:00
intents = intents ,
allowed_mentions = discord . AllowedMentions ( everyone = False , roles = False , users = False , replied_user = True )
)
# Load memory and Markov model for text generation
2025-07-22 19:32:19 +03:00
memory : List [ str | Dict [ Literal [ " _meta " ] , MessageMetadata ] ] = load_memory ( )
2025-07-05 20:15:54 +02:00
markov_model : Optional [ markovify . Text ] = load_markov_model ( )
2025-01-05 21:03:49 +01:00
if not markov_model :
2025-07-22 19:32:19 +03:00
logger . error ( k . markov_model_not_found ( ) )
2025-01-05 21:03:49 +01:00
memory = load_memory ( )
markov_model = train_markov_model ( memory )
2025-07-05 20:15:54 +02:00
generated_sentences : Set [ str ] = set ( )
used_words : Set [ str ] = set ( )
2025-07-22 19:32:19 +03:00
async def load_cogs_from_folder ( bot : commands . Bot , folder_name = " assets/cogs " ) :
for filename in [ file for file in os . listdir ( folder_name ) if file . endswith ( " .py " ) ] :
cog_name : str = filename [ : - 3 ]
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 } "
try :
await bot . load_extension ( module_path )
logger . info ( f " { k . loaded_cog ( ) } { cog_name } " )
except Exception as e :
logger . error ( f " { k . cog_fail ( ) } { cog_name } { e } " )
traceback . print_exc ( )
2025-01-05 21:03:49 +01:00
2025-07-05 20:15:54 +02:00
async def send_alive_ping_periodically ( ) - > None :
2025-07-01 20:26:48 +02:00
while True :
try :
2025-07-22 19:32:19 +03:00
requests . post ( f " { VERSION_URL } /aliveping " , json = { " name " : settings [ " name " ] } )
2025-07-01 20:26:48 +02:00
except Exception as e :
2025-07-22 19:32:19 +03:00
logger . error ( f " { k . error_sending_alive_ping ( e ) } { RESET } { e } " )
2025-07-01 20:26:48 +02:00
await asyncio . sleep ( 60 )
2025-06-22 19:07:41 +02:00
# Event: Called when the bot is ready
2025-01-05 21:03:49 +01:00
@bot.event
2025-07-05 20:15:54 +02:00
async def on_ready ( ) - > None :
2025-06-21 00:33:33 +02:00
global launched
2025-06-22 20:49:07 +02:00
global slash_commands_enabled
2025-06-23 14:10:59 +02:00
global NAME
2025-07-05 20:15:54 +02:00
folder_name : str = " cogs "
if launched :
2025-06-21 00:35:02 +02:00
return
2025-07-05 20:15:54 +02:00
2025-06-22 21:51:27 +02:00
await load_cogs_from_folder ( bot )
2025-01-05 21:03:49 +01:00
try :
2025-07-05 20:15:54 +02:00
synced : List [ discord . app_commands . AppCommand ] = await bot . tree . sync ( )
2025-07-22 19:32:19 +03:00
logger . info ( f " { k . synced_commands ( ) } { len ( synced ) } { k . synced_commands2 ( ) } " )
2025-01-05 21:03:49 +01:00
slash_commands_enabled = True
2025-07-22 19:32:19 +03:00
logger . info ( k . started ( settings [ " name " ] ) )
2025-07-02 15:58:51 +02:00
2025-07-01 20:26:48 +02:00
bot . loop . create_task ( send_alive_ping_periodically ( ) )
2025-06-22 19:07:41 +02:00
except discord . errors . Forbidden as perm_error :
2025-07-08 17:02:10 +03:00
logger . error ( f " Permission error while syncing commands: { perm_error } " )
logger . error ( " Make sure the bot has the ' applications.commands ' scope and is invited with the correct permissions. " )
2025-06-22 19:07:41 +02:00
quit ( )
2025-01-05 21:03:49 +01:00
except Exception as e :
2025-07-22 19:32:19 +03:00
logger . error ( f " { k . fail_commands_sync ( ) } { e } " )
2025-03-16 16:39:26 +01:00
traceback . print_exc ( )
quit ( )
2025-07-05 20:15:54 +02:00
2025-07-22 19:32:19 +03:00
if not settings [ " bot " ] [ " misc " ] [ " active_song " ] :
2025-01-05 21:03:49 +01:00
return
2025-07-22 19:32:19 +03:00
await bot . change_presence ( activity = discord . Activity ( type = discord . ActivityType . listening , name = settings [ " bot " ] [ " misc " ] [ " active_song " ] ) )
2025-06-21 00:29:44 +02:00
launched = True
2025-01-05 21:03:49 +01:00
2025-06-22 20:49:07 +02:00
@bot.event
2025-07-05 20:15:54 +02:00
async def on_command_error ( ctx : commands . Context , error : commands . CommandError ) - > None :
2025-06-22 20:49:07 +02:00
from modules . unhandledexception import handle_exception
if isinstance ( error , commands . CommandInvokeError ) :
2025-07-05 20:15:54 +02:00
original : Exception = error . original
2025-06-22 20:49:07 +02:00
handle_exception (
type ( original ) , original , original . __traceback__ ,
context = f " Command: { ctx . command } | User: { ctx . author } "
)
else :
handle_exception (
type ( error ) , error , error . __traceback__ ,
context = f " Command: { ctx . command } | User: { ctx . author } "
)
2025-06-22 19:07:41 +02:00
# Command: Retrain the Markov model from memory
2025-07-22 19:32:19 +03:00
@bot.hybrid_command ( description = f " { k . command_desc_retrain ( ) } " )
2025-07-05 20:15:54 +02:00
async def retrain ( ctx : commands . Context ) - > None :
2025-07-22 19:32:19 +03:00
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! " )
2025-01-05 21:03:49 +01:00
return
try :
2025-07-22 19:32:19 +03:00
with open ( settings [ " bot " ] [ " active_memory " ] , ' r ' ) as f :
2025-07-05 20:15:54 +02:00
memory : List [ str ] = json . load ( f )
2025-01-05 21:03:49 +01:00
except FileNotFoundError :
2025-07-22 19:32:19 +03:00
await send_message ( ctx , f " { k . command_markov_memory_not_found ( ) } " )
2025-01-05 21:03:49 +01:00
return
except json . JSONDecodeError :
2025-07-22 19:32:19 +03:00
await send_message ( ctx , f " { k . command_markov_memory_is_corrupt ( ) } " )
2025-01-05 21:03:49 +01:00
return
2025-07-22 19:32:19 +03:00
2025-07-05 20:15:54 +02:00
data_size : int = len ( memory )
2025-07-22 19:32:19 +03:00
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! " )
2025-07-05 20:15:54 +02:00
start_time : float = time . time ( )
2025-01-05 21:03:49 +01:00
markov_model = train_markov_model ( memory )
save_markov_model ( markov_model )
2025-07-22 19:32:19 +03:00
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 )
2025-01-05 21:03:49 +01:00
2025-06-22 19:07:41 +02:00
# Command: Generate a sentence using the Markov model
2025-07-22 19:32:19 +03:00
@bot.hybrid_command ( description = f " { k . command_desc_talk ( ) } " )
2025-07-05 20:15:54 +02:00
async def talk ( ctx : commands . Context , sentence_size : int = 5 ) - > None :
2025-01-05 21:03:49 +01:00
if not markov_model :
2025-07-22 19:32:19 +03:00
await send_message ( ctx , f " { k . command_talk_insufficent_text ( ) } " )
2025-01-05 21:03:49 +01:00
return
2025-07-05 20:15:54 +02:00
response : Optional [ str ] = None
2025-03-23 20:18:52 +01:00
for _ in range ( 20 ) :
if sentence_size == 1 :
response = markov_model . make_short_sentence ( max_chars = 100 , tries = 100 )
if response :
response = response . split ( ) [ 0 ]
else :
response = markov_model . make_sentence ( tries = 100 , max_words = sentence_size )
2025-01-05 21:03:49 +01:00
if response and response not in generated_sentences :
2025-03-23 20:18:52 +01:00
if sentence_size > 1 :
response = improve_sentence_coherence ( response )
2025-01-05 21:03:49 +01:00
generated_sentences . add ( response )
break
if response :
2025-07-05 20:15:54 +02:00
cleaned_response : str = re . sub ( r ' [^ \ w \ s] ' , ' ' , response ) . lower ( )
coherent_response : str = rephrase_for_coherence ( cleaned_response )
2025-06-22 19:07:41 +02:00
if random . random ( ) < 0.9 and is_positive ( coherent_response ) :
2025-07-05 20:15:54 +02:00
gif_url : str = random . choice ( positive_gifs )
combined_message : str = f " { coherent_response } \n [jif]( { gif_url } ) "
2025-01-05 21:03:49 +01:00
else :
2025-07-05 20:15:54 +02:00
combined_message : str = coherent_response
2025-07-08 17:02:10 +03:00
logger . info ( combined_message )
2025-07-22 19:32:19 +03:00
2025-03-30 18:53:57 +02:00
os . environ [ ' gooberlatestgen ' ] = combined_message
2025-01-05 21:03:49 +01:00
await send_message ( ctx , combined_message )
else :
2025-07-22 19:32:19 +03:00
await send_message ( ctx , f " { k . command_talk_generation_fail ( ) } " )
2025-01-05 21:03:49 +01:00
2025-06-29 19:44:56 +02:00
2025-07-05 20:15:54 +02:00
# New demotivator command
@bot.hybrid_command ( description = " Generate a demotivator poster with two lines of text " )
async def demotivator ( ctx : commands . Context ) - > None :
assets_folder : str = " assets/images "
2025-07-22 19:32:19 +03:00
temp_input : str | None = None
2025-07-05 20:15:54 +02:00
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 } "
2025-07-22 19:32:19 +03:00
with open ( temp_input , " wb " ) as f :
await attachment . save ( f )
2025-07-05 20:15:54 +02:00
input_path : str = temp_input
else :
fallback_image : Optional [ str ] = get_random_asset_image ( )
if fallback_image is None :
2025-07-22 19:32:19 +03:00
await ctx . reply ( k . no_image_available ( ) )
2025-07-05 20:15:54 +02:00
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 :
2025-07-22 19:32:19 +03:00
await ctx . reply ( k . no_image_available ( ) )
2025-07-05 20:15:54 +02:00
return
temp_input = tempfile . mktemp ( suffix = os . path . splitext ( fallback_image ) [ 1 ] )
shutil . copy ( fallback_image , temp_input )
input_path = temp_input
output_path : Optional [ str ] = await gen_demotivator ( input_path )
2025-06-27 19:17:13 +02:00
2025-07-05 20:15:54 +02:00
if output_path is None or not os . path . isfile ( output_path ) :
if temp_input and os . path . exists ( temp_input ) :
os . remove ( temp_input )
await ctx . reply ( " Failed to generate demotivator. " )
return
2025-06-27 19:17:13 +02:00
2025-07-05 20:15:54 +02:00
await ctx . send ( file = discord . File ( output_path ) )
2025-06-27 19:17:13 +02:00
2025-07-05 20:15:54 +02:00
if temp_input and os . path . exists ( temp_input ) :
os . remove ( temp_input )
bot . remove_command ( ' help ' )
2025-06-22 19:07:41 +02:00
# Command: Show help information
2025-07-22 19:32:19 +03:00
@bot.hybrid_command ( description = f " { k . command_desc_help ( ) } " )
2025-07-05 20:15:54 +02:00
async def help ( ctx : commands . Context ) - > None :
embed : discord . Embed = discord . Embed (
2025-07-22 19:32:19 +03:00
title = f " { k . command_help_embed_title ( ) } " ,
description = f " { k . command_help_embed_desc ( ) } " ,
2025-06-27 19:17:13 +02:00
color = Colour ( 0x000000 )
2025-01-05 21:03:49 +01:00
)
2025-07-05 20:15:54 +02:00
command_categories : Dict [ str , List [ str ] ] = {
2025-07-22 19:32:19 +03:00
f " { k . command_help_categories_general ( ) } " : [ " mem " , " talk " , " about " , " ping " , " impact " , " demotivator " , " help " ] ,
f " { k . command_help_categories_admin ( ) } " : [ " stats " , " retrain " , " setlanguage " ]
2025-01-05 21:03:49 +01:00
}
2025-07-05 20:15:54 +02:00
custom_commands : List [ str ] = [ ]
2025-01-05 21:03:49 +01:00
for cog_name , cog in bot . cogs . items ( ) :
for command in cog . get_commands ( ) :
2025-07-22 19:32:19 +03:00
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 ( ) } " ] :
2025-01-05 21:03:49 +01:00
custom_commands . append ( command . name )
if custom_commands :
2025-07-22 19:32:19 +03:00
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 )
2025-01-05 21:03:49 +01:00
for category , commands_list in command_categories . items ( ) :
2025-07-22 19:32:19 +03:00
commands_in_category : str = " \n " . join ( [ f " { settings [ " bot " ] [ " prefix " ] } { command } " for command in commands_list ] )
2025-01-05 21:03:49 +01:00
embed . add_field ( name = category , value = commands_in_category , inline = False )
await send_message ( ctx , embed = embed )
2025-07-22 19:32:19 +03:00
@bot.hybrid_command ( description = f " { k . command_desc_setlang ( ) } " )
2025-07-07 13:05:50 +02:00
@app_commands.describe ( locale = " Choose your language " )
async def setlanguage ( ctx : commands . Context , locale : str ) - > None :
2025-07-22 19:32:19 +03:00
if ctx . author . id not in settings [ " bot " ] [ " owner_ids " ] :
2025-07-07 16:26:37 +02:00
await ctx . send ( " :thumbsdown: " )
return
2025-07-22 19:32:19 +03:00
2025-07-07 13:05:50 +02:00
await ctx . defer ( )
2025-07-22 19:32:19 +03:00
k . change_language ( locale )
2025-07-07 13:05:50 +02:00
await ctx . send ( " :thumbsup: " )
2025-06-22 19:07:41 +02:00
# Event: Called on every message
2025-01-05 21:03:49 +01:00
@bot.event
2025-07-05 20:15:54 +02:00
async def on_message ( message : discord . Message ) - > None :
2025-06-28 16:56:05 -04:00
global memory , markov_model
2025-07-06 23:31:07 +02:00
EMOJIS = [ " \U0001F604 " , " \U0001F44D " , " \U0001F525 " , " \U0001F4AF " , " \U0001F389 " , " \U0001F60E " ] # originally was emojis but it would probably shit itself on systems without unicode so....
2025-01-05 21:03:49 +01:00
if message . author . bot :
return
2025-07-22 19:32:19 +03:00
if str ( message . author . id ) in settings [ " bot " ] [ " blacklisted_users " ] :
2025-01-05 21:03:49 +01:00
return
2025-07-22 19:32:19 +03:00
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 ) } " )
2025-01-05 21:03:49 +01:00
await bot . process_commands ( message )
return
2025-07-22 19:32:19 +03:00
if profanity . contains_profanity ( message . content ) and settings [ " bot " ] [ " misc " ] [ " block_profanity " ] :
2025-01-05 21:03:49 +01:00
return
if message . content :
2025-07-22 19:32:19 +03:00
if not settings [ " bot " ] [ " user_training " ] :
2025-01-05 21:03:49 +01:00
return
2025-07-22 19:32:19 +03:00
2025-07-05 20:15:54 +02:00
formatted_message : str = append_mentions_to_18digit_integer ( message . content )
cleaned_message : str = preprocess_message ( formatted_message )
2025-01-05 21:03:49 +01:00
if cleaned_message :
memory . append ( cleaned_message )
2025-07-22 19:32:19 +03:00
message_metadata : MessageMetadata = {
2025-07-16 19:27:12 +02:00
" user_id " : str ( message . author . id ) ,
" user_name " : str ( message . author ) ,
" guild_id " : str ( message . guild . id ) if message . guild else " DM " ,
" guild_name " : str ( message . guild . name ) if message . guild else " DM " ,
" channel_id " : str ( message . channel . id ) ,
" channel_name " : str ( message . channel ) ,
" message " : message . content ,
" timestamp " : time . time ( )
}
try :
if isinstance ( memory , list ) :
memory . append ( { " _meta " : message_metadata } )
else :
logger . warning ( " Memory is not a list; can ' t append metadata " )
except Exception as e :
logger . warning ( f " Failed to append metadata to memory: { e } " )
2025-07-05 20:15:54 +02:00
save_memory ( memory )
2025-01-05 21:03:49 +01:00
2025-07-06 23:31:07 +02:00
sentiment_score = is_positive ( message . content ) # doesnt work but im scared to change the logic now please ignore
if sentiment_score > 0.8 :
2025-07-22 19:32:19 +03:00
if not settings [ " bot " ] [ " react_to_messages " ] :
2025-07-06 23:44:04 +02:00
return
2025-07-06 23:31:07 +02:00
emoji = random . choice ( EMOJIS )
try :
await message . add_reaction ( emoji )
except Exception as e :
2025-07-08 17:02:10 +03:00
logger . info ( f " Failed to react with emoji: { e } " )
2025-07-06 23:31:07 +02:00
2025-01-05 21:03:49 +01:00
await bot . process_commands ( message )
2025-06-22 19:07:41 +02:00
# Event: Called on every interaction (slash command, etc.)
2025-03-30 18:53:57 +02:00
@bot.event
2025-07-05 20:15:54 +02:00
async def on_interaction ( interaction : discord . Interaction ) - > None :
2025-07-22 19:32:19 +03:00
logger . info ( f " { k . command_ran_s ( interaction . user . name ) } { interaction . user . name } " )
2025-03-30 18:53:57 +02:00
2025-06-22 19:07:41 +02:00
# Global check: Block blacklisted users from running commands
2025-03-30 21:01:20 +02:00
@bot.check
2025-07-05 20:15:54 +02:00
async def block_blacklisted ( ctx : commands . Context ) - > bool :
2025-07-22 19:32:19 +03:00
if str ( ctx . author . id ) in settings [ " bot " ] [ " blacklisted_users " ] :
2025-03-30 21:01:20 +02:00
try :
if isinstance ( ctx , discord . Interaction ) :
if not ctx . response . is_done ( ) :
2025-07-22 19:32:19 +03:00
await ctx . response . send_message ( k . blacklisted ( ) , ephemeral = True )
2025-03-30 21:01:20 +02:00
else :
2025-07-22 19:32:19 +03:00
await ctx . followup . send ( k . blacklisted ( ) , ephemeral = True )
2025-03-30 21:01:20 +02:00
else :
2025-07-22 19:32:19 +03:00
await ctx . send ( k . blacklisted_user ( ) , ephemeral = True )
2025-03-30 21:01:20 +02:00
except :
pass
return False
return True
2025-06-22 19:07:41 +02:00
# Command: Show bot latency
2025-07-22 19:32:19 +03:00
@bot.hybrid_command ( description = f " { k . command_desc_ping ( ) } " )
2025-07-05 20:15:54 +02:00
async def ping ( ctx : commands . Context ) - > None :
2025-01-05 21:03:49 +01:00
await ctx . defer ( )
2025-07-05 20:15:54 +02:00
latency : int = round ( bot . latency * 1000 )
2025-01-05 21:03:49 +01:00
2025-07-05 20:15:54 +02:00
LOLembed : discord . Embed = discord . Embed (
2025-01-05 21:03:49 +01:00
title = " Pong!! " ,
description = (
2025-07-22 19:32:19 +03:00
settings [ " bot " ] [ " misc " ] [ " ping_line " ] ,
f " ` { k . command_ping_embed_desc ( ) } : { latency } ms` \n "
2025-01-05 21:03:49 +01:00
) ,
2025-06-27 19:17:13 +02:00
color = Colour ( 0x000000 )
2025-01-05 21:03:49 +01:00
)
2025-07-22 19:32:19 +03:00
LOLembed . set_footer ( text = f " { k . command_ping_footer ( ) } { ctx . author . name } " , icon_url = ctx . author . display_avatar . url )
2025-01-05 21:03:49 +01:00
await ctx . send ( embed = LOLembed )
2025-06-22 19:07:41 +02:00
# Command: Show about information
2025-07-22 19:32:19 +03:00
@bot.hybrid_command ( description = f " { k . command_about_desc ( ) } " )
2025-07-05 20:15:54 +02:00
async def about ( ctx : commands . Context ) - > None :
2025-03-16 16:39:26 +01:00
print ( " ----------------------------------- \n \n " )
2025-07-22 19:32:19 +03:00
latest_version : str = str ( check_for_update ( ) )
2025-01-05 21:03:49 +01:00
print ( " ----------------------------------- " )
2025-07-22 19:32:19 +03:00
embed : discord . Embed = discord . Embed ( title = f " { k . command_about_embed_title ( ) } " , description = " " , color = Colour ( 0x000000 ) )
embed . add_field ( name = k . command_about_embed_field1 ( ) , value = f " { settings [ " name " ] } " , inline = False )
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 " )
2025-07-09 22:57:59 +02:00
2025-01-05 21:03:49 +01:00
await send_message ( ctx , embed = embed )
2025-06-22 19:07:41 +02:00
# Command: Show bot statistics (admin only)
2025-01-05 21:03:49 +01:00
@bot.hybrid_command ( description = " stats " )
2025-07-05 20:15:54 +02:00
async def stats ( ctx : commands . Context ) - > None :
2025-07-22 19:32:19 +03:00
if ctx . author . id not in settings [ " bot " ] [ " owner_ids " ] :
2025-01-05 21:03:49 +01:00
return
2025-03-16 16:39:26 +01:00
print ( " ----------------------------------- \n \n " )
2025-07-22 19:32:19 +03:00
latest_version : str = str ( check_for_update ( ) )
2025-01-05 21:03:49 +01:00
print ( " ----------------------------------- " )
2025-07-05 20:15:54 +02:00
memory_file : str = ' memory.json '
file_size : int = os . path . getsize ( memory_file )
2025-01-05 21:03:49 +01:00
with open ( memory_file , ' r ' ) as file :
2025-07-05 20:15:54 +02:00
line_count : int = sum ( 1 for _ in file )
2025-07-22 19:32:19 +03:00
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 " { 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 " { 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 " { 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)
2025-07-05 20:15:54 +02:00
2025-01-05 21:03:49 +01:00
await send_message ( ctx , embed = embed )
2025-06-22 19:07:41 +02:00
# Command: Upload memory.json to litterbox.catbox.moe and return the link
2025-01-05 21:03:49 +01:00
@bot.hybrid_command ( )
2025-07-05 20:15:54 +02:00
async def mem ( ctx : commands . Context ) - > None :
2025-07-22 19:32:19 +03:00
if not settings [ " bot " ] [ " allow_show_mem_command " ] :
2025-01-05 21:03:49 +01:00
return
2025-07-22 19:32:19 +03:00
2025-07-05 20:15:54 +02:00
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 )
2025-07-08 17:02:10 +03:00
logger . debug ( memorylitter )
2025-06-21 18:24:29 +02:00
await send_message ( ctx , memorylitter . stdout . strip ( ) )
2025-01-05 21:03:49 +01:00
2025-06-22 19:07:41 +02:00
# Helper: Improve sentence coherence (simple capitalization fix)
2025-07-05 20:15:54 +02:00
def improve_sentence_coherence ( sentence : str ) - > str :
2025-06-22 19:07:41 +02:00
# Capitalizes "i" to "I" in the sentence
2025-01-05 21:03:49 +01:00
sentence = sentence . replace ( " i " , " I " )
return sentence
2025-07-22 19:32:19 +03:00
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 ( )
2025-06-22 19:07:41 +02:00
# Start the bot
2025-07-22 19:32:19 +03:00
bot . run ( os . environ . get ( " DISCORD_BOT_TOKEN " , " " ) )