matrix: Style fixes and cleanup.

This commit is contained in:
Damir Jelić 2018-08-29 19:40:59 +02:00
parent f8a318fef2
commit 05a413f7cb
13 changed files with 1278 additions and 1278 deletions

View file

@ -130,7 +130,8 @@ disable=print-statement,
bad-whitespace, bad-whitespace,
too-few-public-methods, too-few-public-methods,
too-many-lines, too-many-lines,
missing-docstring missing-docstring,
bad-continuation,
# Enable the message, report, category or checker with the given id(s). You can # Enable the message, report, category or checker with the given id(s). You can
# either give multiple identifier separated by comma (,) or put this option # either give multiple identifier separated by comma (,) or put this option

97
main.py
View file

@ -19,72 +19,51 @@ from __future__ import unicode_literals
import socket import socket
import ssl import ssl
import time
import pprint
import OpenSSL.crypto as crypto
import textwrap import textwrap
from itertools import chain
# pylint: disable=redefined-builtin # pylint: disable=redefined-builtin
from builtins import str from builtins import str
from future.utils import bytes_to_native_str as n from itertools import chain
# pylint: disable=unused-import # pylint: disable=unused-import
from typing import (List, Set, Dict, Tuple, Text, Optional, AnyStr, Deque, Any) from typing import Any, AnyStr, Deque, Dict, List, Optional, Set, Text, Tuple
import logbook import logbook
from logbook import Logger, StderrHandler, StreamHandler import OpenSSL.crypto as crypto
from future.utils import bytes_to_native_str as n
import nio from logbook import Logger, StreamHandler
from nio import TransportType, RemoteTransportError, RemoteProtocolError from nio import RemoteProtocolError, RemoteTransportError, TransportType
from matrix.colors import Formatted
from matrix.utf import utf8_decode
# Weechat searches for the registered callbacks in the scope of the main script
# file, import the callbacks here so weechat can find them.
from matrix.commands import (hook_commands, hook_page_up, matrix_command_cb,
matrix_topic_command_cb, matrix_join_command_cb,
matrix_part_command_cb, matrix_invite_command_cb,
matrix_command_pgup_cb, matrix_redact_command_cb,
matrix_command_buf_clear_cb, matrix_me_command_cb,
matrix_kick_command_cb)
from matrix.buffer import room_buffer_input_cb, room_buffer_close_cb
from matrix.server import (
MatrixServer,
create_default_server,
send_cb,
matrix_timer_cb,
matrix_config_server_read_cb,
matrix_config_server_write_cb,
matrix_config_server_change_cb,
)
from matrix.bar_items import (init_bar_items, matrix_bar_item_name,
matrix_bar_item_plugin, matrix_bar_item_lag,
matrix_bar_item_buffer_modes)
from matrix.completion import (
init_completion, matrix_command_completion_cb,
matrix_server_command_completion_cb, matrix_debug_completion_cb,
matrix_message_completion_cb, matrix_server_completion_cb,
matrix_olm_user_completion_cb, matrix_olm_device_completion_cb,
matrix_user_completion_cb)
from matrix.utils import (key_from_value, server_buffer_prnt,
server_buffer_set_title)
from matrix.config import (
matrix_config_reload_cb,
MatrixConfig,
config_log_level_cb,
config_log_category_cb,
config_server_buffer_cb
)
from matrix import globals as G from matrix import globals as G
from matrix.bar_items import (init_bar_items, matrix_bar_item_buffer_modes,
from matrix.globals import W, SERVERS, SCRIPT_NAME matrix_bar_item_lag, matrix_bar_item_name,
matrix_bar_item_plugin)
from matrix.buffer import room_buffer_close_cb, room_buffer_input_cb
# Weechat searches for the registered callbacks in the scope of the main script
# file, import the callbacks here so weechat can find them.
from matrix.commands import (hook_commands, hook_page_up,
matrix_command_buf_clear_cb, matrix_command_cb,
matrix_command_pgup_cb, matrix_invite_command_cb,
matrix_join_command_cb, matrix_kick_command_cb,
matrix_me_command_cb, matrix_part_command_cb,
matrix_redact_command_cb, matrix_topic_command_cb)
from matrix.completion import (init_completion, matrix_command_completion_cb,
matrix_debug_completion_cb,
matrix_message_completion_cb,
matrix_olm_device_completion_cb,
matrix_olm_user_completion_cb,
matrix_server_command_completion_cb,
matrix_server_completion_cb,
matrix_user_completion_cb)
from matrix.config import (MatrixConfig, config_log_category_cb,
config_log_level_cb, config_server_buffer_cb,
matrix_config_reload_cb)
from matrix.globals import SCRIPT_NAME, SERVERS, W
from matrix.server import (MatrixServer, create_default_server,
matrix_config_server_change_cb,
matrix_config_server_read_cb,
matrix_config_server_write_cb, matrix_timer_cb,
send_cb)
from matrix.utf import utf8_decode
from matrix.utils import server_buffer_prnt, server_buffer_set_title
# yapf: disable # yapf: disable
WEECHAT_SCRIPT_NAME = SCRIPT_NAME WEECHAT_SCRIPT_NAME = SCRIPT_NAME

View file

