136 lines
5.1 KiB
Python
136 lines
5.1 KiB
Python
|
import asyncio
|
||
|
import logging
|
||
|
import traceback
|
||
|
|
||
|
from dotenv import load_dotenv
|
||
|
|
||
|
load_dotenv()
|
||
|
|
||
|
import os
|
||
|
|
||
|
from ads import AsyncDataSet
|
||
|
from devranta.api import Api
|
||
|
from grk import GrokAPIClient
|
||
|
|
||
|
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"
|
||
|
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"])
|
||
|
print("Response: \033[91m" + diss + "\033[0m")
|
||
|
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())
|