mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-11-08 17:05:38 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f92e9e4f1 |
No files matched your search
@@ -0,0 +1,21 @@
|
||||
# Explicit IRC comparison keys
|
||||
|
||||
`IRCName(text, context=None, casemapping=None, connection_id=None)` is an
|
||||
immutable helper, not a str subclass. Construct it explicitly for dictionary/set
|
||||
keys; existing get_info/list/event values remain normal strings.
|
||||
Equality and hashing include the connection ID and captured casemapping, so
|
||||
identical-looking names on different connections are different keys. An IRCName
|
||||
never compares equal to a plain str. `str(key)` / `key.text` recover the spelling.
|
||||
|
||||
`irc_casefold(text, casemapping='rfc1459')` supports ascii, strict-rfc1459 and
|
||||
rfc1459. Only ASCII IRC characters fold; Unicode casefold would be incorrect.
|
||||
The new `get_info('casemapping')` reports the comparator actually used by core.
|
||||
Current master handles ASCII and RFC1459; this patch does not change its handling
|
||||
of advertised strict-rfc1459. An explicit mapping is available for scripts that
|
||||
need strict-rfc1459 independently. Keys capture their mapping so hashes stay
|
||||
stable; rebuild a collection if the connection's mapping changes.
|
||||
|
||||
```python
|
||||
seen = {zoitechat.IRCName('[SomeNick]')}
|
||||
assert zoitechat.IRCName('{somenick}') in seen # RFC1459 connection
|
||||
```
|
||||
@@ -1,21 +0,0 @@
|
||||
# Post-display print observers
|
||||
|
||||
Python: `hook_print_after(name, callback, userdata=None, priority=PRI_NORM)`.
|
||||
The callback is `(word, word_eol, userdata, attrs)`, matching hook_print_attrs().
|
||||
It runs after display_event() has handled the text, so prnt() follows the original
|
||||
text. Return values are ignored. Events eaten by a pre-hook or hidden by client
|
||||
preferences do not invoke these observers. This is post-display notification,
|
||||
not a notification that the entire incoming IRC command has finished updating state.
|
||||
|
||||
C: `zoitechat_hook_print_after(ph, name, priority, flags, callback, userdata)`;
|
||||
flags must be zero. Check `get_info("api_hook_print_after")` for "1" before
|
||||
accessing the appended Windows function-table slot. Existing pre-hook timing is
|
||||
unchanged. Unhook/unload use existing lifecycle functions. Recursive emission
|
||||
skips an already-running observer to prevent it immediately invoking itself.
|
||||
|
||||
```python
|
||||
def after(word, word_eol, userdata, attrs):
|
||||
zoitechat.prnt('This follows the original channel message')
|
||||
|
||||
zoitechat.hook_print_after('Channel Message', after)
|
||||
```
|
||||
@@ -0,0 +1,74 @@
|
||||
"""Explicit immutable IRC names; existing string-returning APIs are unchanged."""
|
||||
import _zoitechat as api
|
||||
|
||||
__all__ = ['IRCName', 'irc_casefold']
|
||||
|
||||
|
||||
def irc_casefold(text, casemapping='rfc1459'):
|
||||
"""Fold ASCII IRC characters only; never apply Unicode lower()/casefold()."""
|
||||
if casemapping not in ('ascii', 'strict-rfc1459', 'rfc1459'):
|
||||
raise ValueError('unsupported IRC casemapping')
|
||||
extra = {'[': '{', ']': '}', '\\': '|'} if casemapping != 'ascii' else {}
|
||||
if casemapping == 'rfc1459':
|
||||
extra['^'] = '~'
|
||||
return ''.join(chr(ord(c) + 32) if 'A' <= c <= 'Z' else extra.get(c, c)
|
||||
for c in text)
|
||||
|
||||
|
||||
class IRCName:
|
||||
"""Equality/hash include connection identity and the captured casemapping."""
|
||||
__slots__ = ('_text', '_casemapping', '_connection_id', '_key')
|
||||
|
||||
def __init__(self, text, context=None, casemapping=None, connection_id=None):
|
||||
context = context or api.get_context()
|
||||
if casemapping is None:
|
||||
casemapping = context.get_info('casemapping')
|
||||
if casemapping is None:
|
||||
raise RuntimeError('host does not expose its comparison casemapping')
|
||||
if connection_id is None:
|
||||
previous = api.get_context()
|
||||
if not context.set():
|
||||
raise ValueError('context has closed')
|
||||
try:
|
||||
connection_id = api.get_prefs('id')
|
||||
finally:
|
||||
previous.set()
|
||||
object.__setattr__(self, '_text', text)
|
||||
object.__setattr__(self, '_casemapping', casemapping)
|
||||
object.__setattr__(self, '_connection_id', connection_id)
|
||||
object.__setattr__(self, '_key', (connection_id, casemapping,
|
||||
irc_casefold(text, casemapping)))
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
raise AttributeError('IRCName is immutable')
|
||||
|
||||
@property
|
||||
def text(self):
|
||||
return self._text
|
||||
|
||||
@property
|
||||
def casemapping(self):
|
||||
return self._casemapping
|
||||
|
||||
@property
|
||||
def connection_id(self):
|
||||
return self._connection_id
|
||||
|
||||
def __str__(self):
|
||||
return self._text
|
||||
|
||||
def __repr__(self):
|
||||
return 'IRCName({!r}, casemapping={!r}, connection_id={!r})'.format(
|
||||
self._text, self._casemapping, self._connection_id)
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, IRCName):
|
||||
return NotImplemented
|
||||
return self._key == other._key
|
||||
|
||||
def __ne__(self, other):
|
||||
result = self.__eq__(other)
|
||||
return NotImplemented if result is NotImplemented else not result
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self._key)
|
||||
@@ -1,21 +0,0 @@
|
||||
"""Opt-in post-display print observers."""
|
||||
import _zoitechat as api
|
||||
from _zoitechat_embedded import ffi, lib
|
||||
|
||||
__all__ = ['hook_print_after']
|
||||
|
||||
|
||||
def hook_print_after(name, callback, userdata=None, priority=api.PRI_NORM):
|
||||
"""callback(word, word_eol, userdata, attrs); its return value is ignored."""
|
||||
if not callable(callback):
|
||||
raise TypeError('callback must be callable')
|
||||
plugin = api.__get_current_plugin()
|
||||
hook = plugin.add_hook(callback, userdata)
|
||||
handle = lib.zoitechat_hook_print_after(lib.ph, name.encode(), priority, 0,
|
||||
lib._on_print_attrs_hook, hook.handle)
|
||||
if handle == ffi.NULL:
|
||||
hook.is_unload = True
|
||||
plugin.remove_hook(id(hook))
|
||||
raise RuntimeError('unable to install post-display hook')
|
||||
hook.zoitechat_hook = handle
|
||||
return id(hook)
|
||||
+10
-40
@@ -143,8 +143,7 @@ enum
|
||||
HOOK_PRINT_ATTRS = 1 << 4, /* same as above, with attributes */
|
||||
HOOK_TIMER = 1 << 5, /* timeouts */
|
||||
HOOK_FD = 1 << 6, /* sockets & fds */
|
||||
HOOK_DELETED = 1 << 7, /* marked for deletion */
|
||||
HOOK_PRINT_AFTER = 1 << 8 /* post-display observers */
|
||||
HOOK_DELETED = 1 << 7 /* marked for deletion */
|
||||
};
|
||||
|
||||
enum
|
||||
@@ -352,7 +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_print_after = zoitechat_hook_print_after;
|
||||
|
||||
/* 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)
|
||||
@@ -623,13 +621,6 @@ plugin_hook_run (session *sess, char *name, char *word[], char *word_eol[],
|
||||
|
||||
hook = list->data;
|
||||
next = list->next;
|
||||
if (type == HOOK_PRINT_AFTER && hook->tag)
|
||||
{
|
||||
list = next;
|
||||
continue;
|
||||
}
|
||||
if (type == HOOK_PRINT_AFTER)
|
||||
hook->tag = 1;
|
||||
hook->pl->context = sess;
|
||||
|
||||
/* run the plugin's callback function */
|
||||
@@ -638,7 +629,6 @@ plugin_hook_run (session *sess, char *name, char *word[], char *word_eol[],
|
||||
case HOOK_COMMAND:
|
||||
ret = ((zoitechat_cmd_cb *)hook->callback) (word, word_eol, hook->userdata);
|
||||
break;
|
||||
case HOOK_PRINT_AFTER:
|
||||
case HOOK_PRINT_ATTRS:
|
||||
ret = ((zoitechat_print_attrs_cb *)hook->callback) (word, attrs, hook->userdata);
|
||||
break;
|
||||
@@ -653,15 +643,6 @@ plugin_hook_run (session *sess, char *name, char *word[], char *word_eol[],
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == HOOK_PRINT_AFTER)
|
||||
{
|
||||
hook->tag = 0;
|
||||
if (!is_session (sess))
|
||||
goto xit;
|
||||
list = next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((ret & ZOITECHAT_EAT_ZOITECHAT) && (ret & ZOITECHAT_EAT_PLUGIN))
|
||||
{
|
||||
eat = 1;
|
||||
@@ -744,15 +725,6 @@ plugin_emit_print (session *sess, char *word[], time_t server_time)
|
||||
HOOK_PRINT | HOOK_PRINT_ATTRS);
|
||||
}
|
||||
|
||||
void
|
||||
plugin_emit_print_after (session *sess, char *word[], time_t server_time)
|
||||
{
|
||||
zoitechat_event_attrs attrs;
|
||||
attrs.server_time_utc = server_time;
|
||||
if (is_session (sess))
|
||||
plugin_hook_run (sess, word[0], word, NULL, &attrs, HOOK_PRINT_AFTER);
|
||||
}
|
||||
|
||||
int
|
||||
plugin_emit_dummy_print (session *sess, char *name)
|
||||
{
|
||||
@@ -1056,15 +1028,6 @@ zoitechat_unhook (zoitechat_plugin *ph, zoitechat_hook *hook)
|
||||
return hook->userdata;
|
||||
}
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_print_after (zoitechat_plugin *ph, const char *name, int pri,
|
||||
int flags, zoitechat_print_attrs_cb *callback, void *userdata)
|
||||
{
|
||||
if (flags != 0 || !name || !callback)
|
||||
return NULL;
|
||||
return plugin_add_hook (ph, HOOK_PRINT_AFTER, pri, name, NULL, callback, 0, userdata);
|
||||
}
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_command (zoitechat_plugin *ph, const char *name, int pri,
|
||||
zoitechat_cmd_cb *callb, const char *help_text, void *userdata)
|
||||
@@ -1224,8 +1187,15 @@ zoitechat_get_info (zoitechat_plugin *ph, const char *id)
|
||||
return text_find_format_string (e);
|
||||
}
|
||||
|
||||
if (strcmp (id, "api_hook_print_after") == 0)
|
||||
return "1";
|
||||
/* Report the comparison actually used by this connection, not an
|
||||
* advertised mapping that the current core does not implement. */
|
||||
if (strcmp (id, "casemapping") == 0)
|
||||
{
|
||||
if (!is_session (ph->context))
|
||||
return NULL;
|
||||
return ph->context->server->p_cmp == g_ascii_strcasecmp
|
||||
? "ascii" : "rfc1459";
|
||||
}
|
||||
|
||||
hash = str_hash (id);
|
||||
/* do the session independant ones first */
|
||||
|
||||
@@ -183,7 +183,6 @@ int plugin_emit_command (session *sess, char *name, char *word[], char *word_eol
|
||||
int plugin_emit_server (session *sess, char *name, char *word[], char *word_eol[],
|
||||
time_t server_time);
|
||||
int plugin_emit_print (session *sess, char *word[], time_t server_time);
|
||||
void plugin_emit_print_after (session *sess, char *word[], time_t server_time);
|
||||
int plugin_emit_dummy_print (session *sess, char *name);
|
||||
int plugin_emit_keypress (session *sess, unsigned int state, unsigned int keyval, gunichar key);
|
||||
GList* plugin_command_list(GList *tmp_list);
|
||||
|
||||
@@ -2320,7 +2320,6 @@ text_emit_with_message (int index, session *sess, char *a, char *b, char *c, cha
|
||||
if (!prefs.hex_away_omit_alerts || !sess->server->is_away)
|
||||
sound_play_event (index);
|
||||
display_event (sess, index, word, stripcolor_args, timestamp, message);
|
||||
plugin_emit_print_after (sess, word, timestamp);
|
||||
}
|
||||
|
||||
char *
|
||||
|
||||
@@ -206,12 +206,6 @@ struct _zoitechat_plugin
|
||||
#endif
|
||||
|
||||
|
||||
/* api_hook_print_after=1: observer returns are ignored; flags must be zero. */
|
||||
zoitechat_hook *zoitechat_hook_print_after (zoitechat_plugin *ph,
|
||||
const char *name, int pri, int flags,
|
||||
int (*callback) (char *word[], zoitechat_event_attrs *attrs, void *userdata),
|
||||
void *userdata);
|
||||
|
||||
zoitechat_hook *
|
||||
zoitechat_hook_command (zoitechat_plugin *ph,
|
||||
const char *name,
|
||||
@@ -431,7 +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_print_after ((ZOITECHAT_PLUGIN_HANDLE)->zoitechat_hook_print_after)
|
||||
#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