Beispiel #1
0
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
Beispiel #2
0
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()
Beispiel #3
0
def run_bot(homeserver, authorize, username, password):
    allowed_users = authorize
    shell_env = os.environ.copy()
    shell_env['TERM'] = 'vt100'
    child_pid, master = pty.fork()
    if child_pid == 0:  # we are the child
        os.execlpe('sh', 'sh', shell_env)
    pin = os.fdopen(master, 'w')
    stop = threading.Event()

    client = MatrixClient(homeserver)
    client.login_with_password_no_sync(username, password)
    # listen for invites during initial event sync so we don't miss any
    client.add_invite_listener(lambda room_id, state: on_invite(
        client, room_id, state, allowed_users))
    client.listen_for_events()  # get rid of initial event sync
    client.add_listener(lambda event: on_message(event, pin, allowed_users),
                        event_type='m.room.message')

    shell_stdout_handler_thread = threading.Thread(target=shell_stdout_handler,
                                                   args=(master, client, stop))
    shell_stdout_handler_thread.start()

    while True:
        try:
            client.listen_forever()
        except KeyboardInterrupt:
            stop.set()
            sys.exit(0)
        except requests.exceptions.Timeout:
            logger.warn("timeout. Trying again in 5s...")
            time.sleep(5)
        except requests.exceptions.ConnectionError as e:
            logger.warn(repr(e))
            logger.warn("disconnected. Trying again in 5s...")
            time.sleep(5)
Beispiel #4
0
def test_remove_listener():
    def dummy_listener():
        pass

    client = MatrixClient("http://example.com")
    handler = client.add_listener(dummy_listener)

    found_listener = False
    for listener in client.listeners:
        if listener["uid"] == handler:
            found_listener = True
            break

    assert found_listener, "listener was not added properly"

    client.remove_listener(handler)
    found_listener = False
    for listener in client.listeners:
        if listener["uid"] == handler:
            found_listener = True
            break

    assert not found_listener, "listener was not removed properly"
Beispiel #5
0
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"
        )
Beispiel #6
0
class matrixConnector:
    """
    Connector to the Matrix network

    Example:
    matrixObj = MatrixConnector(url, username, password, updatePersistentData, token)
        url = the url to the matrix server
        username = the username of the matrix Server
        password = the password of the matrix user
        updatePersistentData = Callback to the function to save the learned token
        token = the token of this client, we will resived this at the first connection.
    """

    # declare the variable
    __rooms = {}


    def __init__(self, url, username, password, updatePersistentData, token=None ):
        # save my own name
        matrixConnector.username = username
        if token is None:
            # Try to login with username and password
            self.__client = MatrixClient(url)
            try:
                self.__token = self.__client.login_with_password(username, password)
                # save the token
                value = self.__token
                updatePersistentData('matrix_token', value)

            except MatrixRequestError as e:
                print(e)
                if e.code == 403:
                    print("Bad username or password.")
                    sys.exit(4)
                else:
                    print("Check your sever details are correct.")
                    sys.exit(2)
            except MissingSchema as e:
                print("Bad URL format.")
                print(e)
                sys.exit(3)
        else:
            # Try to login with token
            try:
                self.__client = MatrixClient(url, token, username)
                print("UserId:", self.__client.user_id)
            except MatrixRequestError as e:
                print(e)
                if e.code == 403:
                    print("Bad username or password.")
                    sys.exit(4)
                else:
                    print("Check your sever details are correct.")
                    sys.exit(2)
            except MissingSchema as e:
                print("Bad URL format.")
                print(e)
                sys.exit(3)

    def joinRoom(self, room_id_alias):
        """ Join a Room
        room = obj.joinRoom(room_id_alias)
        """

        roomID = room_id_alias

        if room_id_alias.startswith("#"):
            roomID = self.__client.api.get_room_id(room_id_alias)


        if roomID not in self.discoverJoinedRooms().keys():
            return False

        try:
            room = self.__client.join_room(roomID)
        except MatrixRequestError as e:
            print(e)
            if e.code == 400:
                print("Room ID/Alias in the wrong format")
                sys.exit(11)
            else:
                print("Couldn't find room.")
                sys.exit(12)

        self.__rooms[roomID] = room

    def discoverJoinedRooms(self):
        # Discover what rooms the user has joined
        return self.__client.get_rooms()

    def start_listener_thread(self, onMessage):
        # Login to the rooms, to get the message out of the room
        matrixConnector.onMessage = onMessage
        self.__client.add_listener(self.__onEvent)
        self.__client.start_listener_thread()

    def __onEvent(room, event):
        # get the events out of the room
        message = event["content"]["body"]
        roomId = event["room_id"]
        matrixConnector.onMessage(message, roomId)

    def sendMessage(self, message, roomId):
        # Let the bot say something
        self.__rooms[roomId].send_html(message)
Beispiel #7
0
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
Beispiel #8
0
def main():
    global client
    global config
    global github
    global repo
    global msc_labels
    global logger
    global room_specific_data

    # Retrieve login information from config file
    with open("config.toml", "r") as f:
        try:
            config = toml.loads(f.read())
        except:
            log_fatal("Error reading config file:")
            return

    # Configure logging
    # Determine whether we are using a logfile or not
    logging_format = "[%(levelname)s] %(asctime)s: %(message)s"
    if "logfile" in config["logging"]:
        logging.basicConfig(
            level=logging.INFO if config["logging"]["level"] != "DEBUG" else logging.DEBUG,
            format=logging_format,
            filename=config["logging"]["logfile"])
    else:
        logging.basicConfig(
            level=logging.INFO if config["logging"]["level"] != "DEBUG" else logging.DEBUG,
            format=logging_format)
    logger = logging.getLogger()

    # Retrieve room-specific data if config file exists
    if "data_filepath" in config["bot"]:
        data_filepath = config["bot"]["data_filepath"]
        if os.path.exists(config["bot"]["data_filepath"]):
            with open(data_filepath, 'r') as f:
                room_specific_data = json.loads(f.read())

    # Schedule daily summary messages per-room
    for room_id in room_specific_data.keys():
        # Check if summaries are enabled in this room
        if get_room_setting(room_id, "summary_enabled") == False:
            continue

        # Check if this room has a custom summary time
        if get_room_setting(room_id, "summary_time"):
            # Set a scheduler for that time
            # Tag with the room ID so we can easily cancel later if necessary
            schedule.every().day.at(config["bot"]["daily_summary_time"]).do(send_summary,
                                                                            room_id).tag(
                room_id)

    # Schedule daily summary messages to rooms that do not have a custom time
    set_up_default_summaries()

    # Login to Github
    github = Github(config["github"]["token"])
    repo = github.get_repo(config["github"]["repo"])
    log_info("Connected to Github")

    # Get MSC-related label objects from specified Github repository
    labels = config["github"]["labels"]
    msc_labels = {label.name: label for label in repo.get_labels() if label.name in labels}

    # Login to Matrix and listen for messages
    homeserver = "https://" + config["matrix"]["user_id"].split(":")[-1]
    client = MatrixClient(homeserver, user_id=config["matrix"]["user_id"],
                          token=config["matrix"]["token"])
    client.add_invite_listener(invite_received)
    client.add_listener(event_received, event_type="m.room.message")
    log_info("Connected to Matrix")

    # Sync continuously and check time for daily summary sending
    while True:
        try:
            client.listen_for_events()
        except:
            log_warn("Unable to contact /sync")
        schedule.run_pending()
        time.sleep(config["matrix"]["sync_interval"])  # Wait a few seconds between syncs
