Add initial backlog event.

The backlog event currently only supports non redacted messages.
This commit is contained in:
poljar (Damir Jelić) 2018-02-16 15:15:56 +01:00
parent afd595b780
commit 3b77689c7d
4 changed files with 280 additions and 30 deletions

View file

@ -421,6 +421,23 @@ class MatrixBacklogMessage(MatrixMessage):
data
)
def decode_body(self, server):
try:
parsed_dict = json.loads(
self.response.body,
encoding='utf-8',
)
self.event = MatrixEvents.MatrixBacklogEvent.from_dict(
server,
self.room_id,
parsed_dict
)
return (True, None)
except json.decoder.JSONDecodeError as error:
return (False, error)
class MatrixJoinMessage(MatrixMessage):
def __init__(self, client, room_id):

View file

@ -18,9 +18,18 @@ from __future__ import unicode_literals
from builtins import str
import time
import math
from functools import partial
from matrix.globals import W, OPTIONS
from matrix.utils import color_for_tags
from matrix.utils import (
color_for_tags,
date_from_age,
sender_to_nick_and_color,
tags_for_message,
add_event_tags
)
from matrix.colors import Formatted
def sanitize_id(string):
# type: (unicode) -> unicode
@ -40,6 +49,38 @@ def sanitize_id(string):
return string.translate(remap)
def sanitize_age(age):
# type: (int) -> int
if not isinstance(age, int):
raise TypeError
if math.isnan(age):
raise ValueError
if math.isinf(age):
raise ValueError
if age < 0:
raise ValueError
return age
def sanitize_text(string):
# type: (str) -> str
if not isinstance(string, str):
raise TypeError
remap = {
ord('\b'): None,
ord('\f'): None,
ord('\r'): None,
ord('\0'): None
}
return string.translate(remap)
class MatrixEvent():
def __init__(self, server):
self.server = server
@ -104,7 +145,7 @@ class MatrixLoginEvent(MatrixEvent):
sanitize_id(parsed_dict["user_id"]),
sanitize_id(parsed_dict["access_token"])
)
except (KeyError, TypeError):
except (KeyError, TypeError, ValueError):
return MatrixErrorEvent.from_dict(
server,
"Error logging in",
@ -153,7 +194,7 @@ class MatrixSendEvent(MatrixEvent):
sanitize_id(parsed_dict["event_id"]),
message
)
except (KeyError, TypeError):
except (KeyError, TypeError, ValueError):
return MatrixErrorEvent.from_dict(
server,
"Error sending message",
@ -178,7 +219,7 @@ class MatrixTopicEvent(MatrixEvent):
sanitize_id(parsed_dict["event_id"]),
topic
)
except (KeyError, TypeError):
except (KeyError, TypeError, ValueError):
return MatrixErrorEvent.from_dict(
server,
"Error setting topic",
@ -203,7 +244,7 @@ class MatrixRedactEvent(MatrixEvent):
sanitize_id(parsed_dict["event_id"]),
reason
)
except (KeyError, TypeError):
except (KeyError, TypeError, ValueError):
return MatrixErrorEvent.from_dict(
server,
"Error redacting message",
@ -226,7 +267,7 @@ class MatrixJoinEvent(MatrixEvent):
room_id,
sanitize_id(parsed_dict["room_id"]),
)
except (KeyError, TypeError):
except (KeyError, TypeError, ValueError):
return MatrixErrorEvent.from_dict(
server,
"Error joining room",
@ -281,3 +322,153 @@ class MatrixInviteEvent(MatrixEvent):
False,
parsed_dict
)
class MatrixBacklogEvent(MatrixEvent):
def __init__(self, server, room_id, end_token, messages):
self.room_id = room_id
self.end_token = end_token
self.messages = messages
MatrixEvent.__init__(self, server)
@staticmethod
def _message_from_event(room_id, event):
if room_id != event["room_id"]:
raise ValueError
if "redacted_by" in event["unsigned"]:
return RedactedMessage.from_dict(event)
return Message.from_dict(event)
@classmethod
def from_dict(cls, server, room_id, parsed_dict):
try:
if not parsed_dict["chunk"]:
return cls(server, room_id, None, [])
end_token = sanitize_id(parsed_dict["end"])
message_func = partial(
MatrixBacklogEvent._message_from_event,
room_id
)
message_events = list(filter(
lambda event: event["type"] == "m.room.message",
parsed_dict["chunk"]
))
messages = [message_func(m) for m in message_events]
return cls(
server,
room_id,
end_token,
messages)
except (KeyError, ValueError, TypeError):
return MatrixErrorEvent.from_dict(
server,
"Error fetching backlog",
False,
parsed_dict
)
def execute(self):
room = self.server.rooms[self.room_id]
buf = self.server.buffers[self.room_id]
tags = tags_for_message("backlog")
for message in self.messages:
message.prnt(room, buf, tags)
room.prev_batch = self.end_token
class AbstractMessage():
def __init__(self, event_id, sender, age):
self.event_id = event_id
self.sender = sender
self.age = age
class RedactedMessage(AbstractMessage):
def __init__(self, event_id, sender, age, censor, reason=None):
self.censor = censor
self.reason = reason
AbstractMessage.__init__(self, event_id, sender, age)
@classmethod
def from_dict(cls, event):
event_id = sanitize_id(event["event_id"])
sender = sanitize_id(event["sender"])
age = event["unsigned"]["age"]
censor = sanitize_id(
event['unsigned']['redacted_because']['sender'])
reason = None
if 'reason' in event['unsigned']['redacted_because']['content']:
reason = sanitize_text(
event['unsigned']['redacted_because']['content']['reason'])
return cls(event_id, sender, age, censor, reason)
def prnt(self, room, buff, tags):
pass
class Message(AbstractMessage):
def __init__(
self,
event_id,
sender,
age,
message,
formatted_message=None
):
self.message = message
self.formatted_message = formatted_message
AbstractMessage.__init__(self, event_id, sender, age)
@classmethod
def from_dict(cls, event):
event_id = sanitize_id(event["event_id"])
sender = sanitize_id(event["sender"])
age = sanitize_age(event["unsigned"]["age"])
msg = ""
formatted_msg = None
if event['content']['msgtype'] == 'm.text':
msg = sanitize_text(event['content']['body'])
if ('format' in event['content'] and
'formatted_body' in event['content']):
if event['content']['format'] == "org.matrix.custom.html":
formatted_msg = Formatted.from_html(
sanitize_text(event['content']['formatted_body']))
return cls(event_id, sender, age, msg, formatted_msg)
def prnt(self, room, buff, tags):
msg = (self.formatted_message.to_weechat() if
self.formatted_message
else self.message)
nick, color_name = sender_to_nick_and_color(room, self.sender)
color = color_for_tags(color_name)
event_tags = add_event_tags(
self.event_id,
nick,
color,
tags
)
tags_string = ",".join(event_tags)
data = "{author}\t{msg}".format(author=nick, msg=msg)
date = date_from_age(self.age)
W.prnt_date_tags(buff, date, tags_string, data)

