mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-10-08 06:51:21 +00:00
65 lines
2.8 KiB
Python
65 lines
2.8 KiB
Python
"""Script-owned capability requests and structured negotiation observations."""
|
|
from collections import namedtuple
|
|
import operator
|
|
import _zoitechat as api
|
|
from _zoitechat_embedded import ffi, lib
|
|
|
|
__all__ = ['CapabilityEvent', 'register_capability', 'hook_capability']
|
|
CapabilityEvent = namedtuple('CapabilityEvent',
|
|
'name subcommand value enabled context connection_id')
|
|
|
|
|
|
def register_capability(name, userdata=None, connection_id=None):
|
|
"""Request an advertised capability on next negotiation; unhook releases ownership."""
|
|
connection_id = -1 if connection_id is None else operator.index(connection_id)
|
|
if connection_id < -1 or connection_id > 2147483647:
|
|
raise ValueError('connection_id must fit the C API; -1 means any')
|
|
plugin = api.__get_current_plugin()
|
|
hook = plugin.add_hook(None, userdata)
|
|
handle = lib.zoitechat_register_capability(lib.ph, name.encode(), 0,
|
|
connection_id, hook.handle)
|
|
if handle == ffi.NULL:
|
|
hook.is_unload = True
|
|
plugin.remove_hook(id(hook))
|
|
raise ValueError('invalid capability name, reserved capability or connection ID')
|
|
hook.zoitechat_hook = handle
|
|
return id(hook)
|
|
|
|
|
|
def hook_capability(name, callback, userdata=None, priority=api.PRI_NORM):
|
|
"""Observe LS/NEW/ACK/NAK/DEL/LIST; this hook does not itself request the capability."""
|
|
if not callable(callback):
|
|
raise TypeError('callback must be callable')
|
|
|
|
def dispatch(word, word_eol, data):
|
|
if not word_eol:
|
|
return api.EAT_NONE
|
|
line = word_eol[0]
|
|
if line.startswith(':'):
|
|
unused, separator, line = line.partition(' ')
|
|
parts = line.split(' ', 3)
|
|
if len(parts) != 4 or parts[0].upper() != 'CAP':
|
|
return api.EAT_NONE
|
|
subcommand, tokens = parts[2].upper(), parts[3]
|
|
if subcommand not in ('LS', 'NEW', 'ACK', 'NAK', 'DEL', 'LIST'):
|
|
return api.EAT_NONE
|
|
if tokens.startswith('* '):
|
|
tokens = tokens[2:]
|
|
tokens = tokens.lstrip(':')
|
|
for token in tokens.split():
|
|
negative = token.startswith('-')
|
|
cap, equals, value = token.lstrip('-').partition('=')
|
|
if cap != name:
|
|
continue
|
|
enabled = None
|
|
if subcommand in ('ACK', 'LIST'):
|
|
enabled = not negative
|
|
elif subcommand in ('DEL', 'NAK'):
|
|
enabled = False
|
|
event = CapabilityEvent(cap, subcommand, value if equals else None,
|
|
enabled, api.get_context(), api.get_prefs('id'))
|
|
callback(event, data)
|
|
return api.EAT_NONE # observing CAP must not suppress core negotiation
|
|
|
|
return api.hook_server('CAP', dispatch, userdata, priority)
|