Beispiel #9
0
class MpyBot:
	def __init__(self, configfile, run=True):
		logger.debug('load config')
		config_dic = load_yaml_config(configfile)
		self.bot_startcmd = config_dic.get('bot_startcmd', STARTCMD)

		self._full_cmds = {}
		self._local_cmds = {}
		self._module_calls = {}
		for moduledic in config_dic.get('modules', []):
			self.add_module(moduledic)

		matrix_server = config_dic['matrix_server']

		logger.debug('init bot')
		self.mcl = MatrixClient(**matrix_server)

		self.auto_join_invited_rooms = config_dic.get('auto_join_invited_rooms', True)
		self.auto_join_servers = set(config_dic.get('auto_join_servers', []))

		self.admin_ids = set(config_dic.get('admin_ids', []))

		disp_name = config_dic.get('bot_display_name', '')
		if disp_name:
			user = self.mcl.get_user(self.mcl.user_id)
			if user.get_display_name() != disp_name:
				user.set_display_name(disp_name)

		self.mcl.add_invite_listener(self._process_invite)
		self.mcl.add_listener(self._process_message, 'm.room.message')

		logger.info('bot initialized')

		if run:
			self._run()

	def _run(self):
		logger.debug('run listen_forever')
		self.mcl.listen_forever()


	def join_room(self, room_id):
		try:
			logger.info('joining new room {}'.format(room_id))
			room = self.mcl.join_room(room_id)
			room.send_text("Welcome! Type {} to control me.".format(self.bot_startcmd))
			return True
		except MatrixError as e:
			logger.exception('{} while joining room {}'.format(repr(e), room_id))
			return False

	def leave_room(self, room_id):
		logger.info('trying to leave room with id {}'.format(room_id))
		leave_room = self.mcl.get_rooms().get(room_id, '')
		if not leave_room:
			logger.debug('bot not in room {}'.format(room_id))
			return False
		if leave_room.leave():
			logger.debug('left room {}'.format(room_id))
			return True
		else:
			logger.debug('failed to leave known room with id {}'.format(leave_room.room_id))
		return False


	def _process_invite(self, room_id, state=None):
		logger.debug('received invitation to {}, state: {}'.format(room_id, state))
		if self.auto_join_invited_rooms:
			if self.auto_join_servers and \
					room_id.split(':')[-1] not in self.auto_join_servers:
				return
			self.join_room(room_id)

	def _process_message(self, roomchunk):
		if roomchunk['sender'] == self.mcl.user_id:
			return

		age = roomchunk.get('unsigned', {}).get('age')
		if age is None:
			# fallback
			age = abs(time.time() - roomchunk['origin_server_ts']/1000)
		else:
			age /= 1000

		if age > 60:
			logger.debug('received old message in {}, event_id: {}'.format(roomchunk['room_id'], roomchunk['event_id']))
			return

		content = roomchunk['content']
		if content['msgtype'] == 'm.text':
			msg = content['body'].lstrip()
			if msg.startswith(self.bot_startcmd):
				room = self.mcl.get_rooms()[roomchunk['room_id']]
				msg = msg[len(self.bot_startcmd):].lstrip()
				self._evaluate_bot_message(room, roomchunk['sender'], msg)
			else:
				s_msg = msg.split(' ', 1)
				cmd = s_msg[0]
				msg = s_msg[1] if len(s_msg) > 1 else ''
				modulename = self._full_cmds.get(cmd)
				if modulename:
					room = self.mcl.get_rooms()[roomchunk['room_id']]
					self._call_module(modulename, room, roomchunk['sender'], msg)

	def _evaluate_bot_message(self, room, sender, msg):
		if msg.startswith('ctl'):
			logger.debug("received control message '{}' in room '{}'".format(msg, room.room_id))
			if sender not in self.admin_ids:
				logger.debug('{} has no permissions to send a ctl-message'.format(sender))
				room.send_notice('{} has no permissions to send a ctl-message'.format(sender))
				return
			data = msg.split()[1:]
			if len(data) == 2:
				if data[0] == 'join':
					if not self.join_room(data[1]):
						room.send_notice('something went wrong while joining room')
				elif data[0] == 'leave':
					if data[1] == 'this':
						data[1] = room.room_id
					if not self.leave_room(data[1]):
						room.send_notice('room could not be left')
			return

		elif msg.startswith('-'):
			msg = msg[1:].strip()
			if msg.startswith('help'):
				text = 'Available local commands:\n'
				for k in self._local_cmds:
					text += ' - ' + k + '\n'
				text += 'Available full commands:\n'
				for k in self._full_cmds:
					text += ' - ' + k + '\n'
				room.send_text(text)
			elif msg.startswith('time'):
				room.send_text('UTC: {:.0f}'.format(time.time()))

		s_msg = msg.split(' ', 1)
		cmd = s_msg[0]
		msg = s_msg[1] if len(s_msg) > 1 else ''

		modulename = self._local_cmds.get(cmd)
		if modulename:
			self._call_module(modulename, room, sender, msg)


	def add_module(self, moduledic):
		try:
			name = moduledic['name']
			module = importlib.import_module('modules.' + name)
			opt = moduledic.get('options')
			logging.debug('here')
			if opt:
				module.set_options(opt)

			self._module_calls[name] = module.msg_call

			cmd = moduledic.get('local_cmd')
			if cmd:
				self._local_cmds[cmd] = name
			cmd = moduledic.get('full_cmd')
			if cmd:
				self._full_cmds[cmd] = name

			logger.info('module {} added'.format(moduledic))
		except Exception as e:
			logger.exception('not possible to add module {}: {}'.format(moduledic, repr(e)))

	def _call_module(self, modulename, room, sender, msg):
		logger.debug("modulecall {} for message '{}' in room {}".format(modulename, msg, room.room_id))
		try:
			res = self._module_calls[modulename](room=room, sender=sender, msg=msg)
			if res and isinstance(res, str):
				room.send_text(res)
		except Exception as e:
			logger.exception('Failed to call module {}'.format(modulename))
Beispiel #10
0
class MatrixProtocol(Protocol):

    # 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.connected = False
        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.start_listener_thread()
            self.connected = True

        except MatrixRequestError as e:
            if (e.code == 403):
                self.log.debug(
                    "Credentials incorrect! Maybe your access token is outdated?"
                )
                raise AuthFailure
            else:
                self.log.debug("Failed to connect to homeserver!")
                raise ConnectFailure

    # @return (bool) True if we are connected to the server
    def is_connected(self):
        return self.connected

    # 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()):
            self.bot._cb_message(self.msg_queue.get())

    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'])

            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')

        except KeyError as e:
            self.log.debug(
                "Incoming message did not have all required fields: " +
                e.message)

    # 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())
        if (mess.get_emote()):
            to.room.send_emote(text)
        else:
            to.room.send_text(text)

    # 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)
        except MatrixRequestError 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):
        memberdict = room.room.get_joined_members()
        return [self.new_user(x) for x in memberdict]

    # @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)
Beispiel #11
0
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)
Beispiel #12
0
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())
    if(mess.get_emote()):
      to.room.send_emote(text)
    else:
      to.room.send_text(text)

  # 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 MatrixRequestError 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:
      memberdict = room.room.get_joined_members()
      users = [ self.new_user(x) for x in memberdict ]
      self.room_occupants[room] = set(users)
      return users

  # @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)
