buffer: Add a matrix specific buffer class.
This commit is contained in:
parent
ae5f97ce1d
commit
66507b23ce
3 changed files with 415 additions and 241 deletions
398
matrix/buffer.py
398
matrix/buffer.py
|
@ -18,11 +18,29 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
from builtins import super
|
||||||
|
from functools import partial
|
||||||
|
|
||||||
from .globals import W, SERVERS, SCRIPT_NAME
|
from .globals import W, SERVERS, OPTIONS, SCRIPT_NAME
|
||||||
from .utf import utf8_decode
|
from .utf import utf8_decode
|
||||||
from .colors import Formatted
|
from .colors import Formatted
|
||||||
from builtins import super
|
from .utils import shorten_sender, server_ts_to_weechat, string_strikethrough
|
||||||
|
from .plugin_options import RedactType
|
||||||
|
|
||||||
|
|
||||||
|
from .rooms import (
|
||||||
|
RoomNameEvent,
|
||||||
|
RoomAliasEvent,
|
||||||
|
RoomMembershipEvent,
|
||||||
|
RoomMemberJoin,
|
||||||
|
RoomMemberLeave,
|
||||||
|
RoomMemberInvite,
|
||||||
|
RoomTopicEvent,
|
||||||
|
RoomMessageText,
|
||||||
|
RoomMessageEmote,
|
||||||
|
RoomRedactionEvent,
|
||||||
|
RoomRedactedMessageEvent
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@utf8_decode
|
@utf8_decode
|
||||||
|
@ -91,6 +109,12 @@ class WeechatChannelBuffer(object):
|
||||||
"self_msg",
|
"self_msg",
|
||||||
"log1"
|
"log1"
|
||||||
],
|
],
|
||||||
|
"action": [
|
||||||
|
SCRIPT_NAME + "_message",
|
||||||
|
SCRIPT_NAME + "_action",
|
||||||
|
"notify_message",
|
||||||
|
"log1",
|
||||||
|
],
|
||||||
"old_message": [
|
"old_message": [
|
||||||
SCRIPT_NAME + "_message",
|
SCRIPT_NAME + "_message",
|
||||||
"notify_message",
|
"notify_message",
|
||||||
|
@ -126,6 +150,91 @@ class WeechatChannelBuffer(object):
|
||||||
"invite": "has been invited to"
|
"invite": "has been invited to"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Line(object):
|
||||||
|
def __init__(self, pointer):
|
||||||
|
self._ptr = pointer
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _hdata(self):
|
||||||
|
return W.hdata_get("line_data")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def prefix(self):
|
||||||
|
return W.hdata_string(self._hdata, self._ptr, "prefix")
|
||||||
|
|
||||||
|
@prefix.setter
|
||||||
|
def prefix(self, new_prefix):
|
||||||
|
new_data = {"prefix": new_prefix}
|
||||||
|
W.hdata_update(self._hdata, self._ptr, new_data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def message(self):
|
||||||
|
return W.hdata_string(self._hdata, self._ptr, "message")
|
||||||
|
|
||||||
|
@message.setter
|
||||||
|
def message(self, new_message):
|
||||||
|
# type: (str) -> None
|
||||||
|
new_data = {"message": new_message}
|
||||||
|
W.hdata_update(self._hdata, self._ptr, new_data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tags(self):
|
||||||
|
tags_count = W.hdata_get_var_array_size(
|
||||||
|
self._hdata,
|
||||||
|
self._ptr,
|
||||||
|
"tags_array"
|
||||||
|
)
|
||||||
|
|
||||||
|
tags = [
|
||||||
|
W.hdata_string(self._hdata, self._ptr, "%d|tags_array" % i)
|
||||||
|
for i in range(tags_count)
|
||||||
|
]
|
||||||
|
return tags
|
||||||
|
|
||||||
|
@tags.setter
|
||||||
|
def tags(self, new_tags):
|
||||||
|
# type: (List[str]) -> None
|
||||||
|
new_data = {"tags_array": ",".join(new_tags)}
|
||||||
|
W.hdata_update(self._hdata, self._ptr, new_data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def date(self):
|
||||||
|
# type: () -> int
|
||||||
|
return W.hdata_time(self._hdata, self._ptr, "date")
|
||||||
|
|
||||||
|
@date.setter
|
||||||
|
def date(self, new_date):
|
||||||
|
# type: (int) -> None
|
||||||
|
new_data = {"date": new_date}
|
||||||
|
W.hdata_update(self._hdata, self._ptr, new_data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def date_printed(self):
|
||||||
|
# type: () -> int
|
||||||
|
return W.hdata_time(self._hdata, self._ptr, "date_printed")
|
||||||
|
|
||||||
|
@date_printed.setter
|
||||||
|
def date_printed(self, new_date):
|
||||||
|
# type: (int) -> None
|
||||||
|
new_data = {"date_printed": new_date}
|
||||||
|
W.hdata_update(self._hdata, self._ptr, new_data)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def highlight(self):
|
||||||
|
# type: () -> bool
|
||||||
|
return bool(W.hdata_char(self._hdata, self._ptr, "highlight"))
|
||||||
|
|
||||||
|
def update(self, date, date_printed, tags, prefix, message):
|
||||||
|
new_data = {
|
||||||
|
"date": date,
|
||||||
|
"date_printed": date_printed,
|
||||||
|
"tags_array": ','.join(tags),
|
||||||
|
"prefix": prefix,
|
||||||
|
"message": message,
|
||||||
|
# "highlight": highlight
|
||||||
|
}
|
||||||
|
W.hdata_update(self._hdata, self._ptr, new_data)
|
||||||
|
|
||||||
def __init__(self, name, server_name, user):
|
def __init__(self, name, server_name, user):
|
||||||
# type: (str, str, str)
|
# type: (str, str, str)
|
||||||
self._ptr = W.buffer_new(
|
self._ptr = W.buffer_new(
|
||||||
|
@ -186,6 +295,8 @@ class WeechatChannelBuffer(object):
|
||||||
W.buffer_set(self._ptr, "nicklist", "1")
|
W.buffer_set(self._ptr, "nicklist", "1")
|
||||||
W.buffer_set(self._ptr, "nicklist_display_groups", "0")
|
W.buffer_set(self._ptr, "nicklist_display_groups", "0")
|
||||||
|
|
||||||
|
W.buffer_set(self._ptr, "highlight_words", user)
|
||||||
|
|
||||||
# TODO make this configurable
|
# TODO make this configurable
|
||||||
W.buffer_set(
|
W.buffer_set(
|
||||||
self._ptr,
|
self._ptr,
|
||||||
|
@ -193,6 +304,36 @@ class WeechatChannelBuffer(object):
|
||||||
SCRIPT_NAME + "_message"
|
SCRIPT_NAME + "_message"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _hdata(self):
|
||||||
|
return W.hdata_get("buffer")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lines(self):
|
||||||
|
own_lines = W.hdata_pointer(
|
||||||
|
self._hdata,
|
||||||
|
self._ptr,
|
||||||
|
"own_lines"
|
||||||
|
)
|
||||||
|
|
||||||
|
if own_lines:
|
||||||
|
hdata_line = W.hdata_get("line")
|
||||||
|
|
||||||
|
line_pointer = W.hdata_pointer(
|
||||||
|
W.hdata_get("lines"), own_lines, "last_line")
|
||||||
|
|
||||||
|
while line_pointer:
|
||||||
|
data_pointer = W.hdata_pointer(
|
||||||
|
hdata_line,
|
||||||
|
line_pointer,
|
||||||
|
"data"
|
||||||
|
)
|
||||||
|
|
||||||
|
if data_pointer:
|
||||||
|
yield WeechatChannelBuffer.Line(data_pointer)
|
||||||
|
|
||||||
|
line_pointer = W.hdata_move(hdata_line, line_pointer, -1)
|
||||||
|
|
||||||
def _print(self, string):
|
def _print(self, string):
|
||||||
# type: (str) -> None
|
# type: (str) -> None
|
||||||
""" Print a string to the room buffer """
|
""" Print a string to the room buffer """
|
||||||
|
@ -247,11 +388,7 @@ class WeechatChannelBuffer(object):
|
||||||
# A message from a non joined user
|
# A message from a non joined user
|
||||||
return RoomUser(nick)
|
return RoomUser(nick)
|
||||||
|
|
||||||
def message(self, nick, message, date, tags=[]):
|
def _print_message(self, user, message, date, tags):
|
||||||
# type: (str, str, int, str) -> None
|
|
||||||
user = self._get_user(nick)
|
|
||||||
tags = tags or self._message_tags(user, "message")
|
|
||||||
|
|
||||||
prefix_string = ("" if not user.prefix else "{}{}{}".format(
|
prefix_string = ("" if not user.prefix else "{}{}{}".format(
|
||||||
W.color(self._get_prefix_color(user.prefix)),
|
W.color(self._get_prefix_color(user.prefix)),
|
||||||
user.prefix,
|
user.prefix,
|
||||||
|
@ -267,6 +404,12 @@ class WeechatChannelBuffer(object):
|
||||||
|
|
||||||
self.print_date_tags(data, date, tags)
|
self.print_date_tags(data, date, tags)
|
||||||
|
|
||||||
|
def message(self, nick, message, date, extra_tags=[]):
|
||||||
|
# type: (str, str, int, str) -> None
|
||||||
|
user = self._get_user(nick)
|
||||||
|
tags = self._message_tags(user, "message") + extra_tags
|
||||||
|
self._print_message(user, message, date, tags)
|
||||||
|
|
||||||
def notice(self, nick, message, date):
|
def notice(self, nick, message, date):
|
||||||
# type: (str, str, int) -> None
|
# type: (str, str, int) -> None
|
||||||
data = "{color}{message}{ncolor}".format(
|
data = "{color}{message}{ncolor}".format(
|
||||||
|
@ -276,11 +419,7 @@ class WeechatChannelBuffer(object):
|
||||||
|
|
||||||
self.message(nick, data, date)
|
self.message(nick, data, date)
|
||||||
|
|
||||||
def action(self, nick, message, date, tags=[]):
|
def _print_action(self, user, message, date, tags):
|
||||||
# type: (str, str, int) -> None
|
|
||||||
user = self._get_user(nick)
|
|
||||||
tags = tags or self._message_tags(user, "action")
|
|
||||||
|
|
||||||
nick_prefix = ("" if not user.prefix else "{}{}{}".format(
|
nick_prefix = ("" if not user.prefix else "{}{}{}".format(
|
||||||
W.color(self._get_prefix_color(user.prefix)),
|
W.color(self._get_prefix_color(user.prefix)),
|
||||||
user.prefix,
|
user.prefix,
|
||||||
|
@ -292,12 +431,18 @@ class WeechatChannelBuffer(object):
|
||||||
prefix=W.prefix("action"),
|
prefix=W.prefix("action"),
|
||||||
nick_prefix=nick_prefix,
|
nick_prefix=nick_prefix,
|
||||||
nick_color=W.color(user.color),
|
nick_color=W.color(user.color),
|
||||||
author=nick,
|
author=user.nick,
|
||||||
ncolor=W.color("reset"),
|
ncolor=W.color("reset"),
|
||||||
msg=message)
|
msg=message)
|
||||||
|
|
||||||
self.print_date_tags(data, date, tags)
|
self.print_date_tags(data, date, tags)
|
||||||
|
|
||||||
|
def action(self, nick, message, date, extra_tags=[]):
|
||||||
|
# type: (str, str, int) -> None
|
||||||
|
user = self._get_user(nick)
|
||||||
|
tags = self._message_tags(user, "action") + extra_tags
|
||||||
|
self._print_action(user, message, date, tags)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_nicklist_group(user):
|
def _get_nicklist_group(user):
|
||||||
# type: (WeechatUser) -> str
|
# type: (WeechatUser) -> str
|
||||||
|
@ -456,13 +601,13 @@ class WeechatChannelBuffer(object):
|
||||||
def self_message(self, nick, message, date):
|
def self_message(self, nick, message, date):
|
||||||
user = self._get_user(nick)
|
user = self._get_user(nick)
|
||||||
tags = self._message_tags(user, "self_message")
|
tags = self._message_tags(user, "self_message")
|
||||||
self.message(nick, message, date, tags)
|
self._print_message(user, message, date, tags)
|
||||||
|
|
||||||
def self_action(self, nick, message, date):
|
def self_action(self, nick, message, date):
|
||||||
user = self._get_user(nick)
|
user = self._get_user(nick)
|
||||||
tags = self._message_tags(user, "self_message")
|
tags = self._message_tags(user, "self_message")
|
||||||
tags.append(SCRIPT_NAME + "_action")
|
tags.append(SCRIPT_NAME + "_action")
|
||||||
self.action(nick, message, date, tags)
|
self._print_action(user, message, date, tags)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def short_name(self):
|
def short_name(self):
|
||||||
|
@ -471,3 +616,226 @@ class WeechatChannelBuffer(object):
|
||||||
@short_name.setter
|
@short_name.setter
|
||||||
def short_name(self, name):
|
def short_name(self, name):
|
||||||
W.buffer_set(self._ptr, "short_name", name)
|
W.buffer_set(self._ptr, "short_name", name)
|
||||||
|
|
||||||
|
def find_lines(self, predicate):
|
||||||
|
lines = []
|
||||||
|
for line in self.lines:
|
||||||
|
if predicate(line):
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
return lines
|
||||||
|
|
||||||
|
|
||||||
|
class RoomBuffer(object):
|
||||||
|
def __init__(self, room, server_name):
|
||||||
|
self.room = room
|
||||||
|
user = shorten_sender(self.room.own_user_id)
|
||||||
|
self.weechat_buffer = WeechatChannelBuffer(
|
||||||
|
room.room_id,
|
||||||
|
server_name,
|
||||||
|
user
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_membership_events(self, event, is_state):
|
||||||
|
def join(event, date, is_state):
|
||||||
|
user = self.room.users[event.sender]
|
||||||
|
buffer_user = RoomUser(user.name, event.sender)
|
||||||
|
# TODO remove this duplication
|
||||||
|
user.nick_color = buffer_user.color
|
||||||
|
|
||||||
|
if self.room.own_user_id == event.sender:
|
||||||
|
buffer_user.color = "weechat.color.chat_nick_self"
|
||||||
|
user.nick_color = "weechat.color.chat_nick_self"
|
||||||
|
|
||||||
|
self.weechat_buffer.join(
|
||||||
|
buffer_user,
|
||||||
|
server_ts_to_weechat(event.timestamp),
|
||||||
|
not is_state
|
||||||
|
)
|
||||||
|
|
||||||
|
date = server_ts_to_weechat(event.timestamp)
|
||||||
|
|
||||||
|
if isinstance(event, RoomMemberJoin):
|
||||||
|
if event.prev_content and "membership" in event.prev_content:
|
||||||
|
if (event.prev_content["membership"] == "leave"
|
||||||
|
or event.prev_content["membership"] == "invite"):
|
||||||
|
join(event, date, is_state)
|
||||||
|
else:
|
||||||
|
# TODO print out profile changes
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# No previous content for this user in this room, so he just
|
||||||
|
# joined.
|
||||||
|
join(event, date, is_state)
|
||||||
|
|
||||||
|
elif isinstance(event, RoomMemberLeave):
|
||||||
|
# TODO the nick can be a display name or a full sender name
|
||||||
|
nick = shorten_sender(event.sender)
|
||||||
|
if event.sender == event.leaving_user:
|
||||||
|
self.weechat_buffer.part(nick, date, not is_state)
|
||||||
|
else:
|
||||||
|
self.weechat_buffer.kick(nick, date, not is_state)
|
||||||
|
|
||||||
|
elif isinstance(event, RoomMemberInvite):
|
||||||
|
if is_state:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.weechat_buffer.invite(event.invited_user, date)
|
||||||
|
return
|
||||||
|
|
||||||
|
room_name = self.room.display_name(self.room.own_user_id)
|
||||||
|
self.weechat_buffer.short_name = room_name
|
||||||
|
|
||||||
|
def _redact_line(self, event):
|
||||||
|
def predicate(event_id, line):
|
||||||
|
def already_redacted(tags):
|
||||||
|
if SCRIPT_NAME + "_redacted" in tags:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
event_tag = SCRIPT_NAME + "_id_{}".format(event_id)
|
||||||
|
tags = line.tags
|
||||||
|
|
||||||
|
if event_tag in tags and not already_redacted(tags):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
lines = self.weechat_buffer.find_lines(
|
||||||
|
partial(predicate, event.redaction_id)
|
||||||
|
)
|
||||||
|
|
||||||
|
# No line to redact, return early
|
||||||
|
if not lines:
|
||||||
|
return
|
||||||
|
|
||||||
|
# TODO multiple lines can contain a single matrix ID, we need to redact
|
||||||
|
# them all
|
||||||
|
line = lines[0]
|
||||||
|
|
||||||
|
# TODO the censor may not be in the room anymore
|
||||||
|
censor = self.room.users[event.sender].name
|
||||||
|
message = line.message
|
||||||
|
tags = line.tags
|
||||||
|
|
||||||
|
reason = ("" if not event.reason else
|
||||||
|
", reason: \"{reason}\"".format(reason=event.reason))
|
||||||
|
|
||||||
|
redaction_msg = ("{del_color}<{log_color}Message redacted by: "
|
||||||
|
"{censor}{log_color}{reason}{del_color}>"
|
||||||
|
"{ncolor}").format(
|
||||||
|
del_color=W.color("chat_delimiters"),
|
||||||
|
ncolor=W.color("reset"),
|
||||||
|
log_color=W.color("logger.color.backlog_line"),
|
||||||
|
censor=censor,
|
||||||
|
reason=reason)
|
||||||
|
|
||||||
|
new_message = ""
|
||||||
|
|
||||||
|
if OPTIONS.redaction_type == RedactType.STRIKETHROUGH:
|
||||||
|
plaintext_msg = W.string_remove_color(message, '')
|
||||||
|
new_message = string_strikethrough(plaintext_msg)
|
||||||
|
elif OPTIONS.redaction_type == RedactType.NOTICE:
|
||||||
|
new_message = message
|
||||||
|
elif OPTIONS.redaction_type == RedactType.DELETE:
|
||||||
|
pass
|
||||||
|
|
||||||
|
message = " ".join(s for s in [new_message, redaction_msg] if s)
|
||||||
|
|
||||||
|
tags.append("matrix_redacted")
|
||||||
|
|
||||||
|
line.message = message
|
||||||
|
line.tags = tags
|
||||||
|
|
||||||
|
def _handle_redacted_message(self, event):
|
||||||
|
# TODO user doesn't have to be in the room anymore
|
||||||
|
user = self.room.users[event.sender]
|
||||||
|
date = server_ts_to_weechat(event.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.room.users[event.censor]
|
||||||
|
|
||||||
|
data = ("{del_color}<{log_color}Message redacted by: "
|
||||||
|
"{censor}{log_color}{reason}{del_color}>{ncolor}").format(
|
||||||
|
del_color=W.color("chat_delimiters"),
|
||||||
|
ncolor=W.color("reset"),
|
||||||
|
log_color=W.color("logger.color.backlog_line"),
|
||||||
|
censor=censor.name,
|
||||||
|
reason=reason)
|
||||||
|
|
||||||
|
self.weechat_buffer.message(user.name, data, date, tags)
|
||||||
|
|
||||||
|
def _handle_topic(self, event, is_state):
|
||||||
|
try:
|
||||||
|
user = self.room.users[event.sender]
|
||||||
|
nick = user.name
|
||||||
|
except KeyError:
|
||||||
|
nick = event.sender
|
||||||
|
|
||||||
|
self.weechat_buffer.change_topic(
|
||||||
|
nick,
|
||||||
|
event.topic,
|
||||||
|
server_ts_to_weechat(event.timestamp),
|
||||||
|
not is_state)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_event_tags(event):
|
||||||
|
return ["matrix_id_{}".format(event.event_id)]
|
||||||
|
|
||||||
|
def handle_state_event(self, event):
|
||||||
|
if isinstance(event, RoomMembershipEvent):
|
||||||
|
self.handle_membership_events(event, True)
|
||||||
|
elif isinstance(event, RoomTopicEvent):
|
||||||
|
self._handle_topic(event, True)
|
||||||
|
|
||||||
|
def handle_timeline_event(self, event):
|
||||||
|
if isinstance(event, RoomMembershipEvent):
|
||||||
|
self.handle_membership_events(event, False)
|
||||||
|
elif isinstance(event, (RoomNameEvent, RoomAliasEvent)):
|
||||||
|
room_name = self.room.display_name(self.room.own_user_id)
|
||||||
|
self.weechat_buffer.short_name = room_name
|
||||||
|
elif isinstance(event, RoomTopicEvent):
|
||||||
|
self._handle_topic(event, False)
|
||||||
|
elif isinstance(event, RoomMessageText):
|
||||||
|
user = self.room.users[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(
|
||||||
|
user.name,
|
||||||
|
data,
|
||||||
|
date,
|
||||||
|
self.get_event_tags(event)
|
||||||
|
)
|
||||||
|
elif isinstance(event, RoomMessageEmote):
|
||||||
|
user = self.room.users[event.sender]
|
||||||
|
date = server_ts_to_weechat(event.timestamp)
|
||||||
|
self.weechat_buffer.action(
|
||||||
|
user.name,
|
||||||
|
event.message,
|
||||||
|
date,
|
||||||
|
self.get_event_tags(event)
|
||||||
|
)
|
||||||
|
elif isinstance(event, RoomRedactionEvent):
|
||||||
|
self._redact_line(event)
|
||||||
|
elif isinstance(event, RoomRedactedMessageEvent):
|
||||||
|
self._handle_redacted_message(event)
|
||||||
|
|
||||||
|
def self_message(self, message):
|
||||||
|
user = self.room.users[self.room.own_user_id]
|
||||||
|
data = (message.formatted_message.to_weechat()
|
||||||
|
if message.formatted_message
|
||||||
|
else message.message)
|
||||||
|
|
||||||
|
date = server_ts_to_weechat(message.timestamp)
|
||||||
|
self.weechat_buffer.self_message(user.name, data, date)
|
||||||
|
|
||||||
|
def self_action(self, message):
|
||||||
|
user = self.room.users[self.room.own_user_id]
|
||||||
|
date = server_ts_to_weechat(message.timestamp)
|
||||||
|
self.weechat_buffer.self_action(user.name, message.message, date)
|
||||||
|
|
131
matrix/rooms.py
131
matrix/rooms.py
|
@ -40,10 +40,11 @@ PowerLevel = namedtuple('PowerLevel', ['user', 'level'])
|
||||||
|
|
||||||
class MatrixRoom:
|
class MatrixRoom:
|
||||||
|
|
||||||
def __init__(self, room_id):
|
def __init__(self, room_id, own_user_id):
|
||||||
# type: (str) -> None
|
# type: (str) -> None
|
||||||
# yapf: disable
|
# yapf: disable
|
||||||
self.room_id = room_id # type: str
|
self.room_id = room_id # type: str
|
||||||
|
self.own_user_id = own_user_id
|
||||||
self.canonical_alias = None # type: str
|
self.canonical_alias = None # type: str
|
||||||
self.name = None # type: str
|
self.name = None # type: str
|
||||||
self.topic = "" # type: str
|
self.topic = "" # type: str
|
||||||
|
@ -153,9 +154,23 @@ class MatrixRoom:
|
||||||
short_name = shorten_sender(event.sender)
|
short_name = shorten_sender(event.sender)
|
||||||
user = MatrixUser(short_name, event.display_name)
|
user = MatrixUser(short_name, event.display_name)
|
||||||
self.users[event.sender] = user
|
self.users[event.sender] = user
|
||||||
|
return True
|
||||||
|
|
||||||
elif isinstance(event, RoomMemberLeave):
|
elif isinstance(event, RoomMemberLeave):
|
||||||
if event.leaving_user in self.users:
|
if event.leaving_user in self.users:
|
||||||
del self.users[event.leaving_user]
|
del self.users[event.leaving_user]
|
||||||
|
return True
|
||||||
|
|
||||||
|
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 False
|
||||||
|
|
||||||
|
|
||||||
class MatrixUser:
|
class MatrixUser:
|
||||||
|
@ -337,35 +352,6 @@ class RoomRedactedMessageEvent(RoomEvent):
|
||||||
|
|
||||||
return cls(event_id, sender, timestamp, censor, reason)
|
return cls(event_id, sender, timestamp, censor, reason)
|
||||||
|
|
||||||
def execute(self, server, room, buff, tags):
|
|
||||||
nick, color_name = sender_to_nick_and_color(room, self.sender)
|
|
||||||
color = color_for_tags(color_name)
|
|
||||||
date = server_ts_to_weechat(self.timestamp)
|
|
||||||
|
|
||||||
event_tags = add_event_tags(self.event_id, nick, color, tags)
|
|
||||||
|
|
||||||
reason = (", reason: \"{reason}\"".format(reason=self.reason)
|
|
||||||
if self.reason else "")
|
|
||||||
|
|
||||||
censor, _ = sender_to_nick_and_color(room, self.censor)
|
|
||||||
|
|
||||||
msg = ("{del_color}<{log_color}Message redacted by: "
|
|
||||||
"{censor}{log_color}{reason}{del_color}>{ncolor}").format(
|
|
||||||
del_color=W.color("chat_delimiters"),
|
|
||||||
ncolor=W.color("reset"),
|
|
||||||
log_color=W.color("logger.color.backlog_line"),
|
|
||||||
censor=censor,
|
|
||||||
reason=reason)
|
|
||||||
|
|
||||||
event_tags.append("matrix_redacted")
|
|
||||||
|
|
||||||
tags_string = ",".join(event_tags)
|
|
||||||
|
|
||||||
data = "{author}\t{msg}".format(author=nick, msg=msg)
|
|
||||||
|
|
||||||
W.prnt_date_tags(buff, date, tags_string, data)
|
|
||||||
|
|
||||||
|
|
||||||
class RoomMessageEvent(RoomEvent):
|
class RoomMessageEvent(RoomEvent):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -464,12 +450,6 @@ class RoomMessageText(RoomMessageEvent):
|
||||||
|
|
||||||
return cls(event_id, sender, timestamp, msg, formatted_msg)
|
return cls(event_id, sender, timestamp, msg, formatted_msg)
|
||||||
|
|
||||||
def execute(self, server, room, buff, tags):
|
|
||||||
msg = (self.formatted_message.to_weechat()
|
|
||||||
if self.formatted_message else self.message)
|
|
||||||
|
|
||||||
self._print_message(msg, room, buff, tags)
|
|
||||||
|
|
||||||
|
|
||||||
class RoomMessageEmote(RoomMessageSimple):
|
class RoomMessageEmote(RoomMessageSimple):
|
||||||
|
|
||||||
|
@ -688,60 +668,6 @@ class RoomRedactionEvent(RoomEvent):
|
||||||
|
|
||||||
return cls(event_id, sender, timestamp, redaction_id, reason)
|
return cls(event_id, sender, timestamp, redaction_id, reason)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def already_redacted(tags):
|
|
||||||
if "matrix_redacted" in tags:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _redact_line(self, data_pointer, tags, room, buff):
|
|
||||||
hdata_line_data = W.hdata_get('line_data')
|
|
||||||
|
|
||||||
message = W.hdata_string(hdata_line_data, data_pointer, 'message')
|
|
||||||
censor, _ = sender_to_nick_and_color(room, self.sender)
|
|
||||||
|
|
||||||
reason = ("" if not self.reason else
|
|
||||||
", reason: \"{reason}\"".format(reason=self.reason))
|
|
||||||
|
|
||||||
redaction_msg = ("{del_color}<{log_color}Message redacted by: "
|
|
||||||
"{censor}{log_color}{reason}{del_color}>"
|
|
||||||
"{ncolor}").format(
|
|
||||||
del_color=W.color("chat_delimiters"),
|
|
||||||
ncolor=W.color("reset"),
|
|
||||||
log_color=W.color("logger.color.backlog_line"),
|
|
||||||
censor=censor,
|
|
||||||
reason=reason)
|
|
||||||
|
|
||||||
new_message = ""
|
|
||||||
|
|
||||||
if OPTIONS.redaction_type == RedactType.STRIKETHROUGH:
|
|
||||||
plaintext_msg = W.string_remove_color(message, '')
|
|
||||||
new_message = string_strikethrough(plaintext_msg)
|
|
||||||
elif OPTIONS.redaction_type == RedactType.NOTICE:
|
|
||||||
new_message = message
|
|
||||||
elif OPTIONS.redaction_type == RedactType.DELETE:
|
|
||||||
pass
|
|
||||||
|
|
||||||
message = " ".join(s for s in [new_message, redaction_msg] if s)
|
|
||||||
|
|
||||||
tags.append("matrix_redacted")
|
|
||||||
|
|
||||||
new_data = {'tags_array': ','.join(tags), 'message': message}
|
|
||||||
|
|
||||||
W.hdata_update(hdata_line_data, data_pointer, new_data)
|
|
||||||
|
|
||||||
def execute(self, server, room, buff, tags):
|
|
||||||
data_pointer, tags = line_pointer_and_tags_from_event(
|
|
||||||
buff, self.redaction_id)
|
|
||||||
|
|
||||||
if not data_pointer:
|
|
||||||
return
|
|
||||||
|
|
||||||
if RoomRedactionEvent.already_redacted(tags):
|
|
||||||
return
|
|
||||||
|
|
||||||
self._redact_line(data_pointer, tags, room, buff)
|
|
||||||
|
|
||||||
|
|
||||||
class RoomNameEvent(RoomEvent):
|
class RoomNameEvent(RoomEvent):
|
||||||
|
|
||||||
|
@ -759,18 +685,6 @@ class RoomNameEvent(RoomEvent):
|
||||||
|
|
||||||
return cls(event_id, sender, timestamp, name)
|
return cls(event_id, sender, timestamp, name)
|
||||||
|
|
||||||
def execute(self, server, room, buff, tags):
|
|
||||||
if not self.name:
|
|
||||||
return
|
|
||||||
|
|
||||||
room.name = self.name
|
|
||||||
W.buffer_set(buff, "name", self.name)
|
|
||||||
W.buffer_set(buff, "localvar_set_channel", self.name)
|
|
||||||
|
|
||||||
# calculate room display name and set it as the buffer list name
|
|
||||||
room_name = room.display_name(server.user_id)
|
|
||||||
W.buffer_set(buff, "short_name", room_name)
|
|
||||||
|
|
||||||
|
|
||||||
class RoomAliasEvent(RoomEvent):
|
class RoomAliasEvent(RoomEvent):
|
||||||
|
|
||||||
|
@ -788,19 +702,6 @@ class RoomAliasEvent(RoomEvent):
|
||||||
|
|
||||||
return cls(event_id, sender, timestamp, canonical_alias)
|
return cls(event_id, sender, timestamp, canonical_alias)
|
||||||
|
|
||||||
def execute(self, server, room, buff, tags):
|
|
||||||
if not self.canonical_alias:
|
|
||||||
return
|
|
||||||
|
|
||||||
# TODO: What should we do with this?
|
|
||||||
# W.buffer_set(buff, "name", self.name)
|
|
||||||
# W.buffer_set(buff, "localvar_set_channel", self.name)
|
|
||||||
|
|
||||||
# calculate room display name and set it as the buffer list name
|
|
||||||
room.canonical_alias = self.canonical_alias
|
|
||||||
room_name = room.display_name(server.user_id)
|
|
||||||
W.buffer_set(buff, "short_name", room_name)
|
|
||||||
|
|
||||||
|
|
||||||
class RoomEncryptionEvent(RoomEvent):
|
class RoomEncryptionEvent(RoomEvent):
|
||||||
|
|
||||||
|
|
127
matrix/server.py
127
matrix/server.py
|
@ -34,7 +34,7 @@ from matrix.utils import (key_from_value, prnt_debug, server_buffer_prnt,
|
||||||
from matrix.utf import utf8_decode
|
from matrix.utf import utf8_decode
|
||||||
from matrix.globals import W, SERVERS, OPTIONS
|
from matrix.globals import W, SERVERS, OPTIONS
|
||||||
import matrix.api as API
|
import matrix.api as API
|
||||||
from .buffer import WeechatChannelBuffer, RoomUser
|
from .buffer import RoomBuffer
|
||||||
from .rooms import (
|
from .rooms import (
|
||||||
MatrixRoom,
|
MatrixRoom,
|
||||||
RoomMessageText,
|
RoomMessageText,
|
||||||
|
@ -633,101 +633,6 @@ class MatrixServer:
|
||||||
server_buffer_prnt(self, pprint.pformat(message.request.payload))
|
server_buffer_prnt(self, pprint.pformat(message.request.payload))
|
||||||
server_buffer_prnt(self, pprint.pformat(message.response.body))
|
server_buffer_prnt(self, pprint.pformat(message.response.body))
|
||||||
|
|
||||||
def handle_room_membership_events(
|
|
||||||
self,
|
|
||||||
room,
|
|
||||||
room_buffer,
|
|
||||||
event,
|
|
||||||
is_state_event
|
|
||||||
):
|
|
||||||
def join(event, date, room, room_buffer, is_state_event):
|
|
||||||
user = room.users[event.sender]
|
|
||||||
buffer_user = RoomUser(user.name, event.sender)
|
|
||||||
# TODO remove this duplication
|
|
||||||
user.nick_color = buffer_user.color
|
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
room.handle_event(event)
|
|
||||||
date = server_ts_to_weechat(event.timestamp)
|
|
||||||
|
|
||||||
joined = False
|
|
||||||
left = False
|
|
||||||
|
|
||||||
if isinstance(event, RoomMemberJoin):
|
|
||||||
if event.prev_content and "membership" in event.prev_content:
|
|
||||||
if (event.prev_content["membership"] == "leave"
|
|
||||||
or event.prev_content["membership"] == "invite"):
|
|
||||||
join(event, date, room, room_buffer, is_state_event)
|
|
||||||
joined = True
|
|
||||||
else:
|
|
||||||
# TODO print out profile changes
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
# No previous content for this user in this room, so he just
|
|
||||||
# joined.
|
|
||||||
join(event, date, room, room_buffer, is_state_event)
|
|
||||||
joined = True
|
|
||||||
|
|
||||||
elif isinstance(event, RoomMemberLeave):
|
|
||||||
# TODO the nick can be a display name or a full sender name
|
|
||||||
nick = shorten_sender(event.sender)
|
|
||||||
if event.sender == event.leaving_user:
|
|
||||||
room_buffer.part(nick, date, not is_state_event)
|
|
||||||
else:
|
|
||||||
room_buffer.kick(nick, date, not is_state_event)
|
|
||||||
|
|
||||||
left = True
|
|
||||||
|
|
||||||
elif isinstance(event, RoomMemberInvite):
|
|
||||||
if is_state_event:
|
|
||||||
return
|
|
||||||
|
|
||||||
room_buffer.invite(event.invited_user, date)
|
|
||||||
return
|
|
||||||
|
|
||||||
# calculate room display name and set it as the buffer list name
|
|
||||||
room_name = room.display_name(self.user_id)
|
|
||||||
room_buffer.short_name = room_name
|
|
||||||
|
|
||||||
# A user has joined or left an encrypted room, we need to check for
|
|
||||||
# new devices and create a new group session
|
|
||||||
if room.encrypted and (joined or left):
|
|
||||||
self.device_check_timestamp = None
|
|
||||||
|
|
||||||
def handle_room_event(self, room, room_buffer, event, is_state_event):
|
|
||||||
if isinstance(event, RoomMembershipEvent):
|
|
||||||
self.handle_room_membership_events(
|
|
||||||
room,
|
|
||||||
room_buffer,
|
|
||||||
event,
|
|
||||||
is_state_event
|
|
||||||
)
|
|
||||||
elif isinstance(event, RoomTopicEvent):
|
|
||||||
try:
|
|
||||||
user = room.users[event.sender]
|
|
||||||
nick = user.name
|
|
||||||
except KeyError:
|
|
||||||
nick = event.sender
|
|
||||||
|
|
||||||
room_buffer.change_topic(
|
|
||||||
nick,
|
|
||||||
event.topic,
|
|
||||||
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):
|
def _loop_events(self, info, n):
|
||||||
|
|
||||||
for i in range(n+1):
|
for i in range(n+1):
|
||||||
|
@ -742,7 +647,15 @@ class MatrixServer:
|
||||||
return i
|
return i
|
||||||
|
|
||||||
room, room_buffer = self.find_room_from_id(info.room_id)
|
room, room_buffer = self.find_room_from_id(info.room_id)
|
||||||
self.handle_room_event(room, room_buffer, event, is_state)
|
# 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)
|
self.event_queue.appendleft(info)
|
||||||
return i
|
return i
|
||||||
|
@ -777,18 +690,10 @@ class MatrixServer:
|
||||||
|
|
||||||
def handle_own_messages(self, room_buffer, message):
|
def handle_own_messages(self, room_buffer, message):
|
||||||
if isinstance(message, RoomMessageText):
|
if isinstance(message, RoomMessageText):
|
||||||
msg = (message.formatted_message.to_weechat()
|
room_buffer.self_message(message)
|
||||||
if message.formatted_message
|
|
||||||
else message.message)
|
|
||||||
|
|
||||||
date = server_ts_to_weechat(message.timestamp)
|
|
||||||
room_buffer.self_message(self.user, msg, date)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
elif isinstance(message, RoomMessageEmote):
|
elif isinstance(message, RoomMessageEmote):
|
||||||
date = server_ts_to_weechat(message.timestamp)
|
room_buffer.self_action(message)
|
||||||
room_buffer.self_action(self.user, message.message, date)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
raise NotImplementedError("Unsupported message of type {}".format(
|
raise NotImplementedError("Unsupported message of type {}".format(
|
||||||
|
@ -850,12 +755,12 @@ class MatrixServer:
|
||||||
return
|
return
|
||||||
|
|
||||||
def create_room_buffer(self, room_id):
|
def create_room_buffer(self, room_id):
|
||||||
buf = WeechatChannelBuffer(room_id, self.name, self.user)
|
room = MatrixRoom(room_id, self.user_id)
|
||||||
|
buf = RoomBuffer(room, self.name)
|
||||||
# TODO this should turned into a propper class
|
# TODO this should turned into a propper class
|
||||||
self.room_buffers[room_id] = buf
|
self.room_buffers[room_id] = buf
|
||||||
self.buffers[room_id] = buf._ptr
|
self.buffers[room_id] = buf.weechat_buffer._ptr
|
||||||
self.rooms[room_id] = MatrixRoom(room_id)
|
self.rooms[room_id] = room
|
||||||
pass
|
|
||||||
|
|
||||||
def find_room_from_ptr(self, pointer):
|
def find_room_from_ptr(self, pointer):
|
||||||
room_id = key_from_value(self.buffers, pointer)
|
room_id = key_from_value(self.buffers, pointer)
|
||||||
|
|
Loading…
Reference in a new issue