More split work.

This commit is contained in:
poljar (Damir Jelić) 2018-01-29 13:07:49 +01:00
parent 5686d1f6c1
commit 3b1c95dea8
7 changed files with 1084 additions and 1009 deletions

1017
matrix.py

File diff suppressed because it is too large Load diff

View file

@ -20,8 +20,10 @@ import time
import json import json
from enum import Enum, unique from enum import Enum, unique
from matrix.http import RequestType, HttpRequest from matrix.globals import OPTIONS
from matrix.socket import send_or_queue
from matrix.http import RequestType, HttpRequest
MATRIX_API_PATH = "/_matrix/client/r0" # type: str MATRIX_API_PATH = "/_matrix/client/r0" # type: str
@ -207,8 +209,51 @@ class MatrixMessage:
) )
class MatrixUser:
def __init__(self, name, display_name):
self.name = name # type: str
self.display_name = display_name # type: str
self.power_level = 0 # type: int
self.nick_color = "" # type: str
self.prefix = "" # type: str
class MatrixRoom:
def __init__(self, room_id):
# type: (str) -> None
self.room_id = room_id # type: str
self.alias = room_id # type: str
self.topic = "" # type: str
self.topic_author = "" # type: str
self.topic_date = None # type: datetime.datetime
self.prev_batch = "" # type: str
self.users = dict() # type: Dict[str, MatrixUser]
self.encrypted = False # type: bool
def get_transaction_id(server): def get_transaction_id(server):
# type: (MatrixServer) -> int # type: (MatrixServer) -> int
transaction_id = server.transaction_id transaction_id = server.transaction_id
server.transaction_id += 1 server.transaction_id += 1
return transaction_id return transaction_id
def matrix_sync(server):
message = MatrixMessage(server, OPTIONS, MessageType.SYNC)
server.send_queue.append(message)
def matrix_login(server):
# type: (MatrixServer) -> None
post_data = {"type": "m.login.password",
"user": server.user,
"password": server.password,
"initial_device_display_name": server.device_name}
message = MatrixMessage(
server,
OPTIONS,
MessageType.LOGIN,
data=post_data
)
send_or_queue(server, message)

71
matrix/bar_items.py Normal file
View file

@ -0,0 +1,71 @@
# -*- 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
from matrix.utf import utf8_decode
from matrix.utils import key_from_value
from matrix.globals import W, SERVERS
@utf8_decode
def matrix_bar_item_plugin(data, item, window, buffer, extra_info):
# pylint: disable=unused-argument
for server in SERVERS.values():
if (buffer in server.buffers.values() or
buffer == server.server_buffer):
return "matrix{color}/{color_fg}{name}".format(
color=W.color("bar_delim"),
color_fg=W.color("bar_fg"),
name=server.name)
return ""
@utf8_decode
def matrix_bar_item_name(data, item, window, buffer, extra_info):
# pylint: disable=unused-argument
for server in SERVERS.values():
if buffer in server.buffers.values():
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]
return "{color}{name}".format(
color=W.color(color),
name=room.alias)
elif buffer in server.server_buffer:
color = ("status_name_ssl"
if server.ssl_context.check_hostname else
"status_name")
return "{color}server{del_color}[{color}{name}{del_color}]".format(
color=W.color(color),
del_color=W.color("bar_delim"),
name=server.name)
return ""
def init_bar_items():
W.bar_item_new("(extra)buffer_plugin", "matrix_bar_item_plugin", "")
W.bar_item_new("(extra)buffer_name", "matrix_bar_item_name", "")

View file