Beispiel #13
0
def main():
    global client
    global data
    global log
    global lock

    lock = threading.RLock()

    log.debug("try lock before main load_data()")
    with lock:
        log.debug("success lock before main load_data()")
        data = load_data()

    #FIXME отладка парсера
    #data["users"]["@progserega:matrix.org"]={}
    #data["users"]["@progserega:matrix.org"]["test"]={}
    #data["users"]["@progserega:matrix.org"]["test"]["alarms"]=[]
    #data["users"]["@progserega:matrix.org"]["test"]["lang"]="ru"
    #print(process_alarm_cmd("@progserega:matrix.org","test","напомни послезавтра после работы проверить звук в машине и подтёки масла, т.к. 11 июня закончится гарантия."))
    #sys.exit(1)

    log.info("try init matrix-client")
    client = MatrixClient(conf.server)
    log.info("success init matrix-client")

    try:
        log.info("try login matrix-client")
        client.login_with_password(username=conf.username,
                                   password=conf.password)
        log.info("success login matrix-client")
    except MatrixRequestError as e:
        print(e)
        log.debug(e)
        if e.code == 403:
            log.error("Bad username or password.")
            sys.exit(4)
        else:
            log.error("Check your sever details are correct.")
            sys.exit(2)
    except MissingSchema as e:
        log.error("Bad URL format.")
        print(e)
        log.debug(e)
        sys.exit(3)

    log.info("try init listeners")
    client.add_listener(on_message)
    client.add_ephemeral_listener(on_event)
    client.add_invite_listener(on_invite)
    #client.start_listener_thread()
    # Слушанье сокета и пересоединение в случае сбоя:
    #client.listen_forever(timeout_ms=30000, exception_handler=exception_handler,bad_sync_timeout=5)
    client.start_listener_thread(exception_handler=exception_handler)
    #client.listen_forever(timeout_ms=30000, exception_handler=exception_handler,bad_sync_timeout=5)
    #client.listen_forever()
    log.info("success init listeners")

    x = 0
    log.info("enter main loop")
    while True:
        # Проверяем уведомления:
        log.debug("try lock before main loop")
        with lock:
            log.debug("success lock before main loop")
            for user in data["users"]:
                for room in data["users"][user]:
                    for item in data["users"][user][room]["alarms"]:
                        alarm_timestamp = item["time"]
                        alarm_text = item["text"]
                        time_now = time.time()
                        if alarm_timestamp < time_now:
                            # Уведомляем:
                            html = "<p><strong>Напоминаю Вам:</strong></p>\n<ul>\n"
                            html += "<li>%s</li>\n" % alarm_text
                            html += "</ul>\n"
                            if send_html(room, html) == True:
                                data["users"][user][room]["alarms"].remove(
                                    item)
                                save_data(data)
                                break  # выходим из текущего цикла, т.к. изменили количество в маассиве (валится в корку) - следующей проверкой проверим оставшиеся
                            else:
                                log.error(
                                    "error send alarm at '%s' with text: '%s'"
                                    % (time.strftime(
                                        "%Y.%m.%d-%T",
                                        time.localtime(alarm_timestamp)),
                                       alarm_text))

        #print("step %d"%x)
        #x+=1
        time.sleep(10)
    log.info("exit main loop")
Beispiel #14
0
class Application(QApplication):
    loggedIn = Signal(str)

    messageReceived = Signal(object, str, object, float)

    roomSwitched = Signal(object)
    roomJoined = Signal(object)
    roomUpdated = Signal(str, str, str)
    roomLeft = Signal(object)
    roomInvited = Signal(object)

    def __init__(self, *args, **kwargs):
        super(Application, self).__init__(*args, **kwargs)

        # setup the application name and icon
        self.setApplicationName('Qui')
        self.iconPath = os.path.dirname(__file__)
        self.iconPath = os.path.join(self.iconPath, 'icons/icon.png')
        self.setWindowIcon(QIcon(self.iconPath))

        # setup the tray icon
        self.showNotifications = False
        self.tray = QSystemTrayIcon(QIcon(self.iconPath))
        self.tray.show()
        self.tray.activated.connect(self.showWindow)

        # setup the tray icon menu
        self.menu = QMenu()
        self.menu.addAction('Quit').triggered.connect(self.quit)
        self.tray.setContextMenu(self.menu)

        # setup signals
        self.messageReceived.connect(self.receiveMessage)

        # show the window
        self.window = None
        self.showWindow(None)

        # try loading the login info
        self.client = None
        self.settings = QSettings('Qui', 'Qui')
        self.url = self.settings.value("url")
        self.token = self.settings.value("token")
        self.user = self.settings.value("user")
        invalid = self.url is None or self.url == "" or self.token is None or self.token == "" or self.user is None or self.user == ""
        if not invalid:
            try:
                self.client = MatrixClient(base_url=self.url,
                                           token=self.token,
                                           user_id=self.user)
                self.postLogin()
            except:
                invalid = True
        # show the login form if we can't login
        if invalid:
            self.loginForm = LoginForm()
            self.loginForm.loggedIn.connect(self.login)
            self.loginForm.show()

    def showWindow(self, reason):
        if self.window is None:
            # make a new window if we don't have one
            self.window = MainWindow()
            # setup signals
            self.loggedIn.connect(self.window.login)
            self.messageReceived.connect(self.window.receiveMessage)
            self.roomLeft.connect(self.window.roomLeft)
            self.roomJoined.connect(self.window.roomJoined)
            self.roomUpdated.connect(self.window.roomUpdated)
            self.window.createRoom.connect(self.createRoom)
            self.window.leaveRoom.connect(self.leaveRoom)
            self.window.joinRoom.connect(self.joinRoom)
            # show it
            self.window.show()
        else:
            # show it if it's minimized or something
            self.window.showNormal()

    def quit(self):
        sys.exit(0)

    def login(self, client, url):
        self.client = client
        self.settings.setValue('url', url)
        self.url = url
        self.settings.setValue('token', client.token)
        self.token = client.token
        self.settings.setValue('user', client.user_id)
        self.user = client.user_id
        self.postLogin()

    def postLogin(self):
        self.loggedIn.emit(self.user)
        #self.messages.userId = self.user
        self.client.add_listener(self.eventCallback)
        self.client.add_presence_listener(self.presenceCallback)
        self.client.add_invite_listener(self.inviteCallback)
        self.client.add_leave_listener(self.leaveCallback)
        self.client.start_listener_thread()
        for room, obj in self.client.get_rooms().items():
            self.roomJoined.emit(obj)
            for event in obj.events:
                self.eventCallback(event)
        self.showNotifications = True

    def eventCallback(self, event):
        if 'type' in event and 'room_id' in event and 'content' in event and event[
                'type'] == 'm.room.message':
            room = Room(self.client, event['room_id'])
            self.messageReceived.emit(room, event['sender'], event['content'],
                                      time.time() - event['unsigned']['age'])
        if 'type' in event and 'room_id' in event and 'content' in event and event[
                'type'] == 'm.room.canonical_alias':
            self.roomUpdated.emit(event['room_id'], 'canonical_alias',
                                  event['content']['alias'])

    def presenceCallback(self, event):
        return
        print('presence: {}'.format(event))

    def inviteCallback(self, roomId, state):
        return
        print('invite: {} {}'.format(roomId, state))

    def leaveCallback(self, roomId, room):
        return
        print('leave: {} {}'.format(roomId, room))

    def receiveMessage(self, room, sender, content, timestamp):
        if self.showNotifications:
            if self.window is None or not self.window.isActiveWindow():
                self.tray.showMessage(sender, content['body'])

    def leaveRoom(self, room):
        self.client.api.leave_room(room.room_id)
        self.roomLeft.emit(room)

    def joinRoom(self, roomId):
        room = self.client.join_room(roomId)
        self.roomJoined.emit(room)

    def createRoom(self, roomId):
        room = self.client.create_room(roomId)
        self.roomJoined.emit(room)
