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-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
import discord
2025-06-28 16:56:05 -04:00
from discord . ext import commands
2025-06-27 19:17:13 +02:00
from discord import Colour
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
from modules . translations import *
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-06-27 19:17:13 +02:00
from modules . image import gen_image
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-01 16:57:39 +02:00
# removed since all locales come with goober now
2025-03-16 16:39:26 +01:00
2025-06-22 19:07:41 +02:00
# Dynamically load all cogs (extensions) from the cogs folder
2025-07-01 16:21:34 +02:00
async def load_cogs_from_folder ( bot , folder_name = " assets/cogs " ) :
2025-01-05 21:03:49 +01:00
for filename in os . listdir ( folder_name ) :
if filename . endswith ( " .py " ) and not filename . startswith ( " _ " ) :
cog_name = filename [ : - 3 ]
2025-07-01 16:21:34 +02:00
module_path = folder_name . replace ( " / " , " . " ) . replace ( " \\ " , " . " ) + f " . { cog_name } "
2025-01-05 21:03:49 +01:00
try :
2025-07-01 16:21:34 +02:00
await bot . load_extension ( module_path )
print ( f " { GREEN } { get_translation ( LOCALE , ' loaded_cog ' ) } { cog_name } { RESET } " )
2025-01-05 21:03:49 +01:00
except Exception as e :
2025-03-16 16:39:26 +01:00
print ( f " { RED } { get_translation ( LOCALE , ' cog_fail ' ) } { cog_name } { e } { RESET } " )
2025-02-26 19:36:19 +01:00
traceback . print_exc ( )
currenthash = " "
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-01-05 21:03:49 +01:00
intents = discord . Intents . default ( )
intents . messages = True
intents . message_content = True
2025-06-22 19:07:41 +02:00
bot = commands . Bot (
command_prefix = PREFIX ,
intents = intents ,
allowed_mentions = discord . AllowedMentions ( everyone = False , roles = False , users = False , replied_user = True )
)
# Load memory and Markov model for text generation
memory = load_memory ( )
2025-01-05 21:03:49 +01:00
markov_model = load_markov_model ( )
if not markov_model :
2025-03-16 16:39:26 +01:00
print ( f " { get_translation ( LOCALE , ' no_model ' ) } " )
2025-01-05 21:03:49 +01:00
memory = load_memory ( )
markov_model = train_markov_model ( memory )
generated_sentences = set ( )
used_words = set ( )
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
async def on_ready ( ) :
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-01-05 21:03:49 +01:00
folder_name = " cogs "
2025-06-21 00:29:44 +02:00
if launched == True :
2025-06-21 00:35:02 +02:00
return
2025-07-01 16:57:39 +02:00
# create folder logic only existed to prevent errors from upgrading from older versions of goober, now got nuked because its been like a billion versions since then
2025-06-22 21:51:27 +02:00
await load_cogs_from_folder ( bot )
2025-01-05 21:03:49 +01:00
try :
synced = await bot . tree . sync ( )
2025-03-16 16:39:26 +01:00
print ( f " { GREEN } { get_translation ( LOCALE , ' synced_commands ' ) } { len ( synced ) } { get_translation ( LOCALE , ' 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-06-23 14:14:01 +02:00
print ( f " { GREEN } { get_translation ( LOCALE , ' started ' ) . format ( name = NAME ) } { RESET } " )
2025-06-22 19:07:41 +02:00
except discord . errors . Forbidden as perm_error :
print ( f " { RED } Permission error while syncing commands: { perm_error } { RESET } " )
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-03-16 16:39:26 +01:00
print ( f " { RED } { get_translation ( LOCALE , ' fail_commands_sync ' ) } { e } { RESET } " )
traceback . print_exc ( )
quit ( )
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 19:07:41 +02:00
# Load positive GIF URLs from environment variable
2025-06-28 16:56:05 -04:00
positive_gifs : list [ str ] = os . getenv ( " POSITIVE_GIFS " , " " ) . split ( ' , ' )
2025-01-05 21:03:49 +01:00
2025-06-22 20:49:07 +02:00
@bot.event
async def on_command_error ( ctx , error ) :
from modules . unhandledexception import handle_exception
if isinstance ( error , commands . CommandInvokeError ) :
original = error . original
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-03-16 16:39:26 +01:00
@bot.hybrid_command ( description = f " { get_translation ( LOCALE , ' command_desc_retrain ' ) } " )
2025-01-05 21:03:49 +01:00
async def retrain ( ctx ) :
if ctx . author . id != ownerid :
return
2025-06-22 19:07:41 +02:00
message_ref = await send_message ( ctx , f " { get_translation ( LOCALE , ' command_markov_retrain ' ) } " ) # send_message from modules/sentenceprocessing.py
2025-01-05 21:03:49 +01:00
try :
with open ( MEMORY_FILE , ' r ' ) as f :
memory = json . load ( f )
except FileNotFoundError :
2025-03-16 23:55:07 +01:00
await send_message ( ctx , f " { get_translation ( LOCALE , ' command_markov_memory_not_found ' ) } " )
2025-01-05 21:03:49 +01:00
return
except json . JSONDecodeError :
2025-03-16 23:55:07 +01:00
await send_message ( ctx , f " { get_translation ( LOCALE , ' command_markov_memory_is_corrupt ' ) } " )
2025-01-05 21:03:49 +01:00
return
data_size = len ( memory )
processed_data = 0
2025-03-16 19:37:48 +01:00
processing_message_ref = await send_message ( ctx , f " { get_translation ( LOCALE , ' command_markov_retraining ' ) . format ( processed_data = processed_data , data_size = data_size ) } " )
2025-01-05 21:03:49 +01:00
start_time = time . time ( )
for i , data in enumerate ( memory ) :
processed_data + = 1
if processed_data % 1000 == 0 or processed_data == data_size :
2025-03-16 19:37:48 +01:00
await send_message ( ctx , f " { get_translation ( LOCALE , ' 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-03-16 19:37:48 +01:00
await send_message ( ctx , f " { get_translation ( LOCALE , ' 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-03-16 16:39:26 +01:00
@bot.hybrid_command ( description = f " { get_translation ( LOCALE , ' command_desc_talk ' ) } " )
2025-03-23 20:18:52 +01:00
async def talk ( ctx , sentence_size : int = 5 ) :
2025-01-05 21:03:49 +01:00
if not markov_model :
2025-03-16 16:39:26 +01:00
await send_message ( ctx , f " { get_translation ( LOCALE , ' command_talk_insufficent_text ' ) } " )
2025-01-05 21:03:49 +01:00
return
response = 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 :
cleaned_response = re . sub ( r ' [^ \ w \ s] ' , ' ' , response ) . lower ( )
2025-06-22 19:07:41 +02:00
coherent_response = rephrase_for_coherence ( cleaned_response )
if random . random ( ) < 0.9 and is_positive ( coherent_response ) :
2025-01-05 21:03:49 +01:00
gif_url = random . choice ( positive_gifs )
combined_message = f " { coherent_response } \n [jif]( { gif_url } ) "
else :
combined_message = 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-03-16 16:39:26 +01:00
await send_message ( ctx , f " { get_translation ( LOCALE , ' command_talk_generation_fail ' ) } " )
2025-01-05 21:03:49 +01:00
2025-06-22 19:07:41 +02:00
# Remove default help command to use custom help
2025-01-05 21:03:49 +01:00
bot . help_command = None
2025-06-27 19:17:13 +02:00
# Command: Show help information
@bot.hybrid_command ( description = f " { get_translation ( LOCALE , ' command_desc_help ' ) } " )
async def image ( ctx ) :
2025-06-29 19:44:56 +02:00
assets_folder = " assets/images "
temp_input = None
def get_random_asset_image ( ) :
files = [ f for f in os . listdir ( assets_folder ) if f . lower ( ) . endswith ( ( ' .png ' , ' .jpg ' , ' .jpeg ' , ' .webp ' ) ) ]
if not files :
return None
return os . path . join ( assets_folder , random . choice ( files ) )
if ctx . message . attachments :
attachment = ctx . message . attachments [ 0 ]
if attachment . content_type and attachment . content_type . startswith ( " image/ " ) :
ext = os . path . splitext ( attachment . filename ) [ 1 ]
temp_input = f " tempy { ext } "
await attachment . save ( temp_input )
input_path = temp_input
else :
fallback_image = get_random_asset_image ( )
if fallback_image is None :
2025-07-01 16:21:34 +02:00
await ctx . reply ( get_translation ( LOCALE , " 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 :
await ctx . reply ( " No image available to process. " )
return
# got lazy here
temp_input = tempfile . mktemp ( suffix = os . path . splitext ( fallback_image ) [ 1 ] )
shutil . copy ( fallback_image , temp_input )
input_path = temp_input
output_path = await gen_image ( 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 )
2025-07-01 16:21:34 +02:00
await ctx . reply ( get_translation ( LOCALE , " 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-06-27 19:17:13 +02:00
# Remove default help command to use custom help
bot . help_command = None
2025-06-22 19:07:41 +02:00
# Command: Show help information
2025-03-16 16:39:26 +01:00
@bot.hybrid_command ( description = f " { get_translation ( LOCALE , ' command_desc_help ' ) } " )
2025-01-05 21:03:49 +01:00
async def help ( ctx ) :
embed = discord . Embed (
2025-03-16 16:39:26 +01:00
title = f " { get_translation ( LOCALE , ' command_help_embed_title ' ) } " ,
description = f " { get_translation ( LOCALE , ' command_help_embed_desc ' ) } " ,
2025-06-27 19:17:13 +02:00
color = Colour ( 0x000000 )
2025-01-05 21:03:49 +01:00
)
command_categories = {
2025-06-29 19:44:56 +02:00
f " { get_translation ( LOCALE , ' command_help_categories_general ' ) } " : [ " mem " , " talk " , " about " , " ping " , " image " ] ,
2025-03-16 16:39:26 +01:00
f " { get_translation ( LOCALE , ' command_help_categories_admin ' ) } " : [ " stats " , " retrain " ]
2025-01-05 21:03:49 +01:00
}
custom_commands = [ ]
for cog_name , cog in bot . cogs . items ( ) :
for command in cog . get_commands ( ) :
2025-03-16 19:37:48 +01:00
if command . name not in command_categories [ f " { get_translation ( LOCALE , ' command_help_categories_general ' ) } " ] and command . name not in command_categories [ f " { get_translation ( LOCALE , ' command_help_categories_admin ' ) } " ] :
2025-01-05 21:03:49 +01:00
custom_commands . append ( command . name )
if custom_commands :
2025-03-16 16:39:26 +01:00
embed . add_field ( name = f " { get_translation ( LOCALE , ' 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 ( ) :
commands_in_category = " \n " . join ( [ f " { PREFIX } { command } " for command in commands_list ] )
embed . add_field ( name = category , value = commands_in_category , inline = False )
await send_message ( ctx , embed = embed )
2025-06-22 19:07:41 +02:00
# Event: Called on every message
2025-01-05 21:03:49 +01:00
@bot.event
async def on_message ( message ) :
2025-06-28 16:56:05 -04:00
global memory , markov_model
2025-01-05 21:03:49 +01:00
2025-06-22 19:07:41 +02:00
# Ignore bot messages
2025-01-05 21:03:49 +01:00
if message . author . bot :
return
2025-06-22 19:07:41 +02:00
# Ignore messages from blacklisted users
2025-01-05 21:03:49 +01:00
if str ( message . author . id ) in BLACKLISTED_USERS :
return
2025-06-22 19:07:41 +02:00
# Process commands if message starts with a command prefix
2025-01-05 21:03:49 +01:00
if message . content . startswith ( ( f " { PREFIX } talk " , f " { PREFIX } mem " , f " { PREFIX } help " , f " { PREFIX } stats " , f " { PREFIX } " ) ) :
2025-03-16 16:39:26 +01:00
print ( f " { get_translation ( LOCALE , ' command_ran ' ) . format ( message = message ) } " )
2025-01-05 21:03:49 +01:00
await bot . process_commands ( message )
return
2025-06-22 19:07:41 +02:00
# Ignore messages with profanity
if profanity . contains_profanity ( message . content ) : # profanity from better_profanity
2025-01-05 21:03:49 +01:00
return
2025-06-22 19:07:41 +02:00
# Add user messages to memory for training if enabled
2025-01-05 21:03:49 +01:00
if message . content :
if not USERTRAIN_ENABLED :
return
2025-06-22 19:07:41 +02:00
formatted_message = append_mentions_to_18digit_integer ( message . content ) # append_mentions_to_18digit_integer from modules/sentenceprocessing.py
cleaned_message = preprocess_message ( formatted_message ) # preprocess_message from modules/sentenceprocessing.py
2025-01-05 21:03:49 +01:00
if cleaned_message :
memory . append ( cleaned_message )
2025-06-22 19:07:41 +02:00
save_memory ( memory ) # save_memory from modules/markovmemory.py
2025-01-05 21:03:49 +01:00
2025-06-22 19:07:41 +02:00
# Process any commands in the message
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
async def on_interaction ( interaction ) :
2025-06-22 19:07:41 +02:00
print ( f " { get_translation ( LOCALE , ' 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
async def block_blacklisted ( ctx ) :
if str ( ctx . author . id ) in BLACKLISTED_USERS :
try :
if isinstance ( ctx , discord . Interaction ) :
if not ctx . response . is_done ( ) :
2025-07-01 16:21:34 +02:00
await ctx . response . send_message ( get_translation ( LOCALE , " blacklisted " ) , ephemeral = True )
2025-03-30 21:01:20 +02:00
else :
2025-07-01 16:21:34 +02:00
await ctx . followup . send ( get_translation ( LOCALE , " blacklisted " ) , ephemeral = True )
2025-03-30 21:01:20 +02:00
else :
2025-07-01 16:21:34 +02:00
await ctx . send ( get_translation ( LOCALE , " 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-03-16 16:39:26 +01:00
@bot.hybrid_command ( description = f " { get_translation ( LOCALE , ' command_desc_ping ' ) } " )
2025-01-05 21:03:49 +01:00
async def ping ( ctx ) :
await ctx . defer ( )
latency = round ( bot . latency * 1000 )
LOLembed = discord . Embed (
title = " Pong!! " ,
description = (
f " { PING_LINE } \n "
2025-03-16 16:39:26 +01:00
f " ` { get_translation ( LOCALE , ' 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-03-16 16:39:26 +01:00
LOLembed . set_footer ( text = f " { get_translation ( LOCALE , ' 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-03-16 16:39:26 +01:00
@bot.hybrid_command ( description = f " { get_translation ( LOCALE , ' command_about_desc ' ) } " )
2025-01-05 21:03:49 +01:00
async def about ( ctx ) :
2025-03-16 16:39:26 +01:00
print ( " ----------------------------------- \n \n " )
2025-06-23 13:56:23 +02:00
latest_version = check_for_update ( ) # check_for_update from modules/version.py
2025-01-05 21:03:49 +01:00
print ( " ----------------------------------- " )
2025-06-27 19:17:13 +02:00
embed = discord . Embed ( title = f " { get_translation ( LOCALE , ' command_about_embed_title ' ) } " , description = " " , color = Colour ( 0x000000 ) )
2025-03-16 16:39:26 +01:00
embed . add_field ( name = f " { get_translation ( LOCALE , ' command_about_embed_field1 ' ) } " , value = f " { NAME } " , inline = False )
2025-03-16 23:34:30 +01:00
embed . add_field ( name = f " { get_translation ( LOCALE , ' command_about_embed_field2name ' ) } " , value = f " { get_translation ( LOCALE , ' 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 " )
async def stats ( ctx ) :
if ctx . author . id != ownerid :
return
2025-03-16 16:39:26 +01:00
print ( " ----------------------------------- \n \n " )
2025-06-23 13:56:23 +02:00
latest_version = check_for_update ( ) # check_for_update from modules/version.py
2025-01-05 21:03:49 +01:00
print ( " ----------------------------------- " )
memory_file = ' memory.json '
file_size = os . path . getsize ( memory_file )
with open ( memory_file , ' r ' ) as file :
line_count = sum ( 1 for _ in file )
2025-06-27 19:17:13 +02:00
embed = discord . Embed ( title = f " { get_translation ( LOCALE , ' command_stats_embed_title ' ) } " , description = f " { get_translation ( LOCALE , ' command_stats_embed_desc ' ) } " , color = Colour ( 0x000000 ) )
2025-03-16 16:39:26 +01:00
embed . add_field ( name = f " { get_translation ( LOCALE , ' command_stats_embed_field1name ' ) } " , value = f " { get_translation ( LOCALE , ' command_stats_embed_field1value ' ) . format ( file_size = file_size , line_count = line_count ) } " , inline = False )
embed . add_field ( name = f " { get_translation ( LOCALE , ' command_stats_embed_field2name ' ) } " , value = f " { get_translation ( LOCALE , ' command_stats_embed_field2value ' ) . format ( local_version = local_version , latest_version = latest_version ) } " , inline = False )
2025-06-28 16:56:05 -04:00
embed . add_field ( name = f " { get_translation ( LOCALE , ' command_stats_embed_field3name ' ) } " , value = f " { get_translation ( LOCALE , ' command_stats_embed_field3value ' ) . format ( NAME = NAME , PREFIX = PREFIX , ownerid = ownerid , cooldown_time = cooldown_time , PING_LINE = PING_LINE , showmemenabled = showmemenabled , USERTRAIN_ENABLED = USERTRAIN_ENABLED , song = song , splashtext = splashtext ) } " , 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: Upload memory.json to litterbox.catbox.moe and return the link
2025-01-05 21:03:49 +01:00
@bot.hybrid_command ( )
async def mem ( ctx ) :
if showmemenabled != " true " :
return
2025-06-21 18:24:29 +02:00
command = """ curl -F " reqtype=fileupload " -F " time=1h " -F " fileToUpload=@memory.json " https://litterbox.catbox.moe/resources/internals/api.php """
memorylitter = subprocess . run ( command , shell = True , capture_output = True , text = True )
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-01-05 21:03:49 +01:00
def improve_sentence_coherence ( sentence ) :
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-03-31 23:23:22 -04:00
bot . run ( TOKEN )