Move the weechat command setup and some commands.

This commit is contained in:
poljar (Damir Jelić) 2018-01-26 17:43:07 +01:00
parent be48b73395
commit 27c0836eb5
7 changed files with 707 additions and 633 deletions

View file

@ -2,6 +2,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
# pylint: disable=redefined-builtin
from builtins import str
from collections import namedtuple from collections import namedtuple
import webcolors import webcolors

445
matrix/commands.py Normal file
View file

@ -0,0 +1,445 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import re
import matrix.globals
from matrix.utf import utf8_decode
from matrix.api import MatrixMessage, MessageType
from matrix.utils import key_from_value, tags_from_line_data
from matrix.socket import send_or_queue
W = matrix.globals.W
GLOBAL_OPTIONS = matrix.globals.OPTIONS
SERVERS = matrix.globals.SERVERS
def hook_commands():
W.hook_completion(
"matrix_server_commands",
"Matrix server completion",
"server_command_completion_cb",
""
)
W.hook_completion(
"matrix_servers",
"Matrix server completion",
"matrix_server_completion_cb",
""
)
W.hook_completion(
"matrix_commands",
"Matrix command completion",
"matrix_command_completion_cb",
""
)
W.hook_completion(
"matrix_messages",
"Matrix message completion",
"matrix_message_completion_cb",
""
)
W.hook_completion(
"matrix_debug_types",
"Matrix debugging type completion",
"matrix_debug_completion_cb",
""
)
W.hook_command(
# Command name and short description
'matrix', 'Matrix chat protocol command',
# Synopsis
(
'server add <server-name> <hostname>[:<port>] ||'
'server delete|list|listfull <server-name> ||'
'connect <server-name> ||'
'disconnect <server-name> ||'
'reconnect <server-name> ||'
'debug <debug-type> ||'
'help <matrix-command>'
),
# Description
(
' server: list, add, or remove Matrix servers\n'
' connect: connect to Matrix servers\n'
'disconnect: disconnect from one or all Matrix servers\n'
' reconnect: reconnect to server(s)\n\n'
' help: show detailed command help\n\n'
' debug: enable or disable debugging\n\n'
'Use /matrix help [command] to find out more\n'
),
# Completions
(
'server %(matrix_server_commands)|%* ||'
'connect %(matrix_servers) ||'
'disconnect %(matrix_servers) ||'
'reconnect %(matrix_servers) ||'
'debug %(matrix_debug_types) ||'
'help %(matrix_commands)'
),
# Function name
'matrix_command_cb', '')
W.hook_command(
# Command name and short description
'redact', 'redact messages',
# Synopsis
(
'<message-number>[:<"message-part">] [<reason>]'
),
# Description
(
"message-number: number of the message to redact (message numbers"
"\n start from the last recieved as "
"1 and count up)\n"
" message-part: a shortened part of the message\n"
" reason: the redaction reason\n"
),
# Completions
(
'%(matrix_messages)'
),
# Function name
'matrix_redact_command_cb', '')
W.hook_command_run('/topic', 'matrix_command_topic_cb', '')
W.hook_command_run('/buffer clear', 'matrix_command_buf_clear_cb', '')
W.hook_command_run('/join', 'matrix_command_join_cb', '')
W.hook_command_run('/part', 'matrix_command_part_cb', '')
W.hook_command_run('/invite', 'matrix_command_invite_cb', '')
if GLOBAL_OPTIONS.enable_backlog:
hook_page_up()
def matrix_fetch_old_messages(server, room_id):
room = server.rooms[room_id]
prev_batch = room.prev_batch
if not prev_batch:
return
message = MatrixMessage(server, GLOBAL_OPTIONS, MessageType.ROOM_MSG,
room_id=room_id, extra_id=prev_batch)
send_or_queue(server, message)
return
def hook_page_up():
GLOBAL_OPTIONS.page_up_hook = W.hook_command_run(
'/window page_up',
'matrix_command_pgup_cb',
''
)
@utf8_decode
def matrix_debug_completion_cb(data, completion_item, buffer, completion):
for debug_type in ["messaging", "network", "timing"]:
W.hook_completion_list_add(
completion,
debug_type,
0,
W.WEECHAT_LIST_POS_SORT)
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_command_buf_clear_cb(data, buffer, command):
for server in SERVERS.values():
if buffer in server.buffers.values():
room_id = key_from_value(server.buffers, buffer)
server.rooms[room_id].prev_batch = server.next_batch
return W.WEECHAT_RC_OK
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_command_pgup_cb(data, buffer, command):
# TODO the highlight status of a line isn't allowed to be updated/changed
# via hdata, therefore the highlight status of a messages can't be
# reoredered this would need to be fixed in weechat
# TODO we shouldn't fetch and print out more messages than
# max_buffer_lines_number or older messages than max_buffer_lines_minutes
for server in SERVERS.values():
if buffer in server.buffers.values():
window = W.window_search_with_buffer(buffer)
first_line_displayed = bool(
W.window_get_integer(window, "first_line_displayed")
)
if first_line_displayed:
room_id = key_from_value(server.buffers, buffer)
matrix_fetch_old_messages(server, room_id)
return W.WEECHAT_RC_OK
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_command_join_cb(data, buffer, command):
def join(server, args):
split_args = args.split(" ", 1)
# TODO handle join for non public rooms
if len(split_args) != 2:
message = ("{prefix}Error with command \"/join\" (help on "
"command: /help join)").format(
prefix=W.prefix("error"))
W.prnt("", message)
return
_, room_id = split_args
message = MatrixMessage(
server,
GLOBAL_OPTIONS,
MessageType.JOIN,
room_id=room_id
)
send_or_queue(server, message)
for server in SERVERS.values():
if buffer in server.buffers.values():
join(server, command)
return W.WEECHAT_RC_OK_EAT
elif buffer == server.server_buffer:
join(server, command)
return W.WEECHAT_RC_OK_EAT
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_command_part_cb(data, buffer, command):
def part(server, buffer, args):
rooms = []
split_args = args.split(" ", 1)
if len(split_args) == 1:
if buffer == server.server_buffer:
message = ("{prefix}Error with command \"/part\" (help on "
"command: /help part)").format(
prefix=W.prefix("error"))
W.prnt("", message)
return
rooms = [key_from_value(server.buffers, buffer)]
else:
_, rooms = split_args
rooms = rooms.split(" ")
for room_id in rooms:
message = MatrixMessage(
server,
GLOBAL_OPTIONS,
MessageType.PART,
room_id=room_id
)
send_or_queue(server, message)
for server in SERVERS.values():
if buffer in server.buffers.values():
part(server, buffer, command)
return W.WEECHAT_RC_OK_EAT
elif buffer == server.server_buffer:
part(server, buffer, command)
return W.WEECHAT_RC_OK_EAT
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_command_invite_cb(data, buffer, command):
def invite(server, buf, args):
split_args = args.split(" ", 1)
# TODO handle join for non public rooms
if len(split_args) != 2:
message = ("{prefix}Error with command \"/invite\" (help on "
"command: /help invite)").format(
prefix=W.prefix("error"))
W.prnt("", message)
return
_, invitee = split_args
room_id = key_from_value(server.buffers, buf)
body = {"user_id": invitee}
message = MatrixMessage(
server,
GLOBAL_OPTIONS,
MessageType.INVITE,
room_id=room_id,
data=body
)
send_or_queue(server, message)
for server in SERVERS.values():
if buffer in server.buffers.values():
invite(server, buffer, command)
return W.WEECHAT_RC_OK_EAT
return W.WEECHAT_RC_OK
def event_id_from_line(buf, target_number):
# type: (weechat.buffer, int) -> str
own_lines = W.hdata_pointer(W.hdata_get('buffer'), buf, 'own_lines')
if own_lines:
line = W.hdata_pointer(
W.hdata_get('lines'),
own_lines,
'last_line'
)
line_number = 1
while line:
line_data = W.hdata_pointer(
W.hdata_get('line'),
line,
'data'
)
if line_data:
tags = tags_from_line_data(line_data)
# Only count non redacted user messages
if ("matrix_message" in tags
and 'matrix_redacted' not in tags
and "matrix_new_redacted" not in tags):
if line_number == target_number:
for tag in tags:
if tag.startswith("matrix_id"):
event_id = tag[10:]
return event_id
line_number += 1
line = W.hdata_move(W.hdata_get('line'), line, -1)
return ""
@utf8_decode
def matrix_redact_command_cb(data, buffer, args):
for server in SERVERS.values():
if buffer in server.buffers.values():
body = {}
room_id = key_from_value(server.buffers, buffer)
matches = re.match(r"(\d+)(:\".*\")? ?(.*)?", args)
if not matches:
message = ("{prefix}matrix: Invalid command arguments (see "
"the help for the command /help redact)").format(
prefix=W.prefix("error"))
W.prnt("", message)
return W.WEECHAT_RC_ERROR
line_string, _, reason = matches.groups()
line = int(line_string)
if reason:
body = {"reason": reason}
event_id = event_id_from_line(buffer, line)
if not event_id:
message = ("{prefix}matrix: No such message with number "
"{number} found").format(
prefix=W.prefix("error"),
number=line)
W.prnt("", message)
return W.WEECHAT_RC_OK
message = MatrixMessage(
server,
GLOBAL_OPTIONS,
MessageType.REDACT,
data=body,
room_id=room_id,
extra_id=event_id
)
send_or_queue(server, message)
return W.WEECHAT_RC_OK
elif buffer == server.server_buffer:
message = ("{prefix}matrix: command \"redact\" must be "
"executed on a Matrix channel buffer").format(
prefix=W.prefix("error"))
W.prnt("", message)
return W.WEECHAT_RC_OK
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_message_completion_cb(data, completion_item, buffer, completion):
own_lines = W.hdata_pointer(W.hdata_get('buffer'), buffer, 'own_lines')
if own_lines:
line = W.hdata_pointer(
W.hdata_get('lines'),
own_lines,
'last_line'
)
line_number = 1
while line:
line_data = W.hdata_pointer(
W.hdata_get('line'),
line,
'data'
)
if line_data:
message = W.hdata_string(W.hdata_get('line_data'), line_data,
'message')
tags = tags_from_line_data(line_data)
# Only add non redacted user messages to the completion
if (message
and 'matrix_message' in tags
and 'matrix_redacted' not in tags):
if len(message) > GLOBAL_OPTIONS.redaction_comp_len + 2:
message = (
message[:GLOBAL_OPTIONS.redaction_comp_len]
+ '..')
item = ("{number}:\"{message}\"").format(
number=line_number,
message=message)
W.hook_completion_list_add(
completion,
item,
0,
W.WEECHAT_LIST_POS_END)
line_number += 1
line = W.hdata_move(W.hdata_get('line'), line, -1)
return W.WEECHAT_RC_OK