Beispiel #15
0
class Matrix:
    _bridge = {}
    _slack = None

    _cache = {}
    _cache_file = 'matrix_cache.json'

    def __init__(self, user_id: str, http_matrix_server: str,
                 token: str) -> None:
        super().__init__()
        self.user_id = user_id
        self._client = MatrixClient(http_matrix_server,
                                    token=token,
                                    user_id=user_id)
        self.__load_cache()

    def __load_cache(self):
        if os.path.isfile(self._cache_file):
            with open(self._cache_file, 'r') as jsf:
                self._cache: json = json.load(jsf)
        if 'rooms' not in self._cache:
            self._cache['rooms'] = {}
        if 'uploaded_avatars' not in self._cache:
            self._cache['uploaded_avatars'] = {}

    def __save_cache(self):
        with open(self._cache_file, 'w') as file:
            file.write(json.dumps(self._cache))

    def set_slack(self, slack):
        self._slack = slack

    def bridge_slack_room(self, matrix_room_id, slack_room_id):
        self._bridge[matrix_room_id] = slack_room_id

    def send_message(self,
                     room_id: str,
                     text: str,
                     name: str = None,
                     avatar_url: str = None,
                     file_url: str = None,
                     file_name: str = None,
                     file_mimetype: str = None,
                     file_authorization: str = None):
        room = Room(self._client, room_id)
        current_avatar_url = None
        current_name = None
        avatar_uri = None
        if room_id in self._cache['rooms']:
            current_name = self._cache['rooms'][room_id]['name']
            current_avatar_url = self._cache['rooms'][room_id]['avatar_url']
        else:
            self._cache['rooms'][room_id] = {}
        if avatar_url is not None and avatar_url != current_avatar_url:
            if avatar_url in self._cache['uploaded_avatars']:
                avatar_uri = self._cache['uploaded_avatars'][avatar_url]
                print("Use cache avatar for an user " + avatar_uri + " (" +
                      avatar_url + ")")
            else:
                avatar_content = request.urlopen(avatar_url).read()
                avatar_uri = self._client.upload(avatar_content, 'image/png')
                self._cache['uploaded_avatars'][avatar_url] = avatar_uri
                print("Uploaded a new avatar for an user " + avatar_uri +
                      " (" + avatar_url + ")")
        if (name is not None
                and name is not current_name) or avatar_uri is not None:
            room.set_user_profile(displayname=name, avatar_url=avatar_uri)
            self._cache['rooms'][room_id]['name'] = name
            self._cache['rooms'][room_id]['avatar_url'] = avatar_url
            self.__save_cache()
        if file_url is not None and file_mimetype is not None and file_name is not None:
            rq = Request(file_url)
            rq.add_header('Authorization', file_authorization)
            file_content = urlopen(rq).read()
            file_uri = self._client.upload(file_content, file_mimetype)
            if file_mimetype in ['image/png', 'image/jpeg']:
                room.send_image(file_uri, file_name)
            else:
                room.send_file(file_uri, file_name)
        if text is not None:
            room.send_text(text)

    def start_listening(self):
        self._client.add_listener(self.__on_event)
        # room.add_listener(on_message)
        # client.add_listener(on_message)
        self._client.start_listener_thread()

    def __on_event(self, event):
        print(event)
        if event['type'] == "m.room.message" and event[
                'sender'] != self.user_id and event['room_id'] in self._bridge:
            if event['content']['msgtype'] == "m.text":
                self._slack.send_message(self._bridge[event['room_id']],
                                         event['content']['body'])

            if event['content']['msgtype'] in ["m.image", 'm.file']:
                image_url = self._client.api.get_download_url(
                    event['content']['url'])
                self._slack.send_file(self._bridge[event['room_id']],
                                      file_url=image_url,
                                      file_title=event['content']['body'])
Beispiel #16
0
def main():
  global client
  global data
  global log
  global lock

  lock = threading.RLock()

  log.debug("try lock before main load_data()")
  with lock:
    log.debug("success lock before main load_data()")
    data=load_data()
  log.debug("release lock() after access global data")

  log.info("try init matrix-client")
  client = MatrixClient(conf.server)
  log.info("success init matrix-client")

  try:
      log.info("try login matrix-client")
      token = client.login(username=conf.username, password=conf.password,device_id=conf.device_id)
      log.info("success login matrix-client")
  except MatrixRequestError as e:
      print(e)
      log.debug(e)
      if e.code == 403:
          log.error("Bad username or password.")
          sys.exit(4)
      else:
          log.error("Check your sever details are correct.")
          sys.exit(2)
  except MissingSchema as e:
      log.error("Bad URL format.")
      print(e)
      log.debug(e)
      sys.exit(3)

  log.info("try init listeners")
  client.add_listener(on_message)
  client.add_ephemeral_listener(on_event)
  client.add_invite_listener(on_invite)
  client.start_listener_thread(exception_handler=exception_handler)
  log.info("success init listeners")

  try:
    x=0
    log.info("enter main loop")
    while True:
      if conf.type_translate == "yandex_long":
        # check yandex_long_jobs:
        num_jobs=0
        for room_id in data["rooms"]:
          if "jobs" in data["rooms"][room_id]:
            for job in data["rooms"][room_id]["jobs"]:
              ret_value=False
              num_jobs+=1
              ret_value=check_long_yandex_job(log,room_id,data["rooms"][room_id]["jobs"],job)
              if ret_value==False:
                log.error("check_long_yandex_job(), room_id=%s, job_id=%s"%(room_id,job["id"]))
                result_string="error get result from yandex speech api - yandex api error"
                log.error(result_string)
                if send_notice(room_id,result_string)==False:
                  log.error("send_notice(%s)"%room_id)
        if num_jobs > 0:
          log.debug("len jobs list for all rooms = %d"%num_jobs)
      time.sleep(3)
  except Exception as e:
    log.error(get_exception_traceback_descr(e))
    log.error("exception at main loop check jobs: %s"%e)
    sys.exit(1)

  log.info("exit main loop")
