Initial topic command implementation.
This commit is contained in:
parent
c54fa5167d
commit
8157f3d052
1 changed files with 206 additions and 33 deletions
|
@ -6,6 +6,7 @@ import json
|
||||||
import socket
|
import socket
|
||||||
import ssl
|
import ssl
|
||||||
import time
|
import time
|
||||||
|
import datetime
|
||||||
|
|
||||||
# pylint: disable=redefined-builtin
|
# pylint: disable=redefined-builtin
|
||||||
from builtins import bytes
|
from builtins import bytes
|
||||||
|
@ -110,7 +111,8 @@ class WeechatWrapper(object):
|
||||||
class MessageType(Enum):
|
class MessageType(Enum):
|
||||||
LOGIN = 0
|
LOGIN = 0
|
||||||
SYNC = 1
|
SYNC = 1
|
||||||
POST_MSG = 2
|
SEND = 2
|
||||||
|
STATE = 3
|
||||||
|
|
||||||
|
|
||||||
@unique
|
@unique
|
||||||
|
@ -118,7 +120,6 @@ class RequestType(Enum):
|
||||||
GET = 0
|
GET = 0
|
||||||
POST = 1
|
POST = 1
|
||||||
PUT = 2
|
PUT = 2
|
||||||
DELETE = 3
|
|
||||||
|
|
||||||
|
|
||||||
class HttpResponse:
|
class HttpResponse:
|
||||||
|
@ -131,6 +132,7 @@ class HttpResponse:
|
||||||
class HttpRequest:
|
class HttpRequest:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
request_type,
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
location,
|
location,
|
||||||
|
@ -138,7 +140,7 @@ class HttpRequest:
|
||||||
user_agent='weechat-matrix/{version}'.format(
|
user_agent='weechat-matrix/{version}'.format(
|
||||||
version=WEECHAT_SCRIPT_VERSION)
|
version=WEECHAT_SCRIPT_VERSION)
|
||||||
):
|
):
|
||||||
# type: (unicode, int, unicode, Dict[unicode, Any], unicode) -> None
|
# type: (RequestType, unicode, int, unicode, Dict[unicode, Any], unicode) -> None
|
||||||
# TODO we need to handle PUT as well
|
# TODO we need to handle PUT as well
|
||||||
host_string = ':'.join([host, str(port)])
|
host_string = ':'.join([host, str(port)])
|
||||||
|
|
||||||
|
@ -149,10 +151,23 @@ class HttpRequest:
|
||||||
end_separator = '\r\n' # type: unicode
|
end_separator = '\r\n' # type: unicode
|
||||||
payload = None # type: unicode
|
payload = None # type: unicode
|
||||||
|
|
||||||
if data:
|
if request_type == RequestType.GET:
|
||||||
|
get = 'GET {location} HTTP/1.1'.format(location=location)
|
||||||
|
request_list = [get, host_header,
|
||||||
|
user_agent, accept_header, end_separator]
|
||||||
|
|
||||||
|
elif (request_type == RequestType.POST or
|
||||||
|
request_type == RequestType.PUT):
|
||||||
|
|
||||||
json_data = json.dumps(data, separators=(',', ':'))
|
json_data = json.dumps(data, separators=(',', ':'))
|
||||||
|
|
||||||
post = 'POST {location} HTTP/1.1'.format(
|
if request_type == RequestType.POST:
|
||||||
|
method = "POST"
|
||||||
|
else:
|
||||||
|
method = "PUT"
|
||||||
|
|
||||||
|
request_line = '{method} {location} HTTP/1.1'.format(
|
||||||
|
method=method,
|
||||||
location=location
|
location=location
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -161,14 +176,10 @@ class HttpRequest:
|
||||||
length=len(json_data)
|
length=len(json_data)
|
||||||
)
|
)
|
||||||
|
|
||||||
request_list = [post, host_header,
|
request_list = [request_line, host_header,
|
||||||
user_agent, accept_header,
|
user_agent, accept_header,
|
||||||
length_header, type_header, end_separator]
|
length_header, type_header, end_separator]
|
||||||
payload = json_data
|
payload = json_data
|
||||||
else:
|
|
||||||
get = 'GET {location} HTTP/1.1'.format(location=location)
|
|
||||||
request_list = [get, host_header,
|
|
||||||
user_agent, accept_header, end_separator]
|
|
||||||
|
|
||||||
request = '\r\n'.join(request_list)
|
request = '\r\n'.join(request_list)
|
||||||
|
|
||||||
|
@ -177,17 +188,30 @@ class HttpRequest:
|
||||||
|
|
||||||
|
|
||||||
class MatrixMessage:
|
class MatrixMessage:
|
||||||
def __init__(self, server, message_type, room_id=None, data=None,
|
def __init__(
|
||||||
extra_data=None):
|
self,
|
||||||
# type: (MatrixServer, MessageType, unicode, Dict[unicode, Any], Dict[unicode, Any]) -> None
|
server, # type: MatrixServer
|
||||||
|
message_type, # type: MessageType
|
||||||
|
room_id=None, # type: unicode
|
||||||
|
event_type=None, # type: unicode
|
||||||
|
data=None, # type: Dict[unicode, Any]
|
||||||
|
extra_data=None # type: Dict[unicode, Any]
|
||||||
|
):
|
||||||
|
# type: (...) -> None
|
||||||
self.type = message_type # MessageType
|
self.type = message_type # MessageType
|
||||||
self.request = None # HttpRequest
|
self.request = None # HttpRequest
|
||||||
self.response = None # HttpRequest
|
self.response = None # HttpResponse
|
||||||
self.extra_data = extra_data # Dict[unicode, Any]
|
self.extra_data = extra_data # Dict[unicode, Any]
|
||||||
|
|
||||||
if message_type == MessageType.LOGIN:
|
if message_type == MessageType.LOGIN:
|
||||||
path = ("{api}/login").format(api=MATRIX_API_PATH)
|
path = ("{api}/login").format(api=MATRIX_API_PATH)
|
||||||
self.request = HttpRequest(server.address, server.port, path, data)
|
self.request = HttpRequest(
|
||||||
|
RequestType.POST,
|
||||||
|
server.address,
|
||||||
|
server.port,
|
||||||
|
path,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
elif message_type == MessageType.SYNC:
|
elif message_type == MessageType.SYNC:
|
||||||
# TODO the limit should be configurable matrix.network.sync_limit
|
# TODO the limit should be configurable matrix.network.sync_limit
|
||||||
|
@ -208,32 +232,54 @@ class MatrixMessage:
|
||||||
path = path + '&since={next_batch}'.format(
|
path = path + '&since={next_batch}'.format(
|
||||||
next_batch=server.next_batch)
|
next_batch=server.next_batch)
|
||||||
|
|
||||||
self.request = HttpRequest(server.address, server.port, path)
|
self.request = HttpRequest(
|
||||||
|
RequestType.GET,
|
||||||
|
server.address,
|
||||||
|
server.port,
|
||||||
|
path
|
||||||
|
)
|
||||||
|
|
||||||
elif message_type == MessageType.POST_MSG:
|
elif message_type == MessageType.SEND:
|
||||||
path = ("{api}/rooms/{room}/send/m.room.message?"
|
path = ("{api}/rooms/{room}/send/m.room.message?"
|
||||||
"access_token={access_token}").format(
|
"access_token={access_token}").format(
|
||||||
api=MATRIX_API_PATH,
|
api=MATRIX_API_PATH,
|
||||||
room=room_id,
|
room=room_id,
|
||||||
access_token=server.access_token)
|
access_token=server.access_token)
|
||||||
|
|
||||||
self.request = HttpRequest(server.address, server.port, path, data)
|
self.request = HttpRequest(
|
||||||
|
RequestType.POST,
|
||||||
|
server.address,
|
||||||
|
server.port,
|
||||||
|
path,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
|
elif message_type == MessageType.STATE:
|
||||||
|
path = ("{api}/rooms/{room}/state/{event_type}?"
|
||||||
|
"access_token={access_token}").format(
|
||||||
|
api=MATRIX_API_PATH,
|
||||||
|
room=room_id,
|
||||||
|
event_type=event_type,
|
||||||
|
access_token=server.access_token)
|
||||||
|
|
||||||
|
self.request = HttpRequest(
|
||||||
|
RequestType.PUT,
|
||||||
|
server.address,
|
||||||
|
server.port,
|
||||||
|
path,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
|
||||||
class Matrix:
|
|
||||||
def __init__(self):
|
|
||||||
# type: () -> None
|
|
||||||
self.access_token = "" # type: unicode
|
|
||||||
self.next_batch = "" # type: unicode
|
|
||||||
self.rooms = {} # type: Dict[unicode, MatrixRoom]
|
|
||||||
|
|
||||||
|
|
||||||
class MatrixRoom:
|
class MatrixRoom:
|
||||||
def __init__(self, room_id, join_rule, alias=None):
|
def __init__(self, room_id):
|
||||||
# type: (unicode, unicode, unicode) -> None
|
# type: (unicode) -> None
|
||||||
self.room_id = room_id # type: unicode
|
self.room_id = room_id # type: unicode
|
||||||
self.alias = alias # type: unicode
|
self.alias = room_id # type: unicode
|
||||||
self.join_rule = join_rule # type: unicode
|
self.topic = "" # type: unicode
|
||||||
|
self.topic_author = "" # type: unicode
|
||||||
|
self.topic_date = None # type: datetime.datetime
|
||||||
|
|
||||||
|
|
||||||
def key_from_value(dictionary, value):
|
def key_from_value(dictionary, value):
|
||||||
|
@ -284,10 +330,12 @@ class MatrixServer:
|
||||||
self.user = "" # type: unicode
|
self.user = "" # type: unicode
|
||||||
self.password = "" # type: unicode
|
self.password = "" # type: unicode
|
||||||
|
|
||||||
|
self.rooms = dict() # type: Dict[unicode, MatrixRoom]
|
||||||
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.numeric_address = "" # type: unicode
|
||||||
|
|
||||||
self.autoconnect = False # type: bool
|
self.autoconnect = False # type: bool
|
||||||
self.connected = False # type: bool
|
self.connected = False # type: bool
|
||||||
|
@ -403,6 +451,8 @@ def wrap_socket(server, file_descriptor):
|
||||||
def handle_http_response(server, message):
|
def handle_http_response(server, message):
|
||||||
# type: (MatrixServer, MatrixMessage) -> None
|
# type: (MatrixServer, MatrixMessage) -> None
|
||||||
|
|
||||||
|
assert message.response
|
||||||
|
|
||||||
status_code = message.response.status
|
status_code = message.response.status
|
||||||
|
|
||||||
# TODO handle error responses
|
# TODO handle error responses
|
||||||
|
@ -462,11 +512,13 @@ def matrix_create_room_buffer(server, room_id):
|
||||||
W.buffer_set(buf, "nicklist_display_groups", "0")
|
W.buffer_set(buf, "nicklist_display_groups", "0")
|
||||||
|
|
||||||
server.buffers[room_id] = buf
|
server.buffers[room_id] = buf
|
||||||
|
server.rooms[room_id] = MatrixRoom(room_id)
|
||||||
|
|
||||||
|
|
||||||
def matrix_handle_room_aliases(server, room_id, event):
|
def matrix_handle_room_aliases(server, room_id, event):
|
||||||
# type: (MatrixServer, unicode, Dict[unicode, Any]) -> None
|
# type: (MatrixServer, unicode, Dict[unicode, Any]) -> None
|
||||||
buf = server.buffers[room_id]
|
buf = server.buffers[room_id]
|
||||||
|
room = server.rooms[room_id]
|
||||||
|
|
||||||
alias = event['content']['aliases'][-1]
|
alias = event['content']['aliases'][-1]
|
||||||
|
|
||||||
|
@ -475,6 +527,7 @@ def matrix_handle_room_aliases(server, room_id, event):
|
||||||
|
|
||||||
short_name = strip_matrix_server(alias)
|
short_name = strip_matrix_server(alias)
|
||||||
|
|
||||||
|
room.alias = alias
|
||||||
W.buffer_set(buf, "name", alias)
|
W.buffer_set(buf, "name", alias)
|
||||||
W.buffer_set(buf, "short_name", short_name)
|
W.buffer_set(buf, "short_name", short_name)
|
||||||
W.buffer_set(buf, "localvar_set_channel", alias)
|
W.buffer_set(buf, "localvar_set_channel", alias)
|
||||||
|
@ -563,10 +616,11 @@ def matrix_handle_room_messages(server, room_id, event):
|
||||||
else:
|
else:
|
||||||
message = ("{prefix}Handling of content type "
|
message = ("{prefix}Handling of content type "
|
||||||
"{type} not implemented").format(
|
"{type} not implemented").format(
|
||||||
type=event['content']['type'],
|
type=event['content']['msgtype'],
|
||||||
prefix=W.prefix("error"))
|
prefix=W.prefix("error"))
|
||||||
W.prnt(server.server_buffer, message)
|
W.prnt(server.server_buffer, message)
|
||||||
|
|
||||||
|
|
||||||
def matrix_handle_room_events(server, room_id, room_events):
|
def matrix_handle_room_events(server, room_id, room_events):
|
||||||
# type: (MatrixServer, unicode, Dict[Any, Any]) -> None
|
# type: (MatrixServer, unicode, Dict[Any, Any]) -> None
|
||||||
for event in room_events:
|
for event in room_events:
|
||||||
|
@ -583,6 +637,36 @@ def matrix_handle_room_events(server, room_id, room_events):
|
||||||
elif event['type'] == 'm.room.message':
|
elif event['type'] == 'm.room.message':
|
||||||
matrix_handle_room_messages(server, room_id, event)
|
matrix_handle_room_messages(server, room_id, event)
|
||||||
|
|
||||||
|
elif event['type'] == 'm.room.topic':
|
||||||
|
buf = server.buffers[room_id]
|
||||||
|
room = server.rooms[room_id]
|
||||||
|
topic = event['content']['topic']
|
||||||
|
|
||||||
|
room.topic = topic
|
||||||
|
room.topic_author = event['sender']
|
||||||
|
|
||||||
|
topic_age = event['unsigned']['age']
|
||||||
|
# TODO put the age calculation in a function
|
||||||
|
room.topic_date = datetime.datetime.fromtimestamp(
|
||||||
|
time.time() - (topic_age / 1000))
|
||||||
|
|
||||||
|
W.buffer_set(buf, "title", topic)
|
||||||
|
|
||||||
|
# TODO nick and topic color
|
||||||
|
# TODO print old topic if configured so
|
||||||
|
# TODO nick display name if configured so and found
|
||||||
|
message = ("{prefix}{nick} has changed the topic for {room} to "
|
||||||
|
"\"{topic}\"").format(
|
||||||
|
prefix=W.prefix("network"),
|
||||||
|
nick=room.topic_author,
|
||||||
|
room=room.alias,
|
||||||
|
topic=topic)
|
||||||
|
|
||||||
|
tags = "matrix_topic,log3"
|
||||||
|
date = int(time.time())
|
||||||
|
|
||||||
|
W.prnt_date_tags(buf, date, tags, message)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
message = ("{prefix}Handling of message type "
|
message = ("{prefix}Handling of message type "
|
||||||
"{type} not implemented").format(
|
"{type} not implemented").format(
|
||||||
|
@ -624,7 +708,7 @@ def matrix_handle_message(server, message_type, response, extra_data):
|
||||||
|
|
||||||
server.next_batch = next_batch
|
server.next_batch = next_batch
|
||||||
|
|
||||||
elif message_type is MessageType.POST_MSG:
|
elif message_type is MessageType.SEND:
|
||||||
author = extra_data["author"]
|
author = extra_data["author"]
|
||||||
message = extra_data["message"]
|
message = extra_data["message"]
|
||||||
room_id = extra_data["room_id"]
|
room_id = extra_data["room_id"]
|
||||||
|
@ -643,6 +727,10 @@ def matrix_handle_message(server, message_type, response, extra_data):
|
||||||
buf = server.buffers[room_id]
|
buf = server.buffers[room_id]
|
||||||
W.prnt_date_tags(buf, date, tag, data)
|
W.prnt_date_tags(buf, date, tag, data)
|
||||||
|
|
||||||
|
# Nothing to do here, we'll handle state changes in the sync
|
||||||
|
elif message_type == MessageType.STATE:
|
||||||
|
pass
|
||||||
|
|
||||||
else:
|
else:
|
||||||
server_buffer_prnt(
|
server_buffer_prnt(
|
||||||
server,
|
server,
|
||||||
|
@ -766,6 +854,21 @@ def server_buffer_prnt(server, string):
|
||||||
W.prnt_date_tags(buffer, now, "", string)
|
W.prnt_date_tags(buffer, now, "", string)
|
||||||
|
|
||||||
|
|
||||||
|
def server_buffer_set_title(server):
|
||||||
|
# type: (MatrixServer) -> None
|
||||||
|
if server.numeric_address:
|
||||||
|
ip_string = " ({address})".format(address=server.numeric_address)
|
||||||
|
else:
|
||||||
|
ip_string = ""
|
||||||
|
|
||||||
|
title = ("Matrix: {address}/{port}{ip}").format(
|
||||||
|
address=server.address,
|
||||||
|
port=server.port,
|
||||||
|
ip=ip_string)
|
||||||
|
|
||||||
|
W.buffer_set(server.server_buffer, "title", title)
|
||||||
|
|
||||||
|
|
||||||
def create_server_buffer(server):
|
def create_server_buffer(server):
|
||||||
# type: (MatrixServer) -> None
|
# type: (MatrixServer) -> None
|
||||||
server.server_buffer = W.buffer_new(
|
server.server_buffer = W.buffer_new(
|
||||||
|
@ -776,6 +879,7 @@ def create_server_buffer(server):
|
||||||
""
|
""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
server_buffer_set_title(server)
|
||||||
W.buffer_set(server.server_buffer, "localvar_set_type", 'server')
|
W.buffer_set(server.server_buffer, "localvar_set_type", 'server')
|
||||||
W.buffer_set(server.server_buffer, "localvar_set_nick", server.user)
|
W.buffer_set(server.server_buffer, "localvar_set_nick", server.user)
|
||||||
W.buffer_set(server.server_buffer, "localvar_set_server", server.name)
|
W.buffer_set(server.server_buffer, "localvar_set_server", server.name)
|
||||||
|
@ -811,7 +915,9 @@ def connect_cb(data, status, gnutls_rc, sock, error, ip_address):
|
||||||
server.connected = True
|
server.connected = True
|
||||||
server.connecting = False
|
server.connecting = False
|
||||||
server.reconnect_count = 0
|
server.reconnect_count = 0
|
||||||
|
server.numeric_address = ip_address
|
||||||
|
|
||||||
|
server_buffer_set_title(server)
|
||||||
server_buffer_prnt(server, "Connected")
|
server_buffer_prnt(server, "Connected")
|
||||||
|
|
||||||
if not server.access_token:
|
if not server.access_token:
|
||||||
|
@ -933,7 +1039,7 @@ def room_input_cb(server_name, buffer, input_data):
|
||||||
"room_id": room_id
|
"room_id": room_id
|
||||||
}
|
}
|
||||||
|
|
||||||
message = MatrixMessage(server, MessageType.POST_MSG,
|
message = MatrixMessage(server, MessageType.SEND,
|
||||||
data=body, room_id=room_id,
|
data=body, room_id=room_id,
|
||||||
extra_data=extra_data)
|
extra_data=extra_data)
|
||||||
|
|
||||||
|
@ -1440,7 +1546,7 @@ def matrix_server_command_add(args):
|
||||||
|
|
||||||
|
|
||||||
def matrix_server_command(command, args):
|
def matrix_server_command(command, args):
|
||||||
def list_servers(args):
|
def list_servers(_):
|
||||||
if SERVERS:
|
if SERVERS:
|
||||||
W.prnt("", "\nAll matrix servers:")
|
W.prnt("", "\nAll matrix servers:")
|
||||||
for server in SERVERS:
|
for server in SERVERS:
|
||||||
|
@ -1592,6 +1698,71 @@ def create_default_server(config_file):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@utf8_decode
|
||||||
|
def matrix_command_topic_cb(data, buffer, command):
|
||||||
|
for server in SERVERS.values():
|
||||||
|
if buffer in server.buffers.values():
|
||||||
|
topic = None
|
||||||
|
room_id = key_from_value(server.buffers, buffer)
|
||||||
|
split_command = command.split(' ', 1)
|
||||||
|
|
||||||
|
if len(split_command) == 2:
|
||||||
|
topic = split_command[1]
|
||||||
|
|
||||||
|
# TODO print out topic in channel
|
||||||
|
if not topic:
|
||||||
|
room = server.rooms[room_id]
|
||||||
|
if room.topic:
|
||||||
|
message = ("{prefix}Topic for {color}{room}{ncolor} is "
|
||||||
|
"\"{topic}\"").format(
|
||||||
|
prefix=W.prefix("network"),
|
||||||
|
color=W.color("chat_buffer"),
|
||||||
|
ncolor=W.color("reset"),
|
||||||
|
room=room.alias,
|
||||||
|
topic=room.topic)
|
||||||
|
|
||||||
|
date = int(time.time())
|
||||||
|
topic_date = room.topic_date.strftime("%a, %d %b %Y "
|
||||||
|
"%H:%M:%S")
|
||||||
|
|
||||||
|
tags = "matrix_topic,log1"
|
||||||
|
W.prnt_date_tags(buffer, date, tags, message)
|
||||||
|
|
||||||
|
# TODO the nick should be colored
|
||||||
|
# TODO we should use the display name as well as
|
||||||
|
# the user name here
|
||||||
|
message = ("{prefix}Topic set by {author} on "
|
||||||
|
"{date}").format(
|
||||||
|
prefix=W.prefix("network"),
|
||||||
|
author=room.topic_author,
|
||||||
|
date=topic_date)
|
||||||
|
W.prnt_date_tags(buffer, date, tags, message)
|
||||||
|
|
||||||
|
return W.WEECHAT_RC_OK_EAT
|
||||||
|
|
||||||
|
body = {"topic": topic}
|
||||||
|
|
||||||
|
message = MatrixMessage(
|
||||||
|
server,
|
||||||
|
MessageType.STATE,
|
||||||
|
data=body,
|
||||||
|
room_id=room_id,
|
||||||
|
event_type="m.room.topic"
|
||||||
|
)
|
||||||
|
send_or_queue(server, message)
|
||||||
|
|
||||||
|
return W.WEECHAT_RC_OK_EAT
|
||||||
|
|
||||||
|
elif buffer == server.server_buffer:
|
||||||
|
message = ("{prefix}matrix: command \"topic\" must be "
|
||||||
|
"executed on a Matrix channel buffer").format(
|
||||||
|
prefix=W.prefix("error"))
|
||||||
|
W.prnt(buffer, message)
|
||||||
|
return W.WEECHAT_RC_OK_EAT
|
||||||
|
|
||||||
|
return W.WEECHAT_RC_OK
|
||||||
|
|
||||||
|
|
||||||
def init_hooks():
|
def init_hooks():
|
||||||
W.hook_completion(
|
W.hook_completion(
|
||||||
"matrix_server_commands",
|
"matrix_server_commands",
|
||||||
|
@ -1646,6 +1817,8 @@ def init_hooks():
|
||||||
# Function name
|
# Function name
|
||||||
'matrix_command_cb', '')
|
'matrix_command_cb', '')
|
||||||
|
|
||||||
|
W.hook_command_run('/topic', 'matrix_command_topic_cb', '')
|
||||||
|
|
||||||
|
|
||||||
def autoconnect(servers):
|
def autoconnect(servers):
|
||||||
for server in servers.values():
|
for server in servers.values():
|
||||||
|
|
Loading…
Add table
Reference in a new issue