diff options
Diffstat (limited to 'python-miniirc.spec')
-rw-r--r-- | python-miniirc.spec | 1288 |
1 files changed, 1288 insertions, 0 deletions
diff --git a/python-miniirc.spec b/python-miniirc.spec new file mode 100644 index 0000000..450b04a --- /dev/null +++ b/python-miniirc.spec @@ -0,0 +1,1288 @@ +%global _empty_manifest_terminate_build 0 +Name: python-miniirc +Version: 1.9.1 +Release: 1 +Summary: A lightweight IRC framework. +License: MIT +URL: https://github.com/luk3yx/miniirc +Source0: https://mirrors.nju.edu.cn/pypi/web/packages/05/46/1887889cc1907a2d7d3258199fed555153ee7fb61c84168960b46d5ed6a0/miniirc-1.9.1.tar.gz +BuildArch: noarch + +Requires: python3-certifi + +%description +# miniirc + +[![Python 3.4+]](#python-version-support) [![Available on PyPI.]](https://pypi.org/project/miniirc/) [![License: MIT]](https://github.com/luk3yx/miniirc/blob/master/LICENSE.md) + +A relatively simple thread-safe(-ish) IRC client framework. + +To install miniirc, simply run `pip3 install miniirc`. + +If you have previously used miniirc, you may want to read the +[deprecations list] (last updated 2020-04-28). + +*This repository is available on both [GitHub](https://github.com/luk3yx/miniirc) and [GitLab](https://gitlab.com/luk3yx/miniirc).* + +[Python 3.4+]: https://img.shields.io/badge/python-3.4+-blue.svg +[Available on PyPI.]: https://img.shields.io/pypi/v/miniirc.svg +[License: MIT]: https://img.shields.io/pypi/l/miniirc.svg +[deprecations list]: #deprecations + +## Parameters + +```py +irc = miniirc.IRC(ip, port, nick, channels=None, *, ssl=None, ident=None, realname=None, persist=True, debug=False, ns_identity=None, auto_connect=True, ircv3_caps=set(), quit_message='I grew sick and died.', ping_interval=60, ping_timeout=None, verify_ssl=True, executor=None) +``` + +*Note that everything before the \* is a positional argument.* + +### Typical usage + +You don't need to add every argument, and the `ip`, `port`, `nick`, and +`channels` arguments should be specified as positional arguments. + +```py +irc = miniirc.IRC('irc.example.com', 6697, 'my-bot', ['#my-channel'], ns_identity=('my-bot', 'hunter2'), executor=concurrent.futures.ThreadPoolExecutor(), keepnick=True) +``` + +If you are not doing anything with the main thread after connecting to IRC, +please call `irc.wait_until_disconnected()` to prevent Python from trying to +shut down while miniirc is still connected, breaking thread pools (in +Python 3.9 and later). + +```py +irc.wait_until_disconnected() +``` + +### Parameter descriptions + +| Parameter | Description | +| ------------- | -------------------------------------------------------- | +| `ip` | The IP/hostname of the IRC server to connect to. | +| `port` | The port to connect to. | +| `nick` | The nickname of the bot. | +| `channels` | The channels to join on connect. This can be an iterable containing strings (list, set, etc), or (since v1.5.0) a comma-delimited string. | +| `ssl` | Enable TLS/SSL. If `None`, TLS is disabled unless the port is `6697`. | +| `ident` | The ident to use, defaults to `nick`. | +| `realname` | The realname to use, defaults to `nick` as well. | +| `persist` | Whether to automatically reconnect. | +| `debug` | Enables debug mode, prints all IRC messages. This can also be a file-like object (with write mode enabled) if you want debug messages to be written into a file instead of being printed to stdout, or a function (for example `logging.debug`). | +| `ns_identity` | The NickServ account to use as a tuple/list of length 2 (`('<user>', '<password>')`). For compatibility, this can be a string (`'<user> <password>'`). | +| `auto_connect`| Runs `irc.connect()` straight away. | +| `ircv3_caps` | A set() of additional IRCv3 capabilities to request. SASL is auto-added if `ns_identity` is specified. | +| `connect_modes` | A mode string (for example `'+B'`) of UMODEs to set when connected. | +| `quit_message`| Sets the default quit message. This can be modified per-quit with `irc.disconnect()`. | +| `ping_interval` | If no packets are sent or received for this amount of seconds, miniirc will send a `PING`, and if no reply is sent, after the ping timeout, miniirc will attempt to reconnect. Set to `None` to disable. | +| `ping_timeout` | The ping timeout used alongside the above `ping_interval` option, if unspecified will default to `ping_interval`. | +| `verify_ssl` | Verifies TLS/SSL certificates. Disabling this is not recommended as it opens the IRC connection up to MiTM attacks. If you have trouble with certificate verification, try running `pip3 install certifi` first. | +| `executor` | An instance of `concurrent.futures.ThreadPoolExecutor` to use when running handlers. | +| `keepnick` | If enabled, miniirc will attempt to obtain the original nick like ZNC's *keepnick. `irc.nick` will be the original nickname rather than the current one. | + +*The only mandatory parameters are `ip`, `port`, and `nick`.* + +## Functions + +| Function | Description | +| ------------- | -------------------------------------------------------- | +| `change_parser(parser=...)` | *See the message parser section for documentation.* | +| `connect()` | Connects to the IRC server if not already connected. | +| `ctcp(target, *msg, reply=False, tags=None)` | Sends a `CTCP` request or reply to `target`. | +| `debug(...)` | Debug, calls `print(...)` if debug mode is on. | +| `disconnect(msg=..., *, auto_reconnect=False)`| Disconnects from the IRC server. `auto_reconnect` will be overridden by `self.persist` if set to `True`. | +| `Handler(...)` | An event handler, see [Handlers](#handlers) for more info. | +| `me(target, *msg, tags=None)` | Sends a `/me` (`CTCP ACTION`) to `target`. | +| `msg(target, *msg, tags=None)` | Sends a `PRIVMSG` to `target`. `target` should not contain spaces or start with a colon. | +| `notice(target, *msg, tags=None)` | Sends a `NOTICE` to `target`. `target` should not contain spaces or start with a colon. | +| `quote(*msg, force=False, tags=None)` | Sends a raw message to IRC, use `force=True` to send while disconnected. Do not send multiple commands in one `irc.quote()`, as the newlines will be stripped and it will be sent as one command. The `tags` parameter optionally allows you to add a `dict` with IRCv3 client tags (all starting in `+`), and will not be sent to IRC servers that do not support client tags. | +| `send(*msg, force=False, tags=None)` | Sends a command to the IRC server, treating every positional argument as a parameter. The usage of this is recommended over `irc.quote()` unless you know what you are doing. | +| `wait_until_disconnected()` | Waits until the IRC server is disconnected and automatic reconnecting is turned off. | + +*Note that if `force=False` on `irc.quote` (or `irc.msg` etc is called) while +miniirc is not connected, messages will be temporarily stored and then sent +once miniirc is connected. Setting `force=True` will throw errors if miniirc is +completely disconnected (`irc.connected` is `None`).* + +### irc.quote and irc.send + +The two functions `irc.quote` and `irc.send` may sound similar, however are +fundamentally different: `irc.quote()` joins all provided arguments with spaces +and sends them as a raw message to IRC, while `irc.send()` treats each argument +as a parameter. If arguments passed to `irc.send()` contain spaces, they are +replaced with U+00A0 (a non-breaking space, visually similar to a regular +space however not interpreted as one). + +#### Examples + + - `irc.quote('PRIVMSG', '#channel :Hello,', 'world!')` sends "Hello, world!" + to #channel. + - `irc.quote('PRIVMSG', '#channel', 'Hello, world!')` is invalid ("Hello," and + "world!" are sent as separate parameters). + - `irc.send('PRIVMSG', '#channel', 'Hello, world!')` will send "Hello, world!" + to "#channel". + - `irc.send('PRIVMSG', '#channel :Hello,', 'world!')` will send "world!" to + `#channel\xa0:Hello,`, where `\xa0` is a non-breaking space. + +*If you are unsure and do not need compatibility with miniirc <1.5.0, use +`irc.send()`. `PRIVMSG` is just used as an example, if you need to send +`PRIVMSG`s use `irc.msg()` instead.* + +## Variables + +*These variables should not be changed outside `miniirc.py`.* + +| Variable | Description | +| ------------- | -------------------------------------------------------- | +| `active_caps` | A `set` of IRCv3 capabilities that have been successfully negotiated with the IRC server. This is empty while disconnected. | +| `connected` | A boolean (or `None`), `True` when miniirc is connected, `False` when miniirc is connecting, and `None` when miniirc is not connected. | +| `current_nick` | The bot/client's current nickname. Do not modify this, and use this instead of `irc.nick` when getting the bot's current nickname. | +| `isupport` | A `dict` with values (not necessarily strings) from `ISUPPORT` messages sent to the client. | +| `msglen` | The maximum length (in bytes) of messages (including `\r\n`). This is automatically changed if the server supports the `oragono.io/maxline-2` capability. | +| `nick` | The nickname to use when connecting to IRC. Until miniirc v2.0.0, you should only use or modify this while disconnected, as it is currently automatically updated with nickname changes. | + +The following arguments passed to `miniirc.IRC` are also available: `ip`, +`port`, `channels`, `ssl`, `ident`, `realname`, `persist`, `connect_modes`, +`quit_message`, `ping_interval`, `verify_ssl`. + +## Handlers + +`miniirc.Handler` and `miniirc.CmdHandler` are function decorators that add +functions to an event handler list. Functions in this list are called in their +own thread when their respective IRC event(s) is/are received. Handlers may +work on every IRC object in existence (`miniirc.Handler`) or only on +specific IRC objects (`irc.Handler`). + +The basic syntax for a handler is as followed, where `*events` is a list of events (`PRIVMSG`, `NOTICE`, etc) are called. + +```py +import miniirc +@miniirc.Handler(*events, colon=False) +def handler(irc, hostmask, args): + # irc: An 'IRC' object. + # hostmask: A 'hostmask' object. + # args: A list containing the arguments sent to the command. Everything + # following the first `:` in the command is put into one item + # (args[-1]). If "colon" is "False", the leading ":" (if any) + # is automatically removed. To prevent your code from horribly + # breaking, always set it to False unless you know what you are + # doing. + pass +``` + +#### Recommendations when using handlers: + + - If you don't need support for miniirc <1.4.0 and are parsing the last + parameter, setting `colon` to `False` is strongly recommended. If the + `colon` parameter is omitted, it defaults to `True`, however this will + change when miniirc v2.0.0 is released. + - Although `Handler` and `CmdHandler` currently accept any object that can be + converted to a string, every event is converted to a string internally. + - Not specifying the [`ircv3`](#ircv3-tags) parameter when it is not required + prevents a redundant `dict` from being created. + - To add handlers to a specific `IRC` object and not every one in existence, + use `irc.Handler` and `irc.CmdHandler` instead. If you want to create a + `Bot` or `Client` class and automatically add handlers to `IRC` objects + created inside it, see + [making existing functions handlers](#making-existing-functions-handlers). + +### Hostmask object + +Hostmasks are tuples with the format `('user', 'ident', 'hostname')`. If `ident` +and `hostname` aren't sent from the server, they will be filled in with the +previous value. If a command is received without a hostmask, all the `hostmask` +elements will be set to the name of the command. This is deprecated, however, +and when miniirc v2.0.0 is released the `hostmask` elements will be set to +empty strings. + +### Making existing functions handlers + +You can make existing functions handlers (for example class instance methods) +with `irc.Handler(*events)(handler_function)`. You probably don't want to use +`miniirc.Handler` for class instance methods, as this will create a handler +that gets triggered for every `IRC` object. + +You can also add multiple handlers of the same type easily: + +```py +add_handler = irc.Handler('PRIVMSG', colon=False) +add_handler(handler_1) +add_handler(self.instance_handler) +``` + +This is useful if you want to create a `Bot` (or `Client`) class and add +class-specific handlers without creating global process-wide handlers or +creating a wrapper function for every class instance. + +### IRCv3 support + +#### IRCv3 tags + +If you want your handler to support IRCv3 message tags, you need to add +`ircv3=True` to the `Handler` or `CmdHandler` decorator. You will need to add a +`tags` parameter to your function after `hostmask`. IRCv3 tags are sent to the +handlers as `dict`s, with values of either strings or `True`. + +*miniirc will automatically un-escape IRCv3 tag values.* + +```py +import miniirc +@miniirc.Handler(*events, colon=False, ircv3=True) +def handler(irc, hostmask, tags, args): + pass +``` + +#### IRCv3 capabilities + +You can handle IRCv3 capabilities before connecting using a handler. +You must use `force=True` on any `irc.quote()` called here, as when this is +called, miniirc may not yet be fully connected. Do not use the `colon` argument +for `Handler` when creating these handlers to avoid unexpected side-effects. + +```py +import miniirc +@miniirc.Handler('IRCv3 my-cap-name') +def handler(irc, hostmask, args): + # Process the capability here + + # IRCv3.2 capabilities: + # args = ['my-cap-name', 'IRCv3.2-parameters'] + + # IRCv3.1 capabilities: + # args = ['my-cap-name'] + + # Remove the capability from the processing list. + irc.finish_negotiation(args[0]) # This can also be 'my-cap-name'. +``` + +### Custom message parsers (not recommended) + +If the IRC server you are connecting to supports a non-standard message syntax, you can +create custom message parsers. These are called with the raw message (as a `str`) and +can either return `None` to ignore the message or a 4-tuple (`cmd, hostmask, tags, args`) +that will then be sent on to the handlers. The items in this 4-tuple should be the same +type as the items expected by handlers (and `cmd` should be a string). + +#### Message parser example + +This message parser makes the normal parser allow `~` as an IRCv3 tag prefix character. + +```py +import miniirc + +def my_message_parser(msg): + if msg.startswith('~'): + msg = '@' + msg[1:] + return miniirc.ircv3_message_parser(msg) +``` + +#### Changing message parsers + +To change message parsers, you can use `irc.change_parser(func=...)`. If `func` is not +specified, it will default to the built-in parser. You can only change message parsers +on-the-fly (for example in an IRCv3 CAP handler). If you need to change message parsers +before connecting, you can disable `auto_connect` and change it then. + +```py +irc = miniirc.IRC(..., auto_connect=False) +irc.change_parser(my_message_parser) +irc.connect() +``` + +### Handling multiple events + +If you want to handle multiple events and/or be able to get the name of the +event being triggered, you can use `irc.CmdHandler`. This will pass an extra +`command` argument to the handler function (between `irc` and `hostmask`), +containing a string with the command name (such as `PRIVMSG`). + +#### Catch-all handlers + +**Please do not use these unless there is no other alternative.** + +If you want to handle *every* event, you can use catch-all handlers. To create +these, you can call `irc.CmdHandler()` *without* any parameters. Note that this +handler will be called many times while connecting (and once connected). + +*You cannot call `irc.Handler()` without parameters.* + +### Example + +```py +import miniirc + +# Not required, however this makes sure miniirc isn't outdated. +assert miniirc.ver >= (1,8,2) + +@miniirc.Handler('PRIVMSG', 'NOTICE', colon=True) +def handler(irc, hostmask, args): + print(hostmask[0], 'sent a message to', args[0], 'with content', args[1]) + # nickname sent a message to #channel with content :Hello, world! + +@miniirc.CmdHandler('PRIVMSG', 'NOTICE', colon=False) +def cmdhandler(irc, command, hostmask, args): + print(hostmask[0], 'sent a', command, 'to', args[0], 'with content', + args[1]) + # nickname sent a PRIVMSG to #channel with content Hello, world! +``` + +This will print a line whenever the bot gets a `PRIVMSG` or `NOTICE`. + +## Misc functions + +miniirc provides the following helper functions: + +| Name | Description | +| ----------------------------- | ----------------------------------------- | +| `miniirc.get_ca_certs()` | Runs `certifi.where()` if `certifi` is installed, otherwise returns `None`. | +| `miniirc.ircv3_message_parser(msg)` | The default IRCv2/IRCv3 message parser, returns `cmd, hostmask, tags, args`. | +| `miniirc.ver` | A tuple containing version information. | +| `miniirc.version` | The `CTCP VERSION` reply, can be changed. | + +The version numbering system should be similar to [SemVer](https://semver.org/), +however backwards compatibility is preserved where possible when major releases +change. + +## Python version support + + - Python 3.3 and below are unsupported and do not work with miniirc. + - Python 3.4, 3.5, and 3.6 are currently supported, but support will likely be + dropped in miniirc v2.1.0. Major bugfixes may be backported to v2.0 for a + few months after v2.1's release. + - Python 3.7 and above should work with the latest stable version of miniirc. + +If there is a bug/error in Python 3.4 or newer, please open an issue or pull +request on [GitHub](https://github.com/luk3yx/miniirc/issues) or +[GitLab](https://gitlab.com/luk3yx/miniirc/issues). + +*If you are using Python 3.7 or an older version of Python, I strongly +recommend updating. Later versions of Python include features such as f-strings +that make software development easier.* + +## miniirc_extras + +If you want more advanced(-ish) features such as user tracking, you can use +[miniirc_extras](https://pypi.org/project/miniirc-extras/) +([GitHub](https://github.com/luk3yx/miniirc_extras), +[GitLab](https://gitlab.com/luk3yx/miniirc_extras)). Note that miniirc_extras +is still in beta and there will be breaking API changes in the future. + +## Deprecations + +If miniirc v2.0.0 is ever released, the following breaking changes will +(probably) be made: + + - Internal-only attributes `irc.handlers`, `irc.sock`, and `irc.sendq` + (please do not use these) will be renamed. Again, please do not use these. + - `irc.nick` will always be the nickname used when connecting to IRC rather + than the current nickname, use `irc.current_nick` for the current nickname + (since v1.4.3). + - `irc.ns_identity` will be stored as a tuple instead of a string, for example + `('username', 'password with spaces')` instead of + `'username password with spaces'`. Both formats are currently accepted and + will be accepted in the `ns_identity` keyword argument. + - No exceptions will be raised in `irc.quote`/`irc.send` with `force=True` + when the socket is closed. Instead of relying on these exceptions, use + `irc.connected` which is set to `None` when completely disconnected. + - As stated in the Python version support section, Python 3.4 support will be + dropped in miniirc v2.1.0, however bugfixes will be backported for a few + months. + - The `colon` keyword argument to `Handler` and `CmdHandler` will default to + `False` instead of `True`. + - Unspecified hostmasks will be an empty string instead of the command. Don't + rely on this "feature" if possible, simply ignore the hostmask if you do + not need it. + - The `extended-join` capability will be requested by default, use `args[0]` + instead of `args[-1]` to get the channel from a `JOIN` event. + - The `tags` keyword argument will be read-only. + +## Working examples/implementations + +Here is a list of some (open-source) bots using miniirc, in alphabetical order: + + - [irc-rss-feed-bot] - Posts RSS entry titles and shortened URLs to IRC + channels. *Python 3.8+* + - [irc-url-title-bot] - Gets webpage titles from URLs posted in IRC channels. + *Python 3.8+* + - [lurklite] - A generic configurable IRC bot. + *[GitHub](https://github.com/luk3yx/lurklite) link.* + - [stdinbot] - A very simple bot that dumps stdin to an IRC channel. + *[GitHub](https://github.com/luk3yx/stdinbot) link.* + +*Want to add your own bot/client to this list? Open an issue on +[GitHub](https://github.com/luk3yx/miniirc/issues) or +[GitLab](https://gitlab.com/luk3yx/miniirc/issues).* + +[irc-rss-feed-bot]: https://github.com/impredicative/irc-rss-feed-bot +[irc-url-title-bot]: https://github.com/impredicative/irc-url-title-bot +[lurklite]: https://gitlab.com/luk3yx/lurklite +[stdinbot]: https://gitlab.com/luk3yx/stdinbot + + +%package -n python3-miniirc +Summary: A lightweight IRC framework. +Provides: python-miniirc +BuildRequires: python3-devel +BuildRequires: python3-setuptools +BuildRequires: python3-pip +%description -n python3-miniirc +# miniirc + +[![Python 3.4+]](#python-version-support) [![Available on PyPI.]](https://pypi.org/project/miniirc/) [![License: MIT]](https://github.com/luk3yx/miniirc/blob/master/LICENSE.md) + +A relatively simple thread-safe(-ish) IRC client framework. + +To install miniirc, simply run `pip3 install miniirc`. + +If you have previously used miniirc, you may want to read the +[deprecations list] (last updated 2020-04-28). + +*This repository is available on both [GitHub](https://github.com/luk3yx/miniirc) and [GitLab](https://gitlab.com/luk3yx/miniirc).* + +[Python 3.4+]: https://img.shields.io/badge/python-3.4+-blue.svg +[Available on PyPI.]: https://img.shields.io/pypi/v/miniirc.svg +[License: MIT]: https://img.shields.io/pypi/l/miniirc.svg +[deprecations list]: #deprecations + +## Parameters + +```py +irc = miniirc.IRC(ip, port, nick, channels=None, *, ssl=None, ident=None, realname=None, persist=True, debug=False, ns_identity=None, auto_connect=True, ircv3_caps=set(), quit_message='I grew sick and died.', ping_interval=60, ping_timeout=None, verify_ssl=True, executor=None) +``` + +*Note that everything before the \* is a positional argument.* + +### Typical usage + +You don't need to add every argument, and the `ip`, `port`, `nick`, and +`channels` arguments should be specified as positional arguments. + +```py +irc = miniirc.IRC('irc.example.com', 6697, 'my-bot', ['#my-channel'], ns_identity=('my-bot', 'hunter2'), executor=concurrent.futures.ThreadPoolExecutor(), keepnick=True) +``` + +If you are not doing anything with the main thread after connecting to IRC, +please call `irc.wait_until_disconnected()` to prevent Python from trying to +shut down while miniirc is still connected, breaking thread pools (in +Python 3.9 and later). + +```py +irc.wait_until_disconnected() +``` + +### Parameter descriptions + +| Parameter | Description | +| ------------- | -------------------------------------------------------- | +| `ip` | The IP/hostname of the IRC server to connect to. | +| `port` | The port to connect to. | +| `nick` | The nickname of the bot. | +| `channels` | The channels to join on connect. This can be an iterable containing strings (list, set, etc), or (since v1.5.0) a comma-delimited string. | +| `ssl` | Enable TLS/SSL. If `None`, TLS is disabled unless the port is `6697`. | +| `ident` | The ident to use, defaults to `nick`. | +| `realname` | The realname to use, defaults to `nick` as well. | +| `persist` | Whether to automatically reconnect. | +| `debug` | Enables debug mode, prints all IRC messages. This can also be a file-like object (with write mode enabled) if you want debug messages to be written into a file instead of being printed to stdout, or a function (for example `logging.debug`). | +| `ns_identity` | The NickServ account to use as a tuple/list of length 2 (`('<user>', '<password>')`). For compatibility, this can be a string (`'<user> <password>'`). | +| `auto_connect`| Runs `irc.connect()` straight away. | +| `ircv3_caps` | A set() of additional IRCv3 capabilities to request. SASL is auto-added if `ns_identity` is specified. | +| `connect_modes` | A mode string (for example `'+B'`) of UMODEs to set when connected. | +| `quit_message`| Sets the default quit message. This can be modified per-quit with `irc.disconnect()`. | +| `ping_interval` | If no packets are sent or received for this amount of seconds, miniirc will send a `PING`, and if no reply is sent, after the ping timeout, miniirc will attempt to reconnect. Set to `None` to disable. | +| `ping_timeout` | The ping timeout used alongside the above `ping_interval` option, if unspecified will default to `ping_interval`. | +| `verify_ssl` | Verifies TLS/SSL certificates. Disabling this is not recommended as it opens the IRC connection up to MiTM attacks. If you have trouble with certificate verification, try running `pip3 install certifi` first. | +| `executor` | An instance of `concurrent.futures.ThreadPoolExecutor` to use when running handlers. | +| `keepnick` | If enabled, miniirc will attempt to obtain the original nick like ZNC's *keepnick. `irc.nick` will be the original nickname rather than the current one. | + +*The only mandatory parameters are `ip`, `port`, and `nick`.* + +## Functions + +| Function | Description | +| ------------- | -------------------------------------------------------- | +| `change_parser(parser=...)` | *See the message parser section for documentation.* | +| `connect()` | Connects to the IRC server if not already connected. | +| `ctcp(target, *msg, reply=False, tags=None)` | Sends a `CTCP` request or reply to `target`. | +| `debug(...)` | Debug, calls `print(...)` if debug mode is on. | +| `disconnect(msg=..., *, auto_reconnect=False)`| Disconnects from the IRC server. `auto_reconnect` will be overridden by `self.persist` if set to `True`. | +| `Handler(...)` | An event handler, see [Handlers](#handlers) for more info. | +| `me(target, *msg, tags=None)` | Sends a `/me` (`CTCP ACTION`) to `target`. | +| `msg(target, *msg, tags=None)` | Sends a `PRIVMSG` to `target`. `target` should not contain spaces or start with a colon. | +| `notice(target, *msg, tags=None)` | Sends a `NOTICE` to `target`. `target` should not contain spaces or start with a colon. | +| `quote(*msg, force=False, tags=None)` | Sends a raw message to IRC, use `force=True` to send while disconnected. Do not send multiple commands in one `irc.quote()`, as the newlines will be stripped and it will be sent as one command. The `tags` parameter optionally allows you to add a `dict` with IRCv3 client tags (all starting in `+`), and will not be sent to IRC servers that do not support client tags. | +| `send(*msg, force=False, tags=None)` | Sends a command to the IRC server, treating every positional argument as a parameter. The usage of this is recommended over `irc.quote()` unless you know what you are doing. | +| `wait_until_disconnected()` | Waits until the IRC server is disconnected and automatic reconnecting is turned off. | + +*Note that if `force=False` on `irc.quote` (or `irc.msg` etc is called) while +miniirc is not connected, messages will be temporarily stored and then sent +once miniirc is connected. Setting `force=True` will throw errors if miniirc is +completely disconnected (`irc.connected` is `None`).* + +### irc.quote and irc.send + +The two functions `irc.quote` and `irc.send` may sound similar, however are +fundamentally different: `irc.quote()` joins all provided arguments with spaces +and sends them as a raw message to IRC, while `irc.send()` treats each argument +as a parameter. If arguments passed to `irc.send()` contain spaces, they are +replaced with U+00A0 (a non-breaking space, visually similar to a regular +space however not interpreted as one). + +#### Examples + + - `irc.quote('PRIVMSG', '#channel :Hello,', 'world!')` sends "Hello, world!" + to #channel. + - `irc.quote('PRIVMSG', '#channel', 'Hello, world!')` is invalid ("Hello," and + "world!" are sent as separate parameters). + - `irc.send('PRIVMSG', '#channel', 'Hello, world!')` will send "Hello, world!" + to "#channel". + - `irc.send('PRIVMSG', '#channel :Hello,', 'world!')` will send "world!" to + `#channel\xa0:Hello,`, where `\xa0` is a non-breaking space. + +*If you are unsure and do not need compatibility with miniirc <1.5.0, use +`irc.send()`. `PRIVMSG` is just used as an example, if you need to send +`PRIVMSG`s use `irc.msg()` instead.* + +## Variables + +*These variables should not be changed outside `miniirc.py`.* + +| Variable | Description | +| ------------- | -------------------------------------------------------- | +| `active_caps` | A `set` of IRCv3 capabilities that have been successfully negotiated with the IRC server. This is empty while disconnected. | +| `connected` | A boolean (or `None`), `True` when miniirc is connected, `False` when miniirc is connecting, and `None` when miniirc is not connected. | +| `current_nick` | The bot/client's current nickname. Do not modify this, and use this instead of `irc.nick` when getting the bot's current nickname. | +| `isupport` | A `dict` with values (not necessarily strings) from `ISUPPORT` messages sent to the client. | +| `msglen` | The maximum length (in bytes) of messages (including `\r\n`). This is automatically changed if the server supports the `oragono.io/maxline-2` capability. | +| `nick` | The nickname to use when connecting to IRC. Until miniirc v2.0.0, you should only use or modify this while disconnected, as it is currently automatically updated with nickname changes. | + +The following arguments passed to `miniirc.IRC` are also available: `ip`, +`port`, `channels`, `ssl`, `ident`, `realname`, `persist`, `connect_modes`, +`quit_message`, `ping_interval`, `verify_ssl`. + +## Handlers + +`miniirc.Handler` and `miniirc.CmdHandler` are function decorators that add +functions to an event handler list. Functions in this list are called in their +own thread when their respective IRC event(s) is/are received. Handlers may +work on every IRC object in existence (`miniirc.Handler`) or only on +specific IRC objects (`irc.Handler`). + +The basic syntax for a handler is as followed, where `*events` is a list of events (`PRIVMSG`, `NOTICE`, etc) are called. + +```py +import miniirc +@miniirc.Handler(*events, colon=False) +def handler(irc, hostmask, args): + # irc: An 'IRC' object. + # hostmask: A 'hostmask' object. + # args: A list containing the arguments sent to the command. Everything + # following the first `:` in the command is put into one item + # (args[-1]). If "colon" is "False", the leading ":" (if any) + # is automatically removed. To prevent your code from horribly + # breaking, always set it to False unless you know what you are + # doing. + pass +``` + +#### Recommendations when using handlers: + + - If you don't need support for miniirc <1.4.0 and are parsing the last + parameter, setting `colon` to `False` is strongly recommended. If the + `colon` parameter is omitted, it defaults to `True`, however this will + change when miniirc v2.0.0 is released. + - Although `Handler` and `CmdHandler` currently accept any object that can be + converted to a string, every event is converted to a string internally. + - Not specifying the [`ircv3`](#ircv3-tags) parameter when it is not required + prevents a redundant `dict` from being created. + - To add handlers to a specific `IRC` object and not every one in existence, + use `irc.Handler` and `irc.CmdHandler` instead. If you want to create a + `Bot` or `Client` class and automatically add handlers to `IRC` objects + created inside it, see + [making existing functions handlers](#making-existing-functions-handlers). + +### Hostmask object + +Hostmasks are tuples with the format `('user', 'ident', 'hostname')`. If `ident` +and `hostname` aren't sent from the server, they will be filled in with the +previous value. If a command is received without a hostmask, all the `hostmask` +elements will be set to the name of the command. This is deprecated, however, +and when miniirc v2.0.0 is released the `hostmask` elements will be set to +empty strings. + +### Making existing functions handlers + +You can make existing functions handlers (for example class instance methods) +with `irc.Handler(*events)(handler_function)`. You probably don't want to use +`miniirc.Handler` for class instance methods, as this will create a handler +that gets triggered for every `IRC` object. + +You can also add multiple handlers of the same type easily: + +```py +add_handler = irc.Handler('PRIVMSG', colon=False) +add_handler(handler_1) +add_handler(self.instance_handler) +``` + +This is useful if you want to create a `Bot` (or `Client`) class and add +class-specific handlers without creating global process-wide handlers or +creating a wrapper function for every class instance. + +### IRCv3 support + +#### IRCv3 tags + +If you want your handler to support IRCv3 message tags, you need to add +`ircv3=True` to the `Handler` or `CmdHandler` decorator. You will need to add a +`tags` parameter to your function after `hostmask`. IRCv3 tags are sent to the +handlers as `dict`s, with values of either strings or `True`. + +*miniirc will automatically un-escape IRCv3 tag values.* + +```py +import miniirc +@miniirc.Handler(*events, colon=False, ircv3=True) +def handler(irc, hostmask, tags, args): + pass +``` + +#### IRCv3 capabilities + +You can handle IRCv3 capabilities before connecting using a handler. +You must use `force=True` on any `irc.quote()` called here, as when this is +called, miniirc may not yet be fully connected. Do not use the `colon` argument +for `Handler` when creating these handlers to avoid unexpected side-effects. + +```py +import miniirc +@miniirc.Handler('IRCv3 my-cap-name') +def handler(irc, hostmask, args): + # Process the capability here + + # IRCv3.2 capabilities: + # args = ['my-cap-name', 'IRCv3.2-parameters'] + + # IRCv3.1 capabilities: + # args = ['my-cap-name'] + + # Remove the capability from the processing list. + irc.finish_negotiation(args[0]) # This can also be 'my-cap-name'. +``` + +### Custom message parsers (not recommended) + +If the IRC server you are connecting to supports a non-standard message syntax, you can +create custom message parsers. These are called with the raw message (as a `str`) and +can either return `None` to ignore the message or a 4-tuple (`cmd, hostmask, tags, args`) +that will then be sent on to the handlers. The items in this 4-tuple should be the same +type as the items expected by handlers (and `cmd` should be a string). + +#### Message parser example + +This message parser makes the normal parser allow `~` as an IRCv3 tag prefix character. + +```py +import miniirc + +def my_message_parser(msg): + if msg.startswith('~'): + msg = '@' + msg[1:] + return miniirc.ircv3_message_parser(msg) +``` + +#### Changing message parsers + +To change message parsers, you can use `irc.change_parser(func=...)`. If `func` is not +specified, it will default to the built-in parser. You can only change message parsers +on-the-fly (for example in an IRCv3 CAP handler). If you need to change message parsers +before connecting, you can disable `auto_connect` and change it then. + +```py +irc = miniirc.IRC(..., auto_connect=False) +irc.change_parser(my_message_parser) +irc.connect() +``` + +### Handling multiple events + +If you want to handle multiple events and/or be able to get the name of the +event being triggered, you can use `irc.CmdHandler`. This will pass an extra +`command` argument to the handler function (between `irc` and `hostmask`), +containing a string with the command name (such as `PRIVMSG`). + +#### Catch-all handlers + +**Please do not use these unless there is no other alternative.** + +If you want to handle *every* event, you can use catch-all handlers. To create +these, you can call `irc.CmdHandler()` *without* any parameters. Note that this +handler will be called many times while connecting (and once connected). + +*You cannot call `irc.Handler()` without parameters.* + +### Example + +```py +import miniirc + +# Not required, however this makes sure miniirc isn't outdated. +assert miniirc.ver >= (1,8,2) + +@miniirc.Handler('PRIVMSG', 'NOTICE', colon=True) +def handler(irc, hostmask, args): + print(hostmask[0], 'sent a message to', args[0], 'with content', args[1]) + # nickname sent a message to #channel with content :Hello, world! + +@miniirc.CmdHandler('PRIVMSG', 'NOTICE', colon=False) +def cmdhandler(irc, command, hostmask, args): + print(hostmask[0], 'sent a', command, 'to', args[0], 'with content', + args[1]) + # nickname sent a PRIVMSG to #channel with content Hello, world! +``` + +This will print a line whenever the bot gets a `PRIVMSG` or `NOTICE`. + +## Misc functions + +miniirc provides the following helper functions: + +| Name | Description | +| ----------------------------- | ----------------------------------------- | +| `miniirc.get_ca_certs()` | Runs `certifi.where()` if `certifi` is installed, otherwise returns `None`. | +| `miniirc.ircv3_message_parser(msg)` | The default IRCv2/IRCv3 message parser, returns `cmd, hostmask, tags, args`. | +| `miniirc.ver` | A tuple containing version information. | +| `miniirc.version` | The `CTCP VERSION` reply, can be changed. | + +The version numbering system should be similar to [SemVer](https://semver.org/), +however backwards compatibility is preserved where possible when major releases +change. + +## Python version support + + - Python 3.3 and below are unsupported and do not work with miniirc. + - Python 3.4, 3.5, and 3.6 are currently supported, but support will likely be + dropped in miniirc v2.1.0. Major bugfixes may be backported to v2.0 for a + few months after v2.1's release. + - Python 3.7 and above should work with the latest stable version of miniirc. + +If there is a bug/error in Python 3.4 or newer, please open an issue or pull +request on [GitHub](https://github.com/luk3yx/miniirc/issues) or +[GitLab](https://gitlab.com/luk3yx/miniirc/issues). + +*If you are using Python 3.7 or an older version of Python, I strongly +recommend updating. Later versions of Python include features such as f-strings +that make software development easier.* + +## miniirc_extras + +If you want more advanced(-ish) features such as user tracking, you can use +[miniirc_extras](https://pypi.org/project/miniirc-extras/) +([GitHub](https://github.com/luk3yx/miniirc_extras), +[GitLab](https://gitlab.com/luk3yx/miniirc_extras)). Note that miniirc_extras +is still in beta and there will be breaking API changes in the future. + +## Deprecations + +If miniirc v2.0.0 is ever released, the following breaking changes will +(probably) be made: + + - Internal-only attributes `irc.handlers`, `irc.sock`, and `irc.sendq` + (please do not use these) will be renamed. Again, please do not use these. + - `irc.nick` will always be the nickname used when connecting to IRC rather + than the current nickname, use `irc.current_nick` for the current nickname + (since v1.4.3). + - `irc.ns_identity` will be stored as a tuple instead of a string, for example + `('username', 'password with spaces')` instead of + `'username password with spaces'`. Both formats are currently accepted and + will be accepted in the `ns_identity` keyword argument. + - No exceptions will be raised in `irc.quote`/`irc.send` with `force=True` + when the socket is closed. Instead of relying on these exceptions, use + `irc.connected` which is set to `None` when completely disconnected. + - As stated in the Python version support section, Python 3.4 support will be + dropped in miniirc v2.1.0, however bugfixes will be backported for a few + months. + - The `colon` keyword argument to `Handler` and `CmdHandler` will default to + `False` instead of `True`. + - Unspecified hostmasks will be an empty string instead of the command. Don't + rely on this "feature" if possible, simply ignore the hostmask if you do + not need it. + - The `extended-join` capability will be requested by default, use `args[0]` + instead of `args[-1]` to get the channel from a `JOIN` event. + - The `tags` keyword argument will be read-only. + +## Working examples/implementations + +Here is a list of some (open-source) bots using miniirc, in alphabetical order: + + - [irc-rss-feed-bot] - Posts RSS entry titles and shortened URLs to IRC + channels. *Python 3.8+* + - [irc-url-title-bot] - Gets webpage titles from URLs posted in IRC channels. + *Python 3.8+* + - [lurklite] - A generic configurable IRC bot. + *[GitHub](https://github.com/luk3yx/lurklite) link.* + - [stdinbot] - A very simple bot that dumps stdin to an IRC channel. + *[GitHub](https://github.com/luk3yx/stdinbot) link.* + +*Want to add your own bot/client to this list? Open an issue on +[GitHub](https://github.com/luk3yx/miniirc/issues) or +[GitLab](https://gitlab.com/luk3yx/miniirc/issues).* + +[irc-rss-feed-bot]: https://github.com/impredicative/irc-rss-feed-bot +[irc-url-title-bot]: https://github.com/impredicative/irc-url-title-bot +[lurklite]: https://gitlab.com/luk3yx/lurklite +[stdinbot]: https://gitlab.com/luk3yx/stdinbot + + +%package help +Summary: Development documents and examples for miniirc +Provides: python3-miniirc-doc +%description help +# miniirc + +[![Python 3.4+]](#python-version-support) [![Available on PyPI.]](https://pypi.org/project/miniirc/) [![License: MIT]](https://github.com/luk3yx/miniirc/blob/master/LICENSE.md) + +A relatively simple thread-safe(-ish) IRC client framework. + +To install miniirc, simply run `pip3 install miniirc`. + +If you have previously used miniirc, you may want to read the +[deprecations list] (last updated 2020-04-28). + +*This repository is available on both [GitHub](https://github.com/luk3yx/miniirc) and [GitLab](https://gitlab.com/luk3yx/miniirc).* + +[Python 3.4+]: https://img.shields.io/badge/python-3.4+-blue.svg +[Available on PyPI.]: https://img.shields.io/pypi/v/miniirc.svg +[License: MIT]: https://img.shields.io/pypi/l/miniirc.svg +[deprecations list]: #deprecations + +## Parameters + +```py +irc = miniirc.IRC(ip, port, nick, channels=None, *, ssl=None, ident=None, realname=None, persist=True, debug=False, ns_identity=None, auto_connect=True, ircv3_caps=set(), quit_message='I grew sick and died.', ping_interval=60, ping_timeout=None, verify_ssl=True, executor=None) +``` + +*Note that everything before the \* is a positional argument.* + +### Typical usage + +You don't need to add every argument, and the `ip`, `port`, `nick`, and +`channels` arguments should be specified as positional arguments. + +```py +irc = miniirc.IRC('irc.example.com', 6697, 'my-bot', ['#my-channel'], ns_identity=('my-bot', 'hunter2'), executor=concurrent.futures.ThreadPoolExecutor(), keepnick=True) +``` + +If you are not doing anything with the main thread after connecting to IRC, +please call `irc.wait_until_disconnected()` to prevent Python from trying to +shut down while miniirc is still connected, breaking thread pools (in +Python 3.9 and later). + +```py +irc.wait_until_disconnected() +``` + +### Parameter descriptions + +| Parameter | Description | +| ------------- | -------------------------------------------------------- | +| `ip` | The IP/hostname of the IRC server to connect to. | +| `port` | The port to connect to. | +| `nick` | The nickname of the bot. | +| `channels` | The channels to join on connect. This can be an iterable containing strings (list, set, etc), or (since v1.5.0) a comma-delimited string. | +| `ssl` | Enable TLS/SSL. If `None`, TLS is disabled unless the port is `6697`. | +| `ident` | The ident to use, defaults to `nick`. | +| `realname` | The realname to use, defaults to `nick` as well. | +| `persist` | Whether to automatically reconnect. | +| `debug` | Enables debug mode, prints all IRC messages. This can also be a file-like object (with write mode enabled) if you want debug messages to be written into a file instead of being printed to stdout, or a function (for example `logging.debug`). | +| `ns_identity` | The NickServ account to use as a tuple/list of length 2 (`('<user>', '<password>')`). For compatibility, this can be a string (`'<user> <password>'`). | +| `auto_connect`| Runs `irc.connect()` straight away. | +| `ircv3_caps` | A set() of additional IRCv3 capabilities to request. SASL is auto-added if `ns_identity` is specified. | +| `connect_modes` | A mode string (for example `'+B'`) of UMODEs to set when connected. | +| `quit_message`| Sets the default quit message. This can be modified per-quit with `irc.disconnect()`. | +| `ping_interval` | If no packets are sent or received for this amount of seconds, miniirc will send a `PING`, and if no reply is sent, after the ping timeout, miniirc will attempt to reconnect. Set to `None` to disable. | +| `ping_timeout` | The ping timeout used alongside the above `ping_interval` option, if unspecified will default to `ping_interval`. | +| `verify_ssl` | Verifies TLS/SSL certificates. Disabling this is not recommended as it opens the IRC connection up to MiTM attacks. If you have trouble with certificate verification, try running `pip3 install certifi` first. | +| `executor` | An instance of `concurrent.futures.ThreadPoolExecutor` to use when running handlers. | +| `keepnick` | If enabled, miniirc will attempt to obtain the original nick like ZNC's *keepnick. `irc.nick` will be the original nickname rather than the current one. | + +*The only mandatory parameters are `ip`, `port`, and `nick`.* + +## Functions + +| Function | Description | +| ------------- | -------------------------------------------------------- | +| `change_parser(parser=...)` | *See the message parser section for documentation.* | +| `connect()` | Connects to the IRC server if not already connected. | +| `ctcp(target, *msg, reply=False, tags=None)` | Sends a `CTCP` request or reply to `target`. | +| `debug(...)` | Debug, calls `print(...)` if debug mode is on. | +| `disconnect(msg=..., *, auto_reconnect=False)`| Disconnects from the IRC server. `auto_reconnect` will be overridden by `self.persist` if set to `True`. | +| `Handler(...)` | An event handler, see [Handlers](#handlers) for more info. | +| `me(target, *msg, tags=None)` | Sends a `/me` (`CTCP ACTION`) to `target`. | +| `msg(target, *msg, tags=None)` | Sends a `PRIVMSG` to `target`. `target` should not contain spaces or start with a colon. | +| `notice(target, *msg, tags=None)` | Sends a `NOTICE` to `target`. `target` should not contain spaces or start with a colon. | +| `quote(*msg, force=False, tags=None)` | Sends a raw message to IRC, use `force=True` to send while disconnected. Do not send multiple commands in one `irc.quote()`, as the newlines will be stripped and it will be sent as one command. The `tags` parameter optionally allows you to add a `dict` with IRCv3 client tags (all starting in `+`), and will not be sent to IRC servers that do not support client tags. | +| `send(*msg, force=False, tags=None)` | Sends a command to the IRC server, treating every positional argument as a parameter. The usage of this is recommended over `irc.quote()` unless you know what you are doing. | +| `wait_until_disconnected()` | Waits until the IRC server is disconnected and automatic reconnecting is turned off. | + +*Note that if `force=False` on `irc.quote` (or `irc.msg` etc is called) while +miniirc is not connected, messages will be temporarily stored and then sent +once miniirc is connected. Setting `force=True` will throw errors if miniirc is +completely disconnected (`irc.connected` is `None`).* + +### irc.quote and irc.send + +The two functions `irc.quote` and `irc.send` may sound similar, however are +fundamentally different: `irc.quote()` joins all provided arguments with spaces +and sends them as a raw message to IRC, while `irc.send()` treats each argument +as a parameter. If arguments passed to `irc.send()` contain spaces, they are +replaced with U+00A0 (a non-breaking space, visually similar to a regular +space however not interpreted as one). + +#### Examples + + - `irc.quote('PRIVMSG', '#channel :Hello,', 'world!')` sends "Hello, world!" + to #channel. + - `irc.quote('PRIVMSG', '#channel', 'Hello, world!')` is invalid ("Hello," and + "world!" are sent as separate parameters). + - `irc.send('PRIVMSG', '#channel', 'Hello, world!')` will send "Hello, world!" + to "#channel". + - `irc.send('PRIVMSG', '#channel :Hello,', 'world!')` will send "world!" to + `#channel\xa0:Hello,`, where `\xa0` is a non-breaking space. + +*If you are unsure and do not need compatibility with miniirc <1.5.0, use +`irc.send()`. `PRIVMSG` is just used as an example, if you need to send +`PRIVMSG`s use `irc.msg()` instead.* + +## Variables + +*These variables should not be changed outside `miniirc.py`.* + +| Variable | Description | +| ------------- | -------------------------------------------------------- | +| `active_caps` | A `set` of IRCv3 capabilities that have been successfully negotiated with the IRC server. This is empty while disconnected. | +| `connected` | A boolean (or `None`), `True` when miniirc is connected, `False` when miniirc is connecting, and `None` when miniirc is not connected. | +| `current_nick` | The bot/client's current nickname. Do not modify this, and use this instead of `irc.nick` when getting the bot's current nickname. | +| `isupport` | A `dict` with values (not necessarily strings) from `ISUPPORT` messages sent to the client. | +| `msglen` | The maximum length (in bytes) of messages (including `\r\n`). This is automatically changed if the server supports the `oragono.io/maxline-2` capability. | +| `nick` | The nickname to use when connecting to IRC. Until miniirc v2.0.0, you should only use or modify this while disconnected, as it is currently automatically updated with nickname changes. | + +The following arguments passed to `miniirc.IRC` are also available: `ip`, +`port`, `channels`, `ssl`, `ident`, `realname`, `persist`, `connect_modes`, +`quit_message`, `ping_interval`, `verify_ssl`. + +## Handlers + +`miniirc.Handler` and `miniirc.CmdHandler` are function decorators that add +functions to an event handler list. Functions in this list are called in their +own thread when their respective IRC event(s) is/are received. Handlers may +work on every IRC object in existence (`miniirc.Handler`) or only on +specific IRC objects (`irc.Handler`). + +The basic syntax for a handler is as followed, where `*events` is a list of events (`PRIVMSG`, `NOTICE`, etc) are called. + +```py +import miniirc +@miniirc.Handler(*events, colon=False) +def handler(irc, hostmask, args): + # irc: An 'IRC' object. + # hostmask: A 'hostmask' object. + # args: A list containing the arguments sent to the command. Everything + # following the first `:` in the command is put into one item + # (args[-1]). If "colon" is "False", the leading ":" (if any) + # is automatically removed. To prevent your code from horribly + # breaking, always set it to False unless you know what you are + # doing. + pass +``` + +#### Recommendations when using handlers: + + - If you don't need support for miniirc <1.4.0 and are parsing the last + parameter, setting `colon` to `False` is strongly recommended. If the + `colon` parameter is omitted, it defaults to `True`, however this will + change when miniirc v2.0.0 is released. + - Although `Handler` and `CmdHandler` currently accept any object that can be + converted to a string, every event is converted to a string internally. + - Not specifying the [`ircv3`](#ircv3-tags) parameter when it is not required + prevents a redundant `dict` from being created. + - To add handlers to a specific `IRC` object and not every one in existence, + use `irc.Handler` and `irc.CmdHandler` instead. If you want to create a + `Bot` or `Client` class and automatically add handlers to `IRC` objects + created inside it, see + [making existing functions handlers](#making-existing-functions-handlers). + +### Hostmask object + +Hostmasks are tuples with the format `('user', 'ident', 'hostname')`. If `ident` +and `hostname` aren't sent from the server, they will be filled in with the +previous value. If a command is received without a hostmask, all the `hostmask` +elements will be set to the name of the command. This is deprecated, however, +and when miniirc v2.0.0 is released the `hostmask` elements will be set to +empty strings. + +### Making existing functions handlers + +You can make existing functions handlers (for example class instance methods) +with `irc.Handler(*events)(handler_function)`. You probably don't want to use +`miniirc.Handler` for class instance methods, as this will create a handler +that gets triggered for every `IRC` object. + +You can also add multiple handlers of the same type easily: + +```py +add_handler = irc.Handler('PRIVMSG', colon=False) +add_handler(handler_1) +add_handler(self.instance_handler) +``` + +This is useful if you want to create a `Bot` (or `Client`) class and add +class-specific handlers without creating global process-wide handlers or +creating a wrapper function for every class instance. + +### IRCv3 support + +#### IRCv3 tags + +If you want your handler to support IRCv3 message tags, you need to add +`ircv3=True` to the `Handler` or `CmdHandler` decorator. You will need to add a +`tags` parameter to your function after `hostmask`. IRCv3 tags are sent to the +handlers as `dict`s, with values of either strings or `True`. + +*miniirc will automatically un-escape IRCv3 tag values.* + +```py +import miniirc +@miniirc.Handler(*events, colon=False, ircv3=True) +def handler(irc, hostmask, tags, args): + pass +``` + +#### IRCv3 capabilities + +You can handle IRCv3 capabilities before connecting using a handler. +You must use `force=True` on any `irc.quote()` called here, as when this is +called, miniirc may not yet be fully connected. Do not use the `colon` argument +for `Handler` when creating these handlers to avoid unexpected side-effects. + +```py +import miniirc +@miniirc.Handler('IRCv3 my-cap-name') +def handler(irc, hostmask, args): + # Process the capability here + + # IRCv3.2 capabilities: + # args = ['my-cap-name', 'IRCv3.2-parameters'] + + # IRCv3.1 capabilities: + # args = ['my-cap-name'] + + # Remove the capability from the processing list. + irc.finish_negotiation(args[0]) # This can also be 'my-cap-name'. +``` + +### Custom message parsers (not recommended) + +If the IRC server you are connecting to supports a non-standard message syntax, you can +create custom message parsers. These are called with the raw message (as a `str`) and +can either return `None` to ignore the message or a 4-tuple (`cmd, hostmask, tags, args`) +that will then be sent on to the handlers. The items in this 4-tuple should be the same +type as the items expected by handlers (and `cmd` should be a string). + +#### Message parser example + +This message parser makes the normal parser allow `~` as an IRCv3 tag prefix character. + +```py +import miniirc + +def my_message_parser(msg): + if msg.startswith('~'): + msg = '@' + msg[1:] + return miniirc.ircv3_message_parser(msg) +``` + +#### Changing message parsers + +To change message parsers, you can use `irc.change_parser(func=...)`. If `func` is not +specified, it will default to the built-in parser. You can only change message parsers +on-the-fly (for example in an IRCv3 CAP handler). If you need to change message parsers +before connecting, you can disable `auto_connect` and change it then. + +```py +irc = miniirc.IRC(..., auto_connect=False) +irc.change_parser(my_message_parser) +irc.connect() +``` + +### Handling multiple events + +If you want to handle multiple events and/or be able to get the name of the +event being triggered, you can use `irc.CmdHandler`. This will pass an extra +`command` argument to the handler function (between `irc` and `hostmask`), +containing a string with the command name (such as `PRIVMSG`). + +#### Catch-all handlers + +**Please do not use these unless there is no other alternative.** + +If you want to handle *every* event, you can use catch-all handlers. To create +these, you can call `irc.CmdHandler()` *without* any parameters. Note that this +handler will be called many times while connecting (and once connected). + +*You cannot call `irc.Handler()` without parameters.* + +### Example + +```py +import miniirc + +# Not required, however this makes sure miniirc isn't outdated. +assert miniirc.ver >= (1,8,2) + +@miniirc.Handler('PRIVMSG', 'NOTICE', colon=True) +def handler(irc, hostmask, args): + print(hostmask[0], 'sent a message to', args[0], 'with content', args[1]) + # nickname sent a message to #channel with content :Hello, world! + +@miniirc.CmdHandler('PRIVMSG', 'NOTICE', colon=False) +def cmdhandler(irc, command, hostmask, args): + print(hostmask[0], 'sent a', command, 'to', args[0], 'with content', + args[1]) + # nickname sent a PRIVMSG to #channel with content Hello, world! +``` + +This will print a line whenever the bot gets a `PRIVMSG` or `NOTICE`. + +## Misc functions + +miniirc provides the following helper functions: + +| Name | Description | +| ----------------------------- | ----------------------------------------- | +| `miniirc.get_ca_certs()` | Runs `certifi.where()` if `certifi` is installed, otherwise returns `None`. | +| `miniirc.ircv3_message_parser(msg)` | The default IRCv2/IRCv3 message parser, returns `cmd, hostmask, tags, args`. | +| `miniirc.ver` | A tuple containing version information. | +| `miniirc.version` | The `CTCP VERSION` reply, can be changed. | + +The version numbering system should be similar to [SemVer](https://semver.org/), +however backwards compatibility is preserved where possible when major releases +change. + +## Python version support + + - Python 3.3 and below are unsupported and do not work with miniirc. + - Python 3.4, 3.5, and 3.6 are currently supported, but support will likely be + dropped in miniirc v2.1.0. Major bugfixes may be backported to v2.0 for a + few months after v2.1's release. + - Python 3.7 and above should work with the latest stable version of miniirc. + +If there is a bug/error in Python 3.4 or newer, please open an issue or pull +request on [GitHub](https://github.com/luk3yx/miniirc/issues) or +[GitLab](https://gitlab.com/luk3yx/miniirc/issues). + +*If you are using Python 3.7 or an older version of Python, I strongly +recommend updating. Later versions of Python include features such as f-strings +that make software development easier.* + +## miniirc_extras + +If you want more advanced(-ish) features such as user tracking, you can use +[miniirc_extras](https://pypi.org/project/miniirc-extras/) +([GitHub](https://github.com/luk3yx/miniirc_extras), +[GitLab](https://gitlab.com/luk3yx/miniirc_extras)). Note that miniirc_extras +is still in beta and there will be breaking API changes in the future. + +## Deprecations + +If miniirc v2.0.0 is ever released, the following breaking changes will +(probably) be made: + + - Internal-only attributes `irc.handlers`, `irc.sock`, and `irc.sendq` + (please do not use these) will be renamed. Again, please do not use these. + - `irc.nick` will always be the nickname used when connecting to IRC rather + than the current nickname, use `irc.current_nick` for the current nickname + (since v1.4.3). + - `irc.ns_identity` will be stored as a tuple instead of a string, for example + `('username', 'password with spaces')` instead of + `'username password with spaces'`. Both formats are currently accepted and + will be accepted in the `ns_identity` keyword argument. + - No exceptions will be raised in `irc.quote`/`irc.send` with `force=True` + when the socket is closed. Instead of relying on these exceptions, use + `irc.connected` which is set to `None` when completely disconnected. + - As stated in the Python version support section, Python 3.4 support will be + dropped in miniirc v2.1.0, however bugfixes will be backported for a few + months. + - The `colon` keyword argument to `Handler` and `CmdHandler` will default to + `False` instead of `True`. + - Unspecified hostmasks will be an empty string instead of the command. Don't + rely on this "feature" if possible, simply ignore the hostmask if you do + not need it. + - The `extended-join` capability will be requested by default, use `args[0]` + instead of `args[-1]` to get the channel from a `JOIN` event. + - The `tags` keyword argument will be read-only. + +## Working examples/implementations + +Here is a list of some (open-source) bots using miniirc, in alphabetical order: + + - [irc-rss-feed-bot] - Posts RSS entry titles and shortened URLs to IRC + channels. *Python 3.8+* + - [irc-url-title-bot] - Gets webpage titles from URLs posted in IRC channels. + *Python 3.8+* + - [lurklite] - A generic configurable IRC bot. + *[GitHub](https://github.com/luk3yx/lurklite) link.* + - [stdinbot] - A very simple bot that dumps stdin to an IRC channel. + *[GitHub](https://github.com/luk3yx/stdinbot) link.* + +*Want to add your own bot/client to this list? Open an issue on +[GitHub](https://github.com/luk3yx/miniirc/issues) or +[GitLab](https://gitlab.com/luk3yx/miniirc/issues).* + +[irc-rss-feed-bot]: https://github.com/impredicative/irc-rss-feed-bot +[irc-url-title-bot]: https://github.com/impredicative/irc-url-title-bot +[lurklite]: https://gitlab.com/luk3yx/lurklite +[stdinbot]: https://gitlab.com/luk3yx/stdinbot + + +%prep +%autosetup -n miniirc-1.9.1 + +%build +%py3_build + +%install +%py3_install +install -d -m755 %{buildroot}/%{_pkgdocdir} +if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi +if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi +if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi +if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi +pushd %{buildroot} +if [ -d usr/lib ]; then + find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/lib64 ]; then + find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/bin ]; then + find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst +fi +if [ -d usr/sbin ]; then + find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst +fi +touch doclist.lst +if [ -d usr/share/man ]; then + find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst +fi +popd +mv %{buildroot}/filelist.lst . +mv %{buildroot}/doclist.lst . + +%files -n python3-miniirc -f filelist.lst +%dir %{python3_sitelib}/* + +%files help -f doclist.lst +%{_docdir}/* + +%changelog +* Wed Apr 12 2023 Python_Bot <Python_Bot@openeuler.org> - 1.9.1-1 +- Package Spec generated |