Beispiel #17
0
def start(stdscr):
    global server, base_url, username, access_token, password
    global size, room, rooms, all_rooms, lastEventRoom, room_keys

    curses.curs_set(0)
    curses.use_default_colors()
    size = stdscr.getmaxyx()

    stdscr.addstr(0, 0, "loading...")
    stdscr.refresh()
    loadCredentials("./credentials.json")

    client = MatrixClient(base_url,
                          token=access_token,
                          user_id='@{}:{}'.format(username, server))
    if access_token is None:
        access_token = client.login_with_password(username, password, size[0])

    rooms = client.get_rooms()

    all_rooms = "all rooms"
    rooms[all_rooms] = all_rooms

    room_keys = list(rooms.keys())
    room = all_rooms

    curses.halfdelay(10)
    maxDisplayName = 24
    displayNamestartingPos = 20
    PAD_COMMENTS = True
    pause = False

    client.add_listener(processMessage)
    client.start_listener_thread()

    curses.echo()
    stdscr.keypad(True)
    inputBuffer = ""
    lastEventRoom = all_rooms
    the_room_to_post_to = None  # store the last room we saw before we started typing

    while (True):
        size = stdscr.getmaxyx()
        maxChars = size[1] - 1 - len(username) - 3

        stdscr.clear()

        line = "redpill v0.7"
        line += " · screen size: " + str(size)
        if isinstance(rooms[room], Room):
            line += " · chat size: " + str(len(rooms[room].events))
        line += " · room: "

        # we want NAME aka ALIAS[0] (ROOM_ID)
        # or 2nd choice: ALIAS[0] (ROOM_ID)
        # or fallback: ROOM_ID

        if room == all_rooms:
            line += str(room)
        elif rooms[room].name:
            if len(rooms[room].aliases) > 0 and rooms[room].aliases[0] != room:
                line += rooms[room].name + " aka " + getFirstRoomAlias(
                    rooms[room]) + " (" + str(room) + ")"
            elif rooms[room].name != room:
                line += rooms[room].name + " (" + str(room) + ")"
            else:
                line += str(room)
        elif len(rooms[room].aliases) > 0 and rooms[room].aliases[0] != room:
            line += rooms[room].aliases[0] + " (" + str(line) + ")"
        else:
            line += str(room)

        if isinstance(rooms[room], Room) and rooms[room].topic is not None:
            line += " · topic: " + rooms[room].topic

        line += " · variables: room: " + room + ", last: " + lastEventRoom

        stdscr.addstr(0, 0, line, curses.A_UNDERLINE)

        current = len(rooms[room].events) - 1 if isinstance(rooms[room],
                                                            Room) else -1

        if True:
            y = 1
            if current >= 0:

                # TODO: something when the first event is a typing event
                currentLine = size[0] - 1

                # input
                space = ""
                for i in range(size[1] - 1):
                    space += " "
                stdscr.addstr(currentLine, 0, space, curses.A_DIM)
                stdscr.addstr(currentLine, 0, "<" + username + ">",
                              curses.A_DIM)
                stdscr.addstr(currentLine - 1, 0, space, curses.A_UNDERLINE)

                for event in reversed(rooms[room].events):
                    log(event)
                    log("maxDisplayName: {}, size: {}, currentLine: {}".format(
                        maxDisplayName, size, currentLine))

                    if event["type"] == "m.typing":
                        #if True:
                        continue  # do something clever
                    elif event["type"] == "m.presence":
                        #if True:
                        continue  # do something clever

                    elif event["type"] == "m.roomchange":
                        room_id = event["room_id"]
                        #lin = (str(rooms[room_id].name) + " aka " + getFirstRoomAlias(rooms[room_id]) + " (" +
                        #    rooms[room_id].room_id + ")")
                        line = room_id
                        if line == all_rooms:
                            pass
                        elif rooms[line].name is None:
                            if len(
                                    rooms[room_id].aliases
                            ) > 0 and rooms[room_id].aliases[0] != room_id:
                                line = rooms[room_id].aliases[
                                    0] + " (" + line + ")"
                        else:
                            if len(
                                    rooms[room_id].aliases
                            ) > 0 and rooms[room_id].aliases[0] != room_id:
                                line = rooms[
                                    room_id].name + " aka " + getFirstRoomAlias(
                                        rooms[room_id]) + " (" + line + ")"
                            else:
                                if rooms[room_id].name != room_id:
                                    line = rooms[
                                        room_id].name + " (" + line + ")"

                        #if rooms[room].topic is not None:
                        #    line += " · topic: " + rooms[room].topic

                        currentLine -= 1
                        stdscr.addstr(currentLine, 0, "Event(s) from " + line,
                                      curses.A_DIM)

                    else:
                        #currentLine = size[0] - y
                        #currentLine -= 1

                        if currentLine < 3:  # how many lines we want to reserve
                            break
                        #if currentLine == 5:
                        #    currentLine -= 1
                        y += 1
                        if "origin_server_ts" in event:
                            convertedDate = datetime.datetime.fromtimestamp(
                                int(event["origin_server_ts"] /
                                    1000)).strftime('%Y-%m-%d %H:%M:%S')

                        # assumption: body == normal message
                        length = len(
                            event["sender"]) if "sender" in event else 0
                        log("length: {}, currentLine: {}".format(
                            length, currentLine))

                        if "body" in event["content"]:

                            rawText = event["content"][
                                "body"]  # .encode('utf-8')

                            if event["content"]["msgtype"] == "m.emote":
                                if len(rawText) > 0 and rawText[0] == " ":
                                    rawText = rawText[1:]

                            linesNeeded = 0

                            buf = ""
                            lineByLineText = []
                            first = True
                            bufSinceLastWord = ""
                            for char in rawText:
                                if True:  #for char in line:

                                    bufSinceLastWord += char

                                    if char == '\n':
                                        linesNeeded += 1
                                        buf += bufSinceLastWord

                                        if PAD_COMMENTS or first:
                                            linesNeeded += int(
                                                (displayNamestartingPos +
                                                 maxDisplayName + 3 + len(buf))
                                                / size[1])
                                        else:
                                            linesNeeded += int(
                                                len(buf) / size[1])

                                        first = False
                                        lineByLineText.append(buf)
                                        buf = ""
                                        bufSinceLastWord = ""
                                    else:
                                        if ((PAD_COMMENTS and
                                             (displayNamestartingPos +
                                              maxDisplayName + 3 +
                                              len(buf + bufSinceLastWord))
                                             == size[1] - 1) or
                                            (not PAD_COMMENTS and
                                             (len(buf + bufSinceLastWord))
                                             == size[1] - 1)):

                                            #if (displayNamestartingPos + maxDisplayName + 3 + len(buf + bufSinceLastWord)) == size[1] - 1:
                                            if len(buf) == 0:
                                                buf += bufSinceLastWord
                                                bufSinceLastWord = ""

                                            if char.isspace():
                                                buf += bufSinceLastWord
                                                lineByLineText.append(buf)
                                                bufSinceLastWord = ""
                                                buf = ""
                                            else:
                                                lineByLineText.append(buf)
                                                buf = bufSinceLastWord
                                                bufSinceLastWord = ""
                                            linesNeeded += 1

                                    if char.isspace():
                                        buf += bufSinceLastWord
                                        bufSinceLastWord = ""