@ -1,9 +1,8 @@
import datetime
import random import random
import string import string
import datetime
WEECHAT_BASE_COLORS = {
weechat_base_colors = {
"black": "0", "black": "0",
"red": "1", "red": "1",
"green": "2", "green": "2",
@ -29,11 +28,11 @@ def color(color_name):
escape_codes = [] escape_codes = []
reset_code = "0" reset_code = "0"
def make_fg_color(color): def make_fg_color(color_code):
return "38;5;{}".format(color) return "38;5;{}".format(color_code)
def make_bg_color(color): def make_bg_color(color_code):
return "48;5;{}".format(color) return "48;5;{}".format(color_code)
attributes = { attributes = {
"bold": "1", "bold": "1",
@ -76,21 +75,21 @@ def color(color_name):
stripped_color = fg_color.lstrip("*_|/!") stripped_color = fg_color.lstrip("*_|/!")
if stripped_color in weechat_base_colors: if stripped_color in WEECHAT_BASE_COLORS:
escape_codes.append( escape_codes.append(
make_fg_color(weechat_base_colors[stripped_color])) make_fg_color(WEECHAT_BASE_COLORS[stripped_color]))
elif stripped_color.isdigit(): elif stripped_color.isdigit():
num_color = int(stripped_color) num_color = int(stripped_color)
if num_color >= 0 and num_color < 256: if 0 <= num_color < 256:
escape_codes.append(make_fg_color(stripped_color)) escape_codes.append(make_fg_color(stripped_color))
if bg_color in weechat_base_colors: if bg_color in WEECHAT_BASE_COLORS:
escape_codes.append(make_bg_color(weechat_base_colors[bg_color])) escape_codes.append(make_bg_color(WEECHAT_BASE_COLORS[bg_color]))
else: else:
if bg_color.isdigit(): if bg_color.isdigit():
num_color = int(bg_color) num_color = int(bg_color)
if num_color >= 0 and num_color < 256: if 0 <= num_color < 256:
escape_codes.append(make_bg_color(bg_color)) escape_codes.append(make_bg_color(bg_color))
escape_string = "\033[{}{}m".format(reset_code, ";".join(escape_codes)) escape_string = "\033[{}{}m".format(reset_code, ";".join(escape_codes))
@ -98,7 +97,7 @@ def color(color_name):
return escape_string return escape_string
def prefix(prefix): def prefix(prefix_string):
prefix_to_symbol = { prefix_to_symbol = {
"error": "=!=", "error": "=!=",
"network": "--", "network": "--",
@ -107,14 +106,14 @@ def prefix(prefix):
"quit": "<--" "quit": "<--"
} }
if prefix in prefix_to_symbol: if prefix_string in prefix_to_symbol:
return prefix_to_symbol[prefix] return prefix_to_symbol[prefix]
return "" return ""
def prnt(_, string): def prnt(_, message):
print(string) print(message)
def prnt_date_tags(_, date, tags_string, data): def prnt_date_tags(_, date, tags_string, data):
@ -126,44 +125,44 @@ def prnt_date_tags(_, date, tags_string, data):
print(message) print(message)
def config_search_section(*args, **kwargs): def config_search_section(*_, **__):
pass pass
def config_new_option(*args, **kwargs): def config_new_option(*_, **__):
pass pass
def mkdir_home(*args, **kwargs): def mkdir_home(*_, **__):
return True return True
def info_get(info, *args): def info_get(info, *_):
if info == "nick_color_name": if info == "nick_color_name":
return random.choice(list(weechat_base_colors.keys())) return random.choice(list(WEECHAT_BASE_COLORS.keys()))
return "" return ""
def buffer_new(*args, **kwargs): def buffer_new(*_, **__):
return "".join( return "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(8) random.choice(string.ascii_uppercase + string.digits) for _ in range(8)
) )
def buffer_set(*args, **kwargs): def buffer_set(*_, **__):
return return
def nicklist_add_group(*args, **kwargs): def nicklist_add_group(*_, **__):
return return
def nicklist_add_nick(*args, **kwargs): def nicklist_add_nick(*_, **__):
return return
def nicklist_remove_nick(*args, **kwargs): def nicklist_remove_nick(*_, **__):
return return

View file

@ -16,21 +16,20 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from matrix.utf import utf8_decode from .globals import SERVERS, W
from .utf import utf8_decode
from matrix.globals import W, SERVERS
@utf8_decode @utf8_decode
def matrix_bar_item_plugin(data, item, window, buffer, extra_info): def matrix_bar_item_plugin(data, item, window, buffer, extra_info):
# pylint: disable=unused-argument # pylint: disable=unused-argument
for server in SERVERS.values(): for server in SERVERS.values():
if (buffer in server.buffers.values() or if buffer in server.buffers.values() or buffer == server.server_buffer:
buffer == server.server_buffer):
return "matrix{color}/{color_fg}{name}".format( return "matrix{color}/{color_fg}{name}".format(
color=W.color("bar_delim"), color=W.color("bar_delim"),
color_fg=W.color("bar_fg"), color_fg=W.color("bar_fg"),
name=server.name) name=server.name,
)
return "" return ""
@ -40,24 +39,31 @@ def matrix_bar_item_name(data, item, window, buffer, extra_info):
# pylint: disable=unused-argument # pylint: disable=unused-argument
for server in SERVERS.values(): for server in SERVERS.values():
if buffer in server.buffers.values(): if buffer in server.buffers.values():
color = ("status_name_ssl" color = (
if server.ssl_context.check_hostname else "status_name") "status_name_ssl"
if server.ssl_context.check_hostname
else "status_name"
)
room_buffer = server.find_room_from_ptr(buffer) room_buffer = server.find_room_from_ptr(buffer)
room = room_buffer.room room = room_buffer.room
return "{color}{name}".format( return "{color}{name}".format(
color=W.color(color), color=W.color(color), name=room.display_name()
name=room.display_name()) )
elif buffer == server.server_buffer: if buffer == server.server_buffer:
color = ("status_name_ssl" color = (
if server.ssl_context.check_hostname else "status_name") "status_name_ssl"
if server.ssl_context.check_hostname
else "status_name"
)
return "{color}server{del_color}[{color}{name}{del_color}]".format( return "{color}server{del_color}[{color}{name}{del_color}]".format(
color=W.color(color), color=W.color(color),
del_color=W.color("bar_delim"), del_color=W.color("bar_delim"),
name=server.name) name=server.name,
)
return "" return ""
@ -66,8 +72,7 @@ def matrix_bar_item_name(data, item, window, buffer, extra_info):
def matrix_bar_item_lag(data, item, window, buffer, extra_info): def matrix_bar_item_lag(data, item, window, buffer, extra_info):
# pylint: disable=unused-argument # pylint: disable=unused-argument
for server in SERVERS.values(): for server in SERVERS.values():
if (buffer in server.buffers.values() or if buffer in server.buffers.values() or buffer == server.server_buffer:
buffer == server.server_buffer):
if server.lag >= 500: if server.lag >= 500:
color = W.color("irc.color.item_lag_counting") color = W.color("irc.color.item_lag_counting")
if server.lag_done: if server.lag_done:
@ -77,7 +82,8 @@ def matrix_bar_item_lag(data, item, window, buffer, extra_info):
lag_string = "Lag: {color}{lag}{ncolor}".format( lag_string = "Lag: {color}{lag}{ncolor}".format(
lag=lag.format((server.lag / 1000)), lag=lag.format((server.lag / 1000)),
color=color, color=color,
ncolor=W.color("reset")) ncolor=W.color("reset"),
)
return lag_string return lag_string
return "" return ""

View file

@ -22,42 +22,40 @@ from builtins import super
from functools import partial from functools import partial
from typing import NamedTuple from typing import NamedTuple
from . import globals as G
from .globals import W, SERVERS, SCRIPT_NAME
from .utf import utf8_decode
from .colors import Formatted
from .utils import (
shorten_sender,
server_ts_to_weechat,
string_strikethrough,
)
from .config import RedactType
from nio import ( from nio import (
Api, Api,
RoomMessageText,
RoomMemberEvent,
PowerLevelsEvent, PowerLevelsEvent,
RoomEncryptionEvent,
RedactedEvent, RedactedEvent,
RedactionEvent,
RoomAliasEvent, RoomAliasEvent,
RoomTopicEvent, RoomEncryptionEvent,
RoomMemberEvent,
RoomMessageEmote, RoomMessageEmote,
RoomNameEvent,
RoomMessageMedia, RoomMessageMedia,
RoomMessageNotice, RoomMessageNotice,
RoomMessageText,
RoomMessageUnknown, RoomMessageUnknown,
RedactionEvent RoomNameEvent,
RoomTopicEvent,
) )
from . import globals as G
from .colors import Formatted
from .config import RedactType
from .globals import SCRIPT_NAME, SERVERS, W
from .utf import utf8_decode
from .utils import server_ts_to_weechat, shorten_sender, string_strikethrough
OwnMessage = NamedTuple("OwnMessage", [ OwnMessage = NamedTuple(
("sender", str), "OwnMessage",
("age", int), [
("event_id", str), ("sender", str),
("room_id", str), ("age", int),
("formatted_message", Formatted) ("event_id", str),
]) ("room_id", str),
("formatted_message", Formatted),
],
)
class OwnAction(OwnMessage): class OwnAction(OwnMessage):
@ -71,7 +69,7 @@ def room_buffer_input_cb(server_name, buffer, input_data):
if not room_buffer: if not room_buffer:
# TODO log error # TODO log error
return return W.WEECHAT_RC_ERROR
if not server.connected: if not server.connected:
room_buffer.error("You are not connected to the server") room_buffer.error("You are not connected to the server")
@ -96,7 +94,7 @@ def room_buffer_close_cb(data, buffer):
class WeechatUser(object): class WeechatUser(object):
def __init__(self, nick, host=None, prefix="", join_time=None): def __init__(self, nick, host=None, prefix="", join_time=None):
# type: (str, str, str) -> None # type: (str, str, str, int) -> None
self.nick = nick self.nick = nick
self.host = host self.host = host
self.prefix = prefix self.prefix = prefix
@ -127,7 +125,7 @@ class WeechatUser(object):
class RoomUser(WeechatUser): class RoomUser(WeechatUser):
def __init__(self, nick, user_id=None, power_level=0, join_time=None): def __init__(self, nick, user_id=None, power_level=0, join_time=None):
# type: (str, str, int) -> None # type: (str, str, int, int) -> None
prefix = self._get_prefix(power_level) prefix = self._get_prefix(power_level)
super().__init__(nick, user_id, prefix, join_time) super().__init__(nick, user_id, prefix, join_time)
@ -152,26 +150,22 @@ class RoomUser(WeechatUser):
# type: (int) -> str # type: (int) -> str
if power_level >= 100: if power_level >= 100:
return "&" return "&"
elif power_level >= 50: if power_level >= 50:
return "@" return "@"
elif power_level > 0: if power_level > 0:
return "+" return "+"
return "" return ""
class WeechatChannelBuffer(object): class WeechatChannelBuffer(object):
tags = { tags = {
"message": [ "message": [SCRIPT_NAME + "_message", "notify_message", "log1"],
SCRIPT_NAME + "_message",
"notify_message",
"log1"
],
"self_message": [ "self_message": [
SCRIPT_NAME + "_message", SCRIPT_NAME + "_message",
"notify_none", "notify_none",
"no_highlight", "no_highlight",
"self_msg", "self_msg",
"log1" "log1",
], ],
"action": [ "action": [
SCRIPT_NAME + "_message", SCRIPT_NAME + "_message",
@ -179,44 +173,25 @@ class WeechatChannelBuffer(object):
"notify_message", "notify_message",
"log1", "log1",
], ],
"notice": [ "notice": [SCRIPT_NAME + "_notice", "notify_message", "log1"],
SCRIPT_NAME + "_notice",
"notify_message",
"log1",
],
"old_message": [ "old_message": [
SCRIPT_NAME + "_message", SCRIPT_NAME + "_message",
"notify_message", "notify_message",
"no_log", "no_log",
"no_highlight" "no_highlight",
], ],
"join": [ "join": [SCRIPT_NAME + "_join", "log4"],
SCRIPT_NAME + "_join", "part": [SCRIPT_NAME + "_leave", "log4"],
"log4" "kick": [SCRIPT_NAME + "_kick", "log4"],
], "invite": [SCRIPT_NAME + "_invite", "log4"],
"part": [ "topic": [SCRIPT_NAME + "_topic", "log3"],
SCRIPT_NAME + "_leave",
"log4"
],
"kick": [
SCRIPT_NAME + "_kick",
"log4"
],
"invite": [
SCRIPT_NAME + "_invite",
"log4"
],
"topic": [
SCRIPT_NAME + "_topic",
"log3",
]
} }
membership_messages = { membership_messages = {
"join": "has joined", "join": "has joined",
"part": "has left", "part": "has left",
"kick": "has been kicked from", "kick": "has been kicked from",
"invite": "has been invited to" "invite": "has been invited to",
} }
class Line(object): class Line(object):
@ -249,9 +224,7 @@ class WeechatChannelBuffer(object):
@property @property
def tags(self): def tags(self):
tags_count = W.hdata_get_var_array_size( tags_count = W.hdata_get_var_array_size(
self._hdata, self._hdata, self._ptr, "tags_array"
self._ptr,
"tags_array"
) )
tags = [ tags = [
@ -300,7 +273,7 @@ class WeechatChannelBuffer(object):
tags=None, tags=None,
prefix=None, prefix=None,
message=None, message=None,
highlight=None highlight=None,
): ):
new_data = {} new_data = {}
@ -309,7 +282,7 @@ class WeechatChannelBuffer(object):
if date_printed: if date_printed:
new_data["date_printed"] = str(date_printed) new_data["date_printed"] = str(date_printed)
if tags: if tags:
new_data["tags_array"] = ','.join(tags) new_data["tags_array"] = ",".join(tags)
if prefix: if prefix:
new_data["prefix"] = prefix new_data["prefix"] = prefix
if message: if message:
@ -337,8 +310,8 @@ class WeechatChannelBuffer(object):
self.topic_author = "" self.topic_author = ""
self.topic_date = None self.topic_date = None
W.buffer_set(self._ptr, "localvar_set_type", 'channel') W.buffer_set(self._ptr, "localvar_set_type", "channel")
W.buffer_set(self._ptr, "type", 'formatted') W.buffer_set(self._ptr, "type", "formatted")
W.buffer_set(self._ptr, "localvar_set_channel", name) W.buffer_set(self._ptr, "localvar_set_channel", name)
@ -350,32 +323,16 @@ class WeechatChannelBuffer(object):
# W.buffer_set(self._ptr, "short_name", short_name) # W.buffer_set(self._ptr, "short_name", short_name)
W.nicklist_add_group( W.nicklist_add_group(
self._ptr, self._ptr, "", "000|o", "weechat.color.nicklist_group", 1
'',
"000|o",
"weechat.color.nicklist_group",
1
) )
W.nicklist_add_group( W.nicklist_add_group(
self._ptr, self._ptr, "", "001|h", "weechat.color.nicklist_group", 1
'',
"001|h",
"weechat.color.nicklist_group",
1
) )
W.nicklist_add_group( W.nicklist_add_group(
self._ptr, self._ptr, "", "002|v", "weechat.color.nicklist_group", 1
'',
"002|v",
"weechat.color.nicklist_group",
1
) )
W.nicklist_add_group( W.nicklist_add_group(
self._ptr, self._ptr, "", "999|...", "weechat.color.nicklist_group", 1
'',
"999|...",
"weechat.color.nicklist_group",
1
) )
W.buffer_set(self._ptr, "nicklist", "1") W.buffer_set(self._ptr, "nicklist", "1")
@ -385,9 +342,7 @@ class WeechatChannelBuffer(object):
# TODO make this configurable # TODO make this configurable
W.buffer_set( W.buffer_set(
self._ptr, self._ptr, "highlight_tags_restrict", SCRIPT_NAME + "_message"
"highlight_tags_restrict",
SCRIPT_NAME + "_message"
) )
@property @property
@ -428,23 +383,18 @@ class WeechatChannelBuffer(object):
@property @property
def lines(self): def lines(self):
own_lines = W.hdata_pointer( own_lines = W.hdata_pointer(self._hdata, self._ptr, "own_lines")
self._hdata,
self._ptr,
"own_lines"
)
if own_lines: if own_lines:
hdata_line = W.hdata_get("line") hdata_line = W.hdata_get("line")
line_pointer = W.hdata_pointer( line_pointer = W.hdata_pointer(
W.hdata_get("lines"), own_lines, "last_line") W.hdata_get("lines"), own_lines, "last_line"
)
while line_pointer: while line_pointer:
data_pointer = W.hdata_pointer( data_pointer = W.hdata_pointer(
hdata_line, hdata_line, line_pointer, "data"
line_pointer,
"data"
) )
if data_pointer: if data_pointer:
@ -469,9 +419,7 @@ class WeechatChannelBuffer(object):
# type: (str) -> None # type: (str) -> None
""" Print an error to the room buffer """ """ Print an error to the room buffer """
message = "{prefix}{script}: {message}".format( message = "{prefix}{script}: {message}".format(
prefix=W.prefix("error"), prefix=W.prefix("error"), script=SCRIPT_NAME, message=string
script=SCRIPT_NAME,
message=string
) )
self._print(message) self._print(message)
@ -493,7 +441,7 @@ class WeechatChannelBuffer(object):
color = self._color_for_tags(user.color) color = self._color_for_tags(user.color)
if message_type != "action" and message_type != "notice": if message_type not in ("action", "notice"):
tags.append("prefix_nick_{color}".format(color=color)) tags.append("prefix_nick_{color}".format(color=color))
return tags return tags
@ -507,18 +455,23 @@ class WeechatChannelBuffer(object):
return WeechatUser(nick) return WeechatUser(nick)
def _print_message(self, user, message, date, tags): def _print_message(self, user, message, date, tags):
prefix_string = ("" if not user.prefix else "{}{}{}".format( prefix_string = (
W.color(self._get_prefix_color(user.prefix)), ""
user.prefix, if not user.prefix
W.color("reset") else "{}{}{}".format(
)) W.color(self._get_prefix_color(user.prefix)),
user.prefix,
W.color("reset"),
)
)
data = "{prefix}{color}{author}{ncolor}\t{msg}".format( data = "{prefix}{color}{author}{ncolor}\t{msg}".format(
prefix=prefix_string, prefix=prefix_string,
color=W.color(user.color), color=W.color(user.color),
author=user.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)
@ -534,27 +487,32 @@ class WeechatChannelBuffer(object):
def notice(self, nick, message, date, extra_tags=None): def notice(self, nick, message, date, extra_tags=None):
# type: (str, str, int, Optional[List[str]]) -> None # type: (str, str, int, Optional[List[str]]) -> None
user = self._get_user(nick) user = self._get_user(nick)
user_prefix = ("" if not user.prefix else "{}{}{}".format( user_prefix = (
W.color(self._get_prefix_color(user.prefix)), ""
user.prefix, if not user.prefix
W.color("reset") else "{}{}{}".format(
)) W.color(self._get_prefix_color(user.prefix)),
user.prefix,
user_string = "{}{}{}{}".format( W.color("reset"),
user_prefix, )
user.color,
user.nick,
W.color("reset")
) )
data = ("{prefix}\t{color}Notice" user_string = "{}{}{}{}".format(
"{del_color}({ncolor}{user}{del_color}){ncolor}" user_prefix, user.color, user.nick, W.color("reset")
": {message}").format(prefix=W.prefix("network"), )
color=W.color("irc.color.notice"),
del_color=W.color("chat_delimiters"), data = (
ncolor=W.color("reset"), "{prefix}\t{color}Notice"
user=user_string, "{del_color}({ncolor}{user}{del_color}){ncolor}"
message=message) ": {message}"
).format(
prefix=W.prefix("network"),
color=W.color("irc.color.notice"),
del_color=W.color("chat_delimiters"),
ncolor=W.color("reset"),
user=user_string,
message=message,
)
tags = self._message_tags(user, "notice") + (extra_tags or []) tags = self._message_tags(user, "notice") + (extra_tags or [])
self.print_date_tags(data, date, tags) self.print_date_tags(data, date, tags)
@ -563,27 +521,33 @@ class WeechatChannelBuffer(object):
self.unmask_smart_filtered_nick(nick) self.unmask_smart_filtered_nick(nick)
def _print_action(self, user, message, date, tags): def _print_action(self, user, message, date, tags):
nick_prefix = ("" if not user.prefix else "{}{}{}".format( nick_prefix = (
W.color(self._get_prefix_color(user.prefix)), ""
user.prefix, if not user.prefix
W.color("reset") else "{}{}{}".format(
)) W.color(self._get_prefix_color(user.prefix)),
user.prefix,
W.color("reset"),
)
)
data = ("{prefix}{nick_prefix}{nick_color}{author}" data = (
"{ncolor} {msg}").format( "{prefix}{nick_prefix}{nick_color}{author}" "{ncolor} {msg}"
).format(
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=user.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=[]): def action(self, nick, message, date, extra_tags=None):
# type: (str, str, int, Optional[List[str]]) -> None # type: (str, str, int, Optional[List[str]]) -> None
user = self._get_user(nick) user = self._get_user(nick)
tags = self._message_tags(user, "action") + extra_tags tags = self._message_tags(user, "action") + (extra_tags or [])
self._print_action(user, message, date, tags) self._print_action(user, message, date, tags)
user.update_speaking_time(date) user.update_speaking_time(date)
@ -624,9 +588,7 @@ class WeechatChannelBuffer(object):
if not nick_pointer: if not nick_pointer:
group = W.nicklist_search_group( group = W.nicklist_search_group(
self._ptr, self._ptr, "", self._get_nicklist_group(user)
"",
self._get_nicklist_group(user)
) )
prefix = user.prefix if user.prefix else " " prefix = user.prefix if user.prefix else " "
W.nicklist_add_nick( W.nicklist_add_nick(
@ -636,22 +598,22 @@ class WeechatChannelBuffer(object):
user.color, user.color,
prefix, prefix,
self._get_prefix_color(user.prefix), self._get_prefix_color(user.prefix),
1 1,
) )
def _membership_message(self, user, message_type): def _membership_message(self, user, message_type):
# type: (WeechatUser, str) -> str # type: (WeechatUser, str) -> str
action_color = ("green" if message_type == "join" action_color = "green" if message_type in ("join", "invite") else "red"
or message_type == "invite" else "red") prefix = "join" if message_type in ("join", "invite") else "quit"
prefix = ("join" if message_type == "join" or message_type == "invite"
else "quit")
membership_message = self.membership_messages[message_type] membership_message = self.membership_messages[message_type]
message = ("{prefix}{color}{author}{ncolor} " message = (
"{del_color}({host_color}{host}{del_color})" "{prefix}{color}{author}{ncolor} "
"{action_color} {message} " "{del_color}({host_color}{host}{del_color})"
"{channel_color}{room}{ncolor}").format( "{action_color} {message} "
"{channel_color}{room}{ncolor}"
).format(
prefix=W.prefix(prefix), prefix=W.prefix(prefix),
color=W.color(user.color), color=W.color(user.color),
author=user.nick, author=user.nick,
@ -662,11 +624,12 @@ class WeechatChannelBuffer(object):
action_color=W.color(action_color), action_color=W.color(action_color),
message=membership_message, message=membership_message,
channel_color=W.color("chat_channel"), channel_color=W.color("chat_channel"),
room=self.short_name) room=self.short_name,
)
return message return message
def join(self, user, date, message=True, extra_tags=[]): def join(self, user, date, message=True, extra_tags=None):
# type: (WeechatUser, int, Optional[bool], Optional[List[str]]) -> None # type: (WeechatUser, int, Optional[bool], Optional[List[str]]) -> None
self._add_user_to_nicklist(user) self._add_user_to_nicklist(user)
self.users[user.nick] = user self.users[user.nick] = user
@ -681,12 +644,12 @@ class WeechatChannelBuffer(object):
self.print_date_tags(message, date, tags) self.print_date_tags(message, date, tags)
self.add_smart_filtered_nick(user.nick) self.add_smart_filtered_nick(user.nick)
def invite(self, nick, date, extra_tags=[]): def invite(self, nick, date, extra_tags=None):
# type: (str, int, Optional[bool], Optional[List[str]]) -> None # type: (str, int, Optional[bool], Optional[List[str]]) -> None
user = self._get_user(nick) user = self._get_user(nick)
tags = self._message_tags(user, "invite") tags = self._message_tags(user, "invite")
message = self._membership_message(user, "invite") message = self._membership_message(user, "invite")
self.print_date_tags(message, date, tags + extra_tags) self.print_date_tags(message, date, tags + (extra_tags or []))
def _remove_user_from_nicklist(self, user): def _remove_user_from_nicklist(self, user):
# type: (WeechatUser) -> None # type: (WeechatUser) -> None
@ -695,7 +658,7 @@ class WeechatChannelBuffer(object):
if nick_pointer: if nick_pointer:
W.nicklist_remove_nick(self._ptr, nick_pointer) W.nicklist_remove_nick(self._ptr, nick_pointer)
def _leave(self, nick, date, message, leave_type, extra_tags): def _leave(self, nick, date, message, leave_type, extra_tags=None):
# type: (str, int, bool, str, List[str]) -> None # type: (str, int, bool, str, List[str]) -> None
user = self._get_user(nick) user = self._get_user(nick)
self._remove_user_from_nicklist(user) self._remove_user_from_nicklist(user)
@ -708,34 +671,36 @@ class WeechatChannelBuffer(object):
tags.append(SCRIPT_NAME + "_smart_filter") tags.append(SCRIPT_NAME + "_smart_filter")
message = self._membership_message(user, leave_type) message = self._membership_message(user, leave_type)
self.print_date_tags(message, date, tags + extra_tags) self.print_date_tags(message, date, tags + (extra_tags or []))
self.remove_smart_filtered_nick(user.nick) self.remove_smart_filtered_nick(user.nick)
if user.nick in self.users: if user.nick in self.users:
del self.users[user.nick] del self.users[user.nick]
def part(self, nick, date, message=True, extra_tags=[]): def part(self, nick, date, message=True, extra_tags=None):
# type: (str, int, Optional[bool], Optional[List[str]]) -> None # type: (str, int, Optional[bool], Optional[List[str]]) -> None
self._leave(nick, date, message, "part", extra_tags) self._leave(nick, date, message, "part", extra_tags)
def kick(self, nick, date, message=True, extra_tags=[]): def kick(self, nick, date, message=True, extra_tags=None):
# type: (str, int, Optional[bool], Optional[List[str]]) -> None # type: (str, int, Optional[bool], Optional[List[str]]) -> None
self._leave(nick, date, message, "kick", extra_tags=[]) self._leave(nick, date, message, "kick", extra_tags)
def _print_topic(self, nick, topic, date): def _print_topic(self, nick, topic, date):
user = self._get_user(nick) user = self._get_user(nick)
tags = self._message_tags(user, "topic") tags = self._message_tags(user, "topic")
data = ("{prefix}{nick} has changed " data = (
"the topic for {chan_color}{room}{ncolor} " "{prefix}{nick} has changed "
"to \"{topic}\"").format( "the topic for {chan_color}{room}{ncolor} "
prefix=W.prefix("network"), 'to "{topic}"'
nick=user.nick, ).format(
chan_color=W.color("chat_channel"), prefix=W.prefix("network"),
ncolor=W.color("reset"), nick=user.nick,
room=self.short_name, chan_color=W.color("chat_channel"),
topic=topic ncolor=W.color("reset"),
) room=self.short_name,
topic=topic,
)
self.print_date_tags(data, date, tags) self.print_date_tags(data, date, tags)
user.update_speaking_time(date) user.update_speaking_time(date)
@ -799,9 +764,7 @@ class RoomBuffer(object):
self.displayed_nicks = {} self.displayed_nicks = {}
user = shorten_sender(self.room.own_user_id) user = shorten_sender(self.room.own_user_id)
self.weechat_buffer = WeechatChannelBuffer( self.weechat_buffer = WeechatChannelBuffer(
buffer_name, buffer_name, server_name, user
server_name,
user
) )
def find_nick(self, user_id): def find_nick(self, user_id):
@ -837,11 +800,7 @@ class RoomBuffer(object):
buffer_user.color = "weechat.color.chat_nick_self" buffer_user.color = "weechat.color.chat_nick_self"
user.nick_color = "weechat.color.chat_nick_self" user.nick_color = "weechat.color.chat_nick_self"
self.weechat_buffer.join( self.weechat_buffer.join(buffer_user, date, not is_state)
buffer_user,
date,
not is_state
)
date = server_ts_to_weechat(event.server_timestamp) date = server_ts_to_weechat(event.server_timestamp)
@ -909,22 +868,28 @@ class RoomBuffer(object):
message = line.message message = line.message
tags = line.tags tags = line.tags
reason = ("" if not event.reason else reason = (
", reason: \"{reason}\"".format(reason=event.reason)) ""
if not event.reason
else ', reason: "{reason}"'.format(reason=event.reason)
)
redaction_msg = ("{del_color}<{log_color}Message redacted by: " redaction_msg = (
"{censor}{log_color}{reason}{del_color}>" "{del_color}<{log_color}Message redacted by: "
"{ncolor}").format( "{censor}{log_color}{reason}{del_color}>"
del_color=W.color("chat_delimiters"), "{ncolor}"
ncolor=W.color("reset"), ).format(
log_color=W.color("logger.color.backlog_line"), del_color=W.color("chat_delimiters"),
censor=censor, ncolor=W.color("reset"),
reason=reason) log_color=W.color("logger.color.backlog_line"),
censor=censor,
reason=reason,
)
new_message = "" new_message = ""
if G.CONFIG.look.redaction_type == RedactType.STRIKETHROUGH: if G.CONFIG.look.redaction_type == RedactType.STRIKETHROUGH:
plaintext_msg = W.string_remove_color(message, '') plaintext_msg = W.string_remove_color(message, "")
new_message = string_strikethrough(plaintext_msg) new_message = string_strikethrough(plaintext_msg)
elif G.CONFIG.look.redaction_type == RedactType.NOTICE: elif G.CONFIG.look.redaction_type == RedactType.NOTICE:
new_message = message new_message = message
@ -944,18 +909,24 @@ class RoomBuffer(object):
tags = self.get_event_tags(event) tags = self.get_event_tags(event)
tags.append(SCRIPT_NAME + "_redacted") tags.append(SCRIPT_NAME + "_redacted")
reason = (", reason: \"{reason}\"".format(reason=event.reason) reason = (
if event.reason else "") ', reason: "{reason}"'.format(reason=event.reason)
if event.reason
else ""
)
censor = self.find_nick(event.redacter) censor = self.find_nick(event.redacter)
data = ("{del_color}<{log_color}Message redacted by: " data = (
"{censor}{log_color}{reason}{del_color}>{ncolor}").format( "{del_color}<{log_color}Message redacted by: "
del_color=W.color("chat_delimiters"), "{censor}{log_color}{reason}{del_color}>{ncolor}"
ncolor=W.color("reset"), ).format(
log_color=W.color("logger.color.backlog_line"), del_color=W.color("chat_delimiters"),
censor=censor, ncolor=W.color("reset"),
reason=reason) log_color=W.color("logger.color.backlog_line"),
censor=censor,
reason=reason,
)
self.weechat_buffer.message(nick, data, date, tags) self.weechat_buffer.message(nick, data, date, tags)
@ -966,20 +937,22 @@ class RoomBuffer(object):
nick, nick,
event.topic, event.topic,
server_ts_to_weechat(event.server_timestamp), server_ts_to_weechat(event.server_timestamp),
not is_state) not is_state,
)
@staticmethod @staticmethod
def get_event_tags(event): def get_event_tags(event):
return ["matrix_id_{}".format(event.event_id)] return ["matrix_id_{}".format(event.event_id)]
def _handle_power_level(self, event): def _handle_power_level(self, _):
for user_id in self.room.power_levels.users: for user_id in self.room.power_levels.users:
if user_id in self.displayed_nicks: if user_id in self.displayed_nicks:
nick = self.find_nick(user_id) nick = self.find_nick(user_id)
user = self.weechat_buffer.users[nick] user = self.weechat_buffer.users[nick]
user.power_level = self.room.power_levels.get_user_level( user.power_level = self.room.power_levels.get_user_level(
user_id) user_id
)
# There is no way to change the group of a user without # There is no way to change the group of a user without
# removing him from the nicklist # removing him from the nicklist
@ -994,9 +967,11 @@ class RoomBuffer(object):
elif isinstance(event, PowerLevelsEvent): elif isinstance(event, PowerLevelsEvent):
self._handle_power_level(event) self._handle_power_level(event)
elif isinstance(event, RoomEncryptionEvent): elif isinstance(event, RoomEncryptionEvent):
message = ("This room is encrypted, encryption is " message = (
"currently unsuported. Message sending is disabled for " "This room is encrypted, encryption is "
"this room.") "currently unsuported. Message sending is disabled for "
"this room."
)
self.weechat_buffer.error(message) self.weechat_buffer.error(message)
def handle_timeline_event(self, event): def handle_timeline_event(self, event):
@ -1016,10 +991,7 @@ class RoomBuffer(object):
nick = self.find_nick(event.sender) nick = self.find_nick(event.sender)
date = server_ts_to_weechat(event.server_timestamp) date = server_ts_to_weechat(event.server_timestamp)
self.weechat_buffer.action( self.weechat_buffer.action(
nick, nick, event.body, date, self.get_event_tags(event)
event.body,
date,
self.get_event_tags(event)
) )
elif isinstance(event, RoomMessageText): elif isinstance(event, RoomMessageText):
@ -1029,25 +1001,18 @@ class RoomBuffer(object):
if event.formatted_body: if event.formatted_body:
formatted = Formatted.from_html(event.formatted_body) formatted = Formatted.from_html(event.formatted_body)
data = (formatted.to_weechat() data = formatted.to_weechat() if formatted else event.body
if formatted else event.body)
date = server_ts_to_weechat(event.server_timestamp) date = server_ts_to_weechat(event.server_timestamp)
self.weechat_buffer.message( self.weechat_buffer.message(
nick, nick, data, date, self.get_event_tags(event)
data,
date,
self.get_event_tags(event)
) )
elif isinstance(event, RoomMessageNotice): elif isinstance(event, RoomMessageNotice):
nick = self.find_nick(event.sender) nick = self.find_nick(event.sender)
date = server_ts_to_weechat(event.server_timestamp) date = server_ts_to_weechat(event.server_timestamp)
self.weechat_buffer.notice( self.weechat_buffer.notice(
nick, nick, event.body, date, self.get_event_tags(event)
event.body,
date,
self.get_event_tags(event)
) )
elif isinstance(event, RoomMessageMedia): elif isinstance(event, RoomMessageMedia):
@ -1056,15 +1021,11 @@ class RoomBuffer(object):
http_url = Api.mxc_to_http(event.url) http_url = Api.mxc_to_http(event.url)
url = http_url if http_url else event.url url = http_url if http_url else event.url
description = ("/{}".format(event.body) description = "/{}".format(event.body) if event.body else ""
if event.body else "")
data = "{url}{desc}".format(url=url, desc=description) data = "{url}{desc}".format(url=url, desc=description)
self.weechat_buffer.message( self.weechat_buffer.message(
nick, nick, data, date, self.get_event_tags(event)
data,
date,
self.get_event_tags(event)
) )
elif isinstance(event, RoomMessageUnknown): elif isinstance(event, RoomMessageUnknown):
@ -1072,10 +1033,7 @@ class RoomBuffer(object):
date = server_ts_to_weechat(event.server_timestamp) date = server_ts_to_weechat(event.server_timestamp)
data = ("Unknown message of type {t}").format(t=event.type) data = ("Unknown message of type {t}").format(t=event.type)
self.weechat_buffer.message( self.weechat_buffer.message(
nick, nick, data, date, self.get_event_tags(event)
data,
date,
self.get_event_tags(event)
) )
elif isinstance(event, RedactionEvent): elif isinstance(event, RedactionEvent):
@ -1085,9 +1043,11 @@ class RoomBuffer(object):
self._handle_redacted_message(event) self._handle_redacted_message(event)
elif isinstance(event, RoomEncryptionEvent): elif isinstance(event, RoomEncryptionEvent):
message = ("This room is encrypted, encryption is " message = (
"currently unsuported. Message sending is disabled for " "This room is encrypted, encryption is "
"this room.") "currently unsuported. Message sending is disabled for "
"this room."
)
self.weechat_buffer.error(message) self.weechat_buffer.error(message)
elif isinstance(event, PowerLevelsEvent): elif isinstance(event, PowerLevelsEvent):
@ -1106,8 +1066,9 @@ class RoomBuffer(object):
# ) # )
else: else:
W.prnt("", "Unhandled event of type {}.".format( W.prnt(
type(event).__name__)) "", "Unhandled event of type {}.".format(type(event).__name__)
)
def self_message(self, message): def self_message(self, message):
# type: (OwnMessage) -> None # type: (OwnMessage) -> None
@ -1125,10 +1086,7 @@ class RoomBuffer(object):
tags = self.get_event_tags(message) tags = self.get_event_tags(message)
self.weechat_buffer.self_action( self.weechat_buffer.self_action(
nick, nick, message.formatted_message.to_weechat(), date, tags
message.formatted_message.to_weechat(),
date,
tags
) )
def old_redacted(self, event): def old_redacted(self, event):
@ -1136,20 +1094,26 @@ class RoomBuffer(object):
SCRIPT_NAME + "_message", SCRIPT_NAME + "_message",
"notify_message", "notify_message",
"no_log", "no_log",
"no_highlight" "no_highlight",
] ]
reason = (", reason: \"{reason}\"".format(reason=event.reason) reason = (
if event.reason else "") ', reason: "{reason}"'.format(reason=event.reason)
if event.reason
else ""
)
censor = self.find_nick(event.redacter) censor = self.find_nick(event.redacter)
data = ("{del_color}<{log_color}Message redacted by: " data = (
"{censor}{log_color}{reason}{del_color}>{ncolor}").format( "{del_color}<{log_color}Message redacted by: "
del_color=W.color("chat_delimiters"), "{censor}{log_color}{reason}{del_color}>{ncolor}"
ncolor=W.color("reset"), ).format(
log_color=W.color("logger.color.backlog_line"), del_color=W.color("chat_delimiters"),
censor=censor, ncolor=W.color("reset"),
reason=reason) log_color=W.color("logger.color.backlog_line"),
censor=censor,
reason=reason,
)
tags += self.get_event_tags(event) tags += self.get_event_tags(event)
nick = self.find_nick(event.sender) nick = self.find_nick(event.sender)
@ -1162,12 +1126,15 @@ class RoomBuffer(object):
SCRIPT_NAME + "_message", SCRIPT_NAME + "_message",
"notify_message", "notify_message",
"no_log", "no_log",
"no_highlight" "no_highlight",
] ]
tags += self.get_event_tags(event) tags += self.get_event_tags(event)
nick = self.find_nick(event.sender) nick = self.find_nick(event.sender)
data = (event.formatted_message.to_weechat() data = (
if event.formatted_message else event.message) event.formatted_message.to_weechat()
if event.formatted_message
else event.message
)
user = self.weechat_buffer._get_user(nick) user = self.weechat_buffer._get_user(nick)
date = server_ts_to_weechat(event.server_timestamp) date = server_ts_to_weechat(event.server_timestamp)
self.weechat_buffer._print_message(user, data, date, tags) self.weechat_buffer._print_message(user, data, date, tags)
@ -1175,13 +1142,7 @@ class RoomBuffer(object):
def sort_messages(self): def sort_messages(self):
class LineCopy(object): class LineCopy(object):
def __init__( def __init__(
self, self, date, date_printed, tags, prefix, message, highlight
date,
date_printed,
tags,
prefix,
message,
highlight
): ):
self.date = date self.date = date
self.date_printed = date_printed self.date_printed = date_printed
@ -1192,18 +1153,25 @@ class RoomBuffer(object):
@classmethod @classmethod
def from_line(cls, line): def from_line(cls, line):
return cls(line.date, line.date_printed, line.tags, return cls(
line.prefix, line.message, line.highlight) line.date,
line.date_printed,
line.tags,
line.prefix,
line.message,
line.highlight,
)
lines = [ lines = [
LineCopy.from_line(line) for line in self.weechat_buffer.lines LineCopy.from_line(line) for line in self.weechat_buffer.lines
] ]
sorted_lines = sorted(lines, key=lambda line: line.date, reverse=True) sorted_lines = sorted(lines, key=lambda line: line.date, reverse=True)
for n, line in enumerate(self.weechat_buffer.lines): for line_number, line in enumerate(self.weechat_buffer.lines):
new = sorted_lines[n] new = sorted_lines[line_number]
line.update(new.date, new.date_printed, new.tags, new.prefix, line.update(
new.message) new.date, new.date_printed, new.tags, new.prefix, new.message
)
def handle_backlog(self, events): def handle_backlog(self, events):
for event in events: for event in events:
@ -1230,7 +1198,7 @@ class RoomBuffer(object):
break break
if leave_index: if leave_index:
timeline_events = info.timeline.events[leave_index+1:] timeline_events = info.timeline.events[leave_index + 1 :]
# Handle our leave as a state event since we're not in the # Handle our leave as a state event since we're not in the
# nicklist anymore but we're already printed out our leave # nicklist anymore but we're already printed out our leave
self.handle_state_event(info.timeline.events[leave_index]) self.handle_state_event(info.timeline.events[leave_index])