View file

@ -660,17 +660,6 @@ def matrix_update_buffer_lines(new_lines, own_lines):
line = W.hdata_move(hdata_line, line, 1)
def matrix_handle_old_messages(server, room_id, events):
for event in events:
if event['type'] == 'm.room.message':
matrix_handle_room_messages(server, room_id, event, old=True)
# TODO do we wan't to handle topics joins/quits here?
else:
pass
matrix_sort_old_messages(server, room_id)
def matrix_handle_message(
server, # type: MatrixServer
message, # type: MatrixMessage
@ -707,6 +696,11 @@ def matrix_handle_message(
event = message.event
event.execute()
elif message_type == MessageType.ROOM_MSG:
event = message.event
event.execute()
matrix_sort_old_messages(server, message.room_id)
elif message_type is MessageType.SYNC:
next_batch = response['next_batch']
@ -723,19 +717,6 @@ def matrix_handle_message(
# TODO add a delay to this
server.sync()
elif message_type == MessageType.ROOM_MSG:
# Response has no messages, that is we already got the oldest message
# in a previous request, nothing to do
if not response['chunk']:
return
room_id = response['chunk'][0]['room_id']
room = server.rooms[room_id]
matrix_handle_old_messages(server, room_id, response['chunk'])
room.prev_batch = response['end']
else:
server_buffer_prnt(
server,

View file

@ -119,3 +119,64 @@ def color_for_tags(color):
option = W.config_get(color)
return W.config_string(option)
return color
def date_from_age(age):
# type: (float) -> int
now = time.time()
date = int(now - (age / 1000))
return date
def strip_matrix_server(string):
# type: (str) -> str
return string.rsplit(":", 1)[0]
def shorten_sender(sender):
# type: (str) -> str
return strip_matrix_server(sender)[1:]
def sender_to_nick_and_color(room, sender):
nick = sender
nick_color_name = "default"
if sender in room.users:
user = room.users[sender]
nick = user.display_name
nick_color_name = user.nick_color
else:
nick = shorten_sender(sender)
nick_color_name = W.info_get("nick_color_name", nick)
return (nick, nick_color_name)
def tags_for_message(message_type):
default_tags = {
"message": [
"matrix_message",
"notify_message",
"log1"
],
"backlog": [
"matrix_message",
"notify_message",
"no_log",
"no_highlight"
]
}
return default_tags[message_type]
def add_event_tags(event_id, nick, color, tags):
if not tags:
tags = tags_for_message("message")
tags.append("nick_{nick}".format(nick=nick))
tags.append("perfix_nick_{color}".format(color=color_for_tags(color)))
tags.append("matrix_id_{event_id}".format(event_id=event_id))
return tags