#                                if (displayNamestartingPos + maxDisplayName + 3 + len(buf + bufSinceLastWord)) == size[1] - 1:
                                if ((PAD_COMMENTS and
                                     (displayNamestartingPos + maxDisplayName +
                                      3 + len(buf + bufSinceLastWord))
                                     == size[1] - 1)
                                        or (not PAD_COMMENTS and
                                            (len(buf + bufSinceLastWord))
                                            == size[1] - 1)):

                                    buf += bufSinceLastWord
                                    bufSinceLastWord = ""
                                    lineByLineText.append(buf)
                                    linesNeeded += 1
                                    buf = ""
                                    #elif char == ' ':   # skip all whitespace
                                    #    self.X += 1
                            buf += bufSinceLastWord
                            lineByLineText.append(buf)
                            linesNeeded += int(
                                (displayNamestartingPos + maxDisplayName + 3 +
                                 len(buf)) / size[1])
                            buf = ""

                            if currentLine - linesNeeded < 2:  # how many lines we want to reserve
                                break

                            if PAD_COMMENTS:
                                pad = displayNamestartingPos + maxDisplayName + 3

                                linesNeeded += 1
                                currentLine -= linesNeeded

                                for i in range(linesNeeded):
                                    buf = rawText[:size[1] - pad]
                                    rawText = rawText[size[1] - pad:]

                                    if currentLine + i == size[0] - 2:
                                        stdscr.addstr(
                                            currentLine + i,
                                            displayNamestartingPos +
                                            maxDisplayName + 3,
                                            lineByLineText[i],
                                            curses.A_BOLD + curses.A_UNDERLINE)
                                    else:
                                        try:
                                            stdscr.addstr(
                                                currentLine + i,
                                                displayNamestartingPos +
                                                maxDisplayName + 3,
                                                lineByLineText[i],
                                                curses.A_BOLD)
                                        except:
                                            e = sys.exc_info()[0]
                                            print(
                                                "Error: unable to start thread. "
                                                + str(e))
                                            stdscr.addstr(1, 0, str(e))

                            else:
                                # TODO: need to split this out to get proper underline

                                currentLine -= linesNeeded
                                if currentLine == size[0] - 2:
                                    stdscr.addstr(
                                        currentLine, displayNamestartingPos +
                                        maxDisplayName + 3, rawText,
                                        curses.A_BOLD + curses.A_UNDERLINE)
                                else:
                                    stdscr.addstr(
                                        currentLine, displayNamestartingPos +
                                        maxDisplayName + 3, rawText,
                                        curses.A_BOLD)

                            usern = event["sender"]

                            if length > maxDisplayName:
                                usern = usern[:maxDisplayName - 3] + "..."

                            if event["content"]["msgtype"] == "m.emote":
                                usern = "* " + usern
                                if currentLine == size[0] - 2:
                                    stdscr.addstr(
                                        currentLine, displayNamestartingPos +
                                        max(0, maxDisplayName - length),
                                        str(usern),
                                        curses.A_UNDERLINE + curses.A_BOLD)
                                else:
                                    stdscr.addstr(
                                        currentLine, displayNamestartingPos +
                                        max(0, maxDisplayName - length),
                                        str(usern), curses.A_BOLD)
                            else:
                                usern = "<" + usern + ">"
                                if currentLine == size[0] - 2:
                                    stdscr.addstr(
                                        currentLine, displayNamestartingPos +
                                        max(0, maxDisplayName - length),
                                        str(usern), curses.A_UNDERLINE)
                                else:
                                    stdscr.addstr(
                                        currentLine, displayNamestartingPos +
                                        max(0, maxDisplayName - length),
                                        str(usern))

                            if currentLine == size[0] - 2:
                                stdscr.addstr(currentLine, 0, convertedDate,
                                              curses.A_UNDERLINE)
                            else:
                                stdscr.addstr(currentLine, 0, convertedDate)

                            #if currentLine == size[1]:  # last line
                            #    stdscr.addstr(
                            #        currentLine, displayNamestartingPos +
                            #        maxDisplayName + 3, buf[:size[1] -
                            #        (displayNamestartingPos + maxDisplayName + 4)],
                            #         curses.A_BOLD
                            #    )
                            #else:
                            #    stdscr.addstr(
                            #        currentLine, displayNamestartingPos +
                            #        maxDisplayName + 3, buf,
                            #        curses.A_BOLD
                            #    )

                        # membership == join/leave events
                        elif "membership" in event["content"]:
                            buf = " invited someone"
                            if event["content"]["membership"] == "invite":
                                if "state_key" in event:
                                    buf = " invited " + event["state_key"]
                            elif event["content"]["membership"] == "join":
                                buf = " has joined"
                            elif event["content"]["membership"] == "leave":
                                buf = " has left"

                            currentLine -= 1
                            if length > maxDisplayName:
                                if currentLine == size[0] - 2:
                                    stdscr.addstr(
                                        currentLine,
                                        displayNamestartingPos + 1,
                                        str(event["sender"]),
                                        curses.A_DIM + curses.A_UNDERLINE)
                                    stdscr.addstr(
                                        currentLine,
                                        displayNamestartingPos + length + 1,
                                        buf, curses.A_DIM + curses.A_UNDERLINE)
                                else:
                                    stdscr.addstr(currentLine,
                                                  displayNamestartingPos + 1,
                                                  str(event["sender"]),
                                                  curses.A_DIM)
                                    stdscr.addstr(
                                        currentLine,
                                        displayNamestartingPos + length + 1,
                                        buf, curses.A_DIM)

                            else:
                                if currentLine == size[0] - 2:
                                    stdscr.addstr(
                                        currentLine, displayNamestartingPos +
                                        1 + maxDisplayName - length,
                                        str(event["sender"]),
                                        curses.A_DIM + curses.A_UNDERLINE)
                                    stdscr.addstr(
                                        currentLine, displayNamestartingPos +
                                        maxDisplayName + 1, buf,
                                        curses.A_DIM + curses.A_UNDERLINE)
                                else:
                                    stdscr.addstr(
                                        currentLine, displayNamestartingPos +
                                        1 + maxDisplayName - length,
                                        str(event["sender"]), curses.A_DIM)
                                    stdscr.addstr(
                                        currentLine, displayNamestartingPos +
                                        maxDisplayName + 1, buf, curses.A_DIM)

                    current -= 1
        if pause:
            stdscr.addstr(
                int(size[0] / 2) - 1, int(size[1] / 2), "          ",
                curses.A_REVERSE)
            stdscr.addstr(int(size[0] / 2), int(size[1] / 2), "  PAUSED  ",
                          curses.A_REVERSE)
            stdscr.addstr(
                int(size[0] / 2) + 1, int(size[1] / 2), "          ",
                curses.A_REVERSE)
        try:
            stdscr.addstr(size[0] - 1,
                          len(username) + 3, inputBuffer[-maxChars:])
        except:
            e = sys.exc_info()[0]
            print("Error: unable to start thread. " + str(e))
            stdscr.addstr(1, 0, str(e))

        stdscr.refresh()

        #       getInput(stdscr)

        #def getInput(stdscr):
        #   if True:
        try:

            c = stdscr.getch(size[0] - 1, len(username) + 3)
            #c = stdscr.getkey(size[0] - 1, len(username) + 3)

            #stri = stdscr.getstr(size[0] - 1, len(username) + 3, 10)
            if c == -1:
                stdscr.addstr(1, 0, "timeout")
            else:
                if c <= 256 and c != 10 and c != 9:  ## enter and tab
                    inputBuffer += chr(c)
                if len(inputBuffer) == 1:  # e.g. just started typing
                    if lastEventRoom != all_rooms:
                        the_room_to_post_to = lastEventRoom

            if c == 9:
                #stdscr.addstr(1, 0, "%s was pressed\n" % c)
                log("key pressed: 0x{:X}".format(c))
                room = room_keys[(room_keys.index(room) + 1) % len(rooms)]
                the_room_to_post_to = None
            elif c == 10:  # enter
                with open('redpill-sends.log', 'a') as the_file:
                    the_file.write("the_room_to_post_to:" +
                                   str(the_room_to_post_to) + "\n")
                    the_file.write("lastEventRoom: " + str(lastEventRoom) +
                                   "\n")
                    the_file.write("room: " + str(room) + "\n")
                    the_file.write("inputBuffer: " + str(inputBuffer) + "\n")
                    the_file.write("---\n")

                if inputBuffer.startswith("/invite"):
                    user_id = inputBuffer[7:].strip()
                    rooms[room].invite_user(user_id)
                elif inputBuffer.startswith("/kick"):
                    user_id = inputBuffer[5:].strip()
                    reason = "no reason..."
                    rooms[room].kick_user(user_id, reason)
                elif inputBuffer.startswith("/power"):
                    user_id = inputBuffer[7:].strip()
                    power_level = 50
                    rooms[room].set_power_level(user_id, power_level)
                elif inputBuffer.startswith("/op"):
                    user_id = inputBuffer[2:].strip()
                    rooms[room].set_power_level(user_id)
                elif inputBuffer.startswith("/ban"):  # reason
                    user_id = inputBuffer[4:].strip()
                    reason = "sux"  #FIXME
                    rooms[room].ban(user_id, reason)
                elif inputBuffer.startswith(
                        "/join"):  # there's a /join that supports aliases
                    room_alias = inputBuffer[5:].strip()
                    client.join_room(room_alias)
                elif inputBuffer.startswith("/j"):
                    room_alias = inputBuffer[2:].strip()
                    client.join_room(room_alias)
                elif inputBuffer.startswith("/leave"):
                    rooms[room].leave_room(room_id)
                elif inputBuffer.startswith("/create"):  # create a new room
                    is_public = True
                    invitees = ()
                    #     def create_room(self, alias=None, is_public=False, invitees=()):
                    room_alias = inputBuffer[7:].strip()
                    client.create_room(room_alias, is_public, invitees)
                elif inputBuffer.startswith("/topic"):  # get or set topic
                    new_topic = inputBuffer[6:].strip()
                    if len(new_topic) > 0:
                        rooms[room].topic = new_topic
                    else:
                        pass
                        #rooms[room].topic = "fail"
                else:
                    if room == all_rooms:
                        if the_room_to_post_to is None:
                            if lastEventRoom != all_rooms:
                                the_room_to_post_to = lastEventRoom
                            else:
                                stdscr.addstr(1, 0,
                                              "No idea what room to post to!")
                                stdscr.refresh()
                                inputBuffer = "No idea what room to post to!"
                                continue
                    else:
                        the_room_to_post_to = room

                    if inputBuffer.startswith("/me"):
                        rooms[the_room_to_post_to].send_emote(inputBuffer[3:])
                    else:
                        rooms[the_room_to_post_to].send_text(inputBuffer)

                inputBuffer = ""
                the_room_to_post_to = None
            elif c == curses.KEY_DC:
                inputBuffer = ""
                the_room_to_post_to = None
            elif c == curses.KEY_BACKSPACE:
                if len(inputBuffer) > 0:
                    inputBuffer = inputBuffer[:-1]
                if len(inputBuffer) == 0:
                    the_room_to_post_to = None
            elif c == curses.KEY_IC:
                pause = not (pause)
                if pause:
                    curses.nocbreak()
                    curses.cbreak()
                    stdscr.timeout(-1)
                    stdscr.addstr(
                        int(size[0] / 2) - 1, int(size[1] / 2), "          ",
                        curses.A_REVERSE)
                    stdscr.addstr(int(size[0] / 2), int(size[1] / 2),
                                  " PAUSING  ", curses.A_REVERSE)
                    stdscr.addstr(
                        int(size[0] / 2) + 1, int(size[1] / 2), "          ",
                        curses.A_REVERSE)
                    stdscr.refresh()
                else:
                    stdscr.addstr(
                        int(size[0] / 2) - 1, int(size[1] / 2), "          ",
                        curses.A_REVERSE)
                    stdscr.addstr(int(size[0] / 2), int(size[1] / 2),
                                  " RESUMING ", curses.A_REVERSE)
                    stdscr.addstr(
                        int(size[0] / 2) + 1, int(size[1] / 2), "          ",
                        curses.A_REVERSE)
                    stdscr.refresh()
                    curses.halfdelay(10)
                    stdscr.timeout(1)
            elif c == 27:  # need to test for alt combo or ESC
                curses.cbreak()
                curses.echo()
                #curses.curs_set(1)
                stdscr.keypad(0)
                curses.endwin()
                quit()
            elif c == curses.KEY_F2:
                PAD_COMMENTS = not PAD_COMMENTS

            #stdscr.addstr(2, 0, "time() == %s\n" % time.time())

        finally:
            do_nothing = True
