diff --git a/main.py b/main.py index cc6753b..fbcdeaf 100644 --- a/main.py +++ b/main.py @@ -43,8 +43,8 @@ from matrix.utf import utf8_decode # Weechat searches for the registered callbacks in the scope of the main script # file, import the callbacks here so weechat can find them. from matrix.commands import (hook_commands, hook_page_up, matrix_command_cb, - matrix_topic_command_cb, matrix_command_join_cb, - matrix_command_part_cb, matrix_invite_command_cb, + matrix_topic_command_cb, matrix_join_command_cb, + matrix_part_command_cb, matrix_invite_command_cb, matrix_command_pgup_cb, matrix_redact_command_cb, matrix_command_buf_clear_cb, matrix_me_command_cb, matrix_kick_command_cb) diff --git a/matrix/buffer.py b/matrix/buffer.py index b9f3dd7..d346ba4 100644 --- a/matrix/buffer.py +++ b/matrix/buffer.py @@ -783,6 +783,8 @@ class RoomBuffer(object): def __init__(self, room, server_name): self.room = room self.backlog_pending = False + self.joined = True + self.leave_event_id = None # type: Optional[str] buffer_name = "{}.{}".format(room.room_id, server_name) @@ -854,6 +856,12 @@ class RoomBuffer(object): if event.state_key in self.displayed_nicks: del self.displayed_nicks[event.state_key] + # We left the room, remember the event id of our leave, if we + # rejoin we get events that came before this event as well as + # after our leave, this way we know where to continue + if event.state_key == self.room.own_user_id: + self.leave_event_id = event.event_id + elif event.content["membership"] == "invite": if is_state: return @@ -1200,6 +1208,44 @@ class RoomBuffer(object): self.sort_messages() + def handle_joined_room(self, info): + for event in info.state: + self.handle_state_event(event) + + timeline_events = None + + # This is a rejoin, skip already handled events + if not self.joined: + leave_index = None + + for i, event in enumerate(info.timeline.events): + if event.event_id == self.leave_event_id: + leave_index = i + break + + if leave_index: + timeline_events = info.timeline.events[leave_index:] + else: + timeline_events = info.timeline.events + + # mark that we are now joined + self.joined = True + + else: + timeline_events = info.timeline.events + + for event in timeline_events: + self.handle_timeline_event(event) + + def handle_left_room(self, info): + self.joined = False + + for event in info.state: + self.handle_state_event(event) + + for event in info.timeline.events: + self.handle_timeline_event(event) + def error(self, string): # type: (str) -> None self.weechat_buffer.error(string) diff --git a/matrix/commands.py b/matrix/commands.py index 021f827..51b2707 100644 --- a/matrix/commands.py +++ b/matrix/commands.py @@ -79,6 +79,18 @@ class WeechatCommandParser(object): return WeechatCommandParser._run_parser(parser, args) + @staticmethod + def join(args): + parser = WeechatArgParse(prog="join") + parser.add_argument("room_id") + return WeechatCommandParser._run_parser(parser, args) + + @staticmethod + def part(args): + parser = WeechatArgParse(prog="part") + parser.add_argument("room_id", nargs="?") + return WeechatCommandParser._run_parser(parser, args) + def hook_commands(): W.hook_command( @@ -185,9 +197,34 @@ def hook_commands(): "matrix_invite_command_cb", "") - # TODO those should be hook_command() calls - # W.hook_command_run('/join', 'matrix_command_join_cb', '') - # W.hook_command_run('/part', 'matrix_command_part_cb', '') + W.hook_command( + # Command name and short description + "join", + "join a room", + # Synopsis + ("|"), + # Description + (" room-id: room-id of the room to join\n" + "room-alias: room alias of the room to join"), + # Completions + "", + # Callback + "matrix_join_command_cb", + "") + + W.hook_command( + # Command name and short description + "part", + "leave a room", + # Synopsis + ("[]"), + # Description + (" room-name: room name of the room to leave"), + # Completions + "", + # Callback + "matrix_part_command_cb", + "") W.hook_command_run('/buffer clear', 'matrix_command_buf_clear_cb', '') @@ -320,64 +357,41 @@ def matrix_command_pgup_cb(data, buffer, command): @utf8_decode -def matrix_command_join_cb(data, buffer, command): - - def join(server, args): - split_args = args.split(" ", 1) - - # TODO handle join for non public rooms - if len(split_args) != 2: - message = ("{prefix}Error with command \"/join\" (help on " - "command: /help join)").format(prefix=W.prefix("error")) - W.prnt("", message) - return - - _, room_id = split_args - - raise NotImplementedError +def matrix_join_command_cb(data, buffer, args): + parsed_args = WeechatCommandParser.join(args) + if not parsed_args: + return W.WEECHAT_RC_OK for server in SERVERS.values(): - if buffer in server.buffers.values(): - join(server, command) - return W.WEECHAT_RC_OK_EAT - elif buffer == server.server_buffer: - join(server, command) - return W.WEECHAT_RC_OK_EAT + if buffer in server.buffers.values() or buffer == server.server_buffer: + server.room_join(parsed_args.room_id) + break return W.WEECHAT_RC_OK @utf8_decode -def matrix_command_part_cb(data, buffer, command): - - def part(server, buffer, args): - rooms = [] - - split_args = args.split(" ", 1) - - if len(split_args) == 1: - if buffer == server.server_buffer: - message = ( - "{prefix}Error with command \"/part\" (help on " - "command: /help part)").format(prefix=W.prefix("error")) - W.prnt("", message) - return - - rooms = [key_from_value(server.buffers, buffer)] - - else: - _, rooms = split_args - rooms = rooms.split(" ") - - raise NotImplementedError +def matrix_part_command_cb(data, buffer, args): + parsed_args = WeechatCommandParser.part(args) + if not parsed_args: + return W.WEECHAT_RC_OK for server in SERVERS.values(): - if buffer in server.buffers.values(): - part(server, buffer, command) - return W.WEECHAT_RC_OK_EAT - elif buffer == server.server_buffer: - part(server, buffer, command) - return W.WEECHAT_RC_OK_EAT + if buffer in server.buffers.values() or buffer == server.server_buffer: + room_id = parsed_args.room_id + + if not room_id: + if buffer == server.server_buffer: + server.error("command \"part\" must be " + "executed on a Matrix room buffer or a room " + "name needs to be given") + return W.WEECHAT_RC_OK + + room_buffer = server.find_room_from_ptr(buffer) + room_id = room_buffer.room.room_id + + server.room_leave(room_id) + break return W.WEECHAT_RC_OK @@ -736,14 +750,14 @@ def matrix_server_command_delete(args): def matrix_server_command_add(args): if len(args) < 2: message = ("{prefix}matrix: Too few arguments for command " - "\"/matrix server add\" (see /matrix help server)" - ).format(prefix=W.prefix("error")) + "\"/matrix server add\" (see /matrix help server)").format( + prefix=W.prefix("error")) W.prnt("", message) return elif len(args) > 4: message = ("{prefix}matrix: Too many arguments for command " "\"/matrix server add\" (see /matrix help server)" - ).format(prefix=W.prefix("error")) + ).format(prefix=W.prefix("error")) W.prnt("", message) return @@ -865,7 +879,6 @@ def matrix_server_command_add(args): def matrix_server_command(command, args): - def list_servers(_): if SERVERS: W.prnt("", "\nAll matrix servers:") @@ -892,7 +905,6 @@ def matrix_server_command(command, args): @utf8_decode def matrix_command_cb(data, buffer, args): - def connect_server(args): for server_name in args: if check_server_existence(server_name, SERVERS): @@ -913,7 +925,8 @@ def matrix_command_cb(data, buffer, args): if len(split_args) < 1: message = ("{prefix}matrix: Too few arguments for command " - "\"/matrix\" (see /help matrix)").format(prefix=W.prefix("error")) + "\"/matrix\" " + "(see /help matrix)").format(prefix=W.prefix("error")) W.prnt("", message) return W.WEECHAT_RC_ERROR diff --git a/matrix/server.py b/matrix/server.py index 0ca08c0..1661b38 100644 --- a/matrix/server.py +++ b/matrix/server.py @@ -528,6 +528,14 @@ class MatrixServer(object): user_id) self.send_or_queue(request) + def room_join(self, room_id): + _, request = self.client.join(room_id) + self.send_or_queue(request) + + def room_leave(self, room_id): + _, request = self.client.room_leave(room_id) + self.send_or_queue(request) + def room_send_message(self, room_buffer, formatted, msgtype="m.text"): # type: (RoomBuffer, Formatted, str) -> None if room_buffer.room.encrypted: @@ -617,17 +625,19 @@ class MatrixServer(object): self.sync(timeout=0, filter=sync_filter) def _handle_room_info(self, response): - for room_id, join_info in response.rooms.join.items(): + for room_id, info in response.rooms.leave.items(): + if room_id not in self.buffers: + continue + + room_buffer = self.find_room_from_id(room_id) + room_buffer.handle_left_room(info) + + for room_id, info in response.rooms.join.items(): if room_id not in self.buffers: self.create_room_buffer(room_id) room_buffer = self.find_room_from_id(room_id) - - for event in join_info.state: - room_buffer.handle_state_event(event) - - for event in join_info.timeline.events: - room_buffer.handle_timeline_event(event) + room_buffer.handle_joined_room(info) def _handle_sync(self, response): # we got the same batch again, nothing to do