Polish for the /matrix command.

This commit is contained in:
poljar (Damir Jelić) 2018-01-06 17:12:54 +01:00
parent 4c8196e2bd
commit d3c18c9e57

View file

@ -7,14 +7,19 @@ import socket
import ssl import ssl
import time import time
# pylint: disable=redefined-builtin
from builtins import bytes from builtins import bytes
from collections import deque, Mapping, Iterable, namedtuple from collections import deque, Mapping, Iterable, namedtuple
from enum import Enum, unique from enum import Enum, unique
from functools import wraps from functools import wraps
# pylint: disable=unused-import
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 http_parser.pyparser import HttpParser from http_parser.pyparser import HttpParser
# pylint: disable=import-error
import weechat import weechat
WEECHAT_SCRIPT_NAME = "matrix" # type: unicode WEECHAT_SCRIPT_NAME = "matrix" # type: unicode
@ -23,11 +28,10 @@ WEECHAT_SCRIPT_AUTHOR = "Damir Jelić <poljar@termina.org.uk>" # type: unic
WEECHAT_SCRIPT_VERSION = "0.1" # type: unicode WEECHAT_SCRIPT_VERSION = "0.1" # type: unicode
WEECHAT_SCRIPT_LICENSE = "MIT" # type: unicode WEECHAT_SCRIPT_LICENSE = "MIT" # type: unicode
SCRIPT_COMMAND = WEECHAT_SCRIPT_NAME # type: unicode
MATRIX_API_PATH = "/_matrix/client/r0" # type: unicode MATRIX_API_PATH = "/_matrix/client/r0" # type: unicode
SERVERS = dict() # type: Dict[unicode, MatrixServer] SERVERS = dict() # type: Dict[unicode, MatrixServer]
CONFIG = None # type: weechat.config
# Unicode handling # Unicode handling
@ -183,10 +187,10 @@ class Matrix:
class MatrixRoom: class MatrixRoom:
def __init__(self, id, join_rule, alias=None): def __init__(self, room_id, join_rule, alias=None):
# type: (unicode, unicode, unicode) -> None # type: (unicode, unicode, unicode) -> None
self.id = id # type: unicode self.room_id = room_id # type: unicode
self.alias = alias # type: unicode self.alias = alias # type: unicode
self.join_rule = join_rule # type: unicode self.join_rule = join_rule # type: unicode
@ -205,10 +209,9 @@ def server_config_change_cb(server_name, option):
break break
if not option_name: if not option_name:
# TODO print error here # TODO print error here, can this happen?
return 0 return 0
# TODO update the changed option in the server class
if option_name == "address": if option_name == "address":
value = W.config_string(option) value = W.config_string(option)
server.address = value server.address = value
@ -231,47 +234,46 @@ def server_config_change_cb(server_name, option):
class MatrixServer: class MatrixServer:
# pylint: disable=too-many-instance-attributes
def __init__(self, name, config_file): def __init__(self, name, config_file):
# type: (unicode, unicode, int) -> None # type: (unicode, weechat.config) -> None
self.name = name # type: unicode self.name = name # type: unicode
self.address = None # type: unicode self.address = "" # type: unicode
self.port = None # type: int self.port = 8448 # type: int
self.options = dict() # type: Dict[unicode, weechat.config] self.options = dict() # type: Dict[unicode, weechat.config]
self.user = "" # type: unicode self.user = "" # type: unicode
self.password = "" # type: unicode self.password = "" # type: unicode
self.buffers = dict() # type: Dict[unicode, weechat.buffer] self.buffers = dict() # type: Dict[unicode, weechat.buffer]
self.server_buffer = None # type: weechat.buffer self.server_buffer = None # type: weechat.buffer
self.fd_hook = None # type: weechat.hook self.fd_hook = None # type: weechat.hook
self.timer_hook = None # type: weechat.hook self.timer_hook = None # type: weechat.hook
self.autoconnect = False # type: bool self.autoconnect = False # type: bool
self.connected = False # type: bool self.connected = False # type: bool
self.connecting = False # type: bool self.connecting = False # type: bool
self.reconnectCount = 0 # type: long self.reconnect_count = 0 # type: int
self.socket = None # type: ssl.SSLSocket self.socket = None # type: ssl.SSLSocket
self.ssl_context = ssl.create_default_context() # type: ssl.SSLContext self.ssl_context = ssl.create_default_context() # type: ssl.SSLContext
self.access_token = None # type: unicode self.access_token = None # type: unicode
self.next_batch = None # type: unicode self.next_batch = None # type: unicode
# TODO this should be made stateless # TODO this should be made stateless
host_string = ':'.join([self.address, host_string = ':'.join([self.address,
str(self.port)]) # type: unicode str(self.port)]) # type: unicode
self.builder = RequestBuilder(host_string) # type: RequestBuilder self.builder = RequestBuilder(host_string) # type: RequestBuilder
self.httpParser = HttpParser() # type: HttpParser self.http_parser = HttpParser() # type: HttpParser
self.httpBodyBuffer = [] # type: List[bytes] self.http_buffer = [] # type: List[bytes]
# Queue of messages we need to send off. # Queue of messages we need to send off.
self.sendQueue = deque() # type: Deque[MatrixMessage] self.send_queue = deque() # type: Deque[MatrixMessage]
# Queue of messages we send off and are waiting a response for # Queue of messages we send off and are waiting a response for
self.recieveQueue = deque() # type: Deque[MatrixMessage] self.receive_queue = deque() # type: Deque[MatrixMessage]
# Queue for messages we got a response of and need to handle self.message_queue = deque() # type: Deque[MatrixMessage]
# TODO is this needed? will we ever deffer message handling?
self.MessageQueue = deque() # type: Deque[MatrixMessage]
self._create_options(config_file) self._create_options(config_file)
@ -284,7 +286,7 @@ class MatrixServer:
self.ssl_context.verify_mode = ssl.CERT_NONE self.ssl_context.verify_mode = ssl.CERT_NONE
def _create_options(self, config_file): def _create_options(self, config_file):
Option = namedtuple( option = namedtuple(
'Option', [ 'Option', [
'name', 'name',
'type', 'type',
@ -296,23 +298,23 @@ class MatrixServer:
]) ])
options = [ options = [
Option( option(
'autoconnect', 'boolean', '', 0, 0, 'off', 'autoconnect', 'boolean', '', 0, 0, 'off',
"Automatically connect to the matrix server when Weechat is starting" "Automatically connect to the matrix server when Weechat is starting"
), ),
Option( option(
'address', 'string', '', 0, 0, '', 'address', 'string', '', 0, 0, '',
"Hostname or IP address for the server" "Hostname or IP address for the server"
), ),
Option( option(
'port', 'integer', '', 0, 65535, '8448', 'port', 'integer', '', 0, 65535, '8448',
"Port for the server" "Port for the server"
), ),
Option( option(
'username', 'string', '', 0, 0, '', 'username', 'string', '', 0, 0, '',
"Username to use on server" "Username to use on server"
), ),
Option( option(
'password', 'string', '', 0, 0, '', 'password', 'string', '', 0, 0, '',
"Password for server" "Password for server"
), ),
@ -324,38 +326,44 @@ class MatrixServer:
option_name = "{server}.{option}".format( option_name = "{server}.{option}".format(
server=self.name, option=option.name) server=self.name, option=option.name)
o = W.config_new_option( self.options[option.name] = W.config_new_option(
config_file, section, option_name, config_file, section, option_name,
option.type, option.description, option.string_values, option.type, option.description, option.string_values,
option.min, option.max, option.value, option.value, 0, "", option.min, option.max, option.value, option.value, 0, "",
"", "server_config_change_cb", self.name, "", "") "", "server_config_change_cb", self.name, "", "")
self.options[option.name] = o
def wrap_socket(server, file_descriptor):
def wrap_socket(server, fd):
# type: (MatrixServer, int) -> socket.socket # type: (MatrixServer, int) -> socket.socket
s = None # type: socket.socket sock = None # type: socket.socket
temp_socket = socket.fromfd(
file_descriptor,
socket.AF_INET,
socket.SOCK_STREAM
)
# TODO explain why these type gymnastics are needed # TODO explain why these type gymnastics are needed
tempSocket = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM) # pylint: disable=protected-access
if isinstance(temp_socket, socket._socket.socket):
if type(tempSocket) == socket._socket.socket: # pylint: disable=no-member
s = socket._socketobject(_sock=tempSocket) sock = socket._socketobject(_sock=temp_socket)
else: else:
s = tempSocket sock = temp_socket
try: try:
ssl_socket = server.ssl_context.wrap_socket(s, ssl_socket = server.ssl_context.wrap_socket(
sock,
server_hostname=server.address) # type: ssl.SSLSocket server_hostname=server.address) # type: ssl.SSLSocket
return ssl_socket return ssl_socket
# TODO add the other relevant exceptions # TODO add the other relevant exceptions
except ssl.SSLError as e: except ssl.SSLError as error:
server_buffer_prnt(server, str(e)) server_buffer_prnt(server, str(error))
return None return None
def handleHttpResponse(server, message): def handle_http_response(server, message):
# type: (MatrixServer, MatrixMessage) -> None # type: (MatrixServer, MatrixMessage) -> None
status_code = message.response.status status_code = message.response.status
@ -365,9 +373,13 @@ def handleHttpResponse(server, message):
if status_code == 200: if status_code == 200:
# TODO json.loads can fail # TODO json.loads can fail
response = json.loads(message.response.body, encoding='utf-8') response = json.loads(message.response.body, encoding='utf-8')
handleMatrixMessage(server, message.type, response) handle_matrix_message(server, message.type, response)
else: else:
server_buffer_prnt(server, "ERROR IN HTTP RESPONSE {status_code}".format(status_code=status_code)) server_buffer_prnt(
server,
"ERROR IN HTTP RESPONSE {status_code}".format(
status_code=status_code))
server_buffer_prnt(server, message.request.request) server_buffer_prnt(server, message.request.request)
server_buffer_prnt(server, message.response.body) server_buffer_prnt(server, message.response.body)
@ -401,7 +413,7 @@ def handle_room_info(server, room_info):
W.buffer_set(buf, "localvar_set_server", "matrix.org") W.buffer_set(buf, "localvar_set_server", "matrix.org")
# TODO put this in a function # TODO put this in a function
short_name = name=alias.rsplit(":", 1)[0] short_name = alias.rsplit(":", 1)[0]
W.buffer_set(buf, "short_name", short_name) W.buffer_set(buf, "short_name", short_name)
server.buffers[room_id] = buf server.buffers[room_id] = buf
@ -467,15 +479,15 @@ def handle_room_info(server, room_info):
event_id = "matrix_id_{id}".format(id=event_id) event_id = "matrix_id_{id}".format(id=event_id)
msg_age = event['unsigned']['age'] msg_age = event['unsigned']['age']
t = time.time() now = time.time()
t = int(t - (msg_age / 1000)) msg_date = int(now - (msg_age / 1000))
buf = server.buffers[room_id] buf = server.buffers[room_id]
# TODO if this is an initial sync tag the messages as backlog # TODO if this is an initial sync tag the messages as backlog
tag = "nick_{a},{event_id},irc_privmsg,notify_message".format( tag = "nick_{a},{event_id},irc_privmsg,notify_message".format(
a=msg_author, event_id=event_id) a=msg_author, event_id=event_id)
W.prnt_date_tags(buf, t, tag, data) W.prnt_date_tags(buf, msg_date, tag, data)
for room_id, room in room_info['join'].iteritems(): for room_id, room in room_info['join'].iteritems():
# TODO do we need these queues or can we just rename the buffer if and # TODO do we need these queues or can we just rename the buffer if and
@ -497,7 +509,7 @@ def handle_room_info(server, room_info):
if event['type'] == 'm.room.member': if event['type'] == 'm.room.member':
handle_members(room_id, event) handle_members(room_id, event)
else: else:
assert("Wrong event type in event queue") assert "Wrong event type in event queue"
while message_queue: while message_queue:
event = message_queue.popleft() event = message_queue.popleft()
@ -513,38 +525,42 @@ def handle_room_info(server, room_info):
else: else:
server_buffer_prnt( server_buffer_prnt(
server, server,
"Handling of content type {type} not implemented".format(type=event['content']['type']) "Handling of content type {type} not implemented".format(
type=event['content']['type'])
) )
def handleMatrixMessage(server, messageType, matrixResponse): def handle_matrix_message(server, message_type, response):
# type: (MatrixServer, MessageType, Dict[Any, Any]) -> None # type: (MatrixServer, MessageType, Dict[Any, Any]) -> None
if messageType is MessageType.LOGIN: if message_type is MessageType.LOGIN:
server.access_token = matrixResponse["access_token"] server.access_token = response["access_token"]
message = generate_matrix_request(server, MessageType.SYNC) message = generate_matrix_request(server, MessageType.SYNC)
send_or_queue(server, message) send_or_queue(server, message)
elif messageType is messageType.SYNC: elif message_type is MessageType.SYNC:
next_batch = matrixResponse['next_batch'] next_batch = response['next_batch']
# we got the same batch again, nothing to do # we got the same batch again, nothing to do
if next_batch == server.next_batch: if next_batch == server.next_batch:
return return
room_info = matrixResponse['rooms'] room_info = response['rooms']
handle_room_info(server, room_info) handle_room_info(server, room_info)
server.next_batch = next_batch server.next_batch = next_batch
else: else:
server_buffer_prnt(server, "Handling of message type {type} not implemented".format(type=messageType)) server_buffer_prnt(
server,
"Handling of message type {type} not implemented".format(
type=message_type))
def generate_matrix_request(server, type, room_id=None, data=None): def generate_matrix_request(server, message_type, room_id=None, data=None):
# type: (MatrixServer, MessageType, unicode, Dict[Any, Any]) -> MatrixMessage # type: (MatrixServer, MessageType, unicode, Dict[Any, Any]) -> MatrixMessage
# TODO clean this up # TODO clean this up
if type == MessageType.LOGIN: if message_type == MessageType.LOGIN:
path = '/_matrix/client/r0/login' path = '/_matrix/client/r0/login'
post_data = {"type": "m.login.password", post_data = {"type": "m.login.password",
"user": server.user, "user": server.user,
@ -554,24 +570,26 @@ def generate_matrix_request(server, type, room_id=None, data=None):
return MatrixMessage(MessageType.LOGIN, request, None) return MatrixMessage(MessageType.LOGIN, request, None)
elif type == MessageType.SYNC: elif message_type == MessageType.SYNC:
path = '/_matrix/client/r0/sync?access_token={access_token}'.format(access_token=server.access_token) path = '/_matrix/client/r0/sync?access_token={access_token}'.format(
access_token=server.access_token)
if server.next_batch: if server.next_batch:
path = path + '&since={next_batch}'.format(next_batch=server.next_batch) path = path + '&since={next_batch}'.format(
next_batch=server.next_batch)
request = server.builder.request(path) request = server.builder.request(path)
return MatrixMessage(MessageType.SYNC, request, None) return MatrixMessage(MessageType.SYNC, request, None)
elif type == MessageType.POST_MSG: elif message_type == MessageType.POST_MSG:
path = '/_matrix/client/r0/rooms/{room}/send/m.room.message?access_token={access_token}'.format(room=room_id, access_token=server.access_token) path = '/_matrix/client/r0/rooms/{room}/send/m.room.message?access_token={access_token}'.format(room=room_id, access_token=server.access_token)
request = server.builder.request(path, data) request = server.builder.request(path, data)
return MatrixMessage(MessageType.POST_MSG, request, None) return MatrixMessage(MessageType.POST_MSG, request, None)
else: else:
assert("Incorrect message type") assert "Incorrect message type"
return None return None
@ -584,7 +602,7 @@ def matrix_login(server):
def send_or_queue(server, message): def send_or_queue(server, message):
# type: (MatrixServer, MatrixMessage) -> None # type: (MatrixServer, MatrixMessage) -> None
if not send(server, message): if not send(server, message):
server.sendQueue.append(message) server.send_queue.append(message)
def send(server, message): def send(server, message):
@ -598,23 +616,22 @@ def send(server, message):
if payload: if payload:
server.socket.sendall(bytes(payload, 'utf-8')) server.socket.sendall(bytes(payload, 'utf-8'))
server.recieveQueue.append(message) server.receive_queue.append(message)
return True return True
except socket.error as e: except socket.error as error:
disconnect(server) disconnect(server)
server_buffer_prnt(server, str(e)) server_buffer_prnt(server, str(error))
return False return False
@utf8_decode @utf8_decode
def recieve_cb(server_name, fd): def receive_cb(server_name, file_descriptor):
server = SERVERS[server_name] server = SERVERS[server_name]
if not server.connected: if not server.connected:
server_buffer_prnt(server, "NOT CONNECTED WHILE RECEIVING") server_buffer_prnt(server, "NOT CONNECTED WHILE RECEIVING")
# can this happen? # can this happen?
# do reconnection # do reconnection
pass
while True: while True:
try: try:
@ -622,42 +639,42 @@ def recieve_cb(server_name, fd):
# TODO add the other relevant exceptions # TODO add the other relevant exceptions
except ssl.SSLWantReadError: except ssl.SSLWantReadError:
break break
except socket.error as e: except socket.error as error:
disconnect(server) disconnect(server)
# Queue the failed message for resending # Queue the failed message for resending
message = server.recieveQueue.popleft() message = server.receive_queue.popleft()
server.sendQueue.appendleft(message) server.send_queue.appendleft(message)
server_buffer_prnt(server, e) server_buffer_prnt(server, error)
return return W.WEECHAT_RC_OK
if not data: if not data:
server_buffer_prnt(server, "No data while reading") server_buffer_prnt(server, "No data while reading")
disconnect(server) disconnect(server)
break break
recieved = len(data) # type: int received = len(data) # type: int
nParsed = server.httpParser.execute(data, recieved) parsed_bytes = server.http_parser.execute(data, received)
assert nParsed == recieved assert parsed_bytes == received
if server.httpParser.is_partial_body(): if server.http_parser.is_partial_body():
server.httpBodyBuffer.append(server.httpParser.recv_body()) server.http_buffer.append(server.http_parser.recv_body())
if server.httpParser.is_message_complete(): if server.http_parser.is_message_complete():
status = server.httpParser.get_status_code() status = server.http_parser.get_status_code()
headers = server.httpParser.get_headers() headers = server.http_parser.get_headers()
body = b"".join(server.httpBodyBuffer) body = b"".join(server.http_buffer)
message = server.recieveQueue.popleft() message = server.receive_queue.popleft()
message.response = HttpResponse(status, headers, body) message.response = HttpResponse(status, headers, body)
# Message done, reset the parser state. # Message done, reset the parser state.
server.httpParser = HttpParser() server.http_parser = HttpParser()
server.httpBodyBuffer = [] server.http_buffer = []
handleHttpResponse(server, message) handle_http_response(server, message)
break break
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@ -677,10 +694,10 @@ def disconnect(server):
def server_buffer_prnt(server, string): def server_buffer_prnt(server, string):
# type: (MatrixServer, unicode) -> None # type: (MatrixServer, unicode) -> None
assert(server.server_buffer) assert server.server_buffer
b = server.server_buffer buffer = server.server_buffer
t = int(time.time()) now = int(time.time())
W.prnt_date_tags(b, t, "", string) W.prnt_date_tags(buffer, now, "", string)
def create_server_buffer(server): def create_server_buffer(server):
@ -708,18 +725,23 @@ def create_server_buffer(server):
# socket creation # socket creation
@utf8_decode @utf8_decode
def connect_cb(data, status, gnutls_rc, sock, error, ip_address): def connect_cb(data, status, gnutls_rc, sock, error, ip_address):
# pylint: disable=too-many-arguments
status_value = int(status) # type: long status_value = int(status) # type: long
server = SERVERS[data] server = SERVERS[data]
print(server.name)
# pylint: disable=too-many-branches
if status_value == W.WEECHAT_HOOK_CONNECT_OK: if status_value == W.WEECHAT_HOOK_CONNECT_OK:
fd = int(sock) # type: int file_descriptor = int(sock) # type: int
socket = wrap_socket(server, fd) sock = wrap_socket(server, file_descriptor)
if socket: if sock:
server.socket = socket server.socket = sock
fd = server.socket.fileno() hook = W.hook_fd(
hook = W.hook_fd(fd, 1, 0, 0, "recieve_cb", server.name) server.socket.fileno(),
1, 0, 0,
"receive_cb",
server.name
)
server.fd_hook = hook server.fd_hook = hook
server.connected = True server.connected = True
@ -770,15 +792,18 @@ def connect_cb(data, status, gnutls_rc, sock, error, ip_address):
def reconnect(server): def reconnect(server):
# type: (MatrixServer) -> None # type: (MatrixServer) -> None
timeout = server.reconnectCount * 5 * 1000 timeout = server.reconnect_count * 5 * 1000
if timeout > 0: if timeout > 0:
server_buffer_prnt(server, "Reconnecting in {timeout} seconds.".format(timeout=timeout / 1000)) server_buffer_prnt(
server,
"Reconnecting in {timeout} seconds.".format(
timeout=timeout / 1000))
W.hook_timer(timeout, 0, 1, "reconnect_cb", server.name) W.hook_timer(timeout, 0, 1, "reconnect_cb", server.name)
else: else:
connect(server) connect(server)
server.reconnectCount += 1 server.reconnect_count += 1
@utf8_decode @utf8_decode
@ -853,23 +878,25 @@ def matrix_timer_cb(server_name, remaining_calls):
reconnect(server) reconnect(server)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
while server.sendQueue: while server.send_queue:
message = server.sendQueue.popleft() message = server.send_queue.popleft()
if not send(server, message): if not send(server, message):
# We got an error while sending the last message return the message # We got an error while sending the last message return the message
# to the queue and exit the loop # to the queue and exit the loop
server.sendQueue.appendleft(message) server.send_queue.appendleft(message)
break break
for message in server.MessageQueue: for message in server.message_queue:
server_buffer_prnt(server, "Handling message: {message}".format(message=message)) server_buffer_prnt(
server,
"Handling message: {message}".format(message=message))
# TODO don't send this out here, if a SYNC fails for some reason (504 try # TODO don't send this out here, if a SYNC fails for some reason (504 try
# again!) we'll hammer the server unnecessarily # again!) we'll hammer the server unnecessarily
if server.next_batch: if server.next_batch:
message = generate_matrix_request(server, MessageType.SYNC) message = generate_matrix_request(server, MessageType.SYNC)
server.sendQueue.append(message) server.send_queue.append(message)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
@ -878,13 +905,14 @@ def matrix_timer_cb(server_name, remaining_calls):
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
@utf8_decode @utf8_decode
def matrix_config_server_read_cb( def matrix_config_server_read_cb(
data, config_file, section, data, config_file, section,
option_name, value option_name, value
): ):
rc = 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)
@ -898,11 +926,11 @@ def matrix_config_server_read_cb(
# Ignore invalid options # Ignore invalid options
if option in server.options: if option in server.options:
rc = W.config_option_set(server.options[option], value, 1) return_code = W.config_option_set(server.options[option], value, 1)
# TODO print out error message in case of erroneous rc # TODO print out error message in case of erroneous return_code
return rc return return_code
@utf8_decode @utf8_decode
@ -922,21 +950,21 @@ def init_matrix_config():
config_file = W.config_new("matrix", "matrix_config_reload_cb", "") config_file = W.config_new("matrix", "matrix_config_reload_cb", "")
section = W.config_new_section(config_file, "color", 0, 0, "", "", "", "", section = W.config_new_section(config_file, "color", 0, 0, "", "", "", "",
"", "", "", "", "", "") "", "", "", "", "", "")
# TODO color options # TODO color options
section = W.config_new_section(config_file, "look", 0, 0, "", "", "", "", section = W.config_new_section(config_file, "look", 0, 0, "", "", "", "",
"", "", "", "", "", "") "", "", "", "", "", "")
# TODO look options # TODO look options
section = W.config_new_section(config_file, "network", 0, 0, "", "", "", "", section = W.config_new_section(config_file, "network", 0, 0, "", "", "",
"", "", "", "", "", "") "", "", "", "", "", "", "")
# TODO network options # TODO network options
section = W.config_new_section( W.config_new_section(
config_file, "server", config_file, "server",
0, 0, 0, 0,
"matrix_config_server_read_cb", "matrix_config_server_read_cb",
@ -950,12 +978,12 @@ def init_matrix_config():
def read_matrix_config(): def read_matrix_config():
# type: () -> bool # type: () -> bool
rc = W.config_read(CONFIG) return_code = W.config_read(CONFIG)
if rc == weechat.WEECHAT_CONFIG_READ_OK: if return_code == weechat.WEECHAT_CONFIG_READ_OK:
return True return True
elif rc == weechat.WEECHAT_CONFIG_READ_MEMORY_ERROR: elif return_code == weechat.WEECHAT_CONFIG_READ_MEMORY_ERROR:
return False return False
elif rc == weechat.WEECHAT_CONFIG_READ_FILE_NOT_FOUND: elif return_code == weechat.WEECHAT_CONFIG_READ_FILE_NOT_FOUND:
return True return True
else: else:
return False return False
@ -964,26 +992,28 @@ def read_matrix_config():
@utf8_decode @utf8_decode
def matrix_unload_cb(): def matrix_unload_cb():
for section in ["network", "look", "color", "server"]: for section in ["network", "look", "color", "server"]:
s = W.config_search_section(CONFIG, section) section_pointer = W.config_search_section(CONFIG, section)
W.config_section_free_options(s) W.config_section_free_options(section_pointer)
W.config_section_free(s) W.config_section_free(section_pointer)
W.config_free(CONFIG) W.config_free(CONFIG)
return W.WEECHAT_RC_OK return W.WEECHAT_RC_OK
def get_boolean(config, section, key):
s = W.config_search_section(config, section)
option = W.config_search_option(config, s, key)
return W.config_boolean(option)
@utf8_decode @utf8_decode
def matrix_server_command_cb(data, buffer, args): def matrix_server_command_cb(data, buffer, args):
a = args.split(' ', 2) def connect_server(servers):
# TODO check if the server exists
for server_name in servers:
server = SERVERS[server_name]
connect(server)
command, args = a[0], a[1:] def disconnect_server(servers):
# TODO check if the server exists
for server_name in servers:
server = SERVERS[server_name]
disconnect(server)
def list_servers(): def list_servers():
if SERVERS: if SERVERS:
@ -994,16 +1024,35 @@ def matrix_server_command_cb(data, buffer, args):
server=server server=server
)) ))
# TODO
def list_full_servers(servers):
W.prnt("", "\nCommand not implemented")
# TODO
def delete_server(servers):
W.prnt("", "\nCommand not implemented")
split_args = args.split(' ', 2)
command, args = split_args[0], split_args[1:]
if command == 'connect': if command == 'connect':
for server_name in args: connect_server(args)
server = SERVERS[server_name]
connect(server) elif command == 'disconnect':
disconnect_server(args)
elif command == 'reconnect':
disconnect_server(args)
connect_server(args)
elif command == 'server': elif command == 'server':
subcommand, args = args[0], args[1:] subcommand, args = args[0], args[1:]
if subcommand == 'list': if subcommand == 'list':
list_servers() list_servers()
if subcommand == 'listfull':
list_full_servers(args)
elif subcommand == 'add': elif subcommand == 'add':
# TODO allow setting the address and port # TODO allow setting the address and port
SERVERS[args[0]] = MatrixServer(args[0], CONFIG) SERVERS[args[0]] = MatrixServer(args[0], CONFIG)
@ -1026,11 +1075,11 @@ def add_servers_to_completion(completion):
@utf8_decode @utf8_decode
def matrix_server_command_completion_cb(data, completion_item, buffer, completion): def server_command_completion_cb(data, completion_item, buffer, completion):
input = weechat.buffer_get_string(buffer, "input").split() buffer_input = weechat.buffer_get_string(buffer, "input").split()
args = input[1:] args = buffer_input[1:]
commands = ['add', 'delete', 'list'] commands = ['add', 'delete', 'list', 'listfull', 'rename']
def complete_commands(): def complete_commands():
for command in commands: for command in commands:
@ -1048,11 +1097,11 @@ def matrix_server_command_completion_cb(data, completion_item, buffer, completio
if args[1] not in commands: if args[1] not in commands:
complete_commands() complete_commands()
else: else:
if args[1] == 'delete': 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': if args[1] == 'delete' or args[1] == 'listfull':
if args[2] not in SERVERS.keys(): if args[2] not in SERVERS.keys():
add_servers_to_completion(completion) add_servers_to_completion(completion)
@ -1073,6 +1122,46 @@ def create_default_server(config_file):
return True return True
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_command(
# Command name and short description
'matrix', 'Matrix chat protocol command',
# Synopsis
'server add <server-name> <hostname>[:<port>] ||' +
'server delete|list|listfull <server-name> ||' +
'server rename <server-name> <new-name> ||' +
'connect <server-name> ||' +
'disconnect <server-name> ||' +
'reconnect <server-name>',
# Description
' server: list, add, remove, or rename Matrix servers' + '\n' +
' connect: connect to Matrix servers' + '\n' +
'disconnect: disconnect from one or all Matrix servers' + '\n' +
' reconnect: reconnect to server(s)' + '\n' +
'\nUse /matrix help [command] to find out more\n',
# Completions
'server %(matrix_server_commands)|%* ||' +
'connect %(matrix_servers) ||' +
'disconnect %(matrix_servers) ||' +
'reconnect %(matrix_servers)',
# Function name
'matrix_server_command_cb', '')
def autoconnect(servers):
for server in servers.values():
if server.autoconnect:
connect(server)
if __name__ == "__main__": if __name__ == "__main__":
W = WeechatWrapper(weechat) W = WeechatWrapper(weechat)
@ -1088,36 +1177,10 @@ if __name__ == "__main__":
CONFIG = init_matrix_config() CONFIG = init_matrix_config()
read_matrix_config() read_matrix_config()
init_hooks()
# TODO this can't be here # TODO this can't be here
if (len(SERVERS) == 0): if not SERVERS:
create_default_server(CONFIG) create_default_server(CONFIG)
subcommands = ['connect', 'disconnect', 'list'] autoconnect(SERVERS)
W.hook_completion(
"matrix_server_commands", "Matrix server completion",
"matrix_server_command_completion_cb", "")
W.hook_completion(
"matrix_servers", "Matrix server completion",
"matrix_server_completion_cb", "")
# TODO implement help
# TODO we want a better description
W.hook_command(
# Command name and description
'matrix', 'Matrix chat protocol',
# Usage
'[command] [command options]',
# Description of arguments
'Commands:\n' +
'\n'.join(subcommands) +
'\nUse /matrix help [command] to find out more\n',
# Completions
'server %(matrix_server_commands)|%* || connect %(matrix_servers)',
# Function name
'matrix_server_command_cb', '')
for server in SERVERS.values():
if server.autoconnect:
connect(server)