Add initial backlog event.
The backlog event currently only supports non redacted messages.
This commit is contained in:
parent
afd595b780
commit
3b77689c7d
4 changed files with 280 additions and 30 deletions
|
@ -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):
|
||||
|
|
203
matrix/events.py
203
matrix/events.py
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue