import asyncio import logging import traceback from dotenv import load_dotenv load_dotenv() import os from ads import AsyncDataSet from grk import GrokAPIClient from devranta.api import Api 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" rant["rant_id"] = rant["id"] 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"]) diss = f"@{obj['user_username']} {diss}" print("Response: \033[91m" + diss + "\033[0m") await self.api.post_comment(obj["rant_id"], diss) 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())