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 change_password(self, mxid, old_password, new_password): """Change a user's password. Warning: this function is slow as balls.""" matrix = MatrixClient(self._server) matrix.login_with_password(username=mxid, password=old_password) body = { "auth": { "type": "m.login.password", "user": mxid, "password": old_password }, "new_password": new_password } try: matrix.api._send('POST', '/account/password', body, api_path='/_matrix/client/r0') return True except MatrixRequestError as exception: print exception return False
def login_by_password(): """Login using password authentication.""" try: _client = MatrixClient(base_url=homeserver, valid_cert_check=verify_tls) _client.login_with_password(username, password) store_token(mx_id, _client.token) return _client except MatrixRequestError as ex: _LOGGER.error("login_by_password: (%d) %s", ex.code, ex.content)
def matrix_client(): global _client if _client: return _client print(f"Signing into {MATRIX_HOST}...") client = MatrixClient(MATRIX_HOST) client.login_with_password(username=MATRIX_USER, password=MATRIX_PASSWORD) _client = client return client
def setup(config): """Sets up the Matrix client. Makes sure the (specified) room is joined. """ client = MatrixClient("https://{0}:{1}".format(config['homeserver'], int(config['port']))) client.login_with_password(username=config['username'], password=config['password']) room = client.join_room('{0}:{1}'.format(config['room'], config['domain'])) return client, room
def login_by_password(): """Login using password authentication.""" try: _client = MatrixClient(base_url=homeserver, valid_cert_check=verify_tls) _client.login_with_password(username, password) store_token(mx_id, _client.token) return _client except MatrixRequestError as ex: _LOGGER.error('login_by_password: (%d) %s', ex.code, ex.content)
def _login_by_password(self): """Login using password authentication and return the client.""" _client = MatrixClient(base_url=self._homeserver, valid_cert_check=self._verify_tls) _client.login_with_password(self._mx_id, self._password) self._store_auth_token(_client.token) return _client
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) self.client.login_with_password(login, passwd) 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.api.send_message(room_id, text)
def login_by_password(self): """Login using password authentication and return the client.""" from matrix_client.client import MatrixClient _client = MatrixClient(base_url=self.homeserver, valid_cert_check=self.verify_tls) _client.login_with_password(self.username, self.password) self.store_auth_token(_client.token) return _client
def _login_by_password(self): """Login using password authentication and return the client.""" from matrix_client.client import MatrixClient _client = MatrixClient( base_url=self._homeserver, valid_cert_check=self._verify_tls) _client.login_with_password(self._mx_id, self._password) self._store_auth_token(_client.token) return _client
def __init__(self, server, username, password, rooms, nick=None): client = MatrixClient(server) self.viewer_url = server.strip('/') + "/_matrix/media/v1/download/" try: client.login_with_password(username, password) except MatrixRequestError as e: if e.code == 403: logger.error("403 Bad username or password.") sys.exit(4) else: logger.error("{} Check your server details are correct.".format(e)) sys.exit(2) except MissingSchema as e: logger.error("{} Bad URL format.".format(e)) sys.exit(3) self.username = client.user_id logger.info("logged in as: {}".format(self.username)) if nick is not None: u = client.get_user(client.user_id) logger.info("Setting display name to {}".format(nick)) try: u.set_display_name(nick) except MatrixRequestError as e: logger.error("Fail to set display name: error = {}".format(e)) self.joined_rooms = {} self.room_id_to_alias = {} self.displaynames = {} for room_id_alias in rooms: try: room = client.join_room(room_id_alias) except MatrixRequestError as e: if e.code == 400: logger.error("400 Room ID/Alias in the wrong format") sys.exit(11) else: logger.error("{} Couldn't find room {}".format(e, room_id_alias)) sys.exit(12) logger.info("Joined room {}".format(room_id_alias)) self.joined_rooms[room_id_alias] = room self.room_id_to_alias[room.room_id] = room_id_alias room.add_listener(self.on_message) self.client = client self.bot_msg_pattern = config['matrix'].get('bot_msg_pattern', None)
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 example(host, user, password, token): """run the example.""" client = None try: if token: print('token login') client = MatrixClient(host, token=token, user_id=user) else: print('password login') client = MatrixClient(host) token = client.login_with_password(user, password) print('got token: %s' % token) except MatrixRequestError as e: print(e) if e.code == 403: print("Bad username or password") exit(2) elif e.code == 401: print("Bad username or token") exit(3) else: print("Verify server details.") exit(4) except MissingSchema as e: print(e) print("Bad formatting of URL.") exit(5) except InvalidSchema as e: print(e) print("Invalid URL schema") exit(6) print("is in rooms") for room_id, room in client.get_rooms().items(): print(room_id)
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 Bot: def __init__(self, hs_url, username, password): self.cli = MatrixClient(hs_url) self.cli.login_with_password(username=username, password=password) self.shelf = shelve.open(data_file, writeback=True) signal.signal(signal.SIGTERM, self.close_shelf) signal.signal(signal.SIGINT, self.close_shelf) self.cli.add_invite_listener(self.on_invite) self.joined_rooms = self.cli.get_rooms() logger.info( f'Joined to {[r.display_name for r in self.joined_rooms.values()]}' ) self.add_room_listeners() def run(self): self.cli.listen_forever(exception_handler=self.sync_exception_handler) logger.info('Bot started.') def add_room_listeners(self): for room in self.joined_rooms.values(): self.add_local_bot(room) def on_invite(self, room_id, state): room = self.cli.join_room(room_id) # Force a sync in order not to process previous room messages self.cli._sync() self.add_local_bot(room) self.joined_rooms[room_id] = room room.send_notice( f'Hi! I\'m a list keeping bot. Send {LocalBot.prefix}help' ' to learn how to use me.') logger.info( f'Received an invite for room {room.display_name}, and joined.') def add_local_bot(self, room): lbot = LocalBot(room, self.cli.api, self.shelf) room.add_listener(lbot.on_message, event_type='m.room.message') def close_shelf(self, *args): logger.info('Closing shelf...') self.shelf.close() logger.info('Shelf is closed.') sys.exit() @staticmethod def sync_exception_handler(exception): logger.warning(exception)
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 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)
def setup(config): """Sets up the Matrix client. Makes sure the (specified) room is joined. """ loginargs = {} if 'token' in config: loginargs['user_id'] = '@{0}:{1}'.format(config['username'], config['domain']) loginargs['token'] = config['token'] client = MatrixClient( "https://{0}:{1}".format(config['homeserver'], int(config['port'])), **loginargs) if 'token' not in config: client.login_with_password(username=config['username'], password=config['password']) room = client.join_room('{0}:{1}'.format(config['room'], config['domain'])) return client, room
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 MatrixSendServer: def __init__(self, config, room): self.queue_path = config['queue_path'] self.client = MatrixClient(config['homeserver_uri']) self.token = self.client.login_with_password(username=config['username'], password=config['password']) self.room = self.client.join_room(room) self.room_id = room self.message_queue = [] self.limited_until = None def dequeue(self): """Dequeue as many messages as the server lets us""" for f in self.message_queue: with open(f, "r", encoding="UTF-8") as fin: message = fin.read() if self.limited_until is not None and time.time() * 1000 < self.limited_until: return try: self.room.send_text(message) self.message_queue.pop(0) except Exception as e: necessary_delay = json.loads(e.content)['retry_after_ms'] sys.stderr.write("Sleeping for %s seconds... Queue depth %s\n" % (necessary_delay, len(self.message_queue))) sys.stderr.flush() self.limited_until = time.time() * 1000 + necessary_delay else: os.remove(f) def enqueue(self, f): if os.path.isfile(f) and not f in self.message_queue: self.message_queue.append(f) def run(self): print("run!") while True: for f in os.listdir(self.queue_path): f = os.path.join(self.queue_path, f) self.enqueue(f) self.dequeue() time.sleep(10)
class MCat: def __init__(self, config): self.client = MatrixClient(config['server']) self.token = self.client.login_with_password(username=config['user'], password=config['password']) self.room = self.client.join_room(config['room']) self.room_id = config['room'] self.message_queue = [] self.limited_until = None def dequeue(self): """Dequeue as many messages as the server lets us""" for message in self.message_queue: if time.time() * 1000 < self.limited_until: return try: self.room.send_html(message) self.message_queue.pop(0) except Exception as e: necessary_delay = json.loads(e.content)['retry_after_ms'] sys.stderr.write("Sleeping for %s seconds... Queue depth %s\n" % (necessary_delay, len(self.message_queue))) sys.stderr.flush() self.limited_until = time.time() * 1000 + necessary_delay def enqueue(self, message): self.message_queue.append(message) def f_to_matrix(self, f): for line in f: self.enqueue(line) self.dequeue() while len(self.message_queue) > 0: self.dequeue() def matrix_to_f(self, f): def stdout_cb(chunk): if chunk[u'type'] != u"m.presence" and chunk[u'room_id'] == self.room_id: f.write(json.dumps(chunk)) f.write('\n') f.flush() self.client.add_listener(stdout_cb) self.client.listen_forever()
class MatrixProtocol(Protocol): """ A Matrix Protocol wrapper. """ def __init__(self, username, password, room, server): super().__init__(username, password, room, server) self.username = username # Connect to Matrix self.client = MatrixClient(server) self.token = self.client.login_with_password(username=username, password=password) self.room = self.client.join_room(room) self.room.add_listener(self.process_event) def listen_forever(self): self.room.send_text('My name is PyAstroBot! I seek the grail!') try: self.client.listen_forever() except KeyboardInterrupt: pass finally: self.room.send_text("PyAstroBot is sneaking away and buggering off...") def send_message(self, message): self.room.send_text(message) def send_emote(self, message): self.room.send_emote(message) def _is_message(self, event): if event['type'] == "m.room.message": if event['content']['msgtype'] == "m.text": # Ignore everything the bot says. if event['sender'] != self.username: return event def process_event(self, room, event): if self._is_message(event): for call in self.event_callbacks: call(event['content']['body'], event['sender'])
import time import datetime import dateutil.relativedelta import os #import sched import threading import re import screenutils import logging my_server="https://matrix.org" my_username="******" my_password="******" my_room="!DJppiagxDRhqgGMLVq:matrix.org" client=MatrixClient(my_server) token=client.login_with_password(my_username,my_password) room=client.join_room(my_room) all_chat=True chat_bot_users=['@vanous','@pokusnykralik'] msg_send="" def send_to_matrix(message): global msg_send try: #print threading.active_count() if threading.active_count()<3: res=room.send_text("%s: %s" % (time.strftime('%X'),message,)) else:
from matrix_client.client import MatrixClient import time client = MatrixClient("http://167.99.238.80:8008") # New user #token = client.register_with_password(username="******", password="******") def listener(event): print(event) # Existing user token = client.login_with_password(username="******", password="******") client.add_listener(listener) #room = client.create_room("my_room_alias") room = client.join_room("#my_room_alias:teamspeak.sigmapenguinplace.net") room.send_text("Hello!") while True: client.listen_for_events()
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
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 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 MatrixBot(): def __init__(self, settings): self.sync_token = None self.logger = utils.get_logger() self.cache = utils.create_cache(settings) self.cache_timeout = int(settings["memcached"]["timeout"]) self.settings = settings self.period = settings["DEFAULT"]["period"] self.uri = settings["matrix"]["uri"] self.username = settings["matrix"]["username"].lower() self.password = settings["matrix"]["password"] self.room_ids = settings["matrix"]["rooms"] self.domain = self.settings["matrix"]["domain"] self.only_local_domain = self.settings["matrix"]["only_local_domain"] self.subscriptions_room_ids = settings["subscriptions"].keys() self.revokations_rooms_ids = settings["revokations"].keys() self.allowed_join_rooms_ids = filter(lambda x: x != 'default', settings["allowed-join"].keys()) self.default_allowed_join_rooms = settings["allowed-join"]["default"] self.client = MatrixClient(self.uri) self.token = self.client.login_with_password(username=self.username, password=self.password) self.api = MatrixHttpApi(self.uri, token=self.token) self.rooms = [] self.room_aliases = {} self.plugins = [] for plugin in settings['plugins'].itervalues(): mod = __import__(plugin['module'], fromlist=[plugin['class']]) klass = getattr(mod, plugin['class']) self.plugins.append(klass(self, plugin['settings'])) def _get_selected_users(self, groups_users_list): def _add_or_remove_user(users, username, append): username = self.normalize_user_id(username) if append and username not in users["in"]: users["in"].append(username) if not append and username not in users["out"]: users["out"].append(username) ldap_settings = self.settings["ldap"] append = True users = {"in": [], "out": []} for item in groups_users_list: if item == ("but"): append = False elif item.startswith("+"): group_name = item[1:] groups_members = bot_ldap.get_ldap_groups_members( ldap_settings) if group_name in groups_members.keys(): map(lambda x: _add_or_remove_user(users, x, append), groups_members[group_name]) else: _add_or_remove_user(users, item, append) selected_users = filter(lambda x: x not in users["out"], users["in"]) return selected_users def normalize_user_id(self, user_id): if not user_id.startswith("@"): user_id = "@" + user_id self.logger.debug("Adding missing '@' to the username: %s" % user_id) if user_id.count(":") == 0: user_id = "%s:%s" % (user_id, self.domain) return user_id def get_user_id(self, username=None, normalized=True): if not username: username = self.username normalized_username = self.normalize_user_id(username) if normalized: return normalized_username else: return normalized_username[1:].split(':')[0] def is_local_user_id(self, username): normalized_username = self.get_user_id(username, normalized=True) if normalized_username.split(':')[1] == self.domain: return True return False def get_real_room_id(self, room_id): if room_id.startswith("#"): room_id = self.api.get_room_id(room_id) return room_id def get_room_members(self, room_id): key = "get_room_members-%s" % room_id res = self.cache.get(key) if res: self.logger.debug("get_room_members (cached): %s" % (key)) return res res = self.call_api("get_room_members", 2, room_id) self.cache.set(key, res, self.cache_timeout) self.logger.debug("get_room_members (non cached): %s" % (key)) return res def is_room_member(self, room_id, user_id): try: r = Room(self.client, room_id) return user_id in r.get_joined_members().keys() except Exception, e: return False return False
def main(): tmp_dir = conf.zbx_matrix_tmp_dir if tmp_dir == "/tmp/" + conf.zbx_tg_prefix: print_message( "WARNING: it is strongly recommended to change `zbx_tg_tmp_dir` variable in config!!!" ) print_message( "https://github.com/ableev/Zabbix-in-Telegram/wiki/Change-zbx_tg_tmp_dir-in-settings" ) tmp_cookie = tmp_dir + "/cookie.py.txt" tmp_uids = tmp_dir + "/uids.txt" tmp_need_update = False # do we need to update cache file with uids or not rnd = random.randint(0, 999) ts = time.time() hash_ts = str(ts) + "." + str(rnd) log_file = conf.log_path args = sys.argv settings = { "zbxtg_itemid": "0", # itemid for graph "zbxtg_title": None, # title for graph "zbxtg_image_period": None, "zbxtg_image_age": "3600", "zbxtg_image_width": "900", "zbxtg_image_height": "200", "tg_method_image": False, # if True - default send images, False - send text "is_debug": False, "is_channel": False, "disable_web_page_preview": False, "location": None, # address "lat": 0, # latitude "lon": 0, # longitude "is_single_message": False, "markdown": False, "html": False, "signature": None, "signature_disable": False, "graph_buttons": False, "extimg": None, "to": None, "to_group": None, "forked": False, } settings_description = { "itemid": { "name": "zbxtg_itemid", "type": "list", "help": "script will attach a graph with that itemid (could be multiple)", "url": "Graphs" }, "title": { "name": "zbxtg_title", "type": "str", "help": "title for attached graph", "url": "Graphs" }, "graphs_period": { "name": "zbxtg_image_period", "type": "int", "help": "graph period", "url": "Graphs" }, "graphs_age": { "name": "zbxtg_image_age", "type": "str", "help": "graph period as age", "url": "Graphs" }, "graphs_width": { "name": "zbxtg_image_width", "type": "int", "help": "graph width", "url": "Graphs" }, "graphs_height": { "name": "zbxtg_image_height", "type": "int", "help": "graph height", "url": "Graphs" }, "graphs": { "name": "tg_method_image", "type": "bool", "help": "enables graph sending", "url": "Graphs" }, "chat": { "name": "tg_chat", "type": "bool", "help": "deprecated, don't use it, see 'group'", "url": "How-to-send-message-to-the-group-chat" }, "group": { "name": "tg_group", "type": "bool", "help": "sends message to a group", "url": "How-to-send-message-to-the-group-chat" }, "debug": { "name": "is_debug", "type": "bool", "help": "enables 'debug'", "url": "How-to-test-script-in-command-line" }, "channel": { "name": "is_channel", "type": "bool", "help": "sends message to a channel", "url": "Channel-support" }, "disable_web_page_preview": { "name": "disable_web_page_preview", "type": "bool", "help": "disable web page preview", "url": "Disable-web-page-preview" }, "location": { "name": "location", "type": "str", "help": "address of location", "url": "Location" }, "lat": { "name": "lat", "type": "str", "help": "specify latitude (and lon too!)", "url": "Location" }, "lon": { "name": "lon", "type": "str", "help": "specify longitude (and lat too!)", "url": "Location" }, "single_message": { "name": "is_single_message", "type": "bool", "help": "do not split message and graph", "url": "Why-am-I-getting-two-messages-instead-of-one" }, "markdown": { "name": "markdown", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML" }, "html": { "name": "html", "type": "bool", "help": "markdown support", "url": "Markdown-and-HTML" }, "signature": { "name": "signature", "type": "str", "help": "bot's signature", "url": "Bot-signature" }, "signature_disable": { "name": "signature_disable", "type": "bool", "help": "enables/disables bot's signature", "url": "Bot-signature" }, "graph_buttons": { "name": "graph_buttons", "type": "bool", "help": "activates buttons under graph, could be using in ZbxTgDaemon", "url": "Interactive-bot" }, "external_image": { "name": "extimg", "type": "str", "help": "should be url; attaches external image from different source", "url": "External-image-as-graph" }, "to": { "name": "to", "type": "str", "help": "rewrite zabbix username, use that instead of arguments", "url": "Custom-to-and-to_group" }, "to_group": { "name": "to_group", "type": "str", "help": "rewrite zabbix username, use that instead of arguments", "url": "Custom-to-and-to_group" }, "forked": { "name": "forked", "type": "bool", "help": "internal variable, do not use it. Ever.", "url": "" }, } if len(args) < 4: do_not_exit = False if "--features" in args: print(("List of available settings, see {0}/Settings\n---".format( url_wiki_base))) for sett, proprt in list(settings_description.items()): print(("{0}: {1}\ndoc: {2}/{3}\n--".format( sett, proprt["help"], url_wiki_base, proprt["url"]))) elif "--show-settings" in args: do_not_exit = True print_message("Settings: " + str(json.dumps(settings, indent=2))) else: print(( "Hi. You should provide at least three arguments.\n" "zbxtg.py [TO] [SUBJECT] [BODY]\n\n" "1. Read main page and/or wiki: {0} + {1}\n" "2. Public Telegram group (discussion): {2}\n" "3. Public Telegram channel: {3}\n" "4. Try dev branch for test purposes (new features, etc): {0}/tree/dev" .format(url_github, url_wiki_base, url_tg_group, url_tg_channel))) if not do_not_exit: sys.exit(0) zbx_to = args[1] zbx_subject = args[2] zbx_body = args[3] zbx = ZabbixWeb(server=conf.zbx_server, username=conf.zbx_api_user, password=conf.zbx_api_pass) client = MatrixClient(conf.server) token = None try: token = client.login_with_password(username=conf.username, password=conf.password) except MatrixRequestError as e: print(e) if e.code == 403: print_message("Bad username or password.") sys.exit(4) else: print_message("Check your sever details are correct.") sys.exit(2) except MissingSchema as e: print_message("Bad URL format.") print(e) sys.exit(3) except: print_message("ERROR (unknown) login_with_password()!") sys.exit(5) room = None try: room = client.join_room(zbx_to) except MatrixRequestError as e: print(e) if e.code == 400: print_message("Room ID/Alias in the wrong format") sys.exit(11) else: print_message("Couldn't find room.") sys.exit(12) except: print_message("ERROR (unknown) join_room()!") sys.exit(13) zbx.tmp_dir = tmp_dir # workaround for Zabbix 4.x zbx_version = 3 try: zbx_version = conf.zbx_server_version except: pass # if conf.proxy_to_zbx: # zbx.proxies = { # "http": "http://{0}/".format(conf.proxy_to_zbx), # "https": "https://{0}/".format(conf.proxy_to_zbx) # } # https://github.com/ableev/Zabbix-in-Telegram/issues/55 try: if conf.zbx_basic_auth: zbx.basic_auth_user = conf.zbx_basic_auth_user zbx.basic_auth_pass = conf.zbx_basic_auth_pass except: pass try: zbx_api_verify = conf.zbx_api_verify zbx.verify = zbx_api_verify except: pass zbxtg_body = (zbx_subject + "\n" + zbx_body).splitlines() zbxtg_body_text = [] ## seems redundant but necessary to avoid errors of using these variables before declaration... shouldn't happen tg_method_image = bool(settings["tg_method_image"]) disable_web_page_preview = bool(settings["disable_web_page_preview"]) is_single_message = bool(settings["is_single_message"]) for line in zbxtg_body: if line.find(conf.zbx_tg_prefix) > -1: setting = re.split("[\s:=]+", line, maxsplit=1) key = setting[0].replace(conf.zbx_tg_prefix + ";", "") if key not in settings_description: # if "--debug" in args: room.send_text( str("[ERROR] There is no '{0}' method, use --features to get help" .format(key))) # continue if settings_description[key]["type"] == "list": value = setting[1].split(",") elif len(setting) > 1 and len(setting[1]) > 0: value = setting[1] elif settings_description[key]["type"] == "bool": value = True else: value = settings[settings_description[key]["name"]] if key in settings_description: settings[settings_description[key]["name"]] = value if key == "graphs" and value is True: tg_method_image = True else: zbxtg_body_text.append(line) if conf.DEBUG: is_debug = True zbx.debug = True log_file = tmp_dir + ".debug." + hash_ts + ".log" print_message(log_file) if not os.path.isdir(tmp_dir): if is_debug: print_message("Tmp dir doesn't exist, creating new one...") try: os.makedirs(tmp_dir) os.chmod(tmp_dir, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO) except: tmp_dir = "/tmp" if is_debug: print_message("Using {0} as a temporary dir".format(tmp_dir)) done_all_work_in_the_fork = False # issue75 if done_all_work_in_the_fork: sys.exit(0) # replace text with emojis internal_using_emoji = False # I hate that, but... https://github.com/ableev/Zabbix-in-Telegram/issues/152 if hasattr(conf, "emoji_map"): zbxtg_body_text_emoji_support = [] for l in zbxtg_body_text: l_new = l for k, v in list(conf.emoji_map.items()): l_new = l_new.replace("{{" + k + "}}", v) zbxtg_body_text_emoji_support.append(l_new) if len("".join(zbxtg_body_text)) - len( "".join(zbxtg_body_text_emoji_support)): internal_using_emoji = True zbxtg_body_text = zbxtg_body_text_emoji_support if not is_single_message and tg_method_image is False: text = """%(zbx_subject)s %(zbx_body)s """ % { "zbx_subject": zbx_subject, "zbx_body": zbx_body } room.send_text(text) if settings["zbxtg_image_age"]: age_sec = age2sec(settings["zbxtg_image_age"]) if age_sec > 0 and age_sec > 3600: settings["zbxtg_image_period"] = age_sec message_id = 0 if tg_method_image is True: zbx.login() room.send_text(settings["zbxtg_title"] + "\n" + str(zbxtg_body_text[0])) if not zbx.cookie: text_warn = "Login to Zabbix web UI has failed (web url, user or password are incorrect), "\ "unable to send graphs check manually" room.send_text([text_warn]) print_message(text_warn) else: if not settings["extimg"]: zbxtg_file_img = zbx.graph_get(settings["zbxtg_itemid"], settings["zbxtg_image_period"], settings["zbxtg_title"], settings["zbxtg_image_width"], settings["zbxtg_image_height"], version=zbx_version) else: zbxtg_file_img = external_image_get(settings["extimg"], tmp_dir=zbx.tmp_dir) zbxtg_body_text, is_modified = list_cut(zbxtg_body_text, 200) if not zbxtg_file_img: text_warn = "Can't get graph image, check script manually, see logs, or disable graphs" room.send_text([text_warn]) print_message(text_warn) else: if not is_single_message: zbxtg_body_text = "" else: if is_modified: text_warn = "probably you will see MEDIA_CAPTION_TOO_LONG error, "\ "the message has been cut to 200 symbols, "\ "https://github.com/ableev/Zabbix-in-Telegram/issues/9"\ "#issuecomment-166895044" print_message(text_warn) in_file = open(zbxtg_file_img, "rb") uploaddata = in_file.read( ) # if you only wanted to read 512 bytes, do .read(512) in_file.close() response = client.upload(uploaddata, "image/png") room.send_image(str(response), zbxtg_file_img) os.remove(zbxtg_file_img) if "--show-settings" in args: print_message("Settings: " + str(json.dumps(settings, indent=2)))
class BridgeBot: xmpp = None # type: ClientXMPP matrix = None # type: MatrixClient topic_room_id_map = None # type: Dict[str, str] special_rooms = None # type: Dict[str, MatrixRoom] special_room_names = None # type: Dict[str, str] groupchat_flag = None # type: str groupchat_jids = None # type: List[str] restore_room_topic = True # type: bool users_to_invite = None # type: List[str] matrix_room_topics = None # type: Dict[str, str] matrix_server = None # type: Dict[str, str] matrix_login = None # type: Dict[str, str] xmpp_server = None # type: Tuple[str, int] xmpp_login = None # type: Dict[str, str] xmpp_roster_options = None # type: Dict[str, bool] xmpp_groupchat_nick = None # type: str send_messages_to_all_chat = True # type: bool disable_all_chat_room = False # type: bool send_presences_to_control = True # type: bool groupchat_mute_own_nick = True # type: bool disabled_jids = set() # type: Set[str] @property def bot_id(self) -> str: return self.matrix_login['username'] def __init__(self, config_file: str = CONFIG_FILE): self.groupchat_jids = [] self.topic_room_id_map = {} self.special_rooms = { 'control': None, 'all_chat': None, } self.special_room_names = { 'control': 'XMPP Control Room', 'all_chat': 'XMPP All Chat', } self.xmpp_roster_options = {} self.load_config(config_file) self.matrix = MatrixClient(**self.matrix_server) self.xmpp = ClientXMPP(**self.xmpp_login, **self.xmpp_roster_options) self.matrix.login_with_password(**self.matrix_login) if self.disable_all_chat_room: self.send_messages_to_all_chat = False #should not be necessary (see load_config) if 'all_chat' in self.special_rooms: del self.special_rooms['all_chat'] if 'all_chat' in self.special_room_names: del self.special_room_names['all_chat'] # Prepare matrix special channels and their listeners for room in self.matrix.get_rooms().values(): room.update_room_topic() topic = room.topic if topic in self.special_rooms.keys(): logging.debug('Recovering special room: ' + topic) self.special_rooms[topic] = room elif topic.startswith(self.groupchat_flag): room_jid = topic[len(self.groupchat_flag):] self.groupchat_jids.append(room_jid) for topic, room in self.special_rooms.items(): if room is None: room = self.matrix.create_room() self.setup_special_room(room, topic) self.special_rooms['control'].add_listener(self.matrix_control_message, 'm.room.message') if not self.disable_all_chat_room: self.special_rooms['all_chat'].add_listener( self.matrix_all_chat_message, 'm.room.message') # Invite users to special rooms for room in self.special_rooms.values(): for user_id in self.users_to_invite: room.invite_user(user_id) # Prepare xmpp listeners self.xmpp.add_event_handler('roster_update', self.xmpp_roster_update) self.xmpp.add_event_handler('message', self.xmpp_message) self.xmpp.add_event_handler('presence_available', self.xmpp_presence_available) self.xmpp.add_event_handler('presence_unavailable', self.xmpp_presence_unavailable) self.xmpp.add_event_handler('groupchat_message', self.xmpp_groupchat_message) # Connect to XMPP and start processing XMPP events self.xmpp.connect(self.xmpp_server) self.xmpp.process(block=False) # Rejoin group chats logging.debug('Rejoining group chats') for room_jid in self.groupchat_jids: self.xmpp.plugin['xep_0045'].joinMUC(room_jid, self.xmpp_groupchat_nick) logging.debug('Done with bot init') def load_config(self, path: str): with open(path, 'r') as conf_file: config = yaml.load(conf_file) self.users_to_invite = config['matrix']['users_to_invite'] self.matrix_room_topics = config['matrix']['room_topics'] self.groupchat_flag = config['matrix']['groupchat_flag'] if 'restore_room_topic' in config[ 'matrix']: # to be compatible to other config files self.restore_room_topic = config['matrix']['restore_room_topic'] self.matrix_server = config['matrix']['server'] self.matrix_login = config['matrix']['login'] self.xmpp_server = (config['xmpp']['server']['host'], config['xmpp']['server']['port']) self.xmpp_login = config['xmpp']['login'] self.xmpp_groupchat_nick = config['xmpp']['groupchat_nick'] self.send_presences_to_control = config['send_presences_to_control'] self.send_messages_to_all_chat = config['send_messages_to_all_chat'] if not self.send_messages_to_all_chat and 'disable_all_chat_room' in config: self.disable_all_chat_room = config['disable_all_chat_room'] else: self.disable_all_chat_room = False self.groupchat_mute_own_nick = config['groupchat_mute_own_nick'] self.xmpp_roster_options = config['xmpp']['roster_options'] if 'disabled_jids' in config: self.disabled_jids = set(config['disabled_jids']) if 'xmpp_login_jid' in self.disabled_jids: self.disabled_jids.add(self.xmpp_login['jid']) self.disabled_jids.remove('xmpp_login_jid') def get_room_for_jid(self, jid: str) -> MatrixRoom: """ Return the room corresponding to the given XMPP JID :param jid: bare XMPP JID, should not include the resource :return: Matrix room object for chatting with that JID """ room_id = self.topic_room_id_map[jid] return self.matrix.get_rooms()[room_id] def get_unmapped_rooms(self) -> List[MatrixRoom]: """ Returns a list of all Matrix rooms which are not a special room (e.g., the control room) and do not have a corresponding entry in the topic -> room map. :return: List of unmapped, non-special Matrix room objects. """ special_room_ids = [r.room_id for r in self.special_rooms.values()] valid_room_ids = [v for v in self.topic_room_id_map.values() ] + special_room_ids unmapped_rooms = [ room for room_id, room in self.matrix.get_rooms().items() if room_id not in valid_room_ids ] return unmapped_rooms def get_empty_rooms(self) -> List[MatrixRoom]: """ Returns a list of all Matrix rooms which are occupied by only one user (the bot itself). :return: List of Matrix rooms occupied by only the bot. """ empty_rooms = [ room for room in self.matrix.get_rooms().values() if len(room.get_joined_members()) < 2 ] return empty_rooms def setup_special_room(self, room, topic: str): """ Sets up a Matrix room with the requested topic and adds it to the self.special_rooms map. If a special room with that topic already exists, it is replaced in the special_rooms map by the new room. :param room: Room to set up :param topic: Topic for the room """ room.set_room_topic(topic) room.set_room_name(self.special_room_names[topic]) self.special_rooms[topic] = room logging.debug('Set up special room with topic {} and id'.format( str(room.topic), room.room_id)) def create_mapped_room(self, topic: str, name: str = None) -> MatrixRoom or None: """ Create a new room and add it to self.topic_room_id_map. :param topic: Topic for the new room :param name: (Optional) Name for the new room :return: Room which was created """ if not name: #room without name is shown as the bot's name in clients like riot name = topic if topic in self.groupchat_jids: logging.debug( 'Topic {} is a groupchat without its flag, ignoring'.format( topic)) return None elif topic in self.topic_room_id_map.keys(): room_id = self.topic_room_id_map[topic] room = self.matrix.get_rooms()[room_id] logging.debug('Room with topic {} already exists!'.format(topic)) else: room = self.matrix.create_room() room.set_room_topic(topic) self.topic_room_id_map[topic] = room logging.info('Created mapped room with topic {} and id {}'.format( topic, str(room.room_id))) room.set_room_name(name) room.update_room_name( ) #room.name is not set automatically in all cases if self.restore_room_topic and room.name != name: room.set_room_name(name) return room def map_rooms_by_topic(self): """ Add unmapped rooms to self.topic_room_id_map, and listen to messages from those rooms. Rooms whose topics are empty or do not contain an '@' symbol are assumed to be special rooms, and will not be mapped. """ unmapped_rooms = self.get_unmapped_rooms() for room in unmapped_rooms: room.update_room_topic() logging.debug('Unmapped room {} ({}) [{}]'.format( room.room_id, room.name, room.topic)) if room.topic is None or '@' not in room.topic: logging.debug( 'Leaving it as-is (special room, topic does not contain @)' ) else: self.topic_room_id_map[room.topic] = room.room_id room.add_listener(self.matrix_message, 'm.room.message') def matrix_control_message(self, room: MatrixRoom, event: Dict): """ Handle a message sent to the control room. Does nothing unless a valid command is received: refresh Probes the presence of all XMPP contacts, and updates the roster. purge Leaves any ((un-mapped and non-special) or empty) Matrix rooms. joinmuc [email protected] Joins a muc leavemuc [email protected] Leaves a muc :param room: Matrix room object representing the control room :param event: The Matrix event that was received. Assumed to be an m.room.message . """ # Always ignore our own messages if event['sender'] == self.bot_id: return logging.debug('matrix_control_message: {} {}'.format( room.room_id, str(event))) if event['content']['msgtype'] == 'm.text': message_body = event['content']['body'] logging.info('Matrix received control message: ' + message_body) message_parts = message_body.split() if len(message_parts) > 0: message_parts[0] = message_parts[0].lower() # what about a empty body? if message_parts[0] == 'refresh': for jid in self.topic_room_id_map.keys(): self.xmpp.send_presence(pto=jid, ptype='probe') self.xmpp.send_presence() self.xmpp.get_roster() elif message_parts[0] == 'purge': self.special_rooms['control'].send_text( 'Purging unused rooms') # Leave from unwanted rooms for room in self.get_unmapped_rooms( ) + self.get_empty_rooms(): logging.info( 'Leaving room {r.room_id} ({r.name}) [{r.topic}]'. format(r=room)) if room.topic.startswith(self.groupchat_flag): room_jid = room.topic[len(self.groupchat_flag):] self.xmpp.plugin['xep_0045'].leaveMUC(room_jid) room.leave() elif len(message_parts) > 1: if message_parts[0] == 'joinmuc': room_jid = message_parts[1] logging.info('XMPP MUC join: {}'.format(room_jid)) self.create_groupchat_room(room_jid) self.xmpp.plugin['xep_0045'].joinMUC( room_jid, self.xmpp_groupchat_nick) elif message_parts[0] == 'leavemuc': room_jid = message_parts[1] logging.info('XMPP MUC leave: {}'.format(room_jid)) self.xmpp.plugin['xep_0045'].leaveMUC( room_jid, self.xmpp_groupchat_nick) room = self.get_room_for_jid(self.groupchat_flag + room_jid) room.leave() def matrix_all_chat_message(self, room: MatrixRoom, event: Dict): """ Handle a message sent to Matrix all-chat room. Currently just sends a warning that nobody will hear your message. :param room: Matrix room object representing the all-chat room :param event: The Matrix event that was received. Assumed to be an m.room.message . """ # Always ignore our own messages if event['sender'] == self.bot_id: return logging.debug('matrix_all_chat_message: {} {}'.format( room.room_id, str(event))) room.send_notice('Don\'t talk in here! Nobody gets your messages.') def matrix_message(self, room: MatrixRoom, event: Dict): """ Handle a message sent to a mapped Matrix room. Sends the message to the xmpp handle specified by the room's topic. :param room: Matrix room object representing the room in which the message was received. :param event: The Matrix event that was received. Assumed to be an m.room.message . """ if event['sender'] == self.bot_id: return if room.topic in self.special_rooms.keys(): logging.error('matrix_message called on special channel') logging.debug('matrix_message: {} {}'.format(room.room_id, event)) if event['content']['msgtype'] == 'm.text': message_body = event['content']['body'] if room.topic.startswith(self.groupchat_flag): jid = room.topic[len(self.groupchat_flag):] message_type = 'groupchat' else: jid = room.topic message_type = 'chat' name = self.xmpp.jid_nick_map[jid] logging.info('Matrix received message to {} : {}'.format( jid, message_body)) self.xmpp.send_message(mto=jid, mbody=message_body, mtype=message_type) if self.send_messages_to_all_chat: self.special_rooms['all_chat'].send_notice('To {} : {}'.format( name, message_body)) def xmpp_message(self, message: Dict): """ Handle a message received by the XMPP client. Sends the message to the relevant mapped Matrix room, as well as the Matrix all-chat room. :param message: The message that was received. :return: """ logging.info('XMPP received {} : {}'.format(message['from'].full, message['body'])) if message['type'] in ('normal', 'chat'): from_jid = message['from'].bare from_name = self.xmpp.jid_nick_map[from_jid] if from_jid in self.groupchat_jids: logging.warning( 'Normal chat message from a groupchat, ignoring...') return room = self.get_room_for_jid(from_jid) room.send_text(message['body']) if self.send_messages_to_all_chat: self.special_rooms['all_chat'].send_text('From {}: {}'.format( from_name, message['body'])) def xmpp_groupchat_message(self, message: Dict): """ Handle a groupchat message received by the XMPP client. Sends the message to the relevant mapped Matrix room, as well as the Matrix all-chat room. :param message: The message that was received. :return: """ logging.info('XMPP MUC received {} : {}'.format( message['from'].full, message['body'])) if message['type'] == 'groupchat': from_jid = message['from'].bare from_name = message['mucnick'] if self.groupchat_mute_own_nick and from_name == self.xmpp_groupchat_nick: return room = self.get_room_for_jid(self.groupchat_flag + from_jid) room.send_text(from_name + ': ' + message['body']) if self.send_messages_to_all_chat: self.special_rooms['all_chat'].send_text( 'Room {}, from {}: {}'.format(from_jid, from_name, message['body'])) def create_groupchat_room(self, room_jid: str): room = self.create_mapped_room(topic=self.groupchat_flag + room_jid) if room_jid not in self.groupchat_jids: self.groupchat_jids.append(room_jid) for user_id in self.users_to_invite: room.invite_user(user_id) def xmpp_presence_available(self, presence: Dict): """ Handle a presence of type "available". Sends a notice to the control channel. :param presence: The presence that was received. """ logging.debug('XMPP received {} : (available)'.format( presence['from'].full)) jid = presence['from'].bare if jid in self.disabled_jids: return if jid not in self.xmpp.jid_nick_map.keys(): logging.error('JID NOT IN ROSTER!?') self.xmpp.get_roster() return if self.send_presences_to_control: name = self.xmpp.jid_nick_map[jid] self.special_rooms['control'].send_notice( '{} available ({})'.format(name, jid)) def xmpp_presence_unavailable(self, presence): """ Handle a presence of type "unavailable". Sends a notice to the control channel. :param presence: The presence that was received. """ logging.debug('XMPP received {} : (unavailable)'.format( presence['from'].full)) jid = presence['from'].bare if jid in self.disabled_jids: return if self.send_presences_to_control: name = self.xmpp.jid_nick_map[jid] self.special_rooms['control'].send_notice( '{} unavailable ({})'.format(name, jid)) def xmpp_roster_update(self, _event): """ Handle an XMPP roster update. Maps all existing Matrix rooms, creates a new mapped room for each JID in the roster which doesn't have one yet, and invites the users specified in the config in to all the rooms. :param _event: The received roster update event (unused). """ logging.debug('######### ROSTER UPDATE ###########') rjids = [jid for jid in self.xmpp.roster] if len(rjids) > 1: raise Exception('Not sure what to do with more than one roster...') roster0 = self.xmpp.roster[rjids[0]] self.xmpp.roster_dict = {jid: roster0[jid] for jid in roster0} roster = self.xmpp.roster_dict self.map_rooms_by_topic() # Create new rooms where none exist for jid, info in roster.items(): if '@' not in jid: logging.warning('Skipping fake jid in roster: ' + jid) continue if jid in self.disabled_jids: continue name = info['name'] self.xmpp.jid_nick_map[jid] = name self.create_mapped_room(topic=jid, name=name) logging.debug('Sending invitations..') # Invite to all rooms for room in self.matrix.get_rooms().values(): users_in_room = room.get_joined_members() for user_id in self.users_to_invite: if user_id not in users_in_room: room.invite_user(user_id) logging.debug('######## Done with roster update #######')
class MatrixBackend(ErrBot): def __init__(self, config): super().__init__(config) if not hasattr(config, 'MATRIX_HOMESERVER'): log.fatal(""" You need to specify a homeserver to connect to in config.MATRIX_HOMESERVER. For example: MATRIX_HOMESERVER = "https://matrix.org" """) sys.exit(1) self._homeserver = config.MATRIX_HOMESERVER self._username = config.BOT_IDENTITY['username'] self._password = config.BOT_IDENTITY['password'] self._api = None self._token = None def serve_once(self): def dispatch_event(event): log.info("Received event: %s" % event) if event['type'] == "m.room.member": if event['membership'] == "invite" and event['state_key'] == self._client.user_id: room_id = event['room_id'] self._client.join_room(room_id) log.info("Auto-joined room: %s" % room_id) if event['type'] == "m.room.message" and event['sender'] != self._client.user_id: sender = event['sender'] room_id = event['room_id'] body = event['content']['body'] log.info("Received message from %s in room %s" % (sender, room_id)) # msg = Message(body) # msg.frm = MatrixPerson(self._client, sender, room_id) # msg.to = MatrixPerson(self._client, self._client.user_id, room_id) # self.callback_message(msg) msg = self.build_message(body) room = MatrixRoom(room_id) msg.frm = MatrixRoomOccupant(self._api, room, sender) msg.to = room self.callback_message(msg) self.reset_reconnection_count() self.connect_callback() self._client = MatrixClient(self._homeserver) try: self._token = self._client.register_with_password(self._username, self._password,) except MatrixRequestError as e: if e.code == 400 or e.code == 403: try: self._token = self._client.login_with_password(self._username, self._password,) except MatrixRequestError: log.fatal(""" Incorrect username or password specified in config.BOT_IDENTITY['username'] or config.BOT_IDENTITY['password']. """) sys.exit(1) self._api = MatrixHttpApi(self._homeserver, self._token) self.bot_identifier = MatrixPerson(self._api) self._client.add_listener(dispatch_event) try: while True: self._client.listen_for_events() except KeyboardInterrupt: log.info("Interrupt received, shutting down...") return True finally: self.disconnect_callback() def rooms(self): rooms = [] raw_rooms = self._client.get_rooms() for rid, robject in raw_rooms: # TODO: Get the canonical alias rather than the first one from # `Room.aliases`. log.debug('Found room %s (aka %s)' % (rid, rid.aliases[0])) def send_message(self, mess): super().send_message(mess) room_id = mess.to.room.id text_content = item_url = mess.body if item_url.startswith("http://") or item_url.startswith("https://"): if item_url.endswith("gif"): self._api.send_content(room_id, item_url, "image", "m.image") return # text_content = Markdown().convert(mess.body) self._api.send_message(room_id, text_content) def connect_callback(self): super().connect_callback() def build_identifier(self, txtrep): raise Exception( "XXX" ) def build_reply(self, mess, text=None, private=False): log.info("build_reply") response = self.build_message(text) response.frm = self.bot_identifier response.to = mess.frm return response def change_presence(self, status: str = '', message: str = ''): raise Exception( "XXX" ) @property def mode(self): return 'matrix' def query_room(self, room): raise Exception( "XXX" )
# 4 - Bad username/password. import sys import samples_common # Common bits used between samples from matrix_client.client import MatrixClient from matrix_client.api import MatrixRequestError from requests.exceptions import MissingSchema host, username, password = samples_common.get_user_details(sys.argv) 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) if len(sys.argv) > 4: userid = sys.argv[4]
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)
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)
def login(self): client = MatrixClient(self.config.server) client.login_with_password(self.config.username, self.config.password) self.client = client
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)
sys.path += ['plugins'] # bootstrap the reloader eval(compile(open(os.path.join('core', 'reload.py'), 'U').read(), os.path.join('core', 'reload.py'), 'exec'), globals()) reload(init=True) print "matrix has u" config = {} with open("./config.yml", "r") as fin: config = load(fin.read()) client = MatrixClient(config["me"]["homeserver"]) token = client.login_with_password(username=config["me"]["user"], password=config["me"]["password"]) rooms = client.get_rooms() def room_callback(event): room = rooms[event[u'room_id']] reload() if event[u'type'] == "m.room.message": print room.name, "<"+event[u'user_id']+">", event[u'content'][u'body'] if event[u'user_id'] == config["me"]["user"]: return else: content = event[u'content'] body = content[u'body'] if body.startswith("."):
1: '*WARNING*: ', 0: 'OK: ' } # Event information try: client = sensu_event['client']['name'] check = sensu_event['check']['name'] output = sensu_event['check']['output'] status = statuses[sensu_event['check']['status']] history = sensu_event['check']['history'] except Exception as e: print(str(e)) previous_status = history[len(history)-1] # Message text # *WARNING*: Client_name\n /tmp/test does not exists text = status + client + "\n" + output # Check previous status and send check information if previous_status != sensu_event['check']['status']: # Initialize Matrix client client = MatrixClient(conf['homeserver']) token = client.login_with_password(username=conf['username'], password=conf['password']) # Join to Room room = client.join_room(conf['room']) room.send_text(text)