mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-10-17 02:37:12 +00:00
negotiate explicitly registered IRC capabilities
This commit is contained in:
@@ -0,0 +1,64 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user