96
matrix/globals.py Normal file
View file

@ -0,0 +1,96 @@
# pylint: disable=import-error
import sys
from matrix.utf import WeechatWrapper
from matrix.config import PluginOptions, Option
import weechat
def init_matrix_config():
config_file = W.config_new("matrix", "matrix_config_reload_cb", "")
look_options = [
Option(
"redactions", "integer",
"strikethrough|notice|delete", 0, 0,
"strikethrough",
(
"Only notice redactions, strike through or delete "
"redacted messages"
)
),
Option(
"server_buffer", "integer",
"merge_with_core|merge_without_core|independent",
0, 0, "merge_with_core", "Merge server buffers"
)
]
network_options = [
Option(
"max_initial_sync_events", "integer",
"", 1, 10000,
"30",
(
"How many events to fetch during the initial sync"
)
),
Option(
"max_backlog_sync_events", "integer",
"", 1, 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"
)
)
]
def add_global_options(section, options):
for option in options:
OPTIONS.options[option.name] = W.config_new_option(
config_file, section, option.name,
option.type, option.description, option.string_values,
option.min, option.max, option.value, option.value, 0, "",
"", "matrix_config_change_cb", "", "", "")
section = W.config_new_section(config_file, "color", 0, 0, "", "", "", "",
"", "", "", "", "", "")
# TODO color options
section = W.config_new_section(config_file, "look", 0, 0, "", "", "", "",
"", "", "", "", "", "")
add_global_options(section, look_options)
section = W.config_new_section(config_file, "network", 0, 0, "", "", "",
"", "", "", "", "", "", "")
add_global_options(section, network_options)
W.config_new_section(
config_file, "server",
0, 0,
"matrix_config_server_read_cb",
"",
"matrix_config_server_write_cb",
"", "", "", "", "", "", ""
)
return config_file
W = weechat if sys.hexversion >= 0x3000000 else WeechatWrapper(weechat)
OPTIONS = PluginOptions() # type: PluginOptions
SERVERS = dict() # type: Dict[str, MatrixServer]
CONFIG = None # type: weechat.config

