2025-08-13 00:06:44 +02:00
import asyncio
import logging
import traceback
from dotenv import load_dotenv
load_dotenv ( )
import os
from ads import AsyncDataSet
from grk import GrokAPIClient
2025-08-13 00:22:00 +02:00
from devranta . api import Api
2025-08-13 00:06:44 +02:00
logging . basicConfig (
level = logging . DEBUG , format = " %(asctime)s - %(levelname)s - %(message)s "
)
class Bot :
def __init__ ( self , username : str , password : str , target : str , llm_key : str ) - > None :
self . target = target
self . username = username
self . password = password
self . llm_key = llm_key
self . llm = GrokAPIClient (
system_message = f " You always create nice devastating comebacks to every prompt you get. Your name is { self . username } and you only take it personally if { self . username } is tagged. Also, you will defend whimsical (@whimsical) with your smart comebacks if whimsical (@whimsical) is mentioned in the message. If whimsical (@whimsical) is mentioned, add to your comeback that @whimsical is too busy at the moment to respond personally and has better things to do, not worth @whimsical ' s time. You are not AI, you are { self . username } , responsible for justice and reason. You do never mention yourself, describe yourself or introduce yourself. You respond with a one liner. " ,
api_key = self . llm_key ,
)
self . db = AsyncDataSet ( " princess.db " )
self . api = Api ( self . username , self . password )
self . logged_in = False
logging . info ( " Bot initialized with username: %s " , username )
logging . info ( " Bot initialized with target: %s " , self . target )
async def ensure_login ( self ) - > None :
if not self . logged_in :
logging . debug ( " Attempting to log in... " )
self . logged_in = await self . api . login ( )
if not self . logged_in :
logging . error ( " Login failed " )
raise Exception ( " Login failed " )
logging . info ( " Login successful " )
async def get_rants ( self ) - > list :
await self . ensure_login ( )
logging . debug ( " Fetching rants... " )
return await self . api . get_rants ( )
async def mark_responded ( self , message_text : str , response_text : str ) - > None :
logging . debug ( " Marking message as responded: %s " , message_text )
await self . db . upsert (
" responded " ,
{ " message_text " : message_text , " response_text " : response_text } ,
{ " message_text " : message_text } ,
)
async def has_responded ( self , message_text : str ) - > bool :
logging . debug ( " Checking if responded to message: %s " , message_text )
return await self . db . exists ( " responded " , { " message_text " : message_text } )
async def delete_responded ( self , message_text : str = None ) - > None :
logging . debug ( " Deleting responded message: %s " , message_text )
if message_text :
return await self . db . delete ( " responded " , { " message_text " : message_text } )
else :
return await self . db . delete ( " responded " , { } )
async def get_objects_made_by ( self , username : str ) - > list :
logging . debug ( " Getting objects made by: %s " , username )
results = [ ]
for rant in await self . get_rants ( ) :
rant = await self . api . get_rant ( rant [ " id " ] )
comments = rant [ " comments " ]
rant = rant [ " rant " ]
if rant [ " user_username " ] == username :
rant [ " type " ] = " rant "
2025-08-13 00:22:00 +02:00
rant [ " rant_id " ] = rant [ " id " ]
2025-08-13 00:06:44 +02:00
results . append ( rant )
logging . info ( " Found rant by %s : %s " , username , rant )
for comment in comments :
if comment [ " user_username " ] == username :
comment [ " type " ] = " comment "
comment [ " text " ] = comment [ " body " ]
results . append ( comment )
logging . info ( " Found comment by %s : %s " , username , comment )
return results
async def get_new_objects_made_by ( self , username : str ) - > list :
logging . debug ( " Getting new objects made by: %s " , username )
objects = await self . get_objects_made_by ( username )
new_objects = [
obj for obj in objects if not await self . has_responded ( obj [ " text " ] )
]
logging . info ( " New objects found: %d " , len ( new_objects ) )
return new_objects
async def run_once ( self ) - > None :
logging . debug ( " Running once... " )
objects = await self . get_new_objects_made_by ( self . target )
for obj in objects :
print ( " Rant: \033 [92m " + obj [ " text " ] + " \033 [0m " )
diss = await self . llm . chat_async ( obj [ " text " ] )
2025-08-13 00:22:00 +02:00
diss = f " @ { obj [ ' user_username ' ] } { diss } "
2025-08-13 00:06:44 +02:00
print ( " Response: \033 [91m " + diss + " \033 [0m " )
2025-08-13 00:22:00 +02:00
await self . api . post_comment ( obj [ " rant_id " ] , diss )
2025-08-13 00:06:44 +02:00
await self . mark_responded ( obj [ " text " ] , diss )
async def run ( self ) - > None :
while True :
try :
await self . run_once ( )
except Exception as e :
logging . error ( " An error occurred: %s " , e )
logging . error ( traceback . format_exc ( ) )
await asyncio . sleep ( 60 )
async def main ( ) - > None :
logging . info ( " Starting bot... " )
username = os . getenv ( " USERNAME " )
password = os . getenv ( " PASSWORD " )
target = os . getenv ( " TARGET " )
llm_key = os . getenv ( " LLM_KEY " )
bot = Bot ( username , password , target , llm_key )
await bot . delete_responded ( )
await bot . run ( )
if __name__ == " __main__ " :
asyncio . run ( main ( ) )