View file

@ -18,34 +18,34 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import html
import re
import textwrap
# pylint: disable=redefined-builtin # pylint: disable=redefined-builtin
from builtins import str from builtins import str
from collections import namedtuple from collections import namedtuple
from matrix import globals as G
from matrix.globals import W
from matrix.utils import string_strikethrough
import re
import textwrap
import webcolors import webcolors
from pygments import highlight from pygments import highlight
from pygments.lexers import guess_lexer, get_lexer_by_name
from pygments.formatter import Formatter from pygments.formatter import Formatter
from pygments.lexers import get_lexer_by_name, guess_lexer
from pygments.util import ClassNotFound from pygments.util import ClassNotFound
from . import globals as G
from .globals import W
from .utils import string_strikethrough
try: try:
from HTMLParser import HTMLParser from HTMLParser import HTMLParser
except ImportError: except ImportError:
from html.parser import HTMLParser from html.parser import HTMLParser
import html
FormattedString = namedtuple('FormattedString', ['text', 'attributes']) FormattedString = namedtuple("FormattedString", ["text", "attributes"])
class Formatted(): class Formatted:
def __init__(self, substrings): def __init__(self, substrings):
# type: (List[FormattedString]) -> None # type: (List[FormattedString]) -> None
self.substrings = substrings self.substrings = substrings
@ -55,8 +55,8 @@ class Formatted():
return textwrap.TextWrapper( return textwrap.TextWrapper(
width=67, width=67,
initial_indent="{}> ".format(W.color(G.CONFIG.color.quote)), initial_indent="{}> ".format(W.color(G.CONFIG.color.quote)),
subsequent_indent="{}> ".format(W.color(G.CONFIG.color.quote)) subsequent_indent="{}> ".format(W.color(G.CONFIG.color.quote)),
) )
def is_formatted(self): def is_formatted(self):
# type: (Formatted) -> bool # type: (Formatted) -> bool
@ -89,21 +89,22 @@ class Formatted():
# Markdown bold # Markdown bold
elif line[i] == "*": elif line[i] == "*":
if attributes["italic"] and not line[i-1].isspace(): if attributes["italic"] and not line[i - 1].isspace():
if text: if text:
substrings.append(FormattedString( substrings.append(
text, attributes.copy())) FormattedString(text, attributes.copy())
)
text = "" text = ""
attributes["italic"] = not attributes["italic"] attributes["italic"] = not attributes["italic"]
i = i + 1 i = i + 1
continue continue
elif attributes["italic"] and line[i-1].isspace(): elif attributes["italic"] and line[i - 1].isspace():
text = text + line[i] text = text + line[i]
i = i + 1 i = i + 1
continue continue
elif i+1 < len(line) and line[i+1].isspace(): elif i + 1 < len(line) and line[i + 1].isspace():
text = text + line[i] text = text + line[i]
i = i + 1 i = i + 1
continue continue
@ -201,27 +202,34 @@ class Formatted():
def add_attribute(string, name, value): def add_attribute(string, name, value):
if name == "bold" and value: if name == "bold" and value:
return "{bold_on}{text}{bold_off}".format( return "{bold_on}{text}{bold_off}".format(
bold_on="<strong>", text=string, bold_off="</strong>") bold_on="<strong>", text=string, bold_off="</strong>"
elif name == "italic" and value: )
if name == "italic" and value:
return "{italic_on}{text}{italic_off}".format( return "{italic_on}{text}{italic_off}".format(
italic_on="<em>", text=string, italic_off="</em>") italic_on="<em>", text=string, italic_off="</em>"
elif name == "underline" and value: )
if name == "underline" and value:
return "{underline_on}{text}{underline_off}".format( return "{underline_on}{text}{underline_off}".format(
underline_on="<u>", text=string, underline_off="</u>") underline_on="<u>", text=string, underline_off="</u>"
elif name == "strikethrough" and value: )
if name == "strikethrough" and value:
return "{strike_on}{text}{strike_off}".format( return "{strike_on}{text}{strike_off}".format(
strike_on="<del>", text=string, strike_off="</del>") strike_on="<del>", text=string, strike_off="</del>"
elif name == "quote" and value: )
if name == "quote" and value:
return "{quote_on}{text}{quote_off}".format( return "{quote_on}{text}{quote_off}".format(
quote_on="<blockquote>", quote_on="<blockquote>",
text=string, text=string,
quote_off="</blockquote>") quote_off="</blockquote>",
elif name == "fgcolor" and value: )
if name == "fgcolor" and value:
return "{color_on}{text}{color_off}".format( return "{color_on}{text}{color_off}".format(
color_on="<font color={color}>".format( color_on="<font color={color}>".format(
color=color_weechat_to_html(value)), color=color_weechat_to_html(value)
),
text=string, text=string,
color_off="</font>") color_off="</font>",
)
return string return string
@ -261,28 +269,32 @@ class Formatted():
return "{bold_on}{text}{bold_off}".format( return "{bold_on}{text}{bold_off}".format(
bold_on=W.color("bold"), bold_on=W.color("bold"),
text=string, text=string,
bold_off=W.color("-bold")) bold_off=W.color("-bold"),
)
elif name == "italic" and value: if name == "italic" and value:
return "{italic_on}{text}{italic_off}".format( return "{italic_on}{text}{italic_off}".format(
italic_on=W.color("italic"), italic_on=W.color("italic"),
text=string, text=string,
italic_off=W.color("-italic")) italic_off=W.color("-italic"),
)
elif name == "underline" and value: if name == "underline" and value:
return "{underline_on}{text}{underline_off}".format( return "{underline_on}{text}{underline_off}".format(
underline_on=W.color("underline"), underline_on=W.color("underline"),
text=string, text=string,
underline_off=W.color("-underline")) underline_off=W.color("-underline"),
)
elif name == "strikethrough" and value: if name == "strikethrough" and value:
return string_strikethrough(string) return string_strikethrough(string)
elif name == "quote" and value: if name == "quote" and value:
return self.textwrapper.fill( return self.textwrapper.fill(
W.string_remove_color(string.replace("\n", ""), "")) W.string_remove_color(string.replace("\n", ""), "")
)
elif name == "code" and value: if name == "code" and value:
try: try:
lexer = get_lexer_by_name(value) lexer = get_lexer_by_name(value)
except ClassNotFound: except ClassNotFound:
@ -292,17 +304,19 @@ class Formatted():
# from the output # from the output
return highlight(string, lexer, WeechatFormatter())[:-1] return highlight(string, lexer, WeechatFormatter())[:-1]
elif name == "fgcolor" and value: if name == "fgcolor" and value:
return "{color_on}{text}{color_off}".format( return "{color_on}{text}{color_off}".format(
color_on=W.color(value), color_on=W.color(value),
text=string, text=string,
color_off=W.color("resetcolor")) color_off=W.color("resetcolor"),
)
elif name == "bgcolor" and value: if name == "bgcolor" and value:
return "{color_on}{text}{color_off}".format( return "{color_on}{text}{color_off}".format(
color_on=W.color("," + value), color_on=W.color("," + value),
text=string, text=string,
color_off=W.color("resetcolor")) color_off=W.color("resetcolor"),
)
return string return string
@ -313,17 +327,18 @@ class Formatted():
# We need to handle strikethrough first, since doing # We need to handle strikethrough first, since doing
# a strikethrough followed by other attributes succeeds in the # a strikethrough followed by other attributes succeeds in the
# terminal, but doing it the other way around results in garbage. # terminal, but doing it the other way around results in garbage.
if 'strikethrough' in attributes: if "strikethrough" in attributes:
text = add_attribute(text, 'strikethrough', text = add_attribute(
attributes['strikethrough']) text, "strikethrough", attributes["strikethrough"]
attributes.pop('strikethrough') )
attributes.pop("strikethrough")
for key, value in attributes.items(): for key, value in attributes.items():
text = add_attribute(text, key, value) text = add_attribute(text, key, value)
return text return text
weechat_strings = map(format_string, self.substrings) weechat_strings = map(format_string, self.substrings)
return re.sub(r'\n+', '\n', "".join(weechat_strings)).strip() return re.sub(r"\n+", "\n", "".join(weechat_strings)).strip()
# TODO this should be a typed dict. # TODO this should be a typed dict.
@ -335,7 +350,7 @@ DEFAULT_ATRIBUTES = {
"quote": False, "quote": False,
"code": None, "code": None,
"fgcolor": None, "fgcolor": None,
"bgcolor": None "bgcolor": None,
} }
@ -566,7 +581,7 @@ def color_line_to_weechat(color_string):
"96": "250", "96": "250",
"97": "254", "97": "254",
"98": "231", "98": "231",
"99": "default" "99": "default",
} }
assert color_string in line_colors assert color_string in line_colors
@ -623,7 +638,7 @@ def colour_find_rgb(r, g, b):
cb = q2c[qb] cb = q2c[qb]
# If we have hit the colour exactly, return early. # If we have hit the colour exactly, return early.
if (cr == r and cg == g and cb == b): if cr == r and cg == g and cb == b:
return 16 + (36 * qr) + (6 * qg) + qb return 16 + (36 * qr) + (6 * qg) + qb
# Work out the closest grey (average of RGB). # Work out the closest grey (average of RGB).
@ -964,8 +979,7 @@ def color_weechat_to_html(color):
# yapf: enable # yapf: enable
if color in weechat_basic_colors: if color in weechat_basic_colors:
return hex_colors[weechat_basic_colors[color]] return hex_colors[weechat_basic_colors[color]]
else: return hex_colors[color]
return hex_colors[color]
class WeechatFormatter(Formatter): class WeechatFormatter(Formatter):
@ -977,7 +991,8 @@ class WeechatFormatter(Formatter):
start = end = "" start = end = ""
if style["color"]: if style["color"]:
start += "{}".format( start += "{}".format(
W.color(color_html_to_weechat(str(style["color"])))) W.color(color_html_to_weechat(str(style["color"])))
)
end = "{}".format(W.color("resetcolor")) + end end = "{}".format(W.color("resetcolor")) + end
if style["bold"]: if style["bold"]:
start += W.color("bold") start += W.color("bold")
@ -985,13 +1000,13 @@ class WeechatFormatter(Formatter):
if style["italic"]: if style["italic"]:
start += W.color("italic") start += W.color("italic")
end = W.color("-italic") + end end = W.color("-italic") + end
if style['underline']: if style["underline"]:
start += W.color("underline") start += W.color("underline")
end = W.color("-underline") + end end = W.color("-underline") + end
self.styles[token] = (start, end) self.styles[token] = (start, end)
def format(self, tokensource, outfile): def format(self, tokensource, outfile):
lastval = '' lastval = ""
lasttype = None lasttype = None
for ttype, value in tokensource: for ttype, value in tokensource:

