pydle/README.md

295 lines
14 KiB
Markdown
Raw Normal View History

2014-01-23 01:44:19 +00:00
pydle
=====
Python IRC library.
-------------------
pydle is a compact, flexible and standards-abiding IRC library for Python 3.
Features
--------
2014-01-23 01:50:52 +00:00
* Compact: At ~1700SLoC at time of writing, it's not hard to find what you're looking for in the well-organized source code.
2014-01-23 01:44:19 +00:00
* Standards-abiding: Based on [RFC1459](https://tools.ietf.org/html/rfc1459.html) with some small extension tweaks, with full support of optional extension standards:
- [TLS](http://tools.ietf.org/html/rfc5246)
- [CTCP](http://www.irchelp.org/irchelp/rfc/ctcpspec.html)
- (coming soon) [DCC](http://www.irchelp.org/irchelp/rfc/dccspec.html) and extensions
- [ISUPPORT/PROTOCTL](http://tools.ietf.org/html/draft-hardy-irc-isupport-00)
- [CAP](http://ircv3.atheme.org/specification/capability-negotiation-3.1)
- [SASL](http://ircv3.atheme.org/extensions/sasl-3.1)
- [IRCv3.1](http://ircv3.atheme.org/)
- (partial, in progress) [IRCv3.2](http://ircv3.atheme.org)
* Asynchronous: IRC is an asynchronous protocol and so should a library that implements it be. Callbacks are used to process events from the server.
* Modularized and extensible: Features on top of RFC1459 are implemented as seperate modules for a user to pick and choose, and write their own. Broad features are written to be as extensible as possible.
2014-01-23 01:50:52 +00:00
* Liberally licensed: The 3-clause BSD license ensures you can use it everywhere.
2014-01-23 01:44:19 +00:00
Structure
---------
* `pydle.Client` - full-featured client that supports `pydle.BasicClient` plus all the features in `pydle.features`.
* `pydle.MinimalClient` - tinier client that supports `pydle.BasicClient` plus some features in `pydle.features`. (currently `ctcp`, `isupport` and `tls`)
* `pydle.BasicClient` - basic [RFC1459](https://tools.ietf.org/html/rfc1459.html) implementation with a few commonly-implemented [RFC281x](https://tools.ietf.org/html/rfc2810.html) extensions.
* `pydle.features` - extra (official/unofficial) IRC extensions
- `pydle.features.ctcp` - [Client-to-Client Protocol](http://www.irchelp.org/irchelp/rfc/ctcpspec.html) support.
- `pydle.features.tls` - [Transport Layer Security](https://tools.ietf.org/html/rfc5246.html) and [STARTTLS](https://ircv3.atheme.org/extensions/tls-3.1) support.
- `pydle.features.isupport` - [ISUPPORT/PROTOCTL](http://tools.ietf.org/html/draft-hardy-irc-isupport-00) support.
- `pydle.features.cap` - [CAP](http://ircv3.atheme.org/specification/capability-negotiation-3.1) capability negotiation support.
- `pydle.features.sasl` - [Simple Authentication and Security Layer](http://ircv3.atheme.org/extensions/sasl-3.1) support - currently limited to the `PLAIN` mechanism.
- `pydle.features.ircv3_1` - [Miscellaneous](http://ircv3.atheme.org/extensions/multi-prefix-3.1) [features](http://ircv3.atheme.org/extensions/account-notify-3.1) [ensuring](http://ircv3.atheme.org/extensions/away-notify-3.1) [support](http://ircv3.atheme.org/extensions/extended-join-3.1) for [IRCv3.1](http://ircv3.atheme.org/).
* `pydle.ClientPool` - a 'pool' of several clients in order to handle multiple clients in one swift main loop.
Basic Usage
-----------
`python3 setup.py install`
From there, you can `import pydle` and subclass `pydle.Client` for your own functionality.
Setting a nickname and starting a connection over TLS:
```python
import pydle
# Simple echo bot.
class MyOwnBot(pydle.Client):
def on_connect(self):
self.join('#bottest')
def on_message(self, source, target, message):
self.message(target, message)
client = MyOwnBot('MyBot', realname='My Bot')
client.connect('irc.rizon.net', 6697, tls=True, tls_verify=False)
client.handle_forever()
```
*But wait, I want to handle multiple clients!*
No worries! Use `pydle.ClientPool` like such:
```python
pool = pydle.ClientPool()
for i in range(10):
client = MyOwnBot('MyBot' + str(i))
client.connect('irc.rizon.net', 6697, tls=True, tls_verify=False)
pool.add(client)
# This will make sure all clients are treated in a fair way priority-wise.
pool.handle_forever()
```
If you want to customize bot features, you can subclass `pydle.BasicClient` and one or more features from `pydle.features` or your own feature classes, like such:
```python
# Only support RFC1459 (+small features), CTCP and our own ACME extension to IRC.
class MyFeaturedBot(pydle.features.ctcp.CTCPSupport, acme.ACMESupport, pydle.BasicClient):
pass
```
To create your own features, just subclass from `pydle.BasicClient` and start adding callbacks for IRC messages:
```python
# Support custom ACME extension.
class ACMESupport(pydle.BasicClient):
def on_raw_999(self, source, params):
""" ACME's custom 999 numeric tells us to change our nickname. """
nickname = params[0]
self.nickname = nickname
```
API
---
**pydle.Client**
`Client(nickname, fallback_nicknames=[], username=None, realname=None)` - construct a client. if `username`/`realname` are not given, they will be constructed from the nickname.
with `pydle.features.tls`, two extra keyword arguments are added:
- `tls_client_cert`: path to client certificate to use for TLS authentication;
- `tls_client_cert_key`: path to keyfile to use for `tls_client_cert`.
with `pydle.features.sasl`, three extra keyword arguments are added:
- `sasl_identity`: `AUTHZID` to use for SASL authentication. Default and most common option is `''` (empty);
- `sasl_username`: SASL username (`AUTHCID`);
- `sasl_password`: SASL password.
`Client.connect(host, port=None, password=None, encoding='utf-8')`- connect to server.
with `pydle.features.tls`, two extra keyword arguments are added:
- `tls`: whether or not to use TLS for this connection. Default is `False`;
- `tls_verify`: whether or not to strictly verify the server certificate. Default is `False`.
`Client.disconnect()` - disconnect from server.
`Client.handle_forever()` - a 'main loop'-esque method. Will not return until the client disconnected.
*Attributes*
`Client.DEFAULT_QUIT_MESSAGE` - default quit message when `Client.quit()` is called without arguments.
`Client.connected` - whether or not this client is connected.
`Client.registered` - whether or not this client has passed the IRC registration stage.
`Client.connection` - the `pydle.connection.Connection` instance associated with this client.
`Client.logger` - the `pydle.logging.Logger` instance associated with this client.
`Client.nickname` - get or set to retrieve or attempt to set current nickname.
`Client.username` - the current username. Changes will only take effect on reconnect.
`Client.realname` - the current realname. Changes will only take effect on reconnect.
`Client.password` - the server password used for this connection. Changes will only take effect on reconnect.
`Client.motd` - set after connecting. The IRC server Message of the Day, if any.
`Client.network` - set after connecting if sent by server. The IRC network this server belongs to.
`Client.server_tag` - a 'tag' to use for the currently connected to server.
`Client.users` - an informational dictionary about users the client knows about.
`Client.channels` - an informational dictionary about the channels the client is in.
with `pydle.features.tls`, two extra attributes are added:
- `Client.tls_client_cert` - file path to TLS client certificate to use;
- `Client.tls_client_cert_key` - file path to keyfile to use for `Client.tls_client_cert`.
with `pydle.features.sasl`, four extra attributes are added:
- `Client.SASL_TIMEOUT`: amount of seconds to wait for response from server before aborting SASL authentication.
- `Client.sasl_identity`: `AUTHZID` to use for SASL authentication. Default and most common option is `''` (empty);
- `Client.sasl_username`: SASL username (`AUTHCID`);
- `Client.sasl_password`: SASL password.
*IRC*
`Client.join(channel, password=None)` - join channel.
`Client.part(channel, reason=None)` - part channel.
`Client.cycle(channel)` - rejoin channel.
`Client.quit(message=pydle.DEFAULT_QUIT_MESSAGE)` - quit network.
`Client.message(target, message)` - send a message.
`Client.notice(target, message)` - send a notice.
`Client.mode(target, *modes)` - set channel or user modes.
`Client.away(message)` - set self as away with message.
`Client.back()` - set self as not away anymore.
`Client.raw(message)` - send raw IRC command.
with `pydle.features.ctcp`, two extra methods are added:
- `Client.ctcp(target, query)` - send CTCP request;
- `Client.ctcp_reply(target, query, response)` - send CTCP response.
with `pydle.features.cap`, one extra method is added:
- `Client.capability_negotiated(cap, success=True)` - indicate the capability `cap` has been negotiated, where `success` indicates if negotiation succeeded.
*Helpers*
`Client.is_channel(target)` - return whether or not `target` is a channel.
`Client.in_channel(channel)` - return whether or not client is in channel.
`Client.same_nick(left, right)` - compare nicknames according to proper IRC case mapping.
*Callbacks*
`Client.on_connect()` - callback called after the client has successfully connected and registered to the server.
`Client.on_disconnect()` - callback called after the client has disconnected from the server.
`Client.on_quit(user, reason=None)` - callback called when someone (maybe the client) quit the network.
`Client.on_kill(user, source, reason)` - callback called when someone (maybe the client) was killed from the network.
`Client.on_message(target, source, message)` - callback called when the client receives a PRIVMSG, either in a channel or privately.
`Client.on_channel_message(channel, source, message)` - callback called when the client receives a PRIVMSG in a channel.
`Client.on_private_message(source, message)` - callback called when the client receives a private PRIVMSG.
`Client.on_notice(target, source, message)` - callback called when the client receives a NOTICE, either in a channel or privately.
`Client.on_channel_notice(target, source, message)` - callback called when the client receives a NOTICE in a channel.
`Client.on_private_notice(source, message)` - callback called when the client receives a private NOTICE.
`Client.on_invite(channel, source)` - callback called when the client receives an invite to a channel.
`Client.on_join(channel, user)` - callback called when someone (maybe the client) joins a channel.
`Client.on_part(channel, user, message=None)` - callback called when someone (maybe the client) parted a channel.
`Client.on_kick(channel, user, source, reason=None)` - callback called when someone (maybe the client) was kicked from a channel.
`Client.on_topic_change(channel, topic, source)` - callback called when someone sets the topic in a channel.
`Client.on_mode_change(target, modes, source)` - callback called when either someone sets new modes on a channel or the client (or server) change their user mode.
`Client.on_nick_change(old, new)` - callback called when someone (maybe the client) changes their nickname.
`Client.on_unknown(command, source, params)` - callback called when the client receives a raw IRC message it doesn't know how to deal with.
with `pydle.features.ctcp`, two extra callbacks are added, and two generic callbacks:
- `Client.on_ctcp(target, source, query)` - callback called when the client receives a CTCP query, either directed to a channel or to the client privately, that is not handled by `Client.on_ctcp_<query>`;
- `Client.on_ctcp_reply(target, source, query, reply)` - callback called when the client receives a CTCP response, that is not handled by `Client.on_ctcp_<query>_reply`;
- `Client.on_ctcp_<query>(target, source)` - callback called when the client receives a CTCP <query>. The query name should be lower case. Example: `on_ctcp_version(target, source)` will be called if the client receives a CTCP VERSION request;
- `Client.on_ctcp_<query>_reply(target, source, reply)` - callback called when the client receives a CTCP <query> response.
with `pydle.features.isupport`, one generic callback is added:
- `Client.on_isupport_<feature>(value)` - callback called when the server announced support for ISUPPORT feature `feature`. `value` is None if not given by server.
with `pydle.features.cap`, three generic callbacks are added:
- `Client.on_capability_<cap>_available()` - callback called when the server announced support for capability `cap`. Should return whether or not the client wants to request this capability.
- `Client.on_capability_<cap>_enabled()` - callback called when the server acknowledges the client's request for capability `cap`. Should return one of three following values:
* `pydle.CAPABILITY_NEGOTIATED` - default value assumed when nothing returned. The capability has been successfully negotiated.
* `pydle.CAPABILITY_NEGOTIATING` - the callback is still negotiating the capability. Stall general capability negotiation until `Client.capability_negotiated(<cap>)` has been called.
* `pydle.CAPABILITY_FAILED` - the callback failed to negotiate the capability. Attempt to disable it again.
- `Client.on_capability_<cap>_disabled()` - callback called when capability `cap` that was requested before has been disabled.
You can also overload `Client.on_raw_<cmd>(source, params)`, where `cmd` is the raw IRC command (either a text command or a zero-filled numeric code) if you really want to, but this is not advisable if you're not building features as it may disable certain built-in functionalities if you're not careful.
**pydle.ClientPool**
`ClientPool(clients)` - instantiate a pool with `clients` as initial clients.
`ClientPool.add(client)` - add client to pool.
`ClientPool.remove(client)` - remove client from pool.
`ClientPool.has_message()` - check whether or not there are unprocessed message(s) available in this pool.
`ClientPool.handle_message()` - handle a single unprocessed message.
`ClientPool.wait_for_message()` - wait for a new message to arrive.
`ClientPool.handle_forever()` - enter main loop for pool. Will not return until all clients disconnected.
Utilities
---------
`python3 -m pydle.utils.irccat` - simple [irccat](http://sourceforge.net/projects/irccat/)-like implementation built on top of pydle. Read raw IRC commands from stdin, dumps incoming messages to stdout.
`python3 -m pydle.utils.console` - interactive console for a Pydle bot. `self` is defined in-scope as the running bot instance.
`python3 -m pydle.utils.run` - run a Pydle bot in the foreground.
TODO
----
* Work on documentation.
* Finalize IRCv3.2 support.
* Add DCC support.
License
-------
Pydle is licensed under the 3-clause BSD license. See LICENSE.md for details.