stdout=subprocess.PIPE).stdout.decode('utf-8') return { 'user': ns.user, 'pass': password.rstrip('\n'), 'server': ns.server.replace('https://', '', 1), 'dir': ns.directory.rstrip('/') } async def main(): client.add_event_callback(handle_message, RoomMessageText) await client.login(args['pass']) await client.sync() #We need the list of rooms await asyncio.gather( client.sync_forever(timeout=500), *[fifo_listener_proxy(room) for room in client.rooms.values()], return_exceptions=True) if __name__ == "__main__": args = get_args() client = AsyncClient('https://' + args['server'], args['user']) args['clientdir'] = os.path.join(args['dir'], args['server'], args['user']) try: asyncio.run(main()) except KeyboardInterrupt: pass finally: asyncio.run(client.close()) os._exit(1) #To kill the other threads.
class smythClient(object): response = None def __init__(self, homeserver, username, password, use_ssl): self.homeserver = homeserver self.username = username self.password = password self.client = AsyncClient(self.homeserver, self.username, ssl = use_ssl) #Initialize per room configurator self.roomConfigsPath = os.path.expanduser("~/.smythbot/rooms.ini") self.smythbotRoomConfigs = configparser.ConfigParser() # Declare the nessescary variables: self.isSynced = False self.smythbot_handler = "!smythbot" #Add callbacks self.client.add_event_callback(self.onNewMatrixEventReccieved, RoomMessageText) sync_event = asyncio.create_task(self.watch_for_sync(self.client.synced)) return async def init_login(self): try: self.response = await self.client.login(self.password) if "M_FORBIDDEN" in str(self.response): print(str(self.response) + "\nExiting") os._exit(1) except: print("There was a problem logging into the specified homeserver.") LoginError = sys.exc_info() print("The error was: " + str(LoginError)) print ("Exiting...") os._exit(1) return async def start_client(self): """ Name: start_client Expected input: None Expected output: None (may change) Description: This function is meant to be called in the main program loop It's job is to start all the other functions asscociated with the Matrix client end of sMythbot. First it calls the init_login function. If that is successful, it will request a list of available rooms that we have joined to check for configuration options (which will be checked via other functons). When that is done, it will start the synchronization loop in AsyncClient and return """ print("Attempting to login to " + self.homeserver) await self.init_login() print(self.response) print("Firing initial sync...") await self.sync_room_configs() print ("Starting \"Sync Forever\" loop.\n") try: await self.client.sync_forever(timeout=30000, full_state=True) except KeyboardInterrupt: print("Caught exit signal. Closing sMythbot.") self.client.close() sys.exit(0) return async def sync_room_configs(self): """ Name: Sync_room_configs Expected input: None Expected output: None (may change) Description: This function reads sMythbot room configurations from the rooms.ini file, or creates them. Each room that sMythbot is part of can be configured with seperate properties. These properties will be updated as the bot's settings are changed. """ # Fire an initial sync to get a rooms list: first_sync = await self.client.sync(300000) # Then we check if roooms.ini exists. if os.path.exists(self.roomConfigsPath): self.smythbotRoomConfigs.read(self.roomConfigsPath) #If so, we read the existing values into the configparser for room_id in first_sync.rooms.join: # Now we check for individual room configurations via the existence of a room ID in the file # First, the easy part. If a room ID DOES NOT exist, we can safely assume there are no config options for it yet: newDataWritten = False if not self.smythbotRoomConfigs.has_section(room_id): newDataWritten = True await self.populateRoomConfigs(room_id, False) if newDataWritten: await self.writeChangesToDisk() else: print ("No new room configurations found") async def populateRoomConfigs(self, room_id, writeToDisk): self.smythbotRoomConfigs[room_id] = {} self.smythbotRoomConfigs[room_id]["MythTv Backend Address"] = "not set" self.smythbotRoomConfigs[room_id]["MythTv Backend Port"] = "6544" self.smythbotRoomConfigs[room_id]["Output Type"] = "table" self.smythbotRoomConfigs[room_id]["Room Notifications"] = "False" print("Added new room Configuration " + room_id) if writeToDisk: await self.writeChangesToDisk() return async def writeChangesToDisk(self): print("Writing New data to file: " + self.roomConfigsPath) with open(self.roomConfigsPath, "w") as roomsFile: self.smythbotRoomConfigs.write(roomsFile) async def watch_for_sync(self, sync_event): """ Input: AsyncClient Sync Event Output: None Description: When AsyncClient fires a synced event (which only happens during a "sync_forever" loop), this function is called. """ while True: await sync_event.wait() await self.onIsSyncedCalled() async def onIsSyncedCalled(self): """ Called from the "watch_for_sync" event. This funtion sets the client state as being up to speed with the current messages. """ #print ("We are synced!") self.isSynced = True return async def onNewMatrixEventReccieved(self, room, event): if self.isSynced and event.body.startswith(self.smythbot_handler): print("Reccieved sMythbot command from room " + room.room_id + ", sent by " + event.sender) if not event.body.isprintable(): print ("ERROR: Not sending command. Non-printable characters may cause a security risk") await self.client.room_send(room.machine_name, "m.room.message", await self.reply("<h1>Command not processed</h1><hr> The command you sent contains at least one non-printable character. As these often pose security risks, sMythbot will not process it.")) return command_runner = smythbotCommandRunner.smythbot_command(event.body, mythtv_backend=self.smythbotRoomConfigs[room.room_id]["MythTv Backend Address"], mythtv_port=self.smythbotRoomConfigs[room.room_id]["MythTv Backend Port"], formatting=self.smythbotRoomConfigs[room.room_id]["Output Type"]) command_outputs = await command_runner.poulate_command_index() # The various smythbot commands will be processed inside of this function for item in command_outputs: for key_item in item.keys(): if key_item == "room settings data": await self.adjust_room_settings(item["room settings data"], room.room_id) await self.client.room_send(room.machine_name, "m.room.message", await self.reply(item["command output"])) else: return async def reply(self, reply_body): reply_content = {} reply_content["msgtype"] = "m.notice" reply_content["body"] ="" reply_content["format"] = "org.matrix.custom.html" reply_content["formatted_body"] = reply_body return reply_content async def adjust_room_settings(self, room_settings_dict, room_id): print("adjusting room setings in room " + room_id) self.smythbotRoomConfigs[room_id][room_settings_dict["property name"]] = room_settings_dict["property value"] await self.writeChangesToDisk() return