View file

@ -5,10 +5,8 @@ from __future__ import unicode_literals
import ssl import ssl
from collections import deque from collections import deque
from http_parser.pyparser import HttpParser from http_parser.pyparser import HttpParser
from matrix.config import Option from matrix.config import Option

72
matrix/socket.py Normal file
View file

@ -0,0 +1,72 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import time
from builtins import bytes, str
import matrix.globals
from matrix.config import DebugType
from matrix.utils import prnt_debug, server_buffer_prnt
W = matrix.globals.W
def disconnect(server):
# type: (MatrixServer) -> None
if server.fd_hook:
W.unhook(server.fd_hook)
server.fd_hook = None
server.socket = None
server.connected = False
server_buffer_prnt(server, "Disconnected")
def send_or_queue(server, message):
# type: (MatrixServer, MatrixMessage) -> None
if not send(server, message):
prnt_debug(DebugType.MESSAGING, server,
("{prefix} Failed sending message of type {t}. "
"Adding to queue").format(
prefix=W.prefix("error"),
t=message.type))
server.send_queue.append(message)
def send(server, message):
# type: (MatrixServer, MatrixMessage) -> bool
request = message.request.request
payload = message.request.payload
prnt_debug(DebugType.MESSAGING, server,
"{prefix} Sending message of type {t}.".format(
prefix=W.prefix("error"),
t=message.type))
try:
start = time.time()
# TODO we probably shouldn't use sendall here.
server.socket.sendall(bytes(request, 'utf-8'))
if payload:
server.socket.sendall(bytes(payload, 'utf-8'))
end = time.time()
message.send_time = end
send_time = (end - start) * 1000
prnt_debug(DebugType.NETWORK, server,
("Message done sending ({t}ms), putting message in the "
"receive queue.").format(t=send_time))
server.receive_queue.append(message)
return True
except OSError as error:
disconnect(server)
server_buffer_prnt(server, str(error))
return False