Beispiel #18
0
class MatrixBackend(ErrBot):
    def __init__(self, config):
        super().__init__(config)
        identity = config.BOT_IDENTITY
        self.token = identity["token"]
        self.url = identity["url"]
        self.user = identity["user"]
        self._client = None

    def build_identifier(self, text_representation: str) -> None:
        """Return an object that idenfifies a matrix person or room."""
        pass

    @staticmethod
    def parse_identfier_pieces(regex: str, text_rep: str):
        m = re.match(regex, text_rep)
        if m:
            data, domain = m.group()
            return data, domain
        return None, None

    @staticmethod
    def parse_identfier(text_rep):
        """Parse matrix identifiers into usable types.
        Expected formats are as follows:
        !<room>:<domain>
        #<room>:<domain>
        @<user>:<domain>
        """

        room, domain, user = None, None, None

        room, domain = MatrixBackend.parse_identfier_pieces(
            r"[!#](.*):(.*)", text_rep)
        if not room or not domain:
            user, domain = MatrixBackend.parse_identfier_pieces(
                r"@:(.*):(.*)", text_rep)

        return room, domain, user

    def send_msg(self, room_id, msg):
        room = self._client.join_room(room_id)
        return room.send_text(msg)

    def send_file(self, room_id, file_path, filename):
        with open(file_path, "rb") as f:
            content = f.read()

        return self.send_stream_content(room_id, content, filename)

    def send_stream_content(self, room_id, content, filename):
        res = self._client.upload(content, 'application/octet-stream',
                                  filename)
        room = self._client.join_room(room_id)
        room.send_file(res, filename)
        return res

    def build_reply(self, msg, text=None, private=False, threaded=False):
        response = self.build_message(text)
        response.frm = self.bot_identifier
        self.send_msg(msg.extras['room_id'], text)
        if private:
            response.to = msg.frm
        else:
            response.to = msg.frm.room if isinstance(msg.frm,
                                                     RoomOccupant) else msg.frm
        return response

    def change_presence(self):
        pass

    def mode(self):
        pass

    def query_room(self):
        pass

    def rooms(self):
        pass

    def invite_callback(self, *args, **kwargs):
        print(args, kwargs)

    def ephemeral_callback(self, *args, **kwargs):
        print(args, kwargs)

    def leave_callback(self, *args, **kwargs):
        print(args, kwargs)

    def presence_callback(self, *args, **kwargs):
        print(args, kwargs)

    def callback(self, *events):
        for event in events:
            log.debug("Saw event %s.", event)
            if event["type"] == "m.room.message":
                content = event["content"]
                sender = event["sender"]
                if content["msgtype"] == "m.text":
                    msg = Message(content["body"],
                                  extras={'room_id': event["room_id"]})
                    msg.frm = MatrixPerson(self._client, sender)
                    msg.to = self.bot_identifier
                    self.callback_message(msg)

    def serve_once(self):
        self._client = MatrixClient(self.url,
                                    token=self.token,
                                    user_id=self.user)
        self._client.add_listener(self.callback)
        self._client.add_invite_listener(self.invite_callback)
        self._client.add_ephemeral_listener(self.ephemeral_callback)
        self._client.add_leave_listener(self.leave_callback)
        self._client.add_presence_listener(self.presence_callback)
        self.connect_callback()

        self.bot_identifier = MatrixPerson(self._client, self.user)

        self._client.listen_forever()
