class Director: def __init__(self): self.BOTUSERNAME = "******" self.BOTPASSWORD = "******" self.BOTSERVO = "matrix.org" self.RID = "!RnpiUpFIsfzZfHdHQf:matrix.org" self.realRID = "!obQcCWaLRAUgiGBvMg:postmarketos.org" self.MainClient = MatrixClient("https://" + self.BOTSERVO) self.token = self.MainClient.login_with_password( username=self.BOTUSERNAME, password=self.BOTPASSWORD) self.APIWrapper = MatrixHttpApi("https://" + self.BOTSERVO, token=self.token) self.target_room = Room(self.MainClient, self.RID) print("ready") def mainThread(self): self.MainClient.add_listener(self.callback_event, event_type="m.room.member") self.MainClient.start_listener_thread() while True: input() def callback_event(room, event): if event.get("content").get("membership") == "join": user_id = event.get("sender") d.target_room.send_html( 'This room is not official postmarketOS room. Please join the <a href="https://matrix.to/#/#porting:postmarketos.org">#porting:postmarketos.org</a> room' ) try: d.APIWrapper.invite_user(d.realRID, user_id) except: pass # user already joined the rooms
class ZmalauBot(): def __init__(self, username, password, roomname): # connect to room self.crypto = CryptoEngine() self.air_pollution = AirPollutionEngine() self.client = MatrixClient("http://matrix.org") self.token = self.client.login_with_password(username=username, password=password) self.room = self.client.join_room(roomname) # add bot reactions self.room.add_listener(self.on_message) self.client.start_listener_thread() self.room.send_text('Dzień dobry') def default_response(self, message): return self.crypto.analyze_message_and_prepare_response(message) def on_message(self, room, event): if event['type'] == "m.room.message": if USERNAME in event['sender']: return if event['content']['msgtype'] == "m.text": message = event['content']['body'] m = message.lower() if 'zmalau' in m or 'urus' in m: if any([word in m for word in self.crypto.trigger_words]): response = self.crypto.analyze_message_and_prepare_response(m) elif any([word in m for word in self.air_pollution.trigger_words]): response = self.air_pollution.analyze_message_and_prepare_response(m) else: response = self.default_response(m) self.room.send_text(response)
def main(host, username, password, room_id_alias): client = MatrixClient(host) try: client.login_with_password(username, password) except MatrixRequestError as e: print(e) if e.code == 403: print "" else: print "" except MissingSchema as e: print(e) try: room = client.join_room(room_id_alias) except MatrixRequestError as e: print(e) if e.code == 400: print "" else: print "" room.add_listener(on_message) client.start_listener_thread() while True: time.sleep(5)
def main(host, username, password): client = MatrixClient(host) rooms = client.get_rooms() try: db = sqlite3.connect('db') cursor = db.cursor() cursor.execute( '''CREATE TABLE IF NOT EXISTS messages(id INTEGER PRIMARY KEY, date TEXT, roomid TEXT, sender TEXT, message TEXT)''' ) db.commit() except Exception as e: db.rollback() raise e finally: db.close() def on_invite(room_id, state): print("joining room " + room_id) room = client.join_room(room_id) room.add_listener(on_message) try: token = client.login_with_password(username, password) global matrix matrix = MatrixHttpApi(host, token) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username or password.") sys.exit(4) else: print("Check if server details are correct.") sys.exit(2) except MissingSchema as e: print("Bad URL format.") print(e) sys.exit(3) for room in rooms: try: roomname = matrix.get_room_name(room) print("Already in room " + roomname['name']) room_to_listen = client.join_room(room) room_to_listen.add_listener(on_message) except MatrixRequestError as e: print(e) if e.code == 400: print("Room ID/Alias in the wrong format") sys.exit(11) else: print("Couldn't find room.") sys.exit(12) client.add_invite_listener(on_invite) client.start_listener_thread() while True: time.sleep(30)
def main(): global g_client print("Connecting to server: {}".format(botconfig.client_url)) g_client = MatrixClient(botconfig.client_url) password = get_password() try: print("Logging in with username: {}".format(botconfig.username)) g_client.login_with_password(botconfig.username, password) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username or password.") sys.exit(4) else: print("Check your sever details are correct.") sys.exit(2) except MissingSchema as e: print("Bad URL format.") print(e) sys.exit(3) # room dictionary that will be passed to the listener manager rooms = {} for room_address in botconfig.rooms: try: room = g_client.join_room(room_address) room.add_listener(on_message) rooms[room_address] = room print("Joined room: {}".format(room_address)) except MatrixRequestError as e: print(e) if e.code == 400: print("{}: Room ID/Alias in the wrong format".format( room_address)) sys.exit(11) else: print("Couldn't find room: {}".format(room_address)) sys.exit(12) g_client.add_invite_listener(on_invite) g_client.start_listener_thread() listener = ListenerManager(rooms, botconfig.listener_port) listener.start_listener_thread() try: while True: pass except KeyboardInterrupt: print("Shutting down.")
class Matrix(object): def __init__(self, main): self.main = main self.login = login self.client = MatrixClient(address, encryption=encryption) self.client.login(login, passwd, device_id=device_id) self.rooms = [] self.room_data = {} for room_id, room in self.client.get_rooms().items(): self.main.system(f'add matrix room {room_id}') self.rooms.append(room_id) self.room_data[room_id] = { 'history': [], 'last_read': 0, 'name': room.name or room_id, 'topic': room.topic or '', } for event in room.events: if event['type'] == 'm.room.message': self.room_data[room_id]['history'].append( self.event_to_history(event)) if chan in room.aliases: self.main.active_room_idx = len(self.rooms) - 1 self.main.active_account_idx = 1 room.add_listener(self.listener) self.client.start_listener_thread() def __str__(self): return f'Matrix {self.login}' def event_to_history(self, event): self.add_sender(event['sender']) return datetime.fromtimestamp( event['origin_server_ts'] / 1000), event['sender'], event['content']['body'] def add_sender(self, sender): if sender not in self.main.users: self.main.users[sender] = self.client.api.get_display_name(sender) def listener(self, room, event): if event['type'] == 'm.room.message': history = self.room_data[room.room_id]['history'] history.append(self.event_to_history(event)) if self.main.account == self and room.room_id == self.main.room_id: self.main.update_text(history) else: self.main.system(str(event)) def send(self, text, room_id): self.client.rooms[room_id].send_text(text)
def run(self): client = MatrixClient(self._mconf.host) try: log.debug('Logging into Matrix') client.login_with_password(self._mconf.username, self._mconf.password) except MatrixRequestError as e: if e.code == 403: log.error('Bad username or password: {}'.format(str(e))) sys.exit(2) else: log.error( 'Could not log in - check the server details are correct: {}' .format(str(e))) sys.exit(2) except Exception as e: log.error('Error while logging in: {}'.format(str(e))) sys.exit(2) log.debug('Joining rooms') self._rooms = [] for room_id in self._mconf.rooms: try: room = client.join_room(room_id) except MatrixRequestError as e: if e.code == 400: log.error( 'Room ID/Alias ("{}") in the wrong format. Can not join room: {}' .format(room_id, str(e))) sys.exit(2) else: log.error('Could not find room "{}"'.format(room_id)) sys.exit(2) room.add_listener(self._on_room_message) self._rooms.append(room) log.info('Logged into Matrix, ready to publish information') client.start_listener_thread() while True: topic, msg_b = self._lhsub_socket.recv_multipart() # TODO: Validate signature here! event = json.loads(str(msg_b, 'utf-8')) self._on_event_received(event)
class Matrix: def __init__(self, credentials, rooms, send_to, transport): self.credentials = credentials self.rooms = rooms self.send_to = send_to self.transport = transport self.connect() def connect(self): self.client = MatrixClient(self.credentials.host, token=self.credentials.token, user_id=self.credentials.userid) # Listen for events in all configured rooms for room in self.rooms: joined_room = self.client.join_room(room) joined_room.add_listener(self.listener) self.client.start_listener_thread() self.print_rooms() self.runner() def print_rooms(self): rooms = self.client.get_rooms() for room_id, room in rooms.items(): print("Matrix: {} ({})".format(room_id, room.display_name)) def runner(self): worker_thread = threading.Timer(1.0, self.runner) worker_thread.daemon = True worker_thread.start() if not self.transport.to_matrix.empty(): message = self.transport.to_matrix.get() self.client.api.send_message(message.destination, message.get_message()) def listener(self, room, message): if self.send_to.get(room.room_id): if message.get('content', {}).get('msgtype') == 'm.text': if self.credentials.userid != message['sender']: message = Message(self.send_to[room.room_id], message['sender'], message['content']['body']) self.transport.send_hangouts(message)
def main(): client = MatrixClient(os.environ.get('BASE_URL'), token=os.environ.get('TOKEN'), user_id=os.environ.get('USER_ID')) # try: # client.login_with_password(username, password) # except MatrixRequestError as e: # print(e) # if e.code == 403: # print("Bad username or password.") # sys.exit(4) # else: # print("Check your sever details are correct.") # sys.exit(2) # except MissingSchema as e: # print("Bad URL format.") # print(e) # sys.exit(3) try: room = client.join_room(os.environ.get('ROOM_ID')) except MatrixRequestError as e: print(e) if e.code == 400: print("Room ID/Alias in the wrong format") sys.exit(11) else: print("Couldn't find room.") sys.exit(12) room.add_listener(on_message) print("Starting listener thread, bot ready") client.start_listener_thread() while True: msg = samples_common.get_input() if msg == "/quit": break else: room.send_text(msg)
def main(host, username, password, room_id_alias): client = MatrixClient(host) try: client.login_with_password(username, password) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username or password.") sys.exit(4) else: print("Check your sever details are correct.") sys.exit(2) except MissingSchema as e: print("Bad URL format.") print(e) sys.exit(3) try: room = client.join_room(room_id_alias) except MatrixRequestError as e: print(e) if e.code == 400: print("Room ID/Alias in the wrong format") sys.exit(11) else: print("Couldn't find room.") sys.exit(12) room.add_listener(on_message) client.start_listener_thread() while True: msg = samples_common.get_input() if msg == "/quit": break else: room.send_text(msg)
class Client(): def __init__(self): self.client = None self.room = None def authenticate(self, username, password, host): self.client = MatrixClient(host) try: self.client.login_with_password(username, password) except MatrixRequestError as e: if e.code == 403: print("Bad username or password.") else: print("Check your sever details are correct.") return False except MissingSchema as e: print("Bad URL format.") print(e) return False def get_contacts(self): return self.client.get_rooms() def get_messages(self, contact): pass def start_conversation(self, room_id): self.room = self.client.join_room(room_id) self.room.add_listener(event_listener) self.client.start_listener_thread() def send_message(self, message): self.room.send_text(message) def event_listener(self, room, event): global app app.send_signal("matrix", {"contact": room, "event": event})
class MatrixHandler(): def __init__(self, username, password, room, gotMsgCallback=blackhole): try: from matrix_client.client import MatrixClient except ImportError: print("ERR:matrix_client import fail: Please exec:") print("cd ./matrix-python-sdk") print("pip install ./") raise RuntimeError("Failed to import matrix_client sdk") self.matrix = MatrixClient("https://matrix.org") self.matrix.login_with_password(username=username, password=password) self.matrix.start_listener_thread() self.room = self.matrix.join_room(room) self.room.send_text("Hello!") self.room.add_listener(gotMsgCallback) #self.gotMsgCallback=gotMsgCallback def sendMsg(self, msgData): res = self.room.send_text(msgData) return res def sendImg(self, imgdir, content_type="image/jpeg"): with open(imgdir, mode="rb") as f: uri = self.matrix.upload(f.read(), content_type) self.room.send_image(uri, 'wximg') f.close() def sendAudio(self, fdir, content_type="audio/mp3"): with open(fdir, mode="rb") as f: uri = self.matrix.upload(f.read(), content_type) self.room.send_audio(uri, 'wxaudio') f.close() def sendHtml(self, htmlData): res = self.room.send_html(htmlData) return res '''
class TinyMatrixtBot(): def __init__(self, path_config): signal.signal(signal.SIGHUP, self.on_signal) signal.signal(signal.SIGINT, self.on_signal) signal.signal(signal.SIGTERM, self.on_signal) self.config = configparser.ConfigParser() self.config.read(path_config) path_current = os.path.dirname(os.path.realpath(__file__)) self.path_lib = self.config.get("tiny-matrix-bot", "lib", fallback=os.path.join( path_current, "scripts")).strip() print("SCRIPTS {}".format(self.path_lib)) if os.access(self.path_lib, os.R_OK): self.scripts = self.load_scripts(self.path_lib) else: print("ERROR {} is not readable".format(self.path_lib)) sys.exit(0) self.path_var = self.config.get("tiny-matrix-bot", "var", fallback=os.path.join( path_current, "data")).strip() print("DATA {}".format(self.path_var)) if os.access(self.path_var, os.W_OK): os.chdir(self.path_var) else: print("ERROR {} is not writeable".format(self.path_var)) sys.exit(0) self.path_run = self.config.get("tiny-matrix-bot", "run", fallback=os.path.join( path_current, "sockets")).strip() if os.access(self.path_run, os.W_OK): print("SOCKETS {}".format(self.path_run)) else: print("SOCKETS {} (not writeable, disabling sockets)".format( self.path_run)) self.path_run = False self.client = MatrixClient(self.config.get("tiny-matrix-bot", "host")) self.client.login_with_password( username=self.config.get("tiny-matrix-bot", "user"), password=self.config.get("tiny-matrix-bot", "pass")) self.user = self.client.get_user(self.client.user_id) self.user.set_display_name(self.config.get("tiny-matrix-bot", "name")) for room_id in self.client.get_rooms(): self.join_room(room_id) self.client.start_listener_thread() self.client.add_invite_listener(self.on_invite) self.client.add_leave_listener(self.on_leave) while True: sleep(1) def on_signal(self, signal, frame): if signal == 1: self.scripts = self.load_scripts(self.path_lib) elif signal in [2, 15]: sys.exit(0) def load_scripts(self, path): scripts = {} for script in os.listdir(path): script_path = os.path.join(path, script) if not os.access(script_path, os.R_OK) \ or not os.access(script_path, os.X_OK): continue output = subprocess.Popen( [script_path], env={ "CONFIG": "1" }, stdout=subprocess.PIPE, universal_newlines=True).communicate()[0].strip() if not output: continue scripts[output] = script_path print("LOAD {} {}".format(script, output)) return scripts def on_invite(self, room_id, state): print("INVITE {}".format(room_id)) self.join_room(room_id) def join_room(self, room_id): room = self.client.join_room(room_id) room.add_listener(self.on_room_event) print("JOIN {}".format(room_id)) if self.path_run is not False: thread = Thread(target=self.create_socket, args=(room, )) thread.daemon = True thread.start() def create_socket(self, room): socket_name = re.search("^\!([a-z]+):", room.room_id, re.IGNORECASE).group(1) socket_path = os.path.join(self.path_run, socket_name) try: os.remove(socket_path) except OSError: pass sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.bind(socket_path) sock.listen(1) print("SOCKET {}".format(socket_path)) while True: conn, addr = sock.accept() recv = conn.recv(4096).decode('utf-8').strip() print("RECV {} {}".format(socket_path, recv)) room.send_text(recv) def on_leave(self, room_id, state): print("LEAVE {}".format(room_id)) def on_room_event(self, room, event): if event["sender"] == self.client.user_id: return if event["type"] == "m.room.message": if event["content"]["msgtype"] == "m.text": body = event["content"]["body"].strip() for regex, script in self.scripts.items(): if re.search(regex, body, re.IGNORECASE): self.run_script(room, event, [script, body]) def run_script(self, room, event, args): print("ROOM {}".format(event["room_id"])) print("SENDER {}".format(event["sender"])) print("RUN {}".format(args)) output = subprocess.Popen( args, env={ "MXROOMID": event["room_id"], "MXSENDER": event["sender"] }, stdout=subprocess.PIPE, universal_newlines=True).communicate()[0].strip() sleep(0.5) for p in output.split("\n\n"): for l in p.split("\n"): print("OUTPUT {}".format(l)) room.send_text(p) sleep(1)
def start(stdscr): global server, base_url, username, access_token, password global size, room, rooms, all_rooms, lastEventRoom, room_keys curses.curs_set(0) curses.use_default_colors() size = stdscr.getmaxyx() stdscr.addstr(0, 0, "loading...") stdscr.refresh() loadCredentials("./credentials.json") client = MatrixClient(base_url, token=access_token, user_id='@{}:{}'.format(username, server)) if access_token is None: access_token = client.login_with_password(username, password, size[0]) rooms = client.get_rooms() all_rooms = "all rooms" rooms[all_rooms] = all_rooms room_keys = list(rooms.keys()) room = all_rooms curses.halfdelay(10) maxDisplayName = 24 displayNamestartingPos = 20 PAD_COMMENTS = True pause = False client.add_listener(processMessage) client.start_listener_thread() curses.echo() stdscr.keypad(True) inputBuffer = "" lastEventRoom = all_rooms the_room_to_post_to = None # store the last room we saw before we started typing while (True): size = stdscr.getmaxyx() maxChars = size[1] - 1 - len(username) - 3 stdscr.clear() line = "redpill v0.7" line += " · screen size: " + str(size) if isinstance(rooms[room], Room): line += " · chat size: " + str(len(rooms[room].events)) line += " · room: " # we want NAME aka ALIAS[0] (ROOM_ID) # or 2nd choice: ALIAS[0] (ROOM_ID) # or fallback: ROOM_ID if room == all_rooms: line += str(room) elif rooms[room].name: if len(rooms[room].aliases) > 0 and rooms[room].aliases[0] != room: line += rooms[room].name + " aka " + getFirstRoomAlias( rooms[room]) + " (" + str(room) + ")" elif rooms[room].name != room: line += rooms[room].name + " (" + str(room) + ")" else: line += str(room) elif len(rooms[room].aliases) > 0 and rooms[room].aliases[0] != room: line += rooms[room].aliases[0] + " (" + str(line) + ")" else: line += str(room) if isinstance(rooms[room], Room) and rooms[room].topic is not None: line += " · topic: " + rooms[room].topic line += " · variables: room: " + room + ", last: " + lastEventRoom stdscr.addstr(0, 0, line, curses.A_UNDERLINE) current = len(rooms[room].events) - 1 if isinstance(rooms[room], Room) else -1 if True: y = 1 if current >= 0: # TODO: something when the first event is a typing event currentLine = size[0] - 1 # input space = "" for i in range(size[1] - 1): space += " " stdscr.addstr(currentLine, 0, space, curses.A_DIM) stdscr.addstr(currentLine, 0, "<" + username + ">", curses.A_DIM) stdscr.addstr(currentLine - 1, 0, space, curses.A_UNDERLINE) for event in reversed(rooms[room].events): log(event) log("maxDisplayName: {}, size: {}, currentLine: {}".format( maxDisplayName, size, currentLine)) if event["type"] == "m.typing": #if True: continue # do something clever elif event["type"] == "m.presence": #if True: continue # do something clever elif event["type"] == "m.roomchange": room_id = event["room_id"] #lin = (str(rooms[room_id].name) + " aka " + getFirstRoomAlias(rooms[room_id]) + " (" + # rooms[room_id].room_id + ")") line = room_id if line == all_rooms: pass elif rooms[line].name is None: if len( rooms[room_id].aliases ) > 0 and rooms[room_id].aliases[0] != room_id: line = rooms[room_id].aliases[ 0] + " (" + line + ")" else: if len( rooms[room_id].aliases ) > 0 and rooms[room_id].aliases[0] != room_id: line = rooms[ room_id].name + " aka " + getFirstRoomAlias( rooms[room_id]) + " (" + line + ")" else: if rooms[room_id].name != room_id: line = rooms[ room_id].name + " (" + line + ")" #if rooms[room].topic is not None: # line += " · topic: " + rooms[room].topic currentLine -= 1 stdscr.addstr(currentLine, 0, "Event(s) from " + line, curses.A_DIM) else: #currentLine = size[0] - y #currentLine -= 1 if currentLine < 3: # how many lines we want to reserve break #if currentLine == 5: # currentLine -= 1 y += 1 if "origin_server_ts" in event: convertedDate = datetime.datetime.fromtimestamp( int(event["origin_server_ts"] / 1000)).strftime('%Y-%m-%d %H:%M:%S') # assumption: body == normal message length = len( event["sender"]) if "sender" in event else 0 log("length: {}, currentLine: {}".format( length, currentLine)) if "body" in event["content"]: rawText = event["content"][ "body"] # .encode('utf-8') if event["content"]["msgtype"] == "m.emote": if len(rawText) > 0 and rawText[0] == " ": rawText = rawText[1:] linesNeeded = 0 buf = "" lineByLineText = [] first = True bufSinceLastWord = "" for char in rawText: if True: #for char in line: bufSinceLastWord += char if char == '\n': linesNeeded += 1 buf += bufSinceLastWord if PAD_COMMENTS or first: linesNeeded += int( (displayNamestartingPos + maxDisplayName + 3 + len(buf)) / size[1]) else: linesNeeded += int( len(buf) / size[1]) first = False lineByLineText.append(buf) buf = "" bufSinceLastWord = "" else: if ((PAD_COMMENTS and (displayNamestartingPos + maxDisplayName + 3 + len(buf + bufSinceLastWord)) == size[1] - 1) or (not PAD_COMMENTS and (len(buf + bufSinceLastWord)) == size[1] - 1)): #if (displayNamestartingPos + maxDisplayName + 3 + len(buf + bufSinceLastWord)) == size[1] - 1: if len(buf) == 0: buf += bufSinceLastWord bufSinceLastWord = "" if char.isspace(): buf += bufSinceLastWord lineByLineText.append(buf) bufSinceLastWord = "" buf = "" else: lineByLineText.append(buf) buf = bufSinceLastWord bufSinceLastWord = "" linesNeeded += 1 if char.isspace(): buf += bufSinceLastWord bufSinceLastWord = "" # if (displayNamestartingPos + maxDisplayName + 3 + len(buf + bufSinceLastWord)) == size[1] - 1: if ((PAD_COMMENTS and (displayNamestartingPos + maxDisplayName + 3 + len(buf + bufSinceLastWord)) == size[1] - 1) or (not PAD_COMMENTS and (len(buf + bufSinceLastWord)) == size[1] - 1)): buf += bufSinceLastWord bufSinceLastWord = "" lineByLineText.append(buf) linesNeeded += 1 buf = "" #elif char == ' ': # skip all whitespace # self.X += 1 buf += bufSinceLastWord lineByLineText.append(buf) linesNeeded += int( (displayNamestartingPos + maxDisplayName + 3 + len(buf)) / size[1]) buf = "" if currentLine - linesNeeded < 2: # how many lines we want to reserve break if PAD_COMMENTS: pad = displayNamestartingPos + maxDisplayName + 3 linesNeeded += 1 currentLine -= linesNeeded for i in range(linesNeeded): buf = rawText[:size[1] - pad] rawText = rawText[size[1] - pad:] if currentLine + i == size[0] - 2: stdscr.addstr( currentLine + i, displayNamestartingPos + maxDisplayName + 3, lineByLineText[i], curses.A_BOLD + curses.A_UNDERLINE) else: try: stdscr.addstr( currentLine + i, displayNamestartingPos + maxDisplayName + 3, lineByLineText[i], curses.A_BOLD) except: e = sys.exc_info()[0] print( "Error: unable to start thread. " + str(e)) stdscr.addstr(1, 0, str(e)) else: # TODO: need to split this out to get proper underline currentLine -= linesNeeded if currentLine == size[0] - 2: stdscr.addstr( currentLine, displayNamestartingPos + maxDisplayName + 3, rawText, curses.A_BOLD + curses.A_UNDERLINE) else: stdscr.addstr( currentLine, displayNamestartingPos + maxDisplayName + 3, rawText, curses.A_BOLD) usern = event["sender"] if length > maxDisplayName: usern = usern[:maxDisplayName - 3] + "..." if event["content"]["msgtype"] == "m.emote": usern = "* " + usern if currentLine == size[0] - 2: stdscr.addstr( currentLine, displayNamestartingPos + max(0, maxDisplayName - length), str(usern), curses.A_UNDERLINE + curses.A_BOLD) else: stdscr.addstr( currentLine, displayNamestartingPos + max(0, maxDisplayName - length), str(usern), curses.A_BOLD) else: usern = "<" + usern + ">" if currentLine == size[0] - 2: stdscr.addstr( currentLine, displayNamestartingPos + max(0, maxDisplayName - length), str(usern), curses.A_UNDERLINE) else: stdscr.addstr( currentLine, displayNamestartingPos + max(0, maxDisplayName - length), str(usern)) if currentLine == size[0] - 2: stdscr.addstr(currentLine, 0, convertedDate, curses.A_UNDERLINE) else: stdscr.addstr(currentLine, 0, convertedDate) #if currentLine == size[1]: # last line # stdscr.addstr( # currentLine, displayNamestartingPos + # maxDisplayName + 3, buf[:size[1] - # (displayNamestartingPos + maxDisplayName + 4)], # curses.A_BOLD # ) #else: # stdscr.addstr( # currentLine, displayNamestartingPos + # maxDisplayName + 3, buf, # curses.A_BOLD # ) # membership == join/leave events elif "membership" in event["content"]: buf = " invited someone" if event["content"]["membership"] == "invite": if "state_key" in event: buf = " invited " + event["state_key"] elif event["content"]["membership"] == "join": buf = " has joined" elif event["content"]["membership"] == "leave": buf = " has left" currentLine -= 1 if length > maxDisplayName: if currentLine == size[0] - 2: stdscr.addstr( currentLine, displayNamestartingPos + 1, str(event["sender"]), curses.A_DIM + curses.A_UNDERLINE) stdscr.addstr( currentLine, displayNamestartingPos + length + 1, buf, curses.A_DIM + curses.A_UNDERLINE) else: stdscr.addstr(currentLine, displayNamestartingPos + 1, str(event["sender"]), curses.A_DIM) stdscr.addstr( currentLine, displayNamestartingPos + length + 1, buf, curses.A_DIM) else: if currentLine == size[0] - 2: stdscr.addstr( currentLine, displayNamestartingPos + 1 + maxDisplayName - length, str(event["sender"]), curses.A_DIM + curses.A_UNDERLINE) stdscr.addstr( currentLine, displayNamestartingPos + maxDisplayName + 1, buf, curses.A_DIM + curses.A_UNDERLINE) else: stdscr.addstr( currentLine, displayNamestartingPos + 1 + maxDisplayName - length, str(event["sender"]), curses.A_DIM) stdscr.addstr( currentLine, displayNamestartingPos + maxDisplayName + 1, buf, curses.A_DIM) current -= 1 if pause: stdscr.addstr( int(size[0] / 2) - 1, int(size[1] / 2), " ", curses.A_REVERSE) stdscr.addstr(int(size[0] / 2), int(size[1] / 2), " PAUSED ", curses.A_REVERSE) stdscr.addstr( int(size[0] / 2) + 1, int(size[1] / 2), " ", curses.A_REVERSE) try: stdscr.addstr(size[0] - 1, len(username) + 3, inputBuffer[-maxChars:]) except: e = sys.exc_info()[0] print("Error: unable to start thread. " + str(e)) stdscr.addstr(1, 0, str(e)) stdscr.refresh() # getInput(stdscr) #def getInput(stdscr): # if True: try: c = stdscr.getch(size[0] - 1, len(username) + 3) #c = stdscr.getkey(size[0] - 1, len(username) + 3) #stri = stdscr.getstr(size[0] - 1, len(username) + 3, 10) if c == -1: stdscr.addstr(1, 0, "timeout") else: if c <= 256 and c != 10 and c != 9: ## enter and tab inputBuffer += chr(c) if len(inputBuffer) == 1: # e.g. just started typing if lastEventRoom != all_rooms: the_room_to_post_to = lastEventRoom if c == 9: #stdscr.addstr(1, 0, "%s was pressed\n" % c) log("key pressed: 0x{:X}".format(c)) room = room_keys[(room_keys.index(room) + 1) % len(rooms)] the_room_to_post_to = None elif c == 10: # enter with open('redpill-sends.log', 'a') as the_file: the_file.write("the_room_to_post_to:" + str(the_room_to_post_to) + "\n") the_file.write("lastEventRoom: " + str(lastEventRoom) + "\n") the_file.write("room: " + str(room) + "\n") the_file.write("inputBuffer: " + str(inputBuffer) + "\n") the_file.write("---\n") if inputBuffer.startswith("/invite"): user_id = inputBuffer[7:].strip() rooms[room].invite_user(user_id) elif inputBuffer.startswith("/kick"): user_id = inputBuffer[5:].strip() reason = "no reason..." rooms[room].kick_user(user_id, reason) elif inputBuffer.startswith("/power"): user_id = inputBuffer[7:].strip() power_level = 50 rooms[room].set_power_level(user_id, power_level) elif inputBuffer.startswith("/op"): user_id = inputBuffer[2:].strip() rooms[room].set_power_level(user_id) elif inputBuffer.startswith("/ban"): # reason user_id = inputBuffer[4:].strip() reason = "sux" #FIXME rooms[room].ban(user_id, reason) elif inputBuffer.startswith( "/join"): # there's a /join that supports aliases room_alias = inputBuffer[5:].strip() client.join_room(room_alias) elif inputBuffer.startswith("/j"): room_alias = inputBuffer[2:].strip() client.join_room(room_alias) elif inputBuffer.startswith("/leave"): rooms[room].leave_room(room_id) elif inputBuffer.startswith("/create"): # create a new room is_public = True invitees = () # def create_room(self, alias=None, is_public=False, invitees=()): room_alias = inputBuffer[7:].strip() client.create_room(room_alias, is_public, invitees) elif inputBuffer.startswith("/topic"): # get or set topic new_topic = inputBuffer[6:].strip() if len(new_topic) > 0: rooms[room].topic = new_topic else: pass #rooms[room].topic = "fail" else: if room == all_rooms: if the_room_to_post_to is None: if lastEventRoom != all_rooms: the_room_to_post_to = lastEventRoom else: stdscr.addstr(1, 0, "No idea what room to post to!") stdscr.refresh() inputBuffer = "No idea what room to post to!" continue else: the_room_to_post_to = room if inputBuffer.startswith("/me"): rooms[the_room_to_post_to].send_emote(inputBuffer[3:]) else: rooms[the_room_to_post_to].send_text(inputBuffer) inputBuffer = "" the_room_to_post_to = None elif c == curses.KEY_DC: inputBuffer = "" the_room_to_post_to = None elif c == curses.KEY_BACKSPACE: if len(inputBuffer) > 0: inputBuffer = inputBuffer[:-1] if len(inputBuffer) == 0: the_room_to_post_to = None elif c == curses.KEY_IC: pause = not (pause) if pause: curses.nocbreak() curses.cbreak() stdscr.timeout(-1) stdscr.addstr( int(size[0] / 2) - 1, int(size[1] / 2), " ", curses.A_REVERSE) stdscr.addstr(int(size[0] / 2), int(size[1] / 2), " PAUSING ", curses.A_REVERSE) stdscr.addstr( int(size[0] / 2) + 1, int(size[1] / 2), " ", curses.A_REVERSE) stdscr.refresh() else: stdscr.addstr( int(size[0] / 2) - 1, int(size[1] / 2), " ", curses.A_REVERSE) stdscr.addstr(int(size[0] / 2), int(size[1] / 2), " RESUMING ", curses.A_REVERSE) stdscr.addstr( int(size[0] / 2) + 1, int(size[1] / 2), " ", curses.A_REVERSE) stdscr.refresh() curses.halfdelay(10) stdscr.timeout(1) elif c == 27: # need to test for alt combo or ESC curses.cbreak() curses.echo() #curses.curs_set(1) stdscr.keypad(0) curses.endwin() quit() elif c == curses.KEY_F2: PAD_COMMENTS = not PAD_COMMENTS #stdscr.addstr(2, 0, "time() == %s\n" % time.time()) finally: do_nothing = True
class matrix_utils_ext(object): __MODULE_NAME__ = "Matrix Bot" __VERSION__ = "v0.0.1a" __MAX_SERVICE__ = 32 # Number of services that can be simultaneously installed. room_tuple = namedtuple("room_tuple", "room_addr listener") def __init__(self, config_path="config.json"): self.room_dic = {} self.services_sensitive_on_clock = set() self.is_timer_on = False self.is_on = False self.nb_current_service = 0 self.service_list = {} # self.logger.setLevel(logging.DEBUG) ; try: with open(config_path) as f: self.config = json.loads(f.read()) except IOError as e: print(e) self.login = self.config["login"] self.password = self.config["password"] module.config = self.config.copy() try: self.client = MatrixClient(self.config["host"]) self.client.login(self.login, self.password) except MatrixRequestError as e: print(e) sys.exit() def add_service_to_room(self, room, service, message_on_start=None): ret = False if self.nb_current_service < matrix_utils_ext.__MAX_SERVICE__: if service.add_room( room): # if room has been added to the service correctly if service in self.service_list: self.service_list[service] += 1 else: self.service_list[service] = 1 ret = True else: #raise Exception("Maximum number of services ({}) reached".format(str(matrix_utils.__MAX_SERVICE__))) ; pass return ret def remove_service_from_room(self, room, service): ret = False if service.remove_room(room): ret = True # if service is not linked to any room, remove from service_list self.service_list[service] -= 1 if self.service_list[service] == 0: del self.service_list[service] else: #raise Exception("Service {} does not exist in room {}.".format(service, room)) ; pass return ret # TODO ; eventuellement vérifier si une room avec une même adresse n'existe pas ? def add_room(self, room_addr, message_on_start=""): room = self.client.join_room(room_addr) listener = room.add_listener(self.callback) self.room_dic[room] = matrix_utils_ext.room_tuple( room_addr, listener ) # (room object address, room_name (room address), listener object) if message_on_start: # Conversion to HTML format message_on_start = message_on_start.replace("\n", "<br>") message_on_start = message_on_start.replace("\t", " ") room.send_html(message_on_start, msgtype="m.notice") return room def remove_room(self, room): if not (room in self.room_dic): return False for service in service_list.keys(): if (room in service.get_room_list()): # there are still some services linked to room. return False listener = self.room_dic[room].listener self.client.remove_listener(listener) room.leave() del self.room_dic[room] return True def callback(self, room, event): if event["type"] == "m.room.message": login = re.search("@[aA-zZ]+[0-9]*", event["sender"]).group(0) login = login[1:] tmp_log = "Event from " + bcolors.OKGREEN + self.room_dic[ room].room_addr + bcolors.ENDC + " at " + str( datetime.datetime.now()) + " by " + login print(tmp_log) text = str(event["content"]["body"]) ## Stop Service Management if text == self.config["bot_down_cmd"]: self.exit() ## Otherwise, run services for service in self.service_list.keys(): if (room in service.get_room_list()): ret = service.run(text, login, room) if ret: for msg in ret: # Conversion to HTML format msg = msg.replace("\n", "<br>") msg = msg.replace("\t", " ") room.send_html(msg, msgtype="m.notice") def spawn(self): self.client.start_listener_thread(exception_handler=self.error_handle) self.is_on = True print(bcolors.OKGREEN + "\n---------------------------------------------------\n" + "---- Matrix Bot v0.0.1 ----------------------------\n" + "---------------------------------------------------\n" + bcolors.ENDC) while (self.is_on): time.sleep(0.5) def timer_callback(self, t): while (self.is_timer_on): if self.is_on: for service in self.service_list: if service.is_clock_sensitive(): service.clock_update() ret = service.run_on_clock() if ret: # Conversion to HTML format ret = ret.replace("\n", "<br>") ret = ret.replace("\t", " ") for room in service.get_room_list(): room.send_html(ret, msgtype="m.notice") time.sleep(t) def start_timer(self, t=1): if not (self.is_timer_on): self.is_timer_on = True t1 = threading.Thread(target=self.timer_callback, args=(t, )) t1.start() def stop_timer(self): self.is_timer_on = False def exit(self): self.is_timer_on = False self.is_on = False for service in self.service_list: print("Module {} {} {} is shutting down.".format( bcolors.OKGREEN, service.module_name, bcolors.ENDC)) tmp_msg = service.exit() if tmp_msg: for room in service.get_room_list(): room.send_text(tmp_msg) # for room in self.room_dic: # room.send_text(self.config["bot_stop_txt"]); sys.exit() def error_handle(self, err): print("Server is not {} responding {} ({}). Restarting ...".format( bcolors.FAIL, bcolors.ENDC, err)) self.exit()
class matrixConnector: """ Connector to the Matrix network Example: matrixObj = MatrixConnector(url, username, password, updatePersistentData, token) url = the url to the matrix server username = the username of the matrix Server password = the password of the matrix user updatePersistentData = Callback to the function to save the learned token token = the token of this client, we will resived this at the first connection. """ # declare the variable __rooms = {} def __init__(self, url, username, password, updatePersistentData, token=None ): # save my own name matrixConnector.username = username if token is None: # Try to login with username and password self.__client = MatrixClient(url) try: self.__token = self.__client.login_with_password(username, password) # save the token value = self.__token updatePersistentData('matrix_token', value) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username or password.") sys.exit(4) else: print("Check your sever details are correct.") sys.exit(2) except MissingSchema as e: print("Bad URL format.") print(e) sys.exit(3) else: # Try to login with token try: self.__client = MatrixClient(url, token, username) print("UserId:", self.__client.user_id) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username or password.") sys.exit(4) else: print("Check your sever details are correct.") sys.exit(2) except MissingSchema as e: print("Bad URL format.") print(e) sys.exit(3) def joinRoom(self, room_id_alias): """ Join a Room room = obj.joinRoom(room_id_alias) """ roomID = room_id_alias if room_id_alias.startswith("#"): roomID = self.__client.api.get_room_id(room_id_alias) if roomID not in self.discoverJoinedRooms().keys(): return False try: room = self.__client.join_room(roomID) except MatrixRequestError as e: print(e) if e.code == 400: print("Room ID/Alias in the wrong format") sys.exit(11) else: print("Couldn't find room.") sys.exit(12) self.__rooms[roomID] = room def discoverJoinedRooms(self): # Discover what rooms the user has joined return self.__client.get_rooms() def start_listener_thread(self, onMessage): # Login to the rooms, to get the message out of the room matrixConnector.onMessage = onMessage self.__client.add_listener(self.__onEvent) self.__client.start_listener_thread() def __onEvent(room, event): # get the events out of the room message = event["content"]["body"] roomId = event["room_id"] matrixConnector.onMessage(message, roomId) def sendMessage(self, message, roomId): # Let the bot say something self.__rooms[roomId].send_html(message)
class ChatBot(object): def __init__(self, opts): jid = opts.jid password = opts.password url = opts.url room_id_alias = opts.room # # Connect to the server # ###################### self.client = MatrixClient(url) try: self.token = self.client.login_with_password(username=jid, password=password) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username or password.") sys.exit(4) else: print("Check your sever details are correct.") sys.exit(2) except MissingSchema as e: print("Bad URL format.") print(e) sys.exit(3) # # Connect to the room # #################### try: self.room = self.client.join_room(room_id_alias) except MatrixRequestError as e: print(e) if e.code == 400: print("Room ID/Alias in the wrong format") sys.exit(11) else: print("Couldn't find room.") sys.exit(12) self.room.send_text('I\'m now online.') self.opts = opts self.regexp = self.opts.regexp if self.opts.regexp else '^do:[ ]*(.*)::$' self.argsep = self.opts.argsep if self.opts.argsep else ';' self.inittalkbacktimeout = int( self.opts.inittalkbacktimeout ) if self.opts.inittalkbacktimeout else 5 self.talkbacktimeout = int( self.opts.talkbacktimeout) if self.opts.talkbacktimeout else 30 # # Listen in the room # ################## self.room.add_listener(self.command) self.client.start_listener_thread() while True: try: time.sleep(5) except Exception, e: self.room.send_text('I\'m leaving now... (' + str(e) + ")")
class CmdListener: rooms = {} mpc = None client = None stream_url = "" cmd_queue = None music_dir = None def __init__(self,config): self.mpc = MPCClient(config["mpc"]["host"],config["mpc"]["port"]) self.music_dir = config["mpc"]["music_dir"] self.cmd_queue = Queue() try: self.mpc.current() except: raise Exception("An error occured while connecting to the mpd server.") return try: self.client = MatrixClient(config["matrix"]["host"]) except: raise Exception("An error occured while connecting to the matrix server!") return self.stream_url = config["mpc"]["streamurl"] try: self.client.login_with_password(config["matrix"]["user"],config["matrix"]["pass"]) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username or password.") sys.exit(4) else: print("Check your sever details are correct.") sys.exit(3) MTX_ROOMS = config["matrix"]["rooms"].split(",") for sroom in MTX_ROOMS: room = self.client.join_room(sroom) room.add_listener(self.__on_cmd) self.rooms[room.room_id] = room def run(self): self.client.start_listener_thread() while True: event = self.cmd_queue.get() if event is None: continue; else: cmd = event['content']['body'] body = cmd.lower() if body.startswith('mpddj:') or body.startswith('!mpddj'): self.__parse_command(body[6:],event,cmd[6:]) elif body.startswith('mpd dj:'): self.__parse_command(body[7:],event,cmd[7:]) def __on_cmd(self,event): if event['type'] == "m.room.message" and event['content']['msgtype'] == "m.text": if event['age'] < 5000: self.cmd_queue.put(event) def __newfile_play(self,fname,max_attempts=25): # Do update check attempts = 0 gotfile = False while attempts < max_attempts and not gotfile: musiclist = self.mpc.listall() gotfile = fname in musiclist if not gotfile: sleep(0.5) attempts += 1 if gotfile: self.mpc.add(fname) self.mpc.play() def __parse_command(self,cmd,event,cmd_regular): cmd = cmd.strip() parts = cmd.split(" ") room = self.rooms[event['room_id']]; if parts[0] == "shuffle": self.mpc.shuffle() elif parts[0] == "prev": self.mpc.next() elif parts[0] == "play": self.mpc.play() elif parts[0] == "next": self.mpc.next() elif parts[0] == "playlist": plist = self.mpc.playlist().split("\n")[:-1][:3] if len(plist) > 0: plist[0] = "▶ " + plist[0] room.send_text("\n".join(plist).replace(".ogg","")) else: room.send_text("The playlist is empty") elif parts[0] == "current": fname = self.mpc.current() fname = fname.replace("_"," ").replace(".ogg","") room.send_text(fname) elif parts[0] == "update": self.mpc.update() elif parts[0] == "help": room.send_text("Commands are: play,prev,next,current,playlist,help,[youtube url],stream url") elif "youtube.com/" in parts[0] or "soundcloud.com/" in parts[0]: pos = 1 try: url = cmd_regular.strip().split(" ")[0] pos = len(self.mpc.playlist().split('\n'))-1 status,fname = download_youtube(url,self.music_dir,self.__newfile_play) except Exception as e: print(e) print(traceback.format_exc()) room.send_text("Couldn't download the file :(") return; self.mpc.update(True) if status: if fname is not False: fi = fname.replace(".ogg","") if pos > 0: room.send_text(fi + " has been queued. It currently at position "+str(pos+1)) else: room.send_text(fi + " has begun playing") else: if pos > 1: room.send_text("Your playlist has been queued. It currently at position "+str(pos)) else: room.send_text("Your playlist has begun playing") if self.mpc.current() == '': sleep(0.5)# Allow it to breathe self.mpc.play() else: room.send_text("I couldn't play the file. This is probably a bug and should be reported to Half-Shot.") elif "stream url" in cmd: room.send_text(self.stream_url)
room = client.join_room(room_id) botinfo = info botinfo['room_id'] = room_id botinfo['room'] = room botinfo['state'] = state botinfo['caller'] = state['events'][0]['sender'] bf[room_id] = BabelFish(botinfo) room.add_listener(handle_message) print('joined room %s' % room_id) else: print( 'Unauthorized user access in room %s. I will not join this room.' % room_id) # **** Main **** if __name__ == '__main__': client = MatrixClient(SERVER) client.login_with_password(USERNAME, PASSWORD) print('Login as %s successful' % USERNAME) client.add_invite_listener(handle_invite) for room_id, room in client.get_rooms().items(): botinfo = info botinfo['room_id'] = room_id botinfo['room'] = room bf[room_id] = BabelFish(botinfo) room.add_listener(handle_message) client.start_listener_thread() print('Listeners started successfully') while True: time.sleep(0.2)
class Glenda: def __init__(self, cfg): self.log = logging.getLogger(__name__) self.cfg = cfg self.matrix_rooms = {} self.lobby_rooms = {} self.matrix_client = None self.lobby_client = None # Called when a message is recieved from the matrix def on_room_message(self, room, event): if event['sender'] == "@{}:{}".format(self.cfg["matrix"]["username"], self.cfg["matrix"]["domain"]): # ignore messages sent by ourself return if event['type'] != "m.room.message": self.log.error("unknown event: %s" % (event)) return if not event["room_id"] in self.matrix_rooms: self.log.error("Unknown room: %s vs %s" % (event, self.matrix_rooms)) return lobby_room = self.matrix_rooms[event["room_id"]] if event['content']['msgtype'] == "m.text": user = self.matrix_client.get_user(event['sender']) user_display_name = user.get_display_name() self.lobby_client.say( lobby_room, "<{}> {}".format(user_display_name, event['content']['body'])) elif event['content']['msgtype'] == "m.emote": user = self.matrix_client.get_user(event['sender']) user_display_name = user.get_display_name() self.lobby_client.say_ex( lobby_room, "<{}> {}".format(user_display_name, event['content']['body'])) elif event['content']['msgtype'] == "m.image": user = self.matrix_client.get_user(event['sender']) user_display_name = user.get_display_name() mxc_url = event['content']['url'] o = urlparse(mxc_url) domain = o.netloc pic_code = o.path url = "https://{0}/_matrix/media/v1/download/{0}{1}".format( domain, pic_code) msg = "<{}> {}".format(user_display_name, url) self.lobby_client.say_ex(lobby_room, "<{}> {}".format(user_display_name, msg)) def setup_bridge(self, lobby_room, matrix_room): self.lobby_client.channels_to_join.append("#{}".format(lobby_room)) try: response = self.matrix_client.join_room(matrix_room) response.add_listener(self.on_room_message) self.lobby_rooms[lobby_room] = response self.matrix_rooms[response.room_id] = lobby_room except MatrixRequestError as e: if e.code == 400: self.log.error("Room ID/Alias in the wrong format") sys.exit(11) else: self.log.error("Couldn't find %s room." % (matrix_room)) sys.exit(12) async def run(self): self.lobby_client = await spring.connect( self.cfg["lobby"]["host"], port=self.cfg["lobby"]["port"]) self.matrix_client = MatrixClient(self.cfg["matrix"]["host"]) try: self.matrix_client.login(self.cfg["matrix"]["username"], self.cfg["matrix"]["pwd"], sync=True) except MatrixRequestError as e: self.log.debug(e) if e.code == 403: self.log.error("Bad username or password.") sys.exit(4) else: self.log.error("Check your sever details are correct.") sys.exit(2) except MissingSchema as e: self.log.error("Bad URL format.") self.log.error(e) sys.exit(3) for lobby_room, matrix_room in self.cfg["rooms"].items(): self.setup_bridge(lobby_room, matrix_room) self.lobby_client.login(self.cfg["lobby"]["username"], self.cfg["lobby"]["pwd"]) try: self.matrix_client.login(self.cfg["matrix"]["username"], self.cfg["matrix"]["pwd"], sync=True) except MatrixRequestError as e: if e.code == 403: self.log.error("Bad username or password.") sys.exit(4) else: self.log.error("Check your sever details are correct.") sys.exit(2) except MissingSchema as e: self.log.error("Bad URL format.") self.log.error(e) sys.exit(3) self.matrix_client.start_listener_thread()
class matrix_utils_room(object): __MODULE_NAME__ = "Matrix Bot" __VERSION__ = "v0.0.2a" __MAX_SERVICE__ = 32 ; # Number of services that can be simultaneously installed. room_tuple = namedtuple("room_tuple", "room_obj, service_set") def __init__(self, config_path = "config.json"): self.room_dic = {} ; self.services_sensitive_on_clock = set() ; self.is_timer_on = False self.is_on = False ; self.nb_current_service = 0 ; self.service_list = {}; # self.logger.setLevel(logging.DEBUG) ; try: with open(config_path) as f: self.config = json.loads(f.read()); except IOError as e: print(e) ; self.login = self.config["login"] ; self.password = self.config["password"] ; module.config = self.config.copy(); try: self.client = MatrixClient(self.config["host"]) self.client.login(self.login, self.password) ; except MatrixRequestError as e: print(e) sys.exit() ; # @lock_dec def add_service_to_room(self, room, service, message_on_start = None): ret = False; if self.nb_current_service < matrix_utils_room.__MAX_SERVICE__: room_id = room.room_id; self.room_dic[room_id].service_set.add(service); service.add_room(room); on_start = service.on_start(); if on_start: on_start = on_start.replace("\n", "<br>"); on_start = on_start.replace("\t", " "); room.send_html(on_start, msgtype="m.notice"); ret = True ; else: #raise Exception("Maximum number of services ({}) reached".format(str(matrix_utils.__MAX_SERVICE__))) ; pass; return ret; @lock_dec def remove_service_from_room(self, room, service): ret = False; room_id = room.room_id; if service in self.room_dic[room_id]: self.room_dic[room_id].service_set.remove(service); service.remove_room(room); ret = True; else: #raise Exception("Service {} does not exist in room {}.".format(service, room)) ; pass; return ret; # TODO ; eventuellement vérifier si une room redirige vers un même salon ... @lock_dec def add_room(self, room_addr, message_on_start = ""): room = self.client.join_room(room_addr) ; room.current_alias = room_addr; room_id = room.room_id; if not(room_id in self.room_dic): listener = room.add_listener(self.callback) ; self.room_dic[room.room_id] = matrix_utils_room.room_tuple(room, set()) # (room object address, room_name (room address), listener object) if message_on_start: # Conversion to HTML format message_on_start = message_on_start.replace("\n", "<br>"); message_on_start = message_on_start.replace("\t", " "); room.send_html(message_on_start, msgtype="m.notice"); else: room = None; return room ; @lock_dec def remove_room(self, room): room_id = room.room_id; if not(room_id in self.room_dic) or (self.room_dic[room_id].service_set): return False; room.leave() ; del self.room_dic[room_id]; return True; @lock_dec # ?? def callback(self, room, event): if event["type"] == "m.room.message": login = re.search("@[aA-zZ]+[0-9]*", event["sender"]).group(0) ; login = login[1:] ; room_id = room.room_id; tmp_log = "Event from " + bcolors.OKGREEN + room.current_alias + bcolors.ENDC + " at " + str(datetime.datetime.now())+ " by "+login ; print(tmp_log) if "m.relates_to" in event["content"]: print("Current Event Ignored: "+bcolors.WARNING+"/!\\ Message Edit - Ignore"+bcolors.ENDC); return None; text = str(event["content"]["body"]) ; ## Stop Service Management if text == self.config["bot_down_cmd"]: self.exit(); ## Otherwise, run services room_id = room.room_id; for service in self.room_dic[room_id].service_set: ret = service.run(text, login, room) ; if ret: for msg in ret: # Conversion to HTML format msg = msg.replace("\n", "<br>"); msg = msg.replace("\t", " "); room.send_html(msg, msgtype="m.notice"); def spawn(self): self.client.start_listener_thread(exception_handler=self.error_handle); self.is_on = True ; print(bcolors.OKGREEN+ "\n---------------------------------------------------\n"+ "---- Matrix Bot v0.0.2 ----------------------------\n"+ "---------------------------------------------------\n"+ bcolors.ENDC) while(self.is_on): time.sleep(1) def timer_callback(self, t): format_to_html = lambda s : s.replace("\n", "<br>").replace("\t", " "); while(self.is_timer_on): time.sleep(t) for room_id in self.room_dic: room = self.room_dic[room_id].room_obj; for service in self.room_dic[room_id].service_set: if service.is_clock_sensitive(): service.clock_update() ; ret = service.run_on_clock(room) ; if ret: # should return a string ret = ret.replace("\n", "<br>"); ret = ret.replace("\t", " "); room.send_html(ret, msgtype="m.notice"); def start_timer(self, t = 1): if not(self.is_timer_on): self.is_timer_on = True ; t1 = threading.Thread(target=self.timer_callback, args=(t,)) t1.start(); def stop_timer(self): self.is_timer_on = False ; @lock_dec def exit(self): self.is_timer_on = False ; self.is_on = False ; service_ret_buffer = {} for room_id in self.room_dic: room = self.room_dic[room_id].room_obj; service_set = self.room_dic[room_id].service_set; for service in service_set: if not(service in service_ret_buffer): print("Module {} {} {} is shutting down.".format(bcolors.OKGREEN, service.module_name, bcolors.ENDC)) ; ret = service.exit(); service_ret_buffer[service] = ret; else: ret = service_ret_buffer[service]; if ret: room.send_html(ret, msgtype="m.notice"); # for room in self.room_dic: # room.send_text(self.config["bot_stop_txt"]); sys.exit() ; def error_handle(self, err): print("Server is not {} responding {} ({}). Restarting ...".format(bcolors.FAIL, bcolors.ENDC, err)); self.exit();
def main(): # type: () -> None signal.signal(signal.SIGINT, die) logging.basicConfig(level=logging.WARNING) parser = generate_parser() options = parser.parse_args() if options.sample_config: try: write_sample_config(options.sample_config) except Bridge_ConfigException as exception: sys.exit(exception) print("Wrote sample configuration to '{}'".format(options.sample_config)) sys.exit(0) elif not options.config: print("Options required: -c or --config to run, OR --write-sample-config.") parser.print_usage() sys.exit(1) try: config = read_configuration(options.config) except Bridge_ConfigException as exception: sys.exit("Could not parse config file: {}".format(exception)) # Get config for each client zulip_config = config["zulip"] matrix_config = config["matrix"] # Initiate clients backoff = zulip.RandomExponentialBackoff(timeout_success_equivalent=300) while backoff.keep_going(): print("Starting matrix mirroring bot") try: zulip_client = zulip.Client(email=zulip_config["email"], api_key=zulip_config["api_key"], site=zulip_config["site"]) matrix_client = MatrixClient(matrix_config["host"]) # Login to Matrix matrix_login(matrix_client, matrix_config) # Join a room in Matrix room = matrix_join_room(matrix_client, matrix_config) room.add_listener(matrix_to_zulip(zulip_client, zulip_config, matrix_config, options.no_noise)) print("Starting listener thread on Matrix client") matrix_client.start_listener_thread() print("Starting message handler on Zulip client") zulip_client.call_on_each_message(zulip_to_matrix(zulip_config, room)) except Bridge_FatalMatrixException as exception: sys.exit("Matrix bridge error: {}".format(exception)) except Bridge_ZulipFatalException as exception: sys.exit("Zulip bridge error: {}".format(exception)) except zulip.ZulipError as exception: sys.exit("Zulip error: {}".format(exception)) except Exception as e: traceback.print_exc() backoff.fail()
class MatrixBotAPI: # username - Matrix username # password - Matrix password # server - Matrix server url : port # rooms - List of rooms ids to operate in, or None to accept all rooms def __init__(self, username, password, server, rooms=None): self.username = username # Authenticate with given credentials self.client = MatrixClient(server) try: self.client.login_with_password(username, password) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username/password") except Exception as e: print("Invalid server URL") traceback.print_exc() # Store allowed rooms self.rooms = rooms # Store empty list of handlers self.handlers = [] # If rooms is None, we should listen for invites and automatically accept them if rooms is None: self.client.add_invite_listener(self.handle_invite) self.rooms = [] # Add all rooms we're currently in to self.rooms and add their callbacks for room_id, room in self.client.get_rooms().items(): room.add_listener(self.handle_message) self.rooms.append(room_id) else: # Add the message callback for all specified rooms for room in self.rooms: room.add_listener(self.handle_message) def add_handler(self, handler): self.handlers.append(handler) def handle_message(self, room, event): # Make sure we didn't send this message if re.match("@" + self.username, event['sender']): return # Loop through all installed handlers and see if they need to be called for handler in self.handlers: if handler.test_callback(room, event): # This handler needs to be called handler.handle_callback(room, event) def handle_invite(self, room_id, state): print("Got invite to room: " + str(room_id)) print("Joining...") room = self.client.join_room(room_id) # Add message callback for this room room.add_listener(self.handle_message) # Add room to list self.rooms.append(room) def start_polling(self): # Starts polling for messages self.client.start_listener_thread()
matrix_config = config["matrix"] # Initiate clients backoff = wyzepal.RandomExponentialBackoff(timeout_success_equivalent=300) while backoff.keep_going(): print("Starting matrix mirroring bot") try: wyzepal_client = wyzepal.Client(email=wyzepal_config["email"], api_key=wyzepal_config["api_key"], site=wyzepal_config["site"]) matrix_client = MatrixClient(matrix_config["host"]) # Login to Matrix matrix_login(matrix_client, matrix_config) # Join a room in Matrix room = matrix_join_room(matrix_client, matrix_config) room.add_listener( matrix_to_wyzepal(wyzepal_client, wyzepal_config, matrix_config)) print("Starting listener thread on Matrix client") matrix_client.start_listener_thread() print("Starting message handler on WyzePal client") wyzepal_client.call_on_each_message( wyzepal_to_matrix(wyzepal_config, room)) except Exception: traceback.print_exc() backoff.fail()
class MatrixBotAPI: # username - Matrix username # password - Matrix password # server - Matrix server url : port # rooms - List of rooms ids to operate in, or None to accept all rooms # accept_invites - If true, accept invites to rooms def __init__(self, username, password, server, rooms=None, accept_invites=True): self.username = username # Authenticate with given credentials self.client = MatrixClient(server) try: self.client.login(username, password) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username/password") except Exception as e: print(e) # Store allowed rooms. If rooms is None, store empty list # of rooms and add all rooms we're currently in if rooms: self.rooms = rooms else: self.rooms = [] for room in self.client.rooms.values(): self.rooms.append(room) print('Total rooms: ', len(self.rooms)) # Store empty list of handlers self.handlers = [] # we should listen for invites and automatically accept them if accept_invites: self.client.add_invite_listener(self.handle_invite) # Add handlers for all rooms for room in self.rooms: room.add_listener(self.handle_message) def add_handler(self, handler): self.handlers.append(handler) def handle_message(self, room, event): # Make sure we didn't send this message if re.match("@" + self.username, event['sender']): return # Loop through all installed handlers and see if they need to be called for handler in self.handlers: if handler.test_callback(event): # This handler needs to be called try: handler.handle_callback(room, event) except: traceback.print_exc() def handle_invite(self, room_id, state): print("Got invite to room: " + str(room_id)) print("Joining...") room = self.client.join_room(room_id) # Add message callback for this room room.add_listener(self.handle_message) # Add room to list self.rooms.append(room) def send_message(self, message, room_id=None, room_alias=None): if not room_id: if not room_alias: # send to all rooms if no room specified for room in self.rooms: room.send_text(message) return True else: # no ID but we have alias, so get room_id from it room_id = self.client.api.get_room_id(room_alias) room = self.client.rooms.get(room_id) if room and room in self.rooms: room.send_text(message) return True return False def start_polling(self): # Starts polling for messages self.client.start_listener_thread() return self.client.sync_thread
chat off - disables all server logs spg:command - run spigot command, no feedback SPG:command - run spigot command, auto enables all logs on webcam - sends current webcam image generatemap - generates latest map minemap - sends current minecraft map image address - sends currect IP addresses help - that is me :) Bye master!""" send_to_matrix("Yes master, {0} !".format(msg[1:])) else: #print(event['type']) pass room.add_listener(on_message) client.start_listener_thread() room.send_notice("I am ready my master!") print "I am ready" server_screen=screenutils.Screen('server') def follow(thefile,curinode,name): thefile.seek(0,2) while True: line = thefile.readline() if not line: time.sleep(0.1) try: if os.stat(name).st_ino != curinode: new = open(name, "r") thefile.close()
def main(): global client global data global log global lock lock = threading.RLock() log.debug("try lock before main load_data()") with lock: log.debug("success lock before main load_data()") data=load_data() log.debug("release lock() after access global data") log.info("try init matrix-client") client = MatrixClient(conf.server) log.info("success init matrix-client") try: log.info("try login matrix-client") token = client.login(username=conf.username, password=conf.password,device_id=conf.device_id) log.info("success login matrix-client") except MatrixRequestError as e: print(e) log.debug(e) if e.code == 403: log.error("Bad username or password.") sys.exit(4) else: log.error("Check your sever details are correct.") sys.exit(2) except MissingSchema as e: log.error("Bad URL format.") print(e) log.debug(e) sys.exit(3) log.info("try init listeners") client.add_listener(on_message) client.add_ephemeral_listener(on_event) client.add_invite_listener(on_invite) client.start_listener_thread(exception_handler=exception_handler) log.info("success init listeners") try: x=0 log.info("enter main loop") while True: if conf.type_translate == "yandex_long": # check yandex_long_jobs: num_jobs=0 for room_id in data["rooms"]: if "jobs" in data["rooms"][room_id]: for job in data["rooms"][room_id]["jobs"]: ret_value=False num_jobs+=1 ret_value=check_long_yandex_job(log,room_id,data["rooms"][room_id]["jobs"],job) if ret_value==False: log.error("check_long_yandex_job(), room_id=%s, job_id=%s"%(room_id,job["id"])) result_string="error get result from yandex speech api - yandex api error" log.error(result_string) if send_notice(room_id,result_string)==False: log.error("send_notice(%s)"%room_id) if num_jobs > 0: log.debug("len jobs list for all rooms = %d"%num_jobs) time.sleep(3) except Exception as e: log.error(get_exception_traceback_descr(e)) log.error("exception at main loop check jobs: %s"%e) sys.exit(1) log.info("exit main loop")
class TinyMatrixtBot(): def __init__(self, hostname, username, password, displayname): signal.signal(signal.SIGTERM, self.on_signal) signal.signal(signal.SIGHUP, self.on_signal) self.current_path = os.path.dirname(os.path.realpath(__file__)) self.scripts_path = os.path.join(self.current_path, "scripts") self.sockets_path = os.path.join(self.current_path, "sockets") if not os.access(self.sockets_path, os.W_OK): self.sockets_path = None os.chdir(self.scripts_path) self.scripts = self.load_scripts(self.scripts_path) self.client = MatrixClient(hostname) self.client.login_with_password(username=username, password=password) self.user = self.client.get_user(self.client.user_id) self.user.set_display_name(displayname) for room_id in self.client.get_rooms(): self.join_room(room_id) self.client.start_listener_thread() self.client.add_invite_listener(self.on_invite) self.client.add_leave_listener(self.on_leave) while True: sleep(10) def on_signal(self, signal, frame): if signal == 1: self.scripts = self.load_scripts(self.scripts_path) if signal == 15: sys.exit() def load_scripts(self, path): scripts = {} for script in os.listdir(path): script = os.path.join(path, script) if not stat.S_IXUSR & os.stat(script)[stat.ST_MODE]: continue output = subprocess.Popen( [script], env={ "CONFIG": "1" }, stdout=subprocess.PIPE, universal_newlines=True).communicate()[0].strip() if not output: continue scripts[output] = script print("script {} {}".format(output, script)) return scripts def on_invite(self, room_id, state): print("invite {}".format(room_id)) self.join_room(room_id) def join_room(self, room_id): room = self.client.join_room(room_id) room.add_listener(self.on_room_event) print("join {}".format(room_id)) if self.sockets_path is not None: thread = Thread(target=self.create_socket, args=(room, )) thread.daemon = True thread.start() def create_socket(self, room): socket_name = re.search("^\!([a-z]+):", room.room_id, re.IGNORECASE).group(1) socket_path = os.path.join(self.sockets_path, socket_name) try: os.remove(socket_path) except OSError: pass sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.bind(socket_path) sock.listen(1) print("bind {}".format(socket_path)) while True: conn, addr = sock.accept() recv = conn.recv(4096).decode('utf-8').strip() print("recv {} {}".format(socket_path, recv)) room.send_text(recv) def on_leave(self, room_id, state): print("leave {}".format(room_id)) def on_room_event(self, room, event): if event["sender"] == self.client.user_id: return if event["type"] == "m.room.message": if event["content"]["msgtype"] == "m.text": body = event["content"]["body"].strip() for regex, script in self.scripts.items(): if re.search(regex, body, re.IGNORECASE): self.run_script(room, script, body) def run_script(self, room, script, args): print("run {} {}".format(script, args)) output = subprocess.Popen( [script, args], stdout=subprocess.PIPE, universal_newlines=True).communicate()[0].strip() for line in output.split("\n\n"): sleep(1) print(line) room.send_text(line)
class MatrixEngine(object): chatapi = '' chatclient = '' chattoken = '' listener_thread_id = 0 new_msg_queue = [] new_msg_senders = [] def __init__(self, host=bothost, user=botuser, pwd=botpwd, room=botroom): print("Logging to matrix server...") self.chatclient = MatrixClient(host) try: self.chattoken = self.chatclient.login(username=user, password=pwd, sync=True) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username or password.") sys.exit(4) else: print("Check your sever details are correct.") sys.exit(2) except MissingSchema as e: print("Bad URL format.") print(e) sys.exit(3) self.chatapi = MatrixHttpApi(host, self.chattoken) print("Login OK") ### handle messages print("Setting up listener") try: room = self.chatclient.join_room(room) except MatrixRequestError as e: print(e) if e.code == 400: print("Room ID/Alias in the wrong format") sys.exit(11) else: print("Couldn't find room.") sys.exit(12) room.add_listener(self.on_message) self.listener_thread_id = self.chatclient.start_listener_thread() print('Listener set.. OK!') def sendmsg(self, msg, chatid=botroom): room = self.chatclient.join_room(chatid) response = self.chatapi.send_message(chatid, msg) def sendhtml(self, msg, chatid=botroom): room = self.chatclient.join_room(chatid) room.send_html(msg) def create_room(self, alias): ex_room = matrix_mongo.matrix_chats.find_one({"alias": alias}) if ex_room: room_id = ex_room['room_id'] room = self.chatclient.join_room(room_id) room.invite_user(ebuser) else: try: aldt = datetime.datetime.strftime(datetime.datetime.now(), '%Y%m%d%H%M%S') new_room = self.chatclient.create_room(alias=alias + "_" + aldt, is_public=False, invitees=[ebuser]) room_id = new_room.room_id dtime = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S') chatdata = { "created_date": dtime, "alias": alias, "room_alias": alias + "_" + aldt, "room_id": room_id, "room_data": room } recordID = matrix_mongo.matrix_chats.insert_one(chatdata) except MatrixRequestError as e: print(str(e)) return room_id def sendfile(self, filename, chatid=botroom): room = self.chatclient.join_room(chatid) with open(filename, 'rb') as datafile: data = datafile.read() uri = self.chatclient.upload(content=data, content_type='image/jpg') print(uri) filedate = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S') room.send_image( uri, filedate + '_' + filename.replace('/', '_') + '.jpg') def on_message(self, room, event): try: # Called when a message is recieved. if event['type'] == "m.room.member": if event['membership'] == "join": # print("{0} joined".format(event['content']['displayname'])) dtime = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S') print(dtime + " new user joined to the room ") elif event['type'] == "m.room.message": if event['content']['msgtype'] == "m.text": # print("{0}: {1}".format(event['sender'], event['content']['body'])) dtime = datetime.datetime.strftime(datetime.datetime.now(), '%Y-%m-%d %H:%M:%S') print(dtime + "| '" + event['content']['body'] + "' msg received from " + event['sender']) if event['sender'] != "@mybot:matrix.org": self.new_msg_senders.append(event['sender']) self.new_msg_queue.append(event['content']['body']) else: print('new event in room:' + event['type']) except Exception as e: print('error @ on_message: ' + str(e)) def parsemsg(self): for i in range(len(self.new_msg_queue)): origMsg = self.new_msg_queue[i] sender = self.new_msg_senders[i] msg = origMsg.lower() print("PARSER: '" + origMsg + "' msg received from " + sender) def doshit(): print('shit done') if msg == "test": doshit() elif msg == "msg": sendmsg("your msg responded!") elif msg == "html": sendhtml("your <b>html</b> message <h1>responded</h1>!") else: print("message not understood") self.new_msg_queue.remove(origMsg) self.new_msg_senders.remove(sender)
def main() -> None: signal.signal(signal.SIGINT, die) logging.basicConfig(level=logging.WARNING) parser = generate_parser() options = parser.parse_args() if options.sample_config: try: write_sample_config(options.sample_config, options.zuliprc) except Bridge_ConfigException as exception: print(f"Could not write sample config: {exception}") sys.exit(1) if options.zuliprc is None: print(f"Wrote sample configuration to '{options.sample_config}'") else: print("Wrote sample configuration to '{}' using zuliprc file '{}'". format(options.sample_config, options.zuliprc)) sys.exit(0) elif not options.config: print( "Options required: -c or --config to run, OR --write-sample-config." ) parser.print_usage() sys.exit(1) try: config = read_configuration(options.config) except Bridge_ConfigException as exception: print(f"Could not parse config file: {exception}") sys.exit(1) # Get config for each client zulip_config = config["zulip"] matrix_config = config["matrix"] # Initiate clients backoff = zulip.RandomExponentialBackoff(timeout_success_equivalent=300) while backoff.keep_going(): print("Starting matrix mirroring bot") try: zulip_client = zulip.Client( email=zulip_config["email"], api_key=zulip_config["api_key"], site=zulip_config["site"], ) matrix_client = MatrixClient(matrix_config["host"]) # Login to Matrix matrix_login(matrix_client, matrix_config) # Join a room in Matrix room = matrix_join_room(matrix_client, matrix_config) room.add_listener( matrix_to_zulip(zulip_client, zulip_config, matrix_config, options.no_noise)) print("Starting listener thread on Matrix client") matrix_client.start_listener_thread() print("Starting message handler on Zulip client") zulip_client.call_on_each_message( zulip_to_matrix(zulip_config, room)) except Bridge_FatalMatrixException as exception: sys.exit(f"Matrix bridge error: {exception}") except Bridge_ZulipFatalException as exception: sys.exit(f"Zulip bridge error: {exception}") except zulip.ZulipError as exception: sys.exit(f"Zulip error: {exception}") except Exception: traceback.print_exc() backoff.fail()
class Client: """ A Matrix client implementation. :param server_url: The Matrix server URL :type server_url: str :param UI: The user interface object :type UI: :class:`.ui.base.BaseUI` """ def __init__(self, server_url, UI): assert server_url, "Missing server URL" self.room = None self.client = MatrixClient(server_url) self.op_executor = OPExecutor(self._server_exception_handler) self.room_event_observer = RoomEventObserver(self) self.users = Users(self.client.api) commands = {} for name in dir(self): attr = getattr(self, name) if isinstance(attr, command.Command): commands[attr.cmd_type] = attr self.ui = UI(self.send_message, self.users, commands) self.users.set_modified_callback(self.ui.refresh_user_list) @property def connected(self): return self.client and self.client.should_listen def register(self, username, password): """ Register a new user on the server. :param username: The username to register :type username: str :param password: The password to register :type password: str :raises RegistrationException: """ assert username, "Missing username" assert password, "Missing password" LOG.info("Registering user {}".format(username)) try: self.client.register_with_password(username=username, password=password) except MatrixRequestError as exc: LOG.exception(exc) try: content = json.loads(exc.content) except json.decoder.JSONDecodeError as json_exc: raise exceptions.RegistrationException(str(json_exc)) try: if content["errcode"] in ("M_USER_IN_USE", "M_EXCLUSIVE"): raise exceptions.UsernameTaken(username) if content["errcode"] == "M_INVALID_USERNAME": raise exceptions.RegistrationException(content["error"]) if content["errcode"] == "M_UNKNOWN": if content["error"] == "Captcha is required.": raise exceptions.CaptchaRequired() except KeyError: pass raise exceptions.RegistrationUnknownError(exc) def login(self, username, password): """ Login to the server. If the login fails we try to register a new user using the same username and password. :param username: The username to login with :type username: str :param password: The password to login with :type password: str :raises LoginException: """ assert username, "Missing username" assert password, "Missing password" LOG.info("Login with username {}".format(username)) try: self.client.login_with_password(username=username, password=password) except MatrixRequestError as exc: LOG.exception(exc) try: content = json.loads(exc.content) except json.decoder.JSONDecodeError as json_exc: raise exceptions.LoginException(str(json_exc)) try: if content["errcode"] == "M_FORBIDDEN": raise exceptions.LoginFailed() except KeyError: pass raise exceptions.LoginUnknownError(exc) def create_room(self, room_alias): """ Create a new room on the server. :param room_alias: The alias of the room to create :type room_alias: str """ assert room_alias, "Missing room" LOG.info("Creating room {}".format(room_alias)) """ #room:host -> room """ room_alias_name = room_alias[1:].split(':')[0] self.room = self.client.create_room(room_alias_name) def join(self, room_alias): """ Join a room. If the room does not already exist on the server we try to automatically create it. :param room_alias: The alias of the room to join :type room_alias: str :raises JoinRoomException: """ assert room_alias, "Missing room" LOG.info("Joining room {}".format(room_alias)) try: self.room = self.client.join_room(room_alias) except MatrixRequestError as exc: LOG.exception(exc) try: content = json.loads(exc.content) except json.decoder.JSONDecodeError as json_exc: exceptions.JoinRoomException(json_exc) try: if content["errcode"] == "M_NOT_FOUND": raise exceptions.RoomNotFound() if content["errcode"] in ("M_FORBIDDEN", "M_UNKNOWN"): raise exceptions.JoinRoomException(content["error"]) except (KeyError, AttributeError): pass raise exceptions.JoinRoomUnknownError(exc) def run(self): """ Run the client. """ assert self.room, "You need to join a room before you run the client" self.room.add_listener(self.room_event_observer.on_room_event) self.op_executor.start() self.connect() self.ui.run() def stop(self): """ Stop the client. """ self.ui.stop() self.op_executor.stop() if self.connected: print("Waiting for server connection to close") print("Press ctrl+c to force stop") try: self.disconnect() except KeyboardInterrupt: pass def _server_exception_handler(self, exc): """ Exception handler for Matrix server errors. """ LOG.exception(exc) if isinstance(exc, ConnectionError): self.ui.draw_client_info("Server connection error") else: self.ui.draw_client_info("Unexpected server error: {}".format(exc)) if not settings.debug: self.ui.draw_client_info( "For more details enable debug mode, " "reproduce the issue and check the logs. " "Debug mode is enabled by setting the " "MATRIX_DEBUG environment variable") self.disconnect() def _populate_room(self): # Clear the users model from old user data. To avoid duplicates when # for example reconnecting self.users.clear() # Temporarily disable UI user list refresh callback when doing the # initial population of the users model. Especially important for large # rooms which would cause an excessive amount of re-draws. modified_callback = self.users.modified_callback self.users.set_modified_callback(lambda: None) users = self.room.get_joined_members().items() for user_id, user in users: self.users.add_user(user_id, nick=user["displayname"]) # Restore refresh callback self.users.set_modified_callback(modified_callback) # Execute an initial refresh modified_callback() @command.cmd(command.CONNECT, help_msg="Reconnect to the server") @op(require_connection=False) def connect(self): """ Connect to the server. Before the client starts syncing events with the server it retrieves the users currently in the room and backfills previous messages. The number of previous messages backfilled are decidede by :const:`HISTORY_LIMIT`. """ if self.connected: self.ui.draw_client_info("Already connected") return # Retrieve users currently in the room self._populate_room() # Get message history self.room.backfill_previous_messages(limit=HISTORY_LIMIT) self.client.start_listener_thread( timeout_ms=SERVER_TIMEOUT_MS, exception_handler=self._server_exception_handler) self.ui.draw_client_info("Connected to server") def disconnect(self): """ Disconnect from the server. This also causes the user to logout when the sync thread has closed. """ if self.connected: # Can't unfortunately cancel an in-flight request # https://github.com/kennethreitz/requests/issues/1353 self.client.should_listen = False self.ui.draw_client_info("Disconnected") # Wait here for the matrix client sync thread to exit before # joining so that we can interrupt using signals. while self.client.sync_thread.isAlive(): time.sleep(0.1) try: self.client.api.logout() except ConnectionError: pass @op def send_message(self, msg): """ Send a message to the room. :param msg: Message to send :type msg: str """ self.room.send_text(msg) @command.cmd(command.INVITE, help_msg=("Invite a user to the room " "(user_id syntax: @[mxid]:[server])")) @op def invite(self, user_id): """ Invite a user to the room. :param user_id: The MXID of the user you want to invite :type user_id: str """ try: self.client.api.invite_user(self.room.room_id, user_id) # self.room.invite_user(args[0]) except MatrixRequestError as exc: try: error_msg = json.loads(exc.content)["error"] except: error_msg = str(exc) self.ui.draw_client_info("Invite error: {}".format(error_msg)) @command.cmd(command.CHANGE_NICK, help_msg="Change nick") @op def change_nick(self, nick): """ Change your nick. :param nick: The displayname you want to change to. :type nick: str """ self.users.get_user(self.client.user_id).set_display_name(nick) @command.cmd(command.LEAVE, help_msg="Leave the room") def leave(self): """ Leave the room. """ # Stop listening for new events, when we leave the room we're forbidden # from interacting with the it anything anyway self.client.should_listen = False self.room.leave() self.ui.stop() @command.cmd(command.QUIT, help_msg="Exit the client") def quit(self): """ Exit the client. """ self.ui.stop() @command.cmd(command.HELP, help_msg="Show this") def show_help(self, cmd_type=None): """ Show a help message explaining all the available commands. :param cmd_type: Use cmd_type to display the help message of a specific command rather than all of them. :type cmd_type: int """ self.ui.draw_help(cmd_type)
class MatrixProtocol(Protocol): # List of occupants in each room # Used to avoid having to re-request the list of members each time room_occupants = {} # Keep track of when we joined rooms this session # Used to filter out historical messages after accepting room invites join_timestamps = {} # called on bot init; the following are already created by __init__: # self.bot = SibylBot instance # self.log = the logger you should use def setup(self): self.rooms = {} self.bot.add_var("credentials",persist=True) # Incoming message queue - messageHandler puts messages in here and # process() looks here periodically to send them to sibyl self.msg_queue = Queue() # Create a client in setup() because we might use self.client before # connect() is called homeserver = self.opt('matrix.server') self.client = MatrixClient(homeserver) # @raise (ConnectFailure) if can't connect to server # @raise (AuthFailure) if failed to authenticate to server def connect(self): homeserver = self.opt('matrix.server') user = self.opt('matrix.username') pw = self.opt('matrix.password') self.log.debug("Connecting to %s" % homeserver) try: self.log.debug("Logging in as %s" % user) # Log in with the existing access token if we already have a token if(self.bot.credentials and self.bot.credentials[0] == user): self.client = MatrixClient(homeserver, user_id=user, token=self.bot.credentials[1]) # Otherwise, log in with the configured username and password else: token = self.client.login_with_password(user,pw) self.bot.credentials = (user, token) self.rooms = self.client.get_rooms() self.log.debug("Already in rooms: %s" % self.rooms) # Connect to Sibyl's message callback self.client.add_listener(self.messageHandler) self.client.add_invite_listener(self.inviteHandler) self.log.debug("Starting Matrix listener thread") self.client.start_listener_thread(exception_handler=self._matrix_exception_handler) except MatrixRequestError as e: if(e.code in [401, 403]): self.log.debug("Credentials incorrect! Maybe your access token is outdated?") raise self.AuthFailure else: if(self.opt('matrix.debug')): tb = traceback.format_exc() self.log.debug(tb) self.log.debug("Failed to connect to homeserver!") raise self.ConnectFailure except MatrixHttpLibError as e: self.log.error("Failed to connect to homeserver!") self.log.debug("Received error:" + str(e)) raise self.ConnectFailure def _matrix_exception_handler(self, e): self.msg_queue.put(e) # receive/process messages and call bot._cb_message() # must ignore msgs from myself and from users not in any of our rooms # @call bot._cb_message(Message) upon receiving a valid status or message # @raise (PingTimeout) if implemented # @raise (ConnectFailure) if disconnected # @raise (ServerShutdown) if server shutdown def process(self): while(not self.msg_queue.empty()): next = self.msg_queue.get() if(isinstance(next, Message)): self.log.debug("Placing message into queue: " + next.get_text()) self.bot._cb_message(next) elif(isinstance(next, MatrixHttpLibError)): self.log.debug("Received error from Matrix SDK, stopping listener thread: " + str(next)) self.client.stop_listener_thread() raise self.ConnectFailure("Connection error returned by requests library: " + str(next)) def messageHandler(self, msg): if(self.opt('matrix.debug')): self.log.debug(str(msg)) try: # Create a new Message to send to Sibyl u = self.new_user(msg['sender'], Message.GROUP) r = self.new_room(msg['room_id']) if(r in self.join_timestamps and datetime.datetime.fromtimestamp(msg['origin_server_ts']/1000, pytz.utc) < self.join_timestamps[r]): self.log.info('Message received in {} from before room join, ignoring'.format(msg['room_id'])) return None if('msgtype' in msg['content']): msgtype = msg['content']['msgtype'] if(msgtype == 'm.text'): m = Message(u, msg['content']['body'], room=r, typ=Message.GROUP) self.log.debug('Handling m.text: ' + msg['content']['body']) self.msg_queue.put(m) elif(msgtype == 'm.emote'): m = Message(u, msg['content']['body'], room=r, typ=Message.GROUP, emote=True) self.log.debug('Handling m.emote: ' + msg['content']['body']) self.msg_queue.put(m) elif(msgtype == 'm.image' or msgtype == 'm.audio' or msgtype == 'm.file' or msgtype == 'm.video'): media_url = urlparse(msg['content']['url']) http_url = self.client.api.base_url + "/_matrix/media/r0/download/{0}{1}".format(media_url.netloc, media_url.path) if(msgtype == 'm.image'): body = "{0} uploaded {1}: {2}".format(msg['sender'], msg['content'].get('body', 'an image'), http_url) elif(msgtype == 'm.audio'): body = "{0} uploaded {1}: {2}".format(msg['sender'], msg['content'].get('body', 'an audio file'), http_url) elif(msgtype == 'm.video'): body = "{0} uploaded {1}: {2}".format(msg['sender'], msg['content'].get('body', 'a video file'), http_url) elif(msgtype == 'm.file'): body = "{0} uploaded {1}: {2}".format(msg['sender'], msg['content'].get('body', 'a file'), http_url) m = Message(u, body, room=r, typ=Message.GROUP) self.log.debug("Handling " + msgtype + ": " + body) self.msg_queue.put(m) elif(msgtype == 'm.location'): body = "{0} sent a location: {1}".format(msg['sender'], msg['content']['geo_uri']) m = Message(u, body, room=r, typ=Message.GROUP) self.log.debug('Handling m.location: ' + body) self.msg_queue.put(m) else: self.log.debug('Not handling message, unknown msgtype') elif('membership' in msg): if(msg['membership'] == 'join'): self.room_occupants[r].add(self.new_user(msg['state_key'], Message.GROUP)) elif(msg['membership'] == 'leave'): self.room_occupants[r].remove(self.new_user(msg['state_key'], Message.GROUP)) except KeyError as e: self.log.debug("Incoming message did not have all required fields: " + e.message) def inviteHandler(self, room_id, state): join_on_invite = self.opt('matrix.join_on_invite') invite_events = [x for x in state['events'] if x['type'] == 'm.room.member' and x['state_key'] == str(self.get_user()) and x['content']['membership'] == 'invite'] if(len(invite_events) != 1): raise KeyError("Something's up, found more than one invite state event for " + room_id) inviter = invite_events[0]['sender'] inviter_domain = inviter.split(':')[1] my_domain = str(self.get_user()).split(':')[1] if(join_on_invite == 'accept' or (join_on_invite == 'domain' and inviter_domain == my_domain)): self.log.debug('Joining {} on invite from {}'.format(room_id, inviter)) self.join_room(MatrixRoom(self, room_id)) elif(join_on_invite == 'domain' and inviter_domain != my_domain): self.log.debug("Received invite for {} but inviter {} is on a different homeserver").format(room_id, inviter) else: self.log.debug("Received invite for {} from {} but join_on_invite is disabled".format(room_id, inviter)) # called when the bot is exiting for whatever reason # NOTE: sibylbot will already call part_room() on every room in get_rooms() def shutdown(self): pass # send a message to a user # @param mess (Message) message to be sent # @raise (ConnectFailure) if failed to send message # Check: get_emote() def send(self,mess): (text,to) = (mess.get_text(),mess.get_to()) try: if(mess.get_emote()): to.room.send_emote(text) else: to.room.send_text(text) except MatrixError as e: raise self.ConnectFailure # send a message with text to every user in a room # optionally note that the broadcast was requested by a specific User # @param mess (Message) the message to broadcast # @return (str,unicode) the text that was actually sent # Check: get_user(), get_users() def broadcast(self,mess): """send a message to every user in a room""" (text,room,frm) = (mess.get_text(),mess.get_to(),mess.get_user()) users = self.get_occupants(room)+(mess.get_users() or []) # Matrix has no built-in broadcast, so we'll just highlight everyone s = 'all: %s --- ' % text if frm: self.log.debug('Broadcast message from: ' + str(frm)) s += frm.get_name()+' --- ' me = self.get_user() names = [u.get_name() for u in users if (u!=me and (not frm or u!=frm))] s += ', '.join(set(names)) self.send(Message(self.get_user(),s,to=room)) return s # join the specified room using the specified nick and password # @param room (Room) the room to join # @call bot._cb_join_room_success(room) on successful join # @call bot._cb_join_room_failure(room,error) on failed join def join_room(self,room): try: res = self.client.join_room(room.room.room_id) self.bot._cb_join_room_success(room) self.join_timestamps[room] = datetime.datetime.now(pytz.utc) except MatrixError as e: self.bot._cb_join_room_failure(room, e.message) # part the specified room # @param room (Room) the room to leave def part_room(self,room): raise NotImplementedError # helper function for get_rooms() for protocol-specific flags # only needs to handle: FLAG_PARTED, FLAG_PENDING, FLAG_IN, FLAG_ALL # @param flag (int) one of Room.FLAG_* enums # @return (list of Room) rooms matching the flag def _get_rooms(self,flag): mxrooms = self.client.get_rooms() return [self.new_room(mxroom) for mxroom in mxrooms] # @param room (Room) the room to query # @return (list of User) the Users in the specified room def get_occupants(self,room): if(room in self.room_occupants): return list(self.room_occupants[room]) else: try: memberdict = room.room.get_joined_members() users = [ self.new_user(x) for x in memberdict ] self.room_occupants[room] = set(users) return users except MatrixError as e: raise self.ConnectFailure # @param room (Room) the room to query # @return (str) the nick name we are using in the specified room def get_nick(self,room): return self.get_user().get_name() # TODO: per-room nicknames # @param room (Room) the room to query # @param nick (str) the nick to examine # @return (User) the "real" User behind the specified nick/room def get_real(self,room,nick): raise NotImplementedError # @return (User) our username def get_user(self): return MatrixUser(self,self.opt('matrix.username'),Message.GROUP) # @param user (str) a user id to parse # @param typ (int) either Message.GROUP or Message.PRIVATE # @param real (User) [self] the "real" user behind this user # @return (User) a new instance of this protocol's User subclass def new_user(self,user,typ=None,real=None): return MatrixUser(self,user,typ,real) # @param name (object) the identifier for this Room # @param nick (str) [None] the nick name to use in this Room # @param pword (str) [None] the password for joining this Room # @return (Room) a new instance of this protocol's Room subclass def new_room(self,room_id_or_alias,nick=None,pword=None): return MatrixRoom(self,room_id_or_alias,nick,pword)
class MatrigramClient(object): def __init__(self, server, tb, username): self.client = MatrixClient(server) self.tb = tb self.token = None self.server = server self.username = username self.focus_room_id = None self.msg_type_router = { 'm.image': self.forward_image_to_tb, 'm.audio': self.forward_voice_to_tb, 'm.video': self.forward_video_to_tb, 'm.emote': self.forward_emote_to_tb, } self.room_listener_uid = None self.ephemeral_listener_uid = None def login(self, username, password): try: self.token = self.client.login_with_password(username, password) logger.info('token = %s', self.token) rooms = self.get_rooms_aliases() if rooms: # set focus room to "first" room self.set_focus_room(rooms.keys()[0]) self.client.add_invite_listener(self.on_invite_event) self.client.add_leave_listener(self.on_leave_event) self.client.start_listener_thread() return True, "OK" except MatrixRequestError: return False, "Failed to login" except ConnectionError: return False, "Server is offline" def logout(self): self.client.logout() def on_event(self, _, event): logger.debug('entered with message %s', pprint_json(event)) sender = event['sender'].split(':')[0].encode('utf-8') # Prevent messages loopback if sender.startswith(u'@{}'.format(self.username)): return if event['type'] == "m.room.message": msgtype = event['content']['msgtype'] if msgtype != 'm.text': callback = self.msg_type_router.get(msgtype) if callback: callback(event) return content = event['content']['body'].encode('utf-8') self.tb.send_message(sender, content, self) elif event['type'] == "m.room.topic": topic = event['content']['topic'].encode('utf-8') self.tb.send_topic(sender, topic, self) def on_ephemeral_event(self, _, ee): logger.debug(pprint_json(ee)) if ee['type'] == 'm.typing': if ee['content']['user_ids']: self.tb.start_typing_thread(self) else: self.tb.stop_typing_thread(self) def on_leave_event(self, room_id, le): logger.debug(pprint_json(le)) if le['timeline']['events'][0]['sender'] != le['timeline']['events'][ 0]['state_key']: self.tb.send_kick(self._room_id_to_alias(room_id), self) def on_invite_event(self, _, ie): logger.debug('invite event %s', pprint_json(ie)) room_name = None for event in ie['events']: if event['type'] == 'm.room.name': room_name = event['content']['name'] if room_name: self.tb.send_invite(self, self._room_id_to_alias(room_name)) def join_room(self, room_id_or_alias): try: self.client.join_room(room_id_or_alias) self.set_focus_room(room_id_or_alias) return True except MatrixRequestError: return False def leave_room(self, room_id_or_alias): room = self.get_room_obj(room_id_or_alias) if not room: logger.error('cant find room') return False if self.focus_room_id == room.room_id: rooms = self.get_rooms_aliases() room_id = self._room_alias_to_id(room_id_or_alias) del rooms[room_id] new_focus_room = rooms.keys()[0] if rooms else None self.set_focus_room(new_focus_room) return room.leave() def set_focus_room(self, room_id_or_alias): if self._room_alias_to_id(room_id_or_alias) == self.focus_room_id: return # remove current focus room listeners if self.focus_room_id is not None: room_obj = self.get_room_obj(self.focus_room_id) room_obj.remove_listener(self.room_listener_uid) self.room_listener_uid = None room_obj.remove_ephemeral_listener(self.ephemeral_listener_uid) self.ephemeral_listener_uid = None logger.info("remove focus room %s", self.focus_room_id) self.focus_room_id = None # set new room on focus if room_id_or_alias is not None: self.focus_room_id = self._room_alias_to_id(room_id_or_alias) room_obj = self.get_room_obj(self.focus_room_id) self.room_listener_uid = room_obj.add_listener(self.on_event) self.ephemeral_listener_uid = room_obj.add_ephemeral_listener( self.on_ephemeral_event) logger.info("set focus room to %s", self.focus_room_id) def get_focus_room_alias(self): return self._room_id_to_alias(self.focus_room_id) def have_focus_room(self): return self.focus_room_id is not None def get_members(self): room_obj = self.get_room_obj(self.focus_room_id) rtn = room_obj.get_joined_members() return [ member['displayname'] for _, member in rtn.items() if member.get('displayname') ] def set_name(self, name): user = self.client.get_user(self.client.user_id) user.set_display_name(name) def emote(self, body): room_obj = self.get_room_obj(self.focus_room_id) room_obj.send_emote(body) def create_room(self, alias, is_public=False, invitees=()): try: room_obj = self.client.create_room(alias, is_public, invitees) room_obj.update_aliases() logger.debug('room_id %s', room_obj.room_id) logger.debug('room_alias %s', room_obj.aliases[0]) return (room_obj.room_id, room_obj.aliases[0]) except MatrixRequestError: logger.error('error creating room') return None, None def backfill_previous_messages(self, limit=10): room_obj = self.get_room_obj(self.focus_room_id) room_obj.backfill_previous_messages(limit=limit) def get_rooms_aliases(self): # returns a dict with id: room obj rooms = self._get_rooms_updated() if not rooms: return rooms logger.debug("rooms got from server are %s", rooms) # return dict with id: list of aliases or id (if no alias exists) return { key: val.aliases if val.aliases else [key] for (key, val) in rooms.items() } def get_room_obj(self, room_id_or_alias): """Get room object of specific id or alias. Args: room_id_or_alias (str): Room's id or alias. Returns (Room): Room object corresponding to room_id_or_alias. """ rooms = self._get_rooms_updated() room_id = self._room_alias_to_id(room_id_or_alias) return rooms.get(room_id) def send_message(self, msg): room_obj = self.get_room_obj(self.focus_room_id) if not room_obj: logger.error('cant find room') else: room_obj.send_text(msg) def send_photo(self, path): with open(path, 'rb') as f: mxcurl = self.client.upload(f.read(), mimetypes.guess_type(path)[0]) room_obj = self.get_room_obj(self.focus_room_id) if not room_obj: logger.error('cant find room') else: room_obj.send_image(mxcurl, os.path.split(path)[1]) def send_voice(self, path): with open(path, 'rb') as f: mxcurl = self.client.upload(f.read(), mimetypes.guess_type(path)[0]) room_obj = self.get_room_obj(self.focus_room_id) room_obj.send_audio(mxcurl, os.path.split(path)[1]) def send_video(self, path): with open(path, 'rb') as f: mxcurl = self.client.upload(f.read(), mimetypes.guess_type(path)[0]) room_obj = self.get_room_obj(self.focus_room_id) room_obj.send_video(mxcurl, os.path.split(path)[1]) def discover_rooms(self): res = requests.get('{}/_matrix/client/r0/publicRooms?limit=20'.format( self.server)) res_json = res.json() room_objs = res_json['chunk'] return [ room['aliases'][0] for room in room_objs if room.get('aliases') ] def download_from_event(self, event): mxcurl = event['content']['url'] link = self.client.api.get_download_url(mxcurl) media_id = mxcurl.split('/')[3] media_type = event['content']['info']['mimetype'].split('/')[1] path = os.path.join(self.tb.config['media_dir'], '{}.{}'.format(media_id, media_type)) download_file(link, path) return path def forward_image_to_tb(self, event): sender = event['sender'].split(':')[0].encode('utf-8') path = self.download_from_event(event) self.tb.send_photo(sender, path, self) def forward_voice_to_tb(self, event): sender = event['sender'].split(':')[0].encode('utf-8') path = self.download_from_event(event) self.tb.send_voice(sender, path, self) def forward_video_to_tb(self, event): sender = event['sender'].split(':')[0].encode('utf-8') path = self.download_from_event(event) self.tb.send_video(sender, path, self) def forward_emote_to_tb(self, event): sender = event['sender'].split(':')[0].encode('utf-8') content = event['content']['body'].encode('utf-8') self.tb.send_emote(sender, content, self) def _room_id_to_alias(self, id): """Convert room id to alias. Args: id (str): Room id. Returns (str): Room alias. """ if id is None: return None if id.startswith('#'): return id rooms = self.get_rooms_aliases() if id in rooms: return rooms[id][0] else: return None def _room_alias_to_id(self, alias): """Convert room alias to id. Args: alias (str): Room alias. Returns (str): Room id. """ if alias is None: return None if not alias.startswith('#'): return alias return self.client.api.get_room_id(alias) def _get_rooms_updated(self): """Return rooms dictionary with updated aliases. Returns (dict): Return rooms dictionary with updated aliases. """ rooms = self.client.get_rooms() for room in rooms.values(): room.update_aliases() return rooms
class MatrixTransport(Transport): def __init__(self, homeserver, username, password, matrix_room): super().__init__() self.homeserver = homeserver self.username = username self.password = password self.room_name = matrix_room self.is_running = gevent.event.Event() self.do_reconnect = gevent.event.AsyncResult() self.retry_timeout = 5 self.client = None def matrix_exception_handler(self, e): """Called whenever an exception occurs in matrix client thread. Any exception other than MatrixHttpLibError will be sent to parent hub, terminating the program. """ if isinstance(e, MatrixHttpLibError): log.warning(str(e)) self.do_reconnect.set(100) raise e gevent.get_hub().parent.throw(e) def connect(self): """Connects to a matrix homeserver and initializes the client class""" if self.client is not None: self.client.logout() self.client = None self.client = MatrixClient(self.homeserver) self.client.login_with_password(self.username, self.password) self.room = self.client.join_room(self.room_name) self.client.start_listener_thread( exception_handler=lambda e: self.matrix_exception_handler(e), ) def get_room_events(self, limit=100): """Get past messages in the broadcast room, up to the @limit""" f = {"room": {"timeline": {"limit": 100}}} result = self.client.api.sync(filter=json.dumps(f)) room_id = self.room.room_id room = result['rooms']['join'][room_id] return room['timeline']['events'] def sync_history(self): """Calls event callback for all events retrived from the broadcast room history""" events = self.get_room_events() for event in events: self.push_event(event) def push_event(self, event): """Calls a registered event callback""" for listener in self.room.listeners: if listener['event_type'] is None or listener[ 'event_type'] == event['type']: listener['callback'](self.room, event) def dispatch(self, room, event): if event['type'] == "m.room.message": if event['content']['msgtype'] == "m.text": self.run_message_callbacks(event['content']['body']) log.debug("{0}: {1}".format(event['sender'], event['content']['body'])) def transmit_data(self, message: str, target_node: str = None): # TODO: fix sending to certain receiver assert self.room is not None self.room.send_text(message) def _run(self): """Gevent loop. The Matrix connection is restored automatically on error.""" while self.is_running.is_set() is False: try: self.connect() self.room.add_listener( lambda room, event: self.dispatch(room, event)) self.sync_history() self.do_reconnect.wait() if self.do_reconnect.get() == 100: gevent.sleep(self.retry_timeout) continue except (requests.exceptions.ConnectionError, MatrixHttpLibError) as e: log.warn( "Connection to %s failed. Retrying in %d seconds (%s)" % ( self.homeserver, self.retry_timeout, str(e), ), ) gevent.sleep(self.retry_timeout)
def start(stdscr): global size, room, data, rooms, access_token, endTime, rooms, all_rooms, lastEventRoom, room_keys curses.curs_set(0) curses.use_default_colors() size = stdscr.getmaxyx() stdscr.addstr(0, 0, "loading...") stdscr.refresh() loadCredentials("./credentials.json") client = MatrixClient(server) access_token = client.login_with_password( username, password, size[0]) rooms = client.get_rooms() all_rooms = "all rooms" rooms[all_rooms] = Room(client, all_rooms) rooms[all_rooms].events = [] room_keys = list(rooms.keys()) room = all_rooms #room_keys[1] # "all_rooms" nextRoom = 1 endTime = client.end curses.halfdelay(10) maxDisplayName = 24 displayNamestartingPos = 20 PAD_COMMENTS = True pause = False client.add_listener(processMessage) client.start_listener_thread() curses.echo() stdscr.keypad(True) inputBuffer = "" lastEventRoom = all_rooms the_room_to_post_to = None # store the last room we saw before we started typing while(True): size = stdscr.getmaxyx() maxChars = size[1] - 1 - len(username) - 3 stdscr.clear() # we want NAME aka ALIAS[0] (ROOM_ID) # or 2nd choice: ALIAS[0] (ROOM_ID) # or fallback: ROOM_ID line = str(room) if line == all_rooms: pass elif rooms[room].name is None: if len(rooms[room].aliases) > 0 and rooms[room].aliases[0] != room: line = rooms[room].aliases[0] + " (" + line + ")" else: if len(rooms[room].aliases) > 0 and rooms[room].aliases[0] != room: line = rooms[room].name + " aka " + getFirstRoomAlias(rooms[room]) + " (" + line + ")" else: if rooms[room].name != room: line = rooms[room].name + " (" + line + ")" #line.encode("utf-8") if rooms[room].topic is not None: line += " · topic: " + rooms[room].topic stdscr.addstr( 0, 0, ( "redpill v0.7 · screen size: " + str(size) + " · chat size: " + str(len(rooms[room].events)) + " · room: " + str(line) + " the variables: room: " + room + " last: " + lastEventRoom ), curses.A_UNDERLINE ) current = len(rooms[room].events) - 1 if True: y = 1 if current >= 0: # TODO: something when the first event is a typing event currentLine = size[0] - 1 # input space = "" for i in range(size[1] - 1): space += " " stdscr.addstr(currentLine, 0, space, curses.A_DIM) stdscr.addstr(currentLine, 0, "<" + username + ">", curses.A_DIM) stdscr.addstr(currentLine - 1, 0, space, curses.A_UNDERLINE) for event in reversed(rooms[room].events): if event["type"] == "m.typing": #if True: continue # do something clever elif event["type"] == "m.presence": #if True: continue # do something clever elif event["type"] == "m.roomchange": room_id = event["room_id"] #lin = (str(rooms[room_id].name) + " aka " + getFirstRoomAlias(rooms[room_id]) + " (" + # rooms[room_id].room_id + ")") line = room_id if line == all_rooms: pass elif rooms[line].name is None: if len(rooms[room_id].aliases) > 0 and rooms[room_id].aliases[0] != room_id: line = rooms[room_id].aliases[0] + " (" + line + ")" else: if len(rooms[room_id].aliases) > 0 and rooms[room_id].aliases[0] != room_id: line = rooms[room_id].name + " aka " + getFirstRoomAlias(rooms[room_id]) + " (" + line + ")" else: if rooms[room_id].name != room_id: line = rooms[room_id].name + " (" + line + ")" #if rooms[room].topic is not None: # line += " · topic: " + rooms[room].topic currentLine -= 1 stdscr.addstr(currentLine, 0, "Event(s) from " + line, curses.A_DIM) else: #currentLine = size[0] - y currentLine -= 1 if currentLine < 3: # how many lines we want to reserve break #if currentLine == 5: # currentLine -= 1 y += 1 if "origin_server_ts" in event: convertedDate = datetime.datetime.fromtimestamp( int( event["origin_server_ts"] / 1000) ).strftime('%Y-%m-%d %H:%M:%S') # assumption: body == normal message length = 0 if "user_id" in event: length = len( event["user_id"] ) if "body" in event["content"]: rawText = event["content"]["body"].encode('utf-8') if event["content"]["msgtype"] == "m.emote": if len(rawText) > 0 and rawText[0] == " ": rawText = rawText[1:] linesNeeded = (displayNamestartingPos + maxDisplayName + 3 + len(rawText)) / size[1] lin = (displayNamestartingPos + maxDisplayName + 3 + len(rawText)) #if currentLine == size[0] - 2: # stdscr.addstr(currentLine, 0, str(lin) + " " + str(size[1]) + " " + str(linesNeeded) + " ", curses.A_UNDERLINE) #else: # stdscr.addstr(currentLine, 0, str(lin) + " " + str(size[1]) + " " + str(linesNeeded) + " ") linesNeeded = 0 buf = "" lineByLineText = [] first = True bufSinceLastWord = "" for char in rawText: if True: #for char in line: bufSinceLastWord += char if char == '\n': linesNeeded += 1 buf += bufSinceLastWord if PAD_COMMENTS or first: linesNeeded += (displayNamestartingPos + maxDisplayName + 3 + len(buf)) / size[1] else: linesNeeded += len(buf) / size[1] first = False lineByLineText.append(buf) buf = "" bufSinceLastWord = "" else: if ((PAD_COMMENTS and (displayNamestartingPos + maxDisplayName + 3 + len(buf + bufSinceLastWord)) == size[1] - 1) or (not PAD_COMMENTS and (len(buf + bufSinceLastWord)) == size[1] - 1)): #if (displayNamestartingPos + maxDisplayName + 3 + len(buf + bufSinceLastWord)) == size[1] - 1: if len(buf) == 0: buf += bufSinceLastWord bufSinceLastWord = "" if char.isspace(): buf += bufSinceLastWord lineByLineText.append(buf) bufSinceLastWord = "" buf = "" else: lineByLineText.append(buf) buf = bufSinceLastWord bufSinceLastWord = "" linesNeeded += 1 if char.isspace(): buf += bufSinceLastWord bufSinceLastWord = "" # if (displayNamestartingPos + maxDisplayName + 3 + len(buf + bufSinceLastWord)) == size[1] - 1: if ((PAD_COMMENTS and (displayNamestartingPos + maxDisplayName + 3 + len(buf + bufSinceLastWord)) == size[1] - 1) or (not PAD_COMMENTS and (len(buf + bufSinceLastWord)) == size[1] - 1)): buf += bufSinceLastWord bufSinceLastWord = "" lineByLineText.append(buf) linesNeeded += 1 buf = "" #elif char == ' ': # skip all whitespace # self.X += 1 buf += bufSinceLastWord lineByLineText.append(buf) linesNeeded += (displayNamestartingPos + maxDisplayName + 3 + len(buf)) / size[1] buf = "" currentLine -= linesNeeded if currentLine - linesNeeded < 2: # how many lines we want to reserve break if currentLine == size[0] - 2: stdscr.addstr(currentLine, 0, str(lin) + " " + str(size[1]) + " " + str(linesNeeded) + " ", curses.A_UNDERLINE) else: stdscr.addstr(currentLine, 0, str(lin) + " " + str(size[1]) + " " + str(linesNeeded) + " ") #for i in range(linesNeeded): if PAD_COMMENTS: pad = displayNamestartingPos + maxDisplayName + 3 #if linesNeeded == 0: linesNeeded += 1 for i in range(linesNeeded): buf = rawText[:size[1] - pad] rawText = rawText[size[1] - pad:] if currentLine + i == size[0] - 2: stdscr.addstr( currentLine + i, displayNamestartingPos + maxDisplayName + 3, lineByLineText[i], curses.A_BOLD + curses.A_UNDERLINE ) else: try: stdscr.addstr( currentLine + i, displayNamestartingPos + maxDisplayName + 3, lineByLineText[i], curses.A_BOLD ) except: e = sys.exc_info()[0] print("Error: unable to start thread. " + str(e)) stdscr.addstr(1, 0, str(e)) else: # TODO: need to split this out to get proper underline if currentLine == size[0] - 2: stdscr.addstr( currentLine, displayNamestartingPos + maxDisplayName + 3, rawText, curses.A_BOLD + curses.A_UNDERLINE ) else: stdscr.addstr( currentLine, displayNamestartingPos + maxDisplayName + 3, rawText, curses.A_BOLD ) usern = event["user_id"] if length > maxDisplayName: usern = usern[:maxDisplayName - 3] + "..." if event["content"]["msgtype"] == "m.emote": usern = "* " + usern if currentLine == size[0] - 2: stdscr.addstr( currentLine, displayNamestartingPos + max(0, maxDisplayName - length), str(usern), curses.A_UNDERLINE + curses.A_BOLD ) else: stdscr.addstr( currentLine, displayNamestartingPos + max(0, maxDisplayName - length), str(usern), curses.A_BOLD ) else: usern = "<" + usern + ">" if currentLine == size[0] - 2: stdscr.addstr( currentLine, displayNamestartingPos + max(0, maxDisplayName - length), str(usern), curses.A_UNDERLINE ) else: stdscr.addstr( currentLine, displayNamestartingPos + max(0, maxDisplayName - length), str(usern) ) if currentLine == size[0] - 2: stdscr.addstr(currentLine, 0, convertedDate, curses.A_UNDERLINE) else: stdscr.addstr(currentLine, 0, convertedDate) #if currentLine == size[1]: # last line # stdscr.addstr( # currentLine, displayNamestartingPos + # maxDisplayName + 3, buf[:size[1] - # (displayNamestartingPos + maxDisplayName + 4)], # curses.A_BOLD # ) #else: # stdscr.addstr( # currentLine, displayNamestartingPos + # maxDisplayName + 3, buf, # curses.A_BOLD # ) # membership == join/leave events elif "membership" in event["content"]: buf = " invited someone" if event["content"]["membership"] == "invite": if "state_key" in event: buf = " invited " + event["state_key"] elif event["content"]["membership"] == "join": buf = " has joined" elif event["content"]["membership"] == "leave": buf = " has left" if length > maxDisplayName: if currentLine == size[0] - 2: stdscr.addstr( currentLine, displayNamestartingPos + 1, str(event["user_id"]), curses.A_DIM + curses.A_UNDERLINE ) stdscr.addstr( currentLine, displayNamestartingPos + length + 1, buf, curses.A_DIM + curses.A_UNDERLINE ) else: stdscr.addstr( currentLine, displayNamestartingPos + 1, str(event["user_id"]), curses.A_DIM ) stdscr.addstr( currentLine, displayNamestartingPos + length + 1, buf, curses.A_DIM ) else: if currentLine == size[0] - 2: stdscr.addstr( currentLine, displayNamestartingPos + 1 + maxDisplayName - length, str(event["user_id"]), curses.A_DIM + curses.A_UNDERLINE ) stdscr.addstr( currentLine, displayNamestartingPos + maxDisplayName + 1, buf, curses.A_DIM + curses.A_UNDERLINE ) else: stdscr.addstr( currentLine, displayNamestartingPos + 1 + maxDisplayName - length, str(event["user_id"]), curses.A_DIM ) stdscr.addstr( currentLine, displayNamestartingPos + maxDisplayName + 1, buf, curses.A_DIM ) current -= 1 if pause: stdscr.addstr( int(size[0] / 2) - 1, int(size[1] / 2), " ", curses.A_REVERSE ) stdscr.addstr( int(size[0] / 2), int(size[1] / 2), " PAUSED ", curses.A_REVERSE ) stdscr.addstr( int(size[0] / 2) + 1, int(size[1] / 2), " ", curses.A_REVERSE ) try: stdscr.addstr(size[0] - 1, len(username) + 3, inputBuffer[-maxChars:]) except: e = sys.exc_info()[0] print("Error: unable to start thread. " + str(e)) stdscr.addstr(1, 0, str(e)) stdscr.refresh() # getInput(stdscr) #def getInput(stdscr): # if True: try: c = stdscr.getch(size[0] - 1, len(username) + 3) #c = stdscr.getkey(size[0] - 1, len(username) + 3) #stri = stdscr.getstr(size[0] - 1, len(username) + 3, 10) if c == -1: stdscr.addstr(1, 0, "timeout") else: if c <= 256 and c != 10 and c != 9: ## enter and tab inputBuffer += chr(c) if len(inputBuffer) == 1: # e.g. just started typing if lastEventRoom != all_rooms: the_room_to_post_to = lastEventRoom if c == 9: #stdscr.addstr(1, 0, "%s was pressed\n" % c) room = room_keys[nextRoom] nextRoom = (nextRoom + 1) % len(rooms) the_room_to_post_to = None elif c == 10: # enter with open('sends.log', 'a') as the_file: the_file.write("the_room_to_post_to:" + str(the_room_to_post_to) + "\n") the_file.write("lastEventRoom: " + str(lastEventRoom) + "\n") the_file.write("room: " + str(room) + "\n") the_file.write("inputBuffer: " + str(inputBuffer) + "\n") the_file.write("---\n") if inputBuffer.startswith("/invite"): user_id = inputBuffer[7:].strip() rooms[room].invite_user(user_id) elif inputBuffer.startswith("/kick"): user_id = inputBuffer[5:].strip() reason = "no reason..." rooms[room].kick_user(user_id, reason) elif inputBuffer.startswith("/power"): user_id = inputBuffer[7:].strip() power_level = 50 rooms[room].set_power_level(user_id, power_level) elif inputBuffer.startswith("/op"): user_id = inputBuffer[2:].strip() rooms[room].set_power_level(user_id) elif inputBuffer.startswith("/ban"): # reason user_id = inputBuffer[4:].strip() reason = "sux" #FIXME rooms[room].ban(user_id, reason) elif inputBuffer.startswith("/join"): # there's a /join that supports aliases room_alias = inputBuffer[5:].strip() client.join_room(room_alias) elif inputBuffer.startswith("/j"): room_alias = inputBuffer[2:].strip() client.join_room(room_alias) elif inputBuffer.startswith("/leave"): rooms[room].leave_room(room_id) elif inputBuffer.startswith("/create"): # create a new room is_public = True invitees = () # def create_room(self, alias=None, is_public=False, invitees=()): room_alias = inputBuffer[7:].strip() client.create_room(room_alias, is_public, invitees) elif inputBuffer.startswith("/topic"): # get or set topic new_topic = inputBuffer[6:].strip() if len(new_topic) > 0: rooms[room].topic = new_topic else: pass #rooms[room].topic = "fail" else: if room == all_rooms: if the_room_to_post_to is None: if lastEventRoom != all_rooms: the_room_to_post_to = lastEventRoom else: stdscr.addstr(1, 0, "No idea what room to post to!") stdscr.refresh() inputBuffer = "No idea what room to post to!" continue else: the_room_to_post_to = room if inputBuffer.startswith("/me"): rooms[the_room_to_post_to].send_emote(inputBuffer[3:]) else: rooms[the_room_to_post_to].send_text(inputBuffer) inputBuffer = "" the_room_to_post_to = None elif c == curses.KEY_DC: inputBuffer = "" the_room_to_post_to = None elif c == curses.KEY_BACKSPACE: if len(inputBuffer) > 0: inputBuffer = inputBuffer[:-1] if len(inputBuffer) == 0: the_room_to_post_to = None elif c == curses.KEY_IC: pause = not(pause) if pause: curses.nocbreak() curses.cbreak() stdscr.timeout(-1) stdscr.addstr( int(size[0] / 2) - 1, int(size[1] / 2), " ", curses.A_REVERSE ) stdscr.addstr( int(size[0] / 2), int(size[1] / 2), " PAUSING ", curses.A_REVERSE ) stdscr.addstr( int(size[0] / 2) + 1, int(size[1] / 2), " ", curses.A_REVERSE ) stdscr.refresh() else: stdscr.addstr( int(size[0] / 2) - 1, int(size[1] / 2), " ", curses.A_REVERSE ) stdscr.addstr( int(size[0] / 2), int(size[1] / 2), " RESUMING ", curses.A_REVERSE ) stdscr.addstr( int(size[0] / 2) + 1, int(size[1] / 2), " ", curses.A_REVERSE ) stdscr.refresh() curses.halfdelay(10) stdscr.timeout(1) elif c == 27: # need to test for alt combo or ESC curses.cbreak() curses.echo() #curses.curs_set(1) stdscr.keypad(0) curses.endwin() quit() elif c == curses.KEY_F2: PAD_COMMENTS = not PAD_COMMENTS #stdscr.addstr(2, 0, "time() == %s\n" % time.time()) finally: do_nothing = True