2023-07-09 00:57:11 -07:00
import discord
2023-07-10 10:38:28 -07:00
from discord . ext import commands , tasks
2023-07-09 00:57:11 -07:00
from discord . utils import get
import shutil
import json
import random
import time
import os
import asyncio
import subprocess
from dotenv import load_dotenv
2024-01-18 14:41:51 -08:00
2023-07-09 00:57:11 -07:00
import aiohttp
2023-12-27 20:48:57 -08:00
import asyncssh
2023-07-09 00:57:11 -07:00
2023-07-13 01:10:28 -07:00
#Stable Diffusion
2023-07-16 22:52:43 -07:00
#Set this env variable to http://host:port or "disabled"
2023-07-13 01:10:28 -07:00
#os.getenv('stablediffusion_url')
2023-07-09 00:57:11 -07:00
#env vars START
load_dotenv ( )
imgflip_username = os . getenv ( ' imgflip_username ' )
imgflip_password = os . getenv ( ' imgflip_password ' )
discord_token = os . getenv ( ' discord_token ' )
ftp_server = os . getenv ( ' ftp_server ' )
ftp_username = os . getenv ( ' ftp_username ' )
ftp_password = os . getenv ( ' ftp_password ' )
ftp_public_html = os . getenv ( ' ftp_public_html ' )
#env vars END
2024-01-17 18:09:45 -08:00
#discord setup START
2023-07-09 00:57:11 -07:00
intents = discord . Intents . default ( )
intents . message_content = True
bot = commands . Bot ( command_prefix = ' ! ' , intents = intents )
2024-01-17 18:09:45 -08:00
#discord setup END
2023-12-27 20:48:57 -08:00
async def upload_sftp ( local_filename , server_folder , server_filename ) :
remotepath = server_folder + server_filename
async with asyncssh . connect ( ftp_server , username = ftp_username , password = ftp_password ) as conn :
async with conn . start_sftp_client ( ) as sftp :
await sftp . put ( local_filename , remotepath = remotepath )
2023-07-09 18:02:15 -07:00
2023-07-11 17:04:28 -07:00
async def handle_error ( error ) :
print ( error )
current_time = time . strftime ( " % Y- % m- %d % H: % M: % S " , time . localtime ( ) )
2023-07-16 16:35:40 -07:00
log_line = current_time + ' : ' + str ( error ) + ' \n '
2023-07-11 17:04:28 -07:00
with open ( " databases/error_log.txt " , ' a ' ) as f :
f . write ( log_line )
return error
2023-07-09 00:57:11 -07:00
2024-01-18 00:13:46 -08:00
async def upload_ftp_ai_images ( folder ) :
for filename in os . listdir ( folder ) :
if filename [ - 4 : ] == ' .png ' :
filepath = folder + filename
prompt = " Unknown Prompt " # Will have to update this later
html_file = " phixxy.com/ai-images/index.html "
html_insert = ''' <!--REPLACE THIS COMMENT-->
< div >
< img src = " <!--filename--> " loading = " lazy " >
< p class = " image-description " > < ! - - description - - > < / p >
< / div > '''
server_folder = ( os . getenv ( ' ftp_public_html ' ) + ' ai-images/ ' )
new_filename = str ( time . time_ns ( ) ) + " .png "
await upload_sftp ( filepath , server_folder , new_filename )
print ( " Uploaded " , new_filename )
with open ( html_file , ' r ' ) as f :
html_data = f . read ( )
html_insert = html_insert . replace ( " <!--filename--> " , new_filename )
html_insert = html_insert . replace ( " <!--description--> " , prompt )
html_data = html_data . replace ( " <!--REPLACE THIS COMMENT--> " , html_insert )
with open ( html_file , " w " ) as f :
f . writelines ( html_data )
await upload_sftp ( html_file , server_folder , " index.html " )
os . rename ( filepath , f " tmp/ { new_filename } " )
2023-07-09 00:57:11 -07:00
def create_channel_config ( filepath ) :
config_dict = {
" personality " : " average " ,
" channel_topic " : " casual " ,
" chat_enabled " : False ,
" commands_enabled " : True ,
" chat_history_len " : 5 ,
" look_at_images " : False ,
" react_to_msgs " : False ,
" ftp_enabled " : False
}
with open ( filepath , " w " ) as f :
json . dump ( config_dict , f )
print ( " Wrote config variables to file. " )
async def get_channel_config ( channel_id ) :
2023-07-09 16:06:45 -07:00
filepath = " channels/config/ {0} .json " . format ( str ( channel_id ) )
2023-07-09 00:57:11 -07:00
if not os . path . exists ( filepath ) :
create_channel_config ( filepath )
with open ( filepath , " r " ) as f :
config_dict = json . loads ( f . readline ( ) )
return config_dict
async def react_to_msg ( ctx , react ) :
2023-07-25 17:49:30 -07:00
def is_emoji ( string ) :
if len ( string ) == 1 :
# Range of Unicode codepoints for emojis
2023-08-19 12:40:46 -07:00
if 0x1F300 < = ord ( string ) < = 0x1F6FF :
2023-07-25 17:49:30 -07:00
return True
return False
2023-07-25 17:20:14 -07:00
if react :
2023-07-09 00:57:11 -07:00
if not random . randint ( 0 , 10 ) and ctx . author . id != 1097302679836971038 :
2023-07-11 18:10:44 -07:00
system_msg = " Send only an emoji as a discord reaction to the following chat message "
message = ctx . content [ 0 ]
2023-07-09 00:57:11 -07:00
headers = {
' Content-Type ' : ' application/json ' ,
' Authorization ' : f ' Bearer { os . getenv ( " openai.api_key " ) } ' ,
}
data = {
" model " : " gpt-3.5-turbo " ,
2023-07-11 18:10:44 -07:00
" messages " : [ { " role " : " system " , " content " : system_msg } , { " role " : " user " , " content " : message } ]
2023-07-09 00:57:11 -07:00
}
url = " https://api.openai.com/v1/chat/completions "
2023-07-25 17:49:30 -07:00
2023-07-09 00:57:11 -07:00
try :
2023-07-19 22:05:56 -07:00
async with bot . http_session . post ( url , headers = headers , json = data ) as resp :
response_data = await resp . json ( )
2023-07-25 17:49:30 -07:00
reaction = response_data [ ' choices ' ] [ 0 ] [ ' message ' ] [ ' content ' ] . strip ( )
if is_emoji ( reaction ) :
await ctx . add_reaction ( reaction )
else :
await ctx . add_reaction ( " 😓 " )
2023-07-09 00:57:11 -07:00
except Exception as error :
print ( " Some error happened while trying to react to a message " )
2023-07-11 17:04:28 -07:00
await handle_error ( error )
2023-07-09 00:57:11 -07:00
async def log_chat_and_get_history ( ctx , logfile , channel_vars ) :
2024-01-17 18:27:17 -08:00
log_line = ' '
2023-07-09 00:57:11 -07:00
log_line + = ctx . content
log_line = ctx . author . name + " : " + log_line + " \n "
chat_history = " "
print ( " Logging: " + log_line , end = " " )
with open ( logfile , " a " , encoding = " utf-8 " ) as f :
f . write ( log_line )
with open ( logfile , " r " , encoding = " utf-8 " ) as f :
for line in ( f . readlines ( ) [ - int ( channel_vars [ " chat_history_len " ] ) : ] ) :
chat_history + = line
return chat_history
async def chat_response ( ctx , channel_vars , chat_history_string ) :
async with ctx . channel . typing ( ) :
await asyncio . sleep ( 1 )
prompt = f " You are a { channel_vars [ ' personality ' ] } chat bot named Sparkytron 3000 created by @phixxy.com. Your personality should be { channel_vars [ ' personality ' ] } . You are currently in a { channel_vars [ ' channel_topic ' ] } chatroom. The message history is: { chat_history_string } "
headers = {
' Content-Type ' : ' application/json ' ,
' Authorization ' : f ' Bearer { os . getenv ( " openai.api_key " ) } ' ,
}
data = {
" model " : " gpt-3.5-turbo " ,
" messages " : [ { " role " : " user " , " content " : prompt } ]
}
url = " https://api.openai.com/v1/chat/completions "
2023-07-17 23:06:04 -07:00
try :
2023-07-19 22:05:56 -07:00
async with bot . http_session . post ( url , headers = headers , json = data ) as resp :
response_data = await resp . json ( )
response = response_data [ ' choices ' ] [ 0 ] [ ' message ' ] [ ' content ' ]
if " Sparkytron 3000: " in response [ 0 : 17 ] :
response = response . replace ( " Sparkytron 3000: " , " " )
max_len = 1999
if len ( response ) > max_len :
messages = [ response [ y - max_len : y ] for y in range ( max_len , len ( response ) + max_len , max_len ) ]
else :
messages = [ response ]
for message in messages :
await ctx . channel . send ( message )
2023-07-09 00:57:11 -07:00
except Exception as error :
2023-07-11 17:04:28 -07:00
await handle_error ( error )
2023-07-09 00:57:11 -07:00
2023-07-09 01:57:35 -07:00
async def folder_setup ( ) :
2024-01-19 17:57:41 -08:00
# Only tmp, extensions and db are supported, all other folders only exist for backwards compatibility and will be removed soon!
folder_names = [ " tmp " , " extensions " , " db " , " plugins " , " tmp/sfw " , " tmp/nsfw " , " tmp/meme " , " channels " , " users " , " channels/config " , " channels/logs " , " databases " , " databases/currency " , " databases/currency/players " ]
2023-07-09 01:57:35 -07:00
for folder_name in folder_names :
if not os . path . exists ( folder_name ) :
os . mkdir ( folder_name )
2024-01-17 22:35:46 -08:00
return folder_names
2023-07-09 16:06:45 -07:00
2024-01-17 22:35:46 -08:00
async def delete_all_files ( path , safe_folders ) :
2023-07-09 16:06:45 -07:00
for filename in os . listdir ( path ) :
2024-01-17 22:35:46 -08:00
if os . path . isdir ( path + filename ) and not path + filename in safe_folders :
2023-07-09 16:06:45 -07:00
shutil . rmtree ( path + filename )
elif os . path . isfile ( path + filename ) :
os . remove ( path + filename )
2023-07-10 17:44:15 -07:00
2023-12-28 15:19:43 -08:00
async def delete_derp_files ( server_folder ) :
async with asyncssh . connect ( ftp_server , username = ftp_username , password = ftp_password ) as conn :
async with conn . start_sftp_client ( ) as sftp :
for filename in ( await sftp . listdir ( server_folder ) ) :
2023-12-28 17:05:26 -08:00
if filename == ' . ' or filename == ' .. ' or filename == ' style.css ' or filename == ' myScript.js ' :
pass
else :
2023-12-28 15:19:43 -08:00
try :
print ( " Deleting " , filename )
await sftp . remove ( server_folder + filename )
except :
print ( " Couldn ' t delete " , filename )
2024-01-17 19:46:10 -08:00
async def meme_handler ( folder ) :
for file in os . listdir ( folder ) :
filepath = folder + file
await update_meme_webpage ( filepath )
2023-07-10 10:38:28 -07:00
2023-07-10 17:44:15 -07:00
@tasks.loop ( seconds = 1 ) # Run the task every second
2023-07-10 10:38:28 -07:00
async def task_loop ( ) :
current_time = time . localtime ( )
2024-01-17 19:46:10 -08:00
#Run every minute
if current_time . tm_sec == 0 :
await meme_handler ( ' tmp/meme/ ' )
2024-01-18 00:42:02 -08:00
await upload_ftp_ai_images ( ' tmp/sfw/ ' )
2024-01-17 19:46:10 -08:00
2023-07-10 17:44:15 -07:00
#Run daily tasks
2023-07-11 11:54:40 -07:00
if current_time . tm_hour == 17 and current_time . tm_min == 0 and current_time . tm_sec == 0 :
2023-07-10 17:44:15 -07:00
bot_stuff = bot . get_channel ( 544408659174883328 )
2023-07-10 17:08:39 -07:00
output = ' The following tasks failed: \n ``` '
2023-07-10 11:23:05 -07:00
failed_tasks = [ ]
await bot_stuff . send ( " <@242018983241318410> The current time is 5pm. Running daily tasks! " )
try :
await delete_all_files ( " tmp/ " )
except Exception as error :
2023-07-11 17:04:28 -07:00
await handle_error ( error )
2023-07-10 17:08:39 -07:00
failed_tasks . append ( " Delete tmp/ " )
2023-12-28 15:19:43 -08:00
try :
await delete_derp_files ( " /home/debian/websites/derp.phixxy.com/files/ " )
except Exception as error :
await handle_error ( error )
failed_tasks . append ( " Delete derp files " )
2023-07-10 11:23:05 -07:00
if failed_tasks != [ ] :
for failed_task in failed_tasks :
2023-07-10 17:08:39 -07:00
output + = failed_task + ' \n '
output + = ' ``` '
await bot_stuff . send ( output )
2023-07-10 11:23:05 -07:00
else :
await bot_stuff . send ( " All daily tasks successfully ran! " )
2023-07-16 21:36:42 -07:00
2023-07-19 15:02:50 -07:00
async def create_session ( ) :
2023-07-25 17:13:23 -07:00
return aiohttp . ClientSession ( )
2023-07-19 15:02:50 -07:00
async def close_session ( http_session ) :
await http_session . close ( )
@bot.event
async def on_connect ( ) :
bot . http_session = await create_session ( )
2023-07-21 23:25:55 -07:00
@bot.event
async def on_resumed ( ) :
bot . http_session = await create_session ( )
2023-07-19 15:02:50 -07:00
@bot.event
async def on_disconnect ( ) :
await close_session ( bot . http_session )
2023-07-09 00:57:11 -07:00
@bot.event
async def on_ready ( ) :
2024-01-17 18:09:45 -08:00
# Import plugins from plugins folder
for plugin_file in os . listdir ( ' plugins/ ' ) :
if plugin_file != ' __init__.py ' and plugin_file [ - 3 : ] == ' .py ' :
await bot . load_extension ( f ' plugins. { plugin_file [ : - 3 ] } ' )
2024-01-19 17:57:41 -08:00
for plugin_file in os . listdir ( ' extensions/ ' ) :
if plugin_file != ' __init__.py ' and plugin_file [ - 3 : ] == ' .py ' :
await bot . load_extension ( f ' plugins. { plugin_file [ : - 3 ] } ' )
2024-01-17 18:09:45 -08:00
2023-07-09 00:57:11 -07:00
print ( ' We have logged in as {0.user} ' . format ( bot ) )
#stuff to do if first run
2024-01-17 22:35:46 -08:00
folders_made = await folder_setup ( )
await delete_all_files ( " tmp/ " , folders_made )
2023-07-10 10:38:28 -07:00
task_loop . start ( )
2023-07-10 17:44:15 -07:00
2024-01-18 14:34:01 -08:00
2024-01-17 19:46:10 -08:00
async def update_meme_webpage ( filename ) :
server_folder = ( os . getenv ( ' ftp_public_html ' ) + ' ai-memes/ ' )
new_file_name = str ( time . time_ns ( ) ) + " .png "
await upload_sftp ( filename , server_folder , new_file_name )
print ( " Uploaded " , new_file_name )
with open ( " phixxy.com/ai-memes/index.html " , ' r ' ) as f :
html_data = f . read ( )
html_insert = ' <!--ADD IMG HERE--> \n <img src= " ' + new_file_name + ' " loading= " lazy " > '
html_data = html_data . replace ( ' <!--ADD IMG HERE--> ' , html_insert )
with open ( " phixxy.com/ai-memes/index.html " , " w " ) as f :
f . writelines ( html_data )
await upload_sftp ( " phixxy.com/ai-memes/index.html " , server_folder , " index.html " )
2024-01-17 22:43:38 -08:00
os . rename ( filename , ' tmp/ ' + new_file_name )
2024-01-17 19:46:10 -08:00
2023-07-09 00:57:11 -07:00
2023-07-21 19:21:40 -07:00
@bot.event
async def on_reaction_add ( reaction , user ) :
if not random . randint ( 0 , 9 ) :
message = reaction . message
emoji = reaction . emoji
await message . add_reaction ( emoji )
2024-01-09 12:10:21 -08:00
async def pkmn_msg ( discord_id ) :
path = " databases/pokemon/ " + str ( discord_id ) + ' .json '
if os . path . isfile ( path ) :
with open ( path , ' r ' ) as f :
json_data = json . loads ( f . readline ( ) )
json_data [ ' buddy_xp ' ] + = random . randint ( 1 , 5 )
json_data = json . dumps ( json_data )
with open ( path , ' w ' ) as f :
f . writelines ( json_data )
2023-07-09 00:57:11 -07:00
@bot.event
async def on_message ( ctx ) :
2024-01-03 08:44:00 -08:00
#log stuff
2023-07-09 16:06:45 -07:00
logfile = " channels/logs/ {0} .log " . format ( str ( ctx . channel . id ) )
2023-07-09 00:57:11 -07:00
channel_vars = await get_channel_config ( ctx . channel . id )
chat_history_string = await log_chat_and_get_history ( ctx , logfile , channel_vars )
2024-01-09 12:10:21 -08:00
#add pokemon xp
await pkmn_msg ( ctx . author . id )
2024-01-03 08:44:00 -08:00
#handle non-text channels (dms, etc)
2024-01-06 09:18:25 -08:00
if ctx . channel . type . value != 0 and ctx . author . id != 242018983241318410 :
#This used to notify the user it cannot respond in this channel, but that spammed threads
return
2024-01-03 08:44:00 -08:00
await react_to_msg ( ctx , channel_vars [ " react_to_msgs " ] ) #emoji reactions
2023-07-09 00:57:11 -07:00
if channel_vars [ " commands_enabled " ] or ( ctx . author . id == 242018983241318410 and ctx . content [ 0 ] == " ! " ) :
await bot . process_commands ( ctx )
if not channel_vars [ " commands_enabled " ] :
await ctx . channel . send ( " This command only ran because you set it to allow to run even when commands are disabled " )
if channel_vars [ " chat_enabled " ] and not ctx . author . bot :
if ctx . content and ctx . content [ 0 ] != " ! " :
await chat_response ( ctx , channel_vars , chat_history_string )
elif not ctx . content :
await chat_response ( ctx , channel_vars , chat_history_string )
bot . run ( discord_token )