weechat-matrix/matrix/server.py

801 lines
26 KiB
Python
Raw Normal View History

2018-01-26 14:38:46 +01:00
# -*- coding: utf-8 -*-
# Copyright © 2018 Damir Jelić <poljar@termina.org.uk>
#
# Permission to use, copy, modify, and/or distribute this software for
# any purpose with or without fee is hereby granted, provided that the
# above copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
# RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
# CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2018-01-26 14:38:46 +01:00
from __future__ import unicode_literals
import os
2018-01-26 14:38:46 +01:00
import ssl
import socket
import time
import pprint
2018-01-26 14:38:46 +01:00
from collections import deque, defaultdict
2018-01-26 14:38:46 +01:00
2018-07-26 11:33:05 +02:00
from nio import (
HttpClient,
LoginResponse,
SyncRepsponse,
RoomSendResponse,
2018-08-07 19:07:54 +02:00
RoomPutStateResponse,
2018-07-26 11:33:05 +02:00
TransportResponse,
TransportType,
2018-07-26 11:33:05 +02:00
LocalProtocolError
)
2018-07-20 15:53:47 +02:00
2018-01-30 11:46:29 +01:00
from matrix.plugin_options import Option, DebugType
2018-02-21 17:00:11 +01:00
from matrix.utils import (key_from_value, prnt_debug, server_buffer_prnt,
2018-07-20 19:14:32 +02:00
create_server_buffer)
from matrix.utf import utf8_decode
from matrix.globals import W, SERVERS, SCRIPT_NAME, OPTIONS
2018-07-20 17:51:48 +02:00
from .buffer import RoomBuffer, OwnMessage, OwnAction
try:
FileNotFoundError
except NameError:
FileNotFoundError = IOError
2018-01-26 14:38:46 +01:00
class ServerConfig(object):
_section_name = "{}.{}".format(SCRIPT_NAME, "server")
def __init__(self, server_name, config_ptr):
# type: (str, str) -> None
self._server_name = server_name
self._ptr = config_ptr
self.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(
'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')
for option in options:
option_name = "{server}.{option}".format(
server=self._server_name, option=option.name)
self.options[option.name] = W.config_new_option(
config_ptr, section, option_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, "", "")
def _get_str_option(self, option_name):
return W.config_string(self.options[option_name])
def _get_bool_option(self, option_name):
return bool(W.config_boolean(self.options[option_name]))
@property
def config_section(self):
# type: () -> str
return "{}.{}".format(self._server_name, self._server_name)
@property
def autoconnect(self):
# type: () -> bool
return self._get_bool_option("autoconnect")
@property
def address(self):
# type: () -> str
return self._get_str_option("address")
@property
def port(self):
# type: () -> int
return W.config_integer(self.options["port"])
@property
def proxy(self):
# type: () -> str
return self._get_str_option("proxy")
@property
def ssl_verify(self):
# type: () -> bool
return self._get_bool_option("ssl_verify")
@property
def username(self):
# type: () -> str
return self._get_str_option("username")
@property
def password(self):
# type: () -> str
return W.string_eval_expression(
self._get_str_option("password"),
{},
{},
{}
)
@property
def device_name(self):
# type: () -> str
return self._get_str_option("device_name")
class MatrixServer(object):
2018-01-26 14:38:46 +01:00
# pylint: disable=too-many-instance-attributes
def __init__(self, name, config_file):
2018-02-12 10:56:28 +01:00
# type: (str, weechat.config) -> None
2018-02-21 17:00:11 +01:00
# yapf: disable
2018-01-27 16:21:10 +01:00
self.name = name # type: str
self.user_id = ""
self.device_id = "" # type: str
self.olm = None # type: Olm
self.encryption_queue = defaultdict(deque)
2018-01-27 16:21:10 +01:00
self.room_buffers = dict() # type: Dict[str, WeechatChannelBuffer]
2018-01-27 16:21:10 +01:00
self.buffers = dict() # type: Dict[str, weechat.buffer]
self.server_buffer = None # type: weechat.buffer
self.fd_hook = None # type: weechat.hook
2018-01-30 19:29:03 +01:00
self.ssl_hook = None # type: weechat.hook
2018-01-27 16:21:10 +01:00
self.timer_hook = None # type: weechat.hook
self.numeric_address = "" # type: str
self.connected = False # type: bool
self.connecting = False # type: bool
self.reconnect_delay = 0 # type: int
self.reconnect_time = None # type: float
self.sync_time = None # type: Optional[float]
self.socket = None # type: ssl.SSLSocket
2018-01-27 16:21:10 +01:00
self.ssl_context = ssl.create_default_context() # type: ssl.SSLContext
self.transport_type = None # type: Optional[nio.TransportType]
2018-01-27 16:21:10 +01:00
2018-07-21 15:15:48 +02:00
# Enable http2 negotiation on the ssl context.
self.ssl_context.set_alpn_protocols(["h2", "http/1.1"])
try:
self.ssl_context.set_npn_protocols(["h2", "http/1.1"])
except NotImplementedError:
pass
self.client = None
2018-01-27 16:21:10 +01:00
self.access_token = None # type: str
self.next_batch = None # type: str
self.transaction_id = 0 # type: int
2018-01-30 12:58:16 +01:00
self.lag = 0 # type: int
2018-02-08 10:58:33 +01:00
self.lag_done = False # type: bool
2018-01-26 14:38:46 +01:00
2018-01-31 13:55:55 +01:00
self.send_fd_hook = None # type: weechat.hook
self.send_buffer = b"" # type: bytes
self.device_check_timestamp = None
2018-01-31 13:55:55 +01:00
2018-07-21 15:15:48 +02:00
self.send_queue = deque()
self.own_message_queue = dict() # type: Dict[OwnMessage]
2018-07-21 15:15:48 +02:00
self.event_queue_timer = None
self.event_queue = deque() # type: Deque[RoomInfo]
2018-01-26 14:38:46 +01:00
# self._create_options(config_file)
self.config = ServerConfig(self.name, config_file)
self._create_session_dir()
2018-02-21 17:00:11 +01:00
# yapf: enable
2018-01-26 14:38:46 +01:00
def _create_session_dir(self):
path = os.path.join("matrix", self.name)
if not W.mkdir_home(path, 0o700):
message = ("{prefix}matrix: Error creating server session "
"directory").format(prefix=W.prefix("error"))
W.prnt("", message)
2018-03-04 17:19:10 +01:00
def get_session_path(self):
home_dir = W.info_get('weechat_dir', '')
return os.path.join(home_dir, "matrix", self.name)
2018-03-04 17:19:10 +01:00
def _load_device_id(self):
file_name = "{}{}".format(self.config.username, ".device_id")
2018-03-04 17:19:10 +01:00
path = os.path.join(self.get_session_path(), file_name)
if not os.path.isfile(path):
return
with open(path, 'r') as f:
device_id = f.readline().rstrip()
if device_id:
self.device_id = device_id
def save_device_id(self):
file_name = "{}{}".format(self.config.username, ".device_id")
2018-03-04 17:19:10 +01:00
path = os.path.join(self.get_session_path(), file_name)
with open(path, 'w') as f:
f.write(self.device_id)
def _change_client(self):
host = ':'.join([self.config.address, str(self.config.port)])
self.client = HttpClient(host, self.config.username)
def update_option(self, option, option_name):
2018-01-26 14:48:34 +01:00
if option_name == "address":
self._change_client()
2018-01-26 14:48:34 +01:00
elif option_name == "port":
self._change_client()
2018-01-26 14:48:34 +01:00
elif option_name == "ssl_verify":
value = W.config_boolean(option)
if value:
self.ssl_context.verify_mode = ssl.CERT_REQUIRED
2018-01-27 14:40:01 +01:00
self.ssl_context.check_hostname = True
2018-01-26 14:48:34 +01:00
else:
self.ssl_context.check_hostname = False
self.ssl_context.verify_mode = ssl.CERT_NONE
elif option_name == "username":
value = W.config_string(option)
self.access_token = ""
2018-03-04 17:19:10 +01:00
2018-07-20 19:14:32 +02:00
if self.client:
self.client.user = value
2018-01-26 14:48:34 +01:00
else:
pass
2018-07-21 15:15:48 +02:00
def send_or_queue(self, request):
# type: (bytes) -> None
if not self.send(request):
self.send_queue.append(request)
def try_send(self, message):
# type: (MatrixServer, bytes) -> bool
sock = self.socket
total_sent = 0
message_length = len(message)
while total_sent < message_length:
try:
sent = sock.send(message[total_sent:])
except ssl.SSLWantWriteError:
2018-02-21 17:00:11 +01:00
hook = W.hook_fd(sock.fileno(), 0, 1, 0, "send_cb", self.name)
self.send_fd_hook = hook
self.send_buffer = message[total_sent:]
return True
except socket.error as error:
2018-02-02 10:36:54 +01:00
self._abort_send()
errno = "error" + str(error.errno) + " " if error.errno else ""
strerr = error.strerror if error.strerror else "Unknown reason"
strerr = errno + strerr
2018-02-12 10:56:28 +01:00
error_message = ("{prefix}Error while writing to "
"socket: {error}").format(
2018-02-21 17:00:11 +01:00
prefix=W.prefix("network"), error=strerr)
2018-02-12 10:56:28 +01:00
server_buffer_prnt(self, error_message)
server_buffer_prnt(
2018-02-21 17:00:11 +01:00
self, ("{prefix}matrix: disconnecting from server..."
).format(prefix=W.prefix("network")))
self.disconnect()
return False
if sent == 0:
2018-02-02 10:36:54 +01:00
self._abort_send()
server_buffer_prnt(
self,
"{prefix}matrix: Error while writing to socket".format(
2018-02-07 12:42:33 +01:00
prefix=W.prefix("network")))
server_buffer_prnt(
2018-02-21 17:00:11 +01:00
self, ("{prefix}matrix: disconnecting from server..."
).format(prefix=W.prefix("network")))
self.disconnect()
return False
total_sent = total_sent + sent
2018-02-02 10:36:54 +01:00
self._finalize_send()
return True
2018-02-02 10:36:54 +01:00
def _abort_send(self):
self.current_message = None
self.send_buffer = ""
2018-02-02 10:36:54 +01:00
def _finalize_send(self):
# type: (MatrixServer) -> None
2018-02-12 10:56:28 +01:00
self.send_buffer = b""
def error(self, message):
buf = ""
if self.server_buffer:
buf = self.server_buffer
2018-07-26 11:33:05 +02:00
msg = "{}{}: {}".format(W.prefix("network"), SCRIPT_NAME, message)
W.prnt(buf, msg)
2018-07-20 19:14:32 +02:00
def send(self, data):
# type: (bytes) -> bool
self.try_send(data)
return True
def reconnect(self):
2018-02-21 17:00:11 +01:00
message = ("{prefix}matrix: reconnecting to server..."
2018-07-20 19:14:32 +02:00
).format(prefix=W.prefix("network"))
server_buffer_prnt(self, message)
self.reconnect_time = None
if not self.connect():
self.schedule_reconnect()
def schedule_reconnect(self):
# type: (MatrixServer) -> None
self.connecting = True
self.reconnect_time = time.time()
if self.reconnect_delay:
self.reconnect_delay = self.reconnect_delay * 2
else:
self.reconnect_delay = 10
message = ("{prefix}matrix: reconnecting to server in {t} "
"seconds").format(
2018-02-21 17:00:11 +01:00
prefix=W.prefix("network"), t=self.reconnect_delay)
server_buffer_prnt(self, message)
def _close_socket(self):
# type: () -> None
if self.socket:
try:
self.socket.shutdown(socket.SHUT_RDWR)
self.socket.close()
except socket.error:
pass
def disconnect(self, reconnect=True):
2018-02-12 10:56:28 +01:00
# type: (bool) -> None
if self.fd_hook:
W.unhook(self.fd_hook)
self._close_socket()
self.fd_hook = None
self.socket = None
self.connected = False
self.access_token = ""
self.send_buffer = b""
self.current_message = None
self.transport_type = None
2018-07-26 11:33:05 +02:00
try:
self.client.disconnect()
except LocalProtocolError:
pass
2018-02-13 10:14:43 +01:00
self.lag = 0
W.bar_item_update("lag")
self.reconnect_delay = 0
self.reconnect_time = None
if self.server_buffer:
2018-02-21 17:00:11 +01:00
message = ("{prefix}matrix: disconnected from server"
).format(prefix=W.prefix("network"))
server_buffer_prnt(self, message)
if reconnect:
self.schedule_reconnect()
def connect(self):
# type: (MatrixServer) -> int
if not self.config.address or not self.config.port:
W.prnt("", self.config.address)
message = "{prefix}Server address or port not set".format(
prefix=W.prefix("error"))
W.prnt("", message)
return False
if not self.config.username or not self.config.password:
message = "{prefix}User or password not set".format(
prefix=W.prefix("error"))
W.prnt("", message)
return False
if self.connected:
return True
if not self.server_buffer:
create_server_buffer(self)
if not self.timer_hook:
self.timer_hook = W.hook_timer(1 * 1000, 0, 0, "matrix_timer_cb",
2018-02-21 17:00:11 +01:00
self.name)
ssl_message = " (SSL)" if self.ssl_context.check_hostname else ""
message = ("{prefix}matrix: Connecting to "
"{server}:{port}{ssl}...").format(
2018-02-07 12:42:33 +01:00
prefix=W.prefix("network"),
server=self.config.address,
port=self.config.port,
2018-02-07 12:42:33 +01:00
ssl=ssl_message)
W.prnt(self.server_buffer, message)
W.hook_connect(self.config.proxy,
self.config.address, self.config.port,
2018-03-03 23:47:44 +01:00
1, 0, "", "connect_cb",
2018-02-21 17:00:11 +01:00
self.name)
return True
def schedule_sync(self):
self.sync_time = time.time()
def sync(self, timeout=None, filter=None):
2018-08-03 12:25:17 +02:00
# type: Optional[int] -> None
self.sync_time = None
_, request = self.client.sync(timeout, filter)
self.send_or_queue(request)
def login(self):
2018-07-20 19:14:32 +02:00
# type: () -> None
_, request = self.client.login(self.config.password)
2018-07-20 19:14:32 +02:00
self.send_or_queue(request)
msg = "{prefix}matrix: Logging in...".format(
prefix=W.prefix("network")
)
2018-02-27 19:55:30 +01:00
W.prnt(self.server_buffer, msg)
2018-08-07 19:07:54 +02:00
def room_send_state(self, room_buffer, body, event_type):
if room_buffer.room.encrypted:
return
2018-08-07 19:07:54 +02:00
_, request = self.client.room_put_state(
room_buffer.room.room_id,
event_type,
body
)
self.send_or_queue(request)
2018-08-07 16:48:18 +02:00
def room_send_message(self, room_buffer, formatted, msgtype="m.text"):
# type: (RoomBuffer, Formatted) -> None
if room_buffer.room.encrypted:
return
2018-08-07 16:48:18 +02:00
if msgtype == "m.emote":
message_class = OwnAction
else:
message_class = OwnMessage
own_message = message_class(
self.user_id,
0,
"",
room_buffer.room.room_id,
formatted
)
2018-08-07 16:48:18 +02:00
body = {"msgtype": msgtype, "body": formatted.to_plain()}
if formatted.is_formatted():
body["format"] = "org.matrix.custom.html"
body["formatted_body"] = formatted.to_html()
uuid, request = self.client.room_send(
room_buffer.room.room_id,
"m.room.message",
body
)
self.own_message_queue[uuid] = own_message
self.send_or_queue(request)
def _print_message_error(self, message):
server_buffer_prnt(self,
("{prefix}Unhandled {status_code} error, please "
"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.request.payload))
server_buffer_prnt(self, pprint.pformat(message.response.body))
def handle_own_messages(self, response):
message = self.own_message_queue.pop(response.uuid)
room_buffer = self.room_buffers[message.room_id]
message = message._replace(event_id=response.event_id)
2018-07-20 17:51:48 +02:00
if isinstance(message, OwnAction):
room_buffer.self_action(message)
return
2018-07-20 17:51:48 +02:00
elif isinstance(message, OwnMessage):
room_buffer.self_message(message)
return
raise NotImplementedError("Unsupported message of type {}".format(
type(message)))
2018-07-20 15:53:47 +02:00
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):
self.access_token = response.access_token
self.user_id = response.user_id
self.client.access_token = response.access_token
self.device_id = response.device_id
self.save_device_id()
message = "{prefix}matrix: Logged in as {user}".format(
prefix=W.prefix("network"), user=self.user_id)
W.prnt(self.server_buffer, message)
# if not self.olm:
# self.create_olm()
# self.store_olm()
# self.upload_keys(device_keys=True, one_time_keys=False)
sync_filter = {"room": {"timeline": {"limit": OPTIONS.sync_limit}}}
self.sync(timeout=0, filter=sync_filter)
2018-07-20 15:53:47 +02:00
2018-07-20 17:51:48 +02:00
def _handle_room_info(self, response):
for room_id, join_info in response.rooms.join.items():
if room_id not in self.buffers:
self.create_room_buffer(room_id)
2018-07-20 15:53:47 +02:00
2018-07-20 17:51:48 +02:00
room_buffer = self.find_room_from_id(room_id)
2018-07-20 15:53:47 +02:00
2018-07-20 17:51:48 +02:00
for event in join_info.state:
room_buffer.handle_state_event(event)
2018-07-20 15:53:47 +02:00
2018-07-20 17:51:48 +02:00
for event in join_info.timeline.events:
room_buffer.handle_timeline_event(event)
2018-07-20 15:53:47 +02:00
def _handle_sync(self, response):
# we got the same batch again, nothing to do
if self.next_batch == response.next_batch:
self.schedule_sync()
2018-07-20 15:53:47 +02:00
return
2018-07-20 17:51:48 +02:00
self._handle_room_info(response)
2018-07-20 15:53:47 +02:00
self.next_batch = response.next_batch
self.schedule_sync()
2018-07-20 15:53:47 +02:00
2018-08-07 19:07:54 +02:00
def handle_transport_response(self, response):
self.error(("Error with response of type type: {}, "
"error code {}").format(
response.request_info.type, response.status_code))
# TODO better error handling.
if response.request_info.type == "sync":
self.disconnect()
2018-07-20 19:14:32 +02:00
def handle_response(self, response):
# type: (MatrixMessage) -> None
self.lag = response.elapsed * 1000
2018-08-03 12:25:17 +02:00
# If the response was a sync response and contained a timeout the
# timeout is expected and should be removed from the lag.
# TODO the timeout isn't a constant
if isinstance(response, SyncRepsponse):
self.lag = max(0, self.lag - (30000))
self.lag_done = True
W.bar_item_update("lag")
2018-07-26 11:33:05 +02:00
if isinstance(response, TransportResponse):
2018-08-07 19:07:54 +02:00
self.handle_transport_response(response)
2018-07-20 15:53:47 +02:00
2018-07-26 11:33:05 +02:00
elif isinstance(response, LoginResponse):
2018-07-20 15:53:47 +02:00
self._handle_login(response)
2018-08-07 19:07:54 +02:00
2018-07-20 17:51:48 +02:00
elif isinstance(response, SyncRepsponse):
self._handle_sync(response)
2018-08-07 19:07:54 +02:00
elif isinstance(response, RoomSendResponse):
self.handle_own_messages(response)
2018-07-20 15:53:47 +02:00
2018-08-07 19:07:54 +02:00
elif isinstance(response, RoomPutStateResponse):
pass
return
def create_room_buffer(self, room_id):
2018-07-20 19:14:32 +02:00
room = self.client.rooms[room_id]
buf = RoomBuffer(room, self.name)
# TODO this should turned into a propper class
self.room_buffers[room_id] = buf
self.buffers[room_id] = buf.weechat_buffer._ptr
def find_room_from_ptr(self, pointer):
2018-08-07 16:48:18 +02:00
try:
room_id = key_from_value(self.buffers, pointer)
room_buffer = self.room_buffers[room_id]
2018-08-07 16:48:18 +02:00
return room_buffer
except (ValueError, KeyError):
return None
def find_room_from_id(self, room_id):
room_buffer = self.room_buffers[room_id]
2018-07-20 17:51:48 +02:00
return room_buffer
@utf8_decode
2018-02-21 17:00:11 +01:00
def matrix_config_server_read_cb(data, config_file, section, option_name,
value):
return_code = W.WEECHAT_CONFIG_OPTION_SET_ERROR
if option_name:
server_name, option = option_name.rsplit('.', 1)
server = None
if server_name in SERVERS:
server = SERVERS[server_name]
else:
server = MatrixServer(server_name, config_file)
SERVERS[server.name] = server
# Ignore invalid options
if option in server.config.options:
return_code = W.config_option_set(
server.config.options[option],
value,
1
)
# TODO print out error message in case of erroneous return_code
return return_code
@utf8_decode
def matrix_config_server_write_cb(data, config_file, section_name):
if not W.config_write_line(config_file, section_name, ""):
return W.WECHAT_CONFIG_WRITE_ERROR
for server in SERVERS.values():
for option in server.config.options.values():
if not W.config_write_option(config_file, option):
return W.WECHAT_CONFIG_WRITE_ERROR
return W.WEECHAT_CONFIG_WRITE_OK
@utf8_decode
def matrix_config_server_change_cb(server_name, option):
# type: (str, weechat.config_option) -> int
server = SERVERS[server_name]
option_name = None
# The function config_option_get_string() is used to get differing
# properties from a config option, sadly it's only available in the plugin
# API of weechat.
option_name = key_from_value(server.config.options, option)
server.update_option(option, option_name)
return 1
2018-01-30 11:46:29 +01:00
@utf8_decode
def matrix_timer_cb(server_name, remaining_calls):
server = SERVERS[server_name]
current_time = time.time()
2018-02-21 17:00:11 +01:00
if ((not server.connected) and server.reconnect_time and
current_time >= (server.reconnect_time + server.reconnect_delay)):
server.reconnect()
return W.WEECHAT_RC_OK
2018-01-30 11:46:29 +01:00
if not server.connected:
return W.WEECHAT_RC_OK
# check lag, disconnect if it's too big
server.lag = server.client.lag * 1000
server.lag_done = False
W.bar_item_update("lag")
# TODO print out message, make timeout configurable
if server.lag > 300000:
server.disconnect()
return W.WEECHAT_RC_OK
2018-07-20 19:14:32 +02:00
if server.sync_time and current_time > (server.sync_time + 2):
timeout = 0 if server.transport_type == TransportType.HTTP else 30000
sync_filter = {"room": {"timeline": {"limit": 5000}}}
server.sync(timeout, sync_filter)
2018-07-21 15:15:48 +02:00
while server.send_queue:
message = server.send_queue.popleft()
prnt_debug(
DebugType.MESSAGING,
server, ("Timer hook found message of type {t} in queue. Sending "
"out.".format(t=message.__class__.__name__)))
2018-07-20 19:14:32 +02:00
2018-07-21 15:15:48 +02:00
if not server.send(message):
# We got an error while sending the last message return the message
# to the queue and exit the loop
server.send_queue.appendleft(message)
break
2018-01-30 11:46:29 +01:00
if not server.next_batch:
return W.WEECHAT_RC_OK
# check for new devices by users in encrypted rooms periodically
2018-07-21 15:15:48 +02:00
# if (not server.device_check_timestamp or
# current_time - server.device_check_timestamp > 600):
2018-07-21 15:15:48 +02:00
# W.prnt(server.server_buffer,
# "{prefix}matrix: Querying user devices.".format(
# prefix=W.prefix("networ")))
2018-07-21 15:15:48 +02:00
# server.device_check_timestamp = current_time
2018-01-30 11:46:29 +01:00
return W.WEECHAT_RC_OK
def create_default_server(config_file):
server = MatrixServer('matrix_org', config_file)
2018-01-30 11:46:29 +01:00
SERVERS[server.name] = server
option = W.config_get(SCRIPT_NAME + ".server." + server.name + ".address")
W.config_option_set(option, "matrix.org", 1)
2018-01-30 11:46:29 +01:00
return True
@utf8_decode
def send_cb(server_name, file_descriptor):
# type: (str, int) -> int
server = SERVERS[server_name]
if server.send_fd_hook:
W.unhook(server.send_fd_hook)
server.send_fd_hook = None
if server.send_buffer:
server.try_send(server, server.send_buffer)
return W.WEECHAT_RC_OK