1914 lines
79 KiB
Python
1914 lines
79 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2009-2013 Sebastien Helleu <flashcode@flashtux.org>
|
|
# Copyright (C) 2010 xt <xt@bash.no>
|
|
# Copyright (C) 2010 Aleksey V. Zapparov <ixti@member.fsf.org>
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
#
|
|
# Jabber/XMPP protocol for WeeChat.
|
|
# (this script requires WeeChat 0.3.0 (or newer) and xmpppy library)
|
|
#
|
|
# For help, see /help jabber
|
|
# Happy chat, enjoy :)
|
|
#
|
|
# History:
|
|
# 2013-09-30, Nils Görs <freenode.nils_2>:
|
|
# version 1.6: add support of /secure for passwords and jid
|
|
# : fix stdout/stderr when no JID was set
|
|
# 2013-05-14, Billiam <billiamthesecond@gmail.com>:
|
|
# version 1.5: fix unicode encoding error in /jabber buddies
|
|
# 2013-05-03, Sebastien Helleu <flashcode@flashtux.org>:
|
|
# version 1.4: add tags in user messages: notify_xxx, no_highlight,
|
|
# nick_xxx, prefix_nick_xxx, log1
|
|
# 2012-05-12, Sebastian Rydberg <sr@rydbergtech.se>:
|
|
# version 1.3: Added support for fetching names from roster
|
|
# 2012-04-11, Sebastien Helleu <flashcode@flashtux.org>:
|
|
# version 1.2: fix deletion of server options
|
|
# 2012-03-09, Sebastien Helleu <flashcode@flashtux.org>:
|
|
# version 1.1: fix reload of config file
|
|
# 2012-01-03, Sebastien Helleu <flashcode@flashtux.org>:
|
|
# version 1.0: changes for future compatibility with Python 3.x
|
|
# 2011-12-15, Sebastien Helleu <flashcode@flashtux.org>:
|
|
# version 0.9: fix utf-8 encoding problem on jid
|
|
# 2011-03-21, Isaac Raway <isaac.raway@gmail.com>:
|
|
# version 0.8: search chat buffer before opening it
|
|
# 2011-02-13, Sebastien Helleu <flashcode@flashtux.org>:
|
|
# version 0.7: use new help format for command arguments
|
|
# 2010-11-23, xt
|
|
# version 0.6: change format of sent ping, to match RFC
|
|
# 2010-10-05, xt, <xt@bash.no>
|
|
# version 0.5: no highlight for status/presence messages
|
|
# 2010-10-01, xt, <xt@bash.no>
|
|
# version 0.4:
|
|
# add kick and invite
|
|
# 2010-08-03, Aleksey V. Zapparov <ixti@member.fsf.org>:
|
|
# version 0.3:
|
|
# add /jabber priority [priority]
|
|
# add /jabber status [message]
|
|
# add /jabber presence [online|chat|away|xa|dnd]
|
|
# 2010-08-02, Aleksey V. Zapparov <ixti@member.fsf.org>:
|
|
# version 0.2.1:
|
|
# fix prexence is set for current resource instead of sending
|
|
# special presences for all buddies
|
|
# 2010-08-02, Aleksey V. Zapparov <ixti@member.fsf.org>:
|
|
# version 0.2:
|
|
# add priority and away_priority of resource
|
|
# 2010-08-02, Sebastien Helleu <flashcode@flashtux.org>:
|
|
# version 0.1: first official version
|
|
# 2010-08-01, ixti <ixti@member.fsf.org>:
|
|
# fix bug with non-ascii resources
|
|
# 2010-06-09, iiijjjiii <iiijjjiii@gmail.com>:
|
|
# add connect server and port options (required for google talk)
|
|
# add private option permitting messages to be displayed in separate
|
|
# chat buffers or in a single server buffer
|
|
# add jid aliases
|
|
# add keepalive ping
|
|
# 2010-03-17, xt <xt@bash.no>:
|
|
# add autoreconnect option, autoreconnects on protocol error
|
|
# 2010-03-17, xt <xt@bash.no>:
|
|
# add autoconnect option, add new command /jmsg with -server option
|
|
# 2009-02-22, Sebastien Helleu <flashcode@flashtux.org>:
|
|
# first version (unofficial)
|
|
#
|
|
|
|
import re
|
|
import warnings
|
|
|
|
import_ok = True
|
|
|
|
try:
|
|
import weechat
|
|
w = weechat
|
|
except:
|
|
print("This script must be run under WeeChat.")
|
|
print("Get WeeChat now at: http://www.weechat.org/")
|
|
import_ok = False
|
|
|
|
from support_jabber import aget_proxy
|
|
|
|
# On import, xmpp may produce warnings about using hashlib instead of
|
|
# deprecated sha and md5. Since the code producing those warnings is
|
|
# outside this script, catch them and ignore.
|
|
original_filters = warnings.filters[:]
|
|
warnings.filterwarnings("ignore",category=DeprecationWarning)
|
|
try:
|
|
import xmpp
|
|
from xmpp import dispatcher
|
|
from xmpp import transports
|
|
dispatcher.DefaultTimeout = 60
|
|
except ImportError as e:
|
|
print(f"Package python-xmpp (xmpppy) must be installed to use Jabber protocol. {e}")
|
|
import_ok = False
|
|
finally:
|
|
warnings.filters = original_filters
|
|
|
|
SCRIPT_NAME = "jabber"
|
|
SCRIPT_AUTHOR = "Sebastien Helleu <flashcode@flashtux.org>"
|
|
SCRIPT_VERSION = "1.7.01"
|
|
SCRIPT_LICENSE = "GPL3"
|
|
SCRIPT_DESC = "Jabber/XMPP protocol for WeeChat"
|
|
SCRIPT_COMMAND = SCRIPT_NAME
|
|
|
|
def LOG_debug(what, level='none'):
|
|
# if jabber_debug_enabled():
|
|
weechat.prnt('', what)
|
|
|
|
# ==============================[ global vars ]===============================
|
|
|
|
jabber_servers = []
|
|
jabber_server_options = {
|
|
"jid" : { "type" : "string",
|
|
"desc" : "jabber id (user@server.tld)",
|
|
"min" : 0,
|
|
"max" : 0,
|
|
"string_values": "",
|
|
"default" : "",
|
|
"value" : "",
|
|
"check_cb" : "",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"priority" : { "type" : "integer",
|
|
"desc" : "Default resource priority",
|
|
"min" : 0,
|
|
"max" : 65535,
|
|
"string_values": "",
|
|
"default" : "8",
|
|
"value" : "8",
|
|
"check_cb" : "",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"away_priority": { "type" : "integer",
|
|
"desc" : "Resource priority on away",
|
|
"min" : 0,
|
|
"max" : 65535,
|
|
"string_values": "",
|
|
"default" : "0",
|
|
"value" : "0",
|
|
"check_cb" : "",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"password" : { "type" : "string",
|
|
"desc" : "password for jabber id on server (evaluated)",
|
|
"min" : 0,
|
|
"max" : 0,
|
|
"string_values": "",
|
|
"default" : "",
|
|
"value" : "",
|
|
"check_cb" : "",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"server" : { "type" : "string",
|
|
"desc" : "connect server host or ip, eg. talk.google.com",
|
|
"min" : 0,
|
|
"max" : 0,
|
|
"string_values": "",
|
|
"default" : "",
|
|
"value" : "",
|
|
"check_cb" : "",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"port" : { "type" : "integer",
|
|
"desc" : "connect server port, eg. 5223",
|
|
"min" : 0,
|
|
"max" : 65535,
|
|
"string_values": "",
|
|
"default" : "5222",
|
|
"value" : "5222",
|
|
"check_cb" : "",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"autoconnect" : { "type" : "boolean",
|
|
"desc" : "automatically connect to server when script is starting",
|
|
"min" : 0,
|
|
"max" : 0,
|
|
"string_values": "",
|
|
"default" : "off",
|
|
"value" : "off",
|
|
"check_cb" : "",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"autoreconnect": { "type" : "boolean",
|
|
"desc" : "automatically reconnect to server when disconnected",
|
|
"min" : 0,
|
|
"max" : 0,
|
|
"string_values": "",
|
|
"default" : "off",
|
|
"value" : "off",
|
|
"check_cb" : "",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"private" : { "type" : "boolean",
|
|
"desc" : "display messages in separate chat buffers instead of a single server buffer",
|
|
"min" : 0,
|
|
"max" : 0,
|
|
"string_values": "",
|
|
"default" : "on",
|
|
"value" : "on",
|
|
"check_cb" : "",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"ping_interval": { "type" : "integer",
|
|
"desc" : "Number of seconds between server pings. 0 = disable",
|
|
"min" : 60,
|
|
"max" : 9999999,
|
|
"string_values": "",
|
|
"default" : "0",
|
|
"value" : "0",
|
|
"check_cb" : "ping_interval_check_cb",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"ping_timeout" : { "type" : "integer",
|
|
"desc" : "Number of seconds to allow ping to respond before timing out",
|
|
"min" : 0,
|
|
"max" : 9999999,
|
|
"string_values": "",
|
|
"default" : "10",
|
|
"value" : "10",
|
|
"check_cb" : "ping_timeout_check_cb",
|
|
"change_cb" : "",
|
|
"delete_cb" : "",
|
|
},
|
|
"proxy": { "type": "string",
|
|
"desc": "name of the weechat.proxy.* = or ''",
|
|
"min": 0,
|
|
"max": 0,
|
|
"string_values": "",
|
|
"default": "",
|
|
"value": "",
|
|
"check_cb": "",
|
|
"change_cb": "",
|
|
"delete_cb": "",
|
|
},
|
|
}
|
|
jabber_config_file = None
|
|
jabber_config_section = {}
|
|
jabber_config_option = {}
|
|
jabber_jid_aliases = {} # { 'alias1': 'jid1', 'alias2': 'jid2', ... }
|
|
|
|
# =================================[ config ]=================================
|
|
|
|
def jabber_config_init():
|
|
""" Initialize config file: create sections and options in memory. """
|
|
global jabber_config_file, jabber_config_section
|
|
jabber_config_file = weechat.config_new("jabber", "jabber_config_reload_cb", "")
|
|
if not jabber_config_file:
|
|
return
|
|
# look
|
|
jabber_config_section["look"] = weechat.config_new_section(
|
|
jabber_config_file, "look", 0, 0, "", "", "", "", "", "", "", "", "", "")
|
|
if not jabber_config_section["look"]:
|
|
weechat.config_free(jabber_config_file)
|
|
return
|
|
jabber_config_option["debug"] = weechat.config_new_option(
|
|
jabber_config_file, jabber_config_section["look"],
|
|
"debug", "boolean", "display debug messages", "", 0, 0,
|
|
"off", "off", 0, "", "", "", "", "", "")
|
|
# color
|
|
jabber_config_section["color"] = weechat.config_new_section(
|
|
jabber_config_file, "color", 0, 0, "", "", "", "", "", "", "", "", "", "")
|
|
if not jabber_config_section["color"]:
|
|
weechat.config_free(jabber_config_file)
|
|
return
|
|
jabber_config_option["message_join"] = weechat.config_new_option(
|
|
jabber_config_file, jabber_config_section["color"],
|
|
"message_join", "color", "color for text in join messages", "", 0, 0,
|
|
"green", "green", 0, "", "", "", "", "", "")
|
|
jabber_config_option["message_quit"] = weechat.config_new_option(
|
|
jabber_config_file, jabber_config_section["color"],
|
|
"message_quit", "color", "color for text in quit messages", "", 0, 0,
|
|
"red", "red", 0, "", "", "", "", "", "")
|
|
# server
|
|
jabber_config_section["server"] = weechat.config_new_section(
|
|
jabber_config_file, "server", 0, 0,
|
|
"jabber_config_server_read_cb", "", "jabber_config_server_write_cb", "",
|
|
"", "", "", "", "", "")
|
|
if not jabber_config_section["server"]:
|
|
weechat.config_free(jabber_config_file)
|
|
return
|
|
jabber_config_section["jid_aliases"] = weechat.config_new_section(
|
|
jabber_config_file, "jid_aliases", 0, 0,
|
|
"jabber_config_jid_aliases_read_cb", "",
|
|
"jabber_config_jid_aliases_write_cb", "",
|
|
"", "", "", "", "", "")
|
|
if not jabber_config_section["jid_aliases"]:
|
|
weechat.config_free(jabber_config_file)
|
|
return
|
|
|
|
def jabber_config_reload_cb(data, config_file):
|
|
""" Reload config file. """
|
|
return weechat.config_reload(config_file)
|
|
|
|
def jabber_config_server_read_cb(data, config_file, section, option_name, value):
|
|
""" Read server option in config file. """
|
|
global jabber_servers
|
|
rc = weechat.WEECHAT_CONFIG_OPTION_SET_ERROR
|
|
items = option_name.split(".", 1)
|
|
if len(items) == 2:
|
|
server = jabber_search_server_by_name(items[0])
|
|
if not server:
|
|
server = Server(items[0])
|
|
jabber_servers.append(server)
|
|
if server:
|
|
rc = weechat.config_option_set(server.options[items[1]], value, 1)
|
|
return rc
|
|
|
|
def jabber_config_server_write_cb(data, config_file, section_name):
|
|
""" Write server section in config file. """
|
|
global jabber_servers
|
|
weechat.config_write_line(config_file, section_name, "")
|
|
for server in jabber_servers:
|
|
for name, option in sorted(server.options.items()):
|
|
weechat.config_write_option(config_file, option)
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
def jabber_config_jid_aliases_read_cb(data, config_file, section, option_name, value):
|
|
""" Read jid_aliases option in config file. """
|
|
global jabber_jid_aliases
|
|
jabber_jid_aliases[option_name] = value
|
|
option = weechat.config_new_option(
|
|
config_file, section,
|
|
option_name, "string", "jid alias", "", 0, 0,
|
|
"", value, 0, "", "", "", "", "", "")
|
|
if not option:
|
|
return weechat.WEECHAT_CONFIG_OPTION_SET_ERROR
|
|
return weechat.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED
|
|
|
|
def jabber_config_jid_aliases_write_cb(data, config_file, section_name):
|
|
""" Write jid_aliases section in config file. """
|
|
global jabber_jid_aliases
|
|
weechat.config_write_line(config_file, section_name, "")
|
|
for alias, jid in sorted(jabber_jid_aliases.items()):
|
|
weechat.config_write_line(config_file, alias, jid)
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
def jabber_config_read():
|
|
""" Read jabber config file (jabber.conf). """
|
|
global jabber_config_file
|
|
return weechat.config_read(jabber_config_file)
|
|
|
|
def jabber_config_write():
|
|
""" Write jabber config file (jabber.conf). """
|
|
global jabber_config_file
|
|
return weechat.config_write(jabber_config_file)
|
|
|
|
def jabber_debug_enabled():
|
|
""" Return True if debug is enabled. """
|
|
global jabber_config_options
|
|
if weechat.config_boolean(jabber_config_option["debug"]):
|
|
return True
|
|
return False
|
|
|
|
def jabber_config_color(color):
|
|
""" Return color code for a jabber color option. """
|
|
global jabber_config_option
|
|
if color in jabber_config_option:
|
|
return weechat.color(weechat.config_color(jabber_config_option[color]))
|
|
return ""
|
|
|
|
def ping_timeout_check_cb(server_name, option, value):
|
|
global jabber_config_file, jabber_config_section
|
|
ping_interval_option = weechat.config_search_option(
|
|
jabber_config_file,
|
|
jabber_config_section["server"],
|
|
"%s.ping_interval" % (server_name)
|
|
)
|
|
ping_interval = weechat.config_integer(ping_interval_option)
|
|
if int(ping_interval) and int(value) >= int(ping_interval):
|
|
weechat.prnt("", "\njabber: unable to update 'ping_timeout' for server %s" % (server_name))
|
|
weechat.prnt("", f"{SCRIPT_NAME}: to prevent multiple concurrent pings, ping_interval must be greater than ping_timeout")
|
|
return weechat.WEECHAT_CONFIG_OPTION_SET_ERROR
|
|
return weechat.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED
|
|
|
|
def ping_interval_check_cb(server_name, option, value):
|
|
global jabber_config_file, jabber_config_section
|
|
ping_timeout_option = weechat.config_search_option(
|
|
jabber_config_file,
|
|
jabber_config_section["server"],
|
|
"%s.ping_timeout" % (server_name)
|
|
)
|
|
ping_timeout = weechat.config_integer(ping_timeout_option)
|
|
if int(value) and int(ping_timeout) >= int(value):
|
|
weechat.prnt("", "\njabber: unable to update 'ping_interval' for server %s" % (server_name))
|
|
weechat.prnt("", f"{SCRIPT_NAME}: to prevent multiple concurrent pings, ping_interval must be greater than ping_timeout")
|
|
return weechat.WEECHAT_CONFIG_OPTION_SET_ERROR
|
|
return weechat.WEECHAT_CONFIG_OPTION_SET_OK_CHANGED
|
|
|
|
# ================================[ servers ]=================================
|
|
|
|
class Server:
|
|
""" Class to manage a server: buffer, connection, send/recv data. """
|
|
|
|
def __init__(self, name, **kwargs):
|
|
""" Init server """
|
|
global jabber_config_file, jabber_config_section, jabber_server_options
|
|
if name and type(name) == bytes:
|
|
name = str(name, 'UTF-8')
|
|
self.name = name
|
|
# create options (user can set them with /set)
|
|
self.options = {}
|
|
# if the value is provided, use it, otherwise use the default
|
|
values = {}
|
|
for option_name, props in jabber_server_options.items():
|
|
values[option_name] = props["default"]
|
|
values['name'] = name
|
|
values.update(**kwargs)
|
|
for option_name, props in jabber_server_options.items():
|
|
self.options[option_name] = weechat.config_new_option(
|
|
jabber_config_file, jabber_config_section["server"],
|
|
self.name + "." + option_name, props["type"], props["desc"],
|
|
props["string_values"], props["min"], props["max"],
|
|
props["default"], values[option_name], 0,
|
|
props["check_cb"], self.name, props["change_cb"], "",
|
|
props["delete_cb"], "")
|
|
# internal data
|
|
self.jid = None
|
|
self.client = None
|
|
self.sfn = None
|
|
self.hook_fd = None
|
|
self.buffer = ""
|
|
self.chats = []
|
|
self.roster = None
|
|
self.buddies = []
|
|
self.buddy = None
|
|
self.ping_timer = None # weechat.hook_timer for sending pings
|
|
self.ping_timeout_timer = None # weechat.hook_timer for monitoring ping timeout
|
|
self.ping_up = False # Connection status as per pings.
|
|
self.presence = xmpp.protocol.Presence()
|
|
|
|
def option_string(self, option_name):
|
|
""" Return a server option, as string. """
|
|
return weechat.config_string(self.options[option_name])
|
|
|
|
def option_boolean(self, option_name):
|
|
""" Return a server option, as boolean. """
|
|
return weechat.config_boolean(self.options[option_name])
|
|
|
|
def option_integer(self, option_name):
|
|
""" Return a server option, as string. """
|
|
return weechat.config_integer(self.options[option_name])
|
|
|
|
def DEBUG(self, *largs):
|
|
# if jabber_debug_enabled():
|
|
message = repr(largs)
|
|
if len(largs) == 1:
|
|
message = largs[0]
|
|
if type(message) == bytes:
|
|
message = str(message, 'UTF-8')
|
|
if len(largs) == 2:
|
|
next = largs[1]
|
|
if type(next) in [list, tuple]:
|
|
next = ' '.join(next)
|
|
if type(next) == bytes:
|
|
next = str(next, 'UTF-8')
|
|
message += ': ' + next
|
|
elif len(largs) == 3:
|
|
next = largs[2]
|
|
if type(next) == bytes:
|
|
next = str(next, 'UTF-8')
|
|
message += ' - ' + next
|
|
LOG_debug(message)
|
|
|
|
def connect(self):
|
|
""" Connect to Jabber server. """
|
|
if not self.buffer:
|
|
bufname = "%s.server.%s" % (SCRIPT_NAME, self.name)
|
|
self.buffer = weechat.buffer_search("python", bufname)
|
|
if not self.buffer:
|
|
self.buffer = weechat.buffer_new(bufname,
|
|
"jabber_buffer_input_cb", "",
|
|
"jabber_buffer_close_cb", "")
|
|
if self.buffer:
|
|
weechat.buffer_set(self.buffer, "short_name", self.name)
|
|
weechat.buffer_set(self.buffer, "localvar_set_type", "server")
|
|
weechat.buffer_set(self.buffer, "localvar_set_server", self.name)
|
|
weechat.buffer_set(self.buffer, "nicklist", "1")
|
|
weechat.buffer_set(self.buffer, "nicklist_display_groups", "1")
|
|
weechat.buffer_set(self.buffer, "display", "auto")
|
|
self.disconnect()
|
|
|
|
if not eval_expression(self.option_string("jid")):
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: JID must contain at least domain name"
|
|
% weechat.prefix("error"))
|
|
self.ping_up = False
|
|
self.client = None
|
|
return self.is_connected()
|
|
|
|
self.buddy = Buddy(jid=eval_expression(self.option_string("jid")), server=self)
|
|
|
|
transport = None
|
|
server = self.option_string("server")
|
|
port = self.option_integer("port")
|
|
aproxy = aget_proxy(self, ltypes=['http'])
|
|
if aproxy and 'type' in aproxy and aproxy['type'] in [0, 'http']:
|
|
lserver = (server, port,)
|
|
transport = transports.HTTPPROXYsocket(aproxy, lserver, use_srv=False)
|
|
|
|
conn = None
|
|
server_tuple = None
|
|
if server:
|
|
if port:
|
|
server_tuple = (server, port)
|
|
else:
|
|
server_tuple = (server)
|
|
|
|
debug=['always', 'nodebuilder']
|
|
self.client = xmpp.client.Client(server=self.buddy.domain,
|
|
debug=debug)
|
|
self.client.DEBUG = self.DEBUG
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: Server.connect {server_tuple} {aproxy} {self.client}"
|
|
% weechat.prefix("network"))
|
|
# self.client.connect() may produce a "socket.ssl() is deprecated"
|
|
# warning. Since the code producing the warning is outside this script,
|
|
# catch it and ignore.
|
|
original_filters = warnings.filters[:]
|
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
try:
|
|
conn = self.client.connect(server=server_tuple,
|
|
proxy=aproxy,
|
|
secure=None,
|
|
use_srv=True,
|
|
transport=transport,
|
|
)
|
|
except AttributeError as e:
|
|
# transports error
|
|
conn = ''
|
|
except Exception as e:
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: error in connect {e}"
|
|
% weechat.prefix("error"))
|
|
conn = ''
|
|
finally:
|
|
warnings.filters = original_filters
|
|
|
|
if not conn:
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: could not connect"
|
|
% weechat.prefix("error"))
|
|
self.ping_up = False
|
|
self.client = None
|
|
return False
|
|
|
|
# dunno
|
|
if hasattr(self.client, 'Connection') and self.client.Connection and \
|
|
hasattr(self.client.Connection, '_tcpsock') and \
|
|
hasattr(self.client.Connection._tcpsock, '_sslObj'):
|
|
ok = f"_sslObj={self.client.Connection._tcpsock._sslObj.fileno()}"
|
|
elif hasattr(self.client.Connection, '_sock'):
|
|
ok = f"_sock={self.client.Connection._sock.fileno()}"
|
|
elif transport:
|
|
ok = f"transport={transport._sock.fileno()}"
|
|
else:
|
|
ok = 'ok'
|
|
|
|
weechat.prnt(self.buffer, f"{SCRIPT_NAME}: connection {ok} with {conn}")
|
|
res = self.buddy.resource
|
|
if not res:
|
|
res = "WeeChat"
|
|
|
|
auth = self.client.auth(self.buddy.username,
|
|
eval_expression(self.option_string("password")),
|
|
res)
|
|
|
|
if auth:
|
|
if hasattr(self.client.Connection, '_sslObj'):
|
|
ok = f"_sslObj={self.client.Connection._sslObj.fileno()}"
|
|
self.sfn = self.client.Connection._sslObj.fileno()
|
|
elif hasattr(self.client.Connection, '_tcpsock'):
|
|
ok = f"_tcpsock={self.client.Connection._tcpsock.fileno()}"
|
|
self.sfn = self.client.Connection._sslObj.fileno()
|
|
elif hasattr(self.client.Connection, '_sock'):
|
|
ok = f"_sock={dir(self.client.Connection._sock)}"
|
|
self.sfn = self.client.Connection._sock.fileno()
|
|
else:
|
|
ok = 'ok'
|
|
weechat.prnt(self.buffer, f"{SCRIPT_NAME}: authentication {ok} (using %s)" % auth)
|
|
|
|
self.roster = self.client.getRoster()
|
|
self.client.RegisterHandler("presence", self.presence_handler)
|
|
self.client.RegisterHandler("iq", self.iq_handler)
|
|
self.client.RegisterHandler("message", self.message_handler)
|
|
self.client.sendInitPresence(requestRoster=1)
|
|
if self.sfn < 0:
|
|
weechat.prnt(self.buffer, f"{SCRIPT_NAME}: NO hooking {self.sfn}")
|
|
else:
|
|
self.hook_fd = weechat.hook_fd(self.sfn, 1, 0, 0, "jabber_fd_cb", "")
|
|
weechat.prnt(self.buffer, f"{SCRIPT_NAME}: hooking {self.sfn}")
|
|
weechat.buffer_set(self.buffer, "highlight_words", self.buddy.username)
|
|
weechat.buffer_set(self.buffer, "localvar_set_nick", self.buddy.username);
|
|
hook_away = weechat.hook_command_run("/away -all*", "jabber_away_command_run_cb", "")
|
|
|
|
|
|
# setting initial presence
|
|
priority = weechat.config_integer(self.options['priority'])
|
|
self.set_presence(show="",priority=priority)
|
|
|
|
|
|
self.ping_up = True
|
|
else:
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: could not authenticate"
|
|
% weechat.prefix("error"))
|
|
self.ping_up = False
|
|
self.client = None
|
|
return self.is_connected()
|
|
|
|
def is_connected(self):
|
|
"""Return connect status"""
|
|
if not self.client or not self.client.isConnected():
|
|
return False
|
|
else:
|
|
return True
|
|
|
|
def display_roster(self):
|
|
"""Return connect status"""
|
|
if not self.roster:
|
|
if hasattr(self.client, 'Roster'):
|
|
self.roster = self.client.Roster
|
|
else:
|
|
self.roster = self.client.getRoster()
|
|
weechat.prnt(self.buffer, "")
|
|
weechat.prnt(self.buffer, "Roster:")
|
|
for elt in self.roster.getItems():
|
|
weechat.prnt(self.buffer, repr(elt))
|
|
|
|
len_max = { 'alias': 5, 'jid': 5 }
|
|
return True
|
|
|
|
def add_chat(self, buddy):
|
|
"""Create a chat buffer for a buddy"""
|
|
chat = Chat(self, buddy, switch_to_buffer=False)
|
|
self.chats.append(chat)
|
|
return chat
|
|
|
|
def add_buddy(self, jid):
|
|
""" Add a new buddy """
|
|
self.client.Roster.Authorize(jid)
|
|
self.client.Roster.Subscribe(jid)
|
|
|
|
def del_buddy(self, jid):
|
|
""" Remove a buddy and/or deny authorization request """
|
|
self.client.Roster.Unauthorize(jid)
|
|
self.client.Roster.Unsubscribe(jid)
|
|
|
|
def print_debug_server(self, message):
|
|
""" Print debug message on server buffer. """
|
|
if jabber_debug_enabled():
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: %s" % (weechat.prefix("network"), message))
|
|
|
|
def print_debug_handler(self, handler_name, node):
|
|
""" Print debug message for a handler on server buffer. """
|
|
self.print_debug_server("%s_handler, xml message:\n%s"
|
|
% (handler_name,
|
|
node.__str__(fancy=True).encode("utf-8")))
|
|
|
|
def print_error(self, message):
|
|
""" Print error message on server buffer. """
|
|
if jabber_debug_enabled():
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: %s" % (weechat.prefix("error"), message))
|
|
|
|
def presence_handler(self, conn, node):
|
|
self.print_debug_handler("presence", node)
|
|
buddy = self.search_buddy_list(node.getFrom().getStripped().encode("utf-8"), by='jid')
|
|
if not buddy:
|
|
buddy = self.add_buddy(jid=node.getFrom())
|
|
action='update'
|
|
node_type = node.getType()
|
|
if node_type in ["error", "unavailable"]:
|
|
action='remove'
|
|
if action == 'update':
|
|
away = node.getShow() in ["away", "xa"]
|
|
status = ''
|
|
if node.getStatus():
|
|
status = node.getStatus().encode("utf-8")
|
|
if self.roster:
|
|
name = self.roster.getName(buddy.bare_jid)
|
|
if name:
|
|
buddy.set_name(name)
|
|
buddy.set_status(status=status, away=away)
|
|
self.update_nicklist(buddy=buddy, action=action)
|
|
return
|
|
|
|
def iq_handler(self, conn, node):
|
|
""" Receive iq message. """
|
|
self.print_debug_handler("iq", node)
|
|
#weechat.prnt(self.buffer, f"{SCRIPT_NAME}: iq handler")
|
|
if node.getFrom() == self.buddy.domain:
|
|
# type='result' => pong from server
|
|
# type='error' => error message from server
|
|
# The ping_up is set True on an error message to handle cases where
|
|
# the ping feature is not implemented on a server. It's a bit of a
|
|
# hack, but if we can receive an error from the server, we assume
|
|
# the connection to the server is up.
|
|
if node.getType() in ['result', 'error']:
|
|
self.delete_ping_timeout_timer() # Disable the timeout feature
|
|
self.ping_up = True
|
|
if not self.client.isConnected() and weechat.config_boolean(self.options['autoreconnect']):
|
|
self.connect()
|
|
|
|
def message_handler(self, conn, node):
|
|
""" Receive message. """
|
|
self.print_debug_handler("message", node)
|
|
node_type = node.getType()
|
|
if node_type in ['headline']: # MUC
|
|
a = """
|
|
jabber: message_handler, xml message: │
|
|
| b'<message xmlns="jabber:client" to="test_h@creep.im" │
|
|
| from="test_h@creep.im" type="headline">\n <event │
|
|
| xmlns="http://jabber.org/protocol/pubsub#event">\n <items │
|
|
| node="storage:bookmarks">\n <item id="current">\n │
|
|
| <storage xmlns="storage:bookmarks">\n <conference │
|
|
| jid="e2ee@muc.xmpp.org" autojoin="true">\n │
|
|
| <nick>test_h</nick>\n</conference>\n <conference │
|
|
| jid="jdev@muc.xmpp.org" autojoin="true">\n <nick>test_ │
|
|
| h</nick>\n</conference>\n</storage>\n</item>\n</items>\n</event>\ │
|
|
| n <addresses xmlns="http://jabber.org/protocol/address">\n │
|
|
| <address jid="test_h@creep.im/gajim.1QKI0MY3" type="replyto" │
|
|
| />\n</addresses>\n</message>\n' """
|
|
return
|
|
if node_type not in ["message", "chat", None]:
|
|
self.print_error("unknown message type: '%s'" % node_type)
|
|
return
|
|
jid = node.getFrom()
|
|
body = node.getBody()
|
|
if not jid or not body:
|
|
return
|
|
buddy = self.search_buddy_list(self.stringify_jid(jid), by='jid')
|
|
if not buddy:
|
|
buddy = self.add_buddy(jid=jid)
|
|
# If a chat buffer exists for the buddy, receive the message with that
|
|
# buffer even if private is off. The buffer may have been created with
|
|
# /jchat.
|
|
recv_object = self
|
|
if not buddy.chat and weechat.config_boolean(self.options['private']):
|
|
self.add_chat(buddy)
|
|
if buddy.chat:
|
|
recv_object = buddy.chat
|
|
recv_object.recv_message(buddy, body.encode("utf-8"))
|
|
|
|
def recv(self):
|
|
""" Receive something from Jabber server. """
|
|
LOG_debug(f"recv: {self.client}")
|
|
if not self.client:
|
|
return
|
|
try:
|
|
self.client.Process(1)
|
|
except xmpp.protocol.StreamError as e:
|
|
weechat.prnt('', '%s: Error from server: %s' %(SCRIPT_NAME, e))
|
|
self.disconnect()
|
|
if weechat.config_boolean(self.options['autoreconnect']):
|
|
autoreconnect_delay = 30
|
|
weechat.command('', '/wait %s /%s connect %s' %
|
|
(autoreconnect_delay, SCRIPT_COMMAND, self.name))
|
|
|
|
def recv_message(self, buddy, message):
|
|
""" Receive a message from buddy. """
|
|
LOG_debug(f"recv_message: {message}")
|
|
if type(message) == bytes:
|
|
message = str(message, 'UTF-8')
|
|
if type(buddy.alias) == bytes:
|
|
buddy.alias = str(buddy.alias, 'UTF-8')
|
|
nickc = weechat.config_get("weechat.color.chat_nick_other")
|
|
weechat.prnt_date_tags(self.buffer,
|
|
0,
|
|
f"notify_private,nick_{buddy.alias}" +
|
|
f",prefix_nick_%s,log1" %
|
|
(weechat.config_string(nickc)),
|
|
"%s%s\t%s" % (weechat.color("chat_nick_other"),
|
|
buddy.alias,
|
|
message))
|
|
|
|
def print_status(self, nickname, status):
|
|
""" Print a status in server window and in chat. """
|
|
if type(nickname) == bytes:
|
|
nickname = str(nickname, 'UTF-8')
|
|
weechat.prnt_date_tags(self.buffer,
|
|
0,
|
|
"no_highlight",
|
|
"%s%s has status %s" %
|
|
(weechat.prefix("action"),
|
|
nickname,
|
|
status))
|
|
for chat in self.chats:
|
|
if type(chat.buddy.alias) == bytes:
|
|
chat.buddy.alias = str(chat.buddy.alias, 'UTF-8')
|
|
if nickname in chat.buddy.alias:
|
|
chat.print_status(status)
|
|
break
|
|
|
|
def send_message(self, buddy, message):
|
|
""" Send a message to buddy.
|
|
|
|
The buddy argument can be either a jid string,
|
|
eg username@domain.tld/resource or a Buddy object instance.
|
|
"""
|
|
recipient = buddy
|
|
if isinstance(buddy, Buddy):
|
|
recipient = buddy.jid
|
|
if not self.ping_up:
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: unable to send message, connection is down"
|
|
% weechat.prefix("error"))
|
|
return
|
|
if not self.client:
|
|
return
|
|
try:
|
|
# bytes?
|
|
msg = xmpp.protocol.Message(to=recipient, body=message, typ='chat')
|
|
self.client.send(msg)
|
|
except Exception as e:
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: send_message error {e}"
|
|
% weechat.prefix("error"))
|
|
|
|
def send_message_from_input(self, input=''):
|
|
""" Send a message from input text on server buffer. """
|
|
# Input must be of format "name: message" where name is a jid, bare_jid
|
|
# or alias. The colon can be replaced with a comma as well.
|
|
# Split input into name and message.
|
|
LOG_debug(f"send_message_from_input: {input}")
|
|
if not re.compile(r'.+[:,].+').match(input):
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: %s" % (weechat.prefix("network"),
|
|
"Invalid send format {input}. Use jid: message"
|
|
))
|
|
return
|
|
name, message = re.split('[:,]', input, maxsplit=1)
|
|
buddy = self.search_buddy_list(name, by='alias')
|
|
if not buddy:
|
|
weechat.prnt(self.buffer,
|
|
f"%s{SCRIPT_NAME}: Invalid jid: %s" % (weechat.prefix("network"),
|
|
name))
|
|
return
|
|
# Send activity indicates user is no longer away, set it so
|
|
if self.buddy and self.buddy.away:
|
|
self.set_away('')
|
|
self.send_message(buddy=buddy, message=message)
|
|
try:
|
|
sender = self.buddy.alias
|
|
except:
|
|
sender = self.jid
|
|
if type(sender) == bytes:
|
|
sender = str(sender, 'UTF-8')
|
|
weechat.prnt_date_tags(self.buffer,
|
|
0,
|
|
"notify_none,no_highlight,nick_%s,prefix_nick_%s,log1" %
|
|
(sender,
|
|
weechat.config_string(weechat.config_get("weechat.color.chat_nick_self"))),
|
|
"%s%s\t%s" % (weechat.color("chat_nick_self"),
|
|
sender,
|
|
message.strip()))
|
|
|
|
def set_away(self, message):
|
|
""" Set/unset away on server.
|
|
|
|
If a message is provided, status is set to 'away'.
|
|
If no message, then status is set to 'online'.
|
|
"""
|
|
if message:
|
|
show = "xa"
|
|
status = message
|
|
priority = weechat.config_integer(self.options['away_priority'])
|
|
self.buddy.set_status(away=True, status=message)
|
|
else:
|
|
show = ""
|
|
status = None
|
|
priority = weechat.config_integer(self.options['priority'])
|
|
self.buddy.set_status(away=False)
|
|
self.set_presence(show, status, priority)
|
|
|
|
def set_presence(self, show=None, status=None, priority=None):
|
|
if not show == None: self.presence.setShow(show)
|
|
if not status == None: self.presence.setStatus(status)
|
|
if not priority == None: self.presence.setPriority(priority)
|
|
self.client.send(self.presence)
|
|
|
|
def add_buddy(self, jid=None):
|
|
buddy = Buddy(jid=jid, server=self)
|
|
buddy.resource = buddy.resource.encode("utf-8")
|
|
self.buddies.append(buddy)
|
|
return buddy
|
|
|
|
def display_buddies(self):
|
|
""" Display buddies. """
|
|
weechat.prnt(self.buffer, "")
|
|
weechat.prnt(self.buffer, "Buddies:")
|
|
|
|
len_max = { 'alias': 5, 'jid': 5 }
|
|
lines = []
|
|
for buddy in sorted(self.buddies, key=lambda x: x.jid.getStripped().encode('utf-8')):
|
|
alias = ''
|
|
if buddy.alias != buddy.bare_jid:
|
|
alias = buddy.alias
|
|
buddy_jid_string = buddy.jid.getStripped()
|
|
if type(buddy_jid_string) == bytes:
|
|
buddy_jid_string = str(buddy_jid_string, 'UTF-8')
|
|
lines.append( {
|
|
'jid': buddy_jid_string,
|
|
'alias': alias,
|
|
'status': buddy.away_string(),
|
|
})
|
|
if len(alias) > len_max['alias']:
|
|
len_max['alias'] = len(alias)
|
|
if len(buddy_jid_string) > len_max['jid']:
|
|
len_max['jid'] = len(buddy_jid_string)
|
|
prnt_format = " %s%-" + str(len_max['jid']) + "s %-" + str(len_max['alias']) + "s %s"
|
|
weechat.prnt(self.buffer, prnt_format % ('', 'JID', 'Alias', 'Status'))
|
|
for line in lines:
|
|
weechat.prnt(self.buffer, prnt_format % (weechat.color("chat_nick"),
|
|
line['jid'],
|
|
line['alias'],
|
|
line['status'],
|
|
))
|
|
|
|
def stringify_jid(self, jid, wresource=1):
|
|
""" Serialise JID into string.
|
|
|
|
Args:
|
|
jid: xmpp.protocol.JID, JID instance to serialize
|
|
|
|
Notes:
|
|
Method is based on original JID.__str__ but with hack to allow
|
|
non-ascii in resource names.
|
|
"""
|
|
if jid.node:
|
|
jid_str = jid.node + '@' + jid.domain
|
|
else:
|
|
jid_str = jid.domain
|
|
if wresource and jid.resource:
|
|
# concatenate jid with resource delimiter first and encode them
|
|
# into utf-8, else it will raise UnicodeException becaouse of
|
|
# slash character :((
|
|
return (jid_str + '/').encode("utf-8") + jid.resource.encode("utf-8")
|
|
return jid_str.encode("utf-8")
|
|
|
|
def search_buddy_list(self, name, by='jid'):
|
|
""" Search for a buddy by name.
|
|
|
|
Args:
|
|
name: string, the buddy name to search, eg the jid or alias
|
|
by: string, either 'alias' or 'jid', determines which Buddy
|
|
property to match on, default 'jid'
|
|
|
|
Notes:
|
|
If the 'by' parameter is set to 'jid', the search matches on all
|
|
Buddy object jid properties, followed by all bare_jid properties.
|
|
Once a match is found it is returned.
|
|
|
|
If the 'by' parameter is set to 'alias', the search matches on all
|
|
Buddy object alias properties.
|
|
|
|
Generally, set the 'by' parameter to 'jid' when the jid is provided
|
|
from a server, for example from a received message. Set 'by' to
|
|
'alias' when the jid is provided by the user.
|
|
"""
|
|
if by == 'jid':
|
|
for buddy in self.buddies:
|
|
if self.stringify_jid(buddy.jid) == name:
|
|
return buddy
|
|
for buddy in self.buddies:
|
|
if buddy.bare_jid == name:
|
|
return buddy
|
|
else:
|
|
for buddy in self.buddies:
|
|
if buddy.alias == name:
|
|
return buddy
|
|
return None
|
|
|
|
def update_nicklist(self, buddy=None, action=None):
|
|
"""Update buddy in nicklist
|
|
Args:
|
|
buddy: Buddy object instance
|
|
action: string, one of 'update' or 'remove'
|
|
"""
|
|
if not buddy:
|
|
return
|
|
if not action in ['remove', 'update']:
|
|
return
|
|
ptr_nick_gui = weechat.nicklist_search_nick(self.buffer, "", buddy.alias)
|
|
weechat.nicklist_remove_nick(self.buffer, ptr_nick_gui)
|
|
msg = ''
|
|
prefix = ''
|
|
color = ''
|
|
away = ''
|
|
if action == 'update':
|
|
nick_color = "bar_fg"
|
|
if buddy.away:
|
|
nick_color = "weechat.color.nicklist_away"
|
|
weechat.nicklist_add_nick(self.buffer, "", buddy.alias,
|
|
nick_color, "", "", 1)
|
|
if not ptr_nick_gui:
|
|
msg = 'joined'
|
|
prefix = 'join'
|
|
color = 'message_join'
|
|
away = buddy.away_string()
|
|
if action == 'remove':
|
|
msg = 'quit'
|
|
prefix = 'quit'
|
|
color = 'message_quit'
|
|
if msg:
|
|
weechat.prnt(self.buffer, "%s%s%s%s has %s %s"
|
|
% (weechat.prefix(prefix),
|
|
weechat.color("chat_nick"),
|
|
buddy.alias,
|
|
jabber_config_color(color),
|
|
msg,
|
|
away))
|
|
return
|
|
|
|
def add_ping_timer(self):
|
|
if self.ping_timer:
|
|
self.delete_ping_timer()
|
|
if not self.option_integer('ping_interval'):
|
|
return
|
|
self.ping_timer = weechat.hook_timer( self.option_integer('ping_interval') * 1000,
|
|
0, 0, "jabber_ping_timer", self.name)
|
|
return
|
|
|
|
def delete_ping_timer(self):
|
|
if self.ping_timer:
|
|
weechat.unhook(self.ping_timer)
|
|
self.ping_time = None
|
|
return
|
|
|
|
def add_ping_timeout_timer(self):
|
|
if self.ping_timeout_timer:
|
|
self.delete_ping_timeout_timer()
|
|
if not self.option_integer('ping_timeout'):
|
|
return
|
|
self.ping_timeout_timer = weechat.hook_timer(
|
|
self.option_integer('ping_timeout') * 1000, 0, 1,
|
|
"jabber_ping_timeout_timer", self.name)
|
|
return
|
|
|
|
def delete_ping_timeout_timer(self):
|
|
if self.ping_timeout_timer:
|
|
weechat.unhook(self.ping_timeout_timer)
|
|
self.ping_timeout_timer = None
|
|
return
|
|
|
|
def ping(self):
|
|
if not self.is_connected():
|
|
if not self.connect():
|
|
return
|
|
iq = xmpp.protocol.Iq(to=self.buddy.domain, typ='get')
|
|
iq.addChild( name= "ping", namespace = "urn:xmpp:ping" )
|
|
id = self.client.send(iq)
|
|
self.print_debug_handler("ping", iq)
|
|
self.add_ping_timeout_timer()
|
|
return
|
|
|
|
def ping_time_out(self):
|
|
self.delete_ping_timeout_timer()
|
|
self.ping_up = False
|
|
# A ping timeout indicates a server connection problem. Disconnect
|
|
# completely.
|
|
try:
|
|
self.client.disconnected()
|
|
except IOError:
|
|
# An IOError is raised by the default DisconnectHandler
|
|
pass
|
|
self.disconnect()
|
|
return
|
|
|
|
def disconnect(self):
|
|
""" Disconnect from Jabber server. """
|
|
if self.hook_fd != None:
|
|
weechat.unhook(self.hook_fd)
|
|
self.hook_fd = None
|
|
if self.client != None:
|
|
#if self.client.isConnected():
|
|
# self.client.disconnect()
|
|
self.client = None
|
|
self.jid = None
|
|
self.sfn = None
|
|
self.buddy = None
|
|
weechat.nicklist_remove_all(self.buffer)
|
|
|
|
def close_buffer(self):
|
|
""" Close server buffer. """
|
|
if self.buffer != "":
|
|
weechat.buffer_close(self.buffer)
|
|
self.buffer = ""
|
|
|
|
def delete(self, deleteOptions=False):
|
|
""" Delete server. """
|
|
for chat in self.chats:
|
|
chat.delete()
|
|
self.delete_ping_timer()
|
|
self.delete_ping_timeout_timer()
|
|
self.disconnect()
|
|
self.close_buffer()
|
|
if deleteOptions:
|
|
for name, option in self.options.items():
|
|
weechat.config_option_free(option)
|
|
|
|
def eval_expression(option_name):
|
|
""" Return a evaluated expression """
|
|
if int(version) >= 0x00040200:
|
|
return weechat.string_eval_expression(option_name,{},{},{})
|
|
else:
|
|
return option_name
|
|
|
|
def jabber_search_server_by_name(name):
|
|
""" Search a server by name. """
|
|
global jabber_servers
|
|
for server in jabber_servers:
|
|
if server.name == name:
|
|
return server
|
|
return None
|
|
|
|
def jabber_search_context(buffer):
|
|
""" Search a server / chat for a buffer. """
|
|
global jabber_servers
|
|
context = { "server": None, "chat": None }
|
|
for server in jabber_servers:
|
|
if server.buffer == buffer:
|
|
context["server"] = server
|
|
return context
|
|
for chat in server.chats:
|
|
if chat.buffer == buffer:
|
|
context["server"] = server
|
|
context["chat"] = chat
|
|
return context
|
|
return context
|
|
|
|
def jabber_search_context_by_name(server_name):
|
|
"""Search for buffer given name of server. """
|
|
|
|
bufname = "%s.server.%s" % (SCRIPT_NAME, server_name)
|
|
return jabber_search_context(weechat.buffer_search("python", bufname))
|
|
|
|
|
|
# =================================[ chats ]==================================
|
|
|
|
class Chat:
|
|
""" Class to manage private chat with buddy or MUC. """
|
|
|
|
def __init__(self, server, buddy, switch_to_buffer):
|
|
""" Init chat """
|
|
self.server = server
|
|
self.buddy = buddy
|
|
buddy.chat = self
|
|
if type(buddy.alias) == bytes:
|
|
buddy.alias = str(buddy.alias, 'UTF-8')
|
|
alias = self.buddy.alias
|
|
bufname = "%s.%s.%s" % (SCRIPT_NAME, server.name, alias)
|
|
self.buffer = weechat.buffer_search("python", bufname)
|
|
if not self.buffer:
|
|
self.buffer = weechat.buffer_new(bufname,
|
|
"jabber_buffer_input_cb", "",
|
|
"jabber_buffer_close_cb", "")
|
|
self.buffer_title = self.buddy.alias
|
|
if self.buffer:
|
|
weechat.buffer_set(self.buffer, "title", self.buffer_title)
|
|
weechat.buffer_set(self.buffer, "short_name", self.buddy.alias)
|
|
weechat.buffer_set(self.buffer, "localvar_set_type", "private")
|
|
weechat.buffer_set(self.buffer, "localvar_set_server", server.name)
|
|
weechat.buffer_set(self.buffer, "localvar_set_channel", self.buddy.alias)
|
|
weechat.hook_signal_send("logger_backlog",
|
|
weechat.WEECHAT_HOOK_SIGNAL_POINTER, self.buffer)
|
|
if switch_to_buffer:
|
|
weechat.buffer_set(self.buffer, "display", "auto")
|
|
|
|
def recv_message(self, buddy, message):
|
|
""" Receive a message from buddy. """
|
|
LOG_debug(f"recv_message: {message}")
|
|
if type(message) == bytes:
|
|
message = str(message, 'UTF-8')
|
|
if type(buddy.alias) == bytes:
|
|
buddy.alias = str(buddy.alias, 'UTF-8')
|
|
if buddy.alias != self.buffer_title:
|
|
self.buffer_title = buddy.alias
|
|
weechat.buffer_set(self.buffer, "title", "%s" % self.buffer_title)
|
|
weechat.prnt_date_tags(self.buffer, 0,
|
|
"notify_private,nick_%s,prefix_nick_%s,log1" %
|
|
(buddy.alias,
|
|
weechat.config_string(weechat.config_get("weechat.color.chat_nick_other"))),
|
|
"%s%s\t%s" % (weechat.color("chat_nick_other"),
|
|
buddy.alias,
|
|
message))
|
|
|
|
def send_message(self, message):
|
|
""" Send message to buddy. """
|
|
if not self.server.ping_up:
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: unable to send message, connection is down"
|
|
% weechat.prefix("error"))
|
|
return
|
|
if type(self.server.buddy.alias) == bytes:
|
|
self.server.buddy.alias = str(self.server.buddy.alias, 'UTF-8')
|
|
try:
|
|
self.server.send_message(self.buddy, message)
|
|
weechat.prnt_date_tags(self.buffer, 0,
|
|
"notify_none,no_highlight,nick_%s,prefix_nick_%s,log1" %
|
|
(self.server.buddy.alias,
|
|
weechat.config_string(weechat.config_get("weechat.color.chat_nick_self"))),
|
|
"%s%s\t%s" % (weechat.color("chat_nick_self"),
|
|
self.server.buddy.alias,
|
|
message))
|
|
except Exception as e:
|
|
weechat.prnt(self.buffer, f"%s{SCRIPT_NAME}: error sending message {e}"
|
|
% weechat.prefix("error"))
|
|
|
|
def print_status(self, status):
|
|
""" Print a status message in chat. """
|
|
weechat.prnt(self.buffer, "%s%s has status %s" %
|
|
(weechat.prefix("action"),
|
|
self.buddy.alias,
|
|
status))
|
|
|
|
def close_buffer(self):
|
|
""" Close chat buffer. """
|
|
if self.buffer != "":
|
|
weechat.buffer_close(self.buffer)
|
|
self.buffer = ""
|
|
|
|
def delete(self):
|
|
""" Delete chat. """
|
|
self.close_buffer()
|
|
|
|
# =================================[ buddies ]==================================
|
|
|
|
class Buddy:
|
|
""" Class to manage buddies. """
|
|
def __init__(self, jid=None, chat=None, server=None ):
|
|
""" Init buddy
|
|
|
|
Args:
|
|
jid: xmpp.protocol.JID object instance or string
|
|
chat: Chat object instance
|
|
server: Server object instance
|
|
|
|
The jid argument can be provided either as a xmpp.protocol.JID object
|
|
instance or as a string, eg "username@domain.tld/resource". If a string
|
|
is provided, it is converted and stored as a xmpp.protocol.JID object
|
|
instance.
|
|
"""
|
|
|
|
# The jid argument of xmpp.protocol.JID can be either a string or a
|
|
# xmpp.protocol.JID object instance itself.
|
|
self.jid = xmpp.protocol.JID(jid=jid)
|
|
self.chat = chat
|
|
self.server = server
|
|
self.bare_jid = ''
|
|
self.username = ''
|
|
self.name = ''
|
|
self.domain = ''
|
|
self.resource = ''
|
|
self.alias = ''
|
|
self.away = True
|
|
self.status = ''
|
|
|
|
self.parse_jid()
|
|
self.set_alias()
|
|
return
|
|
|
|
def away_string(self):
|
|
""" Return a string with away and status, with color codes. """
|
|
if not self:
|
|
return ''
|
|
if not self.away:
|
|
return ''
|
|
str_colon = ": "
|
|
if not self.status:
|
|
str_colon = ""
|
|
return "%s(%saway%s%s%s)" % (weechat.color("chat_delimiters"),
|
|
weechat.color("chat"),
|
|
str_colon,
|
|
self.status.replace("\n", " "),
|
|
weechat.color("chat_delimiters"))
|
|
|
|
def parse_jid(self):
|
|
"""Parse the jid property.
|
|
|
|
The table shows how the jid is parsed and which properties are updated.
|
|
|
|
Property Value
|
|
jid myuser@mydomain.tld/myresource
|
|
|
|
bare_jid myuser@mydomain.tld
|
|
username myuser
|
|
domain mydomain.tld
|
|
resource myresource
|
|
"""
|
|
if not self.jid:
|
|
return
|
|
self.bare_jid = self.jid.getStripped().encode("utf-8")
|
|
self.username = self.jid.getNode()
|
|
self.domain = self.jid.getDomain()
|
|
self.resource = self.jid.getResource()
|
|
return
|
|
|
|
def set_alias(self):
|
|
"""Set the buddy alias.
|
|
|
|
If an alias is defined in jabber_jid_aliases, it is used. Otherwise the
|
|
alias is set to self.bare_jid or self.name if it exists.
|
|
"""
|
|
self.alias = self.bare_jid
|
|
if not self.bare_jid:
|
|
self.alias = ''
|
|
if self.name:
|
|
if type(name) == bytes:
|
|
name = str(name, 'UTF-8')
|
|
self.alias = self.name
|
|
global jabber_jid_aliases
|
|
for alias, jid in jabber_jid_aliases.items():
|
|
if jid == self.bare_jid:
|
|
self.alias = alias
|
|
break
|
|
return
|
|
|
|
def set_name(self, name=''):
|
|
if type(name) == bytes:
|
|
name = str(name, 'UTF-8')
|
|
self.name = name
|
|
self.set_alias()
|
|
return
|
|
|
|
def set_status(self, away=True, status=''):
|
|
"""Set the buddy status.
|
|
|
|
Two properties define the buddy status.
|
|
away - boolean, indicates whether the buddy is away or not.
|
|
status - string, a message indicating the away status, eg 'in a meeting'
|
|
Comparable to xmpp presence <status/> element.
|
|
"""
|
|
if not away and not status:
|
|
status = 'online'
|
|
# If the status has changed print a message on the server buffer
|
|
if self.away != away or self.status != status:
|
|
self.server.print_status(self.alias, status)
|
|
self.away = away
|
|
self.status = status
|
|
return
|
|
|
|
# ================================[ commands ]================================
|
|
|
|
def jabber_hook_commands_and_completions():
|
|
""" Hook commands and completions. """
|
|
weechat.hook_command(SCRIPT_COMMAND, "Manage Jabber servers",
|
|
"list || add <name> <jid> <password> [<server>[:<port>]]"
|
|
" || connect|disconnect|del [<server>] || alias [add|del <alias> <jid>]"
|
|
" || away [<message>] || buddies || priority [<priority>]"
|
|
" || status [<message>] || presence [online|chat|away|xa|dnd]"
|
|
" || ping || debug || set <server> <setting> [<value>]",
|
|
" list: list servers and chats\n"
|
|
" add: add a server\n"
|
|
" connect: connect to server using password\n"
|
|
"disconnect: disconnect from server\n"
|
|
" del: delete server\n"
|
|
" ping: ping server\n"
|
|
" alias: manage jid aliases\n"
|
|
" away: set away with a message (if no message, away is unset)\n"
|
|
" priority: set priority\n"
|
|
" status: set status message\n"
|
|
" presence: set presence status\n"
|
|
" buddies: display buddies on server\n"
|
|
" debug: toggle jabber debug on/off (for all servers)\n"
|
|
"\n"
|
|
"Without argument, this command lists servers and chats.\n"
|
|
"\n"
|
|
"Examples:\n"
|
|
" Add a server: /jabber add myserver user@server.tld password\n"
|
|
" Add gtalk server: /jabber add myserver user@gmail.com password talk.google.com:5223\n"
|
|
" Connect to server: /jabber connect myserver\n"
|
|
" Disconnect: /jabber disconnect myserver\n"
|
|
" Delete server: /jabber del myserver\n"
|
|
"\n"
|
|
"Aliases:\n"
|
|
" List aliases: /jabber alias \n"
|
|
" Add an alias: /jabber alias add alias_name jid\n"
|
|
" Delete an alias: /jabber alias del alias_name\n"
|
|
"\n"
|
|
"Other jabber commands:\n"
|
|
" Chat with a buddy (pv buffer): /jchat\n"
|
|
" Add buddy to roster: /invite\n"
|
|
" Remove buddy from roster: /kick\n"
|
|
" Send message to buddy: /jmsg",
|
|
"list %(jabber_servers)"
|
|
" || add %(jabber_servers)"
|
|
" || connect %(jabber_servers)"
|
|
" || disconnect %(jabber_servers)"
|
|
" || del %(jabber_servers)"
|
|
" || alias add|del %(jabber_jid_aliases)"
|
|
" || away"
|
|
" || ping"
|
|
" || priority"
|
|
" || roster"
|
|
" || status"
|
|
" || presence online|chat|away|xa|dnd"
|
|
" || buddies"
|
|
" || debug",
|
|
"jabber_cmd_jabber", "")
|
|
weechat.hook_command("jchat", "Chat with a Jabber buddy",
|
|
"<buddy>",
|
|
"buddy: buddy id",
|
|
"",
|
|
"jabber_cmd_jchat", "")
|
|
weechat.hook_command("jmsg", "Send a messge to a buddy",
|
|
"[-server <server>] <buddy> <text>",
|
|
"server: name of jabber server buddy is on\n"
|
|
" buddy: buddy id\n"
|
|
" text: text to send",
|
|
"",
|
|
"jabber_cmd_jmsg", "")
|
|
weechat.hook_command("invite", "Add a buddy to your roster",
|
|
"<buddy>",
|
|
"buddy: buddy id",
|
|
"",
|
|
"jabber_cmd_invite", "")
|
|
weechat.hook_command("kick", "Remove a buddy from your roster, or deny auth",
|
|
"<buddy>",
|
|
"buddy: buddy id",
|
|
"",
|
|
"jabber_cmd_kick", "")
|
|
weechat.hook_completion("jabber_servers", "list of jabber servers",
|
|
"jabber_completion_servers", "")
|
|
weechat.hook_completion("jabber_jid_aliases", "list of jabber jid aliases",
|
|
"jabber_completion_jid_aliases", "")
|
|
|
|
def jabber_list_servers_chats(name):
|
|
""" List servers and chats. """
|
|
global jabber_servers
|
|
weechat.prnt("", "")
|
|
if len(jabber_servers) > 0:
|
|
weechat.prnt("", "jabber servers:")
|
|
for server in jabber_servers:
|
|
if name == "" or server.name.find(name) >= 0:
|
|
conn_server = ''
|
|
if server.option_string("server"):
|
|
conn_server = ':'.join(
|
|
(server.option_string("server"),
|
|
server.option_string("port")))
|
|
connected = ""
|
|
if server.sfn and server.sfn >= 0:
|
|
connected = "(connected)"
|
|
|
|
weechat.prnt("", " %s - %s %s %s" % (server.name,
|
|
eval_expression(server.option_string("jid")), conn_server, connected))
|
|
for chat in server.chats:
|
|
weechat.prnt("", " chat with %s" % (chat.buddy))
|
|
else:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: no server defined")
|
|
|
|
def jabber_cmd_jabber(data, buffer, args):
|
|
""" Command '/jabber'. """
|
|
global jabber_servers, jabber_config_option
|
|
if args == "" or args == "list":
|
|
jabber_list_servers_chats("")
|
|
else:
|
|
argv = args.split(" ")
|
|
argv1eol = ""
|
|
pos = args.find(" ")
|
|
if pos > 0:
|
|
argv1eol = args[pos+1:]
|
|
if argv[0] == "list":
|
|
jabber_list_servers_chats(argv[1])
|
|
elif argv[0] == "add":
|
|
if len(argv) >= 4:
|
|
server = jabber_search_server_by_name(argv[1])
|
|
if server:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: server '%s' already exists" % argv[1])
|
|
else:
|
|
kwargs = {'jid': argv[2], 'password': argv[3]}
|
|
if len(argv) > 4:
|
|
conn_server, _, conn_port = argv[4].partition(':')
|
|
if conn_port and not conn_port.isdigit():
|
|
weechat.prnt("", f"{SCRIPT_NAME}: error, invalid port, digits only")
|
|
return weechat.WEECHAT_RC_OK
|
|
if conn_server: kwargs['server'] = conn_server
|
|
if conn_port: kwargs['port'] = conn_port
|
|
server = Server(argv[1], **kwargs)
|
|
jabber_servers.append(server)
|
|
weechat.prnt("", f"{SCRIPT_NAME}: server '%s' created" % argv[1])
|
|
else:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: unable to add server, missing arguments")
|
|
weechat.prnt("", f"{SCRIPT_NAME}: usage: /jabber add name jid password [server[:port]]")
|
|
elif argv[0] == "alias":
|
|
alias_command = AliasCommand(buffer, argv=argv[1:])
|
|
alias_command.run()
|
|
elif argv[0] == "connect":
|
|
server = None
|
|
if len(argv) >= 2:
|
|
server = jabber_search_server_by_name(argv[1])
|
|
if not server:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: server '%s' not found" % argv[1])
|
|
else:
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
server = context["server"]
|
|
if server:
|
|
if weechat.config_boolean(server.options['autoreconnect']):
|
|
server.ping() # This will connect and update ping status
|
|
server.add_ping_timer()
|
|
else:
|
|
server.connect()
|
|
elif argv[0] == "disconnect":
|
|
server = None
|
|
if len(argv) >= 2:
|
|
server = jabber_search_server_by_name(argv[1])
|
|
if not server:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: server '%s' not found" % argv[1])
|
|
else:
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
server = context["server"]
|
|
context = jabber_search_context(buffer)
|
|
if server:
|
|
server.delete_ping_timer()
|
|
server.disconnect()
|
|
elif argv[0] == "del":
|
|
if len(argv) >= 2:
|
|
server = jabber_search_server_by_name(argv[1])
|
|
if server:
|
|
server.delete(deleteOptions=True)
|
|
jabber_servers.remove(server)
|
|
weechat.prnt("", f"{SCRIPT_NAME}: server '%s' deleted" % argv[1])
|
|
else:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: server '%s' not found" % argv[1])
|
|
elif argv[0] == "send":
|
|
if len(argv) >= 3:
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
buddy = context['server'].search_buddy_list(argv[1], by='alias')
|
|
message = ' '.join(argv[2:])
|
|
context["server"].send_message(buddy, message)
|
|
elif argv[0] == "read":
|
|
jabber_config_read()
|
|
elif argv[0] == "away":
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
context["server"].set_away(argv1eol)
|
|
elif argv[0] == "priority":
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
if len(argv) == 1:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: priority = %d" % int(context["server"].presence.getPriority()))
|
|
elif len(argv) == 2 and argv[1].isdigit():
|
|
context["server"].set_presence(priority=int(argv[1]))
|
|
else:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: you need to specify priority as positive integer between 0 and 65535")
|
|
elif argv[0] == "status":
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
if len(argv) == 1:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: status = %s" % context["server"].presence.getStatus())
|
|
else:
|
|
context["server"].set_presence(status=argv1eol)
|
|
|
|
elif argv[0] == "presence":
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
if len(argv) == 1:
|
|
show = context["server"].presence.getShow()
|
|
if show == "": show = "online"
|
|
weechat.prnt("", f"{SCRIPT_NAME}: presence = %s" % show)
|
|
elif not re.match(r'^(?:online|chat|away|xa|dnd)$', argv[1]):
|
|
weechat.prnt("", f"{SCRIPT_NAME}: Presence should be one of: online, chat, away, xa, dnd")
|
|
else:
|
|
if argv[1] == "online": show = ""
|
|
else: show = argv[1]
|
|
context["server"].set_presence(show=show)
|
|
elif argv[0] == "buddies":
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
context["server"].display_buddies()
|
|
elif argv[0] == "roster":
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
context["server"].display_roster()
|
|
elif argv[0] == "ping":
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
context["server"].ping()
|
|
elif argv[0] == "debug":
|
|
weechat.config_option_set(jabber_config_option["debug"], "toggle", 1)
|
|
if jabber_debug_enabled():
|
|
weechat.prnt("", f"{SCRIPT_NAME}: debug is now ON")
|
|
else:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: debug is now off")
|
|
else:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: unknown action")
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
def jabber_cmd_jchat(data, buffer, args):
|
|
""" Command '/jchat'. """
|
|
if args:
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
buddy = context["server"].search_buddy_list(args, by='alias')
|
|
if not buddy:
|
|
buddy = context["server"].add_buddy(jid=args)
|
|
if not buddy.chat:
|
|
context["server"].add_chat(buddy)
|
|
weechat.buffer_set(buddy.chat.buffer, "display", "auto")
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
def jabber_cmd_jmsg(data, buffer, args):
|
|
""" Command '/jmsg'. """
|
|
if args:
|
|
argv = args.split()
|
|
if len(argv) < 2:
|
|
return weechat.WEECHAT_RC_OK
|
|
if argv[0] == '-server':
|
|
context = jabber_search_context_by_name(argv[1])
|
|
recipient = argv[2]
|
|
message = " ".join(argv[3:])
|
|
else:
|
|
context = jabber_search_context(buffer)
|
|
recipient = argv[0]
|
|
message = " ".join(argv[1:])
|
|
if context["server"]:
|
|
buddy = context['server'].search_buddy_list(recipient, by='alias')
|
|
context["server"].send_message(buddy, message)
|
|
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
def jabber_cmd_invite(data, buffer, args):
|
|
""" Command '/invite'. """
|
|
if args:
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
context["server"].add_buddy(args)
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
def jabber_cmd_kick(data, buffer, args):
|
|
""" Command '/kick'. """
|
|
if args:
|
|
context = jabber_search_context(buffer)
|
|
if context["server"]:
|
|
context["server"].del_buddy(args)
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
def jabber_away_command_run_cb(data, buffer, command):
|
|
""" Callback called when /away -all command is run """
|
|
global jabber_servers
|
|
words = command.split(None, 2)
|
|
if len(words) < 2:
|
|
return
|
|
message = ''
|
|
if len(words) > 2:
|
|
message = words[2]
|
|
for server in jabber_servers:
|
|
server.set_away(message)
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
class AliasCommand(object):
|
|
"""Class representing a jabber alias command, ie /jabber alias ..."""
|
|
|
|
def __init__(self, buffer, argv=None):
|
|
"""
|
|
Args:
|
|
bufffer: the weechat buffer the command was run in
|
|
argv: list, the arguments provided with the command.
|
|
Example, if the command is "/jabber alias add abc abc@server.tld"
|
|
argv = ['add', 'abc', 'abc@server.tld']
|
|
"""
|
|
self.buffer = buffer
|
|
self.argv = []
|
|
if argv:
|
|
self.argv = argv
|
|
self.action = ''
|
|
self.jid = ''
|
|
self.alias = ''
|
|
self.parse()
|
|
return
|
|
|
|
def add(self):
|
|
"""Run a "/jabber alias add" command"""
|
|
global jabber_jid_aliases
|
|
if not self.alias or not self.jid:
|
|
weechat.prnt("", "\njabber: unable to add alias, missing arguments")
|
|
weechat.prnt("", f"{SCRIPT_NAME}: usage: /jabber alias add alias_name jid")
|
|
return
|
|
# Restrict the character set of aliases. The characters must be writable to
|
|
# config file.
|
|
invalid_re = re.compile(r'[^a-zA-Z0-9\[\]\\\^_\-{|}@\.]')
|
|
if invalid_re.search(self.alias):
|
|
weechat.prnt("", "\njabber: invalid alias: %s" % self.alias)
|
|
weechat.prnt("", f"{SCRIPT_NAME}: use only characters:" +
|
|
"a-z A-Z 0-9 [ \ ] ^ _ - { | } @ .")
|
|
return
|
|
# Ensure alias and jid are reasonable length.
|
|
max_len = 64
|
|
if len(self.alias) > max_len:
|
|
weechat.prnt("", "\njabber: invalid alias: %s" % self.alias)
|
|
weechat.prnt("", f"{SCRIPT_NAME}: must be no more than %s characters long" % max_len)
|
|
return
|
|
if len(self.jid) > max_len:
|
|
weechat.prnt("", "\njabber: invalid jid: %s" % self.jid)
|
|
weechat.prnt("", f"{SCRIPT_NAME}: must be no more than %s characters long" % max_len)
|
|
return
|
|
jid = self.jid.encode("utf-8")
|
|
alias = self.alias.encode("utf-8")
|
|
if alias in jabber_jid_aliases.keys():
|
|
weechat.prnt("", "\njabber: unable to add alias: %s" % (alias))
|
|
weechat.prnt("", f"{SCRIPT_NAME}: alias already exists, delete first")
|
|
return
|
|
if jid in jabber_jid_aliases.values():
|
|
weechat.prnt("", "\njabber: unable to add alias: %s" % (alias))
|
|
for a, j in jabber_jid_aliases.items():
|
|
if j == jid:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: jid '%s' is already aliased as '%s', delete first" %
|
|
(j, a))
|
|
break
|
|
jabber_jid_aliases[alias] = jid
|
|
self.alias_reset(jid)
|
|
return
|
|
|
|
def alias_reset(self, jid):
|
|
"""Reset objects related to the jid modified by an an alias command
|
|
|
|
Update any existing buddy objects, server nicklists, and chat objects
|
|
that may be using the buddy with the provided jid.
|
|
"""
|
|
global jabber_servers
|
|
for server in jabber_servers:
|
|
buddy = server.search_buddy_list(jid, by='jid')
|
|
if not buddy:
|
|
continue
|
|
server.update_nicklist(buddy=buddy, action='remove')
|
|
buddy.set_alias()
|
|
server.update_nicklist(buddy=buddy, action='update')
|
|
if buddy.chat:
|
|
switch_to_buffer = False
|
|
if buddy.chat.buffer == self.buffer:
|
|
switch_to_buffer = True
|
|
buddy.chat.delete()
|
|
new_chat = server.add_chat(buddy)
|
|
if switch_to_buffer:
|
|
weechat.buffer_set(new_chat.buffer, "display", "auto")
|
|
return
|
|
|
|
def delete(self):
|
|
"""Run a "/jabber alias del" command"""
|
|
global jabber_jid_aliases
|
|
if not self.alias:
|
|
weechat.prnt("", "\njabber: unable to delete alias, missing arguments")
|
|
weechat.prnt("", f"{SCRIPT_NAME}: usage: /jabber alias del alias_name")
|
|
return
|
|
if not self.alias in jabber_jid_aliases:
|
|
weechat.prnt("", "\njabber: unable to delete alias '%s', not found" % (self.alias))
|
|
return
|
|
jid = jabber_jid_aliases[self.alias]
|
|
del jabber_jid_aliases[self.alias]
|
|
self.alias_reset(jid)
|
|
return
|
|
|
|
def list(self):
|
|
"""Run a "/jabber alias" command to list aliases"""
|
|
global jabber_jid_aliases
|
|
weechat.prnt("", "")
|
|
if len(jabber_jid_aliases) <= 0:
|
|
weechat.prnt("", f"{SCRIPT_NAME}: no aliases defined")
|
|
return
|
|
weechat.prnt("", "jabber jid aliases:")
|
|
len_alias = 5
|
|
len_jid = 5
|
|
for alias, jid in jabber_jid_aliases.items():
|
|
if len_alias < len(alias):
|
|
len_alias = len(alias)
|
|
if len_jid < len(jid):
|
|
len_jid = len(jid)
|
|
prnt_format = " %-" + str(len_alias) + "s %-" + str(len_jid) + "s"
|
|
weechat.prnt("", prnt_format % ('Alias', 'JID'))
|
|
for alias, jid in sorted(jabber_jid_aliases.items()):
|
|
weechat.prnt("", prnt_format % (alias, jid))
|
|
return
|
|
|
|
def parse(self):
|
|
"""Parse the alias command into components"""
|
|
if len(self.argv) <= 0:
|
|
return
|
|
self.action = self.argv[0]
|
|
if len(self.argv) > 1:
|
|
# Pad argv list to prevent IndexError exceptions
|
|
while len(self.argv) < 3: self.argv.append('')
|
|
self.alias = self.argv[1]
|
|
self.jid = self.argv[2]
|
|
return
|
|
|
|
def run(self):
|
|
"""Execute the alias command."""
|
|
if self.action == 'add':
|
|
self.add()
|
|
elif self.action == 'del':
|
|
self.delete()
|
|
self.list()
|
|
return
|
|
|
|
def jabber_completion_servers(data, completion_item, buffer, completion):
|
|
""" Completion with jabber server names. """
|
|
global jabber_servers
|
|
for server in jabber_servers:
|
|
weechat.hook_completion_list_add(completion, server.name,
|
|
0, weechat.WEECHAT_LIST_POS_SORT)
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
def jabber_completion_jid_aliases(data, completion_item, buffer, completion):
|
|
""" Completion with jabber alias names. """
|
|
global jabber_jid_aliases
|
|
for alias, jid in sorted(jabber_jid_aliases.items()):
|
|
weechat.hook_completion_list_add(completion, alias,
|
|
0, weechat.WEECHAT_LIST_POS_SORT)
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
# ==================================[ fd ]====================================
|
|
|
|
def jabber_fd_cb(data, fd):
|
|
""" Callback for reading socket. """
|
|
global jabber_servers
|
|
weechat.prnt('', f"{SCRIPT_NAME}: jabber_fd_cb {fd}")
|
|
for server in jabber_servers:
|
|
if server.sfn == int(fd):
|
|
server.recv()
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
# ================================[ buffers ]=================================
|
|
|
|
def jabber_buffer_input_cb(data, buffer, input_data):
|
|
""" Callback called for input data on a jabber buffer. """
|
|
context = jabber_search_context(buffer)
|
|
if context["server"] and context["chat"]:
|
|
context["chat"].send_message(input_data)
|
|
elif context["server"]:
|
|
if input_data == "buddies" or "buddies".startswith(input_data):
|
|
context["server"].display_buddies()
|
|
else:
|
|
context["server"].send_message_from_input(input=input_data)
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
def jabber_buffer_close_cb(data, buffer):
|
|
""" Callback called when a jabber buffer is closed. """
|
|
context = jabber_search_context(buffer)
|
|
if context["server"] and context["chat"]:
|
|
if context["chat"].buddy:
|
|
context["chat"].buddy.chat = None
|
|
context["chat"].buffer = ""
|
|
context["server"].chats.remove(context["chat"])
|
|
elif context["server"]:
|
|
context["server"].buffer = ""
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
# ==================================[ timers ]==================================
|
|
|
|
def jabber_ping_timeout_timer(server_name, remaining_calls):
|
|
server = jabber_search_server_by_name(server_name)
|
|
if server:
|
|
server.ping_time_out()
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
def jabber_ping_timer(server_name, remaining_calls):
|
|
server = jabber_search_server_by_name(server_name)
|
|
if server:
|
|
server.ping()
|
|
return weechat.WEECHAT_RC_OK
|
|
|
|
# ==================================[ main ]==================================
|
|
|
|
if __name__ == "__main__" and import_ok:
|
|
if weechat.register(SCRIPT_NAME, SCRIPT_AUTHOR, SCRIPT_VERSION,
|
|
SCRIPT_LICENSE, SCRIPT_DESC,
|
|
"jabber_unload_script", ""):
|
|
|
|
version = weechat.info_get("version_number", "") or 0
|
|
jabber_hook_commands_and_completions()
|
|
jabber_config_init()
|
|
jabber_config_read()
|
|
for server in jabber_servers:
|
|
if weechat.config_boolean(server.options['autoreconnect']):
|
|
server.ping() # This will connect and update ping status
|
|
server.add_ping_timer()
|
|
else:
|
|
if weechat.config_boolean(server.options['autoconnect']):
|
|
server.connect()
|
|
|
|
# ==================================[ end ]===================================
|
|
|
|
def jabber_unload_script():
|
|
""" Function called when script is unloaded. """
|
|
global jabber_servers
|
|
jabber_config_write()
|
|
for server in jabber_servers:
|
|
server.disconnect()
|
|
server.delete()
|
|
return weechat.WEECHAT_RC_OK
|