View file

@ -15,18 +15,17 @@
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
from __future__ import unicode_literals from __future__ import unicode_literals
import argparse
import re
from builtins import str from builtins import str
import re from . import globals as G
import argparse from .colors import Formatted
from .globals import SERVERS, W
import matrix.globals from .server import MatrixServer
from matrix.globals import W, SERVERS from .utf import utf8_decode
from .utils import key_from_value, tags_from_line_data
from matrix.utf import utf8_decode
from matrix.utils import key_from_value, tags_from_line_data
from matrix.server import MatrixServer
from matrix.colors import Formatted
class ParseError(Exception): class ParseError(Exception):
@ -34,15 +33,15 @@ class ParseError(Exception):
class WeechatArgParse(argparse.ArgumentParser): class WeechatArgParse(argparse.ArgumentParser):
def print_usage(self, file): def print_usage(self, file=None):
pass pass
def error(self, message): def error(self, message):
m = ("{prefix}Error: {message} for command {command} " message = (
"(see /help {command})").format(prefix=W.prefix("error"), "{prefix}Error: {message} for command {command} "
message=message, "(see /help {command})"
command=self.prog) ).format(prefix=W.prefix("error"), message=message, command=self.prog)
W.prnt("", m) W.prnt("", message)
raise ParseError raise ParseError
@ -95,49 +94,59 @@ class WeechatCommandParser(object):
def hook_commands(): def hook_commands():
W.hook_command( W.hook_command(
# Command name and short description # Command name and short description
'matrix', "matrix",
'Matrix chat protocol command', "Matrix chat protocol command",
# Synopsis # Synopsis
('server add <server-name> <hostname>[:<port>] ||' (
'server delete|list|listfull <server-name> ||' "server add <server-name> <hostname>[:<port>] ||"
'connect <server-name> ||' "server delete|list|listfull <server-name> ||"
'disconnect <server-name> ||' "connect <server-name> ||"
'reconnect <server-name> ||' "disconnect <server-name> ||"
'help <matrix-command>'), "reconnect <server-name> ||"
"help <matrix-command>"
),
# Description # Description
(' server: list, add, or remove Matrix servers\n' (
' connect: connect to Matrix servers\n' " server: list, add, or remove Matrix servers\n"
'disconnect: disconnect from one or all Matrix servers\n' " connect: connect to Matrix servers\n"
' reconnect: reconnect to server(s)\n\n' "disconnect: disconnect from one or all Matrix servers\n"
' help: show detailed command help\n\n' " reconnect: reconnect to server(s)\n\n"
'Use /matrix help [command] to find out more.\n'), " help: show detailed command help\n\n"
"Use /matrix help [command] to find out more.\n"
),
# Completions # Completions
('server %(matrix_server_commands)|%* ||' (
'connect %(matrix_servers) ||' "server %(matrix_server_commands)|%* ||"
'disconnect %(matrix_servers) ||' "connect %(matrix_servers) ||"
'reconnect %(matrix_servers) ||' "disconnect %(matrix_servers) ||"
'help %(matrix_commands)'), "reconnect %(matrix_servers) ||"
"help %(matrix_commands)"
),
# Function name # Function name
'matrix_command_cb', "matrix_command_cb",
'') "",
)
W.hook_command( W.hook_command(
# Command name and short description # Command name and short description
'redact', "redact",
'redact messages', "redact messages",
# Synopsis # Synopsis
('<message-number>[:"<message-part>"] [<reason>]'), ('<message-number>[:"<message-part>"] [<reason>]'),
# Description # Description
("message-number: number of message to redact (starting from 1 for\n" (
" the last message received, counting up)\n" "message-number: number of message to redact (starting from 1 for\n"
" message-part: an initial part of the message (ignored, only used\n" " the last message received, counting up)\n"
" as visual feedback when using completion)\n" " message-part: an initial part of the message (ignored, only used\n"
" reason: the redaction reason\n"), " as visual feedback when using completion)\n"
" reason: the redaction reason\n"
),
# Completions # Completions
('%(matrix_messages)'), ("%(matrix_messages)"),
# Function name # Function name
'matrix_redact_command_cb', "matrix_redact_command_cb",
'') "",
)
W.hook_command( W.hook_command(
# Command name and short description # Command name and short description
@ -146,13 +155,13 @@ def hook_commands():
# Synopsis # Synopsis
("[<topic>|-delete]"), ("[<topic>|-delete]"),
# Description # Description
(" topic: topic to set\n" (" topic: topic to set\n" "-delete: delete room topic"),
"-delete: delete room topic"),
# Completions # Completions
"", "",
# Callback # Callback
"matrix_topic_command_cb", "matrix_topic_command_cb",
"") "",
)
W.hook_command( W.hook_command(
# Command name and short description # Command name and short description
@ -166,7 +175,8 @@ def hook_commands():
"", "",
# Callback # Callback
"matrix_me_command_cb", "matrix_me_command_cb",
"") "",
)
W.hook_command( W.hook_command(
# Command name and short description # Command name and short description
@ -175,13 +185,16 @@ def hook_commands():
# Synopsis # Synopsis
("<user-id> [<reason>]"), ("<user-id> [<reason>]"),
# Description # Description
("user-id: user-id to kick\n" (
" reason: reason why the user was kicked"), "user-id: user-id to kick\n"
" reason: reason why the user was kicked"
),
# Completions # Completions
("%(matrix_users)"), ("%(matrix_users)"),
# Callback # Callback
"matrix_kick_command_cb", "matrix_kick_command_cb",
"") "",
)
W.hook_command( W.hook_command(
# Command name and short description # Command name and short description
@ -195,7 +208,8 @@ def hook_commands():
("%(matrix_users)"), ("%(matrix_users)"),
# Callback # Callback
"matrix_invite_command_cb", "matrix_invite_command_cb",
"") "",
)
W.hook_command( W.hook_command(
# Command name and short description # Command name and short description
@ -204,13 +218,16 @@ def hook_commands():
# Synopsis # Synopsis
("<room-id>|<room-alias>"), ("<room-id>|<room-alias>"),
# Description # Description
(" room-id: room-id of the room to join\n" (
"room-alias: room alias of the room to join"), " room-id: room-id of the room to join\n"
"room-alias: room alias of the room to join"
),
# Completions # Completions
"", "",
# Callback # Callback
"matrix_join_command_cb", "matrix_join_command_cb",
"") "",
)
W.hook_command( W.hook_command(
# Command name and short description # Command name and short description
@ -224,12 +241,13 @@ def hook_commands():
"", "",
# Callback # Callback
"matrix_part_command_cb", "matrix_part_command_cb",
"") "",
)
W.hook_command_run('/buffer clear', 'matrix_command_buf_clear_cb', '') W.hook_command_run("/buffer clear", "matrix_command_buf_clear_cb", "")
# if OPTIONS.enable_backlog: # if OPTIONS.enable_backlog:
# hook_page_up() # hook_page_up()
@utf8_decode @utf8_decode
@ -238,8 +256,9 @@ def matrix_me_command_cb(data, buffer, args):
if buffer in server.buffers.values(): if buffer in server.buffers.values():
if not server.connected: if not server.connected:
message = ("{prefix}matrix: you are not connected to " message = (
"the server").format(prefix=W.prefix("error")) "{prefix}matrix: you are not connected to " "the server"
).format(prefix=W.prefix("error"))
W.prnt(server.server_buffer, message) W.prnt(server.server_buffer, message)
return W.WEECHAT_RC_ERROR return W.WEECHAT_RC_ERROR
@ -253,13 +272,16 @@ def matrix_me_command_cb(data, buffer, args):
server.room_send_message(room_buffer, formatted_data, "m.emote") server.room_send_message(room_buffer, formatted_data, "m.emote")
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
elif buffer == server.server_buffer: if buffer == server.server_buffer:
message = ("{prefix}matrix: command \"me\" must be " message = (
"executed on a Matrix channel buffer" '{prefix}matrix: command "me" must be '
).format(prefix=W.prefix("error")) "executed on a Matrix channel buffer"
).format(prefix=W.prefix("error"))
W.prnt("", message) W.prnt("", message)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
return W.WEECHAT_RC_OK
@utf8_decode @utf8_decode
def matrix_topic_command_cb(data, buffer, args): def matrix_topic_command_cb(data, buffer, args):
@ -269,8 +291,9 @@ def matrix_topic_command_cb(data, buffer, args):
for server in SERVERS.values(): for server in SERVERS.values():
if buffer == server.server_buffer: if buffer == server.server_buffer:
server.error("command \"topic\" must be " server.error(
"executed on a Matrix room buffer") 'command "topic" must be ' "executed on a Matrix room buffer"
)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
room = server.find_room_from_ptr(buffer) room = server.find_room_from_ptr(buffer)
@ -310,15 +333,17 @@ def matrix_fetch_old_messages(server, room_id):
def check_server_existence(server_name, servers): def check_server_existence(server_name, servers):
if server_name not in servers: if server_name not in servers:
message = "{prefix}matrix: No such server: {server}".format( message = "{prefix}matrix: No such server: {server}".format(
prefix=W.prefix("error"), server=server_name) prefix=W.prefix("error"), server=server_name
)
W.prnt("", message) W.prnt("", message)
return False return False
return True return True
def hook_page_up(): def hook_page_up():
OPTIONS.page_up_hook = W.hook_command_run('/window page_up', G.CONFIG.page_up_hook = W.hook_command_run(
'matrix_command_pgup_cb', '') "/window page_up", "matrix_command_pgup_cb", ""
)
@utf8_decode @utf8_decode
@ -345,7 +370,8 @@ def matrix_command_pgup_cb(data, buffer, command):
window = W.window_search_with_buffer(buffer) window = W.window_search_with_buffer(buffer)
first_line_displayed = bool( first_line_displayed = bool(
W.window_get_integer(window, "first_line_displayed")) W.window_get_integer(window, "first_line_displayed")
)
if first_line_displayed: if first_line_displayed:
room_id = key_from_value(server.buffers, buffer) room_id = key_from_value(server.buffers, buffer)
@ -382,9 +408,11 @@ def matrix_part_command_cb(data, buffer, args):
if not room_id: if not room_id:
if buffer == server.server_buffer: if buffer == server.server_buffer:
server.error("command \"part\" must be " server.error(
"executed on a Matrix room buffer or a room " 'command "part" must be '
"name needs to be given") "executed on a Matrix room buffer or a room "
"name needs to be given"
)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
room_buffer = server.find_room_from_ptr(buffer) room_buffer = server.find_room_from_ptr(buffer)
@ -404,8 +432,9 @@ def matrix_invite_command_cb(data, buffer, args):
for server in SERVERS.values(): for server in SERVERS.values():
if buffer == server.server_buffer: if buffer == server.server_buffer:
server.error("command \"invite\" must be " server.error(
"executed on a Matrix room buffer") 'command "invite" must be ' "executed on a Matrix room buffer"
)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
room = server.find_room_from_ptr(buffer) room = server.find_room_from_ptr(buffer)
@ -429,8 +458,9 @@ def matrix_kick_command_cb(data, buffer, args):
for server in SERVERS.values(): for server in SERVERS.values():
if buffer == server.server_buffer: if buffer == server.server_buffer:
server.error("command \"kick\" must be " server.error(
"executed on a Matrix room buffer") 'command "kick" must be ' "executed on a Matrix room buffer"
)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
room = server.find_room_from_ptr(buffer) room = server.find_room_from_ptr(buffer)
@ -449,22 +479,24 @@ def matrix_kick_command_cb(data, buffer, args):
def event_id_from_line(buf, target_number): def event_id_from_line(buf, target_number):
# type: (weechat.buffer, int) -> str # type: (weechat.buffer, int) -> str
own_lines = W.hdata_pointer(W.hdata_get('buffer'), buf, 'own_lines') own_lines = W.hdata_pointer(W.hdata_get("buffer"), buf, "own_lines")
if own_lines: if own_lines:
line = W.hdata_pointer(W.hdata_get('lines'), own_lines, 'last_line') line = W.hdata_pointer(W.hdata_get("lines"), own_lines, "last_line")
line_number = 1 line_number = 1
while line: while line:
line_data = W.hdata_pointer(W.hdata_get('line'), line, 'data') line_data = W.hdata_pointer(W.hdata_get("line"), line, "data")
if line_data: if line_data:
tags = tags_from_line_data(line_data) tags = tags_from_line_data(line_data)
# Only count non redacted user messages # Only count non redacted user messages
if ("matrix_message" in tags and if (
'matrix_redacted' not in tags and "matrix_message" in tags
"matrix_new_redacted" not in tags): and "matrix_redacted" not in tags
and "matrix_new_redacted" not in tags
):
if line_number == target_number: if line_number == target_number:
for tag in tags: for tag in tags:
@ -474,7 +506,7 @@ def event_id_from_line(buf, target_number):
line_number += 1 line_number += 1
line = W.hdata_move(W.hdata_get('line'), line, -1) line = W.hdata_move(W.hdata_get("line"), line, -1)
return "" return ""
@ -488,9 +520,10 @@ def matrix_redact_command_cb(data, buffer, args):
matches = re.match(r"(\d+)(:\".*\")? ?(.*)?", args) matches = re.match(r"(\d+)(:\".*\")? ?(.*)?", args)
if not matches: if not matches:
message = ("{prefix}matrix: Invalid command " message = (
"arguments (see /help redact)" "{prefix}matrix: Invalid command "
).format(prefix=W.prefix("error")) "arguments (see /help redact)"
).format(prefix=W.prefix("error"))
W.prnt("", message) W.prnt("", message)
return W.WEECHAT_RC_ERROR return W.WEECHAT_RC_ERROR
@ -500,9 +533,10 @@ def matrix_redact_command_cb(data, buffer, args):
event_id = event_id_from_line(buffer, line) event_id = event_id_from_line(buffer, line)
if not event_id: if not event_id:
message = ("{prefix}matrix: No such message with number " message = (
"{number} found").format( "{prefix}matrix: No such message with number "
prefix=W.prefix("error"), number=line) "{number} found"
).format(prefix=W.prefix("error"), number=line)
W.prnt("", message) W.prnt("", message)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@ -510,10 +544,11 @@ def matrix_redact_command_cb(data, buffer, args):
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
elif buffer == server.server_buffer: if buffer == server.server_buffer:
message = ("{prefix}matrix: command \"redact\" must be " message = (
"executed on a Matrix channel buffer" '{prefix}matrix: command "redact" must be '
).format(prefix=W.prefix("error")) "executed on a Matrix channel buffer"
).format(prefix=W.prefix("error"))
W.prnt("", message) W.prnt("", message)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@ -522,9 +557,10 @@ def matrix_redact_command_cb(data, buffer, args):
def matrix_command_help(args): def matrix_command_help(args):
if not args: if not args:
message = ("{prefix}matrix: Too few arguments for command " message = (
"\"/matrix help\" (see /matrix help help)" "{prefix}matrix: Too few arguments for command "
).format(prefix=W.prefix("error")) '"/matrix help" (see /matrix help help)'
).format(prefix=W.prefix("error"))
W.prnt("", message) W.prnt("", message)
return return
@ -532,88 +568,104 @@ def matrix_command_help(args):
message = "" message = ""
if command == "connect": if command == "connect":
message = ("{delimiter_color}[{ncolor}matrix{delimiter_color}] " message = (
"{ncolor}{cmd_color}/connect{ncolor} " "{delimiter_color}[{ncolor}matrix{delimiter_color}] "
"<server-name> [<server-name>...]" "{ncolor}{cmd_color}/connect{ncolor} "
"\n\n" "<server-name> [<server-name>...]"
"connect to Matrix server(s)" "\n\n"
"\n\n" "connect to Matrix server(s)"
"server-name: server to connect to" "\n\n"
"(internal name)").format( "server-name: server to connect to"
delimiter_color=W.color("chat_delimiters"), "(internal name)"
cmd_color=W.color("chat_buffer"), ).format(
ncolor=W.color("reset")) delimiter_color=W.color("chat_delimiters"),
cmd_color=W.color("chat_buffer"),
ncolor=W.color("reset"),
)
elif command == "disconnect": elif command == "disconnect":
message = ("{delimiter_color}[{ncolor}matrix{delimiter_color}] " message = (
"{ncolor}{cmd_color}/disconnect{ncolor} " "{delimiter_color}[{ncolor}matrix{delimiter_color}] "
"<server-name> [<server-name>...]" "{ncolor}{cmd_color}/disconnect{ncolor} "
"\n\n" "<server-name> [<server-name>...]"
"disconnect from Matrix server(s)" "\n\n"
"\n\n" "disconnect from Matrix server(s)"
"server-name: server to disconnect" "\n\n"
"(internal name)").format( "server-name: server to disconnect"
delimiter_color=W.color("chat_delimiters"), "(internal name)"
cmd_color=W.color("chat_buffer"), ).format(
ncolor=W.color("reset")) delimiter_color=W.color("chat_delimiters"),
cmd_color=W.color("chat_buffer"),
ncolor=W.color("reset"),
)
elif command == "reconnect": elif command == "reconnect":
message = ("{delimiter_color}[{ncolor}matrix{delimiter_color}] " message = (
"{ncolor}{cmd_color}/reconnect{ncolor} " "{delimiter_color}[{ncolor}matrix{delimiter_color}] "
"<server-name> [<server-name>...]" "{ncolor}{cmd_color}/reconnect{ncolor} "
"\n\n" "<server-name> [<server-name>...]"
"reconnect to Matrix server(s)" "\n\n"
"\n\n" "reconnect to Matrix server(s)"
"server-name: server to reconnect" "\n\n"
"(internal name)").format( "server-name: server to reconnect"
delimiter_color=W.color("chat_delimiters"), "(internal name)"
cmd_color=W.color("chat_buffer"), ).format(
ncolor=W.color("reset")) delimiter_color=W.color("chat_delimiters"),
cmd_color=W.color("chat_buffer"),
ncolor=W.color("reset"),
)
elif command == "server": elif command == "server":
message = ("{delimiter_color}[{ncolor}matrix{delimiter_color}] " message = (
"{ncolor}{cmd_color}/server{ncolor} " "{delimiter_color}[{ncolor}matrix{delimiter_color}] "
"add <server-name> <hostname>[:<port>]" "{ncolor}{cmd_color}/server{ncolor} "
"\n " "add <server-name> <hostname>[:<port>]"
"delete|list|listfull <server-name>" "\n "
"\n\n" "delete|list|listfull <server-name>"
"list, add, or remove Matrix servers" "\n\n"
"\n\n" "list, add, or remove Matrix servers"
" list: list servers (without argument, this " "\n\n"
"list is displayed)\n" " list: list servers (without argument, this "
" listfull: list servers with detailed info for each " "list is displayed)\n"
"server\n" " listfull: list servers with detailed info for each "
" add: add a new server\n" "server\n"
" delete: delete a server\n" " add: add a new server\n"
"server-name: server to reconnect (internal name)\n" " delete: delete a server\n"
" hostname: name or IP address of server\n" "server-name: server to reconnect (internal name)\n"
" port: port of server (default: 8448)\n" " hostname: name or IP address of server\n"
"\n" " port: port of server (default: 8448)\n"
"Examples:" "\n"
"\n /matrix server listfull" "Examples:"
"\n /matrix server add matrix matrix.org:80" "\n /matrix server listfull"
"\n /matrix server del matrix").format( "\n /matrix server add matrix matrix.org:80"
delimiter_color=W.color("chat_delimiters"), "\n /matrix server del matrix"
cmd_color=W.color("chat_buffer"), ).format(
ncolor=W.color("reset")) delimiter_color=W.color("chat_delimiters"),
cmd_color=W.color("chat_buffer"),
ncolor=W.color("reset"),
)
elif command == "help": elif command == "help":
message = ("{delimiter_color}[{ncolor}matrix{delimiter_color}] " message = (
"{ncolor}{cmd_color}/help{ncolor} " "{delimiter_color}[{ncolor}matrix{delimiter_color}] "
"<matrix-command> [<matrix-command>...]" "{ncolor}{cmd_color}/help{ncolor} "
"\n\n" "<matrix-command> [<matrix-command>...]"
"display help about Matrix commands" "\n\n"
"\n\n" "display help about Matrix commands"
"matrix-command: a Matrix command name" "\n\n"
"(internal name)").format( "matrix-command: a Matrix command name"
delimiter_color=W.color("chat_delimiters"), "(internal name)"
cmd_color=W.color("chat_buffer"), ).format(
ncolor=W.color("reset")) delimiter_color=W.color("chat_delimiters"),
cmd_color=W.color("chat_buffer"),
ncolor=W.color("reset"),
)
else: else:
message = ("{prefix}matrix: No help available, \"{command}\" " message = (
"is not a matrix command").format( '{prefix}matrix: No help available, "{command}" '
prefix=W.prefix("error"), command=command) "is not a matrix command"
).format(prefix=W.prefix("error"), command=command)
W.prnt("", "") W.prnt("", "")
W.prnt("", message) W.prnt("", message)
@ -622,7 +674,6 @@ def matrix_command_help(args):
def matrix_server_command_listfull(args): def matrix_server_command_listfull(args):
def get_value_string(value, default_value): def get_value_string(value, default_value):
if value == default_value: if value == default_value:
if not value: if not value:
@ -632,7 +683,8 @@ def matrix_server_command_listfull(args):
value_string = "{color}{value}{ncolor}".format( value_string = "{color}{value}{ncolor}".format(
color=W.color("chat_value"), color=W.color("chat_value"),
value=value, value=value,
ncolor=W.color("reset")) ncolor=W.color("reset"),
)
return value_string return value_string
@ -650,14 +702,17 @@ def matrix_server_command_listfull(args):
else: else:
connected = "not connected" connected = "not connected"
message = ("Server: {server_color}{server}{delimiter_color}" message = (
" [{ncolor}{connected}{delimiter_color}]" "Server: {server_color}{server}{delimiter_color}"
"{ncolor}").format( " [{ncolor}{connected}{delimiter_color}]"
server_color=W.color("chat_server"), "{ncolor}"
server=server.name, ).format(
delimiter_color=W.color("chat_delimiters"), server_color=W.color("chat_server"),
connected=connected, server=server.name,
ncolor=W.color("reset")) delimiter_color=W.color("chat_delimiters"),
connected=connected,
ncolor=W.color("reset"),
)
W.prnt("", message) W.prnt("", message)
@ -703,7 +758,7 @@ def matrix_server_command_listfull(args):
if value: if value:
value = "(hidden)" value = "(hidden)"
value_string = get_value_string(value, '') value_string = get_value_string(value, "")
message = " password . . : {value}".format(value=value_string) message = " password . . : {value}".format(value=value_string)
W.prnt("", message) W.prnt("", message)
@ -715,14 +770,17 @@ def matrix_server_command_delete(args):
server = SERVERS[server_name] server = SERVERS[server_name]
if server.connected: if server.connected:
message = ("{prefix}matrix: you can not delete server " message = (
"{color}{server}{ncolor} because you are " "{prefix}matrix: you can not delete server "
"connected to it. Try \"/matrix disconnect " "{color}{server}{ncolor} because you are "
"{color}{server}{ncolor}\" before.").format( 'connected to it. Try "/matrix disconnect '
prefix=W.prefix("error"), '{color}{server}{ncolor}" before.'
color=W.color("chat_server"), ).format(
ncolor=W.color("reset"), prefix=W.prefix("error"),
server=server.name) color=W.color("chat_server"),
ncolor=W.color("reset"),
server=server.name,
)
W.prnt("", message) W.prnt("", message)
return return
@ -735,11 +793,13 @@ def matrix_server_command_delete(args):
for option in server.config._option_ptrs.values(): for option in server.config._option_ptrs.values():
W.config_option_free(option) W.config_option_free(option)
message = ("matrix: server {color}{server}{ncolor} has been " message = (
"deleted").format( "matrix: server {color}{server}{ncolor} has been " "deleted"
server=server.name, ).format(
color=W.color("chat_server"), server=server.name,
ncolor=W.color("reset")) color=W.color("chat_server"),
ncolor=W.color("reset"),
)
del SERVERS[server.name] del SERVERS[server.name]
server = None server = None
@ -749,15 +809,17 @@ def matrix_server_command_delete(args):
def matrix_server_command_add(args): def matrix_server_command_add(args):
if len(args) < 2: if len(args) < 2:
message = ("{prefix}matrix: Too few arguments for command " message = (
"\"/matrix server add\" (see /matrix help server)").format( "{prefix}matrix: Too few arguments for command "
prefix=W.prefix("error")) '"/matrix server add" (see /matrix help server)'
).format(prefix=W.prefix("error"))
W.prnt("", message) W.prnt("", message)
return return
elif len(args) > 4: if len(args) > 4:
message = ("{prefix}matrix: Too many arguments for command " message = (
"\"/matrix server add\" (see /matrix help server)" "{prefix}matrix: Too many arguments for command "
).format(prefix=W.prefix("error")) '"/matrix server add" (see /matrix help server)'
).format(prefix=W.prefix("error"))
W.prnt("", message) W.prnt("", message)
return return
@ -769,16 +831,19 @@ def matrix_server_command_add(args):
server_name = args[0] server_name = args[0]
if server_name in SERVERS: if server_name in SERVERS:
message = ("{prefix}matrix: server {color}{server}{ncolor} " message = (
"already exists, can't add it").format( "{prefix}matrix: server {color}{server}{ncolor} "
prefix=W.prefix("error"), "already exists, can't add it"
color=W.color("chat_server"), ).format(
server=server_name, prefix=W.prefix("error"),
ncolor=W.color("reset")) color=W.color("chat_server"),
server=server_name,
ncolor=W.color("reset"),
)
W.prnt("", message) W.prnt("", message)
return return
server = MatrixServer(server_name, matrix.globals.CONFIG) server = MatrixServer(server_name, G.CONFIG._ptr)
SERVERS[server.name] = server SERVERS[server.name] = server
if len(args) >= 2: if len(args) >= 2:
@ -788,20 +853,21 @@ def matrix_server_command_add(args):
host, port = args[1], None host, port = args[1], None
return_code = W.config_option_set( return_code = W.config_option_set(
server.config._option_ptrs["address"], server.config._option_ptrs["address"], host, 1
host,
1
) )
if return_code == W.WEECHAT_CONFIG_OPTION_SET_ERROR: if return_code == W.WEECHAT_CONFIG_OPTION_SET_ERROR:
remove_server(server) remove_server(server)
message = ("{prefix}Failed to set address for server " message = (
"{color}{server}{ncolor}, failed to add " "{prefix}Failed to set address for server "
"server.").format( "{color}{server}{ncolor}, failed to add "
prefix=W.prefix("error"), "server."
color=W.color("chat_server"), ).format(
server=server.name, prefix=W.prefix("error"),
ncolor=W.color("reset")) color=W.color("chat_server"),
server=server.name,
ncolor=W.color("reset"),
)
W.prnt("", message) W.prnt("", message)
server = None server = None
@ -809,19 +875,20 @@ def matrix_server_command_add(args):
if port: if port:
return_code = W.config_option_set( return_code = W.config_option_set(
server.config._option_ptrs["port"], server.config._option_ptrs["port"], port, 1
port,
1
) )
if return_code == W.WEECHAT_CONFIG_OPTION_SET_ERROR: if return_code == W.WEECHAT_CONFIG_OPTION_SET_ERROR:
remove_server(server) remove_server(server)
message = ("{prefix}Failed to set port for server " message = (
"{color}{server}{ncolor}, failed to add " "{prefix}Failed to set port for server "
"server.").format( "{color}{server}{ncolor}, failed to add "
prefix=W.prefix("error"), "server."
color=W.color("chat_server"), ).format(
server=server.name, prefix=W.prefix("error"),
ncolor=W.color("reset")) color=W.color("chat_server"),
server=server.name,
ncolor=W.color("reset"),
)
W.prnt("", message) W.prnt("", message)
server = None server = None
@ -830,20 +897,21 @@ def matrix_server_command_add(args):
if len(args) >= 3: if len(args) >= 3:
user = args[2] user = args[2]
return_code = W.config_option_set( return_code = W.config_option_set(
server.config._option_ptrs["username"], server.config._option_ptrs["username"], user, 1
user,
1
) )
if return_code == W.WEECHAT_CONFIG_OPTION_SET_ERROR: if return_code == W.WEECHAT_CONFIG_OPTION_SET_ERROR:
remove_server(server) remove_server(server)
message = ("{prefix}Failed to set user for server " message = (
"{color}{server}{ncolor}, failed to add " "{prefix}Failed to set user for server "
"server.").format( "{color}{server}{ncolor}, failed to add "
prefix=W.prefix("error"), "server."
color=W.color("chat_server"), ).format(
server=server.name, prefix=W.prefix("error"),
ncolor=W.color("reset")) color=W.color("chat_server"),
server=server.name,
ncolor=W.color("reset"),
)
W.prnt("", message) W.prnt("", message)
server = None server = None
@ -853,28 +921,31 @@ def matrix_server_command_add(args):
password = args[3] password = args[3]
return_code = W.config_option_set( return_code = W.config_option_set(
server.config._option_ptrs["password"], server.config._option_ptrs["password"], password, 1
password,
1
) )
if return_code == W.WEECHAT_CONFIG_OPTION_SET_ERROR: if return_code == W.WEECHAT_CONFIG_OPTION_SET_ERROR:
remove_server(server) remove_server(server)
message = ("{prefix}Failed to set password for server " message = (
"{color}{server}{ncolor}, failed to add " "{prefix}Failed to set password for server "
"server.").format( "{color}{server}{ncolor}, failed to add "
prefix=W.prefix("error"), "server."
color=W.color("chat_server"), ).format(
server=server.name, prefix=W.prefix("error"),
ncolor=W.color("reset")) color=W.color("chat_server"),
server=server.name,
ncolor=W.color("reset"),
)
W.prnt("", message) W.prnt("", message)
server = None server = None
return return
message = ("matrix: server {color}{server}{ncolor} " message = (
"has been added").format( "matrix: server {color}{server}{ncolor} " "has been added"
server=server.name, ).format(
color=W.color("chat_server"), server=server.name,
ncolor=W.color("reset")) color=W.color("chat_server"),
ncolor=W.color("reset"),
)
W.prnt("", message) W.prnt("", message)
@ -883,23 +954,28 @@ def matrix_server_command(command, args):
if SERVERS: if SERVERS:
W.prnt("", "\nAll matrix servers:") W.prnt("", "\nAll matrix servers:")
for server in SERVERS: for server in SERVERS:
W.prnt("", " {color}{server}".format( W.prnt(
color=W.color("chat_server"), server=server)) "",
" {color}{server}".format(
color=W.color("chat_server"), server=server
),
)
# TODO the argument for list and listfull is used as a match word to # TODO the argument for list and listfull is used as a match word to
# find/filter servers, we're currently match exactly to the whole name # find/filter servers, we're currently match exactly to the whole name
if command == 'list': if command == "list":
list_servers(args) list_servers(args)
elif command == 'listfull': elif command == "listfull":
matrix_server_command_listfull(args) matrix_server_command_listfull(args)
elif command == 'add': elif command == "add":
matrix_server_command_add(args) matrix_server_command_add(args)
elif command == 'delete': elif command == "delete":
matrix_server_command_delete(args) matrix_server_command_delete(args)
else: else:
message = ("{prefix}matrix: Error: unknown matrix server command, " message = (
"\"{command}\" (type /matrix help server for help)").format( "{prefix}matrix: Error: unknown matrix server command, "
prefix=W.prefix("error"), command=command) '"{command}" (type /matrix help server for help)'
).format(prefix=W.prefix("error"), command=command)
W.prnt("", message) W.prnt("", message)
@ -921,41 +997,44 @@ def matrix_command_cb(data, buffer, args):
server.access_token = "" server.access_token = ""
server.disconnect(reconnect=False) server.disconnect(reconnect=False)
split_args = list(filter(bool, args.split(' '))) split_args = list(filter(bool, args.split(" ")))
if len(split_args) < 1: if len(split_args) < 1:
message = ("{prefix}matrix: Too few arguments for command " message = (
"\"/matrix\" " "{prefix}matrix: Too few arguments for command "
"(see /help matrix)").format(prefix=W.prefix("error")) '"/matrix" '
"(see /help matrix)"
).format(prefix=W.prefix("error"))
W.prnt("", message) W.prnt("", message)
return W.WEECHAT_RC_ERROR return W.WEECHAT_RC_ERROR
command, args = split_args[0], split_args[1:] command, args = split_args[0], split_args[1:]
if command == 'connect': if command == "connect":
connect_server(args) connect_server(args)
elif command == 'disconnect': elif command == "disconnect":
disconnect_server(args) disconnect_server(args)
elif command == 'reconnect': elif command == "reconnect":
disconnect_server(args) disconnect_server(args)
connect_server(args) connect_server(args)
elif command == 'server': elif command == "server":
if len(args) >= 1: if len(args) >= 1:
subcommand, args = args[0], args[1:] subcommand, args = args[0], args[1:]
matrix_server_command(subcommand, args) matrix_server_command(subcommand, args)
else: else:
matrix_server_command("list", "") matrix_server_command("list", "")
elif command == 'help': elif command == "help":
matrix_command_help(args) matrix_command_help(args)
else: else:
message = ("{prefix}matrix: Error: unknown matrix command, " message = (
"\"{command}\" (type /help matrix for help)").format( "{prefix}matrix: Error: unknown matrix command, "
prefix=W.prefix("error"), command=command) '"{command}" (type /help matrix for help)'
).format(prefix=W.prefix("error"), command=command)
W.prnt("", message) W.prnt("", message)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK

View file

@ -16,29 +16,32 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from matrix.globals import SERVERS, W
from matrix.utf import utf8_decode from matrix.utf import utf8_decode
from matrix.globals import W, SERVERS
from matrix.utils import tags_from_line_data from matrix.utils import tags_from_line_data
def add_servers_to_completion(completion): def add_servers_to_completion(completion):
for server_name in SERVERS: for server_name in SERVERS:
W.hook_completion_list_add(completion, server_name, 0, W.hook_completion_list_add(
W.WEECHAT_LIST_POS_SORT) completion, server_name, 0, W.WEECHAT_LIST_POS_SORT
)
@utf8_decode @utf8_decode
def matrix_server_command_completion_cb(data, completion_item, buffer, def matrix_server_command_completion_cb(
completion): data, completion_item, buffer, completion
):
buffer_input = W.buffer_get_string(buffer, "input").split() buffer_input = W.buffer_get_string(buffer, "input").split()
args = buffer_input[1:] args = buffer_input[1:]
commands = ['add', 'delete', 'list', 'listfull'] commands = ["add", "delete", "list", "listfull"]
def complete_commands(): def complete_commands():
for command in commands: for command in commands:
W.hook_completion_list_add(completion, command, 0, W.hook_completion_list_add(
W.WEECHAT_LIST_POS_SORT) completion, command, 0, W.WEECHAT_LIST_POS_SORT
)
if len(args) == 1: if len(args) == 1:
complete_commands() complete_commands()
@ -47,11 +50,11 @@ def matrix_server_command_completion_cb(data, completion_item, buffer,
if args[1] not in commands: if args[1] not in commands:
complete_commands() complete_commands()
else: else:
if args[1] == 'delete' or args[1] == 'listfull': if args[1] == "delete" or args[1] == "listfull":
add_servers_to_completion(completion) add_servers_to_completion(completion)
elif len(args) == 3: elif len(args) == 3:
if args[1] == 'delete' or args[1] == 'listfull': if args[1] == "delete" or args[1] == "listfull":
if args[2] not in SERVERS: if args[2] not in SERVERS:
add_servers_to_completion(completion) add_servers_to_completion(completion)
@ -67,18 +70,25 @@ def matrix_server_completion_cb(data, completion_item, buffer, completion):
@utf8_decode @utf8_decode
def matrix_command_completion_cb(data, completion_item, buffer, completion): def matrix_command_completion_cb(data, completion_item, buffer, completion):
for command in [ for command in [
"connect", "disconnect", "reconnect", "server", "help", "debug" "connect",
"disconnect",
"reconnect",
"server",
"help",
"debug",
]: ]:
W.hook_completion_list_add(completion, command, 0, W.hook_completion_list_add(
W.WEECHAT_LIST_POS_SORT) completion, command, 0, W.WEECHAT_LIST_POS_SORT
)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@utf8_decode @utf8_decode
def matrix_debug_completion_cb(data, completion_item, buffer, completion): def matrix_debug_completion_cb(data, completion_item, buffer, completion):
for debug_type in ["messaging", "network", "timing"]: for debug_type in ["messaging", "network", "timing"]:
W.hook_completion_list_add(completion, debug_type, 0, W.hook_completion_list_add(
W.WEECHAT_LIST_POS_SORT) completion, debug_type, 0, W.WEECHAT_LIST_POS_SORT
)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@ -88,46 +98,52 @@ REDACTION_COMP_LEN = 50
@utf8_decode @utf8_decode
def matrix_message_completion_cb(data, completion_item, buffer, completion): def matrix_message_completion_cb(data, completion_item, buffer, completion):
own_lines = W.hdata_pointer(W.hdata_get('buffer'), buffer, 'own_lines') own_lines = W.hdata_pointer(W.hdata_get("buffer"), buffer, "own_lines")
if own_lines: if own_lines:
line = W.hdata_pointer(W.hdata_get('lines'), own_lines, 'last_line') line = W.hdata_pointer(W.hdata_get("lines"), own_lines, "last_line")
line_number = 1 line_number = 1
while line: while line:
line_data = W.hdata_pointer(W.hdata_get('line'), line, 'data') line_data = W.hdata_pointer(W.hdata_get("line"), line, "data")
if line_data: if line_data:
message = W.hdata_string( message = W.hdata_string(
W.hdata_get('line_data'), line_data, 'message') W.hdata_get("line_data"), line_data, "message"
)
tags = tags_from_line_data(line_data) tags = tags_from_line_data(line_data)
# Only add non redacted user messages to the completion # Only add non redacted user messages to the completion
if (message and 'matrix_message' in tags and if (
'matrix_redacted' not in tags): message
and "matrix_message" in tags
and "matrix_redacted" not in tags
):
if len(message) > REDACTION_COMP_LEN + 2: if len(message) > REDACTION_COMP_LEN + 2:
message = (message[:REDACTION_COMP_LEN] + '..') message = message[:REDACTION_COMP_LEN] + ".."
item = ("{number}:\"{message}\"").format( item = ('{number}:"{message}"').format(
number=line_number, message=message) number=line_number, message=message
)
W.hook_completion_list_add(completion, item, 0, W.hook_completion_list_add(
W.WEECHAT_LIST_POS_END) completion, item, 0, W.WEECHAT_LIST_POS_END
)
line_number += 1 line_number += 1
line = W.hdata_move(W.hdata_get('line'), line, -1) line = W.hdata_move(W.hdata_get("line"), line, -1)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
def server_from_buffer(buffer): def server_from_buffer(buffer):
for server in SERVERS.values(): for server in SERVERS.values():
if buffer in server.buffers.values(): if buffer in server.buffers.values():
return server return server
elif buffer == server.server_buffer: if buffer == server.server_buffer:
return server return server
return None return None
@ -141,8 +157,9 @@ def matrix_olm_user_completion_cb(data, completion_item, buffer, completion):
olm = server.olm olm = server.olm
for user in olm.device_keys: for user in olm.device_keys:
W.hook_completion_list_add(completion, user, 0, W.hook_completion_list_add(
W.WEECHAT_LIST_POS_SORT) completion, user, 0, W.WEECHAT_LIST_POS_SORT
)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@ -169,8 +186,9 @@ def matrix_olm_device_completion_cb(data, completion_item, buffer, completion):
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
for device in olm.device_keys[user]: for device in olm.device_keys[user]:
W.hook_completion_list_add(completion, device.device_id, 0, W.hook_completion_list_add(
W.WEECHAT_LIST_POS_SORT) completion, device.device_id, 0, W.WEECHAT_LIST_POS_SORT
)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@ -178,8 +196,9 @@ def matrix_olm_device_completion_cb(data, completion_item, buffer, completion):
@utf8_decode @utf8_decode
def matrix_user_completion_cb(data, completion_item, buffer, completion): def matrix_user_completion_cb(data, completion_item, buffer, completion):
def add_user(completion, user): def add_user(completion, user):
W.hook_completion_list_add(completion, user, 0, W.hook_completion_list_add(
W.WEECHAT_LIST_POS_SORT) completion, user, 0, W.WEECHAT_LIST_POS_SORT
)
for server in SERVERS.values(): for server in SERVERS.values():
if buffer == server.server_buffer: if buffer == server.server_buffer:
@ -201,26 +220,58 @@ def matrix_user_completion_cb(data, completion_item, buffer, completion):
def init_completion(): def init_completion():
W.hook_completion("matrix_server_commands", "Matrix server completion", W.hook_completion(
"matrix_server_command_completion_cb", "") "matrix_server_commands",
"Matrix server completion",
"matrix_server_command_completion_cb",
"",
)
W.hook_completion("matrix_servers", "Matrix server completion", W.hook_completion(
"matrix_server_completion_cb", "") "matrix_servers",
"Matrix server completion",
"matrix_server_completion_cb",
"",
)
W.hook_completion("matrix_commands", "Matrix command completion", W.hook_completion(
"matrix_command_completion_cb", "") "matrix_commands",
"Matrix command completion",
"matrix_command_completion_cb",
"",
)
W.hook_completion("matrix_messages", "Matrix message completion", W.hook_completion(
"matrix_message_completion_cb", "") "matrix_messages",
"Matrix message completion",
"matrix_message_completion_cb",
"",
)
W.hook_completion("matrix_debug_types", "Matrix debugging type completion", W.hook_completion(
"matrix_debug_completion_cb", "") "matrix_debug_types",
"Matrix debugging type completion",
"matrix_debug_completion_cb",
"",
)
W.hook_completion("olm_user_ids", "Matrix olm user id completion", W.hook_completion(
"matrix_olm_user_completion_cb", "") "olm_user_ids",
"Matrix olm user id completion",
"matrix_olm_user_completion_cb",
"",
)
W.hook_completion("olm_devices", "Matrix olm device id completion", W.hook_completion(
"matrix_olm_device_completion_cb", "") "olm_devices",
"Matrix olm device id completion",
"matrix_olm_device_completion_cb",
"",
)
W.hook_completion("matrix_users", "Matrix user id completion", W.hook_completion(
"matrix_user_completion_cb", "") "matrix_users",
"Matrix user id completion",
"matrix_user_completion_cb",
"",
)

View file

@ -16,16 +16,16 @@
# from __future__ import unicode_literals # from __future__ import unicode_literals
from builtins import super from builtins import super
from collections import namedtuple
from enum import Enum, unique
import nio
import logbook import logbook
import nio
from . import globals as G from matrix.globals import SCRIPT_NAME, SERVERS, W
from matrix.globals import W, SERVERS, SCRIPT_NAME
from matrix.utf import utf8_decode from matrix.utf import utf8_decode
from enum import Enum, unique from . import globals as G
from collections import namedtuple
@unique @unique
@ -49,20 +49,22 @@ class DebugType(Enum):
TIMING = 2 TIMING = 2
class Option(namedtuple( class Option(
'Option', namedtuple(
[ "Option",
'name', [
'type', "name",
'string_values', "type",
'min', "string_values",
'max', "min",
'value', "max",
'description', "value",
'cast_func', "description",
'change_callback' "cast_func",
] "change_callback",
)): ],
)
):
__slots__ = () __slots__ = ()
def __new__( def __new__(
@ -75,7 +77,7 @@ class Option(namedtuple(
value, value,
description, description,
cast=None, cast=None,
change_callback=None change_callback=None,
): ):
return super().__new__( return super().__new__(
cls, cls,
@ -87,12 +89,10 @@ class Option(namedtuple(
value, value,
description, description,
cast, cast,
change_callback change_callback,
) )
@utf8_decode @utf8_decode
def matrix_config_reload_cb(data, config_file): def matrix_config_reload_cb(data, config_file):
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@ -121,8 +121,7 @@ def config_server_buffer_cb(data, option):
@utf8_decode @utf8_decode
def config_log_level_cb(data, option): def config_log_level_cb(data, option):
change_log_level( change_log_level(
G.CONFIG.network.debug_category, G.CONFIG.network.debug_category, G.CONFIG.network.debug_level
G.CONFIG.network.debug_level
) )
return 1 return 1
@ -132,8 +131,7 @@ def config_log_category_cb(data, option):
change_log_level(G.CONFIG.debug_category, logbook.ERROR) change_log_level(G.CONFIG.debug_category, logbook.ERROR)
G.CONFIG.debug_category = G.CONFIG.network.debug_category G.CONFIG.debug_category = G.CONFIG.network.debug_category
change_log_level( change_log_level(
G.CONFIG.network.debug_category, G.CONFIG.network.debug_category, G.CONFIG.network.debug_level
G.CONFIG.network.debug_level
) )
return 1 return 1
@ -141,11 +139,11 @@ def config_log_category_cb(data, option):
def level_to_logbook(value): def level_to_logbook(value):
if value == 0: if value == 0:
return logbook.ERROR return logbook.ERROR
elif value == 1: if value == 1:
return logbook.WARNING return logbook.WARNING
elif value == 2: if value == 2:
return logbook.INFO return logbook.INFO
elif value == 3: if value == 3:
return logbook.DEBUG return logbook.DEBUG
return logbook.ERROR return logbook.ERROR
@ -154,13 +152,13 @@ def level_to_logbook(value):
def logbook_category(value): def logbook_category(value):
if value == 0: if value == 0:
return "all" return "all"
elif value == 1: if value == 1:
return "http" return "http"
elif value == 2: if value == 2:
return "client" return "client"
elif value == 3: if value == 3:
return "events" return "events"
elif value == 4: if value == 4:
return "responses" return "responses"
return "all" return "all"
@ -169,9 +167,7 @@ def logbook_category(value):
class WeechatConfig(object): class WeechatConfig(object):
def __init__(self, sections): def __init__(self, sections):
self._ptr = W.config_new( self._ptr = W.config_new(
SCRIPT_NAME, SCRIPT_NAME, SCRIPT_NAME + "_config_reload_cb", ""
SCRIPT_NAME + "_config_reload_cb",
""
) )
for section in sections: for section in sections:
@ -180,8 +176,11 @@ class WeechatConfig(object):
setattr(self, name, section_class(name, self._ptr, options)) setattr(self, name, section_class(name, self._ptr, options))
def free(self): def free(self):
for section in [getattr(self, a) for a in dir(self) if for section in [
isinstance(getattr(self, a), ConfigSection)]: getattr(self, a)
for a in dir(self)
if isinstance(getattr(self, a), ConfigSection)
]:
section.free() section.free()
W.config_free(self._ptr) W.config_free(self._ptr)
@ -190,9 +189,9 @@ class WeechatConfig(object):
return_code = W.config_read(self._ptr) return_code = W.config_read(self._ptr)
if return_code == W.WEECHAT_CONFIG_READ_OK: if return_code == W.WEECHAT_CONFIG_READ_OK:
return True return True
elif return_code == W.WEECHAT_CONFIG_READ_MEMORY_ERROR: if return_code == W.WEECHAT_CONFIG_READ_MEMORY_ERROR:
return False return False
elif return_code == W.WEECHAT_CONFIG_READ_FILE_NOT_FOUND: if return_code == W.WEECHAT_CONFIG_READ_FILE_NOT_FOUND:
return True return True
return False return False
@ -201,8 +200,9 @@ class ConfigSection(object):
@classmethod @classmethod
def build(cls, name, options): def build(cls, name, options):
def constructor(self, name, config_ptr, options): def constructor(self, name, config_ptr, options):
self._ptr = W.config_new_section(config_ptr, name, 0, 0, "", "", self._ptr = W.config_new_section(
"", "", "", "", "", "", "", "") config_ptr, name, 0, 0, "", "", "", "", "", "", "", "", "", ""
)
self._config_ptr = config_ptr self._config_ptr = config_ptr
self._option_ptrs = {} self._option_ptrs = {}
@ -211,18 +211,13 @@ class ConfigSection(object):
attributes = { attributes = {
option.name: cls.option_property( option.name: cls.option_property(
option.name, option.name, option.type, cast_func=option.cast_func
option.type, )
cast_func=option.cast_func for option in options
) for option in options
} }
attributes["__init__"] = constructor attributes["__init__"] = constructor
section_class = type( section_class = type(name.title() + "Section", (cls,), attributes)
name.title() + "Section",
(cls,),
attributes
)
return section_class return section_class
def free(self): def free(self):
@ -232,10 +227,24 @@ class ConfigSection(object):
def _add_option(self, option): def _add_option(self, option):
cb = option.change_callback.__name__ if option.change_callback else "" cb = option.change_callback.__name__ if option.change_callback else ""
option_ptr = W.config_new_option( option_ptr = W.config_new_option(
self._config_ptr, self._ptr, option.name, option.type, self._config_ptr,
option.description, option.string_values, option.min, self._ptr,
option.max, option.value, option.value, 0, "", "", option.name,
cb, "", "", "") option.type,
option.description,
option.string_values,
option.min,
option.max,
option.value,
option.value,
0,
"",
"",
cb,
"",
"",
"",
)
self._option_ptrs[option.name] = option_ptr self._option_ptrs[option.name] = option_ptr
@ -249,25 +258,21 @@ class ConfigSection(object):
def str_evaluate_getter(self): def str_evaluate_getter(self):
return W.string_eval_expression( return W.string_eval_expression(
W.config_string(self._option_ptrs[name]), W.config_string(self._option_ptrs[name]), {}, {}, {}
{},
{},
{}
) )
def int_getter(self): def int_getter(self):
if cast_func: if cast_func:
return cast_func(W.config_integer(self._option_ptrs[name])) return cast_func(W.config_integer(self._option_ptrs[name]))
else: return W.config_integer(self._option_ptrs[name])
return W.config_integer(self._option_ptrs[name])
if option_type == "string" or option_type == "color": if option_type in ("string", "color"):
if evaluate: if evaluate:
return property(str_evaluate_getter) return property(str_evaluate_getter)
return property(str_getter) return property(str_getter)
elif option_type == "boolean": if option_type == "boolean":
return property(bool_getter) return property(bool_getter)
elif option_type == "integer": if option_type == "integer":
return property(int_getter) return property(int_getter)
@ -276,45 +281,111 @@ class MatrixConfig(WeechatConfig):
self.debug_buffer = "" self.debug_buffer = ""
self.debug_category = "all" self.debug_category = "all"
self.page_up_hook = None
look_options = [ look_options = [
Option("redactions", "integer", "strikethrough|notice|delete", 0, Option(
0, "strikethrough", "redactions",
("Only notice redactions, strike through or delete " "integer",
"redacted messages"), RedactType), "strikethrough|notice|delete",
Option("server_buffer", "integer", 0,
"merge_with_core|merge_without_core|independent", 0, 0, 0,
"merge_with_core", "Merge server buffers", ServerBufferType, "strikethrough",
config_server_buffer_cb) (
"Only notice redactions, strike through or delete "
"redacted messages"
),
RedactType,
),
Option(
"server_buffer",
"integer",
"merge_with_core|merge_without_core|independent",
0,
0,
"merge_with_core",
"Merge server buffers",
ServerBufferType,
config_server_buffer_cb,
),
] ]
network_options = [ network_options = [
Option("max_initial_sync_events", "integer", "", 1, 10000, "30", Option(
("How many events to fetch during the initial sync")), "max_initial_sync_events",
Option("max_backlog_sync_events", "integer", "", 1, 100, "10", "integer",
("How many events to fetch during backlog fetching")), "",
Option("fetch_backlog_on_pgup", "boolean", "", 0, 0, "on", 1,
("Fetch messages in the backlog on a window page up event") 10000,
), "30",
Option("debug_level", "integer", "error|warn|info|debug", 0, 0, ("How many events to fetch during the initial sync"),
"error", "Enable network protocol debugging.", ),
level_to_logbook, config_log_level_cb), Option(
Option("debug_category", "integer", "max_backlog_sync_events",
"all|http|client|events|responses", "integer",
0, 0, "all", "Debugging category", logbook_category), "",
Option("debug_buffer", "boolean", "", 0, 0, "off", 1,
("Use a separate buffer for debug logs.")), 100,
"10",
("How many events to fetch during backlog fetching"),
),
Option(
"fetch_backlog_on_pgup",
"boolean",
"",
0,
0,
"on",
("Fetch messages in the backlog on a window page up event"),
),
Option(
"debug_level",
"integer",
"error|warn|info|debug",
0,
0,
"error",
"Enable network protocol debugging.",
level_to_logbook,
config_log_level_cb,
),
Option(
"debug_category",
"integer",
"all|http|client|events|responses",
0,
0,
"all",
"Debugging category",
logbook_category,
),
Option(
"debug_buffer",
"boolean",
"",
0,
0,
"off",
("Use a separate buffer for debug logs."),
),
] ]
color_options = [ color_options = [
Option("quote", "color", "", 0, 0, "lightgreen", Option(
("Color for matrix style blockquotes")) "quote",
"color",
"",
0,
0,
"lightgreen",
("Color for matrix style blockquotes"),
)
] ]
sections = [ sections = [
("network", network_options), ("network", network_options),
("look", look_options), ("look", look_options),
("color", color_options) ("color", color_options),
] ]
super().__init__(sections) super().__init__(sections)
@ -322,10 +393,23 @@ class MatrixConfig(WeechatConfig):
# The server section is essentially a section with subsections and no # The server section is essentially a section with subsections and no
# options, handle that case independently. # options, handle that case independently.
W.config_new_section( W.config_new_section(
self._ptr, "server", 0, 0, "matrix_config_server_read_cb", "", self._ptr,
"matrix_config_server_write_cb", "", "", "", "", "", "", "") "server",
0,
0,
"matrix_config_server_read_cb",
"",
"matrix_config_server_write_cb",
"",
"",
"",
"",
"",
"",
"",
)
def free(self): def free(self):
section_ptr = W.config_search_section(self._ptr, 'server') section_ptr = W.config_search_section(self._ptr, "server")
W.config_section_free(section_ptr) W.config_section_free(section_ptr)
super().free() super().free()

View file

@ -18,13 +18,19 @@ from __future__ import unicode_literals
import sys import sys
from matrix.utf import WeechatWrapper from .utf import WeechatWrapper
if False:
from typing import Dict
try: try:
import weechat import weechat
W = weechat if sys.hexversion >= 0x3000000 else WeechatWrapper(weechat) W = weechat if sys.hexversion >= 0x3000000 else WeechatWrapper(weechat)
except ImportError: except ImportError:
import matrix._weechat as weechat import matrix._weechat as weechat
W = weechat W = weechat
SERVERS = dict() # type: Dict[str, MatrixServer] SERVERS = dict() # type: Dict[str, MatrixServer]

View file

@ -17,30 +17,28 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import os import os
import ssl
import socket
import time
import pprint import pprint
import socket
from collections import deque, defaultdict import ssl
import time
from collections import defaultdict, deque
from nio import ( from nio import (
HttpClient, HttpClient,
LocalProtocolError,
LoginResponse, LoginResponse,
SyncRepsponse,
RoomSendResponse, RoomSendResponse,
SyncRepsponse,
TransportResponse, TransportResponse,
TransportType, TransportType,
LocalProtocolError
) )
from matrix.utils import (key_from_value, server_buffer_prnt,
create_server_buffer)
from matrix.utf import utf8_decode
from . import globals as G from . import globals as G
from matrix.globals import W, SERVERS, SCRIPT_NAME from .buffer import OwnAction, OwnMessage, RoomBuffer
from .buffer import RoomBuffer, OwnMessage, OwnAction from .config import ConfigSection, Option, ServerBufferType
from .config import Option, ServerBufferType, ConfigSection from .globals import SCRIPT_NAME, SERVERS, W
from .utf import utf8_decode
from .utils import create_server_buffer, key_from_value, server_buffer_prnt
try: try:
FileNotFoundError FileNotFoundError
@ -56,39 +54,101 @@ class ServerConfig(ConfigSection):
self._option_ptrs = {} self._option_ptrs = {}
options = [ options = [
Option('autoconnect', 'boolean', '', 0, 0, 'off',
("automatically connect to the matrix server when weechat "
"is starting")),
Option('address', 'string', '', 0, 0, '',
"Hostname or IP address for the server"),
Option('port', 'integer', '', 0, 65535, '443',
"Port for the server"),
Option('proxy', 'string', '', 0, 0, '',
("Name of weechat proxy to use (see /help proxy)")),
Option('ssl_verify', 'boolean', '', 0, 0, 'on',
("Check that the SSL connection is fully trusted")),
Option('username', 'string', '', 0, 0, '',
"Username to use on server"),
Option( Option(
'password', 'string', '', 0, 0, '', "autoconnect",
("Password for server (note: content is evaluated, see /help " "boolean",
"eval)")), "",
Option('device_name', 'string', '', 0, 0, 'Weechat Matrix', 0,
"Device name to use while logging in to the matrix server"), 0,
"off",
(
"automatically connect to the matrix server when weechat "
"is starting"
),
),
Option(
"address",
"string",
"",
0,
0,
"",
"Hostname or IP address for the server",
),
Option(
"port", "integer", "", 0, 65535, "443", "Port for the server"
),
Option(
"proxy",
"string",
"",
0,
0,
"",
("Name of weechat proxy to use (see /help proxy)"),
),
Option(
"ssl_verify",
"boolean",
"",
0,
0,
"on",
("Check that the SSL connection is fully trusted"),
),
Option(
"username", "string", "", 0, 0, "", "Username to use on server"
),
Option(
"password",
"string",
"",
0,
0,
"",
(
"Password for server (note: content is evaluated, see "
"/help eval)"
),
),
Option(
"device_name",
"string",
"",
0,
0,
"Weechat Matrix",
"Device name to use while logging in to the matrix server",
),
] ]
section = W.config_search_section(config_ptr, 'server') section = W.config_search_section(config_ptr, "server")
self._ptr = section self._ptr = section
for option in options: for option in options:
option_name = "{server}.{option}".format( option_name = "{server}.{option}".format(
server=self._server_name, option=option.name) server=self._server_name, option=option.name
)
self._option_ptrs[option.name] = W.config_new_option( self._option_ptrs[option.name] = W.config_new_option(
config_ptr, section, option_name, option.type, config_ptr,
option.description, option.string_values, option.min, section,
option.max, option.value, option.value, 0, "", "", option_name,
"matrix_config_server_change_cb", self._server_name, "", "") option.type,
option.description,
option.string_values,
option.min,
option.max,
option.value,
option.value,
0,
"",
"",
"matrix_config_server_change_cb",
self._server_name,
"",
"",
)
autoconnect = ConfigSection.option_property("autoconnect", "boolean") autoconnect = ConfigSection.option_property("autoconnect", "boolean")
address = ConfigSection.option_property("address", "string") address = ConfigSection.option_property("address", "string")
@ -98,9 +158,7 @@ class ServerConfig(ConfigSection):
username = ConfigSection.option_property("username", "string") username = ConfigSection.option_property("username", "string")
device_name = ConfigSection.option_property("device_name", "string") device_name = ConfigSection.option_property("device_name", "string")
password = ConfigSection.option_property( password = ConfigSection.option_property(
"password", "password", "string", evaluate=True
"string",
evaluate=True
) )
def free(self): def free(self):
@ -169,12 +227,13 @@ class MatrixServer(object):
def _create_session_dir(self): def _create_session_dir(self):
path = os.path.join("matrix", self.name) path = os.path.join("matrix", self.name)
if not W.mkdir_home(path, 0o700): if not W.mkdir_home(path, 0o700):
message = ("{prefix}matrix: Error creating server session " message = (
"directory").format(prefix=W.prefix("error")) "{prefix}matrix: Error creating server session " "directory"
).format(prefix=W.prefix("error"))
W.prnt("", message) W.prnt("", message)
def get_session_path(self): def get_session_path(self):
home_dir = W.info_get('weechat_dir', '') home_dir = W.info_get("weechat_dir", "")
return os.path.join(home_dir, "matrix", self.name) return os.path.join(home_dir, "matrix", self.name)
def _load_device_id(self): def _load_device_id(self):
@ -184,8 +243,8 @@ class MatrixServer(object):
if not os.path.isfile(path): if not os.path.isfile(path):
return return
with open(path, 'r') as f: with open(path, "r") as device_file:
device_id = f.readline().rstrip() device_id = device_file.readline().rstrip()
if device_id: if device_id:
self.device_id = device_id self.device_id = device_id
@ -193,11 +252,11 @@ class MatrixServer(object):
file_name = "{}{}".format(self.config.username, ".device_id") file_name = "{}{}".format(self.config.username, ".device_id")
path = os.path.join(self.get_session_path(), file_name) path = os.path.join(self.get_session_path(), file_name)
with open(path, 'w') as f: with open(path, "w") as device_file:
f.write(self.device_id) device_file.write(self.device_id)
def _change_client(self): def _change_client(self):
host = ':'.join([self.config.address, str(self.config.port)]) host = ":".join([self.config.address, str(self.config.port)])
self.client = HttpClient(host, self.config.username, self.device_id) self.client = HttpClient(host, self.config.username, self.device_id)
def update_option(self, option, option_name): def update_option(self, option, option_name):
@ -255,14 +314,17 @@ class MatrixServer(object):
strerr = error.strerror if error.strerror else "Unknown reason" strerr = error.strerror if error.strerror else "Unknown reason"
strerr = errno + strerr strerr = errno + strerr
error_message = ("{prefix}Error while writing to " error_message = (
"socket: {error}").format( "{prefix}Error while writing to " "socket: {error}"
prefix=W.prefix("network"), error=strerr) ).format(prefix=W.prefix("network"), error=strerr)
server_buffer_prnt(self, error_message) server_buffer_prnt(self, error_message)
server_buffer_prnt( server_buffer_prnt(
self, ("{prefix}matrix: disconnecting from server..." self,
).format(prefix=W.prefix("network"))) ("{prefix}matrix: disconnecting from server...").format(
prefix=W.prefix("network")
),
)
self.disconnect() self.disconnect()
return False return False
@ -273,10 +335,15 @@ class MatrixServer(object):
server_buffer_prnt( server_buffer_prnt(
self, self,
"{prefix}matrix: Error while writing to socket".format( "{prefix}matrix: Error while writing to socket".format(
prefix=W.prefix("network"))) prefix=W.prefix("network")
),
)
server_buffer_prnt( server_buffer_prnt(
self, ("{prefix}matrix: disconnecting from server..." self,
).format(prefix=W.prefix("network"))) ("{prefix}matrix: disconnecting from server...").format(
prefix=W.prefix("network")
),
)
self.disconnect() self.disconnect()
return False return False
@ -286,7 +353,6 @@ class MatrixServer(object):
return True return True
def _abort_send(self): def _abort_send(self):
self.current_message = None
self.send_buffer = "" self.send_buffer = ""
def _finalize_send(self): def _finalize_send(self):
@ -316,8 +382,9 @@ class MatrixServer(object):
return True return True
def reconnect(self): def reconnect(self):
message = ("{prefix}matrix: reconnecting to server..." message = ("{prefix}matrix: reconnecting to server...").format(
).format(prefix=W.prefix("network")) prefix=W.prefix("network")
)
server_buffer_prnt(self, message) server_buffer_prnt(self, message)
@ -336,9 +403,9 @@ class MatrixServer(object):
else: else:
self.reconnect_delay = 10 self.reconnect_delay = 10
message = ("{prefix}matrix: reconnecting to server in {t} " message = (
"seconds").format( "{prefix}matrix: reconnecting to server in {t} " "seconds"
prefix=W.prefix("network"), t=self.reconnect_delay) ).format(prefix=W.prefix("network"), t=self.reconnect_delay)
server_buffer_prnt(self, message) server_buffer_prnt(self, message)
@ -364,7 +431,6 @@ class MatrixServer(object):
self.access_token = "" self.access_token = ""
self.send_buffer = b"" self.send_buffer = b""
self.current_message = None
self.transport_type = None self.transport_type = None
try: try:
@ -378,8 +444,9 @@ class MatrixServer(object):
self.reconnect_time = None self.reconnect_time = None
if self.server_buffer: if self.server_buffer:
message = ("{prefix}matrix: disconnected from server" message = ("{prefix}matrix: disconnected from server").format(
).format(prefix=W.prefix("network")) prefix=W.prefix("network")
)
server_buffer_prnt(self, message) server_buffer_prnt(self, message)
if reconnect: if reconnect:
@ -390,13 +457,15 @@ class MatrixServer(object):
if not self.config.address or not self.config.port: if not self.config.address or not self.config.port:
W.prnt("", self.config.address) W.prnt("", self.config.address)
message = "{prefix}Server address or port not set".format( message = "{prefix}Server address or port not set".format(
prefix=W.prefix("error")) prefix=W.prefix("error")
)
W.prnt("", message) W.prnt("", message)
return False return False
if not self.config.username or not self.config.password: if not self.config.username or not self.config.password:
message = "{prefix}User or password not set".format( message = "{prefix}User or password not set".format(
prefix=W.prefix("error")) prefix=W.prefix("error")
)
W.prnt("", message) W.prnt("", message)
return False return False
@ -407,54 +476,59 @@ class MatrixServer(object):
create_server_buffer(self) create_server_buffer(self)
if not self.timer_hook: if not self.timer_hook:
self.timer_hook = W.hook_timer(1 * 1000, 0, 0, "matrix_timer_cb", self.timer_hook = W.hook_timer(
self.name) 1 * 1000, 0, 0, "matrix_timer_cb", self.name
)
ssl_message = " (SSL)" if self.ssl_context.check_hostname else "" ssl_message = " (SSL)" if self.ssl_context.check_hostname else ""
message = ("{prefix}matrix: Connecting to " message = (
"{server}:{port}{ssl}...").format( "{prefix}matrix: Connecting to " "{server}:{port}{ssl}..."
prefix=W.prefix("network"), ).format(
server=self.config.address, prefix=W.prefix("network"),
port=self.config.port, server=self.config.address,
ssl=ssl_message) port=self.config.port,
ssl=ssl_message,
)
W.prnt(self.server_buffer, message) W.prnt(self.server_buffer, message)
W.hook_connect(self.config.proxy, W.hook_connect(
self.config.address, self.config.port, self.config.proxy,
1, 0, "", "connect_cb", self.config.address,
self.name) self.config.port,
1,
0,
"",
"connect_cb",
self.name,
)
return True return True
def schedule_sync(self): def schedule_sync(self):
self.sync_time = time.time() self.sync_time = time.time()
def sync(self, timeout=None, filter=None): def sync(self, timeout=None, sync_filter=None):
# type: (Optional[int], Optional[Dict[Any, Any]]) -> None # type: (Optional[int], Optional[Dict[Any, Any]]) -> None
self.sync_time = None self.sync_time = None
_, request = self.client.sync(timeout, filter) _, request = self.client.sync(timeout, sync_filter)
self.send_or_queue(request) self.send_or_queue(request)
def login(self): def login(self):
# type: () -> None # type: () -> None
if self.client.logged_in: if self.client.logged_in:
msg = ("{prefix}{script_name}: Already logged in, " msg = (
"syncing...").format( "{prefix}{script_name}: Already logged in, " "syncing..."
prefix=W.prefix("network"), ).format(prefix=W.prefix("network"), script_name=SCRIPT_NAME)
script_name=SCRIPT_NAME
)
W.prnt(self.server_buffer, msg) W.prnt(self.server_buffer, msg)
timeout = (0 if self.transport_type == TransportType.HTTP timeout = 0 if self.transport_type == TransportType.HTTP else 30000
else 30000)
sync_filter = {"room": {"timeline": {"limit": 5000}}} sync_filter = {"room": {"timeline": {"limit": 5000}}}
self.sync(timeout, sync_filter) self.sync(timeout, sync_filter)
return return
_, request = self.client.login( _, request = self.client.login(
self.config.password, self.config.password, self.config.device_name
self.config.device_name
) )
self.send_or_queue(request) self.send_or_queue(request)
@ -469,30 +543,24 @@ class MatrixServer(object):
return return
_, request = self.client.room_put_state( _, request = self.client.room_put_state(
room_buffer.room.room_id, room_buffer.room.room_id, event_type, body
event_type,
body
) )
self.send_or_queue(request) self.send_or_queue(request)
def room_send_redaction(self, room_buffer, event_id, reason=None): def room_send_redaction(self, room_buffer, event_id, reason=None):
_, request = self.client.room_redact( _, request = self.client.room_redact(
room_buffer.room.room_id, room_buffer.room.room_id, event_id, reason
event_id, )
reason)
self.send_or_queue(request) self.send_or_queue(request)
def room_kick(self, room_buffer, user_id, reason=None): def room_kick(self, room_buffer, user_id, reason=None):
_, request = self.client.room_kick( _, request = self.client.room_kick(
room_buffer.room.room_id, room_buffer.room.room_id, user_id, reason
user_id, )
reason)
self.send_or_queue(request) self.send_or_queue(request)
def room_invite(self, room_buffer, user_id): def room_invite(self, room_buffer, user_id):
_, request = self.client.room_invite( _, request = self.client.room_invite(room_buffer.room.room_id, user_id)
room_buffer.room.room_id,
user_id)
self.send_or_queue(request) self.send_or_queue(request)
def room_join(self, room_id): def room_join(self, room_id):
@ -514,11 +582,7 @@ class MatrixServer(object):
message_class = OwnMessage message_class = OwnMessage
own_message = message_class( own_message = message_class(
self.user_id, self.user_id, 0, "", room_buffer.room.room_id, formatted
0,
"",
room_buffer.room.room_id,
formatted
) )
body = {"msgtype": msgtype, "body": formatted.to_plain()} body = {"msgtype": msgtype, "body": formatted.to_plain()}
@ -528,20 +592,22 @@ class MatrixServer(object):
body["formatted_body"] = formatted.to_html() body["formatted_body"] = formatted.to_html()
uuid, request = self.client.room_send( uuid, request = self.client.room_send(
room_buffer.room.room_id, room_buffer.room.room_id, "m.room.message", body
"m.room.message",
body
) )
self.own_message_queue[uuid] = own_message self.own_message_queue[uuid] = own_message
self.send_or_queue(request) self.send_or_queue(request)
def _print_message_error(self, message): def _print_message_error(self, message):
server_buffer_prnt(self, server_buffer_prnt(
("{prefix}Unhandled {status_code} error, please " self,
"inform the developers about this.").format( (
prefix=W.prefix("error"), "{prefix}Unhandled {status_code} error, please "
status_code=message.response.status)) "inform the developers about this."
).format(
prefix=W.prefix("error"), status_code=message.response.status
),
)
server_buffer_prnt(self, pprint.pformat(message.__class__.__name__)) server_buffer_prnt(self, pprint.pformat(message.__class__.__name__))
server_buffer_prnt(self, pprint.pformat(message.request.payload)) server_buffer_prnt(self, pprint.pformat(message.request.payload))
@ -555,21 +621,13 @@ class MatrixServer(object):
if isinstance(message, OwnAction): if isinstance(message, OwnAction):
room_buffer.self_action(message) room_buffer.self_action(message)
return return
elif isinstance(message, OwnMessage): if isinstance(message, OwnMessage):
room_buffer.self_message(message) room_buffer.self_message(message)
return return
raise NotImplementedError("Unsupported message of type {}".format( raise NotImplementedError(
type(message))) "Unsupported message of type {}".format(type(message))
)
def _handle_erorr_response(self, response):
message = ("{prefix}matrix: {error}").format(
prefix=W.prefix("error"), error=self.error_message)
W.prnt(self.server.server_buffer, message)
if self.fatal:
self.server.disconnect(reconnect=False)
def _handle_login(self, response): def _handle_login(self, response):
self.access_token = response.access_token self.access_token = response.access_token
@ -579,7 +637,8 @@ class MatrixServer(object):
self.save_device_id() self.save_device_id()
message = "{prefix}matrix: Logged in as {user}".format( message = "{prefix}matrix: Logged in as {user}".format(
prefix=W.prefix("network"), user=self.user_id) prefix=W.prefix("network"), user=self.user_id
)
W.prnt(self.server_buffer, message) W.prnt(self.server_buffer, message)
@ -590,12 +649,10 @@ class MatrixServer(object):
sync_filter = { sync_filter = {
"room": { "room": {
"timeline": { "timeline": {"limit": G.CONFIG.network.max_initial_sync_events}
"limit": G.CONFIG.network.max_initial_sync_events
}
} }
} }
self.sync(timeout=0, filter=sync_filter) self.sync(timeout=0, sync_filter=sync_filter)
def _handle_room_info(self, response): def _handle_room_info(self, response):
for room_id, info in response.rooms.invite.items(): for room_id, info in response.rooms.invite.items():
@ -604,21 +661,23 @@ class MatrixServer(object):
if room: if room:
if room.inviter: if room.inviter:
inviter_msg = " by {}{}".format( inviter_msg = " by {}{}".format(
W.color("chat_nick_other"), W.color("chat_nick_other"), room.inviter
room.inviter) )
else: else:
inviter_msg = "" inviter_msg = ""
self.info("You have been invited to {} {}({}{}{}){}" self.info(
"{}".format( "You have been invited to {} {}({}{}{}){}"
room.display_name(), "{}".format(
W.color("chat_delimiters"), room.display_name(),
W.color("chat_channel"), W.color("chat_delimiters"),
room_id, W.color("chat_channel"),
W.color("chat_delimiters"), room_id,
W.color("reset"), W.color("chat_delimiters"),
inviter_msg W.color("reset"),
)) inviter_msg,
)
)
else: else:
self.info("You have been invited to {}.".format(room_id)) self.info("You have been invited to {}.".format(room_id))
@ -647,13 +706,17 @@ class MatrixServer(object):
self.schedule_sync() self.schedule_sync()
def handle_transport_response(self, response): def handle_transport_response(self, response):
self.error(("Error with response of type type: {}, " self.error(
"error code {}").format( ("Error with response of type type: {}, " "error code {}").format(
response.request_info.type, response.status_code)) response.request_info.type, response.status_code
)
)
# TODO better error handling. # TODO better error handling.
if (response.request_info.type == "sync" or if (
response.request_info.type == "login"): response.request_info.type == "sync"
or response.request_info.type == "login"
):
self.disconnect() self.disconnect()
def handle_response(self, response): def handle_response(self, response):
@ -681,8 +744,6 @@ class MatrixServer(object):
elif isinstance(response, RoomSendResponse): elif isinstance(response, RoomSendResponse):
self.handle_own_messages(response) self.handle_own_messages(response)
return
def create_room_buffer(self, room_id): def create_room_buffer(self, room_id):
room = self.client.rooms[room_id] room = self.client.rooms[room_id]
buf = RoomBuffer(room, self.name) buf = RoomBuffer(room, self.name)
@ -722,8 +783,7 @@ class MatrixServer(object):
break break
if first: if first:
num = W.buffer_get_integer( num = W.buffer_get_integer(
W.buffer_search_main(), W.buffer_search_main(), "number"
"number"
) )
W.buffer_unmerge(buf, num + 1) W.buffer_unmerge(buf, num + 1)
if buf is not first: if buf is not first:
@ -734,13 +794,14 @@ class MatrixServer(object):
@utf8_decode @utf8_decode
def matrix_config_server_read_cb(data, config_file, section, option_name, def matrix_config_server_read_cb(
value): data, config_file, section, option_name, value
):
return_code = W.WEECHAT_CONFIG_OPTION_SET_ERROR return_code = W.WEECHAT_CONFIG_OPTION_SET_ERROR
if option_name: if option_name:
server_name, option = option_name.rsplit('.', 1) server_name, option = option_name.rsplit(".", 1)
server = None server = None
if server_name in SERVERS: if server_name in SERVERS:
@ -752,9 +813,7 @@ def matrix_config_server_read_cb(data, config_file, section, option_name,
# Ignore invalid options # Ignore invalid options
if option in server.config._option_ptrs: if option in server.config._option_ptrs:
return_code = W.config_option_set( return_code = W.config_option_set(
server.config._option_ptrs[option], server.config._option_ptrs[option], value, 1
value,
1
) )
# TODO print out error message in case of erroneous return_code # TODO print out error message in case of erroneous return_code
@ -796,8 +855,11 @@ def matrix_timer_cb(server_name, remaining_calls):
current_time = time.time() current_time = time.time()
if ((not server.connected) and server.reconnect_time and if (
current_time >= (server.reconnect_time + server.reconnect_delay)): (not server.connected)
and server.reconnect_time
and current_time >= (server.reconnect_time + server.reconnect_delay)
):
server.reconnect() server.reconnect()
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@ -844,7 +906,7 @@ def matrix_timer_cb(server_name, remaining_calls):
def create_default_server(config_file): def create_default_server(config_file):
server = MatrixServer('matrix_org', config_file._ptr) server = MatrixServer("matrix_org", config_file._ptr)
SERVERS[server.name] = server SERVERS[server.name] = server
option = W.config_get(SCRIPT_NAME + ".server." + server.name + ".address") option = W.config_get(SCRIPT_NAME + ".server." + server.name + ".address")

View file

@ -28,7 +28,7 @@ import sys
# pylint: disable=redefined-builtin # pylint: disable=redefined-builtin
from builtins import bytes, str from builtins import bytes, str
from collections import Mapping, Iterable from collections import Iterable, Mapping
from functools import wraps from functools import wraps
# These functions were written by Trygve Aaberge for wee-slack and are under a # These functions were written by Trygve Aaberge for wee-slack and are under a
@ -39,13 +39,11 @@ from functools import wraps
class WeechatWrapper(object): class WeechatWrapper(object):
def __init__(self, wrapped_class): def __init__(self, wrapped_class):
self.wrapped_class = wrapped_class self.wrapped_class = wrapped_class
# Helper method used to encode/decode method calls. # Helper method used to encode/decode method calls.
def wrap_for_utf8(self, method): def wrap_for_utf8(self, method):
def hooked(*args, **kwargs): def hooked(*args, **kwargs):
result = method(*encode_to_utf8(args), **encode_to_utf8(kwargs)) result = method(*encode_to_utf8(args), **encode_to_utf8(kwargs))
# Prevent wrapped_class from becoming unwrapped # Prevent wrapped_class from becoming unwrapped
@ -69,7 +67,8 @@ class WeechatWrapper(object):
def prnt_date_tags(self, buffer, date, tags, message): def prnt_date_tags(self, buffer, date, tags, message):
message = message.replace("\n", "\n \t") message = message.replace("\n", "\n \t")
return self.wrap_for_utf8(self.wrapped_class.prnt_date_tags)( return self.wrap_for_utf8(self.wrapped_class.prnt_date_tags)(
buffer, date, tags, message) buffer, date, tags, message
)
def utf8_decode(function): def utf8_decode(function):
@ -92,7 +91,7 @@ def utf8_decode(function):
def decode_from_utf8(data): def decode_from_utf8(data):
if isinstance(data, bytes): if isinstance(data, bytes):
return data.decode('utf-8') return data.decode("utf-8")
if isinstance(data, str): if isinstance(data, str):
return data return data
elif isinstance(data, Mapping): elif isinstance(data, Mapping):
@ -104,7 +103,7 @@ def decode_from_utf8(data):
def encode_to_utf8(data): def encode_to_utf8(data):
if isinstance(data, str): if isinstance(data, str):
return data.encode('utf-8') return data.encode("utf-8")
if isinstance(data, bytes): if isinstance(data, bytes):
return data return data
elif isinstance(data, Mapping): elif isinstance(data, Mapping):

View file

@ -15,18 +15,10 @@
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
from __future__ import unicode_literals from __future__ import unicode_literals
from builtins import str
import time import time
import math
from matrix import globals as G from .globals import W
from matrix.globals import W, SERVERS
try:
from urlparse import urlparse
except ImportError:
from urllib.parse import urlparse
def key_from_value(dictionary, value): def key_from_value(dictionary, value):
@ -45,11 +37,13 @@ def server_buffer_prnt(server, string):
def tags_from_line_data(line_data): def tags_from_line_data(line_data):
# type: (weechat.hdata) -> List[str] # type: (weechat.hdata) -> List[str]
tags_count = W.hdata_get_var_array_size( tags_count = W.hdata_get_var_array_size(
W.hdata_get('line_data'), line_data, 'tags_array') W.hdata_get("line_data"), line_data, "tags_array"
)
tags = [ tags = [
W.hdata_string( W.hdata_string(
W.hdata_get('line_data'), line_data, '%d|tags_array' % i) W.hdata_get("line_data"), line_data, "%d|tags_array" % i
)
for i in range(tags_count) for i in range(tags_count)
] ]
@ -59,16 +53,15 @@ def tags_from_line_data(line_data):
def create_server_buffer(server): def create_server_buffer(server):
# type: (MatrixServer) -> None # type: (MatrixServer) -> None
buffer_name = "server.{}".format(server.name) buffer_name = "server.{}".format(server.name)
server.server_buffer = W.buffer_new(buffer_name, "server_buffer_cb", server.server_buffer = W.buffer_new(
server.name, "", "") buffer_name, "server_buffer_cb", server.name, "", ""
)
server_buffer_set_title(server) server_buffer_set_title(server)
W.buffer_set(server.server_buffer, "short_name", server.name) W.buffer_set(server.server_buffer, "short_name", server.name)
W.buffer_set(server.server_buffer, "localvar_set_type", 'server') W.buffer_set(server.server_buffer, "localvar_set_type", "server")
W.buffer_set( W.buffer_set(
server.server_buffer, server.server_buffer, "localvar_set_nick", server.config.username
"localvar_set_nick",
server.config.username
) )
W.buffer_set(server.server_buffer, "localvar_set_server", server.name) W.buffer_set(server.server_buffer, "localvar_set_server", server.name)
W.buffer_set(server.server_buffer, "localvar_set_channel", server.name) W.buffer_set(server.server_buffer, "localvar_set_channel", server.name)
@ -84,18 +77,12 @@ def server_buffer_set_title(server):
ip_string = "" ip_string = ""
title = ("Matrix: {address}:{port}{ip}").format( title = ("Matrix: {address}:{port}{ip}").format(
address=server.config.address, port=server.config.port, ip=ip_string) address=server.config.address, port=server.config.port, ip=ip_string
)
W.buffer_set(server.server_buffer, "title", title) W.buffer_set(server.server_buffer, "title", title)
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 server_ts_to_weechat(timestamp): def server_ts_to_weechat(timestamp):
# type: (float) -> int # type: (float) -> int
date = int(timestamp / 1000) date = int(timestamp / 1000)
@ -112,241 +99,5 @@ def shorten_sender(sender):
return strip_matrix_server(sender)[1:] return strip_matrix_server(sender)[1:]
def sender_to_prefix_and_color(room, sender):
if sender in room.users:
user = room.users[sender]
prefix = user.prefix
prefix_color = get_prefix_color(prefix)
return prefix, prefix_color
return None, None
def sender_to_nick_and_color(room, sender):
nick = sender
nick_color_name = "default"
if sender in room.users:
user = room.users[sender]
nick = (user.display_name if user.display_name else user.name)
nick_color_name = user.nick_color
else:
nick = sender
nick_color_name = W.info_get("nick_color_name", nick)
return (nick, nick_color_name)
def tags_for_message(message_type):
default_tags = {
"message": ["matrix_message", "notify_message", "log1"],
"backlog":
["matrix_message", "notify_message", "no_log", "no_highlight"]
}
return default_tags[message_type]
def add_event_tags(event_id, nick, color=None, tags=[]):
tags.append("nick_{nick}".format(nick=nick))
if color:
tags.append("prefix_nick_{color}".format(color=color_for_tags(color)))
tags.append("matrix_id_{event_id}".format(event_id=event_id))
return tags
def sanitize_token(string):
# type: (str) -> str
string = sanitize_string(string)
if len(string) > 512:
raise ValueError
if string == "":
raise ValueError
return string
def sanitize_string(string):
# type: (str) -> str
if not isinstance(string, str):
raise TypeError
# string keys can have empty string values sometimes (e.g. room names that
# got deleted)
if string == "":
return None
remap = {
ord('\b'): None,
ord('\f'): None,
ord('\n'): None,
ord('\r'): None,
ord('\t'): None,
ord('\0'): None
}
return string.translate(remap)
def sanitize_id(string):
# type: (str) -> str
string = sanitize_string(string)
if len(string) > 128:
raise ValueError
if string == "":
raise ValueError
return string
def sanitize_int(number, minimum=None, maximum=None):
# type: (int, int, int) -> int
if not isinstance(number, int):
raise TypeError
if math.isnan(number):
raise ValueError
if math.isinf(number):
raise ValueError
if minimum:
if number < minimum:
raise ValueError
if maximum:
if number > maximum:
raise ValueError
return number
def sanitize_ts(timestamp):
# type: (int) -> int
return sanitize_int(timestamp, 0)
def sanitize_power_level(level):
# type: (int) -> int
return sanitize_int(level, 0, 100)
def sanitize_text(string):
# type: (str) -> str
if not isinstance(string, str):
raise TypeError
# yapf: disable
remap = {
ord('\b'): None,
ord('\f'): None,
ord('\r'): None,
ord('\0'): None
}
# yapf: enable
return string.translate(remap)
def add_user_to_nicklist(buf, user_id, 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)
prefix = user.prefix if user.prefix else " "
# TODO make it configurable so we can use a display name or user_id here
W.nicklist_add_nick(buf, group, user_id, user.nick_color, prefix,
get_prefix_color(user.prefix), 1)
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 string_strikethrough(string): def string_strikethrough(string):
return "".join(["{}\u0336".format(c) for c in string]) return "".join(["{}\u0336".format(c) for c in string])
def line_pointer_and_tags_from_event(buff, event_id):
# type: (str, str) -> str
own_lines = W.hdata_pointer(W.hdata_get('buffer'), buff, '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:
tags = tags_from_line_data(data_pointer)
message_id = event_id_from_tags(tags)
if event_id == message_id:
return data_pointer, tags
line_pointer = W.hdata_move(hdata_line, line_pointer, -1)
return None, []
def event_id_from_tags(tags):
# type: (List[str]) -> str
for tag in tags:
if tag.startswith("matrix_id"):
return tag[10:]
return ""
def mxc_to_http(mxc):
# type: (str) -> str
url = urlparse(mxc)
if url.scheme != "mxc":
return None
if not url.netloc or not url.path:
return None
return "https://{}/_matrix/media/r0/download/{}{}".format(
url.netloc,
url.netloc,
url.path
)