@ -17,19 +17,22 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import re import re
import time
import matrix.globals import matrix.globals
from matrix.utf import utf8_decode from matrix.utf import utf8_decode
from matrix.api import MatrixMessage, MessageType from matrix.api import MatrixMessage, MessageType
from matrix.utils import key_from_value, tags_from_line_data from matrix.utils import key_from_value, tags_from_line_data
from matrix.socket import send_or_queue from matrix.socket import send_or_queue, disconnect, connect
from matrix.config import DebugType
from matrix.server import MatrixServer
W = matrix.globals.W W = matrix.globals.W
GLOBAL_OPTIONS = matrix.globals.OPTIONS GLOBAL_OPTIONS = matrix.globals.OPTIONS
SERVERS = matrix.globals.SERVERS SERVERS = matrix.globals.SERVERS
CONFIG = matrix.globals.CONFIG
def hook_commands(): def hook_commands():
W.hook_completion( W.hook_completion(
@ -149,6 +152,15 @@ def matrix_fetch_old_messages(server, room_id):
return return
def check_server_existence(server_name, servers):
if server_name not in servers:
message = "{prefix}matrix: No such server: {server} found".format(
prefix=W.prefix("error"), server=server_name)
W.prnt("", message)
return False
return True
def hook_page_up(): def hook_page_up():
GLOBAL_OPTIONS.page_up_hook = W.hook_command_run( GLOBAL_OPTIONS.page_up_hook = W.hook_command_run(
'/window page_up', '/window page_up',

857
matrix/messages.py Normal file
View file

@ -0,0 +1,857 @@
# -*- 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 time
import json
import pprint
import datetime
from operator import itemgetter
import matrix.colors as colors
from matrix.globals import W, OPTIONS
from matrix.api import (
MessageType,
matrix_sync,
MatrixMessage,
MatrixRoom,
MatrixUser
)
from matrix.socket import send_or_queue, disconnect, close_socket
from matrix.utils import server_buffer_prnt, tags_from_line_data, prnt_debug
from matrix.config import RedactType, DebugType
def strip_matrix_server(string):
# type: (str) -> str
return string.rsplit(":", 1)[0]
def add_user_to_nicklist(buf, user):
group_name = "999|..."
if user.power_level >= 100:
group_name = "000|o"
elif user.power_level >= 50:
group_name = "001|h"
elif user.power_level > 0:
group_name = "002|v"
group = W.nicklist_search_group(buf, "", group_name)
# TODO make it configurable so we can use a display name or user_id here
W.nicklist_add_nick(
buf,
group,
user.display_name,
user.nick_color,
user.prefix,
get_prefix_color(user.prefix),
1
)
def matrix_create_room_buffer(server, room_id):
# type: (MatrixServer, str) -> None
buf = W.buffer_new(
room_id,
"room_input_cb",
server.name,
"room_close_cb",
server.name
)
W.buffer_set(buf, "localvar_set_type", 'channel')
W.buffer_set(buf, "type", 'formatted')
W.buffer_set(buf, "localvar_set_channel", room_id)
W.buffer_set(buf, "localvar_set_nick", server.user)
W.buffer_set(buf, "localvar_set_server", server.name)
short_name = strip_matrix_server(room_id)
W.buffer_set(buf, "short_name", short_name)
W.nicklist_add_group(buf, '', "000|o", "weechat.color.nicklist_group", 1)
W.nicklist_add_group(buf, '', "001|h", "weechat.color.nicklist_group", 1)
W.nicklist_add_group(buf, '', "002|v", "weechat.color.nicklist_group", 1)
W.nicklist_add_group(buf, '', "999|...", "weechat.color.nicklist_group", 1)
W.buffer_set(buf, "nicklist", "1")
W.buffer_set(buf, "nicklist_display_groups", "0")
server.buffers[room_id] = buf
server.rooms[room_id] = MatrixRoom(room_id)
def matrix_handle_room_aliases(server, room_id, event):
# type: (MatrixServer, str, Dict[str, Any]) -> None
buf = server.buffers[room_id]
room = server.rooms[room_id]
alias = event['content']['aliases'][-1]
if not alias:
return
short_name = strip_matrix_server(alias)
room.alias = alias
W.buffer_set(buf, "name", alias)
W.buffer_set(buf, "short_name", short_name)
W.buffer_set(buf, "localvar_set_channel", alias)
def matrix_handle_room_members(server, room_id, event):
# type: (MatrixServer, str, Dict[str, Any]) -> None
buf = server.buffers[room_id]
room = server.rooms[room_id]
# TODO print out a informational message
if event['membership'] == 'join':
# TODO set the buffer type to a channel if we have more than 2 users
display_name = event['content']['displayname']
full_name = event['sender']
short_name = strip_matrix_server(full_name)[1:]
if not display_name:
display_name = short_name
user = MatrixUser(short_name, display_name)
if full_name == server.user_id:
user.nick_color = "weechat.color.chat_nick_self"
W.buffer_set(
buf,
"highlight_words",
",".join([full_name, user.name, user.display_name]))
else:
user.nick_color = W.info_get("nick_color_name", user.name)
room.users[full_name] = user
nick_pointer = W.nicklist_search_nick(buf, "", user.display_name)
if not nick_pointer:
add_user_to_nicklist(buf, user)
else:
# TODO we can get duplicate display names
pass
elif event['membership'] == 'leave':
full_name = event['sender']
if full_name in room.users:
user = room.users[full_name]
nick_pointer = W.nicklist_search_nick(buf, "", user.display_name)
if nick_pointer:
W.nicklist_remove_nick(buf, nick_pointer)
del room.users[full_name]
def date_from_age(age):
# type: (float) -> int
now = time.time()
date = int(now - (age / 1000))
return date
def color_for_tags(color):
if color == "weechat.color.chat_nick_self":
option = W.config_get(color)
return W.config_string(option)
return color
def matrix_handle_room_text_message(server, room_id, event, old=False):
# type: (MatrixServer, str, Dict[str, Any], bool) -> None
tag = ""
msg_author = ""
nick_color_name = ""
room = server.rooms[room_id]
msg = event['content']['body']
if 'format' in event['content'] and 'formatted_body' in event['content']:
if event['content']['format'] == "org.matrix.custom.html":
formatted_data = colors.html_to_formatted(
event['content']['formatted_body'])
msg = colors.formatted_to_weechat(W, formatted_data)
if event['sender'] in room.users:
user = room.users[event['sender']]
msg_author = user.display_name
nick_color_name = user.nick_color
else:
msg_author = strip_matrix_server(event['sender'])[1:]
nick_color_name = W.info_get("nick_color_name", msg_author)
data = "{author}\t{msg}".format(author=msg_author, msg=msg)
event_id = event['event_id']
msg_date = date_from_age(event['unsigned']['age'])
# TODO if this is an initial sync tag the messages as backlog
# TODO handle self messages from other devices
if old:
tag = ("nick_{a},prefix_nick_{color},matrix_id_{event_id},"
"matrix_message,notify_message,no_log,no_highlight").format(
a=msg_author,
color=color_for_tags(nick_color_name),
event_id=event_id)
else:
tag = ("nick_{a},prefix_nick_{color},matrix_id_{event_id},"
"matrix_message,notify_message,log1").format(
a=msg_author,
color=color_for_tags(nick_color_name),
event_id=event_id)
buf = server.buffers[room_id]
W.prnt_date_tags(buf, msg_date, tag, data)
def matrix_handle_redacted_message(server, room_id, event):
# type: (MatrixServer, str, Dict[Any, Any]) -> None
reason = ""
room = server.rooms[room_id]
# TODO check if the message is already printed out, in that case we got the
# message a second time and a redaction event will take care of it.
censor = event['unsigned']['redacted_because']['sender']
nick_color_name = ""
if censor in room.users:
user = room.users[censor]
nick_color_name = user.nick_color
censor = ("{nick_color}{nick}{ncolor} {del_color}"
"({host_color}{full_name}{ncolor}{del_color})").format(
nick_color=W.color(nick_color_name),
nick=user.display_name,
ncolor=W.color("reset"),
del_color=W.color("chat_delimiters"),
host_color=W.color("chat_host"),
full_name=censor)
else:
censor = strip_matrix_server(censor)[1:]
nick_color_name = W.info_get("nick_color_name", censor)
censor = "{color}{censor}{ncolor}".format(
color=W.color(nick_color_name),
censor=censor,
ncolor=W.color("reset"))
if 'reason' in event['unsigned']['redacted_because']['content']:
reason = ", reason: \"{reason}\"".format(
reason=event['unsigned']['redacted_because']['content']['reason'])
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)
msg_author = strip_matrix_server(event['sender'])[1:]
data = "{author}\t{msg}".format(author=msg_author, msg=msg)
event_id = event['event_id']
msg_date = date_from_age(event['unsigned']['age'])
tag = ("nick_{a},prefix_nick_{color},matrix_id_{event_id},"
"matrix_message,matrix_redacted,"
"notify_message,no_highlight").format(
a=msg_author,
color=color_for_tags(nick_color_name),
event_id=event_id)
buf = server.buffers[room_id]
W.prnt_date_tags(buf, msg_date, tag, data)
def matrix_handle_room_messages(server, room_id, event, old=False):
# type: (MatrixServer, str, Dict[str, Any], bool) -> None
if event['type'] == 'm.room.message':
if 'redacted_by' in event['unsigned']:
matrix_handle_redacted_message(server, room_id, event)
return
if event['content']['msgtype'] == 'm.text':
matrix_handle_room_text_message(server, room_id, event, old)
# TODO handle different content types here
else:
message = ("{prefix}Handling of content type "
"{type} not implemented").format(
type=event['content']['msgtype'],
prefix=W.prefix("error"))
W.prnt(server.server_buffer, message)
def event_id_from_tags(tags):
# type: (List[str]) -> str
for tag in tags:
if tag.startswith("matrix_id"):
return tag[10:]
return ""
def string_strikethrough(string):
return "".join(["{}\u0336".format(c) for c in string])
def matrix_redact_line(data, tags, event):
reason = ""
hdata_line_data = W.hdata_get('line_data')
message = W.hdata_string(hdata_line_data, data, 'message')
censor = strip_matrix_server(event['sender'])[1:]
if 'reason' in event['content']:
reason = ", reason: \"{reason}\"".format(
reason=event['content']['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)
if OPTIONS.redaction_type == RedactType.STRIKETHROUGH:
message = string_strikethrough(message)
message = message + " " + redaction_msg
elif OPTIONS.redaction_type == RedactType.DELETE:
message = redaction_msg
elif OPTIONS.redaction_type == RedactType.NOTICE:
message = message + " " + redaction_msg
tags.append("matrix_new_redacted")
new_data = {'tags_array': tags,
'message': message}
W.hdata_update(hdata_line_data, data, new_data)
return W.WEECHAT_RC_OK
def matrix_handle_room_redaction(server, room_id, event):
buf = server.buffers[room_id]
event_id = event['redacts']
own_lines = W.hdata_pointer(W.hdata_get('buffer'), buf, 'own_lines')
if own_lines:
hdata_line = W.hdata_get('line')
line = W.hdata_pointer(
W.hdata_get('lines'),
own_lines,
'last_line'
)
while line:
data = W.hdata_pointer(hdata_line, line, 'data')
if data:
tags = tags_from_line_data(data)
message_id = event_id_from_tags(tags)
if event_id == message_id:
# If the message is already redacted there is nothing to do
if ("matrix_redacted" not in tags and
"matrix_new_redacted" not in tags):
matrix_redact_line(data, tags, event)
return W.WEECHAT_RC_OK
line = W.hdata_move(hdata_line, line, -1)
return W.WEECHAT_RC_OK
def get_prefix_for_level(level):
# type: (int) -> str
if level >= 100:
return "&"
elif level >= 50:
return "@"
elif level > 0:
return "+"
return ""
# TODO make this configurable
def get_prefix_color(prefix):
# type: (str) -> str
if prefix == "&":
return "lightgreen"
elif prefix == "@":
return "lightgreen"
elif prefix == "+":
return "yellow"
return ""
def matrix_handle_room_power_levels(server, room_id, event):
if not event['content']['users']:
return
buf = server.buffers[room_id]
room = server.rooms[room_id]
for full_name, level in event['content']['users'].items():
if full_name not in room.users:
continue
user = room.users[full_name]
user.power_level = level
user.prefix = get_prefix_for_level(level)
nick_pointer = W.nicklist_search_nick(buf, "", user.display_name)
W.nicklist_remove_nick(buf, nick_pointer)
add_user_to_nicklist(buf, user)
def matrix_handle_room_events(server, room_id, room_events):
# type: (MatrixServer, str, Dict[Any, Any]) -> None
for event in room_events:
if event['event_id'] in server.ignore_event_list:
server.ignore_event_list.remove(event['event_id'])
continue
if event['type'] == 'm.room.aliases':
matrix_handle_room_aliases(server, room_id, event)
elif event['type'] == 'm.room.member':
matrix_handle_room_members(server, room_id, event)
elif event['type'] == 'm.room.message':
matrix_handle_room_messages(server, room_id, event)
elif event['type'] == 'm.room.topic':
buf = server.buffers[room_id]
room = server.rooms[room_id]
topic = event['content']['topic']
room.topic = topic
room.topic_author = event['sender']
topic_age = event['unsigned']['age']
room.topic_date = datetime.datetime.fromtimestamp(
time.time() - (topic_age / 1000))
W.buffer_set(buf, "title", topic)
nick_color = W.info_get("nick_color_name", room.topic_author)
author = room.topic_author
if author in room.users:
user = room.users[author]
nick_color = user.nick_color
author = user.display_name
author = ("{nick_color}{user}{ncolor}").format(
nick_color=W.color(nick_color),
user=author,
ncolor=W.color("reset"))
# TODO print old topic if configured so
# TODO nick display name if configured so and found
message = ("{prefix}{nick} has changed "
"the topic for {chan_color}{room}{ncolor} "
"to \"{topic}\"").format(
prefix=W.prefix("network"),
nick=author,
chan_color=W.color("chat_channel"),
ncolor=W.color("reset"),
room=strip_matrix_server(room.alias),
topic=topic)
tags = "matrix_topic,no_highlight,log3,matrix_id_{event_id}".format(
event_id=event['event_id'])
date = date_from_age(topic_age)
W.prnt_date_tags(buf, date, tags, message)
elif event['type'] == "m.room.redaction":
matrix_handle_room_redaction(server, room_id, event)
elif event["type"] == "m.room.power_levels":
matrix_handle_room_power_levels(server, room_id, event)
# These events are unimportant for us.
elif event["type"] in ["m.room.create", "m.room.join_rules",
"m.room.history_visibility",
"m.room.canonical_alias",
"m.room.guest_access",
"m.room.third_party_invite"]:
pass
elif event["type"] == "m.room.name":
buf = server.buffers[room_id]
room = server.rooms[room_id]
name = event['content']['name']
if not name:
return
room.alias = name
W.buffer_set(buf, "name", name)
W.buffer_set(buf, "short_name", name)
W.buffer_set(buf, "localvar_set_channel", name)
elif event["type"] == "m.room.encryption":
buf = server.buffers[room_id]
room = server.rooms[room_id]
room.encrypted = True
message = ("{prefix}This room is encrypted, encryption is "
"currently unsuported. Message sending is disabled for "
"this room.").format(prefix=W.prefix("error"))
W.prnt(buf, message)
# TODO implement message decryption
elif event["type"] == "m.room.encrypted":
pass
else:
message = ("{prefix}Handling of room event type "
"{type} not implemented").format(
type=event['type'],
prefix=W.prefix("error"))
W.prnt(server.server_buffer, message)
def matrix_handle_invite_events(server, room_id, events):
# type: (MatrixServer, str, List[Dict[str, Any]]) -> None
for event in events:
if event["type"] != "m.room.member":
continue
if 'membership' not in event:
continue
if event["membership"] == "invite":
sender = event["sender"]
# TODO does this go to the server buffer or to the channel buffer?
message = ("{prefix}You have been invited to {chan_color}{channel}"
"{ncolor} by {nick_color}{nick}{ncolor}").format(
prefix=W.prefix("network"),
chan_color=W.color("chat_channel"),
channel=room_id,
ncolor=W.color("reset"),
nick_color=W.color("chat_nick"),
nick=sender)
W.prnt(server.server_buffer, message)
def matrix_handle_room_info(server, room_info):
# type: (MatrixServer, Dict) -> None
for room_id, room in room_info['join'].items():
if not room_id:
continue
if room_id not in server.buffers:
matrix_create_room_buffer(server, room_id)
if not server.rooms[room_id].prev_batch:
server.rooms[room_id].prev_batch = room['timeline']['prev_batch']
matrix_handle_room_events(server, room_id, room['state']['events'])
matrix_handle_room_events(server, room_id, room['timeline']['events'])
for room_id, room in room_info['invite'].items():
matrix_handle_invite_events(
server,
room_id,
room['invite_state']['events']
)
def matrix_sort_old_messages(server, room_id):
lines = []
buf = server.buffers[room_id]
own_lines = W.hdata_pointer(W.hdata_get('buffer'), buf, 'own_lines')
if own_lines:
hdata_line = W.hdata_get('line')
hdata_line_data = W.hdata_get('line_data')
line = W.hdata_pointer(
W.hdata_get('lines'),
own_lines,
'first_line'
)
while line:
data = W.hdata_pointer(hdata_line, line, 'data')
line_data = {}
if data:
date = W.hdata_time(hdata_line_data, data, 'date')
print_date = W.hdata_time(hdata_line_data, data,
'date_printed')
tags = tags_from_line_data(data)
prefix = W.hdata_string(hdata_line_data, data, 'prefix')
message = W.hdata_string(hdata_line_data, data,
'message')
line_data = {'date': date,
'date_printed': print_date,
'tags_array': ','.join(tags),
'prefix': prefix,
'message': message}
lines.append(line_data)
line = W.hdata_move(hdata_line, line, 1)
sorted_lines = sorted(lines, key=itemgetter('date'))
lines = []
# We need to convert the dates to a string for hdata_update(), this
# will reverse the list at the same time
while sorted_lines:
line = sorted_lines.pop()
new_line = {k: str(v) for k, v in line.items()}
lines.append(new_line)
matrix_update_buffer_lines(lines, own_lines)
def matrix_update_buffer_lines(new_lines, own_lines):
hdata_line = W.hdata_get('line')
hdata_line_data = W.hdata_get('line_data')
line = W.hdata_pointer(
W.hdata_get('lines'),
own_lines,
'first_line'
)
while line:
data = W.hdata_pointer(hdata_line, line, 'data')
if data:
W.hdata_update(hdata_line_data, data, new_lines.pop())
line = W.hdata_move(hdata_line, line, 1)
def matrix_handle_old_messages(server, room_id, events):
for event in events:
if event['type'] == 'm.room.message':
matrix_handle_room_messages(server, room_id, event, old=True)
# TODO do we wan't to handle topics joins/quits here?
else:
pass
matrix_sort_old_messages(server, room_id)
def matrix_handle_message(
server, # type: MatrixServer
message_type, # type: MessageType
response, # type: Dict[str, Any]
extra_data # type: Dict[str, Any]
):
# type: (...) -> None
if message_type is MessageType.LOGIN:
server.access_token = response["access_token"]
server.user_id = response["user_id"]
message = MatrixMessage(server, OPTIONS, MessageType.SYNC)
send_or_queue(server, message)
elif message_type is MessageType.SYNC:
next_batch = response['next_batch']
# we got the same batch again, nothing to do
if next_batch == server.next_batch:
matrix_sync(server)
return
room_info = response['rooms']
matrix_handle_room_info(server, room_info)
server.next_batch = next_batch
# TODO add a delay to this
matrix_sync(server)
elif message_type is MessageType.SEND:
author = extra_data["author"]
message = extra_data["message"]
room_id = extra_data["room_id"]
date = int(time.time())
# TODO the event_id can be missing if sending has failed for
# some reason
event_id = response["event_id"]
# This message will be part of the next sync, we already printed it out
# so ignore it in the sync.
server.ignore_event_list.append(event_id)
tag = ("notify_none,no_highlight,self_msg,log1,nick_{a},"
"prefix_nick_{color},matrix_id_{event_id},"
"matrix_message").format(
a=author,
color=color_for_tags("weechat.color.chat_nick_self"),
event_id=event_id)
data = "{author}\t{msg}".format(author=author, msg=message)
buf = server.buffers[room_id]
W.prnt_date_tags(buf, date, tag, data)
elif message_type == MessageType.ROOM_MSG:
# Response has no messages, that is we already got the oldest message
# in a previous request, nothing to do
if not response['chunk']:
return
room_id = response['chunk'][0]['room_id']
room = server.rooms[room_id]
matrix_handle_old_messages(server, room_id, response['chunk'])
room.prev_batch = response['end']
# Nothing to do here, we'll handle state changes and redactions in the sync
elif (message_type == MessageType.STATE or
message_type == MessageType.REDACT):
pass
else:
server_buffer_prnt(
server,
"Handling of message type {type} not implemented".format(
type=message_type))
def handle_http_response(server, message):
# type: (MatrixServer, MatrixMessage) -> None
assert message.response
status_code = message.response.status
def decode_json(server, json_string):
try:
return json.loads(json_string, encoding='utf-8')
except Exception as error:
message = ("{prefix}matrix: Error decoding json response from "
"server: {error}").format(
prefix=W.prefix("error"),
error=error)
W.prnt(server.server_buffer, message)
return None
if status_code == 200:
response = decode_json(server, message.response.body)
# if not response:
# # Resend the message
# message.response = None
# send_or_queue(server, message)
# return
matrix_handle_message(
server,
message.type,
response,
message.extra_data
)
# TODO handle try again response
elif status_code == 504:
if message.type == MessageType.SYNC:
matrix_sync(server)
elif status_code == 403:
if message.type == MessageType.LOGIN:
response = decode_json(server, message.response.body)
reason = ("." if not response or not response["error"] else
": {r}.".format(r=response["error"]))
error_message = ("{prefix}Login error{reason}").format(
prefix=W.prefix("error"),
reason=reason)
server_buffer_prnt(server, error_message)
W.unhook(server.timer_hook)
server.timer_hook = None
close_socket(server)
disconnect(server)
elif message.type == MessageType.STATE:
response = decode_json(server, message.response.body)
reason = ("." if not response or not response["error"] else
": {r}.".format(r=response["error"]))
error_message = ("{prefix}Can't set state{reason}").format(
prefix=W.prefix("network"),
reason=reason)
server_buffer_prnt(server, error_message)
else:
error_message = ("{prefix}Unhandled 403 error, please inform the "
"developers about this: {error}").format(
prefix=W.prefix("error"),
error=message.response.body)
server_buffer_prnt(server, error_message)
else:
server_buffer_prnt(
server,
("{prefix}Unhandled {status_code} error, please inform "
"the developers about this.").format(
prefix=W.prefix("error"),
status_code=status_code))
server_buffer_prnt(server, pprint.pformat(message.type))
server_buffer_prnt(server, pprint.pformat(message.request.payload))
server_buffer_prnt(server, pprint.pformat(message.response.body))
creation_date = datetime.datetime.fromtimestamp(message.creation_time)
done_time = time.time()
info_message = ("Message of type {t} created at {c}."
"\nMessage lifetime information:"
"\n Send delay: {s} ms"
"\n Receive delay: {r} ms"
"\n Handling time: {h} ms"
"\n Total time: {total} ms").format(
t=message.type,
c=creation_date,
s=(message.send_time - message.creation_time) * 1000,
r=(message.receive_time - message.send_time) * 1000,
h=(done_time - message.receive_time) * 1000,
total=(done_time - message.creation_time) * 1000,)
prnt_debug(DebugType.TIMING, server, info_message)
return

View file

@ -17,17 +17,24 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import time import time
import socket
from builtins import bytes, str from builtins import bytes, str
import matrix.globals import matrix.globals
from matrix.config import DebugType from matrix.config import DebugType
from matrix.utils import prnt_debug, server_buffer_prnt from matrix.utils import prnt_debug, server_buffer_prnt, create_server_buffer
W = matrix.globals.W W = matrix.globals.W
def close_socket(server):
# type: (MatrixServer) -> None
server.socket.shutdown(socket.SHUT_RDWR)
server.socket.close()
def disconnect(server): def disconnect(server):
# type: (MatrixServer) -> None # type: (MatrixServer) -> None
if server.fd_hook: if server.fd_hook:
@ -51,6 +58,41 @@ def send_or_queue(server, message):
server.send_queue.append(message) server.send_queue.append(message)
def connect(server):
# type: (MatrixServer) -> int
if not server.address or not server.port:
message = "{prefix}Server address or port not set".format(
prefix=W.prefix("error"))
W.prnt("", message)
return False
if not server.user or not server.password:
message = "{prefix}User or password not set".format(
prefix=W.prefix("error"))
W.prnt("", message)
return False
if server.connected:
return True
if not server.server_buffer:
create_server_buffer(server)
if not server.timer_hook:
server.timer_hook = W.hook_timer(
1 * 1000,
0,
0,
"matrix_timer_cb",
server.name
)
W.hook_connect("", server.address, server.port, 1, 0, "",
"connect_cb", server.name)
return W.WEECHAT_RC_OK
def send(server, message): def send(server, message):
# type: (MatrixServer, MatrixMessage) -> bool # type: (MatrixServer, MatrixMessage) -> bool

View file

@ -20,6 +20,7 @@ import time
import matrix.globals import matrix.globals
from matrix.config import ServerBufferType
W = matrix.globals.W W = matrix.globals.W
GLOBAL_OPTIONS = matrix.globals.OPTIONS GLOBAL_OPTIONS = matrix.globals.OPTIONS
@ -58,3 +59,43 @@ def tags_from_line_data(line_data):
) for i in range(tags_count)] ) for i in range(tags_count)]
return tags return tags
def create_server_buffer(server):
# type: (MatrixServer) -> None
server.server_buffer = W.buffer_new(
server.name,
"server_buffer_cb",
server.name,
"",
""
)
server_buffer_set_title(server)
W.buffer_set(server.server_buffer, "localvar_set_type", 'server')
W.buffer_set(server.server_buffer, "localvar_set_nick", server.user)
W.buffer_set(server.server_buffer, "localvar_set_server", server.name)
W.buffer_set(server.server_buffer, "localvar_set_channel", server.name)
# TODO merge without core
if GLOBAL_OPTIONS.look_server_buf == ServerBufferType.MERGE_CORE:
W.buffer_merge(server.server_buffer, W.buffer_search_main())
elif GLOBAL_OPTIONS.look_server_buf == ServerBufferType.MERGE:
pass
else:
pass
def server_buffer_set_title(server):
# type: (MatrixServer) -> None
if server.numeric_address:
ip_string = " ({address})".format(address=server.numeric_address)
else:
ip_string = ""
title = ("Matrix: {address}/{port}{ip}").format(
address=server.address,
port=server.port,
ip=ip_string)
W.buffer_set(server.server_buffer, "title", title)