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
from typing import List , Dict , Set , Optional , Tuple , Any , Union , Callable , Coroutine , TypeVar , Type
2025-06-27 19:45:44 +02:00
from modules . globalvars import *
from modules . prestartchecks import start_checks
# Print splash text and check for updates
print ( splashtext ) # Print splash text (from modules/globalvars.py)
start_checks ( )
2025-06-22 19:07:41 +02:00
import requests
2025-07-07 20:41:24 +02:00
import asqlite
2025-06-22 19:07:41 +02:00
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 . central import ping_server
2025-07-07 13:05:50 +02:00
from modules . volta . main import _ , set_language
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-05 20:15:54 +02:00
# Constants with type hints
positive_gifs : List [ str ] = os . getenv ( " POSITIVE_GIFS " , " " ) . split ( ' , ' )
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-05 20:15:54 +02:00
bot : commands . Bot = commands . Bot (
2025-06-22 19:07:41 +02:00
command_prefix = PREFIX ,
intents = intents ,
allowed_mentions = discord . AllowedMentions ( everyone = False , roles = False , users = False , replied_user = True )
)
2025-07-07 20:41:24 +02:00
# Initialize database pool for fireboard functionality
bot . fireboard_pool = None
2025-06-22 19:07:41 +02:00
# Load memory and Markov model for text generation
2025-07-05 20:15:54 +02:00
memory : List [ str ] = load_memory ( )
markov_model : Optional [ markovify . Text ] = load_markov_model ( )
2025-01-05 21:03:49 +01:00
if not markov_model :
2025-07-05 20:15:54 +02:00
print ( f " { RED } { ( _ ( ' markov_model_not_found ' ) ) } { RESET } " )
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 ( )
async def load_cogs_from_folder ( bot , folder_name = " assets/cogs " ) :
for filename in os . listdir ( folder_name ) :
if filename . endswith ( " .py " ) and not filename . startswith ( " _ " ) :
cog_name = filename [ : - 3 ]
module_path = folder_name . replace ( " / " , " . " ) . replace ( " \\ " , " . " ) + f " . { cog_name } "
try :
await bot . load_extension ( module_path )
print ( f " { GREEN } { ( _ ( ' loaded_cog ' ) ) } { cog_name } { RESET } " )
except Exception as e :
print ( f " { RED } { ( _ ( ' cog_fail ' ) ) } { cog_name } { e } { RESET } " )
traceback . print_exc ( )
2025-01-05 21:03:49 +01:00
2025-07-05 20:15:54 +02:00
async def fetch_active_users ( ) - > str :
2025-07-01 20:26:48 +02:00
try :
2025-07-05 20:15:54 +02:00
response : requests . Response = requests . get ( f " { VERSION_URL } /active-users " )
2025-07-01 20:26:48 +02:00
if response . status_code == 200 :
return response . text . strip ( )
else :
return " ? "
except Exception as e :
2025-07-05 20:15:54 +02:00
print ( f " { RED } { ( _ ( ' error_fetching_active_users ' ) ) } { RESET } { e } " )
2025-07-01 20:26:48 +02:00
return " ? "
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 :
requests . post ( f " { VERSION_URL } /aliveping " , json = { " name " : NAME } )
except Exception as e :
2025-07-05 20:15:54 +02:00
print ( f " { RED } { ( _ ( ' error_sending_alive_ping ' ) ) } { 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-07 20:41:24 +02:00
# Initialize database pool for fireboard functionality
try :
bot . fireboard_pool = await asqlite . create_pool ( " fireboard.db " )
print ( f " { GREEN } Database pool initialized successfully { RESET } " )
except Exception as e :
print ( f " { RED } Failed to initialize database pool: { e } { RESET } " )
bot . fireboard_pool = None
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 ( )
print ( f " { GREEN } { _ ( ' synced_commands ' ) } { len ( synced ) } { ( _ ( ' synced_commands2 ' ) ) } { RESET } " )
2025-01-05 21:03:49 +01:00
slash_commands_enabled = True
2025-06-22 19:07:41 +02:00
ping_server ( ) # ping_server from modules/central.py
2025-07-05 20:15:54 +02:00
active_users : str = await fetch_active_users ( )
print ( f " { GREEN } { ( _ ( ' active_users: ' ) ) } { active_users } { RESET } " )
print ( f " { GREEN } { ( _ ( ' started ' ) ) . format ( name = NAME ) } { RESET } " )
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-05 20:15:54 +02:00
print ( f " { RED } Permission error while syncing commands: { perm_error } { RESET } " )
2025-06-22 19:07:41 +02:00
print ( f " { RED } Make sure the bot has the ' applications.commands ' scope and is invited with the correct permissions. { RESET } " )
quit ( )
2025-01-05 21:03:49 +01:00
except Exception as e :
2025-07-05 20:15:54 +02:00
print ( f " { RED } { ( _ ( ' fail_commands_sync ' ) ) } { e } { RESET } " )
2025-03-16 16:39:26 +01:00
traceback . print_exc ( )
quit ( )
2025-07-05 20:15:54 +02:00
2025-01-05 21:03:49 +01:00
if not song :
return
await bot . change_presence ( activity = discord . Activity ( type = discord . ActivityType . listening , name = f " { 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-05 20:15:54 +02:00
@bot.hybrid_command ( description = f " { ( _ ( ' command_desc_retrain ' ) ) } " )
async def retrain ( ctx : commands . Context ) - > None :
2025-01-05 21:03:49 +01:00
if ctx . author . id != ownerid :
return
2025-07-05 20:15:54 +02:00
message_ref : MessageReference = await send_message ( ctx , f " { ( _ ( ' command_markov_retrain ' ) ) } " )
2025-01-05 21:03:49 +01:00
try :
with open ( MEMORY_FILE , ' 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-05 20:15:54 +02:00
await send_message ( ctx , f " { ( _ ( ' command_markov_memory_not_found ' ) ) } " )
2025-01-05 21:03:49 +01:00
return
except json . JSONDecodeError :
2025-07-05 20:15:54 +02:00
await send_message ( ctx , f " { ( _ ( ' command_markov_memory_is_corrupt ' ) ) } " )
2025-01-05 21:03:49 +01:00
return
2025-07-05 20:15:54 +02:00
data_size : int = len ( memory )
processed_data : int = 0
processing_message_ref : MessageReference = await send_message ( ctx , f " { ( _ ( ' command_markov_retraining ' ) ) . format ( processed_data = processed_data , data_size = data_size ) } " )
start_time : float = time . time ( )
2025-01-05 21:03:49 +01:00
for i , data in enumerate ( memory ) :
processed_data + = 1
if processed_data % 1000 == 0 or processed_data == data_size :
2025-07-05 20:15:54 +02:00
await send_message ( ctx , f " { ( _ ( ' command_markov_retraining ' ) ) . format ( processed_data = processed_data , data_size = data_size ) } " , edit = True , message_reference = processing_message_ref )
2025-01-05 21:03:49 +01:00
global markov_model
markov_model = train_markov_model ( memory )
save_markov_model ( markov_model )
2025-07-05 20:15:54 +02:00
await send_message ( ctx , f " { ( _ ( ' command_markov_retrain_successful ' ) ) . format ( data_size = 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-05 20:15:54 +02:00
@bot.hybrid_command ( description = f " { ( _ ( ' command_desc_talk ' ) ) } " )
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-05 20:15:54 +02:00
await send_message ( ctx , f " { ( _ ( ' 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-03-30 18:53:57 +02:00
print ( combined_message )
os . environ [ ' gooberlatestgen ' ] = combined_message
2025-01-05 21:03:49 +01:00
await send_message ( ctx , combined_message )
else :
2025-07-05 20:15:54 +02:00
await send_message ( ctx , f " { ( _ ( ' command_talk_generation_fail ' ) ) } " )
2025-01-05 21:03:49 +01:00
2025-07-05 20:15:54 +02:00
# Command: Generate an image
@bot.hybrid_command ( description = f " { ( _ ( ' command_desc_help ' ) ) } " )
2025-07-07 00:57:54 +02:00
async def impact ( ctx : commands . Context ) - > None :
2025-07-05 20:15:54 +02:00
assets_folder : str = " assets/images "
temp_input : Optional [ str ] = None
2025-06-29 19:44:56 +02:00
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 ' ) ) ]
2025-06-29 19:44:56 +02:00
if not files :
return None
return os . path . join ( assets_folder , random . choice ( files ) )
if ctx . message . attachments :
2025-07-05 20:15:54 +02:00
attachment : discord . Attachment = ctx . message . attachments [ 0 ]
2025-06-29 19:44:56 +02:00
if attachment . content_type and attachment . content_type . startswith ( " image/ " ) :
2025-07-05 20:15:54 +02:00
ext : str = os . path . splitext ( attachment . filename ) [ 1 ]
2025-06-29 19:44:56 +02:00
temp_input = f " tempy { ext } "
await attachment . save ( temp_input )
2025-07-05 20:15:54 +02:00
input_path : str = temp_input
2025-06-29 19:44:56 +02:00
else :
2025-07-05 20:15:54 +02:00
fallback_image : Optional [ str ] = get_random_asset_image ( )
2025-06-29 19:44:56 +02:00
if fallback_image is None :
2025-07-05 20:15:54 +02:00
await ctx . reply ( ( _ ( ' no_image_available ' ) ) )
2025-06-29 19:44:56 +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-05 20:15:54 +02:00
await ctx . reply ( ( _ ( ' no_image_available ' ) ) )
2025-06-29 19:44:56 +02:00
return
temp_input = tempfile . mktemp ( suffix = os . path . splitext ( fallback_image ) [ 1 ] )
shutil . copy ( fallback_image , temp_input )
input_path = temp_input
2025-07-05 20:15:54 +02:00
output_path : Optional [ str ] = await gen_meme ( input_path )
2025-06-29 19:44:56 +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 )
2025-07-05 20:15:54 +02:00
await ctx . reply ( ( _ ( ' failed_generate_image ' ) ) )
2025-06-29 19:44:56 +02:00
return
await ctx . send ( file = discord . File ( output_path ) )
if temp_input and os . path . exists ( temp_input ) :
os . remove ( temp_input )
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 "
temp_input : Optional [ str ] = None
def get_random_asset_image ( ) - > Optional [ str ] :
files : List [ str ] = [ f for f in os . listdir ( assets_folder ) if f . lower ( ) . endswith ( ( ' .png ' , ' .jpg ' , ' .jpeg ' , ' .webp ' ) ) ]
if not files :
return None
return os . path . join ( assets_folder , random . choice ( files ) )
if ctx . message . attachments :
attachment : discord . Attachment = ctx . message . attachments [ 0 ]
if attachment . content_type and attachment . content_type . startswith ( " image/ " ) :
ext : str = os . path . splitext ( attachment . filename ) [ 1 ]
temp_input = f " tempy { ext } "
await attachment . save ( temp_input )
input_path : str = temp_input
else :
fallback_image : Optional [ str ] = get_random_asset_image ( )
if fallback_image is None :
await ctx . reply ( ( _ ( ' no_image_available ' ) ) )
return
temp_input = tempfile . mktemp ( suffix = os . path . splitext ( fallback_image ) [ 1 ] )
shutil . copy ( fallback_image , temp_input )
input_path = temp_input
else :
fallback_image = get_random_asset_image ( )
if fallback_image is None :
await ctx . reply ( ( _ ( ' no_image_available ' ) ) )
return
temp_input = tempfile . mktemp ( suffix = os . path . splitext ( fallback_image ) [ 1 ] )
shutil . copy ( fallback_image , temp_input )
input_path = temp_input
output_path : Optional [ str ] = await gen_demotivator ( input_path )
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-05 20:15:54 +02:00
@bot.hybrid_command ( description = f " { ( _ ( ' command_desc_help ' ) ) } " )
async def help ( ctx : commands . Context ) - > None :
embed : discord . Embed = discord . Embed (
title = f " { ( _ ( ' command_help_embed_title ' ) ) } " ,
description = f " { ( _ ( ' command_help_embed_desc ' ) ) } " ,
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-07 16:26:37 +02:00
f " { ( _ ( ' command_help_categories_general ' ) ) } " : [ " mem " , " talk " , " about " , " ping " , " impact " , " demotivator " , " help " ] ,
f " { ( _ ( ' 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-05 20:15:54 +02:00
if command . name not in command_categories [ f " { ( _ ( ' command_help_categories_general ' ) ) } " ] and command . name not in command_categories [ f " { ( _ ( ' command_help_categories_admin ' ) ) } " ] :
2025-01-05 21:03:49 +01:00
custom_commands . append ( command . name )
if custom_commands :
2025-07-05 20:15:54 +02:00
embed . add_field ( name = f " { ( _ ( ' command_help_categories_custom ' ) ) } " , value = " \n " . join ( [ f " { 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-05 20:15:54 +02:00
commands_in_category : str = " \n " . join ( [ f " { 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-07 13:05:50 +02:00
@bot.hybrid_command ( description = f " { ( _ ( ' command_desc_setlang ' ) ) } " )
@app_commands.describe ( locale = " Choose your language " )
async def setlanguage ( ctx : commands . Context , locale : str ) - > None :
2025-07-07 16:26:37 +02:00
if ctx . author . id != ownerid :
await ctx . send ( " :thumbsdown: " )
return
2025-07-07 13:05:50 +02:00
await ctx . defer ( )
set_language ( locale )
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
if str ( message . author . id ) in BLACKLISTED_USERS :
return
if message . content . startswith ( ( f " { PREFIX } talk " , f " { PREFIX } mem " , f " { PREFIX } help " , f " { PREFIX } stats " , f " { PREFIX } " ) ) :
2025-07-06 21:06:04 +02:00
print ( f " { ( _ ( ' command_ran ' ) ) . format ( message = message ) } " )
2025-01-05 21:03:49 +01:00
await bot . process_commands ( message )
return
2025-07-05 20:15:54 +02:00
if profanity . contains_profanity ( message . content ) :
2025-01-05 21:03:49 +01:00
return
if message . content :
if not USERTRAIN_ENABLED :
return
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-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-06 23:44:04 +02:00
if REACT != " True " :
return
2025-07-06 23:31:07 +02:00
emoji = random . choice ( EMOJIS )
try :
await message . add_reaction ( emoji )
except Exception as e :
print ( f " Failed to react with emoji: { e } " )
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 :
print ( f " { ( _ ( ' command_ran_s ' ) ) . format ( interaction = interaction ) } { interaction . data [ ' 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-03-30 21:01:20 +02:00
if str ( ctx . author . id ) in BLACKLISTED_USERS :
try :
if isinstance ( ctx , discord . Interaction ) :
if not ctx . response . is_done ( ) :
2025-07-05 20:15:54 +02:00
await ctx . response . send_message ( ( _ ( ' blacklisted ' ) ) , ephemeral = True )
2025-03-30 21:01:20 +02:00
else :
2025-07-05 20:15:54 +02:00
await ctx . followup . send ( ( _ ( ' blacklisted ' ) ) , ephemeral = True )
2025-03-30 21:01:20 +02:00
else :
2025-07-05 20:15:54 +02:00
await ctx . send ( ( _ ( ' 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-05 20:15:54 +02:00
@bot.hybrid_command ( description = f " { ( _ ( ' command_desc_ping ' ) ) } " )
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 = (
f " { PING_LINE } \n "
2025-07-05 20:15:54 +02:00
f " ` { ( _ ( ' 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-05 20:15:54 +02:00
LOLembed . set_footer ( text = f " { ( _ ( ' command_ping_footer ' ) ) } { ctx . author . name } " , icon_url = ctx . author . 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-05 20:15:54 +02:00
@bot.hybrid_command ( description = f " { ( _ ( ' command_about_desc ' ) ) } " )
async def about ( ctx : commands . Context ) - > None :
2025-03-16 16:39:26 +01:00
print ( " ----------------------------------- \n \n " )
2025-07-05 20:15:54 +02:00
latest_version : str = check_for_update ( )
2025-01-05 21:03:49 +01:00
print ( " ----------------------------------- " )
2025-07-05 20:15:54 +02:00
embed : discord . Embed = discord . Embed ( title = f " { ( _ ( ' command_about_embed_title ' ) ) } " , description = " " , color = Colour ( 0x000000 ) )
embed . add_field ( name = f " { ( _ ( ' command_about_embed_field1 ' ) ) } " , value = f " { NAME } " , inline = False )
embed . add_field ( name = f " { ( _ ( ' command_about_embed_field2name ' ) ) } " , value = f " { ( _ ( ' command_about_embed_field2value ' ) ) . format ( local_version = local_version , latest_version = latest_version ) } " , inline = False )
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-01-05 21:03:49 +01:00
if ctx . author . id != ownerid :
return
2025-03-16 16:39:26 +01:00
print ( " ----------------------------------- \n \n " )
2025-07-05 20:15:54 +02:00
latest_version : 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 )
embed : discord . Embed = discord . Embed ( title = f " { ( _ ( ' command_stats_embed_title ' ) ) } " , description = f " { ( _ ( ' command_stats_embed_desc ' ) ) } " , color = Colour ( 0x000000 ) )
embed . add_field ( name = f " { ( _ ( ' command_stats_embed_field1name ' ) ) } " , value = f " { ( _ ( ' command_stats_embed_field1value ' ) ) . format ( file_size = file_size , line_count = line_count ) } " , inline = False )
embed . add_field ( name = f " { ( _ ( ' command_stats_embed_field2name ' ) ) } " , value = f " { ( _ ( ' command_stats_embed_field2value ' ) ) . format ( local_version = local_version , latest_version = latest_version ) } " , inline = False )
2025-07-07 20:41:24 +02:00
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 )
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-01-05 21:03:49 +01:00
if showmemenabled != " true " :
return
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-06-21 18:24:29 +02:00
print ( memorylitter )
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-06-22 19:07:41 +02:00
# Start the bot
2025-07-05 20:15:54 +02:00
bot . run ( TOKEN )