matrix: Change the way responses and events are executed.

This commit is contained in:
poljar (Damir Jelić) 2018-07-05 15:13:19 +02:00
parent e7208ded62
commit 38d6a14a33
6 changed files with 218 additions and 132 deletions

View file

@ -46,6 +46,7 @@ from matrix.commands import (hook_commands, hook_page_up, matrix_command_cb,
matrix_command_pgup_cb, matrix_redact_command_cb,
matrix_command_buf_clear_cb, matrix_me_command_cb,
matrix_command_kick_cb)
from matrix.buffer import room_buffer_input_cb, room_buffer_close_cb
from matrix.server import (
MatrixServer,

View file

@ -28,7 +28,7 @@ from builtins import super
@utf8_decode
def room_buffer_input_cb(server_name, buffer, input_data):
server = SERVERS[server_name]
room, room_buffer = server.find_room(buffer)
room, room_buffer = server.find_room_from_ptr(buffer)
if not room_buffer:
# TODO log error
@ -40,9 +40,7 @@ def room_buffer_input_cb(server_name, buffer, input_data):
formatted_data = Formatted.from_input_line(input_data)
if room.encrypted:
server.send_room_message(room.id, formatted_data)
return W.WEECHAT_RC_OK
server.send_room_message(room, formatted_data)
return W.WEECHAT_RC_OK
@ -86,6 +84,13 @@ class WeechatChannelBuffer(object):
"notify_message",
"log1"
],
"self_message": [
SCRIPT_NAME + "_message",
"notify_none",
"no_highlight",
"self_msg",
"log1"
],
"old_message": [
SCRIPT_NAME + "_message",
"notify_message",
@ -107,6 +112,10 @@ class WeechatChannelBuffer(object):
"invite": [
SCRIPT_NAME + "_invite",
"log4"
],
"topic": [
SCRIPT_NAME + "_topic",
"log3",
]
}
@ -123,13 +132,17 @@ class WeechatChannelBuffer(object):
name,
"room_buffer_input_cb",
server_name,
"room_buffer_input_cb",
"room_buffer_close_cb",
server_name,
)
self.name = ""
self.users = {} # type: Dict[str, RoomUser]
self.topic = ""
self.topic_author = ""
self.topic_date = None
W.buffer_set(self._ptr, "localvar_set_type", 'channel')
W.buffer_set(self._ptr, "type", 'formatted')
@ -216,7 +229,7 @@ class WeechatChannelBuffer(object):
def _message_tags(self, user, message_type):
# type: (str, RoomUser, str) -> List[str]
tags = self.tags[message_type].copy()
tags = list(self.tags[message_type])
tags.append("nick_{nick}".format(nick=user.nick))
@ -235,10 +248,10 @@ class WeechatChannelBuffer(object):
# A message from a non joined user
return RoomUser(nick)
def message(self, nick, message, date):
def message(self, nick, message, date, tags=[]):
# type: (str, str, int, str) -> None
user = self._get_user(nick)
tags = self._message_tags(user, "message")
tags = tags or self._message_tags(user, "message")
prefix_string = ("" if not user.prefix else "{}{}{}".format(
W.color(self._get_prefix_color(user.prefix)),
@ -264,10 +277,10 @@ class WeechatChannelBuffer(object):
self.message(nick, data, date)
def action(self, nick, message, date):
def action(self, nick, message, date, tags=[]):
# type: (str, str, int) -> None
user = self._get_user(nick)
tags = self._message_tags(user, "action")
tags = tags or self._message_tags(user, "action")
nick_prefix = ("" if not user.prefix else "{}{}{}".format(
W.color(self._get_prefix_color(user.prefix)),
@ -282,7 +295,7 @@ class WeechatChannelBuffer(object):
nick_color=W.color(user.color),
author=nick,
ncolor=W.color("reset"),
msg=self.message)
msg=message)
self.print_date_tags(data, date, tags)
@ -293,9 +306,9 @@ class WeechatChannelBuffer(object):
if user.prefix == "&":
group_name = "000|o"
elif user.power_level == "@":
elif user.prefix == "@":
group_name = "001|h"
elif user.power_level > "+":
elif user.prefix > "+":
group_name = "002|v"
return group_name
@ -400,3 +413,41 @@ class WeechatChannelBuffer(object):
def kick(self, user, date, message=True, extra_tags=[]):
# type: (WeechatUser, int, Optional[bool], Optional[List[str]]) -> None
self._leave(user, date, message, "kick", extra_tags=[])
def _print_topic(self, nick, topic, date):
user = self._get_user(nick)
tags = self._message_tags(user, "topic")
data = ("{prefix}{nick} has changed "
"the topic for {chan_color}{room}{ncolor} "
"to \"{topic}\"").format(
prefix=W.prefix("network"),
nick=user.nick,
chan_color=W.color("chat_channel"),
ncolor=W.color("reset"),
room=self.name,
topic=topic
)
self.print_date_tags(data, date, tags)
def topic(self, nick, topic, date, message=True):
W.buffer_set(self._ptr, "title", topic)
if message:
self._print_topic(nick, topic, date)
self.topic = topic
self.topic_author = nick
self.topic_date = date
def self_message(self, nick, message, date):
user = self._get_user(nick)
tags = self._message_tags(user, "self_message")
self.message(nick, message, date, tags)
def self_action(self, nick, message, date):
user = self._get_user(nick)
tags = self._message_tags(user, "self_message")
tags.append(SCRIPT_NAME + "_action")
self.action(nick, message, date, tags)

View file

@ -74,7 +74,8 @@ class WeechatArgParse(argparse.ArgumentParser):
def error(self, message):
m = ("{prefix}Error: {message} for command {command} "
"(see /help {command})").format(prefix=W.prefix("error"),
message=message, command=self.prog)
message=message,
command=self.prog)
W.prnt("", m)
raise ParseError

View file

@ -27,11 +27,12 @@ from operator import itemgetter
from matrix.globals import W
from matrix.utils import (tags_for_message, sanitize_id, sanitize_token,
sanitize_text, tags_from_line_data)
from matrix.rooms import (matrix_create_room_buffer, RoomInfo, RoomMessageText,
from matrix.rooms import (RoomInfo, RoomMessageText,
RoomMessageEvent, RoomRedactedMessageEvent,
RoomMessageEmote)
from matrix.encryption import OlmDeviceKey, OneTimeKey
from .buffer import RoomUser
try:
from olm.session import OlmMessage, OlmPreKeyMessage
@ -657,7 +658,7 @@ class MatrixSyncEvent(MatrixEvent):
info = self.joined_room_infos.pop()
if info.room_id not in server.buffers:
matrix_create_room_buffer(server, info.room_id)
server.create_room_buffer(info.room_id)
room = server.rooms[info.room_id]

View file

@ -156,45 +156,15 @@ class MatrixUser:
# yapf: enable
def matrix_create_room_buffer(server, room_id):
# type: (MatrixServer, str) -> None
buf = W.buffer_new(room_id, "room_input_cb", server.name, "room_close_cb",
server.name)
W.buffer_set(buf, "localvar_set_type", 'channel')
W.buffer_set(buf, "type", 'formatted')
W.buffer_set(buf, "localvar_set_channel", room_id)
W.buffer_set(buf, "localvar_set_nick", server.user)
W.buffer_set(buf, "localvar_set_server", server.name)
short_name = strip_matrix_server(room_id)
W.buffer_set(buf, "short_name", short_name)
W.nicklist_add_group(buf, '', "000|o", "weechat.color.nicklist_group", 1)
W.nicklist_add_group(buf, '', "001|h", "weechat.color.nicklist_group", 1)
W.nicklist_add_group(buf, '', "002|v", "weechat.color.nicklist_group", 1)
W.nicklist_add_group(buf, '', "999|...", "weechat.color.nicklist_group", 1)
W.buffer_set(buf, "nicklist", "1")
W.buffer_set(buf, "nicklist_display_groups", "0")
# TODO make this configurable
W.buffer_set(buf, "highlight_tags_restrict", "matrix_message")
server.buffers[room_id] = buf
server.rooms[room_id] = MatrixRoom(room_id)
class RoomInfo():
def __init__(self, room_id, prev_batch, events):
def __init__(self, room_id, prev_batch, state, timeline):
# type: (str, str, List[Any], List[Any]) -> None
self.room_id = room_id
self.prev_batch = prev_batch
self.events = deque(events)
self.state = deque(state)
self.timeline = deque(timeline)
@staticmethod
def _message_from_event(event):
@ -219,66 +189,40 @@ class RoomInfo():
raise ValueError
if event_dict["content"]["membership"] == "join":
event = RoomMemberJoin.from_dict(event_dict)
try:
message = RoomMembershipMessage(
event.event_id, event.sender, event.timestamp,
"has joined", "join")
return event, message
except AttributeError:
return event, None
return RoomMemberJoin.from_dict(event_dict)
elif event_dict["content"]["membership"] == "leave":
event = RoomMemberLeave.from_dict(event_dict)
return RoomMemberLeave.from_dict(event_dict)
try:
msg = ("has left" if event.sender == event.leaving_user else
"has been kicked")
message = RoomMembershipMessage(
event.event_id, event.leaving_user, event.timestamp, msg, "quit")
return event, message
except AttributeError:
return event, None
return None, None
return None
@staticmethod
def parse_event(olm, room_id, event_dict):
# type: (Dict[Any, Any]) -> (RoomEvent, RoomEvent)
state_event = None
message_event = None
event = None
if "redacted_by" in event_dict["unsigned"]:
message_event = RoomRedactedMessageEvent.from_dict(event_dict)
event = RoomRedactedMessageEvent.from_dict(event_dict)
elif event_dict["type"] == "m.room.message":
message_event = RoomInfo._message_from_event(event_dict)
event = RoomInfo._message_from_event(event_dict)
elif event_dict["type"] == "m.room.member":
state_event, message_event = (
RoomInfo._membership_from_dict(event_dict))
event = RoomInfo._membership_from_dict(event_dict)
elif event_dict["type"] == "m.room.power_levels":
state_event = RoomPowerLevels.from_dict(event_dict)
event = RoomPowerLevels.from_dict(event_dict)
elif event_dict["type"] == "m.room.topic":
state_event = RoomTopicEvent.from_dict(event_dict)
message_event = RoomTopiceMessage(
state_event.event_id,
state_event.sender,
state_event.timestamp,
state_event.topic)
event = RoomTopicEvent.from_dict(event_dict)
elif event_dict["type"] == "m.room.redaction":
message_event = RoomRedactionEvent.from_dict(event_dict)
event = RoomRedactionEvent.from_dict(event_dict)
elif event_dict["type"] == "m.room.name":
state_event = RoomNameEvent.from_dict(event_dict)
event = RoomNameEvent.from_dict(event_dict)
elif event_dict["type"] == "m.room.canonical_alias":
state_event = RoomAliasEvent.from_dict(event_dict)
event = RoomAliasEvent.from_dict(event_dict)
elif event_dict["type"] == "m.room.encryption":
state_event = RoomEncryptionEvent.from_dict(event_dict)
event = RoomEncryptionEvent.from_dict(event_dict)
elif event_dict["type"] == "m.room.encrypted":
state_event, message_event = RoomInfo._decrypt_event(olm, room_id,
event_dict)
event = RoomInfo._decrypt_event(olm, room_id, event_dict)
return state_event, message_event
return event
@staticmethod
def _decrypt_event(olm, room_id, event_dict):
@ -287,7 +231,7 @@ class RoomInfo():
plaintext = olm.group_decrypt(room_id, session_id, ciphertext)
if not plaintext:
return None, None
return None
parsed_plaintext = json.loads(plaintext, encoding="utf-8")
@ -297,18 +241,13 @@ class RoomInfo():
return RoomInfo.parse_event(olm, room_id, event_dict)
@staticmethod
def _parse_events(olm, room_id, parsed_dict, messages=True, state=True):
state_events = []
message_events = []
if not messages and not state:
return []
def _parse_events(olm, room_id, parsed_dict):
events = []
try:
for event in parsed_dict:
m_event, s_event = RoomInfo.parse_event(olm, room_id, event)
state_events.append(m_event)
message_events.append(s_event)
e = RoomInfo.parse_event(olm, room_id, event)
events.append(e)
except (ValueError, TypeError, KeyError) as error:
message = ("{prefix}matrix: Error parsing "
"room event of type {type}: {error}\n{event}").format(
@ -319,14 +258,6 @@ class RoomInfo():
W.prnt("", message)
raise
events = []
if state:
events = events + state_events
if messages:
events = events + message_events
return events
@classmethod
@ -336,12 +267,23 @@ class RoomInfo():
state_dict = parsed_dict['state']['events']
timeline_dict = parsed_dict['timeline']['events']
state_events = RoomInfo._parse_events(olm, room_id, state_dict, messages=False)
timeline_events = RoomInfo._parse_events(olm, room_id, timeline_dict)
state_events = RoomInfo._parse_events(
olm,
room_id,
state_dict
)
timeline_events = RoomInfo._parse_events(
olm,
room_id,
timeline_dict
)
events = state_events + timeline_events
return cls(room_id, prev_batch, list(filter(None, events)))
return cls(
room_id,
prev_batch,
list(filter(None, state_events)),
list(filter(None, timeline_events))
)
class RoomEvent():

View file

@ -29,10 +29,19 @@ from http_parser.pyparser import HttpParser
from matrix.plugin_options import Option, DebugType
from matrix.utils import (key_from_value, prnt_debug, server_buffer_prnt,
create_server_buffer, tags_for_message)
create_server_buffer, tags_for_message,
server_ts_to_weechat, shorten_sender)
from matrix.utf import utf8_decode
from matrix.globals import W, SERVERS, OPTIONS
import matrix.api as API
from .buffer import WeechatChannelBuffer, RoomUser
from .rooms import (
MatrixRoom,
RoomMessageText,
RoomMessageEmote,
MatrixUser,
RoomMemberJoin
)
from matrix.api import (
MatrixClient,
MatrixSyncMessage,
@ -40,10 +49,13 @@ from matrix.api import (
MatrixKeyUploadMessage,
MatrixKeyQueryMessage,
MatrixToDeviceMessage,
MatrixSendMessage,
MatrixEncryptedMessage,
MatrixKeyClaimMessage
)
from .events import MatrixSendEvent
from matrix.encryption import (
Olm,
EncryptionError,
@ -77,6 +89,7 @@ class MatrixServer:
self.password = "" # type: str
self.rooms = dict() # type: Dict[str, MatrixRoom]
self.room_buffers = dict() # type: Dict[str, WeechatChannelBuffer]
self.buffers = dict() # type: Dict[str, weechat.buffer]
self.server_buffer = None # type: weechat.buffer
self.fd_hook = None # type: weechat.hook
@ -486,16 +499,20 @@ class MatrixServer:
message = MatrixSyncMessage(self.client, self.next_batch, limit)
self.send_queue.append(message)
def _send_unencrypted_message(self, room_id, formatted_data):
message = MatrixSendMessage(
self.client, room_id=room_id, formatted_message=formatted_data)
self.send_or_queue(message)
def send_room_message(
self,
room_id,
room,
formatted_data,
already_claimed=False
):
# type: (str, Formatted) -> None
room = self.rooms[room_id]
if not room.encrypted:
self._send_unencrypted_message(room.room_id, formatted_data)
return
# TODO don't send messages unless all the devices are verified
@ -505,8 +522,8 @@ class MatrixServer:
W.prnt("", "{prefix}matrix: Olm session missing for room, can't"
" encrypt message.")
W.prnt("", pprint.pformat(missing))
self.encryption_queue[room_id].append(formatted_data)
message = MatrixKeyClaimMessage(self.client, room_id, missing)
self.encryption_queue[room.room_id].append(formatted_data)
message = MatrixKeyClaimMessage(self.client, room.room_id, missing)
self.send_or_queue(message)
return
@ -525,7 +542,7 @@ class MatrixServer:
try:
payload_dict, to_device_dict = self.olm.group_encrypt(
room_id,
room.room_id,
plaintext_dict,
self.user_id,
room.users.keys()
@ -538,7 +555,7 @@ class MatrixServer:
message = MatrixEncryptedMessage(
self.client,
room_id,
room.room_id,
formatted_data,
payload_dict
)
@ -612,19 +629,46 @@ class MatrixServer:
server_buffer_prnt(self, pprint.pformat(message.request.payload))
server_buffer_prnt(self, pprint.pformat(message.response.body))
def handle_room_event(self, room, room_buffer, event, is_state_event):
if isinstance(event, RoomMemberJoin):
if event.sender in room.users:
user = room.users[event.sender]
if event.display_name:
user.display_name = event.display_name
else:
short_name = shorten_sender(event.sender)
user = MatrixUser(short_name, event.display_name)
buffer_user = RoomUser(user.name, event.sender)
room.users[event.sender] = user
if self.user_id == event.sender:
buffer_user.color = "weechat.color.chat_nick_self"
user.nick_color = "weechat.color.chat_nick_self"
room_buffer.join(
buffer_user,
server_ts_to_weechat(event.timestamp),
not is_state_event
)
else:
tags = tags_for_message("message")
event.execute(self, room, room_buffer._ptr, tags)
def _loop_events(self, info, n):
for i in range(n+1):
is_state = False
try:
event = info.events.popleft()
event = info.state.popleft()
is_state = True
except IndexError:
try:
event = info.timeline.popleft()
except IndexError:
return i
room = self.rooms[info.room_id]
buf = self.buffers[info.room_id]
tags = tags_for_message("message")
event.execute(self, room, buf, tags)
room, room_buffer = self.find_room_from_id(info.room_id)
self.handle_room_event(room, room_buffer, event, is_state)
self.event_queue.appendleft(info)
return i
@ -657,6 +701,32 @@ class MatrixServer:
return
def handle_own_messages(self, room_buffer, message):
if isinstance(message, RoomMessageText):
msg = (message.formatted_message.to_weechat()
if message.formatted_message
else message.message)
date = server_ts_to_weechat(message.timestamp)
room_buffer.self_message(self.user, msg, date)
return
elif isinstance(message, RoomMessageEmote):
date = server_ts_to_weechat(message.timestamp)
room_buffer.self_action(self.user, message.message, date)
return
raise NotImplementedError("Unsupported message of type {}".format(
type(message)))
def handle_matrix_response(self, response):
if isinstance(response, MatrixSendEvent):
_, room_buffer = self.find_room_from_id(response.room_id)
self.handle_own_messages(room_buffer, response.message)
else:
response.execute()
def handle_response(self, message):
# type: (MatrixMessage) -> None
@ -674,7 +744,7 @@ class MatrixServer:
return
event = message.event
event.execute()
self.handle_matrix_response(event)
else:
status_code = message.response.status
if status_code == 504:
@ -705,6 +775,26 @@ class MatrixServer:
return
def create_room_buffer(self, room_id):
buf = WeechatChannelBuffer(room_id, self.name, self.user)
# TODO this should turned into a propper class
self.room_buffers[room_id] = buf
self.buffers[room_id] = buf._ptr
self.rooms[room_id] = MatrixRoom(room_id)
pass
def find_room_from_ptr(self, pointer):
room_id = key_from_value(self.buffers, pointer)
room = self.rooms[room_id]
room_buffer = self.room_buffers[room_id]
return room, room_buffer
def find_room_from_id(self, room_id):
room = self.rooms[room_id]
room_buffer = self.room_buffers[room_id]
return room, room_buffer
@utf8_decode
def matrix_config_server_read_cb(data, config_file, section, option_name,