46
matrix/utils.py Normal file
View file

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import time
import matrix.globals
W = matrix.globals.W
GLOBAL_OPTIONS = matrix.globals.OPTIONS
def key_from_value(dictionary, value):
# type: (Dict[str, Any], Any) -> str
return list(dictionary.keys())[list(dictionary.values()).index(value)]
def prnt_debug(debug_type, server, message):
if debug_type in GLOBAL_OPTIONS.debug:
W.prnt(server.server_buffer, message)
def server_buffer_prnt(server, string):
# type: (MatrixServer, str) -> None
assert server.server_buffer
buffer = server.server_buffer
now = int(time.time())
W.prnt_date_tags(buffer, now, "", string)
def tags_from_line_data(line_data):
# type: (weechat.hdata) -> List[str]
tags_count = W.hdata_get_var_array_size(
W.hdata_get('line_data'),
line_data,
'tags_array')
tags = [
W.hdata_string(
W.hdata_get('line_data'),
line_data,
'%d|tags_array' % i
) for i in range(tags_count)]
return tags

View file

@ -8,11 +8,9 @@ import ssl
import time import time
import datetime import datetime
import pprint import pprint
import re
import sys
# pylint: disable=redefined-builtin # pylint: disable=redefined-builtin
from builtins import bytes, str from builtins import str
from operator import itemgetter from operator import itemgetter
@ -20,20 +18,48 @@ from operator import itemgetter
from typing import (List, Set, Dict, Tuple, Text, Optional, AnyStr, Deque, Any) from typing import (List, Set, Dict, Tuple, Text, Optional, AnyStr, Deque, Any)
from matrix import colors from matrix import colors
from matrix.utf import WeechatWrapper, utf8_decode from matrix.utf import utf8_decode
from matrix.http import HttpResponse from matrix.http import HttpResponse
from matrix.api import MatrixMessage, MessageType from matrix.api import MatrixMessage, MessageType
from matrix.server import MatrixServer from matrix.server import MatrixServer
from matrix.socket import disconnect, send_or_queue, send
# Weechat searches for the registered callbacks in the global scope, import the
# callbacks here so weechat can find them.
from matrix.commands import (
hook_commands,
hook_page_up,
matrix_command_join_cb,
matrix_command_part_cb,
matrix_command_invite_cb,
matrix_command_pgup_cb,
matrix_redact_command_cb,
matrix_command_buf_clear_cb,
matrix_debug_completion_cb,
matrix_message_completion_cb
)
from matrix.utils import (
key_from_value,
server_buffer_prnt,
prnt_debug,
tags_from_line_data
)
from matrix.config import ( from matrix.config import (
PluginOptions,
Option,
DebugType, DebugType,
RedactType, RedactType,
ServerBufferType ServerBufferType
) )
# pylint: disable=import-error import matrix.globals
import weechat
W = matrix.globals.W
GLOBAL_OPTIONS = matrix.globals.OPTIONS
CONFIG = matrix.globals.CONFIG
SERVERS = matrix.globals.SERVERS
WEECHAT_SCRIPT_NAME = "matrix" # type: str WEECHAT_SCRIPT_NAME = "matrix" # type: str
WEECHAT_SCRIPT_DESCRIPTION = "matrix chat plugin" # type: str WEECHAT_SCRIPT_DESCRIPTION = "matrix chat plugin" # type: str
@ -41,15 +67,6 @@ WEECHAT_SCRIPT_AUTHOR = "Damir Jelić <poljar@termina.org.uk>" # type: str
WEECHAT_SCRIPT_VERSION = "0.1" # type: str WEECHAT_SCRIPT_VERSION = "0.1" # type: str
WEECHAT_SCRIPT_LICENSE = "MIT" # type: str WEECHAT_SCRIPT_LICENSE = "MIT" # type: str
SERVERS = dict() # type: Dict[str, MatrixServer]
CONFIG = None # type: weechat.config
GLOBAL_OPTIONS = None # type: PluginOptions
def prnt_debug(debug_type, server, message):
if debug_type in GLOBAL_OPTIONS.debug:
W.prnt(server.server_buffer, message)
class MatrixUser: class MatrixUser:
def __init__(self, name, display_name): def __init__(self, name, display_name):
@ -73,11 +90,6 @@ class MatrixRoom:
self.encrypted = False # type: bool self.encrypted = False # type: bool
def key_from_value(dictionary, value):
# type: (Dict[str, Any], Any) -> str
return list(dictionary.keys())[list(dictionary.values()).index(value)]
@utf8_decode @utf8_decode
def server_config_change_cb(server_name, option): def server_config_change_cb(server_name, option):
# type: (str, weechat.config_option) -> int # type: (str, weechat.config_option) -> int
@ -363,8 +375,8 @@ def date_from_age(age):
def color_for_tags(color): def color_for_tags(color):
if color == "weechat.color.chat_nick_self": if color == "weechat.color.chat_nick_self":
option = weechat.config_get(color) option = W.config_get(color)
return weechat.config_string(option) return W.config_string(option)
return color return color
@ -964,52 +976,6 @@ def matrix_login(server):
send_or_queue(server, message) send_or_queue(server, message)
def send_or_queue(server, message):
# type: (MatrixServer, MatrixMessage) -> None
if not send(server, message):
prnt_debug(DebugType.MESSAGING, server,
("{prefix} Failed sending message of type {t}. "
"Adding to queue").format(
prefix=W.prefix("error"),
t=message.type))
server.send_queue.append(message)
def send(server, message):
# type: (MatrixServer, MatrixMessage) -> bool
request = message.request.request
payload = message.request.payload
prnt_debug(DebugType.MESSAGING, server,
"{prefix} Sending message of type {t}.".format(
prefix=W.prefix("error"),
t=message.type))
try:
start = time.time()
# TODO we probably shouldn't use sendall here.
server.socket.sendall(bytes(request, 'utf-8'))
if payload:
server.socket.sendall(bytes(payload, 'utf-8'))
end = time.time()
message.send_time = end
send_time = (end - start) * 1000
prnt_debug(DebugType.NETWORK, server,
("Message done sending ({t}ms), putting message in the "
"receive queue.").format(t=send_time))
server.receive_queue.append(message)
return True
except socket.error as error:
disconnect(server)
server_buffer_prnt(server, str(error))
return False
@utf8_decode @utf8_decode
def receive_cb(server_name, file_descriptor): def receive_cb(server_name, file_descriptor):
server = SERVERS[server_name] server = SERVERS[server_name]
@ -1081,26 +1047,6 @@ def close_socket(server):
server.socket.close() server.socket.close()
def disconnect(server):
# type: (MatrixServer) -> None
if server.fd_hook:
W.unhook(server.fd_hook)
server.fd_hook = None
server.socket = None
server.connected = False
server_buffer_prnt(server, "Disconnected")
def server_buffer_prnt(server, string):
# type: (MatrixServer, str) -> None
assert server.server_buffer
buffer = server.server_buffer
now = int(time.time())
W.prnt_date_tags(buffer, now, "", string)
def server_buffer_set_title(server): def server_buffer_set_title(server):
# type: (MatrixServer) -> None # type: (MatrixServer) -> None
if server.numeric_address: if server.numeric_address:
@ -1421,7 +1367,7 @@ def matrix_config_change_cb(data, option):
if GLOBAL_OPTIONS.enable_backlog: if GLOBAL_OPTIONS.enable_backlog:
if not GLOBAL_OPTIONS.page_up_hook: if not GLOBAL_OPTIONS.page_up_hook:
hook_page_up() hook_page_up(CONFIG)
else: else:
if GLOBAL_OPTIONS.page_up_hook: if GLOBAL_OPTIONS.page_up_hook:
W.unhook(GLOBAL_OPTIONS.page_up_hook) W.unhook(GLOBAL_OPTIONS.page_up_hook)
@ -1430,96 +1376,14 @@ def matrix_config_change_cb(data, option):
return 1 return 1
def init_matrix_config():
config_file = W.config_new("matrix", "matrix_config_reload_cb", "")
look_options = [
Option(
"redactions", "integer",
"strikethrough|notice|delete", 0, 0,
"strikethrough",
(
"Only notice redactions, strike through or delete "
"redacted messages"
)
),
Option(
"server_buffer", "integer",
"merge_with_core|merge_without_core|independent",
0, 0, "merge_with_core", "Merge server buffers"
)
]
network_options = [
Option(
"max_initial_sync_events", "integer",
"", 1, 10000,
"30",
(
"How many events to fetch during the initial sync"
)
),
Option(
"max_backlog_sync_events", "integer",
"", 1, 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"
)
)
]
def add_global_options(section, options):
for option in options:
GLOBAL_OPTIONS.options[option.name] = W.config_new_option(
config_file, section, option.name,
option.type, option.description, option.string_values,
option.min, option.max, option.value, option.value, 0, "",
"", "matrix_config_change_cb", "", "", "")
section = W.config_new_section(config_file, "color", 0, 0, "", "", "", "",
"", "", "", "", "", "")
# TODO color options
section = W.config_new_section(config_file, "look", 0, 0, "", "", "", "",
"", "", "", "", "", "")
add_global_options(section, look_options)
section = W.config_new_section(config_file, "network", 0, 0, "", "", "",
"", "", "", "", "", "", "")
add_global_options(section, network_options)
W.config_new_section(
config_file, "server",
0, 0,
"matrix_config_server_read_cb",
"",
"matrix_config_server_write_cb",
"", "", "", "", "", "", ""
)
return config_file
def read_matrix_config(): def read_matrix_config():
# type: () -> bool # type: () -> bool
return_code = W.config_read(CONFIG) return_code = W.config_read(CONFIG)
if return_code == weechat.WEECHAT_CONFIG_READ_OK: if return_code == W.WEECHAT_CONFIG_READ_OK:
return True return True
elif return_code == weechat.WEECHAT_CONFIG_READ_MEMORY_ERROR: elif return_code == W.WEECHAT_CONFIG_READ_MEMORY_ERROR:
return False return False
elif return_code == weechat.WEECHAT_CONFIG_READ_FILE_NOT_FOUND: elif return_code == W.WEECHAT_CONFIG_READ_FILE_NOT_FOUND:
return True return True
return False return False
@ -2051,13 +1915,13 @@ def add_servers_to_completion(completion):
completion, completion,
server_name, server_name,
0, 0,
weechat.WEECHAT_LIST_POS_SORT W.WEECHAT_LIST_POS_SORT
) )
@utf8_decode @utf8_decode
def server_command_completion_cb(data, completion_item, buffer, completion): def server_command_completion_cb(data, completion_item, buffer, completion):
buffer_input = weechat.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']
@ -2068,7 +1932,7 @@ def server_command_completion_cb(data, completion_item, buffer, completion):
completion, completion,
command, command,
0, 0,
weechat.WEECHAT_LIST_POS_SORT W.WEECHAT_LIST_POS_SORT
) )
if len(args) == 1: if len(args) == 1:
@ -2109,7 +1973,7 @@ def matrix_command_completion_cb(data, completion_item, buffer, completion):
completion, completion,
command, command,
0, 0,
weechat.WEECHAT_LIST_POS_SORT) W.WEECHAT_LIST_POS_SORT)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@ -2190,348 +2054,6 @@ def matrix_command_topic_cb(data, buffer, command):
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
def matrix_fetch_old_messages(server, room_id):
room = server.rooms[room_id]
prev_batch = room.prev_batch
if not prev_batch:
return
message = MatrixMessage(server, GLOBAL_OPTIONS, MessageType.ROOM_MSG,
room_id=room_id, extra_id=prev_batch)
send_or_queue(server, message)
return
@utf8_decode
def matrix_command_buf_clear_cb(data, buffer, command):
for server in SERVERS.values():
if buffer in server.buffers.values():
room_id = key_from_value(server.buffers, buffer)
server.rooms[room_id].prev_batch = server.next_batch
return W.WEECHAT_RC_OK
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_command_pgup_cb(data, buffer, command):
# TODO the highlight status of a line isn't allowed to be updated/changed
# via hdata, therefore the highlight status of a messages can't be
# reoredered this would need to be fixed in weechat
# TODO we shouldn't fetch and print out more messages than
# max_buffer_lines_number or older messages than max_buffer_lines_minutes
for server in SERVERS.values():
if buffer in server.buffers.values():
window = W.window_search_with_buffer(buffer)
first_line_displayed = bool(
W.window_get_integer(window, "first_line_displayed")
)
if first_line_displayed:
room_id = key_from_value(server.buffers, buffer)
matrix_fetch_old_messages(server, room_id)
return W.WEECHAT_RC_OK
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_command_join_cb(data, buffer, command):
def join(server, args):
split_args = args.split(" ", 1)
# TODO handle join for non public rooms
if len(split_args) != 2:
message = ("{prefix}Error with command \"/join\" (help on "
"command: /help join)").format(
prefix=W.prefix("error"))
W.prnt("", message)
return
_, room_id = split_args
message = MatrixMessage(
server,
GLOBAL_OPTIONS,
MessageType.JOIN,
room_id=room_id
)
send_or_queue(server, message)
for server in SERVERS.values():
if buffer in server.buffers.values():
join(server, command)
return W.WEECHAT_RC_OK_EAT
elif buffer == server.server_buffer:
join(server, command)
return W.WEECHAT_RC_OK_EAT
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_command_part_cb(data, buffer, command):
def part(server, buffer, args):
rooms = []
split_args = args.split(" ", 1)
if len(split_args) == 1:
if buffer == server.server_buffer:
message = ("{prefix}Error with command \"/part\" (help on "
"command: /help part)").format(
prefix=W.prefix("error"))
W.prnt("", message)
return
rooms = [key_from_value(server.buffers, buffer)]
else:
_, rooms = split_args
rooms = rooms.split(" ")
for room_id in rooms:
message = MatrixMessage(
server,
GLOBAL_OPTIONS,
MessageType.PART,
room_id=room_id
)
send_or_queue(server, message)
for server in SERVERS.values():
if buffer in server.buffers.values():
part(server, buffer, command)
return W.WEECHAT_RC_OK_EAT
elif buffer == server.server_buffer:
part(server, buffer, command)
return W.WEECHAT_RC_OK_EAT
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_command_invite_cb(data, buffer, command):
def invite(server, buf, args):
split_args = args.split(" ", 1)
# TODO handle join for non public rooms
if len(split_args) != 2:
message = ("{prefix}Error with command \"/invite\" (help on "
"command: /help invite)").format(
prefix=W.prefix("error"))
W.prnt("", message)
return
_, invitee = split_args
room_id = key_from_value(server.buffers, buf)
body = {"user_id": invitee}
message = MatrixMessage(
server,
GLOBAL_OPTIONS,
MessageType.INVITE,
room_id=room_id,
data=body
)
send_or_queue(server, message)
for server in SERVERS.values():
if buffer in server.buffers.values():
invite(server, buffer, command)
return W.WEECHAT_RC_OK_EAT
return W.WEECHAT_RC_OK
def tags_from_line_data(line_data):
# type: (weechat.hdata) -> List[str]
tags_count = W.hdata_get_var_array_size(
W.hdata_get('line_data'),
line_data,
'tags_array')
tags = [
W.hdata_string(
W.hdata_get('line_data'),
line_data,
'%d|tags_array' % i
) for i in range(tags_count)]
return tags
def event_id_from_line(buf, target_number):
# type: (weechat.buffer, int) -> str
own_lines = W.hdata_pointer(W.hdata_get('buffer'), buf, 'own_lines')
if own_lines:
line = W.hdata_pointer(
W.hdata_get('lines'),
own_lines,
'last_line'
)
line_number = 1
while line:
line_data = W.hdata_pointer(
W.hdata_get('line'),
line,
'data'
)
if line_data:
tags = tags_from_line_data(line_data)
# Only count non redacted user messages
if ("matrix_message" in tags
and 'matrix_redacted' not in tags
and "matrix_new_redacted" not in tags):
if line_number == target_number:
for tag in tags:
if tag.startswith("matrix_id"):
event_id = tag[10:]
return event_id
line_number += 1
line = W.hdata_move(W.hdata_get('line'), line, -1)
return ""
@utf8_decode
def matrix_redact_command_cb(data, buffer, args):
for server in SERVERS.values():
if buffer in server.buffers.values():
body = {}
room_id = key_from_value(server.buffers, buffer)
matches = re.match(r"(\d+)(:\".*\")? ?(.*)?", args)
if not matches:
message = ("{prefix}matrix: Invalid command arguments (see "
"the help for the command /help redact)").format(
prefix=W.prefix("error"))
W.prnt("", message)
return W.WEECHAT_RC_ERROR
line_string, _, reason = matches.groups()
line = int(line_string)
if reason:
body = {"reason": reason}
event_id = event_id_from_line(buffer, line)
if not event_id:
message = ("{prefix}matrix: No such message with number "
"{number} found").format(
prefix=W.prefix("error"),
number=line)
W.prnt("", message)
return W.WEECHAT_RC_OK
message = MatrixMessage(
server,
GLOBAL_OPTIONS,
MessageType.REDACT,
data=body,
room_id=room_id,
extra_id=event_id
)
send_or_queue(server, message)
return W.WEECHAT_RC_OK
elif buffer == server.server_buffer:
message = ("{prefix}matrix: command \"redact\" must be "
"executed on a Matrix channel buffer").format(
prefix=W.prefix("error"))
W.prnt("", message)
return W.WEECHAT_RC_OK
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_debug_completion_cb(data, completion_item, buffer, completion):
for debug_type in ["messaging", "network", "timing"]:
W.hook_completion_list_add(
completion,
debug_type,
0,
weechat.WEECHAT_LIST_POS_SORT)
return W.WEECHAT_RC_OK
@utf8_decode
def matrix_message_completion_cb(data, completion_item, buffer, completion):
own_lines = W.hdata_pointer(W.hdata_get('buffer'), buffer, 'own_lines')
if own_lines:
line = W.hdata_pointer(
W.hdata_get('lines'),
own_lines,
'last_line'
)
line_number = 1
while line:
line_data = W.hdata_pointer(
W.hdata_get('line'),
line,
'data'
)
if line_data:
message = W.hdata_string(W.hdata_get('line_data'), line_data,
'message')
tags = tags_from_line_data(line_data)
# Only add non redacted user messages to the completion
if (message
and 'matrix_message' in tags
and 'matrix_redacted' not in tags):
if len(message) > GLOBAL_OPTIONS.redaction_comp_len + 2:
message = (
message[:GLOBAL_OPTIONS.redaction_comp_len]
+ '..')
item = ("{number}:\"{message}\"").format(
number=line_number,
message=message)
W.hook_completion_list_add(
completion,
item,
0,
weechat.WEECHAT_LIST_POS_END)
line_number += 1
line = W.hdata_move(W.hdata_get('line'), line, -1)
return W.WEECHAT_RC_OK
def hook_page_up():
GLOBAL_OPTIONS.page_up_hook = W.hook_command_run(
'/window page_up',
'matrix_command_pgup_cb',
''
)
@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
@ -2576,109 +2098,6 @@ def matrix_bar_item_name(data, item, window, buffer, extra_info):
return "" return ""
def init_hooks():
W.hook_completion(
"matrix_server_commands",
"Matrix server completion",
"server_command_completion_cb",
""
)
W.hook_completion(
"matrix_servers",
"Matrix server completion",
"matrix_server_completion_cb",
""
)
W.hook_completion(
"matrix_commands",
"Matrix command completion",
"matrix_command_completion_cb",
""
)
W.hook_completion(
"matrix_messages",
"Matrix message completion",
"matrix_message_completion_cb",
""
)
W.hook_completion(
"matrix_debug_types",
"Matrix debugging type completion",
"matrix_debug_completion_cb",
""
)
W.hook_command(
# Command name and short description
'matrix', 'Matrix chat protocol command',
# Synopsis
(
'server add <server-name> <hostname>[:<port>] ||'
'server delete|list|listfull <server-name> ||'
'connect <server-name> ||'
'disconnect <server-name> ||'
'reconnect <server-name> ||'
'debug <debug-type> ||'
'help <matrix-command>'
),
# Description
(
' server: list, add, or remove Matrix servers\n'
' connect: connect to Matrix servers\n'
'disconnect: disconnect from one or all Matrix servers\n'
' reconnect: reconnect to server(s)\n\n'
' help: show detailed command help\n\n'
' debug: enable or disable debugging\n\n'
'Use /matrix help [command] to find out more\n'
),
# Completions
(
'server %(matrix_server_commands)|%* ||'
'connect %(matrix_servers) ||'
'disconnect %(matrix_servers) ||'
'reconnect %(matrix_servers) ||'
'debug %(matrix_debug_types) ||'
'help %(matrix_commands)'
),
# Function name
'matrix_command_cb', '')
W.hook_command(
# Command name and short description
'redact', 'redact messages',
# Synopsis
(
'<message-number>[:<"message-part">] [<reason>]'
),
# Description
(
"message-number: number of the message to redact (message numbers"
"\n start from the last recieved as "
"1 and count up)\n"
" message-part: a shortened part of the message\n"
" reason: the redaction reason\n"
),
# Completions
(
'%(matrix_messages)'
),
# Function name
'matrix_redact_command_cb', '')
W.hook_command_run('/topic', 'matrix_command_topic_cb', '')
W.hook_command_run('/buffer clear', 'matrix_command_buf_clear_cb', '')
W.hook_command_run('/join', 'matrix_command_join_cb', '')
W.hook_command_run('/part', 'matrix_command_part_cb', '')
W.hook_command_run('/invite', 'matrix_command_invite_cb', '')
if GLOBAL_OPTIONS.enable_backlog:
hook_page_up()
def autoconnect(servers): def autoconnect(servers):
for server in servers.values(): for server in servers.values():
if server.autoconnect: if server.autoconnect:
@ -2686,8 +2105,6 @@ def autoconnect(servers):
if __name__ == "__main__": if __name__ == "__main__":
W = weechat if sys.hexversion >= 0x3000000 else WeechatWrapper(weechat)
if W.register(WEECHAT_SCRIPT_NAME, if W.register(WEECHAT_SCRIPT_NAME,
WEECHAT_SCRIPT_AUTHOR, WEECHAT_SCRIPT_AUTHOR,
WEECHAT_SCRIPT_VERSION, WEECHAT_SCRIPT_VERSION,
@ -2696,13 +2113,11 @@ if __name__ == "__main__":
'matrix_unload_cb', 'matrix_unload_cb',
''): ''):
GLOBAL_OPTIONS = PluginOptions()
# TODO if this fails we should abort and unload the script. # TODO if this fails we should abort and unload the script.
CONFIG = init_matrix_config() CONFIG = matrix.globals.init_matrix_config()
read_matrix_config() read_matrix_config()
init_hooks() hook_commands()
W.bar_item_new("(extra)buffer_plugin", "matrix_bar_item_plugin", "") W.bar_item_new("(extra)buffer_plugin", "matrix_bar_item_plugin", "")
W.bar_item_new("(extra)buffer_name", "matrix_bar_item_name", "") W.bar_item_new("(extra)buffer_name", "matrix_bar_item_name", "")