89bcc077d5 | ||
---|---|---|
docs | ||
pydle | ||
.gitignore | ||
LICENSE.md | ||
README.md | ||
setup.py |
README.md
pydle
Python IRC library.
pydle is a compact, flexible and standards-abiding IRC library for Python 3.
Features
- Compact: At ~1700SLoC at time of writing, it's not hard to find what you're looking for in the well-organized source code.
- Standards-abiding: Based on RFC1459 with some small extension tweaks, with full support of optional extension standards:
- Callback-based: 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.
- Liberally licensed: The 3-clause BSD license ensures you can use it everywhere.
Structure
pydle.Client
- full-featured client that supportspydle.BasicClient
plus all the features inpydle.features
.pydle.MinimalClient
- tinier client that supportspydle.BasicClient
plus some features inpydle.features
. (currentlyctcp
,isupport
andtls
)pydle.BasicClient
- basic RFC1459 implementation with a few commonly-implemented RFC281x extensions.pydle.ClientPool
- a 'pool' of several clients in order to handle multiple clients in one swift main loop.pydle.features
- extra (official/unofficial) IRC extensionspydle.features.ctcp
- Client-to-Client Protocol support.pydle.features.tls
- Transport Layer Security and STARTTLS support.pydle.features.isupport
- ISUPPORT/PROTOCTL support.pydle.features.cap
- CAP capability negotiation support.pydle.features.sasl
- Simple Authentication and Security Layer support - currently limited to thePLAIN
mechanism.pydle.features.ircv3_1
- Miscellaneous features ensuring support for IRCv3.1.
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:
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:
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:
# 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:
# 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
FAQ
Q: When constructing my own client class from several base classes, I get the following error: TypeError: Cannot create a consistent method resolution order (MRO) for bases X, Y, Z. What causes this and how can I solve it?
Pydle's use of class inheritance as a feature model may cause method resolution order conflicts if a feature inherits from a different feature, while a class inherits from both the original feature and the inheriting feature. To solve such problem, pydle offers a featurize
function that will automatically put all classes in the right order and create an appropriate base class:
# Purposely mis-ordered base classes, as SASLSupport inherits from CapabilityNegotiationSupport, but everything works fine.
MyBase = pydle.featurize(pydle.features.CapabilityNegotiationSupport, pydle.features.SASLSupport)
class Client(MyBase):
pass
API
pydle
featurize(*bases)
: create a client base class out of the given feature classes with a proper method resolution order. See the FAQ for details.
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 fortls_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 isFalse
;tls_verify
: whether or not to strictly verify the server certificate. Default isFalse
.
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 forClient.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 capabilitycap
has been negotiated, wheresuccess
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 byClient.on_ctcp_<query>
;Client.on_ctcp_reply(target, source, query, reply)
- callback called when the client receives a CTCP response, that is not handled byClient.on_ctcp_<query>_reply
;Client.on_ctcp_<query>(target, source)
- callback called when the client receives a CTCP . 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 response.
with pydle.features.isupport
, one generic callback is added:
Client.on_isupport_<feature>(value)
- callback called when the server announced support for ISUPPORT featurefeature
.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 capabilitycap
. 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 capabilitycap
. 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 untilClient.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 capabilitycap
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-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.