Beispiel #19
0
class JokeBot:
    bot_startcmd = '!joke'
    bot_display_name = 'JokeBot'
    auto_join_invited_rooms = True
    mcl = None
    init_done = False
    admin_ids = set()

    def __init__(self, filename=CONFIG_FILENAME):
        logging.debug('load config')
        config_dic = load_yaml_config(filename)
        matrix_server = config_dic['matrix_server']
        login_with_token = False
        if matrix_server.get('token', ''):
            if not matrix_server.get('user_id', ''):
                matrix_server['user_id'] = config_dic['matrix_user'][
                    'username']
            login_with_token = True
        else:
            matrix_user = config_dic['matrix_user']

        bot_startcmd = config_dic.get('bot_startcmd')
        if bot_startcmd:
            self.bot_startcmd = bot_startcmd
        bot_display_name = config_dic.get('bot_display_name')
        if bot_display_name:
            self.bot_display_name = bot_display_name
        self.auto_join_invited_rooms = config_dic.get(
            'auto_join_invited_rooms', True)
        self.admin_ids = set(config_dic.get('admin_ids', []))

        logging.debug('init bot')

        if login_with_token:
            logging.debug('init bot with token')
            self.mcl = MatrixClient(**matrix_server)
        else:
            logging.debug('init bot with password')
            self.mcl = MatrixClient(**matrix_server)
            self.mcl.login_with_password_no_sync(**matrix_user)

        m_user = self.mcl.get_user(self.mcl.user_id)
        if m_user.get_display_name() != self.bot_display_name:
            m_user.set_display_name(self.bot_display_name)

        self.mcl.add_invite_listener(self.process_invite)
        self.mcl.add_listener(self.process_message, 'm.room.message')

        self.init_done = True
        logging.info('bot initialization successful')

    def run(self):
        if self.init_done:
            logging.debug('run listen_forever')
            self.mcl.listen_forever()
        else:
            logging.warning('bot not initialized successful')

    def join_room(self, room_id):
        self.ignore_room_temporary(
            room_id
        )  # necessary while joining room because otherwise old messages would be processed
        try:
            logging.info('joining new room {}'.format(room_id))
            room = self.mcl.join_room(room_id)
            room.send_text(
                "Welcome! I'm a joke bot. Type '{}' and I will tell you a joke."
                .format(self.bot_startcmd))
            return True
        except:
            logging.exception(
                'Exception while joining room {}'.format(room_id))
            return False

    temp_ignored_rooms = set()

    def temp_ignore_room_thread(self, room_id):
        logging.debug('temporary ignoring room {}'.format(room_id))
        self.temp_ignored_rooms.add(room_id)
        time.sleep(10)
        self.temp_ignored_rooms.remove(room_id)
        logging.debug('not ignoring room {} any more'.format(room_id))

    def ignore_room_temporary(self, room_id):
        threading.Thread(target=self.temp_ignore_room_thread,
                         args=[room_id],
                         daemon=True).start()

    def leave_room(self, room_id):
        logging.debug('trying to leave room with id {}'.format(room_id))
        leave_room = self.mcl.get_rooms().get(room_id, '')
        if not leave_room:
            logging.debug('bot not in room with id {}'.format(room_id))
            return False
        if leave_room.leave():
            logging.debug('leaving room {} was successful'.format(room_id))
            return True
        else:
            logging.debug('failed to leave known room with id {}'.format(
                leave_room.room_id))
        return False

    def process_invite(self, room_id, state=None):
        logging.debug('received invitation of {}'.format(room_id))
        if self.auto_join_invited_rooms:
            self.join_room(room_id)

    def evaluate_bot_message(self, room, sender, msg):
        if msg.startswith('ctl'):
            logging.debug("received control message '{}' in room '{}'".format(
                msg, room.room_id))
            if sender not in self.admin_ids:
                logging.debug(
                    '{} has no permissions to send a ctl-message'.format(
                        sender))
                room.send_notice(
                    '{} has no permissions to send a ctl-message'.format(
                        sender))
                return
            data = msg.split(' ')[1:]
            if len(data) == 2:
                if data[0] == 'join':
                    if not self.join_room(data[1]):
                        room.send_notice(
                            'something went wrong while joining room')
                elif data[0] == 'leave':
                    if data[1] == 'this':
                        data[1] = room.room_id
                    if not self.leave_room(data[1]):
                        room.send_notice('room could not be left')
            return

        logging.info('sending joke to room {}'.format(room.room_id))
        answer = '...'
        data = msg.split(' ')[1:]  # remove first empty string
        if len(data) == 0:
            answer = get_joke()
        elif len(data) == 1:
            answer = get_joke(data[0])
        elif len(data) == 2:
            answer = get_joke(data[0], data[1])
        logging.debug('starting room send text')
        room.send_text(answer)
        logging.debug('done room send text')

    def process_message(self, roomchunk):
        if roomchunk['sender'] == self.mcl.user_id:
            return

        if roomchunk['room_id'] in self.temp_ignored_rooms:
            logging.debug('ignoring room {} temporary'.format(
                roomchunk['room_id']))
            return

        content = roomchunk['content']
        if content['msgtype'] == 'm.text':
            msg = content['body']
            if msg.startswith(self.bot_startcmd):
                room = self.mcl.get_rooms()[roomchunk['room_id']]
                msg = msg[len(self.bot_startcmd):]
                self.evaluate_bot_message(room, roomchunk['sender'], msg)
Beispiel #20
0
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:
                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")
Beispiel #21
0
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()
Beispiel #22
0
class Matrix():
    def __init__(self):
        self.rooms = {}
        self.matrixClient = {}

    def login(self):
        global theBot
        config = bot.theBot.config

        #variables
        self.matrixClient = MatrixClient(config.matrix["clienturl"])

        if config.matrix["password"] == "":
            config.matrix["password"] = getpass.getpass(prompt='Password: '******'t find room:"+room)
                    sys.exit(12)

    def handleException( self, exception ):
        print ( exception )
        print ("An Error occured, ignoring")
        return

    def listen(self):
        global theBot
        config = bot.theBot.config

        #if success, start the command listener.
        for i in self.rooms:
            self.rooms[i].add_listener( self.listener )
        self.matrixClient.add_listener( self.clientEvent );
        self.matrixClient.add_leave_listener( self.leftRoom );
        self.matrixClient.start_listener_thread( exception_handler = self.handleException )

    def clientEvent( self, event ):
        if event["type"] == "m.room.power_levels":
            print("Damn it Hellbacon!")

    def leftRoom(self, room_id, event): #room_id, not to be mistaken for room name.
        #this shouldn't happen, so rejoin.
        room = ""
        time.sleep(10)
        try:
            for i in self.rooms:
                if self.rooms[i].room_id == room_id:
                    room = i
                    self.rooms[room].leave();#remove the room, before rejoining.
                    self.rooms[room] = self.matrixClient.join_room(room)
                    self.rooms[room].add_listener( self.listener )

        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:"+room)
                sys.exit(12)

    def listener(self, room, event):
        global theBot
        config = bot.theBot.config

        if event['type'] == "m.room.message":
            if event['content']['msgtype'] == "m.text":
                #print("{0}: {1}".format(event['sender'], event['content']['body']))

                # ignore anything the bot might send to itself
                if(event['sender'] == "@"+config.matrix["username"]+":cclub.cs.wmich.edu"):
                    return

                #built in auto response to mention.
                if ( config.matrix["username"] + ":" in event['content']['body']):
                    room.send_text("Hi! I am a bot. If you want to know my commands type \""+config.prefix+"commands\" for available commands")

                # split the string to commands
                input = event['content']['body'].split(" ")

                # create responses for messages starting with prefix
                if len(input) > 0:
                    if len(input[0]) > 0:
                        ep = commandcenter.EventPackage()
                        ep.body = input
                        ep.room_id = event["room_id"]
                        ep.sender = event["sender"]
                        ep.event = event
                        ep.command = input[0]
                        if ep.command == config.prefix+"purge":
                            self.purge(ep)
                        else:
                            output = bot.theBot.cc.run(ep)

                            # if the command is in our dictionary of functions, use it (from commands.py)
                            if len(output) > 0:
                                room.send_text(output)

        else:
            print(event['type'])

    def purge(self, event ):
        return