class QuantumJumpBot: def __init__(self, settings): self._ws = None self.state = BotState(BotState.INITIALIZED) self.start_time = time.time() self.api = Http() self.cm = CogManager() self.settings = settings self.botconfig = self.settings.Bot self.ul = UserList() self.room = self.botconfig.roomname if self.settings.Bot.debug: self.log = QuantumLogger('QuantumJump', self.room, 10) else: self.log = QuantumLogger('QuantumJump', self.room, 19) async def wsend(self, data): if type(data) is list: data = f"42{json.dumps(data)}" elif type(data) is str: type_exemptions = ["2probe", "5", "2"] if not data.startswith("42") and data not in type_exemptions: data = f"42{data}" self.log.ws_send(data) await self._ws.send(data) async def run(self): enabled_modules = self.settings.Modules["enabled"] self.cm.load_all(enabled_modules, bot=self) await self.connect() async def disconnect(self): self.state = BotState.DISCONNECT await self._ws.close() async def connect(self): logged_in = await self.api.login(self.botconfig.username, self.botconfig.password) async with websockets.connect( uri=await self.api.get_wss(), timeout=600, origin="https://jumpin.chat" ) as self._ws: self.log.info("Connected to websocket.") self.state = BotState.RUNNING await self.wsend("2probe") async for message in self._ws: await self._recv(message=message) async def _recv(self, message: str): if not message: return self.log.ws_event(message) if message.isdigit(): return if message == "3probe": await self.wsend("5") roommsg = [ "room::join", {"room": self.botconfig.roomname} ] await self.wsend(roommsg) asyncio.create_task(self.pacemaker()) # await self.api.enroll() return data = json.loads(message[2:]) if data[0] == "room::updateUserList": for user in data[1].get("users", []): if user: self.ul.add(User(**user)) if user := data[1].get("user", None): if user: self.ul.add(User(**user)) if data[0] == "room::updateUser": self.ul.update(User(**data[1].get("user", None))) if data[0] == "room::updateUsers": for user in data[1].get("users", []): self.ul.update(User(**user)) # todo update userlist when a name changes. if data[0] == "room::handleChange": # ["room::handleChange",{"userId":"5e22c017be8a4900076d3e21","handle":"Tech"}] pass if data[0] == "room::disconnect": self.ul.remove(User(**data[1].get("user", None))) if data[0] == "self::join": nickmsg = [ "room::handleChange", { "userId": self.api.login_data.user.get("userId"), "handle": self.botconfig.nickname } ] await self.wsend(nickmsg) await self.wsend('42["room::users", {}]') # deprecated # user_list_data = await self.api.getroominfo(room=str(self.room)) # self.ul = UserList(**user_list_data) if data[0] == "client::error": if error := data[1].get("error", False): if error == 'ERR_ACCOUNT_REQUIRED': await self.disconnect() raise Exception("Account must be signed in to join this room.") if error == 'ENOSESSION': await self.disconnect() raise Exception("Session was invalidated.")
class Cog: def __init__(self, bot): self.bot = bot self.name = self.__class__.__name__ self.__cog__ = True self.log = QuantumLogger(self.name, self.bot.room) self.log.info(f"initializing cog") self.bot_settings = bot.botconfig self.settings = bot.settings.Modules.get(self.__class__.__name__, None) self.events = [getattr(self, name) # what gets stored. for name in dir(self) # loop if "__" not in name # ignore builtins and callable(getattr(self, name)) # is callable and hasattr(getattr(self, name), "__event__") ] self.commands = [getattr(self, name) # what gets stored. for name in dir(self) # loop if "__" not in name # ignore builtins and callable(getattr(self, name)) # is callable and hasattr(getattr(self, name), "__command__") ] ##### # client control ###### async def ws_send(self, data): await self.bot.wsend(data=data) async def send_message(self, message: str, room: str = None, color=None, style=None): if not room: room = self.bot_settings.roomname if color is None and self.bot_settings.rainbow: color = Colors.random() await self.change_color(color) elif color is not None: await self.change_color(color) if len(message) > 254: # re.DOTALL makes . match everything, including newline messages = re.findall("(.{1,254}[.,;:]|.{1,254})", message, re.DOTALL) chunk_limit = self.bot_settings.chunk_limit if chunk_limit == 0 or chunk_limit == None: chunk_limit = len(messages) for i in range(0, chunk_limit): message = messages[i][:254] if style is not None: message = encodetxt(message, style) await self.send_message(message, room=room, color=color, style=style) return else: if style is not None: # TODO check if valid style message = encodetxt(message, style) data = [ "room::message", { "message": message, "room": room } ] await self.ws_send(data=data) async def send_action(self, message: str, room: str = None, color=None, style=None): """/me messages, styling doesn't work""" if color is None and self.bot_settings.rainbow: color = Colors.random() await self.change_color(color) elif color is not None: await self.change_color(color) if style is not None: # TODO check if valid style message = encodetxt(message, style) if not room: room = self.bot_settings.roomname data = [ "room::command", { "message": { "command": "me", "value": message }, "room": room } ] await self.ws_send(data=data) ##### # Jumpin Commands ##### async def remove_yt(self, id): data = ["youtube::remove", {"id": id}] await self.ws_send(data=data) async def checkisplaying(self, notify: bool = True): data = [ "youtube::checkisplaying", { "notify": notify } ] await self.ws_send(data=data) async def play(self, video_id: str, title: str): data = [ "youtube::play", { "videoId": video_id, "title": title } ] await self.ws_send(data=data) async def remove(self, id: str): data = [ "youtube::remove", { "id": id } ] await self.ws_send(data=data) async def get_ignore_list(self, room: str): data = [ "room::getIgnoreList", { "roomName": room } ] await self.ws_send(data=data) async def kick(self, user_id: str): data = [ "room::operation::kick", { "user_list_id": user_id } ] await self.ws_send(data=data) async def banlist(self): data = [ "room::operation::banlist", { "user_list_id": self.bot.api.session.user.user_id } ] await self.ws_send(data=data) async def ban(self, user_id: str, duration: int = 24): # perm is 4464 data = [ "room::operation::ban", { "user_list_id": user_id, "duration": duration } ] await self.ws_send(data=data) async def unban(self, ban_id: str, handle: str): data = [ "room::operation::unban", { "banlistId": ban_id, "handle": handle } ] await self.ws_send(data=data) async def handle_change(self, nick: str): data = [ "room::handleChange", { "handle": nick } ] await self.ws_send(data=data) async def change_color(self, color: str): data = [ "room::changeColor", { "color": color } ] await self.ws_send(data=data) async def is_still_joined(self, room: str = None): if not room: room = self.bot_settings.roomname data = [ "room::isStillJoined", { "room": room } ] await self.ws_send(data=data) async def join(self, room: str = None): if not room: room = self.bot_settings.roomname data = ["room::join", {"room": room}] await self.ws_send(data=data) async def close_broadcast(self, user_id: str): data = [ "room::operation::closeBroadcast", { "user_list_id": user_id } ] await self.ws_send(data=data) async def do_pm(self): pass def __repr__(self) -> str: return self.name # these dont actually need to be here anymore. # left for reference @event(event="room::updateUser") async def updateUser(self, user: User): pass @event(event="room::updateUserList") async def updateUserList(self, userlist: UserList): pass @event(event="room::updateIgnore") async def updateIgnore(self, ignore_list: list): pass @event(event="room::status") async def status(self, status: Status): pass @event(event="room::handleChange") async def handleChange(self, handle_change: HandleChange): pass @event(event="room::message") async def message(self, message: Message): pass @event(event="room::error") async def error(self, message): pass @event(event="room::alert") async def alert(self, message): pass @event(event="youtube::playlistUpdate") # 42["youtube::playlistUpdate",[{"startTime":null,"endTime":null,"description":null,"channelId":"UCqukXrA3L_B0EVHiM14EU7g","pausedAt":null,"_id":"5dea8a123533d70008b01aa9","mediaId":"jesc3yvZSws","title":"DANZIG - Mother Lyrics","link":"https://youtu.be/jesc3yvZSws","duration":226,"thumb":"https://i.ytimg.com/vi/jesc3yvZSws/default.jpg","mediaType":"TYPE_YOUTUBE","startedBy":"5c4b7b6746bb1a000712c13c","createdAt":"2019-12-06T17:04:18.334Z"}]] async def playlistUpdate(self, playlistUpdate: list): pass
class QuantumJumpBot: def __init__(self, settings): self._ws = None self.state = BotState(BotState.INITIALIZED) self.start_time = time.time() self.api = Http() self.cm = CogManager() self.settings = settings self.botconfig = self.settings.Bot self.ul = UserList() self.room = self.botconfig.roomname if self.settings.Bot.debug: self.log = QuantumLogger('QuantumJump', 10) else: self.log = QuantumLogger('QuantumJump', 19) async def wsend(self, data): if type(data) is list: data = f"42{json.dumps(data)}" elif type(data) is str: type_exemptions = ["2probe", "5", "2"] if not data.startswith("42") and data not in type_exemptions: data = f"42{data}" await self._ws.send(data) self.log.ws_send(data) async def run(self): enabled_modules = self.settings.Modules["enabled"] self.cm.load_all(enabled_modules, bot=self) await self.connect() async def disconnect(self): self.state = BotState.DISCONNECT await self._ws.close() async def connect(self): logged_in = await self.api.login(self.botconfig.username, self.botconfig.password) self.log.info(f"Logged in: {logged_in}") async with websockets.connect( uri=await self.api.get_wss(), timeout=600, origin="https://jumpin.chat") as self._ws: self.log.info("Connected to websocket.") self.state = BotState.RUNNING await self.wsend("2probe") async for message in self._ws: await self._recv(message=message) async def _recv(self, message: str): if message.isdigit(): return self.log.ws_event(message) if message == "3probe": await self.wsend("5") roommsg = ["room::join", {"room": self.botconfig.roomname}] await self.wsend(roommsg) asyncio.create_task(self.pacemaker()) return data = json.loads(message[2:]) if data[0] == "room::status": msg = data[1].get("message") if "added a video to the playlist" in msg: ytid = re.search("(?:v=|\.be\/)(.{11})", msg)[1] myid = msg.split(' ')[0] title = msg.split('added a video to the playlist: ')[1] title = title.split('(https://')[0] #data = await self.endpoint(f"https://www.googleapis.com/youtube/v3/videos/?part=snippet%2CcontentDetails%2Cstatistics&id={ytid}",ytid) mydb = mysql.connector.connect( host= "gk90usy5ik2otcvi.cbetxkdyhwsb.us-east-1.rds.amazonaws.com", user="******", passwd="n9rgtn2meyhthi9t", database="ppyh9hbf089e36w6") cursor = mydb.cursor() ids = ((ytid)) myv = f"SELECT COUNT(*) as count FROM videos WHERE ytid LIKE '{ytid}'" add = f"""INSERT INTO videos (ytid, title) VALUES ('{ytid}', '{title}')""" our = "SELECT COUNT(*) as count FROM videos" cursor.execute(myv) myvideos = cursor.fetchone()[0] if myvideos == 0: try: cursor.execute(add, ids) mydb.commit() print(f"Added {title} to video.coder.ml/watch/{ytid}.") except: mydb.rollback() cursor.execute(our) allvids = cursor.fetchone()[0] print(f"{allvids} videos in cache.") mydb.close() if data[0] == "room::updateUserList": for user in data[1].get("users", []): if user: self.ul.add(User(**user)) if user := data[1].get("user", None): if user: self.ul.add(User(**user)) if data[0] == "room::updateUser": self.ul.update(User(**data[1].get("user", None))) if data[0] == "room::updateUsers": for user in data[1].get("users", []): self.ul.update(User(**user)) #todo update userlist when a name changes. if data[0] == "room::handleChange": # ["room::handleChange",{"userId":"5e22c017be8a4900076d3e21","handle":"Tech"}] pass if data[0] == "room::disconnect": self.ul.remove(User(**data[1].get("user", None))) if data[0] == "self::join": nickmsg = [ "room::handleChange", { "userId": self.api.login_data.user.get("userId"), "handle": self.botconfig.nickname } ] await self.wsend(nickmsg) await self.wsend('42["room::users", {}]') # deprecated # user_list_data = await self.api.getroominfo(room=str(self.room)) # self.ul = UserList(**user_list_data) if data[0] == "client::error": if error := data[1].get("error", False): if error == 'ERR_ACCOUNT_REQUIRED': await self.disconnect() raise Exception( "Account must be signed in to join this room.") if error == 'ENOSESSION': await self.disconnect() raise Exception("Session was invalidated.")