mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-11-08 17:05:38 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
37c93bb0f0 |
No files matched your search
@@ -0,0 +1,26 @@
|
||||
# Semantic IRC hooks
|
||||
|
||||
`on_join`, `on_part`, `on_quit`, `on_nick`, `on_kick`, `on_message`, and
|
||||
`on_notice` accept `(callback, userdata=None, priority=PRI_NORM)`. The general
|
||||
form is `hook_event(command, callback, userdata=None, priority=PRI_NORM)`.
|
||||
Callbacks receive `(event, userdata)`, where the immutable IRCEvent has
|
||||
command, prefix, nick, user, host, params, context, connection_id and time.
|
||||
`params` preserves the trailing parameter, including spaces; JOIN parameters
|
||||
also retain extended-join account/realname data when supplied by the server.
|
||||
|
||||
These wrappers use existing server hooks, before core command processing;
|
||||
return the ordinary EAT_* constants. EAT_ZOITECHAT suppresses the entire IRC
|
||||
command (including client state updates), not just its text. Use EAT_NONE for
|
||||
observation. Other plugins' priority and eating rules remain unchanged.
|
||||
The context is the core-selected context, which can be the server tab when a
|
||||
query/channel does not yet exist. QUIT is one event per incoming command,
|
||||
not one callback per affected channel. `event.time` is the existing server-time
|
||||
attribute (zero when unavailable); other message tags are not exposed here.
|
||||
|
||||
```python
|
||||
def joined(event, userdata):
|
||||
event.context.prnt('{} joined {}'.format(event.nick, event.params[0]))
|
||||
return zoitechat.EAT_NONE
|
||||
|
||||
zoitechat.on_join(joined)
|
||||
```
|
||||
@@ -1,24 +0,0 @@
|
||||
# Core-filtered hooks
|
||||
|
||||
Python: `hook_server_filtered(name, callback, userdata=None, priority=PRI_NORM,
|
||||
connection_id=None, channel=None)` and `hook_print_filtered(...)`.
|
||||
Callbacks match the existing attrs variants: `(word, word_eol, userdata, attrs)`.
|
||||
The connection ID distinguishes duplicate names; None/-1 means any connection
|
||||
(including connection ID zero). A channel restriction uses that connection's
|
||||
IRC comparator. None means any context; an empty string is an exact restriction.
|
||||
|
||||
C: the corresponding zoitechat_hook_*_filtered functions accept
|
||||
`(ph, name, pri, flags, connection_id, channel, callback, userdata)`;
|
||||
flags=0, ID=-1 for any and NULL channel for any. Check
|
||||
`get_info("api_context_filters")` for "1" before accessing the new Windows slots.
|
||||
|
||||
Filtering takes place before Python/interpreter entry. Existing unrestricted
|
||||
hooks, priority, EAT semantics and unload/unhook handling are unchanged.
|
||||
Filters refer to the core-selected context, not an IRC target parsed from the
|
||||
line: unknown queries/channels can still have a server-tab context, and QUIT
|
||||
has its usual single server-hook context. Connection IDs last only this process.
|
||||
|
||||
```python
|
||||
zoitechat.hook_print_filtered('Channel Message', callback,
|
||||
connection_id=zoitechat.get_prefs('id'), channel='#zoitechat')
|
||||
```
|
||||
@@ -0,0 +1,77 @@
|
||||
"""Structured, opt-in hooks before the IRC command's core processing."""
|
||||
from collections import namedtuple
|
||||
import _zoitechat as api
|
||||
|
||||
__all__ = ['IRCEvent', 'hook_event', 'on_join', 'on_part', 'on_quit',
|
||||
'on_nick', 'on_kick', 'on_message', 'on_notice']
|
||||
IRCEvent = namedtuple('IRCEvent', 'command prefix nick user host params context connection_id time')
|
||||
_SUPPORTED = frozenset(('JOIN', 'PART', 'QUIT', 'NICK', 'KICK', 'PRIVMSG', 'NOTICE'))
|
||||
|
||||
|
||||
def _parse(line):
|
||||
prefix = ''
|
||||
if line.startswith(':'):
|
||||
prefix, separator, line = line[1:].partition(' ')
|
||||
if not separator:
|
||||
return None
|
||||
command, separator, rest = line.partition(' ')
|
||||
params = []
|
||||
while rest:
|
||||
rest = rest.lstrip(' ')
|
||||
if rest.startswith(':'):
|
||||
params.append(rest[1:])
|
||||
break
|
||||
param, separator, rest = rest.partition(' ')
|
||||
if param:
|
||||
params.append(param)
|
||||
nick, marker, userhost = prefix.partition('!')
|
||||
user, marker, host = userhost.partition('@')
|
||||
return command.upper(), prefix, nick, user, host, tuple(params)
|
||||
|
||||
|
||||
def hook_event(command, callback, userdata=None, priority=api.PRI_NORM):
|
||||
"""callback(event, userdata) returns existing EAT_* constants."""
|
||||
command = command.upper()
|
||||
if command not in _SUPPORTED:
|
||||
raise ValueError('unsupported semantic event: ' + command)
|
||||
if not callable(callback):
|
||||
raise TypeError('callback must be callable')
|
||||
|
||||
def dispatch(word, word_eol, data, attrs):
|
||||
if not word_eol:
|
||||
return api.EAT_NONE
|
||||
parsed = _parse(word_eol[0])
|
||||
if parsed is None or parsed[0] != command:
|
||||
return api.EAT_NONE
|
||||
event = IRCEvent(*(parsed + (api.get_context(), api.get_prefs('id'), attrs.time)))
|
||||
return callback(event, data)
|
||||
|
||||
return api.hook_server_attrs(command, dispatch, userdata, priority)
|
||||
|
||||
|
||||
def on_join(callback, userdata=None, priority=api.PRI_NORM):
|
||||
return hook_event('JOIN', callback, userdata, priority)
|
||||
|
||||
|
||||
def on_part(callback, userdata=None, priority=api.PRI_NORM):
|
||||
return hook_event('PART', callback, userdata, priority)
|
||||
|
||||
|
||||
def on_quit(callback, userdata=None, priority=api.PRI_NORM):
|
||||
return hook_event('QUIT', callback, userdata, priority)
|
||||
|
||||
|
||||
def on_nick(callback, userdata=None, priority=api.PRI_NORM):
|
||||
return hook_event('NICK', callback, userdata, priority)
|
||||
|
||||
|
||||
def on_kick(callback, userdata=None, priority=api.PRI_NORM):
|
||||
return hook_event('KICK', callback, userdata, priority)
|
||||
|
||||
|
||||
def on_message(callback, userdata=None, priority=api.PRI_NORM):
|
||||
return hook_event('PRIVMSG', callback, userdata, priority)
|
||||
|
||||
|
||||
def on_notice(callback, userdata=None, priority=api.PRI_NORM):
|
||||
return hook_event('NOTICE', callback, userdata, priority)
|
||||
@@ -1,36 +0,0 @@
|
||||
"""Context restrictions evaluated by core before entering Python."""
|
||||
import operator
|
||||
import _zoitechat as api
|
||||
from _zoitechat_embedded import ffi, lib
|
||||
|
||||
__all__ = ['hook_server_filtered', 'hook_print_filtered']
|
||||
|
||||
|
||||
def _register(name, callback, userdata, priority, connection_id, channel, server):
|
||||
if not callable(callback):
|
||||
raise TypeError('callback must be callable')
|
||||
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(callback, userdata)
|
||||
register = lib.zoitechat_hook_server_filtered if server else lib.zoitechat_hook_print_filtered
|
||||
dispatch = lib._on_server_attrs_hook if server else lib._on_print_attrs_hook
|
||||
handle = register(lib.ph, name.encode(), priority, 0, connection_id,
|
||||
ffi.NULL if channel is None else channel.encode(), dispatch, hook.handle)
|
||||
if handle == ffi.NULL:
|
||||
hook.is_unload = True
|
||||
plugin.remove_hook(id(hook))
|
||||
raise RuntimeError('unable to install filtered hook')
|
||||
hook.zoitechat_hook = handle
|
||||
return id(hook)
|
||||
|
||||
|
||||
def hook_server_filtered(name, callback, userdata=None, priority=api.PRI_NORM,
|
||||
connection_id=None, channel=None):
|
||||
return _register(name, callback, userdata, priority, connection_id, channel, True)
|
||||
|
||||
|
||||
def hook_print_filtered(name, callback, userdata=None, priority=api.PRI_NORM,
|
||||
connection_id=None, channel=None):
|
||||
return _register(name, callback, userdata, priority, connection_id, channel, False)
|
||||
@@ -99,9 +99,6 @@ struct _zoitechat_hook
|
||||
int tag; /* for timers & FDs only */
|
||||
int type; /* HOOK_* */
|
||||
int pri; /* fd */ /* priority / fd for HOOK_FD only */
|
||||
int connection_filter;
|
||||
char *channel_filter;
|
||||
gboolean context_filtered;
|
||||
};
|
||||
|
||||
struct _zoitechat_list
|
||||
@@ -354,8 +351,6 @@ plugin_add (session *sess, char *filename, void *handle, void *init_func,
|
||||
pl->zoitechat_emit_print_attrs = zoitechat_emit_print_attrs;
|
||||
pl->zoitechat_event_attrs_create = zoitechat_event_attrs_create;
|
||||
pl->zoitechat_event_attrs_free = zoitechat_event_attrs_free;
|
||||
pl->zoitechat_hook_server_filtered = zoitechat_hook_server_filtered;
|
||||
pl->zoitechat_hook_print_filtered = zoitechat_hook_print_filtered;
|
||||
|
||||
/* run zoitechat_plugin_init, if it returns 0, close the plugin */
|
||||
if (((zoitechat_init_func *)init_func) (pl, &pl->name, &pl->desc, &pl->version, arg) == 0)
|
||||
@@ -626,15 +621,6 @@ plugin_hook_run (session *sess, char *name, char *word[], char *word_eol[],
|
||||
|
||||
hook = list->data;
|
||||
next = list->next;
|
||||
if (hook->context_filtered &&
|
||||
(!is_session (sess) ||
|
||||
(hook->connection_filter != -1 && hook->connection_filter != sess->server->id) ||
|
||||
(hook->channel_filter && sess->server->p_cmp (hook->channel_filter, sess->channel))))
|
||||
{
|
||||
list = next;
|
||||
continue; /* filter in C before entering an interpreter */
|
||||
}
|
||||
|
||||
hook->pl->context = sess;
|
||||
|
||||
/* run the plugin's callback function */
|
||||
@@ -1038,45 +1024,10 @@ zoitechat_unhook (zoitechat_plugin *ph, zoitechat_hook *hook)
|
||||
|
||||
g_free (hook->name); /* NULL for timers & fds */
|
||||
g_free (hook->help_text); /* NULL for non-commands */
|
||||
g_free (hook->channel_filter);
|
||||
hook->channel_filter = NULL;
|
||||
|
||||
return hook->userdata;
|
||||
}
|
||||
|
||||
static zoitechat_hook *
|
||||
plugin_add_filtered_hook (zoitechat_plugin *ph, const char *name, int pri,
|
||||
int flags, int connection_id, const char *channel,
|
||||
int type, void *callback, void *userdata)
|
||||
{
|
||||
zoitechat_hook *hook;
|
||||
if (flags != 0 || connection_id < -1 || !name || !callback)
|
||||
return NULL;
|
||||
hook = plugin_add_hook (ph, type, pri, name, NULL, callback, 0, userdata);
|
||||
hook->context_filtered = TRUE;
|
||||
hook->connection_filter = connection_id;
|
||||
hook->channel_filter = g_strdup (channel);
|
||||
return hook;
|
||||
}
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_server_filtered (zoitechat_plugin *ph, const char *name, int pri,
|
||||
int flags, int connection_id, const char *channel,
|
||||
zoitechat_serv_attrs_cb *callback, void *userdata)
|
||||
{
|
||||
return plugin_add_filtered_hook (ph, name, pri, flags, connection_id, channel,
|
||||
HOOK_SERVER_ATTRS, callback, userdata);
|
||||
}
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_print_filtered (zoitechat_plugin *ph, const char *name, int pri,
|
||||
int flags, int connection_id, const char *channel,
|
||||
zoitechat_print_attrs_cb *callback, void *userdata)
|
||||
{
|
||||
return plugin_add_filtered_hook (ph, name, pri, flags, connection_id, channel,
|
||||
HOOK_PRINT_ATTRS, callback, userdata);
|
||||
}
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_command (zoitechat_plugin *ph, const char *name, int pri,
|
||||
zoitechat_cmd_cb *callb, const char *help_text, void *userdata)
|
||||
@@ -1236,9 +1187,6 @@ zoitechat_get_info (zoitechat_plugin *ph, const char *id)
|
||||
return text_find_format_string (e);
|
||||
}
|
||||
|
||||
if (strcmp (id, "api_context_filters") == 0)
|
||||
return "1";
|
||||
|
||||
hash = str_hash (id);
|
||||
/* do the session independant ones first */
|
||||
switch (hash)
|
||||
|
||||
@@ -206,18 +206,6 @@ struct _zoitechat_plugin
|
||||
#endif
|
||||
|
||||
|
||||
/* api_context_filters=1: connection_id -1 is any; NULL channel is any. */
|
||||
zoitechat_hook *zoitechat_hook_server_filtered (zoitechat_plugin *ph,
|
||||
const char *name, int pri, int flags, int connection_id, const char *channel,
|
||||
int (*callback) (char *word[], char *word_eol[],
|
||||
zoitechat_event_attrs *attrs, void *userdata),
|
||||
void *userdata);
|
||||
|
||||
zoitechat_hook *zoitechat_hook_print_filtered (zoitechat_plugin *ph,
|
||||
const char *name, int pri, int flags, int connection_id, const char *channel,
|
||||
int (*callback) (char *word[], zoitechat_event_attrs *attrs, void *userdata),
|
||||
void *userdata);
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_command (zoitechat_plugin *ph,
|
||||
const char *name,
|
||||
@@ -437,8 +425,6 @@ zoitechat_pluginpref_list (zoitechat_plugin *ph,
|
||||
#define zoitechat_hook_server ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_server)
|
||||
#define zoitechat_hook_server_attrs ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_server_attrs)
|
||||
#define zoitechat_hook_print ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_print)
|
||||
#define zoitechat_hook_server_filtered ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_server_filtered)
|
||||
#define zoitechat_hook_print_filtered ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_print_filtered)
|
||||
#define zoitechat_hook_print_attrs ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_print_attrs)
|
||||
#define zoitechat_hook_timer ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_timer)
|
||||
#define zoitechat_hook_fd ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_fd)
|
||||
|
||||
Reference in new issue
Block a user