server: Continue nio migration.
This commit is contained in:
parent
91eec1ad85
commit
45be743c07
7 changed files with 199 additions and 1069 deletions
34
main.py
34
main.py
|
@ -145,14 +145,6 @@ def print_certificate_info(buff, sock, cert):
|
|||
|
||||
W.prnt(buff, message)
|
||||
|
||||
|
||||
@utf8_decode
|
||||
def matrix_event_timer_cb(server_name, remaining_calls):
|
||||
server = SERVERS[server_name]
|
||||
server.handle_events()
|
||||
return W.WEECHAT_RC_OK
|
||||
|
||||
|
||||
def wrap_socket(server, file_descriptor):
|
||||
# type: (MatrixServer, int) -> None
|
||||
sock = None # type: socket.socket
|
||||
|
@ -399,32 +391,6 @@ def connect_cb(data, status, gnutls_rc, sock, error, ip_address):
|
|||
return W.WEECHAT_RC_OK
|
||||
|
||||
|
||||
@utf8_decode
|
||||
def room_input_cb(server_name, buffer, input_data):
|
||||
server = SERVERS[server_name]
|
||||
|
||||
if not server.connected:
|
||||
message = "{prefix}matrix: you are not connected to the server".format(
|
||||
prefix=W.prefix("error"))
|
||||
W.prnt(buffer, message)
|
||||
return W.WEECHAT_RC_ERROR
|
||||
|
||||
room_id = key_from_value(server.buffers, buffer)
|
||||
room = server.rooms[room_id]
|
||||
|
||||
formatted_data = Formatted.from_input_line(input_data)
|
||||
|
||||
if room.encrypted:
|
||||
server.send_room_message(room_id, formatted_data)
|
||||
return W.WEECHAT_RC_OK
|
||||
|
||||
message = MatrixSendMessage(
|
||||
server.client, room_id=room_id, formatted_message=formatted_data)
|
||||
|
||||
server.send_or_queue(message)
|
||||
return W.WEECHAT_RC_OK
|
||||
|
||||
|
||||
@utf8_decode
|
||||
def room_close_cb(data, buffer):
|
||||
W.prnt("",
|
||||
|
|
|
@ -45,13 +45,12 @@ def matrix_bar_item_name(data, item, window, buffer, extra_info):
|
|||
color = ("status_name_ssl"
|
||||
if server.ssl_context.check_hostname else "status_name")
|
||||
|
||||
room_id = key_from_value(server.buffers, buffer)
|
||||
|
||||
room = server.rooms[room_id]
|
||||
room_buffer = server.find_room_from_ptr(buffer)
|
||||
room = room_buffer.room
|
||||
|
||||
return "{color}{name}".format(
|
||||
color=W.color(color),
|
||||
name=room.display_name(server.user_id))
|
||||
name=room.display_name())
|
||||
|
||||
elif buffer == server.server_buffer:
|
||||
color = ("status_name_ssl"
|
||||
|
@ -92,14 +91,14 @@ def matrix_bar_item_buffer_modes(data, item, window, buffer, extra_info):
|
|||
# pylint: disable=unused-argument
|
||||
for server in SERVERS.values():
|
||||
if buffer in server.buffers.values():
|
||||
room_id = key_from_value(server.buffers, buffer)
|
||||
room = server.rooms[room_id]
|
||||
room_buffer = server.find_room_from_ptr(buffer)
|
||||
room = room_buffer.room
|
||||
modes = []
|
||||
|
||||
if room.encrypted:
|
||||
modes.append("🔐")
|
||||
|
||||
if room.backlog_pending:
|
||||
if room_buffer.backlog_pending:
|
||||
modes.append("⏳")
|
||||
|
||||
return "".join(modes)
|
||||
|
|
223
matrix/buffer.py
223
matrix/buffer.py
|
@ -20,6 +20,7 @@ from __future__ import unicode_literals
|
|||
import time
|
||||
from builtins import super
|
||||
from functools import partial
|
||||
from typing import NamedTuple
|
||||
|
||||
from .globals import W, SERVERS, OPTIONS, SCRIPT_NAME, ENCRYPTION
|
||||
from .utf import utf8_decode
|
||||
|
@ -32,29 +33,35 @@ from .utils import (
|
|||
)
|
||||
from .plugin_options import RedactType
|
||||
|
||||
|
||||
from .rooms import (
|
||||
RoomNameEvent,
|
||||
RoomAliasEvent,
|
||||
RoomMembershipEvent,
|
||||
RoomTopicEvent,
|
||||
from nio import (
|
||||
RoomMessageText,
|
||||
RoomMessageEmote,
|
||||
RoomMessageNotice,
|
||||
RoomMessageMedia,
|
||||
RoomMessageUnknown,
|
||||
RoomRedactionEvent,
|
||||
RoomRedactedMessageEvent,
|
||||
RoomMemberEvent,
|
||||
PowerLevelsEvent,
|
||||
RoomEncryptionEvent,
|
||||
RoomPowerLevels,
|
||||
UndecryptedEvent
|
||||
RedactedEvent,
|
||||
RoomAliasEvent,
|
||||
RoomTopicEvent,
|
||||
RoomMessageEmote,
|
||||
RoomNameEvent
|
||||
)
|
||||
|
||||
|
||||
OwnMessage = NamedTuple("OwnMessage", [
|
||||
("sender", str),
|
||||
("age", int),
|
||||
("event_id", str),
|
||||
("formatted_message", Formatted)
|
||||
])
|
||||
|
||||
|
||||
class OwnAction(OwnMessage):
|
||||
pass
|
||||
|
||||
|
||||
@utf8_decode
|
||||
def room_buffer_input_cb(server_name, buffer, input_data):
|
||||
server = SERVERS[server_name]
|
||||
room, room_buffer = server.find_room_from_ptr(buffer)
|
||||
room_buffer = server.find_room_from_ptr(buffer)
|
||||
|
||||
if not room_buffer:
|
||||
# TODO log error
|
||||
|
@ -66,7 +73,7 @@ def room_buffer_input_cb(server_name, buffer, input_data):
|
|||
|
||||
formatted_data = Formatted.from_input_line(input_data)
|
||||
|
||||
server.send_room_message(room, formatted_data)
|
||||
server.send_room_message(room_buffer.room, formatted_data)
|
||||
|
||||
return W.WEECHAT_RC_OK
|
||||
|
||||
|
@ -669,6 +676,7 @@ class WeechatChannelBuffer(object):
|
|||
class RoomBuffer(object):
|
||||
def __init__(self, room, server_name):
|
||||
self.room = room
|
||||
self.backlog_pending = False
|
||||
|
||||
# This dict remembers the connection from a user_id to the name we
|
||||
# displayed in the buffer
|
||||
|
@ -692,12 +700,14 @@ class RoomBuffer(object):
|
|||
def join(event, date, is_state):
|
||||
user = self.room.users[event.sender]
|
||||
|
||||
short_name = shorten_sender(user.user_id)
|
||||
|
||||
# TODO make this configurable
|
||||
if user.name in self.displayed_nicks.values():
|
||||
if short_name in self.displayed_nicks.values():
|
||||
# Use the full user id, but don't include the @
|
||||
nick = event.sender[1:]
|
||||
else:
|
||||
nick = user.name
|
||||
nick = short_name
|
||||
|
||||
buffer_user = RoomUser(nick, event.sender, user.power_level)
|
||||
self.displayed_nicks[event.sender] = nick
|
||||
|
@ -708,11 +718,11 @@ class RoomBuffer(object):
|
|||
|
||||
self.weechat_buffer.join(
|
||||
buffer_user,
|
||||
server_ts_to_weechat(event.timestamp),
|
||||
server_ts_to_weechat(event.server_timestamp),
|
||||
not is_state
|
||||
)
|
||||
|
||||
date = server_ts_to_weechat(event.timestamp)
|
||||
date = server_ts_to_weechat(event.server_timestamp)
|
||||
|
||||
if event.content["membership"] == "join":
|
||||
if event.prev_content and "membership" in event.prev_content:
|
||||
|
@ -743,7 +753,7 @@ class RoomBuffer(object):
|
|||
self.weechat_buffer.invite(event.state_key, date)
|
||||
return
|
||||
|
||||
room_name = self.room.display_name(self.room.own_user_id)
|
||||
room_name = self.room.display_name()
|
||||
self.weechat_buffer.short_name = room_name
|
||||
|
||||
def _redact_line(self, event):
|
||||
|
@ -808,14 +818,14 @@ class RoomBuffer(object):
|
|||
|
||||
def _handle_redacted_message(self, event):
|
||||
nick = self.find_nick(event.sender)
|
||||
date = server_ts_to_weechat(event.timestamp)
|
||||
date = server_ts_to_weechat(event.server_timestamp)
|
||||
tags = self.get_event_tags(event)
|
||||
tags.append(SCRIPT_NAME + "_redacted")
|
||||
|
||||
reason = (", reason: \"{reason}\"".format(reason=event.reason)
|
||||
if event.reason else "")
|
||||
|
||||
censor = self.find_nick(event.censor)
|
||||
censor = self.find_nick(event.redacter)
|
||||
|
||||
data = ("{del_color}<{log_color}Message redacted by: "
|
||||
"{censor}{log_color}{reason}{del_color}>{ncolor}").format(
|
||||
|
@ -833,7 +843,7 @@ class RoomBuffer(object):
|
|||
self.weechat_buffer.change_topic(
|
||||
nick,
|
||||
event.topic,
|
||||
server_ts_to_weechat(event.timestamp),
|
||||
server_ts_to_weechat(event.server_timestamp),
|
||||
not is_state)
|
||||
|
||||
@staticmethod
|
||||
|
@ -841,12 +851,13 @@ class RoomBuffer(object):
|
|||
return ["matrix_id_{}".format(event.event_id)]
|
||||
|
||||
def _handle_power_level(self, event):
|
||||
for user_id in self.room.power_levels:
|
||||
for user_id in self.room.power_levels.users:
|
||||
if user_id in self.displayed_nicks:
|
||||
nick = self.find_nick(user_id)
|
||||
|
||||
user = self.weechat_buffer.users[nick]
|
||||
user.power_level = self.room.power_levels[user_id]
|
||||
user.power_level = self.room.power_levels.get_user_level(
|
||||
user_id)
|
||||
|
||||
# There is no way to change the group of a user without
|
||||
# removing him from the nicklist
|
||||
|
@ -854,67 +865,48 @@ class RoomBuffer(object):
|
|||
self.weechat_buffer._add_user_to_nicklist(user)
|
||||
|
||||
def handle_state_event(self, event):
|
||||
if isinstance(event, RoomMembershipEvent):
|
||||
if isinstance(event, RoomMemberEvent):
|
||||
self.handle_membership_events(event, True)
|
||||
elif isinstance(event, RoomTopicEvent):
|
||||
self._handle_topic(event, True)
|
||||
elif isinstance(event, RoomPowerLevels):
|
||||
elif isinstance(event, PowerLevelsEvent):
|
||||
self._handle_power_level(event)
|
||||
|
||||
def handle_timeline_event(self, event):
|
||||
if isinstance(event, RoomMembershipEvent):
|
||||
if isinstance(event, RoomMemberEvent):
|
||||
self.handle_membership_events(event, False)
|
||||
|
||||
elif isinstance(event, (RoomNameEvent, RoomAliasEvent)):
|
||||
room_name = self.room.display_name(self.room.own_user_id)
|
||||
room_name = self.room.display_name()
|
||||
self.weechat_buffer.short_name = room_name
|
||||
|
||||
elif isinstance(event, RoomTopicEvent):
|
||||
self._handle_topic(event, False)
|
||||
|
||||
elif isinstance(event, RoomMessageText):
|
||||
nick = self.find_nick(event.sender)
|
||||
data = (event.formatted_message.to_weechat()
|
||||
if event.formatted_message else event.message)
|
||||
|
||||
date = server_ts_to_weechat(event.timestamp)
|
||||
self.weechat_buffer.message(
|
||||
nick,
|
||||
data,
|
||||
date,
|
||||
self.get_event_tags(event)
|
||||
)
|
||||
|
||||
# Emotes are a subclass of RoomMessageText, so put them before the text
|
||||
# ones
|
||||
elif isinstance(event, RoomMessageEmote):
|
||||
nick = self.find_nick(event.sender)
|
||||
date = server_ts_to_weechat(event.timestamp)
|
||||
date = server_ts_to_weechat(event.server_timestamp)
|
||||
self.weechat_buffer.action(
|
||||
nick,
|
||||
event.message,
|
||||
event.body,
|
||||
date,
|
||||
self.get_event_tags(event)
|
||||
)
|
||||
|
||||
elif isinstance(event, RoomMessageNotice):
|
||||
|
||||
elif isinstance(event, RoomMessageText):
|
||||
nick = self.find_nick(event.sender)
|
||||
date = server_ts_to_weechat(event.timestamp)
|
||||
self.weechat_buffer.notice(
|
||||
nick,
|
||||
event.message,
|
||||
date,
|
||||
self.get_event_tags(event)
|
||||
)
|
||||
formatted = None
|
||||
|
||||
elif isinstance(event, RoomMessageMedia):
|
||||
nick = self.find_nick(event.sender)
|
||||
date = server_ts_to_weechat(event.timestamp)
|
||||
http_url = mxc_to_http(event.url)
|
||||
url = http_url if http_url else event.url
|
||||
if event.formatted_body:
|
||||
formatted = Formatted.from_html(event.formatted_body)
|
||||
|
||||
description = ("/{}".format(event.description)
|
||||
if event.description else "")
|
||||
data = "{url}{desc}".format(url=url, desc=description)
|
||||
data = (formatted.to_weechat()
|
||||
if formatted else event.body)
|
||||
|
||||
date = server_ts_to_weechat(event.server_timestamp)
|
||||
self.weechat_buffer.message(
|
||||
nick,
|
||||
data,
|
||||
|
@ -922,24 +914,51 @@ class RoomBuffer(object):
|
|||
self.get_event_tags(event)
|
||||
)
|
||||
|
||||
elif isinstance(event, RoomMessageUnknown):
|
||||
nick = self.find_nick(event.sender)
|
||||
date = server_ts_to_weechat(event.timestamp)
|
||||
data = ("Unknown message of type {t}, body: {body}").format(
|
||||
t=event.message_type,
|
||||
body=event.message
|
||||
)
|
||||
self.weechat_buffer.message(
|
||||
nick,
|
||||
data,
|
||||
date,
|
||||
self.get_event_tags(event)
|
||||
)
|
||||
# elif isinstance(event, RoomMessageNotice):
|
||||
# nick = self.find_nick(event.sender)
|
||||
# date = server_ts_to_weechat(event.server_timestamp)
|
||||
# self.weechat_buffer.notice(
|
||||
# nick,
|
||||
# event.message,
|
||||
# date,
|
||||
# self.get_event_tags(event)
|
||||
# )
|
||||
|
||||
elif isinstance(event, RoomRedactionEvent):
|
||||
self._redact_line(event)
|
||||
# elif isinstance(event, RoomMessageMedia):
|
||||
# nick = self.find_nick(event.sender)
|
||||
# date = server_ts_to_weechat(event.server_timestamp)
|
||||
# http_url = mxc_to_http(event.url)
|
||||
# url = http_url if http_url else event.url
|
||||
|
||||
elif isinstance(event, RoomRedactedMessageEvent):
|
||||
# description = ("/{}".format(event.description)
|
||||
# if event.description else "")
|
||||
# data = "{url}{desc}".format(url=url, desc=description)
|
||||
|
||||
# self.weechat_buffer.message(
|
||||
# nick,
|
||||
# data,
|
||||
# date,
|
||||
# self.get_event_tags(event)
|
||||
# )
|
||||
|
||||
# elif isinstance(event, RoomMessageUnknown):
|
||||
# nick = self.find_nick(event.sender)
|
||||
# date = server_ts_to_weechat(event.server_timestamp)
|
||||
# data = ("Unknown message of type {t}, body: {body}").format(
|
||||
# t=event.message_type,
|
||||
# body=event.message
|
||||
# )
|
||||
# self.weechat_buffer.message(
|
||||
# nick,
|
||||
# data,
|
||||
# date,
|
||||
# self.get_event_tags(event)
|
||||
# )
|
||||
|
||||
# elif isinstance(event, RoomRedactionEvent):
|
||||
# self._redact_line(event)
|
||||
|
||||
elif isinstance(event, RedactedEvent):
|
||||
self._handle_redacted_message(event)
|
||||
|
||||
elif isinstance(event, RoomEncryptionEvent):
|
||||
|
@ -951,38 +970,44 @@ class RoomBuffer(object):
|
|||
"this room.")
|
||||
self.weechat_buffer.error(message)
|
||||
|
||||
elif isinstance(event, RoomPowerLevels):
|
||||
elif isinstance(event, PowerLevelsEvent):
|
||||
self._handle_power_level(event)
|
||||
|
||||
elif isinstance(event, UndecryptedEvent):
|
||||
nick = self.find_nick(event.sender)
|
||||
date = server_ts_to_weechat(event.timestamp)
|
||||
data = ("Error decrypting event session "
|
||||
"id: {}".format(event.session_id))
|
||||
self.weechat_buffer.message(
|
||||
nick,
|
||||
data,
|
||||
date,
|
||||
self.get_event_tags(event)
|
||||
)
|
||||
# elif isinstance(event, UndecryptedEvent):
|
||||
# nick = self.find_nick(event.sender)
|
||||
# date = server_ts_to_weechat(event.server_timestamp)
|
||||
# data = ("Error decrypting event session "
|
||||
# "id: {}".format(event.session_id))
|
||||
# self.weechat_buffer.message(
|
||||
# nick,
|
||||
# data,
|
||||
# date,
|
||||
# self.get_event_tags(event)
|
||||
# )
|
||||
|
||||
else:
|
||||
W.prnt("", "Unhandled event of type {}.".format(
|
||||
type(event).__name__))
|
||||
|
||||
def self_message(self, message):
|
||||
# type: (OwnMessage) -> None
|
||||
nick = self.find_nick(self.room.own_user_id)
|
||||
data = (message.formatted_message.to_weechat()
|
||||
if message.formatted_message
|
||||
else message.message)
|
||||
data = message.formatted_message.to_weechat()
|
||||
|
||||
date = server_ts_to_weechat(message.timestamp)
|
||||
# TODO event_id tag is missing
|
||||
date = message.age
|
||||
self.weechat_buffer.self_message(nick, data, date)
|
||||
|
||||
def self_action(self, message):
|
||||
# type: (OwnMessage) -> None
|
||||
nick = self.find_nick(self.room.own_user_id)
|
||||
date = server_ts_to_weechat(message.timestamp)
|
||||
self.weechat_buffer.self_action(nick, message.message, date)
|
||||
date = message.age
|
||||
# TODO event_id tag is missing
|
||||
self.weechat_buffer.self_action(
|
||||
nick,
|
||||
message.formatted_message.to_weechat(),
|
||||
date
|
||||
)
|
||||
|
||||
def old_redacted(self, event):
|
||||
tags = [
|
||||
|
@ -994,7 +1019,7 @@ class RoomBuffer(object):
|
|||
reason = (", reason: \"{reason}\"".format(reason=event.reason)
|
||||
if event.reason else "")
|
||||
|
||||
censor = self.find_nick(event.censor)
|
||||
censor = self.find_nick(event.redacter)
|
||||
|
||||
data = ("{del_color}<{log_color}Message redacted by: "
|
||||
"{censor}{log_color}{reason}{del_color}>{ncolor}").format(
|
||||
|
@ -1007,7 +1032,7 @@ class RoomBuffer(object):
|
|||
tags += self.get_event_tags(event)
|
||||
nick = self.find_nick(event.sender)
|
||||
user = self.weechat_buffer._get_user(nick)
|
||||
date = server_ts_to_weechat(event.timestamp)
|
||||
date = server_ts_to_weechat(event.server_timestamp)
|
||||
self.weechat_buffer._print_message(user, data, date, tags)
|
||||
|
||||
def old_message(self, event):
|
||||
|
@ -1022,7 +1047,7 @@ class RoomBuffer(object):
|
|||
data = (event.formatted_message.to_weechat()
|
||||
if event.formatted_message else event.message)
|
||||
user = self.weechat_buffer._get_user(nick)
|
||||
date = server_ts_to_weechat(event.timestamp)
|
||||
date = server_ts_to_weechat(event.server_timestamp)
|
||||
self.weechat_buffer._print_message(user, data, date, tags)
|
||||
|
||||
def sort_messages(self):
|
||||
|
@ -1062,7 +1087,7 @@ class RoomBuffer(object):
|
|||
for event in events:
|
||||
if isinstance(event, RoomMessageText):
|
||||
self.old_message(event)
|
||||
elif isinstance(event, RoomRedactedMessageEvent):
|
||||
elif isinstance(event, RedactedEvent):
|
||||
self.old_redacted(event)
|
||||
|
||||
self.sort_messages()
|
||||
|
|
|
@ -67,23 +67,23 @@ def hook_commands():
|
|||
'matrix_command_cb',
|
||||
'')
|
||||
|
||||
W.hook_command(
|
||||
# Command name and short description
|
||||
'redact',
|
||||
'redact messages',
|
||||
# Synopsis
|
||||
('<message-number>[:"<message-part>"] [<reason>]'),
|
||||
# Description
|
||||
("message-number: number of message to redact (starting from 1 for\n"
|
||||
" the last message received, counting up)\n"
|
||||
" message-part: an initial part of the message (ignored, only used\n"
|
||||
" as visual feedback when using completion)\n"
|
||||
" reason: the redaction reason\n"),
|
||||
# Completions
|
||||
('%(matrix_messages)'),
|
||||
# Function name
|
||||
'matrix_redact_command_cb',
|
||||
'')
|
||||
# W.hook_command(
|
||||
# # Command name and short description
|
||||
# 'redact',
|
||||
# 'redact messages',
|
||||
# # Synopsis
|
||||
# ('<message-number>[:"<message-part>"] [<reason>]'),
|
||||
# # Description
|
||||
# ("message-number: number of message to redact (starting from 1 for\n"
|
||||
# " the last message received, counting up)\n"
|
||||
# " message-part: an initial part of the message (ignored, only used\n"
|
||||
# " as visual feedback when using completion)\n"
|
||||
# " reason: the redaction reason\n"),
|
||||
# # Completions
|
||||
# ('%(matrix_messages)'),
|
||||
# # Function name
|
||||
# 'matrix_redact_command_cb',
|
||||
# '')
|
||||
|
||||
W.hook_command(
|
||||
# Command name and short description
|
||||
|
@ -101,15 +101,15 @@ def hook_commands():
|
|||
|
||||
matrix_hook_olm_command()
|
||||
|
||||
W.hook_command_run('/topic', 'matrix_command_topic_cb', '')
|
||||
# W.hook_command_run('/topic', 'matrix_command_topic_cb', '')
|
||||
W.hook_command_run('/buffer clear', 'matrix_command_buf_clear_cb', '')
|
||||
W.hook_command_run('/join', 'matrix_command_join_cb', '')
|
||||
W.hook_command_run('/part', 'matrix_command_part_cb', '')
|
||||
W.hook_command_run('/invite', 'matrix_command_invite_cb', '')
|
||||
W.hook_command_run('/kick', 'matrix_command_kick_cb', '')
|
||||
|
||||
if OPTIONS.enable_backlog:
|
||||
hook_page_up()
|
||||
# if OPTIONS.enable_backlog:
|
||||
# hook_page_up()
|
||||
|
||||
|
||||
@utf8_decode
|
||||
|
@ -123,7 +123,7 @@ def matrix_me_command_cb(data, buffer, args):
|
|||
W.prnt(server.server_buffer, message)
|
||||
return W.WEECHAT_RC_ERROR
|
||||
|
||||
room_id = key_from_value(server.buffers, buffer)
|
||||
room_buffer = server.find_room_from_ptr(buffer)
|
||||
|
||||
if not args:
|
||||
return W.WEECHAT_RC_OK
|
||||
|
@ -131,10 +131,10 @@ def matrix_me_command_cb(data, buffer, args):
|
|||
formatted_data = Formatted.from_input_line(args)
|
||||
message = MatrixEmoteMessage(
|
||||
server.client,
|
||||
room_id=room_id,
|
||||
room_id=room_buffer.room.room_id,
|
||||
formatted_message=formatted_data)
|
||||
|
||||
if server.rooms[room_id].encrypted:
|
||||
if room_buffer.room.encrypted:
|
||||
return W.WEECHAT_RC_OK
|
||||
|
||||
server.send_or_queue(message)
|
||||
|
@ -150,9 +150,10 @@ def matrix_me_command_cb(data, buffer, args):
|
|||
|
||||
|
||||
def matrix_fetch_old_messages(server, room_id):
|
||||
room = server.rooms[room_id]
|
||||
room_buffer = server.find_room_from_id(room_id)
|
||||
room = room_buffer.room
|
||||
|
||||
if room.backlog_pending:
|
||||
if room_buffer.backlog_pending:
|
||||
return
|
||||
|
||||
prev_batch = room.prev_batch
|
||||
|
@ -165,7 +166,7 @@ def matrix_fetch_old_messages(server, room_id):
|
|||
room_id=room_id,
|
||||
token=prev_batch,
|
||||
limit=OPTIONS.backlog_limit)
|
||||
room.backlog_pending = True
|
||||
room_buffer.backlog_pending = True
|
||||
W.bar_item_update("buffer_modes")
|
||||
|
||||
server.send_or_queue(message)
|
||||
|
@ -913,14 +914,15 @@ def matrix_command_topic_cb(data, buffer, command):
|
|||
for server in SERVERS.values():
|
||||
if buffer in server.buffers.values():
|
||||
topic = None
|
||||
room_id = key_from_value(server.buffers, buffer)
|
||||
room_buffer = server.find_room_from_ptr(buffer)
|
||||
split_command = command.split(' ', 1)
|
||||
|
||||
if len(split_command) == 2:
|
||||
topic = split_command[1]
|
||||
|
||||
if not topic:
|
||||
room = server.rooms[room_id]
|
||||
room_buffer = server.find_room_from_ptr(buffer)
|
||||
room = room_buffer.room
|
||||
if not room.topic:
|
||||
return W.WEECHAT_RC_OK
|
||||
|
||||
|
@ -938,8 +940,8 @@ def matrix_command_topic_cb(data, buffer, command):
|
|||
topic=room.topic)
|
||||
|
||||
date = int(time.time())
|
||||
topic_date = room.topic_date.strftime("%a, %d %b %Y "
|
||||
"%H:%M:%S")
|
||||
topic_date = room_buffer.weechat_buffer.topic_date.strftime(
|
||||
"%a, %d %b %Y %H:%M:%S")
|
||||
|
||||
tags = "matrix_topic,log1"
|
||||
W.prnt_date_tags(buffer, date, tags, message)
|
||||
|
@ -958,7 +960,7 @@ def matrix_command_topic_cb(data, buffer, command):
|
|||
return W.WEECHAT_RC_OK_EAT
|
||||
|
||||
message = MatrixTopicMessage(
|
||||
server.client, room_id=room_id, topic=topic)
|
||||
server.client, room_id=room.room_id, topic=topic)
|
||||
server.send_or_queue(message)
|
||||
|
||||
return W.WEECHAT_RC_OK_EAT
|
||||
|
|
156
matrix/events.py
156
matrix/events.py
|
@ -27,12 +27,9 @@ 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 (RoomInfo, RoomMessageText,
|
||||
RoomMessageEvent, RoomRedactedMessageEvent,
|
||||
RoomMessageEmote)
|
||||
|
||||
from matrix.encryption import OlmDeviceKey, OneTimeKey
|
||||
from .buffer import RoomUser
|
||||
from .buffer import RoomUser, OwnMessage, OwnAction
|
||||
|
||||
try:
|
||||
from olm.session import OlmMessage, OlmPreKeyMessage
|
||||
|
@ -96,25 +93,6 @@ class MatrixKeyUploadEvent(MatrixEvent):
|
|||
"keys", False, parsed_dict)
|
||||
|
||||
|
||||
class MatrixLoginEvent(MatrixEvent):
|
||||
|
||||
def __init__(self, server, user_id, device_id, access_token):
|
||||
self.user_id = user_id
|
||||
self.access_token = access_token
|
||||
self.device_id = device_id
|
||||
MatrixEvent.__init__(self, server)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, server, parsed_dict):
|
||||
try:
|
||||
return cls(server, sanitize_id(parsed_dict["user_id"]),
|
||||
sanitize_id(parsed_dict["device_id"]),
|
||||
sanitize_token(parsed_dict["access_token"]))
|
||||
except (KeyError, TypeError, ValueError):
|
||||
return MatrixErrorEvent.from_dict(server, "Error logging in", True,
|
||||
parsed_dict)
|
||||
|
||||
|
||||
class MatrixSendEvent(MatrixEvent):
|
||||
|
||||
def __init__(self, server, room_id, message):
|
||||
|
@ -128,11 +106,9 @@ class MatrixSendEvent(MatrixEvent):
|
|||
event_id = sanitize_id(parsed_dict["event_id"])
|
||||
sender = server.user_id
|
||||
age = 0
|
||||
plain_message = message.to_plain()
|
||||
formatted_message = message
|
||||
|
||||
message = RoomMessageText(event_id, sender, age, plain_message,
|
||||
formatted_message)
|
||||
message = OwnMessage(sender, age, event_id, formatted_message)
|
||||
|
||||
return cls(server, room_id, message)
|
||||
except (KeyError, TypeError, ValueError):
|
||||
|
@ -148,11 +124,9 @@ class MatrixEmoteEvent(MatrixSendEvent):
|
|||
event_id = sanitize_id(parsed_dict["event_id"])
|
||||
sender = server.user_id
|
||||
age = 0
|
||||
plain_message = message.to_plain()
|
||||
formatted_message = message
|
||||
|
||||
message = RoomMessageEmote(event_id, sender, age, plain_message,
|
||||
formatted_message)
|
||||
message = OwnAction(sender, age, event_id, formatted_message)
|
||||
|
||||
return cls(server, room_id, message)
|
||||
except (KeyError, TypeError, ValueError):
|
||||
|
@ -410,127 +384,3 @@ class MatrixBacklogEvent(MatrixEvent):
|
|||
except (KeyError, ValueError, TypeError):
|
||||
return MatrixErrorEvent.from_dict(server, "Error fetching backlog",
|
||||
False, parsed_dict)
|
||||
|
||||
|
||||
class MatrixSyncEvent(MatrixEvent):
|
||||
|
||||
def __init__(self, server, next_batch, room_infos, invited_infos,
|
||||
one_time_key_count):
|
||||
self.next_batch = next_batch
|
||||
self.joined_room_infos = room_infos
|
||||
self.invited_room_infos = invited_infos
|
||||
self.one_time_key_count = one_time_key_count
|
||||
|
||||
MatrixEvent.__init__(self, server)
|
||||
|
||||
@staticmethod
|
||||
def _infos_from_dict(olm, parsed_dict):
|
||||
join_infos = []
|
||||
invite_infos = []
|
||||
|
||||
for room_id, room_dict in parsed_dict['join'].items():
|
||||
if not room_id:
|
||||
continue
|
||||
|
||||
join_infos.append(RoomInfo.from_dict(olm, room_id, room_dict))
|
||||
|
||||
return (join_infos, invite_infos)
|
||||
|
||||
@staticmethod
|
||||
def _get_olm_device_event(server, parsed_dict):
|
||||
device_key = server.olm.account.identity_keys["curve25519"]
|
||||
|
||||
if device_key not in parsed_dict["content"]["ciphertext"]:
|
||||
return None
|
||||
|
||||
sender = sanitize_id(parsed_dict["sender"])
|
||||
sender_key = sanitize_id(parsed_dict["content"]["sender_key"])
|
||||
|
||||
ciphertext = parsed_dict["content"]["ciphertext"].pop(device_key)
|
||||
|
||||
message = None
|
||||
|
||||
if ciphertext["type"] == 0:
|
||||
message = OlmPreKeyMessage(ciphertext["body"])
|
||||
elif ciphertext["type"] == 1:
|
||||
message = OlmMessage(ciphertext["body"])
|
||||
else:
|
||||
raise ValueError("Invalid Olm message type")
|
||||
|
||||
olm = server.olm
|
||||
plaintext = olm.decrypt(sender, sender_key, message)
|
||||
|
||||
if not plaintext:
|
||||
return None
|
||||
|
||||
# TODO check sender key
|
||||
decrypted_sender = plaintext["sender"]
|
||||
decrypted_recepient = plaintext["recipient"]
|
||||
decrypted_recepient_key = plaintext["recipient_keys"]["ed25519"]
|
||||
|
||||
if (sender != decrypted_sender or
|
||||
server.user_id != decrypted_recepient or
|
||||
olm.account.identity_keys["ed25519"] !=
|
||||
decrypted_recepient_key):
|
||||
error_message = ("{prefix}matrix: Mismatch in decrypted Olm "
|
||||
"message").format(prefix=W.prefix("error"))
|
||||
W.prnt("", error_message)
|
||||
return None
|
||||
|
||||
if plaintext["type"] != "m.room_key":
|
||||
return None
|
||||
|
||||
MatrixSyncEvent._handle_key_event(server, sender_key, plaintext)
|
||||
|
||||
@staticmethod
|
||||
def _handle_key_event(server, sender_key, parsed_dict):
|
||||
# type: (MatrixServer, str, Dict[Any, Any] -> None
|
||||
olm = server.olm
|
||||
content = parsed_dict.pop("content")
|
||||
|
||||
if content["algorithm"] != "m.megolm.v1.aes-sha2":
|
||||
return
|
||||
|
||||
room_id = content["room_id"]
|
||||
session_id = content["session_id"]
|
||||
session_key = content["session_key"]
|
||||
|
||||
if session_id in olm.inbound_group_sessions[room_id]:
|
||||
return
|
||||
|
||||
olm.create_group_session(room_id, session_id, session_key)
|
||||
|
||||
@staticmethod
|
||||
def _get_to_device_events(server, parsed_dict):
|
||||
# type: (MatrixServer, Dict[Any, Any]) -> None
|
||||
for event in parsed_dict["events"]:
|
||||
if event["type"] == "m.room.encrypted":
|
||||
if (event["content"]["algorithm"] ==
|
||||
'm.olm.v1.curve25519-aes-sha2'):
|
||||
MatrixSyncEvent._get_olm_device_event(server, event)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, server, parsed_dict):
|
||||
try:
|
||||
next_batch = sanitize_id(parsed_dict["next_batch"])
|
||||
one_time_key_count = 0
|
||||
|
||||
if "device_one_time_keys_count" in parsed_dict:
|
||||
if ("signed_curve25519" in
|
||||
parsed_dict["device_one_time_keys_count"]):
|
||||
one_time_key_count = (
|
||||
parsed_dict["device_one_time_keys_count"]["signed_curve25519"])
|
||||
|
||||
MatrixSyncEvent._get_to_device_events(
|
||||
server, parsed_dict.pop("to_device"))
|
||||
|
||||
room_info_dict = parsed_dict["rooms"]
|
||||
|
||||
join_infos, invite_infos = MatrixSyncEvent._infos_from_dict(
|
||||
server.olm, room_info_dict)
|
||||
|
||||
return cls(server, next_batch, join_infos, invite_infos,
|
||||
one_time_key_count)
|
||||
except (KeyError, ValueError, TypeError):
|
||||
return MatrixErrorEvent.from_dict(server, "Error syncing", False,
|
||||
parsed_dict)
|
||||
|
|
642
matrix/rooms.py
642
matrix/rooms.py
|
@ -1,642 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
# Copyright © 2018 Damir Jelić <poljar@termina.org.uk>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for
|
||||
# any purpose with or without fee is hereby granted, provided that the
|
||||
# above copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
|
||||
# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
|
||||
# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
|
||||
# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
|
||||
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
from pprint import pformat
|
||||
|
||||
from collections import namedtuple, deque
|
||||
|
||||
from matrix.globals import W
|
||||
|
||||
from matrix.colors import Formatted
|
||||
from matrix.utils import (
|
||||
strip_matrix_server, color_for_tags, server_ts_to_weechat,
|
||||
sender_to_nick_and_color, add_event_tags, sanitize_id, sanitize_ts,
|
||||
sanitize_string, sanitize_text, shorten_sender, add_user_to_nicklist,
|
||||
get_prefix_for_level, sanitize_power_level, string_strikethrough,
|
||||
line_pointer_and_tags_from_event, sender_to_prefix_and_color)
|
||||
|
||||
PowerLevel = namedtuple('PowerLevel', ['user', 'level'])
|
||||
|
||||
|
||||
class MatrixRoom:
|
||||
|
||||
def __init__(self, room_id, own_user_id):
|
||||
# type: (str) -> None
|
||||
# yapf: disable
|
||||
self.room_id = room_id # type: str
|
||||
self.own_user_id = own_user_id
|
||||
self.canonical_alias = None # type: str
|
||||
self.name = None # type: str
|
||||
self.prev_batch = "" # type: str
|
||||
self.users = dict() # type: Dict[str, MatrixUser]
|
||||
self.encrypted = False # type: bool
|
||||
self.backlog_pending = False # type: bool
|
||||
self.power_levels = {}
|
||||
# yapf: enable
|
||||
|
||||
def display_name(self, own_user_id):
|
||||
"""
|
||||
Calculate display name for a room.
|
||||
|
||||
Prefer returning the room name if it exists, falling back to
|
||||
a group-style name if not.
|
||||
|
||||
Mostly follows:
|
||||
https://matrix.org/docs/spec/client_server/r0.3.0.html#id268
|
||||
|
||||
An exception is that we prepend '#' before the room name to make it
|
||||
visually distinct from private messages and unnamed groups of users
|
||||
("direct chats") in weechat's buffer list.
|
||||
"""
|
||||
if self.is_named():
|
||||
return self.named_room_name()
|
||||
else:
|
||||
return self.group_name(own_user_id)
|
||||
|
||||
def named_room_name(self):
|
||||
"""
|
||||
Returns the name of the room, if it's a named room. Otherwise return
|
||||
None.
|
||||
"""
|
||||
if self.name:
|
||||
return "#" + self.name
|
||||
elif self.canonical_alias:
|
||||
return self.canonical_alias
|
||||
else:
|
||||
return None
|
||||
|
||||
def group_name(self, own_user_id):
|
||||
"""
|
||||
Returns the group-style name of the room, i.e. a name based on the room
|
||||
members.
|
||||
"""
|
||||
# Sort user display names, excluding our own user and using the
|
||||
# mxid as the sorting key.
|
||||
#
|
||||
# TODO: Hook the user display name disambiguation algorithm here.
|
||||
# Currently, we use the user display names as is, which may not be
|
||||
# unique.
|
||||
users = [user.name for mxid, user
|
||||
in sorted(self.users.items(), key=lambda t: t[0])
|
||||
if mxid != own_user_id]
|
||||
|
||||
num_users = len(users)
|
||||
|
||||
if num_users == 1:
|
||||
return users[0]
|
||||
elif num_users == 2:
|
||||
return " and ".join(users)
|
||||
elif num_users >= 3:
|
||||
return "{first_user} and {num} others".format(
|
||||
first_user=users[0],
|
||||
num=num_users-1)
|
||||
else:
|
||||
return "Empty room?"
|
||||
|
||||
def machine_name(self):
|
||||
"""
|
||||
Calculate an unambiguous, unique machine name for a room.
|
||||
|
||||
Either use the more human-friendly canonical alias, if it exists, or
|
||||
the internal room ID if not.
|
||||
"""
|
||||
if self.canonical_alias:
|
||||
return self.canonical_alias
|
||||
else:
|
||||
return self.room_id
|
||||
|
||||
def is_named(self):
|
||||
"""
|
||||
Is this a named room?
|
||||
|
||||
A named room is a room with either the name or a canonical alias set.
|
||||
"""
|
||||
return self.canonical_alias or self.name
|
||||
|
||||
def is_group(self):
|
||||
"""
|
||||
Is this an ad hoc group of users?
|
||||
|
||||
A group is an unnamed room with no canonical alias.
|
||||
"""
|
||||
return not self.is_named()
|
||||
|
||||
def _handle_membership(self, event):
|
||||
if event.content["membership"] == "join":
|
||||
if event.sender in self.users:
|
||||
user = self.users[event.sender]
|
||||
if "display_name" in event.content:
|
||||
user.display_name = event.content["display_name"]
|
||||
else:
|
||||
short_name = shorten_sender(event.sender)
|
||||
# TODO the default power level doesn't have to be 0
|
||||
level = (self.power_levels[event.sender] if event.sender in
|
||||
self.power_levels else 0)
|
||||
display_name = (event.content["display_name"]
|
||||
if "display_name" in event.content else None)
|
||||
|
||||
user = MatrixUser(short_name, display_name, level)
|
||||
self.users[event.sender] = user
|
||||
return True
|
||||
|
||||
elif event.content["membership"] == "leave":
|
||||
if event.state_key in self.users:
|
||||
del self.users[event.state_key]
|
||||
return True
|
||||
|
||||
elif event.content["membership"] == "invite":
|
||||
pass
|
||||
|
||||
def handle_event(self, event):
|
||||
if isinstance(event, RoomMembershipEvent):
|
||||
return self._handle_membership(event)
|
||||
|
||||
elif isinstance(event, RoomNameEvent):
|
||||
self.name = event.name
|
||||
|
||||
elif isinstance(event, RoomAliasEvent):
|
||||
self.canonical_alias = event.canonical_alias
|
||||
|
||||
elif isinstance(event, RoomEncryptionEvent):
|
||||
self.encrypted = True
|
||||
return True
|
||||
|
||||
elif isinstance(event, RoomPowerLevels):
|
||||
self.power_levels = event.power_levels
|
||||
|
||||
# Update the power levels of the joined users
|
||||
for user_id, level in self.power_levels.items():
|
||||
if user_id in self.users:
|
||||
self.users[user_id].power_level = level
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class MatrixUser:
|
||||
|
||||
def __init__(self, name, display_name=None, power_level=0):
|
||||
# yapf: disable
|
||||
self.name = name # type: str
|
||||
self.display_name = display_name # type: str
|
||||
self.power_level = power_level # type: int
|
||||
# yapf: enable
|
||||
|
||||
|
||||
class RoomInfo():
|
||||
|
||||
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.state = deque(state)
|
||||
self.timeline = deque(timeline)
|
||||
|
||||
@staticmethod
|
||||
def _message_from_event(event):
|
||||
# The transaction id will only be present for events that are send out
|
||||
# from this client, since we print out our own messages as soon as we
|
||||
# get a receive confirmation from the server we don't care about our
|
||||
# own messages in a sync event. More info under:
|
||||
# https://github.com/matrix-org/matrix-doc/blob/master/api/client-server/definitions/event.yaml#L53
|
||||
if "transaction_id" in event["unsigned"]:
|
||||
return None
|
||||
|
||||
if "redacted_by" in event["unsigned"]:
|
||||
return RoomRedactedMessageEvent.from_dict(event)
|
||||
|
||||
return RoomMessageEvent.from_dict(event)
|
||||
|
||||
@staticmethod
|
||||
def parse_event(olm, room_id, event_dict):
|
||||
# type: (Dict[Any, Any]) -> (RoomEvent, RoomEvent)
|
||||
event = None
|
||||
|
||||
if "redacted_by" in event_dict["unsigned"]:
|
||||
event = RoomRedactedMessageEvent.from_dict(event_dict)
|
||||
elif event_dict["type"] == "m.room.message":
|
||||
event = RoomInfo._message_from_event(event_dict)
|
||||
elif event_dict["type"] == "m.room.member":
|
||||
event = RoomMembershipEvent.from_dict(event_dict)
|
||||
elif event_dict["type"] == "m.room.power_levels":
|
||||
event = RoomPowerLevels.from_dict(event_dict)
|
||||
elif event_dict["type"] == "m.room.topic":
|
||||
event = RoomTopicEvent.from_dict(event_dict)
|
||||
elif event_dict["type"] == "m.room.redaction":
|
||||
event = RoomRedactionEvent.from_dict(event_dict)
|
||||
elif event_dict["type"] == "m.room.name":
|
||||
event = RoomNameEvent.from_dict(event_dict)
|
||||
elif event_dict["type"] == "m.room.canonical_alias":
|
||||
event = RoomAliasEvent.from_dict(event_dict)
|
||||
elif event_dict["type"] == "m.room.encryption":
|
||||
event = RoomEncryptionEvent.from_dict(event_dict)
|
||||
elif event_dict["type"] == "m.room.encrypted":
|
||||
event = RoomInfo._decrypt_event(olm, room_id, event_dict)
|
||||
|
||||
return event
|
||||
|
||||
@staticmethod
|
||||
def _decrypt_event(olm, room_id, event_dict):
|
||||
session_id = event_dict["content"]["session_id"]
|
||||
ciphertext = event_dict["content"]["ciphertext"]
|
||||
plaintext, message_index = olm.group_decrypt(
|
||||
room_id,
|
||||
session_id,
|
||||
ciphertext
|
||||
)
|
||||
|
||||
if not plaintext:
|
||||
return UndecryptedEvent.from_dict(event_dict)
|
||||
|
||||
parsed_plaintext = json.loads(plaintext, encoding="utf-8")
|
||||
|
||||
event_dict["content"] = parsed_plaintext["content"]
|
||||
event_dict["type"] = parsed_plaintext["type"]
|
||||
|
||||
return RoomInfo.parse_event(olm, room_id, event_dict)
|
||||
|
||||
@staticmethod
|
||||
def _parse_events(olm, room_id, parsed_dict):
|
||||
events = []
|
||||
|
||||
for event in parsed_dict:
|
||||
try:
|
||||
e = RoomInfo.parse_event(olm, room_id, event)
|
||||
except (ValueError, TypeError, KeyError) as error:
|
||||
message = ("{prefix}matrix: Error parsing "
|
||||
"room event of type {type}: "
|
||||
"{error}\n{event}").format(
|
||||
prefix=W.prefix("error"),
|
||||
type=event["type"],
|
||||
error=pformat(error),
|
||||
event=pformat(event))
|
||||
W.prnt("", message)
|
||||
e = BadEvent.from_dict(event)
|
||||
|
||||
events.append(e)
|
||||
|
||||
return events
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, olm, room_id, parsed_dict):
|
||||
prev_batch = sanitize_id(parsed_dict['timeline']['prev_batch'])
|
||||
|
||||
state_dict = parsed_dict['state']['events']
|
||||
timeline_dict = parsed_dict['timeline']['events']
|
||||
|
||||
state_events = RoomInfo._parse_events(
|
||||
olm,
|
||||
room_id,
|
||||
state_dict
|
||||
)
|
||||
timeline_events = RoomInfo._parse_events(
|
||||
olm,
|
||||
room_id,
|
||||
timeline_dict
|
||||
)
|
||||
|
||||
return cls(
|
||||
room_id,
|
||||
prev_batch,
|
||||
list(filter(None, state_events)),
|
||||
list(filter(None, timeline_events))
|
||||
)
|
||||
|
||||
|
||||
class RoomEvent(object):
|
||||
|
||||
def __init__(self, event_id, sender, timestamp):
|
||||
self.event_id = event_id
|
||||
self.sender = sender
|
||||
self.timestamp = timestamp
|
||||
|
||||
|
||||
class UndecryptedEvent(RoomEvent):
|
||||
def __init__(self, event_id, sender, timestamp, session_id):
|
||||
self.session_id = session_id
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event):
|
||||
event_id = (sanitize_id(event["event_id"])
|
||||
if "event_id" in event else None)
|
||||
sender = (sanitize_id(event["sender"])
|
||||
if "sender" in event else None)
|
||||
timestamp = (sanitize_ts(event["origin_server_ts"])
|
||||
if "origin_server_ts" in event else None)
|
||||
session_id = event["content"]["session_id"]
|
||||
|
||||
return cls(event_id, sender, timestamp, session_id)
|
||||
|
||||
|
||||
class BadEvent(RoomEvent):
|
||||
def __init__(self, event_id, sender, timestamp, source):
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
self.source = source
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event):
|
||||
event_id = (sanitize_id(event["event_id"])
|
||||
if "event_id" in event else None)
|
||||
sender = (sanitize_id(event["sender"])
|
||||
if "sender" in event else None)
|
||||
timestamp = (sanitize_ts(event["origin_server_ts"])
|
||||
if "origin_server_ts" in event else None)
|
||||
source = json.dumps(event)
|
||||
|
||||
return cls(event_id, sender, timestamp, source)
|
||||
|
||||
|
||||
class RoomRedactedMessageEvent(RoomEvent):
|
||||
|
||||
def __init__(self, event_id, sender, timestamp, censor, reason=None):
|
||||
self.censor = censor
|
||||
self.reason = reason
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event):
|
||||
event_id = sanitize_id(event["event_id"])
|
||||
sender = sanitize_id(event["sender"])
|
||||
timestamp = sanitize_ts(event["origin_server_ts"])
|
||||
|
||||
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, timestamp, censor, reason)
|
||||
|
||||
class RoomMessageEvent(RoomEvent):
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event):
|
||||
if event['content']['msgtype'] == 'm.text':
|
||||
return RoomMessageText.from_dict(event)
|
||||
elif event['content']['msgtype'] == 'm.image':
|
||||
return RoomMessageMedia.from_dict(event)
|
||||
elif event['content']['msgtype'] == 'm.audio':
|
||||
return RoomMessageMedia.from_dict(event)
|
||||
elif event['content']['msgtype'] == 'm.file':
|
||||
return RoomMessageMedia.from_dict(event)
|
||||
elif event['content']['msgtype'] == 'm.video':
|
||||
return RoomMessageMedia.from_dict(event)
|
||||
elif event['content']['msgtype'] == 'm.emote':
|
||||
return RoomMessageEmote.from_dict(event)
|
||||
elif event['content']['msgtype'] == 'm.notice':
|
||||
return RoomMessageNotice.from_dict(event)
|
||||
return RoomMessageUnknown.from_dict(event)
|
||||
|
||||
def _print_message(self, message, room, buff, tags):
|
||||
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)
|
||||
|
||||
prefix, prefix_color = sender_to_prefix_and_color(room, self.sender)
|
||||
|
||||
prefix_string = ("" if not prefix else "{}{}{}".format(
|
||||
W.color(prefix_color), prefix, W.color("reset")))
|
||||
|
||||
data = "{prefix}{color}{author}{ncolor}\t{msg}".format(
|
||||
prefix=prefix_string,
|
||||
color=W.color(color_name),
|
||||
author=nick,
|
||||
ncolor=W.color("reset"),
|
||||
msg=message)
|
||||
|
||||
date = server_ts_to_weechat(self.timestamp)
|
||||
W.prnt_date_tags(buff, date, tags_string, data)
|
||||
|
||||
|
||||
class RoomMessageSimple(RoomMessageEvent):
|
||||
|
||||
def __init__(self, event_id, sender, timestamp, message, message_type):
|
||||
self.message = message
|
||||
self.message_type = message_type
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event):
|
||||
event_id = sanitize_id(event["event_id"])
|
||||
sender = sanitize_id(event["sender"])
|
||||
timestamp = sanitize_ts(event["origin_server_ts"])
|
||||
|
||||
message = sanitize_text(event["content"]["body"])
|
||||
message_type = sanitize_text(event["content"]["msgtype"])
|
||||
|
||||
return cls(event_id, sender, timestamp, message, message_type)
|
||||
|
||||
|
||||
class RoomMessageUnknown(RoomMessageSimple):
|
||||
pass
|
||||
|
||||
|
||||
class RoomMessageText(RoomMessageEvent):
|
||||
|
||||
def __init__(self, event_id, sender, timestamp, message, formatted_message=None):
|
||||
self.message = message
|
||||
self.formatted_message = formatted_message
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event):
|
||||
event_id = sanitize_id(event["event_id"])
|
||||
sender = sanitize_id(event["sender"])
|
||||
timestamp = sanitize_ts(event["origin_server_ts"])
|
||||
|
||||
msg = ""
|
||||
formatted_msg = None
|
||||
|
||||
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, timestamp, msg, formatted_msg)
|
||||
|
||||
|
||||
class RoomMessageEmote(RoomMessageSimple):
|
||||
pass
|
||||
|
||||
|
||||
class RoomMessageNotice(RoomMessageText):
|
||||
pass
|
||||
|
||||
|
||||
class RoomMessageMedia(RoomMessageEvent):
|
||||
|
||||
def __init__(self, event_id, sender, timestamp, url, description):
|
||||
self.url = url
|
||||
self.description = description
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event):
|
||||
event_id = sanitize_id(event["event_id"])
|
||||
sender = sanitize_id(event["sender"])
|
||||
timestamp = sanitize_ts(event["origin_server_ts"])
|
||||
|
||||
mxc_url = sanitize_text(event['content']['url'])
|
||||
description = sanitize_text(event["content"]["body"])
|
||||
|
||||
return cls(event_id, sender, timestamp, mxc_url, description)
|
||||
|
||||
|
||||
class RoomMembershipEvent(RoomEvent):
|
||||
def __init__(
|
||||
self,
|
||||
event_id,
|
||||
sender,
|
||||
timestamp,
|
||||
state_key,
|
||||
content,
|
||||
prev_content
|
||||
):
|
||||
self.state_key = state_key
|
||||
self.content = content
|
||||
self.prev_content = prev_content
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event_dict):
|
||||
event_id = sanitize_id(event_dict["event_id"])
|
||||
sender = sanitize_id(event_dict["sender"])
|
||||
timestamp = sanitize_ts(event_dict["origin_server_ts"])
|
||||
state_key = sanitize_id(event_dict["state_key"])
|
||||
content = event_dict["content"]
|
||||
prev_content = (event_dict["unsigned"]["prev_content"]
|
||||
if "prev_content" in event_dict["unsigned"] else None)
|
||||
|
||||
return cls(
|
||||
event_id,
|
||||
sender,
|
||||
timestamp,
|
||||
state_key,
|
||||
content,
|
||||
prev_content
|
||||
)
|
||||
|
||||
|
||||
class RoomPowerLevels(RoomEvent):
|
||||
|
||||
def __init__(self, event_id, sender, timestamp, power_levels):
|
||||
self.power_levels = power_levels
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event_dict):
|
||||
event_id = sanitize_id(event_dict["event_id"])
|
||||
sender = sanitize_id(event_dict["sender"])
|
||||
timestamp = sanitize_ts(event_dict["origin_server_ts"])
|
||||
|
||||
power_levels = event_dict["content"].pop("users")
|
||||
|
||||
return cls(event_id, sender, timestamp, power_levels)
|
||||
|
||||
|
||||
class RoomTopicEvent(RoomEvent):
|
||||
|
||||
def __init__(self, event_id, sender, timestamp, topic):
|
||||
self.topic = topic
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event_dict):
|
||||
event_id = sanitize_id(event_dict["event_id"])
|
||||
sender = sanitize_id(event_dict["sender"])
|
||||
timestamp = sanitize_ts(event_dict["origin_server_ts"])
|
||||
|
||||
topic = sanitize_text(event_dict["content"]["topic"])
|
||||
|
||||
return cls(event_id, sender, timestamp, topic)
|
||||
|
||||
|
||||
class RoomRedactionEvent(RoomEvent):
|
||||
|
||||
def __init__(self, event_id, sender, timestamp, redaction_id, reason=None):
|
||||
self.redaction_id = redaction_id
|
||||
self.reason = reason
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event_dict):
|
||||
event_id = sanitize_id(event_dict["event_id"])
|
||||
sender = sanitize_id(event_dict["sender"])
|
||||
timestamp = sanitize_ts(event_dict["origin_server_ts"])
|
||||
|
||||
redaction_id = sanitize_id(event_dict["redacts"])
|
||||
|
||||
reason = (sanitize_text(event_dict["content"]["reason"])
|
||||
if "reason" in event_dict["content"] else None)
|
||||
|
||||
return cls(event_id, sender, timestamp, redaction_id, reason)
|
||||
|
||||
|
||||
class RoomNameEvent(RoomEvent):
|
||||
|
||||
def __init__(self, event_id, sender, timestamp, name):
|
||||
self.name = name
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event_dict):
|
||||
event_id = sanitize_id(event_dict["event_id"])
|
||||
sender = sanitize_id(event_dict["sender"])
|
||||
timestamp = sanitize_ts(event_dict["origin_server_ts"])
|
||||
|
||||
name = sanitize_string(event_dict['content']['name'])
|
||||
|
||||
return cls(event_id, sender, timestamp, name)
|
||||
|
||||
|
||||
class RoomAliasEvent(RoomEvent):
|
||||
|
||||
def __init__(self, event_id, sender, timestamp, canonical_alias):
|
||||
self.canonical_alias = canonical_alias
|
||||
RoomEvent.__init__(self, event_id, sender, timestamp)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event_dict):
|
||||
event_id = sanitize_id(event_dict["event_id"])
|
||||
sender = sanitize_id(event_dict["sender"])
|
||||
timestamp = sanitize_ts(event_dict["origin_server_ts"])
|
||||
|
||||
canonical_alias = sanitize_id(event_dict["content"]["alias"])
|
||||
|
||||
return cls(event_id, sender, timestamp, canonical_alias)
|
||||
|
||||
|
||||
class RoomEncryptionEvent(RoomEvent):
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, event_dict):
|
||||
event_id = sanitize_id(event_dict["event_id"])
|
||||
sender = sanitize_id(event_dict["sender"])
|
||||
timestamp = sanitize_ts(event_dict["origin_server_ts"])
|
||||
|
||||
return cls(event_id, sender, timestamp)
|
134
matrix/server.py
134
matrix/server.py
|
@ -28,7 +28,7 @@ import json
|
|||
from collections import deque, defaultdict
|
||||
from http_parser.pyparser import HttpParser
|
||||
|
||||
from nio import Client, LoginResponse
|
||||
from nio import Client, LoginResponse, SyncRepsponse
|
||||
|
||||
from matrix.plugin_options import Option, DebugType
|
||||
from matrix.utils import (key_from_value, prnt_debug, server_buffer_prnt,
|
||||
|
@ -37,12 +37,7 @@ from matrix.utils import (key_from_value, prnt_debug, server_buffer_prnt,
|
|||
from matrix.utf import utf8_decode
|
||||
from matrix.globals import W, SERVERS, OPTIONS
|
||||
import matrix.api as API
|
||||
from .buffer import RoomBuffer
|
||||
from .rooms import (
|
||||
MatrixRoom,
|
||||
RoomMessageText,
|
||||
RoomMessageEmote,
|
||||
)
|
||||
from .buffer import RoomBuffer, OwnMessage, OwnAction
|
||||
from matrix.api import (
|
||||
MatrixClient,
|
||||
MatrixSyncMessage,
|
||||
|
@ -56,8 +51,6 @@ from matrix.api import (
|
|||
)
|
||||
|
||||
from .events import (
|
||||
MatrixLoginEvent,
|
||||
MatrixSyncEvent,
|
||||
MatrixSendEvent,
|
||||
MatrixBacklogEvent,
|
||||
MatrixErrorEvent,
|
||||
|
@ -97,7 +90,6 @@ class MatrixServer:
|
|||
self.user = "" # type: str
|
||||
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
|
||||
|
@ -610,10 +602,10 @@ class MatrixServer:
|
|||
def query_keys(self):
|
||||
users = []
|
||||
|
||||
for room in self.rooms.values():
|
||||
if not room.encrypted:
|
||||
for room_buffer in self.room_buffers.values():
|
||||
if not room_buffer.room.encrypted:
|
||||
continue
|
||||
users += list(room.users)
|
||||
users += list(room_buffer.room.users)
|
||||
|
||||
if not users:
|
||||
return
|
||||
|
@ -642,68 +634,13 @@ class MatrixServer:
|
|||
server_buffer_prnt(self, pprint.pformat(message.request.payload))
|
||||
server_buffer_prnt(self, pprint.pformat(message.response.body))
|
||||
|
||||
def _loop_events(self, info, n):
|
||||
|
||||
for i in range(n+1):
|
||||
is_state = False
|
||||
try:
|
||||
event = info.state.popleft()
|
||||
is_state = True
|
||||
except IndexError:
|
||||
try:
|
||||
event = info.timeline.popleft()
|
||||
except IndexError:
|
||||
return i
|
||||
|
||||
room, room_buffer = self.find_room_from_id(info.room_id)
|
||||
# The room changed it's members, if the room is encrypted update
|
||||
# the device list
|
||||
if room.handle_event(event) and room.encrypted:
|
||||
self.device_check_timestamp = None
|
||||
|
||||
if is_state:
|
||||
room_buffer.handle_state_event(event)
|
||||
else:
|
||||
room_buffer.handle_timeline_event(event)
|
||||
|
||||
self.event_queue.appendleft(info)
|
||||
return i
|
||||
|
||||
def handle_events(self):
|
||||
n = 25
|
||||
|
||||
while True:
|
||||
try:
|
||||
info = self.event_queue.popleft()
|
||||
except IndexError:
|
||||
if self.event_queue_timer:
|
||||
W.unhook(self.event_queue_timer)
|
||||
self.event_queue_timer = None
|
||||
|
||||
self.sync()
|
||||
return
|
||||
|
||||
ret = self._loop_events(info, n)
|
||||
|
||||
if ret < n:
|
||||
n = n - ret
|
||||
else:
|
||||
self.event_queue.appendleft(info)
|
||||
|
||||
if not self.event_queue_timer:
|
||||
hook = W.hook_timer(1 * 100, 0, 0, "matrix_event_timer_cb",
|
||||
self.name)
|
||||
self.event_queue_timer = hook
|
||||
|
||||
return
|
||||
|
||||
def handle_own_messages(self, room_buffer, message):
|
||||
if isinstance(message, RoomMessageText):
|
||||
room_buffer.self_message(message)
|
||||
return
|
||||
elif isinstance(message, RoomMessageEmote):
|
||||
if isinstance(message, OwnAction):
|
||||
room_buffer.self_action(message)
|
||||
return
|
||||
elif isinstance(message, OwnMessage):
|
||||
room_buffer.self_message(message)
|
||||
return
|
||||
|
||||
raise NotImplementedError("Unsupported message of type {}".format(
|
||||
type(message)))
|
||||
|
@ -736,19 +673,18 @@ class MatrixServer:
|
|||
|
||||
self.sync()
|
||||
|
||||
def _queue_joined_info(self, response):
|
||||
while response.joined_room_infos:
|
||||
info = response.joined_room_infos.pop()
|
||||
def _handle_room_info(self, response):
|
||||
for room_id, join_info in response.rooms.join.items():
|
||||
if room_id not in self.buffers:
|
||||
self.create_room_buffer(room_id)
|
||||
|
||||
if info.room_id not in self.buffers:
|
||||
self.create_room_buffer(info.room_id)
|
||||
room_buffer = self.find_room_from_id(room_id)
|
||||
|
||||
room = self.rooms[info.room_id]
|
||||
for event in join_info.state:
|
||||
room_buffer.handle_state_event(event)
|
||||
|
||||
if not room.prev_batch:
|
||||
room.prev_batch = info.prev_batch
|
||||
|
||||
self.event_queue.append(info)
|
||||
for event in join_info.timeline.events:
|
||||
room_buffer.handle_timeline_event(event)
|
||||
|
||||
def _handle_sync(self, response):
|
||||
# we got the same batch again, nothing to do
|
||||
|
@ -756,27 +692,20 @@ class MatrixServer:
|
|||
self.sync()
|
||||
return
|
||||
|
||||
self._queue_joined_info(response)
|
||||
self._handle_room_info(response)
|
||||
# self._queue_joined_info(response)
|
||||
self.next_batch = response.next_batch
|
||||
# self.check_one_time_keys(response.one_time_key_count)
|
||||
self.handle_events()
|
||||
# self.handle_events()
|
||||
|
||||
def handle_matrix_response(self, response):
|
||||
if isinstance(response, MatrixLoginEvent):
|
||||
self._handle_login(response)
|
||||
|
||||
elif isinstance(response, MatrixSyncEvent):
|
||||
self._handle_sync(response)
|
||||
|
||||
elif isinstance(response, MatrixSendEvent):
|
||||
_, room_buffer = self.find_room_from_id(response.room_id)
|
||||
if isinstance(response, MatrixSendEvent):
|
||||
room_buffer = self.find_room_from_id(response.room_id)
|
||||
self.handle_own_messages(room_buffer, response.message)
|
||||
|
||||
elif isinstance(response, MatrixBacklogEvent):
|
||||
room, room_buffer = self.find_room_from_id(response.room_id)
|
||||
room_buffer = self.find_room_from_id(response.room_id)
|
||||
room_buffer.handle_backlog(response.events)
|
||||
room.prev_batch = response.end_token
|
||||
room.backlog_pending = False
|
||||
W.bar_item_update("buffer_modes")
|
||||
|
||||
elif isinstance(response, MatrixErrorEvent):
|
||||
|
@ -787,10 +716,14 @@ class MatrixServer:
|
|||
|
||||
if isinstance(response, LoginResponse):
|
||||
self._handle_login(response)
|
||||
elif isinstance(response, SyncRepsponse):
|
||||
self._handle_sync(response)
|
||||
|
||||
def nio_parse_response(self, response):
|
||||
if isinstance(response, MatrixLoginMessage):
|
||||
self.nio_client.receive("login", response.response.body)
|
||||
elif isinstance(response, MatrixSyncMessage):
|
||||
self.nio_client.receive("sync", response.response.body)
|
||||
|
||||
self.nio_receive()
|
||||
|
||||
|
@ -804,7 +737,7 @@ class MatrixServer:
|
|||
if ('content-type' in message.response.headers and
|
||||
message.response.headers['content-type'] == 'application/json'):
|
||||
|
||||
if isinstance(message, MatrixLoginMessage):
|
||||
if isinstance(message, (MatrixLoginMessage, MatrixSyncMessage)):
|
||||
self.nio_parse_response(message)
|
||||
|
||||
else:
|
||||
|
@ -850,24 +783,21 @@ class MatrixServer:
|
|||
return
|
||||
|
||||
def create_room_buffer(self, room_id):
|
||||
room = MatrixRoom(room_id, self.user_id)
|
||||
room = self.nio_client.rooms[room_id]
|
||||
buf = RoomBuffer(room, self.name)
|
||||
# TODO this should turned into a propper class
|
||||
self.room_buffers[room_id] = buf
|
||||
self.buffers[room_id] = buf.weechat_buffer._ptr
|
||||
self.rooms[room_id] = room
|
||||
|
||||
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
|
||||
return 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
|
||||
return room_buffer
|
||||
|
||||
|
||||
@utf8_decode
|
||||
|
|
Loading…
Add table
Reference in a new issue