3
0
mirror of https://github.com/jlu5/PyLink.git synced 2025-08-02 19:07:22 +02:00

Compare commits

...

2546 Commits

Author SHA1 Message Date
James Lu
43e41cdfa5 PyLink 3.1.0
Update README links and mark the project as discontinued.
2023-01-03 18:48:24 -08:00
Paige Thompson
c453926718
Allow loading a custom CA certificate via a ssl_cafile option (#677)
Co-authored-by: Paige Thompson <paigeadele@gmail.com>
2022-09-06 19:04:54 -07:00
James Lu
523c1d2b13 relay: strip / from idents
Closes #673.
2022-02-21 02:21:49 -08:00
James Lu
ab982662b1 Remove travis-ci config
[skip ci]
2022-02-05 14:34:59 -08:00
James Lu
fb2327d4e7 Update README
[skip ci]
2022-02-04 12:27:28 -08:00
James Lu
8c4efc030a raw: fix permission check logic 2022-01-02 11:19:38 -08:00
James Lu
499a0dd403 drone: add PyPI push, build Docker on a branch to allow for weekly cron 2021-12-30 11:19:35 -08:00
James Lu
ca2d603fa1 RELNOTES: fix date 2021-12-30 11:04:52 -08:00
James Lu
cd2ecc3853 Merge branch 'ci-update' 2021-12-30 11:04:29 -08:00
James Lu
a0a2cda49f PyLink 3.1-beta1 2021-12-30 11:02:41 -08:00
James Lu
6391d6c282 Run CI on push, against multiple Python versions 2021-12-28 10:56:50 -08:00
James Lu
563b48e9c5 Revert "drone CI: remove date based tags, it isn't strictly necessary"
This reverts commit 0cba40e4c512932a80aae1413707faac67d74f9d.
2021-12-28 10:46:46 -08:00
James Lu
2915bf2236 opercmds: more consistent formatting for KILL messages
Any "Killed (sender (...)" prefixes should be added by the protocol module.
2021-12-25 01:02:19 -08:00
James Lu
d6de0d97f3 unreal: send kill messages without killpath
Closes #671.
2021-12-25 01:00:42 -08:00
James Lu
171eccf9c7 Further harden exec and raw in the default setup
Closes #568.
2021-12-25 00:47:50 -08:00
James Lu
d3ccdca3d1 Merge branch 'py37-base' 2021-12-25 00:33:45 -08:00
James Lu
3155172fc8 Revert "setup.py: work around installation error on Python 3.4"
This reverts commit c91b5f74ea5449f65205d0581416856f018cd2a0.
2021-12-25 00:33:36 -08:00
James Lu
243efbd0f8 Revert "test_irc_parsers: fix Python 3.5 support"
This reverts commit 8cf1beb1839ce874730256aad7dfa4496d37c217.
2021-12-25 00:33:36 -08:00
James Lu
8d01eaa5c8 Bump base version to Python 3.7 2021-12-25 00:32:32 -08:00
James Lu
ada130b1cd Drop references to devel branch
This project is not currently active enough to justify a multi-branch setup..
2021-12-25 00:31:15 -08:00
James Lu
e6401a19df relay: remove race condition-prone print
Speculative fix #670
2021-12-25 00:27:34 -08:00
James Lu
ac7339e460 Default to system IPv4/IPv6 preference when resolving hostnames
Fixes #667. This implementation is similar but also preserves compatibility with the "ipv6" option,
which allows setting the preferred address type without hardcoding a bind IP.
2021-12-25 00:18:03 -08:00
James Lu
f55057092a inspircd: set default target_version to insp3 2021-12-24 23:43:12 -08:00
James Lu
46cc621df1 More concise UID generators 2021-07-14 21:57:56 -07:00
James Lu
bc3a7abe02 ircs2s_common: don't strip away other whitespace chars when tokenizing 2021-07-14 21:57:56 -07:00
James Lu
3f59dcd884 unreal: bounce attempts to CHGIDENT/HOST/NAME protected services clients 2021-06-23 09:35:10 -07:00
James Lu
fc971aa679 pmodule-spec: Update notes about non-IRC protocols and PyLinkNetworkCoreWithUtils 2021-06-15 00:22:14 -07:00
James Lu
e25e3834a8 Add support for oper notices (GLOBOPS/OPERWALL) (#511) 2021-06-15 00:21:03 -07:00
James Lu
2ae72d6723 Expose SSL/TLS state in UID hooks when available (#169) 2021-06-13 01:00:41 -07:00
James Lu
8322817395 p10: fix message handling; sender numerics are not prefixed with ":" here
Regression from 8db238e869208d29d35cac10f0aa6899d3eb3cb1
2021-06-13 00:44:41 -07:00
James Lu
6ba99b302f Merge remote-tracking branch 'origin/wip/track-user-ssl' into devel 2021-06-13 00:11:18 -07:00
James Lu
92f60ecb4b Bump to 3.1-dev 2021-06-13 00:11:08 -07:00
James Lu
075c1d141d relay: sanitize idents on hybrid further 2021-06-09 20:19:26 -07:00
James Lu
d94593e4f6 unreal: declare EXTSWHOIS support 2021-06-09 20:18:34 -07:00
James Lu
8db238e869 ircs2s_common: only read sender prefixes on lines starting with ":"
This fixes incorrect behaviour if the nick of a sender matches that of a S2S command (e.g. "ping")
2021-06-09 20:17:27 -07:00
James Lu
da7f9611bc Remove my old nickname from examples 2021-06-09 20:15:49 -07:00
James Lu
ed1644a636 relay: workaround strict ident checks on hybrid 2021-06-05 00:39:23 -07:00
James Lu
1b433b741b classes: add ProtocolError to __all__ 2021-06-05 00:39:18 -07:00
James Lu
53e30520b8 Guard exec plugin behind a config option 2021-06-04 17:48:29 -07:00
James Lu
ba5f89d03c Move to libera.chat
[skip ci]
2021-05-19 18:03:13 -07:00
James Lu
88294ff0b0 relay-quickstart: a long needed refresh
- Document in more detail how Relay actually works (mirroring / puppeting via a services server)
- Document more clearly the steps to create a channel: especially mention how CREATE and LINK have to be run from different networks.
- Various typo fixes
2021-05-03 21:18:24 -07:00
James Lu
0b6845fa92 relay: allow deleting channels created before a casemapping change 2021-02-27 12:02:42 -08:00
James Lu
4500e27931 relay: fix channel not found errors on LINK when remote casemapping differs
This can happen, e.g. if the remote network uses casemapping=ascii and has a channel containing "|", and the local network uses casemapping=rfc1459.

Thanks to iTzNavic for reporting.
2021-02-27 12:01:01 -08:00
James Lu
537c643ed0 ircs2s_common: gracefully handle QUIT messages without a reason
Closes #663.
2021-02-14 22:39:57 -08:00
James Lu
d9aa5e9869 Merge remote-tracking branch 'origin/master' into devel 2021-02-14 22:33:59 -08:00
James Lu
17fccfeca9 Add a basic linting config 2021-02-09 12:31:25 -08:00
James Lu
c5541b58e5 docs: mention that Clientbot-only relays are not supported 2021-02-09 12:31:21 -08:00
James Lu
5eca51d979 example-conf: inspircd 3 support is not beta anymore 2021-01-20 11:58:50 -08:00
James Lu
15d51b3455 Don't loop infinitely if _send fails... 2021-01-10 10:28:34 -08:00
James Lu
b254a7f971 readme refresh
yeah, I'm officially looking for new maintainers
2020-12-07 12:50:15 -08:00
James Lu
d53ed4f25d readme refresh
yeah, I'm officially looking for new maintainers
2020-12-07 12:08:19 -08:00
James Lu
cc2298d0be inspircd: track SSL/TLS status of remote users 2020-10-19 14:03:35 -07:00
James Lu
8ee0f3fdab hybrid, p10, ts6, unreal: track SSL/TLS status of remote users (#169) 2020-10-19 13:58:55 -07:00
James Lu
e0cc238001 ts6: fix ssl umode definitions 2020-10-19 13:58:03 -07:00
James Lu
b02aadf378 _send: break if the socket is None 2020-09-29 18:49:43 +00:00
James Lu
d50de12834 Retry when socket.send() fails with BlockingIOError / EAGAIN 2020-09-29 17:45:13 +00:00
James Lu
2aa00d6efc relay: skip messages from clientbot networks when relay_clientbot isn't loaded 2020-08-15 23:23:29 -07:00
Celelibi
16a7cef1aa plugin relay: Rename homeirc to origirc
Signed-off-by: Celelibi <celelibi@gmail.com>
2020-06-18 21:15:53 -07:00
Celelibi
43532fd1cc ClientbotWrapperProtocol: Missing log message argument: channel
Signed-off-by: Celelibi <celelibi@gmail.com>
2020-06-18 21:15:53 -07:00
Celelibi
cb0af148d8 IRCNetwork: Unused attribute _selector_key
Was always None because the called function doesn't return a value.

Signed-off-by: Celelibi <celelibi@gmail.com>
2020-06-18 21:15:53 -07:00
Celelibi
dcd0a28c89 PyLinkNetworkCore: rename filename, config -> channel, chandata
Signed-off-by: Celelibi <celelibi@gmail.com>
2020-06-18 21:15:53 -07:00
Celelibi
7204ef1cf1 plugin stats: Refactor multiline function call
Signed-off-by: Celelibi <celelibi@gmail.com>
2020-06-18 21:15:53 -07:00
Celelibi
88116dbe8d plugin stats: Missing parentheses change number of arguments
Signed-off-by: Celelibi <celelibi@gmail.com>
2020-06-18 21:15:53 -07:00
Celelibi
b49d5775e2 TS6Protocol: Missing logging argument in handle_realhost
Signed-off-by: Celelibi <celelibi@gmail.com>
2020-06-18 21:15:53 -07:00
Celelibi
03c9c71dc3 TS6Protocol: change servername to numeric in handle_pass
Signed-off-by: Celelibi <celelibi@gmail.com>
2020-06-18 21:15:53 -07:00
Celelibi
26bdc90781 TS6Protocol: target_ircd renamed when refactored
Signed-off-by: Celelibi <celelibi@gmail.com>
2020-06-18 21:15:53 -07:00
Celelibi
6bf66f9e4d KeyedDefaultdict: super() already bind to self
Signed-off-by: Celelibi <celelibi@gmail.com>
2020-06-18 21:15:53 -07:00
Celelibi
84b73bb89f
Tidy up imports and define __all__ in modules (#660) 2020-06-18 15:47:20 -07:00
Celelibi
9470e9329a
Implement path configuration for files created by pylink (#659)
* Configure log directory

Signed-off-by: Celelibi <celelibi@gmail.com>

* Configure data store directory

Signed-off-by: Celelibi <celelibi@gmail.com>

* Configure PID file directory

Signed-off-by: Celelibi <celelibi@gmail.com>
2020-05-30 11:49:01 -07:00
James Lu
947732580a PyLink 3.0.0 2020-04-11 00:28:24 -07:00
James Lu
3d5f9685c5 README: update badge colours & order
[skip ci]
2020-04-10 16:27:42 -07:00
James Lu
c53ee0a80c README: refresh, add Docker install instructions
[skip ci]
2020-04-10 14:56:52 -07:00
James Lu
0cba40e4c5 drone CI: remove date based tags, it isn't strictly necessary 2020-04-10 13:38:18 -07:00
James Lu
c14fc5cf07 Add Drone CI pipeline for building Docker image 2020-04-10 13:27:27 -07:00
James Lu
ba22b18cc4 Replace references to PyLink 2.1 with 3.0 2020-04-10 11:15:54 -07:00
James Lu
a143d98ac1 global: always coerse channel argument to str
These can be type int as of v3.0, e.g. in pylink-discord
2020-03-30 13:09:38 -07:00
James Lu
a19f257bd8 clientbot: remove references to self.irc
Reported by @genius3000.
2020-03-29 01:15:11 -07:00
James Lu
380854f88a Remove references to ./pylink
Running the launcher from the source folder isn't necessary anymore.
2020-03-24 18:07:18 -07:00
James Lu
8ffd787075 global: call match_text() on the target network object
Technically this will only make a difference if a server object overrides match_text(), but this is the more correct solution.
2020-03-16 19:07:18 -07:00
James Lu
b4d7883a71 global: ignore networks that don't have .pseudoclient set
This is the case for e.g. the control network object in pylink-discord.
2020-03-16 19:06:28 -07:00
James Lu
f17540a7e2 README refresh; update supported IRCds list
- Move charybdis to extended support - I have not seen any networks use this in production
- Move legacy p10 / ts6 variants into a new Legacy support section

[skip ci]
2020-03-14 10:28:21 -07:00
James Lu
b433fed718 unreal: update module header 2020-03-08 16:47:31 -07:00
James Lu
475349dc39 p10: warn when receiving an invalid subcommand with use_extended_accounts=true 2020-03-08 16:29:16 -07:00
James Lu
a8f0bd4fb7 classes: remove self.proto, self.irc compat links 2020-03-08 15:37:06 -07:00
James Lu
f7e84e7816 PyLink 3.0-rc1 2020-02-22 23:57:43 -08:00
James Lu
bdfeec09c6 Merge remote-tracking branch 'origin/master' into devel
Conflicts:
	classes.py
2020-02-22 23:57:02 -08:00
James Lu
7ce8f41f95 Add changelog for 3.0-rc1 2020-02-22 23:56:27 -08:00
James Lu
99c379e32f example-conf: unmark encoding option as experimental 2020-02-22 23:56:10 -08:00
James Lu
908dcb4873 p10: ignore ACCOUNT subcommands other than R, M, and U 2020-02-16 11:31:02 -08:00
James Lu
b083b6dede commands.version: replace license notice with (a much more useful) Python version 2020-01-26 13:48:43 -08:00
James Lu
5c3306bcff ircs2s_common: fix handling when failing to extract kill reason 2020-01-26 00:31:17 -08:00
James Lu
7c0deaa684 relay-quickstart: describe delinking another network from our channels
[skip ci]
2020-01-09 08:53:44 -08:00
James Lu
574a3056a3 modelists: update githack URLs
[skip ci]
2019-12-29 10:08:47 -08:00
James Lu
0e13243d02 chatircd: fold usermode +t into sslonlymsg 2019-12-29 10:04:38 -08:00
James Lu
6e1352dfd3 inspircd: alias antiredirect (umode +L) to noforward 2019-12-29 10:02:02 -08:00
James Lu
34cd0ea7d5 modelists: mark module-provided modes on InspIRCd 2019-12-29 10:00:55 -08:00
James Lu
ea2925ef37 modelists: drop m_ prefix from insp3 column 2019-12-29 09:53:15 -08:00
James Lu
0e10b62705 ngircd: fixup mode definitions 2019-12-29 09:50:47 -08:00
James Lu
f01fada92f unreal: declare support for umodes +Z and +G 2019-12-29 09:50:00 -08:00
James Lu
5a487114b6 docs/modelists: add some missing RFC1459 mode definitions
[skip ci]
2019-12-29 09:32:27 -08:00
James Lu
52730268b0 exttarget: use match_text() to match exttarget text fields
(cherry picked from commit bfe8a23dbc363aee0813dce9784f03ec87127411)
2019-12-26 11:59:33 -08:00
James Lu
4d24aa236b exttargets.account: fix extraneous lowercasing of the network name
(cherry picked from commit b45fe4c12110fa3d8925a53e5386e8216768da25)
2019-12-26 11:59:33 -08:00
James Lu
a178a92d6a classes: fix incorrect wrap_messages() stub
(cherry picked from commit 5731a301ce98400e77108b3f6dbc98d675c0a902)
2019-12-26 11:59:33 -08:00
James Lu
bfe8a23dbc exttarget: use match_text() to match exttarget text fields 2019-12-25 16:04:58 -08:00
James Lu
b45fe4c121 exttargets.account: fix extraneous lowercasing of the network name 2019-12-25 15:58:18 -08:00
James Lu
ae01c5e418 Add Dockerfile
Closes #652.

Co-authored-by: James Newton <hello@jamesnewton.com>
2019-12-23 00:02:57 -08:00
James Lu
a79354cd52 apply_modes: fix removing multiple ban modes in one command 2019-12-22 23:57:36 -08:00
James Lu
847c26cd7b Declare Python 3.8 support
Also switch CI base version from xenial to bionic.
2019-12-22 23:13:49 -08:00
James Lu
44770fb6d1 classes: fix SyntaxWarning: "is" with a literal on Python 3.8 2019-12-22 23:11:58 -08:00
James Lu
594b7124ff inspircd: warn when using InspIRCd 2 compat mode on an InspIRCd 3 uplink
Some commands like KICK are not mangled correctly in this mode.
2019-12-22 22:37:49 -08:00
James Lu
dadf2c3211 structures: suppress CopyWrapper logging 2019-12-22 22:32:10 -08:00
James Lu
b24dc206e0 inspircd: negotiate casemapping setting on link for InspIRCd 3
Closes #654.
2019-12-22 22:27:12 -08:00
James Lu
b495cb4c5e classes: clean up _expandPUID
Also make the logging less noisy.
2019-12-22 22:14:26 -08:00
James Lu
b1b14dfb55 classes: disable logging about modes by default 2019-12-22 22:14:26 -08:00
James Lu
b15c885dbc RELNOTES: move all github references to my new username
[skip ci]
2019-12-08 12:21:33 -08:00
James Lu
6a71ca64ac PyLink 2.1-beta1 2019-12-08 12:18:13 -08:00
James Lu
0a264b430e docs/t: fix wrong version for the last two protocol caps 2019-12-08 12:17:33 -08:00
James Lu
daf496cc96 PyLink 2.0.3
-----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEwmmRkfzwXj5hKJctxWWyWArxYlwFAl2gudoACgkQxWWyWArx
 YlwcUg/9ERp6HnviDa46cXt2OcQg0eNAuTFl0H+YCpjzj2Qvg8uBdCjh0jzAahh4
 rGw4XbKp5l3kXWMyjoy1roKqYbYy6nUdE4HCiER7wxpHqbUnfprCv6REIOxazz7Z
 jRwKbD89FKPtn957C7qfBBY93QX6h5v92QsotuAwyxDpN/3UTtpP11c/zb48m6Fx
 Mmabgj7HWAalB5w7wny0Xe97z7nTozzqlZeI3lrCShuST57n9w5Jh+rLD0CDqoro
 hzeezG4z9VvbaO7Pg/i+tdWinsZCKM5sLX9UuGen/3I2ncn7C3/e0mbtsQnFnCRL
 +BhJJanZWFcjxzIa/FhGall01yID/Uz5KIWsa/cOGe0KXDUPkqcBLmWs6/7zBFi8
 /QZL74SZXZrURFwpK/UCEmfJ0xX6reMMI+5Pj4HwAZsj8ZWGsfqp4criWiFsGjds
 XMa5/kM7bfC+fB3yHQIRQiu6z4QwB+t4B2HiECpRsx/Rr7zNu3lFMZsRj78KnWN8
 Hi2AoG8K0VMUSZKJIv/97turXmWKZE3D/+RnsteNIAxIgunjEg5Loq882O4p9jsj
 x4aolzXxtgYIqKDIn+WA8ilmOIF/eXKWp5QI7ciEKrEwSC/deVP734BLaPOJwMlA
 84XOwRy0a0WXdLexdRg6ANgwXzXFU1B/UayWDuNSbVAirM42naWJAjMEAAEKAB0W
 IQRPiKnwz3Cu98LInv4I4XxWErSJPwUCXaC53QAKCRAI4XxWErSJP6BEEADNSCxm
 8/xG5I7CxbBsODfD9wyOqgNNHD5xMsOhf6VILFauScE6M5afdfHCQQ9YSDERGIPu
 AvpeP0+R2Zy+ef08JZJcjn1DAH4FzgoAUOrlb2mtuAK/+fzDYAzgA+93BB3s+/ny
 CzEA60B2Yx2vhRYyZrU+TztCnUhDvbx/rNuCP/SKBG/dCSyRjm6el6C+1tQkyk6t
 LcFlh4Qvn6WcFxICKfWe4vscIoF3WmIj9q8fuoBQh700DEIoOg0bI08WgZctFLtC
 TMtKX6Or3K8G6HT3UJKHOYD2l04iXGxHbs/bbtvvUWnlBxAE2LDxGrJr251HaGgh
 rHHCp+eQZt21zmESnZ1FX8eDeTCEKbFTgEwIpQFOVttsNhZ4brdJDXL7p1aw5PsX
 pSmo/bfhOrhreFijFB7UlnYbPUINCW3ISgjypuKN1GQbA9gmulcK19LB4f+dmvTU
 MQi6Mo7j2ew0J3zc5kP6DNX2AkdAc2Nw8dXPtrWJr/ubjUBZDgM2CrWnTE+QJlk5
 5g/Lx0V4BG6GeYxmsuOEOV3EEH1QvCehgsmpyeJgYVYqq1wtIk1g4CGKtn+fY+7K
 y8TMueCPCXJtLOoH8iWTd//oy58c/IxQQGYmfk+sxUOIrIgvhU/oFIE+77LpkgEx
 E1Zd6Lct0+S6XiadvopE5IeE2CDr5DV9DqEMvA==
 =Oxxo
 -----END PGP SIGNATURE-----

Merge tag '2.0.3' into devel

PyLink 2.0.3

Conflicts:
	RELNOTES.md
	VERSION
	coremods/control.py
	example-conf.yml
	plugins/changehost.py
2019-12-08 11:34:18 -08:00
James Lu
b92f39d79c Update IRCd notes & supported IRCd versions
[skip ci]
2019-11-30 00:10:55 -08:00
James Lu
b9561500fd relay: simplify clientbot forcepart/kick formatting 2019-11-29 23:52:19 -08:00
James Lu
72c57d433a clientbot: add option to always autorejoin channels
Closes #647.
2019-11-29 23:51:57 -08:00
James Lu
7eff7861ed structures: implement _from_iterable() so that set operations work 2019-11-29 23:35:52 -08:00
James Lu
1a89813cd4 unreal: stop sending NETINFO on link
NETINFO isn't strictly necessary for services servers, and not sending it suppresses protocol version/network name mismatch warnings.
2019-11-29 23:03:35 -08:00
James Lu
d73e2fc209 unreal: read user modes from PROTOCTL USERMODES when available
This is sent by UnrealIRCd 4.2.3 and later.
2019-11-17 12:24:54 -08:00
James Lu
8a48d4d8cc inspircd: fix sending ping replies from subservers 2019-11-17 10:48:58 -08:00
James Lu
7bf1a9e08d Add can-manage-bot-channels protocol capability
This allows skipping part/join for service bots on platforms where this is not possible.
2019-11-02 13:55:49 -07:00
James Lu
85922cd376 README: drop webchat link
[skip ci]

(cherry picked from commit 4ceeb1630f5bc45947a9fd215da27e06eed37375)
2019-11-01 08:13:07 -07:00
James Lu
4ceeb1630f README: drop webchat link
[skip ci]
2019-10-28 19:28:07 -07:00
James Lu
41497a8a13 automode: mangle channels to "#chanid" on networks where they're stored as int
This allows the bulk of automode's commands to actually function.
2019-10-14 09:57:47 -07:00
James Lu
2c53ce0682 PyLink 2.0.3 2019-10-11 10:18:43 -07:00
James Lu
d28a9681ac automode: disable on networks where IRC modes aren't supported
Closes #638.
2019-10-10 22:24:44 -07:00
James Lu
297d31dab2 Add has-irc-modes capability (#620) 2019-10-10 22:17:11 -07:00
James Lu
f99be51515 changehost: add enable and enforce as network specific options.
Closes #611.
2019-10-10 21:45:18 -07:00
James Lu
da67d6c42f changehost: port most options to get_service_option(s) (#611, #642) 2019-10-10 21:04:15 -07:00
James Lu
1623462b73 relay: use get_service_options() to combine clientbot styles options (#642) 2019-10-10 19:16:58 -07:00
James Lu
e0d82cdf3d Add get_service_options API to merge together global & local network options
First part of #642.
2019-10-10 18:49:07 -07:00
James Lu
9ec83f3995 Base test for get_service_option() 2019-10-09 20:55:52 -07:00
James Lu
601b811912 test/ptf: reorganize tests into sections 2019-10-09 20:53:58 -07:00
James Lu
72e96156b5 changehost: listen for services account changes
This allows for consistent account based hostmasks for SASL gateways, etc.
2019-10-09 20:32:47 -07:00
James Lu
4095eea3a7 changehost: simplify _changehost() syntax 2019-10-09 20:30:53 -07:00
James Lu
8cf1beb183 test_irc_parsers: fix Python 3.5 support
open() only supports pathlib paths on 3.6 and later.
2019-09-15 16:27:17 -07:00
James Lu
52001ac82d .travis.yml: add 3.7, remove 3.4 tests 2019-09-15 16:24:55 -07:00
James Lu
c8ba6291a6 parse_irc_command: ignore empty IRC lines
I seem to be getting this on my InspIRCd 2 test server?
2019-09-10 19:46:46 -07:00
James Lu
083dc6a58f Rewrite is_server_name() to fail on hostnames with - and _ 2019-09-10 19:31:57 -07:00
James Lu
462fa91622 Add validate-hostname tests from ircdocs/parser-tests 2019-09-10 19:22:53 -07:00
James Lu
b803c23b57 Add in mask-match tests from ircdocs/parser-tests 2019-09-10 19:19:16 -07:00
James Lu
899443d2fe split_hostmask: raise an error on empty nick/ident/host 2019-09-10 19:12:26 -07:00
James Lu
fe4bea2948 Add in userhost-split tests from ircdocs/parser-tests 2019-09-10 19:12:18 -07:00
James Lu
01705f8393 Skip message tag parse tests for now 2019-09-10 19:10:58 -07:00
James Lu
943168df53 parse_message_args: remove extraneous \'s that aren't escaping characters 2019-09-10 18:31:07 -07:00
James Lu
aba198dbd6 parse_args: ignore extra spaces not part of the final multi-word arg 2019-09-10 18:11:07 -07:00
James Lu
188d0f647e ircs2s_common: make parse_message_tags() a classmethod 2019-09-10 18:11:07 -07:00
James Lu
19f7ba38b3 Begin integrating ircdocs/parser-tests 2019-09-10 18:11:07 -07:00
James Lu
c1859b64fa inspircd: fix handling of SVSTOPIC on insp3 2019-08-29 11:16:10 -07:00
James Lu
f9368dd5cc Protocol tests for get_hostmask(), get_friendly_name() 2019-08-26 16:54:27 -07:00
James Lu
2baec4c65a Protocol tests for wrap_modes() 2019-08-26 16:47:49 -07:00
James Lu
ee4997dd72 Tests for join_modes, base case tests for apply_modes / reverse_modes 2019-08-26 16:24:58 -07:00
James Lu
ebce431ba4 reverse_modes: test cycling prefix modes 2019-08-26 16:10:08 -07:00
James Lu
a1f3af9099 reverse_modes: deduplicate reversing modes with arguments 2019-08-26 16:10:08 -07:00
James Lu
d93c071446 reverse_modes: test mode cycling with simple modes and bans 2019-08-26 16:10:08 -07:00
James Lu
9168880204 parse_modes: fix handling of +b-b ban cycles 2019-08-26 16:10:08 -07:00
James Lu
c2b5966739 reverse_modes: ignore unsetting simple modes that didn't exist 2019-08-26 16:10:08 -07:00
James Lu
b685f416f6 reverse_modes: treat mode arguments case insensitively 2019-08-26 16:10:08 -07:00
James Lu
0533827ddf reverse_modes: add basic tests 2019-08-26 16:10:02 -07:00
James Lu
32219ccb78 reverse_modes: return a list and not a set of modes
This ensures that order is kept when the input is a list.
2019-08-26 13:16:52 -07:00
James Lu
808e1d1f5a protocol tests: cleanup 2019-08-26 12:59:57 -07:00
James Lu
304631ebd0 Fixes to clientbot._get_UID() behaviour 2019-08-26 12:17:07 -07:00
James Lu
27eed3334b parse_modes: test combinations of nicks and UIDs in prefix modes 2019-08-23 21:24:00 -07:00
James Lu
c1dbfdab48 classes, clientbot: don't allow _get_UID in parse_modes to create new users 2019-08-23 21:22:28 -07:00
James Lu
da58669de5 parse_modes: case fold parameters to modes 2019-08-23 21:01:55 -07:00
James Lu
6ad34672d3 apply_modes: fix statekeeping with current modes mapping 2019-08-23 00:22:25 -07:00
James Lu
46f081e19b apply_modes: treat modes with arguments case-insensitively 2019-08-23 00:11:21 -07:00
James Lu
cb4d2cc384 Add more mode cycling (+b/-b, etc.) tests 2019-08-23 00:11:21 -07:00
James Lu
3eb90fa65c More rigorous testing of +k/-k parsing 2019-08-23 00:11:21 -07:00
James Lu
fe51f71a6e apply_modes: refactor checks for existing modes 2019-08-23 00:10:30 -07:00
James Lu
087ca0947b inspircd: write InspIRCd 3.x in file header 2019-08-22 22:58:34 -07:00
James Lu
a885b79306 More tests for parse_modes(), apply_modes() 2019-08-22 22:58:24 -07:00
James Lu
575cff297d Channel: remove call to deprecated function name 2019-08-22 22:58:15 -07:00
James Lu
e5493eac87 docs/modelists: regenerate & update channel modes list
- channel-modes: split inspircd column into insp20, insp3 sections - this will let us account for current and future differences between the two
- channel-modes: modularize unreal mode list
2019-08-22 21:04:58 -07:00
James Lu
26bfc06869 unreal: get rid of weird cmode +f workaround 2019-08-22 21:02:30 -07:00
James Lu
d3f2a370da Revert "inspircd: don't allow _ in hosts"
This reverts commit ac8b7babf15e1ef760c8fe2fdf83178aaed09ddf.
2019-08-22 19:02:59 -07:00
James Lu
a8832a5f93 modelists: update extban listing 2019-08-22 19:00:39 -07:00
James Lu
0b8ed2dae9 unreal: declare support for msgbypass and timedban extbans
Closes #557.
2019-08-22 18:48:46 -07:00
James Lu
452a47d4f1 relay: handle acting extbans for +e too
InspIRCd acting extbans and UnrealIRCd ~m are both used in theis context.
2019-08-22 18:40:23 -07:00
James Lu
d57b121600 unreal: work around a potential race when sending kills on join
(cherry picked from commit 1780271dd0077cb19fb0367d1df99827b10362b5)
2019-08-22 17:46:15 -07:00
James Lu
e0a618f317 [SECURITY] permissions: only whitelist the defined login:user for legacy accounts
It's possible for login:user and login:accounts to be used together, although this is discouraged.

(cherry picked from commit 4eb0420378e7b627dbf1f3c90e0e33012f54d4b6)
2019-08-22 17:46:15 -07:00
James Lu
e02ab9f2ff relay: consistency fixes for the hideoper setting
- Don't enforce +H on /oper when the hideoper option is disabled
- Skip relaying -H if the hideoper option is enabled - closes #629

(cherry picked from commit 9a74626d62b18a695f76449369645a42ceffb8cc)
2019-08-22 17:46:15 -07:00
James Lu
2cdcd8e193 clientbot: fix error when MODES is defined in ISUPPORT but given no value
(cherry picked from commit 61ca8dd781aa1bc0458d9c4cbb2e6223f0fa0499)

This fixes connections to e.g. Oragono
2019-08-22 17:46:15 -07:00
James Lu
fae63d77b2 README: mention that ngIRCd's CloakHost and CloakUserToNick are not supported
Cloak tools that enforce hosts on remote users are by nature unsupportable because they cause hostmask desyncs when forwarding Relay users. This in turn makes channel moderation impossible.

[skip ci]

(cherry picked from commit 1a692f55ad6e4a08f0b7eab08c11f6677b1d0ae9)
2019-08-22 17:44:16 -07:00
James Lu
f3569b4fd9 ts6: add support for hiding PyLink servers 2019-08-22 17:39:00 -07:00
James Lu
5d579481aa Base protocol tests for _get_UID, parse_modes 2019-08-18 20:55:10 -07:00
James Lu
6b78b45b20 ngircd: make linking to non-ngIRCd servers a fatal error 2019-08-18 19:51:37 -07:00
James Lu
1a692f55ad README: mention that ngIRCd's CloakHost and CloakUserToNick are not supported
Cloak tools that enforce hosts on remote users are by nature unsupportable because they cause hostmask desyncs when forwarding Relay users. This in turn makes channel moderation impossible.

[skip ci]
2019-08-18 19:50:48 -07:00
James Lu
4a8c96c883 And now, a test fixture for protocols/ 2019-08-18 16:36:02 -07:00
James Lu
07d8c8828a relay: fix incorrect variable when logging invalid channels in LINK
Where on earth is c even defined?
2019-08-04 11:41:28 -07:00
James Lu
80188c3673 Sort imports via isort 2019-07-14 15:12:29 -07:00
James Lu
19d794a6f5 relay_clientbot: refactor 'rpm' to handle duplicate nicks & nicks containing spaces
Closes #650.
2019-07-14 13:21:47 -07:00
James Lu
6ac2daebfa commands: improvements to the 'showuser' command
- Indent output lines for each specific user
- Skip showing Home server / Nick TS line if neither is available
- Handle nicks with spaces in them
- Show user modes after basic details
2019-07-14 13:21:47 -07:00
James Lu
8e85fa935d PyLink 2.1-alpha2 2019-07-14 12:29:35 -07:00
James Lu
350ba5f89c Changelog draft for 2.1-alpha2
[skip ci]
2019-07-13 02:16:29 -07:00
James Lu
edd27eea41 relay: format KILL sources when relaying local kills
Kill reason formatting was changed in #520.
2019-07-01 14:18:35 -07:00
James Lu
bcdd26926d IRCNetwork: use bytearray for buffers 2019-07-01 14:18:35 -07:00
James Lu
4bd334e2b8 antispam: read nicks from userdata when handling QUITs (#617)
get_hostmask() doesn't work on someone who has already quit.
2019-07-01 14:18:31 -07:00
James Lu
e3e0eac747 classes: revise docstrings
Mostly, mention which methods are IRC specific and which should be overridden to support other platforms.
2019-07-01 14:17:26 -07:00
James Lu
c7fd037879 Revise handling of KILL and QUIT hooks
- Both of these now always contain a non-empty userdata argument.
- If we receive both a KILL and a QUIT for any client, only the one received first will be sent as a hook.
- Also, adjust _remove_client() to return the data of the user that was removed.
2019-07-01 13:36:53 -07:00
James Lu
35b38dfb05 antispam: add part / quit message filtering for plugins like Relay
Closes #617.
2019-06-27 13:07:04 -07:00
James Lu
b6cf09ae52 example-conf: fixes to antispam examples
- It should be servers::<server name>::antispam_textfilter_globs, not servers::<server name>::antispam_textfilters_globs
- Matches (via utils.match_text) are Unicode case-insensitive as of PyLink 2.1
2019-06-27 13:07:04 -07:00
James Lu
93f608a504 writing-plugins: mention that editing hook payloads is allowed
Part of #452.
2019-06-27 13:07:02 -07:00
James Lu
9ad2b03833 permissions-reference: briefly mention (pi)eval, iexec commands 2019-06-26 13:54:32 -07:00
James Lu
19c7dce931 commands: add a 'shownet' command
Basic info available to everyone include network name, protocol module, and encoding.

For those with the commands.shownet.extended permission, this also allows looking up disconnected networks defined in the config, and shows configured IP:port, PyLink hostname, SID, and SID range.

Closes #578.
2019-06-26 13:54:32 -07:00
James Lu
37822fda42 inspircd: implement spawn_server() on InspIRCd 3 (#644) 2019-06-26 13:54:25 -07:00
James Lu
4eb0420378 permissions: only whitelist the defined login:user, not all accounts
It's possible for login:user and login:accounts to be used together, although this is discouraged.
2019-06-26 13:18:32 -07:00
James Lu
9a74626d62 relay: consistency fixes for the hideoper setting
- Don't enforce +H on /oper when the hideoper option is disabled
- Skip relaying -H if the hideoper option is enabled - closes #629
2019-06-26 13:18:32 -07:00
James Lu
c1158fd33a exttargets: convert $account target to str before matching
Closes #639.
2019-06-26 13:18:32 -07:00
James Lu
caa94f983f relay: mangle <( to [ and >) to ] for better displays 2019-06-24 15:08:13 -07:00
James Lu
729abbd6bf Update dependency definitions
- Make cachetools a hard dependency - closes #648
- Mark unidecode as an optional dependency for Relay - #561
2019-06-24 15:03:51 -07:00
James Lu
61ca8dd781 clientbot: fix error when MODES is defined in ISUPPORT but given no value 2019-06-23 20:13:04 -07:00
James Lu
df468064d6 clientbot: rework to support freeform nicks
By overriding _get_UID() to only return non-virtual clients, we can stop worrying about nick conflicts and remove relay nick tags from Clientbot.
2019-06-23 19:46:23 -07:00
James Lu
c56713887e classes: use _get_UID in parse_modes() to allow overriding nick lookup behaviour 2019-06-23 19:45:29 -07:00
James Lu
798fc7b0bf match_host: stop implicitly coersing target nicks to UIDs 2019-06-23 19:45:08 -07:00
James Lu
1852ff5774 relay: passthrough nicks in normalize_nick() on server supporting freeform-nicks 2019-06-23 17:48:15 -07:00
James Lu
30f7a77d18 Revert most of 1c0ea24acdd2d1fbba36073cf62907bf0e1a84c3
1c0ea24acdd2d1fbba36073cf62907bf0e1a84c3 "relay_clientbot: normalize sender names to the senders' home networks"

In the future we hope to remove nick restrictions in Clientbot entirely, and just use freeform nicks for virtual users.
2019-06-23 17:48:08 -07:00
James Lu
957697d275 networks: don't allow disconnecting servers marked virtual-server 2019-06-23 17:43:12 -07:00
James Lu
c5b94cdf21 control: ignore virtual servers in rehash 2019-06-23 17:39:15 -07:00
James Lu
f2b6de8889 Declare new protocol capabilities: virtual-server, freeform-nicks 2019-06-23 17:29:43 -07:00
James Lu
ed4404bf4b relay: fake revert mode changes we couldn't bounce (#23)
This allows services to revert mode changes CLAIM was not happy with, instead of causing another mode war during this process.
2019-06-21 15:38:49 -07:00
James Lu
dcab011673 relay: pretend mode reverts on SJOIN always succeed (#23)
This prevents remote services from bypassing CLAIM, since the end result of a mode war is that they remained opped.
2019-06-21 15:28:52 -07:00
James Lu
94cd1d8f22 relay: implement kick/mode/topic war prevention (#23)
This adds cachetools as a dependency for Relay.
2019-06-21 14:57:43 -07:00
James Lu
042d11d7ba relay: remove extraneous variable 2019-06-21 14:03:21 -07:00
James Lu
a6205e1ebc README, setup.py: drop ircmatch dependency (#636) 2019-06-21 12:51:12 -07:00
James Lu
74566c3aab antispam, changehost: remove references to ircmatch (#636) 2019-06-21 12:51:12 -07:00
James Lu
9f31a0a587 classes: drop use of ircmatch (#636) 2019-06-21 12:51:12 -07:00
James Lu
b7d93fe86a utils: add match_text(), general glob matching function
In preparation for ircmatch removal (#636)
2019-06-21 12:51:12 -07:00
James Lu
46d1738f66 example-conf: mention PyLink 2.0.3 instead of 2.1 for CryptContext changes 2019-06-16 11:39:07 -07:00
James Lu
6054476900 More secure password hashing defaults
(cherry picked from commit eba5d912999cd3d5346eb12949f592fa755ebdc5)

  Default hash method to pbkdf2-sha256 & allow customizing CryptContext options

  This introduces a new login::cryptcontext_settings config option.

  Closes #645.
2019-06-16 11:36:34 -07:00
James Lu
c7e4c05cbd changehost: only send a host change if new host != original
(cherry picked from commit 13be40e08bf4ca842b3908ec9b15ee9008ef95b9)
2019-06-16 11:36:34 -07:00
James Lu
e25a5df4db ClientbotBaseProtocol: disallow part() from the main pseudoclient by default 2019-06-16 11:24:45 -07:00
James Lu
0836845ff9 Merge relay showchan/showuser info into commands.py
This makes error handling easier and is needed to support duplicate nicks anyways.
2019-06-16 11:22:36 -07:00
James Lu
dfc4e4954a commands: remove explicit cutoff of 20 users/line in showchan
irc.reply() in PyLink 2.0+ handles line wrapping automatically.
2019-06-16 11:20:26 -07:00
James Lu
fe95a4a571 commands: rework showuser to better handle duplicate nicks
This is freely allowed on Discord, for example.
2019-06-16 11:19:19 -07:00
James Lu
fc4a16eda1 bots, opercmds: handle cases where target nick is disambiguous 2019-06-16 11:04:07 -07:00
James Lu
242267a4a2 classes: revise some function descriptions 2019-06-16 10:31:23 -07:00
James Lu
011d70e816 classes: make nick_to_uid more versatile against duplicate nicks
This adds a couple of new options:
- multi: return all matches for nick instead of just the last result. (Return an empty list if no matches)
- filterfunc: if specified, filter matched users by the given function first."""
2019-06-16 10:30:46 -07:00
James Lu
3b07f8ab2b fantasy: accept "@servicebot" as fantasy trigger prefix
For platforms like Discord where this form of address is the norm.

Closes https://github.com/PyLink/pylink-discord/issues/17
2019-06-15 23:56:55 -07:00
James Lu
3d039b78e2 relay: use [] as altchars for Base64 fallback
This ensures that mangled nicks can be reversed more easily (by removing leading _'s and replacing - with =)
2019-06-15 23:48:46 -07:00
James Lu
886a98a396 Drop official support for Python 3.4
Our lowest support target is now Python 3.5 (Debian 9, Ubuntu 16.04)
2019-06-07 14:34:04 -07:00
James Lu
c62ec4fde0 setup.py: move from expiringdict to cachetools (#445) 2019-06-07 14:31:04 -07:00
James Lu
3d5e7cd1c1 pylink-mkpasswd: use hash() instead of encrypt()
- This function was renamed in Passlib 1.7, deprecating the old name.

- Depend accordingly on Passlib >= 1.7.0
2019-06-07 14:25:22 -07:00
James Lu
eba5d91299 Default hash method to pbkdf2-sha256 & allow customizing CryptContext options
This introduces a new login::cryptcontext_settings config option.

Closes #645.
2019-06-07 14:13:39 -07:00
James Lu
8b298df362 example-conf: various wording tweaks (SSL -> TLS, etc.) 2019-06-06 23:57:01 -07:00
James Lu
42a2061783 Merge branch 'wip/insp3' into devel
protocols/inspircd: add native support for InspIRCd 3.x

Closes #644.
2019-06-06 23:54:57 -07:00
James Lu
dd58dcf377 inspircd: show a note when linking to insp3 servers using insp20 compat 2019-06-06 23:50:08 -07:00
James Lu
04d36e93a1 inspircd: document target_version variable 2019-06-06 23:49:27 -07:00
James Lu
2b04050bf5 inspircd: minor cleanup 2019-05-31 19:01:25 -07:00
James Lu
8d2ae6af50 example-conf: rewrap comments for the first server example 2019-05-31 18:44:07 -07:00
James Lu
762b47120d inspircd: support insp3 INVITE 2019-05-31 18:28:28 -07:00
James Lu
722881bc33 inspircd: fix incorrect lstrip() usage when mangling mode names 2019-05-31 18:13:21 -07:00
James Lu
917543dd12 inspircd: burst shorter version strings on insp3
These get shown in /map, for example.
2019-05-31 18:13:15 -07:00
James Lu
b260a28c8f inspircd: handle insp3 SERVER command 2019-05-31 18:12:06 -07:00
James Lu
12784a4b5b inspircd: handle insp3 IJOIN with TS & flags 2019-05-31 17:46:36 -07:00
James Lu
ea753774fd inspircd: check for local protocol version instead of the remote's
We should be speaking the insp20 protocol even to insp3 servers if configured to do so, not some broken hybrid of the two.
OPERTYPE handling remains an exception.
2019-05-31 17:35:49 -07:00
James Lu
1c0ea24acd relay_clientbot: normalize sender names to the senders' home networks
This should work for most messages, except NICK changes and MODE targets.
2019-05-18 19:44:45 -07:00
James Lu
50e9d2d959 example-conf: load servprotect by default 2019-05-13 17:08:11 +08:00
James Lu
26fa5d38a2 README: update list of optional dependencies (#445) 2019-05-13 17:07:50 +08:00
James Lu
ec379a6e81 servprotect: migrate to cachetools (but leave expiringdict as fallback)
Closes #445.
2019-05-13 16:59:57 +08:00
James Lu
c43d13ef61 inspircd: FTOPIC handling for InspIRCd 3 2019-05-02 18:05:54 -07:00
James Lu
66485ec6a2 inspircd: send SINFO instead of VERSION on 1205 2019-05-02 17:42:45 -07:00
James Lu
3d69b7f4e8 ircs2s_common: fix sending the wrong target in PING 2019-05-02 17:36:42 -07:00
James Lu
ad4cb9561c inspircd: add FJOIN, IJOIN, KICK handling for InspIRCd 3
IJOIN is new. Strip membership IDs from incoming FJOIN and KICK for now.
2019-05-02 17:36:42 -07:00
James Lu
08386a8ef7 inspircd: get rid of MIN_PROTO_VER
We should always check that our remote has a protocol version >= our own.

i.e. support links using PyLink 1202 <-> InspIRCd 1205, PyLink 1205 <-> InspIRCd 1205, but NOT PyLink 1205 <-> InspIRCd 1202
2019-05-02 17:36:42 -07:00
James Lu
db6d5d6d05 inspircd: actually read our DEFAULT_IRCD setting 2019-05-02 17:36:38 -07:00
James Lu
42e1eda51a inspircd: use NUM to send numerics on insp3 2019-05-02 17:06:04 -07:00
James Lu
4276607ee4 inspircd: rework modelist negotiation to support InspIRCd 3.0 2019-05-02 16:52:29 -07:00
James Lu
0fe8a8d51a inspircd: move protocol version check into CAPAB START handler
InspIRCd 3.0 stopped sending the protocol version in CAPAB CAPABILITIES, but it's always available in CAPAB START.
2019-05-02 16:11:53 -07:00
James Lu
6f617cb068 inspircd: allow choosing the target IRCd via "target_version" option 2019-05-02 16:11:21 -07:00
James Lu
44a364df98 Move message tags code from clientbot to ircs2s_common 2019-05-02 15:54:34 -07:00
James Lu
e3d72c43a4 inspircd: move proto_ver constants into the class definition 2019-05-02 15:47:03 -07:00
James Lu
d082495297 launcher: drop experimental tag from -d/--daemonize 2019-05-02 15:41:27 -07:00
James Lu
0273faf933 PyLink 2.1-alpha1 2019-05-02 15:38:08 -07:00
James Lu
81bf6480df clientbot: avoid adding empty nicks to the state
It looks like names replies may end with an extra space, which should not be considered as part of the nick list..

(cherry picked from commit f90b0c857745090ff330fe70421966e11d65a9d6)
2019-04-29 12:19:16 -07:00
James Lu
7e088dfacb clientbot: log the entire args list when splitting /names reply fails
(cherry picked from commit a8bb5f66e53c9d3d3f7872587b2c752ff8a1b16d)
2019-04-29 12:19:16 -07:00
James Lu
f90b0c8577 clientbot: avoid adding empty nicks to the state
It looks like names replies may end with an extra space, which should not be considered as part of the nick list..
2019-04-29 12:04:11 -07:00
James Lu
a8bb5f66e5 clientbot: log the entire args list when splitting /names reply fails 2019-04-29 11:58:18 -07:00
James Lu
ad9a51fc33 commands.showuser: properly handle numeric-type UIDs and channels 2019-04-09 19:10:20 -07:00
James Lu
13be40e08b changehost: only send a host change if new host != original 2019-04-09 19:01:53 -07:00
James Lu
739c87ef50 clientbot: only split /names replies by spaces
This fixes issues with colored hostnames because \x1f is treated as a whitespace char by str.split().

Closes #641.

(cherry picked from commit 905679963331764db17f0bda2dfd7b299d8d2a20)
2019-04-08 22:36:28 -07:00
James Lu
9056799633 clientbot: only split /names replies by spaces
This fixes issues with colored hostnames because \x1f is treated as a whitespace char by str.split().

Closes #641.
2019-04-08 22:35:37 -07:00
James Lu
5ca57cb3c1 Decrease default log file size from 50 MiB to 20 MiB 2019-04-06 02:20:28 -07:00
James Lu
41cbd455d6 relay: only check _invisible flag on actual users 2019-04-02 21:22:40 -07:00
James Lu
8f10af9942 PyLink 2.0.2 2019-03-31 01:33:46 -07:00
Ian Carpenter
28166ed4ee Only write the pid file when --no-pid isn't passed (#633)
Prevents --no-pid from breaking

(cherry picked from commit 30387c9ed5e02205a637f34fa9fc0a7fdcb3f98c)
2019-03-31 01:25:23 -07:00
James Lu
71353a29c2 relay: add support for hiding users marked invisible or offline
First part of https://github.com/PyLink/pylink-discord/issues/19
2019-03-28 20:14:58 -07:00
James Lu
0ffbaa8e5e control: suppress NotImplementedError when disconnecting networks on shutdown 2019-03-28 20:14:28 -07:00
James Lu
2c028e2762 classes: remove channels, modes from User.get_fields()
These don't really make sense to be formatted as a string.
2019-03-28 20:14:04 -07:00
James Lu
63d63c0137 relay: allow trailing .'s in subserver names 2019-03-20 21:14:12 -07:00
James Lu
88f45fb1d5 relay: whitelist ~ in idents 2019-03-20 21:08:55 -07:00
James Lu
c9176a06fc relay: check for nicks starting with numbers or - after removing Unicode 2019-03-20 21:08:21 -07:00
James Lu
1780271dd0 unreal: work around a potential race when sending kills on join 2019-03-01 23:34:41 -08:00
James Lu
190e51211f log: use pylinkirc as logger name 2019-02-20 13:22:26 -08:00
James Lu
4f17a7986b PyLinkNC: don't overwrite sid, serverdata if they're already set 2019-02-20 13:22:26 -08:00
Ian Carpenter
24f85acfb8 Explicitly specify UTF-8 for the log files (#634)
For some reason, Windows defaults to cp1252, which breaks logging when some utf-8 characters get logged
2019-02-18 17:14:30 -08:00
Ian Carpenter
30387c9ed5 Only write the pid file when --no-pid isn't passed (#633)
Prevents --no-pid from breaking
2019-02-18 17:14:04 -08:00
James Lu
ba17821af4 Declare visible-state-only by default in ClientbotBaseProtocol 2019-02-16 16:43:05 -08:00
James Lu
9d459fab92 relay: fixes to support IDs as channel name
- Don't enforce local channel validity rules on the remote channel name
- Normalize channels to str before looking them up in the DB
2019-02-16 16:35:47 -08:00
James Lu
55360dd0b2 classes: allow Server name to be a non-string type 2019-02-16 16:32:42 -08:00
James Lu
96815a0a32 relay: sanitize idents before mirroring them to IRC 2019-02-12 15:55:14 -08:00
James Lu
ac8b7babf1 inspircd: don't allow _ in hosts
CHGHOST on InspIRCd 2.0 does not see this as valid.
2019-02-12 15:55:10 -08:00
James Lu
a9f59307c9 relay: use base64 as fallback if unidecode returns an empty string for nick
This is the case for e.g. nicks that are only an emoji.
2019-02-12 14:17:53 -08:00
James Lu
11b65ee809 relay: rework nick normalization with optional unidecode support
This will attempt to translate UTF-8 nicks to ASCII ones instead of doing the ugly '||||' replace.
Also, the fallback character for disallowed nick characters is now "-" instead of "|".

TODO: document relay::use_unidecode

Closes #561.
2019-02-12 00:58:20 -08:00
James Lu
cfbadb4539 Move _squit, _get_SID, _get_UID wrappers into PyLinkNCWUtils
ClientbotBaseProtocol requires these for the squit wrapper to work.
2019-02-12 00:38:37 -08:00
James Lu
11e7589304 relay_clientbot: fix previous commit 2019-02-10 14:35:34 -08:00
James Lu
76f534ce84 relay_clienbot: fix rpm when UIDs are ints 2019-02-10 14:22:18 -08:00
James Lu
873283e61e clientbot: properly bounce kicks on networks not implementing them 2019-02-10 13:01:31 -08:00
James Lu
a9b8bfe94d classes: allow _expandPUID() to work when UIDs are ints 2019-02-10 13:01:11 -08:00
James Lu
5731a301ce classes: fix incorrect wrap_messages() stub 2019-02-10 13:00:53 -08:00
James Lu
d089b8d40e clientbot: split wrapper stuff into a ClientbotBaseProtocol class
Closes #632.
2019-02-10 12:40:02 -08:00
James Lu
5e1da09901 control: don't remove network objects with virtual_parent on rehash 2019-02-08 15:10:21 -08:00
James Lu
6e7c58ee36 PyLinkNCWithUtils: don't assume mode args are strings 2019-02-07 14:55:27 -08:00
James Lu
61c8677802 classes, relay_clientbot: more type safety for protocols/discord 2019-02-07 13:50:32 -08:00
James Lu
23cb7c173a stats: hide login blocks not relevant to this network 2019-02-07 13:50:32 -08:00
James Lu
52f588c920 Track affected servers in SQUIT hooks 2018-12-27 12:09:40 -08:00
James Lu
82ce9aac6c writing-plugins.md: mention that the default hook priority is 100 2018-12-27 12:08:36 -08:00
James Lu
cb7708e095 Bump VERSION to 2.1-dev 2018-12-27 12:08:17 -08:00
James Lu
852c257e88 relay: remove use of re-entrant locks
This shouldn't be necessary anymore.
2018-12-27 11:58:39 -08:00
James Lu
bf9eb8d4ea relay: fix incorrect in-place changes of modedelta modes
This caused the database to be filled with extraneous "-modename" entries when removing modes from the previous modedelta.
2018-11-30 10:21:51 -08:00
James Lu
61d559926f Bump VERSION to 2.0.2-dev 2018-11-10 23:22:07 -08:00
James Lu
d01a9fe9b4 antispam: more lookalike chars for o, \, # 2018-11-10 23:21:54 -08:00
James Lu
aa5412712a antispam: more lookalike unicode chars
Courtesy of @nathan0
2018-11-10 10:20:41 -08:00
James Lu
32e9cc689e antispam: filter away Unicode lookalike characters when processing
Based off 56b48e4e51
2018-11-07 16:16:46 -08:00
James Lu
6462ae3a3c example-conf: minor rewording for show_unknown_commands
[skip ci]
2018-10-28 21:13:38 -07:00
James Lu
77febfe69f Allow disabling dynamic channels via a new "join_empty_channels" option 2018-10-27 18:48:12 -07:00
James Lu
94a345423e classes: allow callers to override the make_channel_ban() ban style 2018-10-20 12:34:11 -07:00
James Lu
eb231a2aad classes: always raise an error if make_channel_ban creates something invalid 2018-10-20 12:33:42 -07:00
James Lu
31a65697a3 example-conf: document the ban_style option 2018-10-20 12:30:40 -07:00
James Lu
6ceaabe092 classes: use get_fields() in make_channel_ban() for more reliable substitutions 2018-10-20 12:30:09 -07:00
James Lu
5a482118b8 example-conf: describe more thoroughly how antispam handles unsupported punishments 2018-10-20 12:28:41 -07:00
James Lu
5c4fba653f IRCNetwork: disable throttling by default
On large networks, this seems to slows down relay bursts to the point they're no longer usable.
2018-10-10 22:49:10 -07:00
James Lu
dac8410b63 relay: shortcut if the remote network is not ready 2018-10-10 22:49:10 -07:00
James Lu
12d1412cba PyLinkNCWUtils: stop logging the entirety of prefixmodes
This creates a lot of spam on larger channels.
2018-10-10 22:49:04 -07:00
James Lu
60b7894cd6 IRCNetwork: try to abort immediately if the send queue is full 2018-10-08 16:26:29 -07:00
James Lu
0b3793380b relay: remove TCONDITION_TIMEOUT 2018-10-08 14:59:01 -07:00
James Lu
1ee93d2cc4 relay: remove world.started check, this shouldn't be needed anymore 2018-10-08 14:44:37 -07:00
James Lu
767ff15200 relay: add an explicit forcetag command
We used to be able to just /kill to forcetag, but with PyLink 2.0 kills actually get relayed.
2018-10-08 12:42:38 -07:00
James Lu
57faa1443a relay: rename nick_collide() to forcetag_nick() 2018-10-08 12:41:52 -07:00
James Lu
20b3a61cd6 relay: simplify is_relay_client() 2018-10-08 12:12:09 -07:00
James Lu
7fb4c8da04 automode: manage persistent channels on the right network 2018-10-06 23:46:49 -07:00
James Lu
724a71e1b2 PyLink 2.0.1 2018-10-06 00:39:12 -07:00
James Lu
97603fe169 Changelog for PyLink 2.0.1
[skip ci]
2018-10-06 00:35:30 -07:00
James Lu
4d39ad1c84 unreal: bump protocol version to 4200
Corresponding to UnrealIRCd 4.2.0.
2018-10-05 23:57:20 -07:00
James Lu
a3e18081a6 relay: don't relay as text modes being set on netburst (#627) 2018-09-21 21:53:34 -07:00
James Lu
2f4476eb0c unreal: remove invalid comparison
(Regression from commit fac6fe506f9c58c3d96b0f6bea67a04da4a64632)
2018-09-21 21:40:56 -07:00
James Lu
667fa610ec unreal: remove duplicate conversion 2018-09-16 11:29:25 -07:00
James Lu
e929fda293 unreal: Bump protocol version to 4019 (4.0.19-rc1) 2018-09-12 17:35:30 -07:00
James Lu
d5fd7b03f5 unreal: enable slash-in-hosts, it seems to work fine 2018-09-12 17:35:25 -07:00
James Lu
81170e8062 Try to fix Travis deployments
- Use unittest discover instead of 'cd' to run tests
- Disable cleanup before deploy (as our __init__.py is generated at build time)
2018-08-31 10:09:22 -07:00
James Lu
fac6fe506f unreal: use SJOIN in join() to work around weird TS corrupting bugs
This seems to be what Unreal itself does anyways.
2018-08-25 18:50:51 -04:00
James Lu
119873a9ed RELNOTES: fix some mistakes in the 2.0.0 changelog
[skip ci]
2018-08-24 23:46:52 -07:00
James Lu
298913200c clientbot: properly handle cases when the bot is told to kick itself
- Treat kicks to the PyLink client as a real user kick instead of a virtual one
- Forward on kick hooks if the PyLink client is kick target - this allows things like autorejoin to work

Closes #377.
2018-08-25 01:14:26 -04:00
James Lu
79b3387bcb Revert "docs/t: warn in main articles that specifications in master may be outdated"
This reverts commit 58b717a2a0178f283981b04eeb18bb1e34132e85.
2018-08-23 21:31:34 -07:00
James Lu
5838d88404 networks: reload shared modules used by protocol modules too 2018-08-23 02:57:03 -04:00
James Lu
8a096e537c PyLinkNCWUtils: add "ignore_ts_errors" server option to suppress bogus TS warnings 2018-08-23 02:52:54 -04:00
James Lu
dda8a2a081 launcher: rename has_pid variable to pid_exists
This name is a lot more descriptive.
2018-08-19 17:00:14 -07:00
James Lu
874dfaf3f1 launcher: fix PID files not being read if psutil isn't installed 2018-08-19 16:57:57 -07:00
James Lu
49badd1665 updateTS: silently ignore messages with ts = 0
Closes #625.
2018-08-19 19:41:29 -04:00
James Lu
6a2a6a769f docs/adv-relay-config: resync default message formats & add missing line for MODE
[skip ci]
2018-08-19 15:57:21 -07:00
James Lu
e95b11d329 PyLink 2.0.0 2018-07-31 15:04:25 -07:00
James Lu
51f441085e .travis.yml refresh
- Update to xenial
- Remove unused pandoc dependency
- Run our (currently very minimal) test suite
2018-07-28 11:01:01 -07:00
James Lu
c91b5f74ea setup.py: work around installation error on Python 3.4 2018-07-28 10:53:49 -07:00
James Lu
9cf507183d clientbot: filter PART hooks for only parts we didn't initialize
This fixes Relay being confused by its own "Relay plugin unloaded" /part's, and makes Clientbot's behaviour in this regard consistent with other protocols.
2018-07-18 19:14:47 -07:00
James Lu
12f6bb5e18 relay: don't relay kill->kick when remotechan is None 2018-07-18 18:45:49 -07:00
James Lu
e7b0458091 _state_cleanup_core: don't delete internal clients, period 2018-07-18 18:45:34 -07:00
James Lu
ed50202cf2 Release PyLink 2.0-rc1 2018-07-18 17:46:06 -07:00
James Lu
9273dd459b README: removing PPA support due to lack of demand
[skip ci]
2018-07-14 21:29:52 -07:00
James Lu
9a5e67412e README: minor rewording, fix markdown syntax
[skip ci]
2018-07-14 21:17:11 -07:00
James Lu
f1ce8351b9 Quick refresh of exttarget docs
- Mention that the $pylinkirc: prefix is implied as of 2.0
- Link to the exttargets reference in example-conf
- docs/exttargets: wording tweaks for the lead section
2018-07-14 21:09:16 -07:00
James Lu
d844ff5186 example-conf: remove references to my IRC nick & the old repository address 2018-07-14 21:09:16 -07:00
James Lu
44aa9af235 Get rid of example-permissions.yml, it's been a long-standing source of confusion 2018-07-14 21:09:16 -07:00
James Lu
6b65ab5f88
README: remove reference to deprecated relay_no_ips option
[skip ci]
2018-07-14 20:53:28 -07:00
James Lu
2246aea13c relay-quickstart: clarify the problem of duplicate network links some more
[skip ci]
2018-07-14 15:11:15 -07:00
James Lu
584b7e3712 RELNOTES: first pass at summarizing all of 2.0's changes (compared to 1.3.x)
[skip ci]
2018-07-14 15:09:11 -07:00
James Lu
bf1f8210bd relay-quickstart: also list ChanFix in incompatible services
[skip ci]
2018-07-14 15:08:55 -07:00
James Lu
a7196d7b79 example-conf: remove ALPHA tag from antispam 2018-07-11 22:56:47 -07:00
James Lu
16ac91a718 Merge the long-awaited 2.0 branch into master
Merge branch 'devel'

Conflicts:
	RELNOTES.md
	VERSION
	classes.py
	conf.py
	coremods/control.py
	coremods/corecommands.py
	coremods/service_support.py
	docs/advanced-relay-config.md
	docs/faq.md
	example-conf.yml
	launcher.py
	plugins/global.py
	plugins/relay.py
	plugins/relay_clientbot.py
	protocols/p10.py
	utils.py
2018-07-11 22:45:52 -07:00
James Lu
d356b53425 docs/t/hooks-reference,pmodule-spec: bump doc version to 2.0.0
[skip ci]
2018-07-11 22:40:29 -07:00
James Lu
0199daec76 RELNOTES: first draft of a 2.0-rc1 changelog
[skip ci]
2018-07-11 22:40:20 -07:00
James Lu
a1783ed2be bots: "bots.joinclient" should be "bots.join" (matching the command name) 2018-07-11 22:16:05 -07:00
James Lu
b5884d4cb3 docs/permissions-reference: s/Allows access to /Grants access to /g
This wording is really just clearer.

[skip ci]
2018-07-11 21:59:52 -07:00
James Lu
0eb0c49cb1 docs/permissions-reference: resort most sections alphabetically
[skip ci]
2018-07-11 21:59:10 -07:00
James Lu
04b17c30e3 docs/permissions-reference: resort Relay permissions by default assignment
[skip ci]
2018-07-11 21:56:39 -07:00
James Lu
f9611ef6bc relay: grant CHANDESC permissions to opers if allow_free_oper_links is true 2018-07-11 21:56:29 -07:00
James Lu
d8c1511b28 docs/permissions-reference: refresh for 2.0
Closes #501.

[skip ci]
2018-07-11 21:38:47 -07:00
James Lu
a5b77c18dd docs/t/writing-plugins: document hook priorities with some examples
Closes #595.

[skip ci]
2018-07-11 21:23:03 -07:00
James Lu
3208782225 docs: readd pylink-opers.md as a redirect to relay-quickstart.md
[skip ci]
2018-07-11 18:59:16 -07:00
James Lu
5b321f9f6f relay-quickstart: explicitly write PyLink 2.0 instead of "2.0"
[skip ci]
2018-07-10 19:00:51 -07:00
James Lu
d3fc95953e relay-quickstart: fix some typos / unclear wording
[skip ci]
2018-07-10 18:59:31 -07:00
James Lu
d310abeec3 faq, relay-quickstart: consistently capitalize Relay as a proper noun
[skip ci]
2018-07-10 18:55:33 -07:00
James Lu
66125530ef faq: discuss how relay handles kills, modes, and server bans (G/K/ZLINE)
Closes #619.

[skip ci]
2018-07-10 18:53:11 -07:00
James Lu
1b26c17d81 docs/channel-modes: add missing entry for auditorium (inspircd +u)
[skip ci]
2018-07-10 18:53:11 -07:00
James Lu
aacb65ab9e relay-quickstart: rewrap, fix typo (at the latest -> at a minimum)
[skip ci]
2018-07-10 18:53:11 -07:00
James Lu
71a24b8b9f relay: remove noctcp, regdeaf, stripcolor from whitelisted umodes
Filter-type umodes don't work properly with relay yet.
2018-07-10 18:09:24 -07:00
James Lu
5ffc629bce plugins/example: update word wrap note
As of PyLink 2.0, long paragraphs are automatically word-wrapped by irc.reply().
2018-07-10 18:04:25 -07:00
James Lu
bba235bba2 Update GitHub repository address 2018-07-08 12:54:10 -07:00
James Lu
c1f37c2236 relay: don't allow servers to forward KILLs
Closes #621.
2018-07-08 12:49:30 -07:00
James Lu
86b93ea969 docs: rewrite the Relay Quick Start Guide (#619)
- Rename pylink-opers.md -> relay-quickstart.md to better reflect its contents.
- Add a section regarding services compatibility and which features _not_ to use with Relay
- Document LINKACL whitelists, CHANDESC, CLAIM/LINKACL options introduced in 2.0
- Document reworked KILL handling in 2.0 and why K/G/ZLINE support hasn't been implemented
- Explain Relay concepts in more depth for people not familiar with Janus
2018-07-08 12:30:57 -07:00
James Lu
5a9b870e00 services-api: less awkward wording
[skip ci]
2018-07-02 00:59:29 -07:00
James Lu
a520496f81 writing-plugins: fix markdown syntax errors
[ci skip]
2018-07-02 00:57:41 -07:00
James Lu
86bfda7281 services-api: various wording and typo fixes
[ci skip]
2018-07-02 00:57:36 -07:00
James Lu
a74fe9bf08 writing-plugins: refer to the services API guide for utils.add_cmd features 2018-07-02 00:47:30 -07:00
James Lu
b2e85fa385 services-api: mention command alias support in 2.0-alpha1+ 2018-07-02 00:45:11 -07:00
James Lu
75b0ae6054 example-conf: detail explicitly how "spawn_services: false" affects a plugin's behavior 2018-07-02 00:35:55 -07:00
James Lu
0c55569c1f utils: clarify ServiceBot.(join|part) docstrings 2018-07-02 00:35:40 -07:00
James Lu
0ccaac595b services-api: refresh + expand for 2.0 and the persistent channel rework
Closes #603.

Other changes:
- Move the "Removing services" section to earlier in the doc - it is important!
- Consistently use 'strings' instead of "strings" in example code
- Mention briefly how "spawn_service:" false affects plugins
- Mention the "dynamic" channel handling added in 2.0-alpha3
2018-07-02 00:35:31 -07:00
James Lu
348dc7348c services-api: use snake case function names for (un)register_service()
[skip ci]
2018-07-01 23:36:14 -07:00
James Lu
d015e1e41a PyLink 2.0-beta1 2018-06-27 11:18:43 -07:00
James Lu
8362e5f234 RELNOTES: fix typo, elaborate a bit more on ipshare pools
[skip ci]
2018-06-26 23:37:22 -07:00
James Lu
f90ec284a4 pmodule-spec: document protocol capabilities
Closes #436.
2018-06-26 22:47:39 -07:00
James Lu
bdd568f75b pmodule-spec: consistently refer to protocol module attrs as self.<whatever> instead of irc.<whatever>
[skip ci]
2018-06-26 22:23:35 -07:00
James Lu
a548ae0714 pmodule-spec: reflow, mention self.connected in Special variables
[skip ci]
2018-06-26 22:20:28 -07:00
James Lu
763ffcf903 pmodule-spec: various wording tweaks
[skip ci]
2018-06-26 22:14:38 -07:00
James Lu
c48846727e pmodule-spec: fix link to protocol-modules.svg
[skip ci]
2018-06-26 22:02:09 -07:00
James Lu
420f523dfd writing-plugins: briefly mention log, world, irc.connected, and useful builtin modules
Closes #522.
2018-06-26 21:58:17 -07:00
James Lu
e340f6e9a2 writing-plugins.md: rewrite for clarity for conciseness
Also document the introduction of hook handler return values and irc.error()

[skip ci]
2018-06-26 21:36:17 -07:00
James Lu
e037b927f8 adv-relay-conf: fix reference to wrong relay version
$mode_prefix in particular was only added in 2.x.

[skip ci]
2018-06-26 14:44:56 -07:00
James Lu
086a5f4496 example-conf, relay, utils: replace 2.0-alpha4 references with 2.0-beta1 2018-06-26 14:44:07 -07:00
James Lu
bbf1b34b12 RELNOTES: more proofreading and reordering
[skip ci]
2018-06-20 16:29:07 -07:00
James Lu
a543de9d73 RELNOTES: refine for flow and add some more issue links
[skip ci]
2018-06-20 16:26:17 -07:00
James Lu
5d11774442 RELNOTES: initial draft of a 2.0-beta1 changelog
[skip ci]
2018-06-20 16:17:57 -07:00
James Lu
c8b8762c12 utils: add parse_duration(), which takes in a duration string and returns the equiv. amt of seconds (#504) 2018-06-15 19:40:05 -07:00
James Lu
5e1cb232b0 IRCNetwork: also detect address types from supplied bindhosts 2018-06-15 18:43:00 -07:00
James Lu
26361c4cc9 IRCNetwork: warn when using plaintext links to non-local addresses 2018-06-15 18:30:21 -07:00
James Lu
e5f817fc95 IRCNetwork: suppress "You can enhance...security...[with] ssl_fingerprint" notices when TLS cert validation is enabled 2018-06-15 15:57:45 -07:00
James Lu
ab9df93898 IRCNetwork: more consistent log format when remote certfp is verified 2018-06-15 15:52:04 -07:00
James Lu
b26d75a6a8 IRCNetwork: handle the case when certificate fingerprint checking is on but the remote provides no cert 2018-06-15 15:50:32 -07:00
James Lu
fefd5a1f6b IRCNetwork: raise ssl.CertificateError instead of an in house exception if certfp is mismatched
Also, fix the expected and real fingerprints being logged in the wrong order.
2018-06-15 15:48:09 -07:00
James Lu
68837aa927 example-conf: enable TLS/SSL by default in all the example server blocks 2018-06-15 15:43:20 -07:00
James Lu
17cd7af22d example-conf: reorder sample server options
Move TLS/SSL options higher up; shift pingfreq and autoconnect options further down
2018-06-15 15:41:20 -07:00
James Lu
17f0b09eb2 example-conf: suggest turning on TLS/SSL in the hostname-as-IP example 2018-06-15 15:36:59 -07:00
James Lu
8fa53f60cb example-conf: copy some more autoconnect examples to the clientbot block 2018-06-15 15:32:35 -07:00
James Lu
76c0db15c4 core: merge TLS validation code into IRCNetwork (#592)
Certificate verification is now enabled for all Clientbot networks, but not yet for S2S links (self-signed certs are common here and direct IP links even more so)
2018-06-15 15:29:15 -07:00
James Lu
e38cd0ada2 get_hostname_type: return 0 for hostnames instead of False for consistency 2018-06-15 14:02:30 -07:00
James Lu
4524aebbac clientbot: initial pass of TLS cert validation (#592)
This works OK, but we should make the validation options built-in instead of clientbot-specific.
2018-06-15 02:47:12 -07:00
James Lu
d3125d9a8f core: automatically detect between IPv4 / IPv6 addresses on connect
Closes #212.
2018-06-15 02:43:33 -07:00
James Lu
5ea33baa8e utils: add get_hostname_type() to autodetect address types (#212) 2018-06-15 02:39:20 -07:00
James Lu
040b009fcb clientbot: ignore RPL_ENDOFBANLIST (368) responses for removed channels 2018-06-15 02:19:56 -07:00
James Lu
c3bb0f7aca relay: rework kill->kick forwarding to send from the sender network's subservers
This makes the kick message a lot neater.

Before:
* net1.relay has kicked GL/net1 from #test ((net2.relay) KILL FWD from GL/net2: test)

After:
* net2.relay has kicked GL/net1 from #test (KILL FWD from GL/net2: test)
2018-06-14 14:48:54 -07:00
James Lu
a98dd36810 example-conf: mention kill -> kick forwarding briefly
[skip ci]
2018-06-14 14:44:03 -07:00
James Lu
56c035a1f5 IRCNetwork: fix broken ping timeout handling
Check for ping outs in the ping scheduler instead of the listener... If the connection is dead, the listener won't ever be called.
2018-06-14 01:18:33 -07:00
James Lu
b2421f5e15 IRCNetwork: simplify connection error handling 2018-06-14 01:18:33 -07:00
James Lu
579b5ce93f IRCNetwork: split SSL connection setup into separate functions
* _make_ssl_context(): returns the SSLContext to use on he network (with options set)
* _setup_ssl(): sets up TLS by loading certfile / keyfile and calling wrap_socket()
* _verify_ssl(): implements certificate fingerprint verification, raising TLSVerificationError (a new subclass of ConnectionError) if this fails

This is a prerequisite for #592.
2018-06-14 01:17:39 -07:00
James Lu
8386edc6d5 conf: rename ConfigValidationError -> ConfigurationError & inherit from RuntimeError 2018-06-13 22:46:58 -07:00
James Lu
76b58c4432 relay: log chandesc changes to INFO 2018-06-12 02:36:37 -07:00
James Lu
77fd9475b6 relay: show channel descriptions before "created by" info 2018-06-12 02:33:29 -07:00
James Lu
8c42825612 relay: allow disabling free link access for all opers 2018-06-12 00:26:24 -07:00
James Lu
5617224780 example-conf: roughly sort "relay:" block options by usefulness
[skip ci]
2018-06-12 00:13:54 -07:00
James Lu
deff6d077d match_host: remove deprecation notice for non-host globs
This is too flaky with commands taking arbitrary user inputs (e.g. opercmds/checkban)
2018-06-12 00:02:18 -07:00
James Lu
1b68bfadc6 coremods, plugins, protocols: drop now redundant allowAuthed=False in is_oper() calls 2018-06-11 23:56:44 -07:00
James Lu
2ca9de2ea8 PyLinkNCWUtils: make the allowAuthed, allowOper options to is_oper no-ops 2018-06-11 23:55:19 -07:00
James Lu
18f108c328 PyLinkNCWUtils: remove check_authenticated() (#422) 2018-06-11 23:54:48 -07:00
James Lu
ed5d46e28a utils: remove deprecated is* functions 2018-06-11 23:48:12 -07:00
James Lu
a30921eeb8 classes: remove irc.conf (#422) 2018-06-11 23:44:59 -07:00
James Lu
3bea214cb0 classes: remove irc.botdata (#422) 2018-06-11 23:43:57 -07:00
James Lu
2e3317ce07 relay: explicitly mention forwarding in relayed kill messages (#520) 2018-06-11 19:26:52 -07:00
James Lu
7d56b30582 opercmds: skip verbose formatting of kill reasons for internal targets (#520) 2018-06-11 19:26:51 -07:00
James Lu
5ecbc2750e exec: fix textwrap error caused by passing the wrong type to reply() 2018-06-11 19:26:51 -07:00
James Lu
1a97a32ef5 ircs2s_common: return just the kill reason as text in kill parsing (#520) 2018-06-11 19:26:42 -07:00
James Lu
93fef9b923 relay: use match_text() to check forcetag_nicks globs 2018-06-11 18:51:30 -07:00
James Lu
73d0e153cf relay: support relaying kills (#520)
Instead of always bouncing, kills to a relay client are now handled as follows:

1) If the target and source networks are both in any killshare pool, relay the kill entirely
2) Otherwise, iterate over all channels the kill target is in:
    3) If the killer has claim access in a channel, forward the KILL as a kick
    4) Otherwise, bounce the kill (so far, silently)

TODO: kill messages are currently very cluttered, we should make our parser deliver more concise strings...
* GL|unreal has quit (Killed (chary.relay (KILL from GL/chary: Killed (GL (test)))))
2018-06-11 18:34:52 -07:00
James Lu
9466813ba1 relay: switch to a flexible, pool-based configuration scheme for IP sharing
This deprecates the "relay::show_ips" and network-specific "relay_no_ips" options, replacing it with the "relay::ip_share_pools" list.
2018-06-11 17:29:29 -07:00
James Lu
5f9904126a log: drop client-in-channel requirement for channel logging 2018-06-11 17:26:04 -07:00
James Lu
17ffd1f640 automode: log mass-removals to INFO as well 2018-06-10 14:36:40 -07:00
James Lu
372e7fb405 automode: send delacc confirmations from the right client
Also bold some parts of the output for easier viewing.
2018-06-10 14:31:56 -07:00
James Lu
8608c72b16 automode: allow removing entries by entry numbers
Closes #506.

This isn't the most efficient implementation because Automode entry lists are actually unordered...
So far we're relying on consistent sorting of entries between LISTACC and DELACC, and indrectly removing entries by comparing the entry list with remove_range's output.
2018-06-09 23:12:37 -07:00
James Lu
c919c523dc utils: add remove_range()
"""
    Removes a range string of (one-indexed) items from the list.
    Range strings are indices or ranges of them joined together with a ",":
    e.g. "5", "2", "2-10", "1,3,5-8"

    See test/test_utils.py for more complete examples.
    """
2018-06-09 17:03:40 -07:00
James Lu
f8e3cfa346 antispam: strip IRC formatting by default before processing
Closes #615.
2018-06-09 16:22:14 -07:00
James Lu
ebf7443d97 antispam: add a "block" verb, and make textfilter use it by default
Closes #616.
2018-06-09 16:22:14 -07:00
James Lu
de62b2e77a utils: add strip_irc_formatting() 2018-06-09 16:22:14 -07:00
James Lu
f9d21c2b10 UserMapping: fix wrong args to __copy__ 2018-06-09 10:44:36 -07:00
James Lu
0ae7eb2563 relay_clientbot: allow overriding clientbot styles by network
Closes #455.
2018-06-08 19:15:47 -07:00
James Lu
b1248524a9 relay: raise an error when trying to delink a leaf channel from another leaf network
Previously this would (confusingly) delink the channel from the network the command was called on instead of the intended target.
2018-06-08 18:45:45 -07:00
James Lu
31a0d36990 clientbot: ignore missing args in 324 / RPL_CHANNELMODEIS
Fixes #537.
2018-06-08 18:45:20 -07:00
James Lu
180da83b4e global: reply with a confirmation
We can also use this space to show the number of channels and networks announced to.
2018-06-08 18:28:41 -07:00
James Lu
f82ddb5336 global: allow configuring channels to exempt from announcements
Closes #453.
2018-06-08 18:25:23 -07:00
James Lu
0edbeb7fad global: do not allow sending empty messages 2018-06-08 18:25:23 -07:00
James Lu
f4de604b7d classes: split match_host() into match_host() and match_text() 2018-06-08 18:25:23 -07:00
James Lu
6085b21e48 antispam: normalize logging format in handle_masshighlight 2018-06-08 17:54:32 -07:00
James Lu
10416013e8 example-conf: mention how kick, ban, etc. won't work for non-channel specific events 2018-06-08 17:54:03 -07:00
James Lu
18bc1942e5 antispam: implement text filters with optional PM spam checks
Also refactor the _punish code to account for events without a channel attached.

Closes #359.
2018-06-08 17:49:26 -07:00
James Lu
b0188dab92 get_friendly_name: strip STATUSMSG prefixes before checking is_channel 2018-06-08 17:26:25 -07:00
James Lu
7b744655ee Merge branch 'devel' into wip/antispam-textfilters 2018-06-08 15:57:40 -07:00
James Lu
8cc838e5ca relay: allow "relay.link.force_ts" as an alternate permission to 'link --force' 2018-06-08 15:56:42 -07:00
James Lu
6f3813d3a4 UserMapping: add in missing reference to the parent irc instance 2018-06-08 15:54:06 -07:00
James Lu
06d57a5b28 relay: rename 'link --force' to 'link --force-ts' to better reflect its purpose
Also mention explicitly that this option does not bypass LINKACL and other channel restrictions (e.g. the Clientbot one)
2018-06-07 13:48:38 -07:00
James Lu
88bd9b2791 relay: oops, the op check in 'link' should be specific to clientbot
(cherry picked from commit d4bf407c5d891edad4ab7cd2ca4f6dc71a9f353c)
2018-06-07 13:46:01 -07:00
James Lu
d4bf407c5d relay: oops, the op check in 'link' should be specific to clientbot 2018-06-07 13:44:37 -07:00
James Lu
b202954be4 relay: check permissions before clientbot op status to prevent arbitrary join triggering
This mirrors the fix in 1.x: commit 141e941fcddaffc93906b0b5e7cb632f21dde464
2018-06-07 13:39:16 -07:00
James Lu
fee64ece04 relay: fix clientbot op requirement not being checked if the sender is in the target channel
Also, show a slightly different error when using the command with 'remote', since the clientbot client gets overridden to be the message sender.

This mirrors the fix in 1.x: commit 9578fd5ac306866f3535ee1cf9c5a7e241fc4511
2018-06-07 13:38:52 -07:00
James Lu
141e941fcd relay: check permissions before clientbot op status to prevent arbitrary join triggers 2018-06-07 13:20:30 -07:00
James Lu
9578fd5ac3 relay: fix clientbot op requirement not being checked if the sender is in the target channel 2018-06-07 13:19:53 -07:00
James Lu
1e0b0dbc70 relay: part the PyLink service bot after relaying other parts on 'delink'
This prevents these messages from being overriden by the "Clientbot was force parted" one if the IRCd responds too quickly.
2018-06-07 13:07:51 -07:00
James Lu
e76c48f24c Initial config skeleton for antispam text filters (#359) 2018-06-02 01:05:00 -07:00
James Lu
5b94a10c67 example-conf: rewrap antispam section, typo fix
[skip ci]
2018-06-02 00:57:26 -07:00
James Lu
b120e2d701 antispam: allow individual punishments to fail gracefully when not supported 2018-06-02 00:31:14 -07:00
James Lu
186b73c72d example-conf: more wording tweaks 2018-06-02 00:15:26 -07:00
James Lu
df6562dcff example-conf: add antispam to the available plugins list 2018-06-02 00:09:52 -07:00
James Lu
cb774ac3da example-conf: document antispam::exempt_level 2018-06-02 00:07:20 -07:00
James Lu
e65d84960a antispam: make punishments method-specific 2018-06-02 00:06:21 -07:00
James Lu
8cc4527ff7 example-conf: initial documentation for the antispam plugin 2018-06-01 23:50:50 -07:00
James Lu
83aa3d262c antispam: allow masshighlight blocking to be disabled 2018-06-01 23:48:22 -07:00
James Lu
62fcdf880c relay: support setting freeform channel descriptions for LINKED
Closes #576.
2018-06-01 23:03:31 -07:00
James Lu
32acc27967 relay: tweak the "wrong network" error message for claim, modedelta 2018-06-01 22:48:13 -07:00
James Lu
c8b2a676fd relay: rename CHANNEL_DELINKED_PARTMSG -> CHANNEL_DELINKED_MSG 2018-05-30 12:45:20 -07:00
James Lu
08a7b5c837 relay: remove our persistent channels on unload 2018-05-30 12:43:59 -07:00
James Lu
5ac283f018 ServiceBot: introduce clear_persistent_channels() to clear all persistent channels in a namespace 2018-05-30 12:43:51 -07:00
James Lu
1ab5d614c0 protocols: convert user TS to an int when receiving new users (#594) 2018-05-26 16:26:14 -07:00
James Lu
e3a935d0b7 classes: make User, Channel TS a property for type-safety (#594) 2018-05-26 02:30:25 -07:00
James Lu
f20fa5e995 Add User.get_fields(), and rework plugins to fix $nick broken expansions
User.nick is no longer a writable attribute since a085aed92435c98713173e3e379202caa4fcddd0, so it won't show up in __dict__ now.

get_fields() replaces the User.__dict__ hack various plugins used and also provides some new expansion variables:
- $sid and $server expand to the server ID and name respectively
- $modes and $channels are now preformatted strings
- $netname expands to the network name

$manipulatable and $_irc were removed since their values aren't quite meaningful as strings
2018-05-26 02:12:38 -07:00
James Lu
108d4b86d9 login: fix login for legacy accounts (#590) 2018-05-26 01:27:12 -07:00
James Lu
2df3dc280c commands.showuser: show home server and TS as "N/A" if they are spoofed values 2018-05-26 01:14:32 -07:00
James Lu
b72420a8aa Revert "NetworkCore: return the server in get_server if the arg was a server"
The complete implementation of is_privileged_service() in ec3a94c4ca61f2b9b32dab9a18d590d206cc4484 explicitly checks the entity ID type, so this strange hack is not needed.

This reverts commit 9113b34b46ba6aa2b381b3c7e9822b4f411caaec.
2018-05-26 01:14:32 -07:00
James Lu
ec3a94c4ca Move U:line checking into core as PyLinkNCWUtils.is_privileged_server()
Closes #604.
2018-05-26 00:14:04 -07:00
James Lu
9e936f1612 Rewrite login handling (Closes #590)
* Move identify command and login helpers into coremods.login
   - corecommands._login -> login._irc_try_login
* Add login._get_account() function to consistently fetch login block info
* Rename functions in coremods.login to snake case:
   - checkLogin -> check_login
   - verifyHash -> verify_hash
* Replace explicit returns in login checks with raising utils.NotAuthorizedError()
2018-05-25 23:50:55 -07:00
James Lu
73261a31bd opercmds: forbid killing the main PyLink client 2018-05-21 02:37:53 -07:00
James Lu
b9f782868c inspircd: remove users from the state immediately when sending a kill
This matches Anope and Atheme's behaviours.

Closes #607.
2018-05-21 00:33:13 -07:00
James Lu
a66a9b6336 core: demote KeyError logging in _remove_client to DEBUG
This is needed for #607.
2018-05-21 00:28:16 -07:00
James Lu
c9c937e7a7 relay: block networks not on the claim list from merging in modes when relinking
This can cause channels to be inadvertently set modes that it shouldn't be on a relink (e.g. modes set by services DEFCON), since relay ignores modes from defined u-lines instead of bouncing them.
2018-05-21 00:09:57 -07:00
James Lu
1fb2a90580 relay: log rejected links due to LINKACL to WARNING
Closes #609.
2018-05-20 22:17:23 -07:00
James Lu
20bbf531e6 wrap_message: fall back to bufsize=510 on protocols declaring S2S_BUFSIZE to be 0 (unlimited) 2018-05-18 19:09:06 -07:00
James Lu
559b262db8 core: break attempts to read from a socket if no data is available 2018-05-18 19:08:37 -07:00
James Lu
f87e646f35 core: use the most recent UID if we have an unresolved nick collision / desync
This is the one most likely to be correct, assuming IRCds deals with nick collisions properly on their own.
2018-05-18 18:17:04 -07:00
James Lu
73322bd9ba wrap_message: expand UID targets into nicks before processing (#153) 2018-05-11 14:47:27 -07:00
James Lu
244c4fe0eb classes: return valid channels as-is in get_friendly_name()
This makes it safe to use when processing message targets. (ref #153)
2018-05-11 14:47:18 -07:00
James Lu
0ac5d424d8 core: implement text wrapping in irc.msg()
Closes #153.
2018-05-11 14:38:21 -07:00
James Lu
5d098f57d7 example-conf: relay::tag_nicks = false is no longer experimental
We've been using this in production with 2.0 for quite some time.

[skip ci]
2018-05-11 14:01:27 -07:00
James Lu
13c315c9a2 example-conf: fix a typo (enable_default_claim -> relay_enable_default_claim) 2018-05-11 13:28:57 -07:00
James Lu
28862281fe example-conf: rework some relay config descriptions for consistent style 2018-05-11 13:26:37 -07:00
James Lu
741e2c8ece relay: allow claim to be disabled by default on new channels
Closes #581.
2018-05-11 13:26:13 -07:00
James Lu
fc275cfdca relay: remove service bots joined persistently when the home network disconnects 2018-05-11 13:21:16 -07:00
James Lu
aa4cedd945 relay: allow default LINKACL mode to be configured as an option (#394) 2018-05-11 13:09:54 -07:00
James Lu
fb6aa88d83 Merge branch 'relay-linkacl-whitelist' into devel
Closes #394.
2018-05-11 12:58:45 -07:00
James Lu
77a6d69f29 RELNOTES: more changelog clarifications and fixes for 2.0-alpha3
[skip ci]
2018-05-10 17:42:53 -07:00
James Lu
16f630560e PyLink 2.0-alpha3 2018-05-10 17:34:52 -07:00
James Lu
613e6412a2 networks.remote: properly error if the target service is not available on the target network
Closes #554.
2018-05-10 16:06:16 -07:00
James Lu
30c1980b59 relay: consistently use bold instead of repr in LINKACL output 2018-05-09 23:33:00 -07:00
James Lu
0ae4aea133 relay: add a whitelist mode for LINKACL (#394) 2018-05-09 23:29:56 -07:00
James Lu
f1b3d8d0ad README: typo fix
[skip ci]
2018-05-09 22:47:16 -07:00
James Lu
d1ac33a1af utils: remove references to deprecated irc.proto 2018-05-09 22:44:36 -07:00
James Lu
64a98120bf relay: remove references to deprecated irc.proto 2018-05-09 22:44:17 -07:00
James Lu
3120fa5396 clientbot: stop sending duplicate JOIN hooks
Also set _clientbot_initial_who_received on 315/ENDOFWHO, instead of on the first /who response we get.

Really fixes #551.
2018-05-09 22:31:19 -07:00
James Lu
451db74f0c clientbot: don't send duplicate away statuses 2018-05-09 22:23:14 -07:00
James Lu
f54382534c kick and kill should raise NotImplementedError when not supported by a protocol
Closes #605.
2018-05-09 22:19:03 -07:00
James Lu
b50ae89acc relay: check service bot status before remote user presence
add_persistent_channels() is usable regardless of whether the service bot is ready, so we do not need to break if the remote copy of the service bot doesn't exist.

Closes #606.
2018-05-09 21:44:04 -07:00
James Lu
f3c2149d7a relay: fix variable confusion when managing service bots
We should be checking for service bot presence locally, and applying changes on the *remote* network.
2018-05-09 21:34:17 -07:00
James Lu
0c19b3719e automode: clarify comments to main() slightly 2018-05-09 21:29:20 -07:00
James Lu
c44aa64503 services_support: check service bot state, not is_internal_client before dynamically parting channels
If a channel still has e.g. relay users when the last local user leaves the channel, the services bots should stay put.
2018-05-09 20:47:51 -07:00
James Lu
d46c494351 faq: move service bot issues under a separate heading
[skip ci]

(cherry picked from commit eb078056e17a71d50e6c024460b6c239a61ed4ce)
2018-05-08 13:37:22 -07:00
James Lu
b5133aebbb faq: more minor edits
[skip ci]

(cherry picked from commit 030facdb7591f93f8cee42453a9190492bbf9dc5)
2018-05-08 13:37:22 -07:00
James Lu
940ff357c9 faq: add links to YAML guides, complete with a mini-rant on YAML misconceptions
[skip ci]

(cherry picked from commit 074019b77aff00318b890123aca3c27e05bc2686)
2018-05-08 13:37:22 -07:00
James Lu
a425015c13 faq: link to the new Disabling Colors/Control Codes section in adv-relay-config
(cherry picked from commit 0ce80f0edec5ed5637f482c402b2e4ab6fa59a23)
2018-05-08 13:37:22 -07:00
James Lu
19c9d4031d Add a donate badge
[skip ci]

(cherry picked from commit d0568a6ad73700e1236f2023990c744b07e02b10)
2018-05-08 13:37:22 -07:00
James Lu
83dd37c4e7 Drop pypandoc stuff and use Markdown descriptions on PyPI
(cherry picked from commit 8a09f321da723e8616976e4354b6e179e867976c)
2018-05-08 13:37:22 -07:00
James Lu
f06a8f09b4 docs/adv-relay-config: resync with master
Remaining changes: restore documentation for
  - $mode_prefix in Custom Clientbot Styles
  - The relay_endburst_delay option on InspIRCd networks

[skip ci]
2018-05-08 13:36:20 -07:00
James Lu
83a7f14b5a PyLink 1.3.0 2018-05-08 13:16:46 -07:00
James Lu
eb078056e1 faq: move service bot issues under a separate heading
[skip ci]
2018-05-06 12:20:49 -07:00
James Lu
030facdb75 faq: more minor edits
[skip ci]
2018-05-06 12:19:08 -07:00
James Lu
074019b77a faq: add links to YAML guides, complete with a mini-rant on YAML misconceptions
[skip ci]
2018-05-06 12:12:17 -07:00
James Lu
0ce80f0ede faq: link to the new Disabling Colors/Control Codes section in adv-relay-config 2018-05-06 11:44:18 -07:00
James Lu
09d2f99855 RELNOTES: refresh with more 2.0-alpha3 changes
[skip ci]
2018-05-06 11:39:48 -07:00
James Lu
2f6c8d2938 Revert "relay: shortcut get_remote_user some more; only grab spawn lock if the user doesn't exist"
This seems to have caused sporadic duplicate user spawns once more (#602)

This reverts commit 0bc24c94b265a68b9830cde42de1ff0c29b223af.
2018-05-05 23:17:24 -07:00
James Lu
c71e9a6410 Merge branch 'services-v3' into devel
- Revamp persistent channel registration to be plugin specific, effectively working around relay-services conflicts (closes #265)
- New abstraction in ServiceBot: add/remove_persistent_channel() to manage persistent channels independently of explicit joins and parts
- Introduce ServiceBot.part(), which sends a part "request" that succeeds only if a channel is not marked persistent by any plugin
- Replace ServiceBot.join() calls with the new registration mechanism, which queues joins instead of dropping them if the service client is not yet ready (closes #596)
2018-05-05 23:17:10 -07:00
James Lu
5a0cb9a4ff automode: add/remove persistent channels on set/del/clearacc 2018-05-05 21:51:27 -07:00
James Lu
8aa67b93fa automode: also rejoin DB channels on reload 2018-05-05 21:10:08 -07:00
James Lu
bf4863eb6d relay, ServiceBot: remove dead code 2018-05-05 13:20:55 -07:00
James Lu
72c2fa38e9 relay: consistently use "Channel delinked." as part message for service bots too 2018-05-05 13:19:57 -07:00
James Lu
c7da7f0025 ServiceBot: allow sending service parts with reasons 2018-05-05 13:19:43 -07:00
James Lu
61d7bf18d3 relay: also attempt to part the PyLink service bot on delink 2018-05-05 13:15:17 -07:00
James Lu
2532042506 services_support: raise endburst handler priority
Other plugins may implement endburst handlers assuming that their service bots have already been created.
2018-05-05 13:07:28 -07:00
James Lu
01c22aac21 Revert "service_support: consistently rejoin all channels on kick and kill"
This is no longer needed with plugin-specific persistent channels.

This reverts commit 3c9dac7e6b2bfd20586a4fea9db40172e537344a.
2018-05-05 13:07:07 -07:00
James Lu
92be421fad relay: attempt to remove persistent channels on delink as well
Also, wrap remove_persistent_channel calls with a try/except when they may fail.
2018-05-05 12:57:17 -07:00
James Lu
8994811f54 relay: further fixes for persistent channels
Also, merge the 'relay_local' and 'relay_remote' namespaces into one.
2018-05-05 12:52:00 -07:00
James Lu
e9fe15bd7d [WIP] Further revise the persistent channels implementation
- Make dynamic_channels per plugin as well as per network to work around relay-service conflicts (#265)
- Introduce ServiceBot.(add|remove)_persistent_channel() to add/remove persistent channels and optionally join/part them
- Introduce ServiceBot.part(), which checks remaining persistent channels list and parts a channel only if it is still not marked persistent
- Refactor automode to autojoin channels on ENDBURST instead of plugin load
- Refactor relay to manage persistent channels on join/part/(de)init, both locally (namespace 'relay_local') and remotely (namespace 'relay_remote')
2018-05-04 22:52:26 -07:00
James Lu
b46ab844fe classes: really fix KeyError crashes on duplicate QUIT 2018-04-30 11:11:14 -07:00
James Lu
7476c6cf05 ServiceBot: log access denials to warning - closes #593
(backported from commit 655221491cdce25ae292ebfc36721f2ef781b6aa)
2018-04-25 23:19:29 -07:00
James Lu
8eba402a33 clientbot: drop pre-WHO join bursting with userhost-in-names, it's too unreliable
Closes #602.
Closes #551.
2018-04-24 12:39:29 -07:00
James Lu
d0568a6ad7
Add a donate badge
[skip ci]
2018-04-21 13:28:34 -07:00
James Lu
8a09f321da Drop pypandoc stuff and use Markdown descriptions on PyPI 2018-04-21 11:08:39 -07:00
James Lu
e96081aa6e PyLinkNetworkCore: make deletion from self.users non-fatal 2018-04-20 19:39:35 -07:00
James Lu
af744123e6 automode: join channels regardless of whether they're empty 2018-04-20 19:35:06 -07:00
James Lu
0ead868546 service_support: skip dynamic join/part hooks on bot-only servers 2018-04-20 19:35:06 -07:00
James Lu
4bfcd52731 RELNOTES: fix formatting of old versions' changelogs
[skip ci]

(cherry picked from commit ce82c231fef41c24a4488f6e7714e23f4bfa0bea)
2018-04-17 12:03:42 -07:00
James Lu
9967b7d9fe adv-relay-config: fix up in-page link [skip ci] 2018-04-17 11:52:09 -07:00
James Lu
041b6aecb6 adv-relay-config: more revisions (closes #597) [skip ci]
commit c82dcb95a6fa1584104a258c9772845b5549b35e
Author: James Lu <james@overdrivenetworks.com>
Date:   Tue Apr 17 11:49:44 2018 -0700

    adv-relay-config: more cleanup and stylistic tweaks

commit 515974a54a3a5970b4b78569e395ba69b7ee2ebc
Author: James Lu <james@overdrivenetworks.com>
Date:   Tue Apr 17 11:45:02 2018 -0700

    adv-relay-config: revert sorting events

    I'd rather have this match the code 1 to 1.

commit c63a298bd44b341831c2492d667becac67b42b42
Author: Ken Spencer <me@iotaspencer.me>
Date:   Tue Apr 17 02:15:31 2018 -0400

    adv-relay-config: change a bit of formatting (#597)

commit aa9577e8300d30713249e054905e6975897e1965
Author: Ken Spencer <me@iotaspencer.me>
Date:   Tue Apr 17 02:09:31 2018 -0400

    adv-relay-config: add more about coloring, sort events
2018-04-17 11:50:28 -07:00
James Lu
89a7f59262 adv-relay-config: various wording adjustments 2018-04-16 22:09:01 -07:00
James Lu
236d800b47 adv-relay-config: move the supported events slightly lower (#597)
[skip ci]
2018-04-16 22:01:49 -07:00
Ken Spencer
601259c771 adv-relay-config: fix up and change table to markdown (#600) 2018-04-16 21:56:20 -07:00
Ken Spencer
5623c06b8f adv-relay-config: fix colons so they match example-conf (#599)
First part of #597.
2018-04-16 20:33:10 -07:00
Ken Spencer
78d1d20856 adv-relay-config: fix colons so they match example-conf (#598)
First part of #597.
2018-04-16 20:27:00 -07:00
James Lu
281ac7aa31 antispam: add a workaround for clientbot support (#359) 2018-04-14 11:40:19 -07:00
James Lu
47052a3bba antispam: use kick+ban as default punishment 2018-04-13 22:18:41 -07:00
James Lu
3e07239db4 antispam: don't kill users if they quit after previous punishments 2018-04-13 22:18:03 -07:00
James Lu
dd8f9411b6 antispam: filter protection-triggering messages from reaching other plugins (#359) 2018-04-13 22:15:24 -07:00
James Lu
3825b93dee Initial pass of a mass-highlight blocking plugin (#359) 2018-04-13 22:08:37 -07:00
James Lu
4d9fbc55ba Merge branch 'wip/dynamic-services' into devel 2018-04-13 22:08:28 -07:00
James Lu
3c9dac7e6b service_support: consistently rejoin all channels on kick and kill
This is the cheap fix to part one of #265: "When a services client is killed, it won't join any relay leaf channels"
2018-04-13 20:34:26 -07:00
James Lu
4cdc19ac78 relay: fix is_internal_client call 2018-04-13 20:31:56 -07:00
James Lu
2f12a5b710 Initial work on dynamic service bot joining / parting (#265) 2018-04-12 12:45:33 -07:00
James Lu
2cb4a06e64 commands: report times in showuser, showchan in UTC
XXX: we could perhaps use abstraction to this since we're repetitively chaining commands
XXX: 'ts' is not type-safe yet, some protocol modules are storing it as a string?!
2018-04-12 10:58:09 -07:00
James Lu
25d24e9bb2 Use non-blocking sockets again, since Linux select() doesn't guarantee that recv() won't block 2018-04-12 10:58:09 -07:00
James Lu
bbc7a981ba example-conf: tweak some more defaults for clarity
(cherry picked from commit 66ec3d9755943bab7fe17a177c0873ecec4b32ad)

Conflicts:
	example-conf.yml
2018-04-10 22:31:24 -07:00
James Lu
c82b12d9d6 example-conf: clarify nick/ident setting on clientbot
(cherry picked from commit 170d793939bf64931f474e4977225101870ea7f0)
2018-04-10 22:29:43 -07:00
James Lu
c7159b9cad core: move clearing channels on kick/part to coremods/handlers
This is for consistency with the rest of the state cleanup code.
2018-04-07 22:44:00 -07:00
James Lu
16faac83eb core: delete empty permanent channels when -P is set on them 2018-04-07 22:38:19 -07:00
James Lu
6bcf7d325f PyLinkNetworkCore: add get_service_option()
Closes #574.
2018-04-07 22:20:35 -07:00
James Lu
138a52611e classes: oops, actually tell the queue thread to abort before we wait for it to stop 2018-04-07 22:12:17 -07:00
James Lu
80cbd7a257 classes: make disconnections more synchronized
- Make ping timer abort instantly if the network is dead
- Shut down the read and write parts of the socket separately, and only close the socket once both parts are done.
2018-04-07 21:56:10 -07:00
James Lu
84dbca4bda classes: thread socket connects once more since they block 2018-04-07 21:56:06 -07:00
James Lu
655221491c ServiceBot: log access denials to warning - closes #593 2018-04-07 20:22:55 -07:00
James Lu
0202d88124 PyLink 1.3-beta1 2018-04-07 16:40:24 -07:00
James Lu
66ec3d9755 example-conf: tweak some more defaults for clarity 2018-04-07 15:44:44 -07:00
James Lu
170d793939 example-conf: clarify nick/ident setting on clientbot 2018-04-07 15:37:42 -07:00
Austin Ellis
3266e1a430 plugins/automode: fix SETACC example (#507)
Small fix to plugins/automode SETACC example given in help output.

(cherry picked from commit fa0dd100e57a5ceb7eba0cf0daea3e1f65eb6398)
2018-04-07 15:16:10 -07:00
James Lu
c33f7437ef Rename servers::<netname>::server_suffix -> servers::<netname>::relay_server_suffix
(cherry picked from commit f75b1eb3566c02800919d861dc2e9f136e87b324)

Conflicts:
	example-conf.yml
	plugins/relay.py
2018-04-04 12:44:14 -07:00
James Lu
5339ddcf08 example-conf: reword description for servers::<netname>::relay_forcetag_nicks
(cherry picked from commit ba4e0aed851d6ee0c3dba56584d9b67e7447b978)
2018-04-04 12:40:28 -07:00
James Lu
95f806dc46 relay: allow defining server-specific nicks to always tags
Closes #564.

(cherry picked from commit 60c05af9ed30c0f4407cc8b2d72bdc2cc808a507)

Conflicts:
	example-conf.yml
2018-04-04 12:40:01 -07:00
Mitchell Cooper
3e16469b25 allow realname to be specified the same way
(cherry picked from commit a1dfa14d201a350dec38a0aa98cf86694cdcd4f0)

Conflicts:
	coremods/service_support.py
	example-conf.yml
2018-04-04 12:35:48 -07:00
Mitchell Cooper
95dbacdba6 allow host to be specified in service bot block or per-network
(cherry picked from commit 33630e8f9dc5152425622d885ff31a6201eef2c0)
2018-04-04 12:32:58 -07:00
Mitchell Cooper
8200d92d23 relay: add server-specific server_suffix (closes #462) (#484)
(cherry picked from commit c92bb1e33b8787d4b2f34be8e56900d94fe6f497)

Conflicts:
	example-conf.yml
2018-04-04 12:30:02 -07:00
James Lu
f75b1eb356 Rename servers::<netname>::server_suffix -> servers::<netname>::relay_server_suffix 2018-04-03 17:43:02 -07:00
James Lu
7586989763 classes: really quash duplicate disconnect calls as much as possible 2018-03-31 11:53:50 -07:00
James Lu
d6952f0361 handlers: fix names of state cleanup function
Where did "stats cleanup" even come from?!
2018-03-31 00:09:30 -07:00
James Lu
ac4296b56b README: the Ubuntu nightlies PPA is no longer supported for trusty / 14.04
[skip ci]
2018-03-31 00:00:30 -07:00
James Lu
57d7a70933 launcher: mention that -c is explicitly kept as a no-op for PyLink <= 1.2.x
(cherry picked from commit c0a061eff3c5e82ec9ed43aff1c8609d075d9d34)

Conflicts:
	launcher.py
2018-03-30 23:52:19 -07:00
James Lu
c0a061eff3 launcher: mention that -c is explicitly kept as a no-op for PyLink <= 1.2.x 2018-03-30 23:44:47 -07:00
James Lu
b14f52b547 unreal: bump protocol version to 4017 (no changes needed)
(cherry picked from commit a2783d74c56cdd007070df7ef44ca18cb4a0ddb4)
2018-03-30 23:41:27 -07:00
James Lu
481561f972 control: don't spew "Stopping plugins" notices if none are loaded
(cherry picked from commit d818c170726cf343678a0e98b8fac1e527df4a58)
2018-03-30 23:27:38 -07:00
James Lu
7dec2d15db launcher: daemonize and write PID file only after reading control options (-s/-R/-r)
(cherry picked from commit b9a66244f04e12509271feadf5d30d7bce2e81c9)
2018-03-30 23:24:41 -07:00
James Lu
b9a66244f0 launcher: daemonize and write PID file only after reading control options (-s/-R/-r) 2018-03-30 23:23:10 -07:00
James Lu
2ddb0ee18f launcher: add experimental daemonization support
Closes #187.

(cherry picked from commit 0b0da2cfe63819bf012bf875d1399455f8647b94)
2018-03-30 23:07:30 -07:00
James Lu
7d5d5a385e control: handle SIGUSR1 as well as SIGHUP as rehash
This is used by the backported launcher for rehash.
2018-03-30 23:06:58 -07:00
James Lu
b5735702f7 Backport the launcher as of commit 8321485315c9e83c1a45a053ec51ce81cd7977cc to 1.3
This adds support for stale PID file checking (#182), as well as the --rehash/--stop/--restart options
2018-03-30 23:06:01 -07:00
James Lu
79ff9f23fb setup: update PyPI classifiers
- Declare Python 3.6 support
- Change development status to Production/Stable

(cherry picked from commit dbc9d1690d46d01a56b0333624bcca3956423af1)
2018-03-30 22:44:31 -07:00
James Lu
58e6527719 Irc: don't abort on BlockingIOError, ssl.SSLWantReadError, ssl.SSLWantWriteError
This effectively merges in the following 2.0 commits:
- "IRCNetwork: do not break on socket BlockingIOError" 6a90e99de4
- "IRCNetwork: also catch ssl.SSLWantReadError and ssl.SSLWantWriteError" ccc9f8e5c8
- "IRCNetwork: bump SOCKET_REPOLL_WAIT to 1 sec" 92460716d1

This fixes one part of #463.
2018-03-30 12:24:01 -07:00
James Lu
c54bb557dd relay: only look up nick once in normalize_nick
(cherry picked from commit ec8f74444906347dda3329c929f2c7315a1da507)

Conflicts:
	plugins/relay.py
2018-03-30 12:14:48 -07:00
James Lu
78b515144f Bump VERSION to 1.3-dev
[skip ci]
2018-03-30 12:09:01 -07:00
James Lu
9d21a5269f relay: speed up shutdowns by not manually splitting off every relay server
The connection will soon be gone anyways, so this is fairly pointless.
2018-03-30 12:06:45 -07:00
James Lu
c978e1c52f relay: fix a typo: CLIENTBOT_WHITELISTED_UMODES -> CLIENTBOT_WHITELISTED_CMODES 2018-03-30 12:01:38 -07:00
James Lu
310ad345a3 SECURITY: normalize account names before checking network / oper filters
(cherry picked from commit a6c1beaad0f715a28495edab8b4ae0f53a8968a7)
2018-03-30 11:59:29 -07:00
James Lu
22efe1384c _login: tweak error message: opered up => opered
(cherry picked from commit 557f1578bcd5b43b9be75b41ee887d659c49d3a9)

Conflicts:
	coremods/corecommands.py
2018-03-30 11:58:50 -07:00
James Lu
a6c1beaad0 SECURITY: normalize account names before checking network / oper filters 2018-03-30 11:55:29 -07:00
James Lu
557f1578bc _login: tweak error message: opered up => opered 2018-03-30 11:55:29 -07:00
James Lu
79143a1e40 Allow limiting login blocks to opers & certain hosts
Closes #502.

(backported from commit f439267129f88b8bd6f52f6b80483cf1d7853762)

This also fixes the previous commit "Allow specifying login blocks that are local to certain networks" for 1.3.
2018-03-30 11:35:45 -07:00
James Lu
c5970ba26d Allow specifying login blocks that are local to certain networks
(cherry picked from commit 8059f3f7fcd856e245732f2129b5813b31309039)
2018-03-30 11:35:45 -07:00
James Lu
1fbe3c6891 relay: sync whitelisted mode lists with 2.0-alpha3 2018-03-30 11:21:15 -07:00
James Lu
209fa9722c inspircd: move definitions for cmodes +J and +E to kicknorejoin_insp and repeat_insp
These use InspIRCd specific arguments which don't map cleanly to other IRCds. #559

(cherry picked from commit ec3b230eab29083a231b5c5623bbe070dea88e97)
2018-03-30 11:20:41 -07:00
James Lu
597685d77c relay_clientbot: fix @#channel messages not being treated as channel-specific
This is a lighter version of the more comprehensive fix in 2.0[1], which
depends on reworked STATUSMSG handling in relay.

[1]: https://github.com/GLolol/PyLink/commit/57334183
2018-03-30 11:14:56 -07:00
James Lu
a24d4d5b9c example-conf: sort clientbot ex. options so that cb-specific ones are at the end
(backported from commit 0c2927fb1eab5ab318800c6cd91eeff52c66ad0b)
2018-03-30 11:00:53 -07:00
James Lu
59d52920f3 example-conf: revise the example servers: section
- Remove "8P#" as a sidrange default - it does not leave adequate room for larger networks
- Move the TS6 example to near the end; it has less demand than Unreal and P10
- Consistently use "must" to describe P10 cloaking options, where any misconfiguration will lead to serious desyncs
- Bumped example autoconnect times from 5 to 10 seconds
- Add the 'netname' option to server blocks that previously didn't include it
- Comment out the 'channels' setting on each server example by default
- Various wording clarifications

(backported from commit 390b7a327a44e05409097050104bdaa19553ecb1)
2018-03-30 11:00:53 -07:00
James Lu
77eb9b4060 example-conf: revise notes for P10, TS6 servers
Backport commit 3d61bfd1148c74d030acf505ee2eeb7594b09d42 from 2.0.
2018-03-30 11:00:53 -07:00
James Lu
6d08e0b953 docs: various fixes pointed out by @MrBenC
- Clarify the project's goals of being an IRC services *framework*
- Briefly mention in the FAQ that the relay plugin is needed for...well, relay!

[skip ci]

(cherry picked from commit 484822e5d793ebb0d565a4b96cb2005dc630dc72)
2018-03-30 11:00:53 -07:00
James Lu
f274088ea0 classes: more checks on _aborted to (hopefully) prevent duplicate disconnects triggered by _send 2018-03-30 10:47:34 -07:00
James Lu
93d590fdea UserMapping: check for lower_nick rather than isinstance(userobj, User)
This makes classes reload-safe again.
2018-03-30 10:46:49 -07:00
Jordy Zomer
7a1dcbd460 Use yaml.safe_load instead of yaml.load to prevent executing arbitrary code (#589) 2018-03-29 14:12:32 -07:00
Jordy Zomer
be8e4be49d Use yaml.safe_load instead of yaml.load to prevent executing arbitrary code (#589) 2018-03-29 14:10:33 -07:00
James Lu
335fb352ec classes: ignore errors on duplicate selector.unregister()
This should really be fixed more thoroughly: there are still some remaining issues after the port to select, with disconnect / reconnect being triggered twice.
2018-03-29 14:04:58 -07:00
James Lu
29cda543e4 launcher: use a clearer description for --trace 2018-03-24 01:07:24 -07:00
James Lu
22b2410913 RELNOTES: mention new --trace arguments for 2.0-alpha3 2018-03-24 01:05:47 -07:00
James Lu
22f781189d Refresh 2.0-alpha3 changelog with engine-rework batch 2 changes so far 2018-03-24 01:02:55 -07:00
James Lu
84187a07ec Refresh 2.0-alpha3 changelog (up to 815535d76b0b2c150a250eea9d7372901e1586af) 2018-03-24 01:02:14 -07:00
James Lu
0bc24c94b2 relay: shortcut get_remote_user some more; only grab spawn lock if the user doesn't exist 2018-03-24 00:31:15 -07:00
James Lu
bbb36cd956 relay: reuse get_relay_server_sid output when bursting groups of users 2018-03-24 00:25:05 -07:00
James Lu
a085aed924 Rework irc.users and User() to transparently create a store of nicks -> UIDs
- This turns IRCNetwork.users into a new UserMapping class, which stores User objects by UID (str) and provides a 'bynick' dict storing case-normalized nicks to lists of UIDs.
- Turn User.nick into a property, where the setter implicitly updates the 'bynick' index and computes a case-normalized version of the nick (User.lower_nick)
2018-03-24 00:12:19 -07:00
James Lu
815535d76b IRCNetwork: fix autoconnect not applying when socket.connect() fails 2018-03-23 20:21:49 -07:00
James Lu
814dd9a3c5 IRCNetwork: only register a socket with selectdriver after connecting 2018-03-23 20:16:59 -07:00
James Lu
ec8f744449 relay: only look up nick once in normalize_nick 2018-03-22 22:45:40 -07:00
James Lu
bb1334696c classes: cache more of to_lower() 2018-03-22 22:03:08 -07:00
James Lu
c11a476257 Revert "selectdriver: actually, force a disconnect when _run_irc() fails"
This reverts commit f10f5bee52c395ef93e5032a90cf41d78531590b.
2018-03-22 21:21:33 -07:00
James Lu
b522967760 Fix duplicate calls to _run_autoconnect 2018-03-22 17:42:28 -07:00
James Lu
f10f5bee52 selectdriver: actually, force a disconnect when _run_irc() fails 2018-03-21 21:07:44 -07:00
James Lu
989259af97 selectdriver: don't crash if _run_irc hits an error 2018-03-21 20:14:27 -07:00
James Lu
5172841378 launcher: add support for tracing (most of) PyLink's execution 2018-03-17 16:13:35 -07:00
James Lu
91b86ce0e4 Remove structures.DeprecatedAttributesObject, it's vastly inefficient for what it accomplishes 2018-03-17 15:49:48 -07:00
James Lu
fb6c3bf6d5 selectdriver: don't try to deregister dead sockets 2018-03-17 15:33:39 -07:00
James Lu
ab70d7c8fb selectdriver: stop delivering events when _aborted is set 2018-03-17 15:27:35 -07:00
James Lu
8100a4cea6 IRCNetwork: run _run_autoconnect in a thread so it doesn't block whatever calls disconnect() 2018-03-17 15:26:36 -07:00
James Lu
30bcd8ca79 control: remove check for _connection_thread (removed in select rework) 2018-03-17 12:18:34 -07:00
James Lu
0151f77f7b Don't clear the read buffer with every _run_irc call 2018-03-17 12:18:16 -07:00
James Lu
10d2fb93ed classes: fix syntaxerror from merge 2018-03-17 11:06:02 -07:00
James Lu
0033612fa3 Merge branch 'devel' into engine-rework
Conflicts:
	classes.py
2018-03-17 11:03:58 -07:00
James Lu
f7ab2564fe Rework inbound connection handling to use select
Closes #588.
2018-03-17 11:01:32 -07:00
James Lu
180bfa9917 relay: don't spam ulines with "notice failed" errors 2018-03-17 10:27:56 -07:00
James Lu
5bffe67416 relay: bandaid patch for freezes on startup when there are a ton of networks
The side effect of this patch is that it makes large bursts *really* CPU intensive. A proper fix for this will hopefully be introduced in the future.
2018-03-11 21:26:37 -07:00
James Lu
b7b49769e0 relay: silently abort if a network splits while we try to spawn a server or client 2018-03-11 21:25:42 -07:00
James Lu
5733418380 relay_cb: bandaid fix to prevent STATUSMSG messages from being interpreted as non-channel specific 2018-03-10 19:54:33 -08:00
James Lu
b6bac994c6 servermaps: show the uplink server name for Clientbot links 2018-03-07 22:28:34 -08:00
James Lu
92460716d1 IRCNetwork: bump SOCKET_REPOLL_WAIT to 1 sec 2018-03-07 18:32:20 -08:00
James Lu
ccc9f8e5c8 IRCNetwork: also catch ssl.SSLWantReadError and ssl.SSLWantWriteError 2018-03-07 18:31:43 -08:00
James Lu
8f9b56e9d9 IRCNetwork: abort when _send() fails to avoid deadlocks 2018-03-07 18:30:14 -08:00
James Lu
c49147f232 stats: route permission error replies to notice and not privmsg
This prevents "unknown command" flood loops with stats services which poll these on link.
2018-03-04 12:11:50 -08:00
James Lu
57f77c676d relay: don't show the network name when routing kicks through a server
This is redundant, as relay has always spawned subservers for quite some time now.
2018-03-02 21:42:25 -08:00
James Lu
9f39e484da Merge branch 'mode-rework' into devel 2018-03-02 21:34:09 -08:00
James Lu
0ca185fada classes: fix some prefixmodes list vs. state confusion 2018-03-02 21:07:47 -08:00
James Lu
5a00454a8d _parse_modes: apply modes to a temporary mode list as we parse them
Fixes #573.

Old, broken behaviour:
irc.parse_modes('#test', '+b-bb *!*@new.ban *!*@nonexistent.ban *!*@new.ban')
=> [('+b', '*!*@new.ban')]

Fixed:
irc.parse_modes('#test', '+b-bb *!*@new.ban *!*@nonexistent.ban *!*@new.ban')
=> [('+b', '*!*@new.ban'), ('-b', '*!*@new.ban')]
2018-03-02 20:57:16 -08:00
James Lu
f12318b5dc classes: add an is_channel argument to _parse_modes
This is required for the following commit.
2018-03-02 20:56:59 -08:00
James Lu
1413aa6042 _apply_modes: don't add prefix modes to mode sets even when prefixmodes=None 2018-03-02 20:47:41 -08:00
James Lu
054680c806 classes: split apply_modes into two functions 2018-03-02 20:43:25 -08:00
James Lu
9cca695d14 classes: split parse_modes into core and wrapper functions 2018-03-02 20:43:25 -08:00
James Lu
d172831805 conf: rename methods to snake case (#523) 2018-03-02 20:23:48 -08:00
James Lu
03103bea14 conf: use a more informative description for validate() 2018-03-02 20:23:47 -08:00
James Lu
87fdb1dde1 opercmds: migrate from utils.isServerName to irc.is_server_name 2018-03-02 20:23:47 -08:00
James Lu
9e212fc0a4 protocols: migrate utils.wrapArguments, splitHostmask use to camel case (#523) 2018-03-02 20:23:47 -08:00
James Lu
3e656cd943 utils: mark reset_module_dirs, load_plugin, get_protocol_module as private 2018-03-02 20:23:47 -08:00
James Lu
1cdf16f5c9 various: migrate utils.loadPlugin, getProtocolModule, resetModuleDirs calls to snake-case (#523) 2018-03-02 20:23:47 -08:00
James Lu
bea2ea8ebd plugins, coremods: migrate (un)registerService calls to snake case (#523) 2018-03-02 20:23:47 -08:00
James Lu
9e3f412f0b log: rename methods to snake case (#523) 2018-03-02 20:23:47 -08:00
James Lu
ad5a11bf34 control: skip networks that fail to initialize on rehash
This follows the launcher fix in 8321485315c9e83c1a45a053ec51ce81cd7977cc.
2018-03-02 20:23:47 -08:00
James Lu
a2783d74c5 unreal: bump protocol version to 4017 (no changes needed) 2018-03-02 20:23:47 -08:00
James Lu
1c3f71ac1b servermaps: assign servermaps.localmap to all opers 2018-03-02 16:18:55 -05:00
James Lu
d5d94f86e8 servermaps: split into two perms (servermaps.map and servermaps.localmap) 2018-03-02 16:14:04 -05:00
James Lu
6a90e99de4 IRCNetwork: do not break on socket BlockingIOError
On non-blocking sockets, recv() raises BlockingIOError instead of blocking when there's no data to be read.
The correct behaviour is to wait and try again instead of breaking the connection.
2018-03-01 12:52:41 -05:00
James Lu
e8e26daf05 ctcp: fix wrong logging format for unknown CTCPs 2018-03-01 02:48:04 -05:00
James Lu
5d25b3c105 ctcp: remove extraneous keyword argument
private is not defined in irc.msg()
2018-02-25 14:29:19 -05:00
James Lu
3c0809dce0 ctcp: don't use irc.reply in hook functions
This is undefined behaviour because nothing in this stack actually updates the 'last caller' variables irc.reply() use.
2018-02-24 14:19:57 -05:00
James Lu
50f8cde694 classes: make _to_lower_core and _expandPUID type-safe 2018-02-24 14:19:57 -05:00
James Lu
2aca4e39c1 RELNOTES: fill in release dates for 1.2.0, 1.2.1, 2.0-alpha1
[skip ci]
2018-02-24 01:48:26 -08:00
James Lu
a2e793755f RELNOTES: first batch of 2.0-alpha3 changes 2018-02-24 01:44:04 -08:00
James Lu
5c9639b4a9 opercmds: alias 'trace' to checkban 2018-02-21 00:12:08 -08:00
James Lu
390b7a327a example-conf: revise the example servers: section
- Remove "8P#" as a sidrange default - it does not leave adequate room for larger networks
- Move the TS6 example to near the end; it has less demand than Unreal and P10
- Consistently use "must" to describe P10 cloaking options, where any misconfiguration will lead to serious desyncs
- Bumped example autoconnect times from 5 to 10 seconds
- Add the 'netname' option to server blocks that previously didn't include it
- Comment out the 'channels' setting on each server example by default
- Various wording clarifications
2018-02-20 23:42:13 -08:00
James Lu
3d61bfd114 example-conf: revise notes for P10, TS6 servers 2018-02-20 23:26:06 -08:00
James Lu
0c2927fb1e example-conf: sort clientbot ex. options so that cb-specific ones are at the end 2018-02-20 23:23:04 -08:00
James Lu
804791b8af clientbot: support expansions ($nick, etc) in autoperform 2018-02-20 23:19:32 -08:00
James Lu
0b0da2cfe6 launcher: add experimental daemonization support
Closes #187.
2018-02-19 21:05:15 -08:00
James Lu
9cdb224c02 Replace use of conf.conf['bot'] with conf.conf['pylink'] 2018-02-18 23:26:39 -08:00
James Lu
c40250330d ctcp: log the service bot receiving CTCP messages 2018-02-18 23:13:44 -08:00
James Lu
e68db3689d Rewrite the CTCP plugin
- Extend CTCP replies to all service bots - closes #468.
- Use a generic hook handler instead of wrapping around the commands handler (#407).

This code takes advantage of the hook suppression feature introduced in 2e66b9bde61d796a821cfc2d30f94ac6baa26e40 (#547).
2018-02-18 23:11:36 -08:00
James Lu
2e66b9bde6 classes: allow hook functions to block further execution by returning False
Closes #547.
2018-02-18 22:42:39 -08:00
James Lu
4a01948647 relay: oops, multiple STATUSMSG prefixes should pick the lowest, not highest (#570)
This behaviour really isn't consistent across IRCds though...
- Unreal, Hybrid, and charybdis mangle messages at the server side to use the lowest prefix
- InspIRCd throws a "No such nick/channel" error
- Nefarious silently drops messages with multiple prefixes?
2018-02-18 22:29:16 -08:00
James Lu
37be73d39c clientbot: add STATUSMSG support (#570) 2018-02-18 22:15:19 -08:00
James Lu
81bd1e8474 relay: add basic support for STATUSMSG (#570)
So far, this code only knows about changing prefixes while keeping mode characters as-is.
A complete but longer solution would be to actually go through irc.cmodes, but I don't
think doing so is necessary given how little STATUSMSG is actually used in production.
2018-02-18 22:03:12 -08:00
James Lu
1405b01597 ServiceBot: clean up some function descriptions 2018-02-18 19:40:46 -08:00
James Lu
484822e5d7 docs: various fixes pointed out by @MrBenC
- Clarify the project's goals of being an IRC services *framework*
- Briefly mention in the FAQ that the relay plugin is needed for...well, relay!

[skip ci]
2018-02-17 00:50:12 -08:00
James Lu
7114c6942f README: the shared{} requirement for KLINEs likely applies to chatircd too 2018-02-12 11:18:48 -08:00
James Lu
8321485315 launcher: prevent protocol module init errors from aborting execution
This fixes various issues including:
- Networks going missing (the server list is read in a non-deterministic order)
- world.started never being set, causing relay to never work!
2018-02-10 16:17:18 -08:00
James Lu
3f7e2328fe relay: make endburst delay configurable
Also, raise the default to 10 seconds.
2018-02-10 15:53:49 -08:00
James Lu
ea84497359 protocols: remove the endburst_delay option from spawn_server
Interestingly, this was never documented in the protocol module spec...
2018-02-10 15:44:09 -08:00
James Lu
a425f873b5 relay, inspircd: move endburst delay code to a private API
This is a very specific hack that shouldn't be extended across the protocol module spec. So far, all other protocol modules ignore the endburst_delay option anyways.
2018-02-10 15:34:07 -08:00
James Lu
ccbd79a95c relay: fix KeyError when a local client is kicked from a claimed channel
Fixes #572.
2018-02-10 15:12:40 -08:00
James Lu
18c1a277f5 clientbot: remove extraneous use of to_lower() in handle_part 2018-01-31 19:35:24 -08:00
James Lu
74848853ac clientbot: fix KeyError caused by lower() in spawn_server
This affected connections to afternet for example.
2018-01-31 19:33:05 -08:00
James Lu
9f6e4306cd automode: fix handling of channels with multiple #'s in them
(cherry-picked from commit 09c8b03705e9513f3381f6a1c43d44e7dc1cc214)
2018-01-29 08:21:37 -08:00
James Lu
84f6190478 inspircd: only read METADATA modules changes from the uplink
Closes #567.
2018-01-25 09:52:29 -08:00
James Lu
28a62f629a automode: replace assert usage with proper exceptions 2018-01-22 08:17:20 -08:00
James Lu
09c8b03705 automode: fix handling of channels with multiple #'s in them 2018-01-22 08:15:04 -08:00
James Lu
5fd216c720 commands: fix 'showchan' displaying status prefixes in reverse 2018-01-22 08:10:55 -08:00
James Lu
d608661a33 permissions-reference: document perms for 'raw' plugin 2018-01-21 13:52:29 -08:00
James Lu
8000d51453 Split the 'raw' command into a new plugin
Closes #565.
2018-01-21 13:50:37 -08:00
James Lu
e446e0e27b control: continue handling SIGUSR1 as rehash for compat with older 2.0 versions 2018-01-21 13:36:29 -08:00
James Lu
44be5910e0 Revert "control: move rehash signal to SIGUSR1, and shutdown on SIGHUP (terminal close)"
This wasn't an incredibly popular decision because it broke a simple 'pylink &'

This reverts commit 883f9199ecf63ef91df25a5dc47d8f7d353d7af0.

Conflicts:
	coremods/control.py
2018-01-21 13:31:15 -08:00
James Lu
6bb2198710 inspircd: move _modsupport.clear() to a _post_disconnect override
Speculative fix for #567.
This may be caused by a race condition between post_connect and handle_capab, since the remote server can send its server data before we start sending ours.
2018-01-21 13:27:59 -08:00
James Lu
67dea6f748 classes: add docstrings to _pre_connect, _pre_disconnect, _post_disconnect 2018-01-21 13:20:42 -08:00
James Lu
58b717a2a0 docs/t: warn in main articles that specifications in master may be outdated
[skip ci]
2018-01-13 18:29:57 -08:00
James Lu
31c96bd1ed hooks-reference: bump to 2.0-alpha2
- Replace `IrcChannel`, `IrcUser`, and `IrcServer` with their new class names (`classes.Channel`, `classes.User`, and `classes.Server`)
- Replace `irc.fullVersion()` with `irc.version()`
- Various minor wording tweaks.
2018-01-13 18:23:12 -08:00
James Lu
06ee01b7a7 hooks-reference: don't use the wrong terminology in example channel names 2018-01-13 18:23:12 -08:00
James Lu
7a51220309 relay: match P10 WALL* commands as notices 2018-01-08 20:56:06 -08:00
James Lu
ec9063b9e8 Revert "relay: differentiate between PRIVMSG vs. NOTICE via a blacklist"
This reverts commit d81a9cd5c3f51174755d64434ddf6c5f2b0d7988.
2018-01-08 20:56:06 -08:00
James Lu
bcb0fecfa8 PyLink 2.0-alpha2 2018-01-06 17:30:50 -08:00
James Lu
ce3b1152b2 modelists: fix page title for extbans table
[skip ci]
2018-01-05 18:46:08 -08:00
James Lu
7a32e7d8a2 global: ignore empty "global:" configuration blocks
(cherry picked from commit 15a231a3711b96bbad2b2cda7339bcb838ea95df)
2018-01-03 08:01:27 -08:00
James Lu
d5d140f4b0 p10: fix hashed cloaks check reading from the wrong config variable
(cherry picked from commit 043a147b41d5d23633bfad6ec261d1f058e8c276)

Conflicts:
	protocols/p10.py
2018-01-02 20:38:12 -08:00
James Lu
f8abdd1244 RELNOTES: update for previous commit 2017-12-31 12:09:52 -08:00
James Lu
043a147b41 p10: fix hashed cloaks check reading from the wrong config variable 2017-12-31 12:09:36 -08:00
James Lu
3d661c9713 RELNOTES: update 2.0-alpha2 changes so far 2017-12-30 01:34:33 -08:00
James Lu
ba4e0aed85 example-conf: reword description for servers::<netname>::relay_forcetag_nicks 2017-12-30 01:33:14 -08:00
James Lu
60c05af9ed relay: allow defining server-specific nicks to always tags
Closes #564.
2017-12-30 01:22:24 -08:00
James Lu
56fa626605 inspircd: use clear() instead of replacing the _modsupport set
Maybe this will fix issues with _modsupport not being completely filled?
2017-12-27 11:48:00 -08:00
James Lu
7c0d279f61 inspircd: raise NotImplementedError instead of only warning when a CHG* module is missing 2017-12-22 12:41:48 -08:00
James Lu
92427201f1 inspircd: track module (un)loading
Closes #555.
2017-12-22 12:38:48 -08:00
James Lu
c62580d228 bots: don't allow 'spawnclient' on protocols where it is stubbed 2017-12-22 12:28:27 -08:00
James Lu
958bb351ca clientbot: log warnings if the bot cannot join a channel
Closes #533.

This adds handlers for the following numerics:
* ERR_TOOMANYCHANNELS (405)
* ERR_CHANNELISFULL (471)
* ERR_INVITEONLYCHAN (473)
* ERR_BANNEDFROMCHAN (474)
* ERR_BADCHANNELKEY (475)
* ERR_BADCHANMASK (476)
* ERR_NEEDREGGEDNICK (477)
* ERR_BADCHANNAME (479)
* ERR_SECUREONLYCHAN / ERR_SSLONLYCHAN (489)
* ERR_DELAYREJOIN (495)
* ERR_OPERONLY (520)
2017-12-22 12:28:27 -08:00
James Lu
7afe193259 bots: fix KeyError when attempting to join a channel not in the index 2017-12-22 12:28:27 -08:00
James Lu
444d8c53bb RELNOTES: fix wacky line spacing
[skip ci]
2017-12-22 01:05:40 -08:00
James Lu
5b2fdc94e7 RELNOTES: add a list of changes since 2.0-alpha1 so far 2017-12-22 01:03:19 -08:00
James Lu
dbc9d1690d setup: update PyPI classifiers
- Declare Python 3.6 support
- Change development status to Production/Stable
2017-12-22 00:07:49 -08:00
James Lu
5f9365a521 relay, inspircd: add support for blockhighlight +V 2017-12-21 21:18:20 -08:00
James Lu
145a4677f6 ircs2s_common: add handling for nick@servername messages 2017-12-21 02:57:10 -08:00
James Lu
8dbbe65a1c ircs2s_common: remove useless statusmsg splitting code
We used to do this in order to lowercase the channel part of ~#channel messages correctly, but that is no longer needed as of 9702030bf5437a9749f5435c87d9c7a2757eaadc.
2017-12-21 02:11:28 -08:00
James Lu
d01b9aaa23 relay: improve fallback KNOCK notices
- Show the network that the /knock originated from (it is *not* obvious when we have a user with no relay client)
- Specifically hint that users with no relay client cannot be invited directly because there is no client to actually /invite!
- Prefer sending the notice to %#channel when halfops are available, as they usually have the power to /invite
2017-12-21 01:57:43 -08:00
James Lu
f64976b1ed p10: implement outgoing knock() as a wrapper over NOTICE 2017-12-21 01:53:41 -08:00
James Lu
2df608307d relay: block sending STATUSMSG messages to IRCds not supporting them 2017-12-21 01:41:01 -08:00
James Lu
16b491fdab ts6_common, p10: declare protocol cap has-statusmsg 2017-12-21 01:34:41 -08:00
James Lu
d81a9cd5c3 relay: differentiate between PRIVMSG vs. NOTICE via a blacklist
This is so that P10 WALL* commands are forwarded correctly as notices instead of privmsgs.
2017-12-21 01:27:34 -08:00
James Lu
20e730ba2b p10: add inbound handlers for WALLCHOPS/WALLHOPS/WALLVOICES
This essentially finishes off STATUSMSG support on P10.
2017-12-21 01:27:19 -08:00
James Lu
2cc1195ff9 p10: refactor message() to send @%+#channel messages correctly 2017-12-21 01:20:08 -08:00
James Lu
63f3cdaea8 relay: add knock forwarding support 2017-12-21 00:19:09 -08:00
James Lu
454539185e Move knock handling to ts6_common
This adds support for (reasonless) KNOCK on TS6.
2017-12-21 00:04:55 -08:00
James Lu
2245af1dba Revert "ts6: handle ChatIRCd ENCAP USERMODE"
This reverts commit 785fc8d2d23adeb05061d7b01945a9d58d1ccef4.

This feature isn't actually used yet and is relatively non-standard. Aside from that, the biggest issue so far is that ENCAP USERMODE doesn't give servers a way to explicitly acknowledge or reject(ignore) the mode change, which can lead to desyncs.
2017-12-18 17:20:37 -08:00
James Lu
ab91acb2f7 relay_clientbot: rename isRelayClient => is_relay_client() 2017-12-18 13:23:53 -08:00
James Lu
954f4f9886 relay: capitalize constants related to modes 2017-12-18 13:23:16 -08:00
James Lu
0104462782 relay: whitelist cmodes kicknorejoin, kicknorejoin_insp, repeat, repeat_insp
Closes #559.
2017-12-18 13:17:05 -08:00
James Lu
ec3b230eab inspircd: move definitions for cmodes +J and +E to kicknorejoin_insp and repeat_insp
These use InspIRCd specific arguments which don't map cleanly to other IRCds. #559
2017-12-18 13:10:48 -08:00
James Lu
6c65d5523e IRCNetwork: potentially fix queue thread shutdowns (#558)
Replace unreliable appendleft() usage with replacing the first element (or adding None if the queue is empty).
2017-12-17 01:01:21 -08:00
James Lu
9a5072824d relay: stop lowercasing modedelta args, as that can break things like unreal +f 2017-12-14 13:40:47 -08:00
James Lu
f908e407d4 relay: also ignore clientbot networks in modedelta
TODO: abstract all these checks out!
2017-12-14 13:26:43 -08:00
James Lu
923795719f relay: drop all list and prefix modes in modedelta 2017-12-14 13:07:00 -08:00
James Lu
57a2132d5d relay: always enforce modedelta modes, and prevent them from being unset 2017-12-14 12:46:25 -08:00
James Lu
b2270ca3eb relay: move claim enforcement routines outside the mode/kick loops
This prevents claim responses from being sent multiple times.
2017-12-14 12:15:19 -08:00
James Lu
59c12ff354 relay: merge in modedelta branch, port to latest 2.x relay
Merge remote-tracking branch 'origin/wip/relay-modedelta' into devel

Conflicts:
	plugins/relay.py
2017-12-14 11:56:41 -08:00
James Lu
8490bee634 handlers: pick the highest prefix when displaying status in WHOIS
This fixes a regression from aa44bc15a37ef690eb1cc47d5f8d315116b919a0
2017-12-12 12:46:43 -08:00
James Lu
9dfa0a478e classes: fix inverted order in get_prefix_modes() description
No, no, no. Nobody sorts modes that way!
2017-12-07 12:07:08 -08:00
James Lu
d54bf0d06c relay: also log the extban prefix strings being readded 2017-12-07 11:38:27 -08:00
James Lu
a2cb4daa46 relay: mangle the mode name when forwarding extban->cmode so that +b syntax filters don't trigger
This should allow bidirectional forwarding between UnrealIRCd +b ~T:block:<glob> and InspIRCd +g <glob> to work.
(#557)
2017-12-07 11:26:16 -08:00
James Lu
8fcb5f9df0 relay: ignore static extbans when looking up dynamic extban prefixes
This fixes #560, which was caused by relay confusing ban_all_registered ($a) with ban_account ($a:), since $a:account also starts with $a.
2017-12-07 11:05:39 -08:00
James Lu
6adeada598 relay: add more complete debug logging in extban handlers 2017-12-07 11:04:26 -08:00
James Lu
a0c57d0a5a unreal: register ~T extbans as filter and filter_censor (#557)
This doesn't work in relay yet because of #560 (as well as a restriction where acting extbans must match n!u@h...)
2017-12-07 00:36:38 -08:00
James Lu
bd2cd90957 modelists/channel-modes: add inspircd +D definition
[skip ci]
2017-12-05 12:04:35 -08:00
James Lu
d30eca77e9 relay: add more channel modes to the whitelist
* blockcaps: inspircd +B, elemental-ircd +G
* exemptchanops: inspircd +X
* filter: inspircd +g
* hidequits: nefarious +Q, snircd +u
* history: inspircd +H
* largebanlist: ts6 +L
* noamsg: snircd/nefarious +T
2017-12-05 12:01:57 -08:00
James Lu
80ef2ca788 hybrid: remove slash-in-hosts as it is not supported 2017-12-03 18:56:43 -08:00
James Lu
2fc5d32e3f NetworkCore: don't clear state on disconnect
This is already reset on connect, so doing it here too is a bit pointless.
2017-12-03 18:56:39 -08:00
James Lu
bebdf2e4ff IRCNetwork: avoid sending multiple disconnect hooks for one disconnection 2017-12-03 17:46:45 -08:00
James Lu
8b62d6d458 modelists: resort data by ircd names defined in protocol modules 2017-11-21 07:39:45 -08:00
James Lu
52f40ad7a2 networks.remote: don't clobber command switches for other commands
This also moves the --service argument to before the network name to prevent ambiguity with argparse.REMAINDER.

Closes #538.
2017-11-14 18:14:23 -08:00
James Lu
f969197436 Convert protocol-modules graphic to a .svg 2017-11-12 12:07:08 -08:00
James Lu
bff53c6e69 Remove .codeclimate.yml, practically unused
This reverts commit 3869c069919bbc928e54dd6e748d62c5167f2617.
2017-11-12 12:03:25 -08:00
James Lu
03e02dda51 relay: replace garbage locking code with proper filtering in relay_joins
I'm not even going to start on how much time I spent working on this...

Closes #548, #529
2017-11-12 11:58:36 -08:00
James Lu
d4cbf1d2af services_support: fix rejoin-on-kill to the main service bot
Clear the irc.pseudoclient state on kill as the respawning code will check for it first and reuse UIDs even if they don't exist.
2017-11-12 10:54:30 -08:00
James Lu
847854aac3 Merge branch 'master' into devel
Conflicts:
	README.md
2017-11-07 19:19:52 -08:00
James Lu
48ea58c1fb bots: remove use of deprecated utils.isNick() 2017-11-07 19:19:11 -08:00
James Lu
c35c8cd4aa
FAQ updates [skip ci]
- add answers for "services bots not spawning" and "inconsistent config spacing causing errors"
- reword the connection troubleshooting section slightly
- mention using 'showchan' to help determine the cause of missing user issues
2017-11-07 09:59:37 -08:00
James Lu
15a231a371 global: ignore empty "global:" configuration blocks 2017-11-06 12:07:33 -08:00
James Lu
b6af6dddc5 relay: fix RuntimeError on handle_part for clientbot networks 2017-11-05 01:18:42 -08:00
James Lu
509c2e52c4 clientbot: fix KeyError when there are still queued outgoing messages to a channel we just left 2017-11-05 01:17:59 -08:00
James Lu
62cef5c3f5
classes: clarify comments on mode-related functions 2017-11-03 23:40:11 -07:00
James Lu
b366aa8d61 Merge branch 'wip/ts6-updates' into devel 2017-10-27 06:50:37 -07:00
James Lu
544e078512 clientbot: treat 0 as an empty account name (for WHOX)
This fixes incorrect "X is logged in (on somenet) as 0" messages in WHOIS.
2017-10-25 16:01:10 -07:00
James Lu
c974ee9b44 hybrid: various cleanup 2017-10-22 01:19:38 -07:00
James Lu
c636e064e7 ts6: remove SAVE from required capabs
We don't actually send SAVE out to any IRCd, so we don't need to demand that it's supported.

Closes #545.
2017-10-22 01:08:30 -07:00
James Lu
5e7469b56f Remove protocols/ratbox, superseded by ts6
Closes #543.
2017-10-22 01:06:55 -07:00
James Lu
975d835c92 ts6: add support for ratbox, send EUID only when supported (#543) 2017-10-22 01:00:12 -07:00
James Lu
1a24bc19af ts6: rename self.caps to self._caps 2017-10-22 00:44:30 -07:00
James Lu
3d3300e542 ts6: merge in CHGHOST checks and umode definitions from protocols/ratbox 2017-10-22 00:41:15 -07:00
James Lu
c2dbb74f5a ts6: clean up mode definitions and target_ircd code 2017-10-22 00:29:00 -07:00
James Lu
d0dff2c5ae Move permission enumeration to runtime, fix default perms not applying at startup
Closes #542.
2017-10-22 00:08:16 -07:00
James Lu
26b8292564 relay_clientbot: handle errors if the relay: or clientbot_styles: blocks are empty 2017-10-21 13:19:31 -07:00
James Lu
9ffe2edc74 README: mention fix for #526 (unreal hostname desyncs)
[skip ci]
2017-10-18 22:36:22 -07:00
James Lu
472b73cf65 classes.Channel: clarify the intended behaviour of sort_prefixes() and get_prefix_modes() 2017-10-15 02:16:18 -07:00
James Lu
a63e2557be unreal: fix wrong hook name for legacy user introduction
(cherry picked from commit 4935ef521e047b02339315b8d55849cd74192444)
2017-10-15 02:00:02 -07:00
James Lu
4935ef521e unreal: fix wrong hook name for legacy user introduction 2017-10-15 01:55:53 -07:00
James Lu
e8958962dd unreal: fix authentication-in-progress check in handle_server 2017-10-15 01:54:39 -07:00
James Lu
1470e7691f relay_clientbot: add support for showing prefix modes
This adds a new expansion $mode_prefix, and adds it to the default formats for MESSAGE and ACTION.
Closes #540
2017-10-15 01:42:07 -07:00
James Lu
aa44bc15a3 classes: fix backwards sorting in Channel.sort_prefixes()
Also remove various workaround code added to address this.
2017-10-15 01:29:42 -07:00
James Lu
b8df1a1b61 Merge remote-tracking branch 'origin/master' into devel 2017-10-11 18:56:21 -07:00
James Lu
e8b7116888 example-conf: clarify terminology regarding server IDs
P10 calls them server numerics, for example. Also clarify what the sid and sidrange options are used for (i.e. why they need to be set).

[skip ci]
2017-10-11 17:59:16 -07:00
James Lu
a9916a74f2 Revert "NetworkCoreWUtils: strip off leading and trailing quotes from parse_modes"
This was a band-aid fix not ready to be committed - we should work on this in IRCParser and opercmds instead.

This reverts commit aeaee491f352fc76399a0210915b83886327391a.
2017-10-10 22:33:24 -07:00
James Lu
fdaee37b7b example-conf: fix a typo
[skip ci]
2017-10-10 22:31:50 -07:00
James Lu
aeaee491f3 NetworkCoreWUtils: strip off leading and trailing quotes from parse_modes 2017-10-10 22:25:06 -07:00
James Lu
762ec3a0eb structures: fix _keymangle when key isn't a string
I really need to add unit tests for these...
2017-10-10 22:14:33 -07:00
James Lu
66c762b63f Merge remote-tracking branch 'origin/clientbot-fixes' into devel 2017-10-10 20:53:12 -07:00
James Lu
08917f8aae PyLink 2.0-alpha1 2017-10-07 22:54:24 -07:00
James Lu
ce82c231fe RELNOTES: fix formatting of old versions' changelogs
[skip ci]
2017-10-07 22:51:34 -07:00
James Lu
fe4fb9c84c stats: use a simpler /stats c format 2017-10-07 22:27:43 -07:00
James Lu
25ec88c566 clientbot: don't send empty MODE hooks when enumerating empty ban lists 2017-10-07 21:51:38 -07:00
James Lu
eca40a3d7c coremods/handlers: implement cleanup code for visible-state-only servers
Closes #536.
Closes #517.
2017-10-07 21:49:17 -07:00
James Lu
de5ab051aa clientbot: rename cap. clear-channels-on-leave => visible-state-only (#517) 2017-10-07 21:48:48 -07:00
James Lu
740b399ec2 clientbot: block attempts from virtual clients to change to an existing nick (#535) 2017-10-07 20:50:09 -07:00
James Lu
d7766d54d5 clientbot: check for nick collisions with virtual clients on NICK
Closes #535.
2017-10-07 20:07:26 -07:00
James Lu
84ff797b5f clientbot: rewrite _get_UID nick collision handling to be less confusing 2017-10-07 20:03:25 -07:00
James Lu
85ac0bb80a docs/modelists: add a GitHack link to extbans.html
[skip ci]
2017-10-05 19:37:26 -07:00
James Lu
0ad2bc2f7b docs/modelists: use a different color to represent modes implemented as extbans and vice versa
[skip ci]
2017-10-05 19:32:57 -07:00
James Lu
61fe97b646 docs/modelists: drop :data suffix from extbans lists
[skip ci]
2017-10-05 19:28:43 -07:00
James Lu
48aab1cf16 docs/services-api: revise
- Rewrite lead section to be more concise
- Move to snake case method names
- Consistently use the terms "services" and "service bots"

[skip ci]
2017-10-05 19:21:52 -07:00
James Lu
39b1e28061 Remove plugins/example_service, it is out of date and broken with 2.x
Closes #532.
2017-10-05 19:08:28 -07:00
James Lu
f79168ce5f docs/t: remove future articles that won't be around for a while
[skip ci]
2017-10-05 19:05:36 -07:00
James Lu
5574c746b9 docs: update table of contents
Mode lists are in a separate folder now.

[skip ci]
2017-10-05 19:04:45 -07:00
James Lu
82a6ceb99e docs: Refresh release-process.md
[skip ci]
2017-10-05 18:20:59 -07:00
James Lu
f0d1c1bb89 pmodule-spec: mention in detail how users are tracked
Closes #478.
2017-10-05 18:18:09 -07:00
James Lu
8fff9ea641 pmodule-spec: mention post_connect definition for IRCNetwork derivatives (#478) 2017-10-05 18:18:09 -07:00
James Lu
d8768bcb73 Revise docs/automode.md
- The default bot name has been "Automode" instead of "ModeBot" for a while now; reflect that change here
- General edits for flow

[skip ci]
2017-10-04 23:28:27 -07:00
James Lu
d09c1be688 README: update IRC link 2017-10-01 00:39:28 -07:00
James Lu
3022274f6b Merge branch 'master' into devel
Conflicts:
	README.md
	VERSION
2017-09-24 12:26:06 -07:00
James Lu
d2a3bb8d28 actually no, forget having 3 tiers of support for IRCds
[skip ci]
2017-09-24 12:23:25 -07:00
James Lu
3da61f0f2a Revert "Demote ratbox to tier 3 [skip ci]"
This reverts commit d0fbfcd2d8bf13fec0d8d7a9770386747d0f6a73.
2017-09-24 12:22:37 -07:00
James Lu
67d5766cde README: remove expiringdict install note
1.1.4 has since been released, fixing installation via pip
[skip ci]
2017-09-24 10:31:07 -07:00
James Lu
d0fbfcd2d8 Demote ratbox to tier 3 [skip ci]
I don't know of any network actually using PyLink with this IRCd.
2017-09-24 00:04:36 -07:00
James Lu
91e7d4f47a README: bump juno version [skip ci] 2017-09-23 23:53:17 -07:00
James Lu
4d6f80f58e README: update IRCd notes [skip ci]
- Add notes for beware-ircd
- Update notes for InspIRCd
2017-09-23 23:50:25 -07:00
James Lu
e25f6fd470 ircs2s_common: expand PUIDs in squit()
This fixes SQUIT not working correctly on ngIRCd.
2017-09-23 23:15:29 -07:00
James Lu
f74b34e99b ts6: fix wrong prefix char for +a on ChatIRCd
This fixes users with +a disappearing when bursted to ChatIRCd.
2017-09-23 22:43:27 -07:00
James Lu
8443de4701 servermaps: display hopcount in 'map' for local servers 2017-09-23 22:36:49 -07:00
James Lu
ad32ce20da protocols: send outgoing hop counts for servers and users
Closes #527.
2017-09-23 22:36:31 -07:00
James Lu
663bfe462c classes: track hopcount in Server 2017-09-23 22:35:55 -07:00
James Lu
76a0eb78e3 clientbot: fix possible TypeError in squit() 2017-09-23 21:54:42 -07:00
James Lu
a2a32ed32f clientbot: wrap outgoing modes to prevent cutoff 2017-09-23 21:54:02 -07:00
James Lu
6e89dbed24 clientbot: implement ban list enumeration on JOIN
Closes #530.
2017-09-23 21:26:13 -07:00
James Lu
b52082ed05 relay: various cleanup
- Raise desync-related state checks to warning
- Rename get_remote_sid to get_relay_server_sid - it's less ambiguous
- Clarify and add some missing function docstrings
2017-09-23 20:43:21 -07:00
James Lu
6cbb6617ef relay: don't forward simple bans as text to clientbot links if modesync is on
Closes #528.
2017-09-23 14:39:26 -07:00
James Lu
a60e6e7f22 relay: add missing comment to last commit 2017-09-23 14:09:28 -07:00
James Lu
b667bed1e6 relay: only allow one thread to run initialize_channel at a time
Closes #529.
2017-09-23 13:58:15 -07:00
James Lu
113bfcba9d PyLinkNetworkCore: copy world.hooks before iterating
This fixes a race condition where the order of PRIVMSG handlers could be changed as 'load <plugin>' runs, causing the load command to be processed multiple times.
2017-09-23 13:39:43 -07:00
James Lu
2535aa145f corecommands: fix unloading plugins that define hooks
This fixes a regression from 5e92aefcd49a7a4944d9b6c308cc7ae482a216b6.
2017-09-23 13:39:13 -07:00
James Lu
42d62fe28a p10: pass IPv6 IPs on to supported servers
Closes #254.
2017-09-23 13:20:58 -07:00
James Lu
ae02a9ba4f README: reword branch notes to be more neutral
[ci skip]
2017-09-20 20:54:18 -07:00
James Lu
746c6783da PyLink 1.2.1 2017-09-19 21:15:48 -07:00
James Lu
0e45fbdf55 Revert "faq: add a note regarding #497 (bans and modes blocking clientbot from relaying)"
This reverts commit 7ae22dc848a3dc4988daa67fc1cd095dc5511c49.
2017-09-19 21:15:48 -07:00
James Lu
594e8ad771 p10: fix wrong hook name for user introduction
(cherry picked from commit 6dec4bd96fde8b6c0d7285680768dfd427180622)
2017-09-19 21:06:14 -07:00
James Lu
6dec4bd96f p10: fix wrong hook name for user introduction 2017-09-17 13:59:42 -07:00
James Lu
0c50091d11 Merge branch 'master' into devel 2017-09-08 19:12:17 -07:00
James Lu
49136d5abd core: raise better errors on common logging block syntax mistakes
These are commonly reported and include:
- Commenting out the contents of logging:channels without commenting out the "channels:" heading, causing that block to become None.
- Commenting out headers like "filerotation:", causing its body to become pairs in logging:files or something similar.
- Leaving logging:channels:<netname> empty: this causes it to become None, so using get() on it fails.
2017-09-08 19:07:03 -07:00
James Lu
499fe319aa permissions-api: fix markdown syntax [skip ci] 2017-09-08 19:02:51 -07:00
James Lu
4cf7b36b7b permissions-api: clarify how globs are processed
[skip ci]
2017-09-08 19:02:03 -07:00
James Lu
b9a4010acc Merge branch 'master' into devel
Conflicts:
	plugins/relay.py
2017-09-05 21:22:16 -07:00
James Lu
93704d85bf unreal: fix TypeError when a set is given for modes in mode() 2017-09-05 21:21:16 -07:00
James Lu
ae06484aea exttargets: tweak docstring syntax for better readability 2017-09-05 19:19:44 -07:00
James Lu
d3892a85be Add $service exttarget for matching service bots 2017-09-05 19:19:01 -07:00
James Lu
149fdde92f README: drop Debian repo instructions
There seems to be little user interest in these builds, and they take too much maintenance effort (3 different dists) to be worthwhile.
2017-09-05 19:11:37 -07:00
James Lu
f3e82cc15b Concisify match_host CIDR logging again
We're replacing the glob, not the target's host.

(cherry picked from commit 7aa836efa68962c5202a8e6346785e2ea60d2003)
2017-09-05 19:03:18 -07:00
James Lu
d1f8358159 Fix wrong logging for CIDR ranges in match_host
(cherry picked from commit 761d3ef500db4991c2546df89eda35ba67c243ea)
2017-09-05 19:03:18 -07:00
James Lu
90884924a8 relay: remove a useless logging line
(cherry picked from commit 3b091f9e204ef17e6f4a63245f8f251fe982f877)
2017-09-05 19:03:17 -07:00
James Lu
f27b179211 Merge branch 'master' into devel
Conflicts:
	plugins/automode.py
	protocols/clientbot.py
2017-09-05 18:57:24 -07:00
James Lu
7188081511 networks: throw a proper error in 'remote' if the remote network isn't connected 2017-09-05 18:55:19 -07:00
James Lu
8420587318 clientbot: warn when an outgoing message is blocked
Closes #497.

(backported from commit 5112fcd7d105388b324e2fce6b18d022f84b8ab8)
2017-09-05 18:53:33 -07:00
James Lu
f4c51cde00 automode: don't send empty mode lines if no users match the ACL 2017-09-05 18:36:37 -07:00
James Lu
0a72519155 exttargets.md: bump "as of" PyLink version
[skip ci]
2017-09-05 06:30:23 -07:00
James Lu
5112fcd7d1 clientbot: warn when an outgoing message is blocked
Closes #497.
2017-09-02 21:46:35 -07:00
James Lu
0136ac9e41 relay: fix potential irc.channels KeyErrors in get_prefix_modes and handle_join 2017-09-02 21:35:21 -07:00
James Lu
afd4558531 relay: bind handle_messages at a higher priority than fantasy
This fixes #123, where responses for fantasy commands are relayed before the original message if the Fantasy plugin is loaded before Relay.
2017-09-02 21:17:54 -07:00
James Lu
5e92aefcd4 Implement priorities in utils.add_hook()
This changes world.hooks to store lists of tuples indicating (priority, hook_func).
2017-09-02 21:17:50 -07:00
James Lu
0ea35dab18 Merge branch 'master' into devel
Conflicts:
	docs/technical/channel-modes.csv
	docs/technical/user-modes.csv
	protocols/hybrid.py
2017-08-31 15:18:38 -07:00
James Lu
0eb605219b conf: use splitext() to fetch the config name
This fixes incorrect behaviour when there are multiple .'s in the config filename (e.g. '1.2.3.yml' got truncated to confname='1')
2017-08-31 15:14:47 -07:00
James Lu
1f270c985f hybrid: drop EX and IE from required capabilities
This fixes compatibility with hybrid trunk, commit ircd-hybrid/ircd-hybrid@981c61e36c

(cherry picked from commit 30dc4a2b27d6b6e6d68b0d154fbafd737207596e)

Conflicts:
	protocols/hybrid.py
2017-08-31 15:07:04 -07:00
James Lu
a03214514c hybrid, ts6, ratbox: fix +p mode definitions properly
+p is noknock and rfc1459-style private on all 3 IRCds, though the latter bit is undocumented in /help cmodes (maybe it's assumed?)
+p is also "paranoia" on hybrid, which adds more restrictions to halfops and /invite

(cherry picked from commit c107f0062ffb25a54d52946a26725940865318bb)
2017-08-31 15:06:25 -07:00
James Lu
32130a7988 channel/user-modes.csv: add RFC1459 & ngIRCd columns
(cherry picked from commit 0a56ab662bbb01ecc23f4de8dae955333a8b8eb4)
2017-08-31 15:06:25 -07:00
James Lu
dd5a0c4892 ts6: fallback realhost to host, not None
(cherry picked from commit dfa90378dfd056ade41c18ba213cd8408fc237b6)
2017-08-31 14:57:28 -07:00
James Lu
74ae6fd7c0 p10: add support for nefarious2 extbans
Closes #524.
2017-08-31 14:52:03 -07:00
James Lu
7fcefa41af example-conf: remove wrong advice regarding P10 extended_accounts
Clearly I misread readme.features, oops.

(cherry picked from commit 7f112e3c661ff19c1317df84228eb9ab7489ed41)
2017-08-31 14:35:32 -07:00
James Lu
7f112e3c66 example-conf: remove wrong advice regarding P10 extended_accounts
Clearly I misread readme.features, oops.
2017-08-31 14:33:23 -07:00
James Lu
5c14a9c8c2 relay: add iterate_all_present to condense network iteration code
Closes #471.
2017-08-31 14:19:30 -07:00
James Lu
2d2b524a63 ngircd: disable slash-in-nicks
This breaks user mode changes, as it is one of the only commands to undergo nick validation when received from a remote server.
2017-08-31 14:09:04 -07:00
James Lu
5c981c83b1 core: abort autoconnect and socket connectons when the daemon is shutting down 2017-08-31 13:40:11 -07:00
James Lu
82a7b914b6 Move control.tried_shutdown to world.shutting_down 2017-08-31 13:36:50 -07:00
James Lu
19bd3ec0b2 Merge branch 'wip/rework-endburst' into devel 2017-08-31 13:27:54 -07:00
James Lu
9e7af9ac3d ServiceBot: migrate to irc.is_channel 2017-08-31 13:19:34 -07:00
James Lu
ac89f45683 ngircd: rework NJOIN code
- Fix "Internal NJOIN error"s caused by joining users already in the channel again
- Fix NJOIN being sent from the wrong internal server
- Condense two iterations over the user list into one
2017-08-31 13:17:28 -07:00
James Lu
450718cce6 relay: don't block on client spawning
Just fail instantly if the remote isn't ready.
2017-08-31 12:27:52 -07:00
James Lu
e02393c22b Merge branch 'devel' into wip/rework-endburst 2017-08-31 12:20:58 -07:00
James Lu
85a7dd3dff example, relay_cb: migrate to irc.is_channel 2017-08-30 21:22:19 -07:00
James Lu
36d6581bba automode, bots: migrate to irc.is_channel 2017-08-30 21:20:19 -07:00
James Lu
979d5a48f1 relay: migrate away from deprecated utils.isHostmask 2017-08-30 21:09:19 -07:00
James Lu
9380336948 relay: fall back to the current time on TS-less servers if the remote channel doesn't exist 2017-08-30 21:08:54 -07:00
James Lu
136e5fbee7 conf: fix getDatabaseName calling the wrong variable name
(cherry picked from commit 21b8b51cba98f0f8e8208748758f4d00347374d4)
2017-08-30 20:13:51 -07:00
James Lu
94e05a6233 services_support: fix clientbot service spawning when irc.pseudoclient exists but isn't in the user index 2017-08-30 19:50:25 -07:00
James Lu
bc48709595 PyLinkNetworkCore: fix extraneous warnings in get_service_bot 2017-08-30 19:48:46 -07:00
James Lu
8170e777e8 protocols: move setting irc.connected to endburst 2017-08-30 19:39:57 -07:00
James Lu
cad55097f1 core: reuse existing service client UIDs for all service bots
This prevents nick collision wars caused by spawn_service when an ENDBURST hook for the uplink is received multiple times.
2017-08-30 19:29:46 -07:00
James Lu
21b8b51cba conf: fix getDatabaseName calling the wrong variable name 2017-08-30 19:21:43 -07:00
James Lu
9a84dbde71 protocols: consistently track ENDBURST on sub-servers too 2017-08-30 19:18:39 -07:00
James Lu
87639ddeb2 classes: add a has_eob attribute to each server object 2017-08-30 19:16:54 -07:00
James Lu
5084cc2c69 pmodule-spec: rewording, fix headings 2017-08-30 01:29:09 -07:00
James Lu
46e9975bd5 Continue rewriting the protocol module spec (#478) 2017-08-30 01:26:35 -07:00
James Lu
4a363aee50 Move _expandPUID into PyLinkNetworkCoreWithUtils 2017-08-30 00:56:18 -07:00
James Lu
5b941daf4d Begin rewriting the protocol spec for PyLink 2.0 (#478) 2017-08-30 00:34:42 -07:00
James Lu
3922d44173 utils: rename remaining functions to snake case (#476) 2017-08-28 20:28:10 -07:00
James Lu
43b6566aa8 Move getDatabaseName from utils to conf (#476) 2017-08-28 20:27:39 -07:00
James Lu
ff8587736f fantasy, relay: migrate to irc.is_* 2017-08-28 20:14:14 -07:00
James Lu
d79f1766b6 classes, coremods: migrate to irc.is_* 2017-08-28 20:13:25 -07:00
James Lu
c4a3baca7d protocols: switch to self.is* 2017-08-28 20:07:36 -07:00
James Lu
e39b4e9c69 Move utils.is* methods into PyLinkNetworkCoreWithUtils (#476) 2017-08-28 20:07:31 -07:00
James Lu
2a7594e56e Move PUIDGenerator->classes, IncrementalUIDGenerator->ircs2s_common (#476) 2017-08-28 19:42:24 -07:00
James Lu
b1159400f1 Move DeprecatedAttributesObject, CamelCaseToSnakeCase to structures (#476) 2017-08-28 19:36:55 -07:00
James Lu
0907f05296 Condense (deep)copy definitions into structures.CopyWrapper 2017-08-25 17:05:53 -07:00
James Lu
8c0f19422f core: Add irc argument to User and Server classes
Also, add a __deepcopy__ override to channel because we cannot clone IRCNetwork objects (locks cannot be pickled).
2017-08-25 13:53:45 -07:00
James Lu
96a202acce core: make User.channels use IRCCaseInsensitiveSet
Closes #515. This is an API breaking change!
2017-08-25 13:26:34 -07:00
James Lu
2700e42ebf structures: rework classes & implement (IRC)CaseInsensitiveSet 2017-08-25 13:21:02 -07:00
James Lu
1cdb5fc025 hybrid, ratbox: remove extban definitions 2017-08-25 12:24:09 -07:00
James Lu
ba649fb8b4 utils, exttargets: add checks for channel presence 2017-08-25 02:31:26 -07:00
James Lu
1031aaa320 relay: add checks for channel presence 2017-08-25 02:29:10 -07:00
James Lu
cdb575236e inspircd: remove a useless and misformatted debug line 2017-08-25 02:13:15 -07:00
James Lu
80766e051e core: define two (joined) versions of the channels index
Closes #509.

PyLinkNetworkCore.channels is split into the following:
- irc._channels which implicitly creates channels on access (mostly used in protocol modules)
- irc.channels which does not (recommended for use by plugins)
2017-08-25 02:12:08 -07:00
James Lu
f34198647e structures: support a 'data' keyword argument in (IRC)CaseInsensitiveDict 2017-08-25 02:07:54 -07:00
James Lu
a02fa45d96 clientbot: use existing fallback hostname abstraction instead of hardcoding it separately 2017-08-24 01:12:45 -07:00
James Lu
7230aaa7df Add extbans docs (#498) 2017-08-24 01:07:50 -07:00
James Lu
b214a8f4c0 unreal: fix wrong case in opertype extban 2017-08-24 00:55:55 -07:00
James Lu
1408622694 ts6: add definitions for inverted extbans as well 2017-08-23 23:23:28 -07:00
James Lu
579bfecdb4 relay: improve logging related to extban handling 2017-08-23 23:23:02 -07:00
James Lu
11d63e19cd relay: allow adding back multiple extban prefixes (#498)
This fixes inbound relaying of modes such as +q $a:test, which are eventually converted into double extbans on InspIRCd and UnrealIRCd.
2017-08-23 22:29:14 -07:00
James Lu
c6ca89b48a Fill in the rest of the extbans list for inspircd & unreal (#498) 2017-08-23 21:47:43 -07:00
James Lu
de1a6379df Add (experimental) support for matching extbans (#498) 2017-08-23 21:18:57 -07:00
James Lu
903f86a342 clientbot: add extended-join support (#290) 2017-08-23 19:28:10 -07:00
James Lu
abdc67e0c1 clientbot: track numeric 900 (RPL_LOGGEDIN) and set our own account name
This is mostly for completeness.
2017-08-23 19:19:53 -07:00
James Lu
c9f10796ee clientbot: move services account setting bits into a shared function 2017-08-23 19:16:20 -07:00
James Lu
c2fc9080cc clientbot: don't send JOIN hooks for the bot itself when receiving JOIN
Closes #519.
2017-08-23 19:11:15 -07:00
James Lu
96c89b13b1 opercmds: send KILL hook payloads from the calling user
This is so that Relay's kill warnings, etc. can be actually be routed back to the sender.
2017-08-22 23:55:53 -07:00
James Lu
bd6272abf6 clientbot: add a dummy internal host for the clientbot bot... 2017-08-21 23:28:10 -07:00
James Lu
060a947798 clientbot: whoops, fix wrong arg count for WHOX services acc handling 2017-08-21 23:27:47 -07:00
James Lu
29bb4c3dfd relay_clientbot: bump default startup delay to 20 seconds 2017-08-21 23:16:38 -07:00
James Lu
f5f30c118a structures: add collections.abc import (py3.4 support) 2017-08-21 23:15:38 -07:00
James Lu
83183b366a exec: import all of pylinkirc for convenience 2017-08-21 23:12:42 -07:00
James Lu
62e4e66321 example-conf: dcument the altnicks option on clientbot 2017-08-21 23:12:22 -07:00
James Lu
0bb4a35c6f Support pre-auth irc.pseudoclient enumeration & configurable altnicks
Closes #516. Closes #288.
2017-08-21 23:05:56 -07:00
James Lu
04cfa9c93e ServiceBot: add altnick fetching capabilities to get_nick (#288) 2017-08-21 22:22:01 -07:00
James Lu
d28006ae62 Move ProtocolError to utils, and link the copy in classes to it 2017-08-21 22:21:09 -07:00
James Lu
89699051d5 Services API rework
- Move nick/ident/host/gecos fetching from services_support into functions
- Remove the unused 'ident' argument from ServiceBot
- Rename the 'nick' argument in ServiceBot to 'default_nick'
- Define default nicks for the PyLink, Automode, and Games services
2017-08-21 21:51:45 -07:00
James Lu
3e150d8514 Break up classes.ChannelState into structures.(IRC)CaseInsensitiveDict 2017-08-21 21:50:44 -07:00
James Lu
6981869c06 clientbot: split up join() and handle_join() to prevent duplicate JOIN/MODE/WHO on connect 2017-08-20 19:24:46 -07:00
James Lu
81f3112d01 clientbot: use WHOX where available to fetch account names on join 2017-08-20 18:49:07 -07:00
James Lu
55f50eb251 clientbot: don't send /NAMES on join, it isn't needed 2017-08-20 17:29:03 -07:00
James Lu
85f283c5f8 clientbot: add support for IRCv3.2 account-tag (#290) 2017-08-20 16:52:23 -07:00
James Lu
48c6765411 clientbot: add support for IRCv3.1 account-notify (#290) 2017-08-20 16:12:25 -07:00
James Lu
0bbe5d3a1a clientbot: add support for IRCv3 CHGHOST (#290) 2017-08-19 23:55:41 -07:00
James Lu
36e8ddd97e Remove log/ from source folder, it's not necessary 2017-08-19 23:23:41 -07:00
James Lu
766450afd3 clientbot: add experimental userhost-in-names support (#290) 2017-08-19 23:23:19 -07:00
James Lu
471733bfd0 clientbot: track /who received status by user, not by channel 2017-08-19 22:29:08 -07:00
James Lu
72145e09b8 clientbot: implement away-notify support (#290) 2017-08-19 22:04:24 -07:00
James Lu
ffc734d8e2 relay: only send RAW_MODES payloads to networks without can-spawn-clients 2017-08-18 15:51:14 -07:00
James Lu
15be760b19 relay: send RELAY_RAW_MODE payloads to the *remote* channel name 2017-08-18 13:30:17 -07:00
James Lu
8563556850 relay: fix relaying between channels not matching their lowercase (DB) name
This rewrites get_relay() to be case insensitive by taking the IRC object instead of a string name.
This fixes a regression introduced by 32249ac (case-insensitive channel state)
2017-08-18 12:42:47 -07:00
James Lu
70fbbb206e Merge branch 'master' into devel
Conflicts:
	VERSION
2017-08-16 23:51:31 -07:00
James Lu
d273941dc8 launcher: add a -R/--rehash option 2017-08-16 12:28:40 -07:00
James Lu
5526dcedca launcher: add --restart/-r and --stop/-s options
Closes #244.
2017-08-16 12:22:43 -07:00
James Lu
d818c17072 control: don't spew "Stopping plugins" notices if none are loaded 2017-08-16 12:17:19 -07:00
James Lu
3b6c1e56c4 launcher: move reset_permissions() call into the permissions module 2017-08-16 11:21:06 -07:00
James Lu
d03924ae82 launcher: add checks for stale PID files via psutil on Unix
This hasn't been tested on other systems, so it is disabled there.

Closes #512.
2017-08-16 11:12:20 -07:00
James Lu
361a3de9dd faq: stop advertising PyLink as cross platform
Windows support has not been tested for a long time, and there doesn't seem to be much developer/user interest in this.
2017-08-16 10:51:17 -07:00
James Lu
7aa836efa6 Concisify match_host CIDR logging again
We're replacing the glob, not the target's host.
2017-08-16 00:03:57 -07:00
James Lu
761d3ef500 Fix wrong logging for CIDR ranges in match_host 2017-08-15 21:26:18 -07:00
James Lu
394057c7a1 PyLink 1.2.0 2017-08-14 17:34:20 -07:00
James Lu
d679859d7d relay: explain/comment the code for extban handling 2017-08-12 17:12:56 -07:00
James Lu
e180f9fa56 docs/modelists: add in chatircd +qah 2017-08-12 13:58:57 -07:00
James Lu
d2466dd33c relay: support relaying channel mode changes as text
Closes #389.

This adds a new internal hook RELAY_RAW_MODE, which is called once on every relayed network but with the mode list from the source network.
2017-08-11 19:22:14 -07:00
James Lu
92dae6db5e docs/t: link to html versions of mode lists 2017-08-11 15:19:46 -07:00
James Lu
4c0d765a2f docs: revamp mode list docs
- HTML version now available via a .csv -> .html generator script
- Addded a column for ChatIRCd
2017-08-11 15:14:55 -07:00
James Lu
30dc4a2b27 hybrid: drop EX and IE from required capabilities
This fixes compatibility with hybrid trunk, commit ircd-hybrid/ircd-hybrid@981c61e36c
2017-08-11 15:09:53 -07:00
James Lu
c107f0062f hybrid, ts6, ratbox: fix +p mode definitions properly
+p is noknock and rfc1459-style private on all 3 IRCds, though the latter bit is undocumented in /help cmodes (maybe it's assumed?)
+p is also "paranoia" on hybrid, which adds more restrictions to halfops and /invite
2017-08-11 13:45:40 -07:00
James Lu
e17837cbb1 relay: add nickflood (inspircd +F) to whitelisted cmodes 2017-08-11 13:32:04 -07:00
James Lu
5250e41a94 relay: reformat whitelisted mode lists 2017-08-11 13:29:45 -07:00
James Lu
4a6f94f8fc relay: add workarounds for u-lined servers
- Allow ulines to join with modes (this status gets forwarded as part of relay_joins()), but *ignore* this status when checking for claim
    - For U-lined clients to set modes properly and kick, they be on the claim list
- Allow ulines to set modes on ulined clients including itself. These changes are ignored: not forwarded, not bounced, but just left there.
- Ignore uline attempts to set non-list modes - this is mostly for compatibility with Anope's DEFCON, as it would otherwise lead to a mode flood.

This requires commit 9113b34b46ba6aa2b381b3c7e9822b4f411caaec, i.e. the get_server() update.
2017-08-11 13:15:03 -07:00
James Lu
9113b34b46 NetworkCore: return the server in get_server if the arg was a server 2017-08-11 13:13:50 -07:00
James Lu
046fe0c385 protocols: add headers to modules where missing 2017-08-11 12:27:58 -07:00
James Lu
8df7b5319e Move handle_realhost/handle_login to ts6 2017-08-11 12:21:41 -07:00
James Lu
dfa90378df ts6: fallback realhost to host, not None 2017-08-11 12:19:23 -07:00
James Lu
785fc8d2d2 ts6: handle ChatIRCd ENCAP USERMODE
Per https://github.com/ChatLounge/ChatIRCd/blob/master/doc/technical/ChatIRCd-extra.txt
2017-08-11 12:15:05 -07:00
James Lu
4ec1727888 unreal: rewrite/condense usermode change handling to be more consistent
Consistently call _check_oper_status_change() and _check_cloak_change() through a _handle_umode() wrapper. Also, handle the real portion of the mode change given in SVS2MODE.
2017-08-11 12:03:56 -07:00
James Lu
e13f2fdbb0 unreal: remove obsolete note regaring server-sourced notices 2017-08-10 19:53:16 -07:00
James Lu
6a5ca6b508 Merge bits of handle_server into ts6_common 2017-08-10 19:50:32 -07:00
James Lu
d0f9a2465d Move handle_sid to ts6_common 2017-08-10 19:42:04 -07:00
James Lu
614b8b87da inspircd: rewrite handle_ping to handle one-arg PING 2017-08-09 16:55:02 -07:00
James Lu
5381e85d3c IRCS2SProtocol: fix wrong if: statement regarding user presence 2017-08-09 16:06:37 -07:00
James Lu
00f70a9432 opercmds: remove extra newline in 'massbanre' help 2017-08-08 21:28:27 -07:00
James Lu
9044d17863 Merge pull request #507 from IRC4Fun/devel
automode: use 'setacc' instead of 'set' as the command name in the 'setacc' example
2017-08-08 20:19:16 -07:00
Austin Ellis
fa0dd100e5 plugins/automode: fix SETACC example
Small fix to plugins/automode SETACC example given in help output.
2017-08-08 22:12:23 -05:00
James Lu
17ba9be238 exec.threadinfo: use case-insensitive sort 2017-08-08 00:30:47 -07:00
James Lu
486a225156 inspircd: mark endburstf() threads as daemon=True
There is no reason whatsoever for this to even potentially block shutdown.
2017-08-08 00:27:05 -07:00
James Lu
64f05bd28b inspircd: set a name on endburstf() threads 2017-08-08 00:26:32 -07:00
James Lu
8b771f6d28 exec: add 'threadinfo' command 2017-08-08 00:22:57 -07:00
James Lu
8558a4e56d stats: treat stats characters case-insensitively 2017-08-08 00:00:12 -07:00
James Lu
7d26ce4ab5 stats: log to INFO attempts to call unknown /stats 2017-08-07 23:58:30 -07:00
James Lu
29fc73193f stats: removed unused code 2017-08-07 23:55:53 -07:00
James Lu
be41f57795 PyLink 2.0-dev, now with /stats support!
Closes #131.
2017-08-07 23:52:16 -07:00
James Lu
9702030bf5 protocols: continue removing to_lower() calls on channels (#372) 2017-08-07 21:54:33 -07:00
James Lu
29458c8e47 commands, opercmds: remove explicit to_lower usage
Closes #500 - I'm going to skip relay and automode because DataStore doesn't do case normalization yet.
2017-08-07 21:47:31 -07:00
James Lu
f439267129 Allow limiting login blocks to opers & certain hosts
Closes #502.
2017-08-07 21:44:15 -07:00
James Lu
716bb6da5e opercmds: exempt service bots from masskill/massban 2017-08-07 17:24:44 -07:00
James Lu
90eee9f5cb opercmds: skip opers by default in masskill/massban 2017-08-07 17:22:56 -07:00
James Lu
a080fd253f opercmds: log massban and masskill results to INFO 2017-08-07 17:15:23 -07:00
James Lu
14bebd98e7 control: fix import loop with pylinkirc.classes 2017-08-07 17:06:56 -07:00
James Lu
1597c78089 opercmds: fix wrong counting in 'masskill' if a user gets kicked from multiple channels 2017-08-07 16:55:00 -07:00
James Lu
7266f09879 opercmds: add masskillre variant
Closes #499.
2017-08-07 16:50:15 -07:00
James Lu
8987b91845 opercmds: fix --force-kb check on masskill 2017-08-07 16:50:04 -07:00
James Lu
dd1444dcd9 corecommands: block 'identify' from being used by command proxies such as networks.remote
This would have pretty bad unintentional consequences...
2017-08-07 16:31:28 -07:00
James Lu
e0e929492e match_host: implicitly convert string masks such as "user1" to "$pylinkacc:user1"
This keeps it in line with other services packages and is way nicer to read.
2017-08-07 16:31:17 -07:00
James Lu
8059f3f7fc Allow specifying login blocks that are local to certain networks 2017-08-07 16:16:17 -07:00
James Lu
21a39de0b4 corecommands: alias 'identify' to 'login' and 'id' 2017-08-07 16:15:54 -07:00
James Lu
187ca11946 commands: show an error if 'echo' is called without a text argument. 2017-08-07 00:48:28 -07:00
James Lu
f3acb3c21d networks: support more specific 'remote' permissions by target network, service, and command
Closes #457.
2017-08-07 00:46:53 -07:00
James Lu
6e0145c3b7 networks: use nargs='+' instead of manual verification in 'networks' 2017-08-07 00:38:59 -07:00
James Lu
075b746f32 opercmds: add a note to 'remote' in 'masskill' 2017-08-07 00:37:09 -07:00
James Lu
d10da72545 opercmds: reword docs/errors and add a separate permission for --force-kb 2017-08-07 00:34:03 -07:00
James Lu
f0a82859a0 opercmds: apply claim checks to massban as well 2017-08-07 00:33:01 -07:00
James Lu
8b4e73e0ed opercmds: show failed kills in 'masskill' due to claim 2017-08-07 00:18:56 -07:00
James Lu
6dc41ca15a opercmds: join reason fields in massban/masskill properly 2017-08-07 00:18:11 -07:00
James Lu
b1b2394836 ircs2s_common: ignore PART for channels that the user wasn't on
These extra parts caused issues with relay when receiving P10 KICK acknowledgements as they are treated as if the user was already in the channel.
2017-08-07 00:05:44 -07:00
James Lu
aca78b52cf opercmds: add masskill command (#499) 2017-08-06 23:49:09 -07:00
James Lu
5a00a632d6 Remove core-structure doc, outdated and not really useful 2017-08-06 23:13:20 -07:00
James Lu
a070ec5c32 PyLinkNetworkCore: add stubs for disconnect() and connect() 2017-08-06 21:49:52 -07:00
James Lu
d3f635901b classes: reorganize methods in NetworkCore 2017-08-06 21:49:41 -07:00
James Lu
ea70d34b28 docs/exttargets: refresh for 2.0 2017-08-06 21:42:09 -07:00
James Lu
2ff0007e56 exttargets: add $realname target 2017-08-06 21:41:44 -07:00
James Lu
9e97dd0b75 opercmds: rename hook payloads to be more consistent 2017-08-06 20:03:20 -07:00
James Lu
a72f710a69 Add regex variants to checkban and massban 2017-08-06 20:02:20 -07:00
James Lu
d12f12ae22 Add a 'massban' command
Closes #174.
2017-08-06 19:21:55 -07:00
James Lu
99790bfae2 p10: remove direct usage of to_lower() for channels
Ref #372
2017-08-06 18:11:34 -07:00
James Lu
c8a9163f57 ChannelState: add __repr__ 2017-08-06 18:09:37 -07:00
James Lu
07fa53d128 protocols: remove direct usage of to_lower() for channels
Not needed as of 32249ace3e579d8dc938a359cb5c7c4019abc31b (ref #372)
2017-08-06 18:05:14 -07:00
James Lu
c9c0e0a85b ChannelState: add copy and __contains__ methods 2017-08-06 18:04:46 -07:00
James Lu
cb36e29b92 opercmds: migrate to irc.match_all() 2017-08-06 17:56:27 -07:00
James Lu
cbb3c88e11 NetworkCoreWithUtils: add new match_all() method 2017-08-06 17:55:43 -07:00
James Lu
32249ace3e Store channels case-insensitively in a new classes.ChannelState
Closes #372.
2017-08-06 17:52:52 -07:00
James Lu
3c675bb163 inspircd, unreal: support the "block nick changes" extban
This is n: and ~n: on inspircd and unreal respectively.
2017-08-06 01:47:43 -07:00
James Lu
9ae851e1fc classes: rename NetworkCore.aborted to _aborted 2017-08-05 22:16:52 -07:00
James Lu
8d15d05711 IRCNetwork: use disconnect() to kill networks if parsing a command errors
This is more standard, as aborted is solely an internal value.
2017-08-05 22:14:44 -07:00
James Lu
f4da1fc94c Merge branch 'master' into devel
Conflicts:
	VERSION
	classes.py
	docs/faq.md
	example-conf.yml
	protocols/p10.py
	pylink
	world.py
2017-08-05 22:11:22 -07:00
James Lu
b456966dd3 relay: move ban-style and whitelist checks to earlier in get_supported_cmodes 2017-08-05 22:03:58 -07:00
James Lu
ffde2c6b32 relay: add in muteban relaying on InspIRCd and UnrealIRCd 2017-08-05 22:03:51 -07:00
James Lu
7db811f2dd protocols: declare quiet extban support where applicable 2017-08-05 21:52:34 -07:00
James Lu
b9a58670ef relay: cleanup code flow in handle_mode 2017-08-05 21:13:39 -07:00
James Lu
7ae22dc848 faq: add a note regarding #497 (bans and modes blocking clientbot from relaying)
This will be fixed more completely in the future. Thanks to @Ryan-Goldstein for reporting!
2017-08-03 10:44:47 -07:00
James Lu
a0a295f7d2 Set Irc.aborted earlier in the disconnect loop
This prevents plugins from getting really confused as we remove things.
2017-08-03 10:22:57 -07:00
James Lu
0d5afd266f Irc: stop extraneous queue threads when removing from world.networkobjects 2017-08-03 10:15:29 -07:00
James Lu
d734fc3280 servprotect: bump default conf up to 10 hits/10 seconds 2017-08-03 10:10:28 -07:00
James Lu
dcc171095f example-conf: spacing & comment tweaks
(cherry picked from commit a55f60c6dc7c02dcde42b43f751e8083593faeec)

Conflicts:
	example-conf.yml
2017-08-03 09:57:15 -07:00
James Lu
a639efa93e relay: allow overriding tag_nicks per network
Closes #494.

(cherry picked from commit 1d6b692e1490e538f5c8d9c16542ff45feab1f9f)

Conflicts:
	example-conf.yml
2017-08-03 09:55:29 -07:00
James Lu
e8efbb8e83 Merge branch 'master+example-conf-updates' 2017-08-03 09:54:02 -07:00
James Lu
819aab2248 example-conf: roughly sort the plugins' order by usefulness/popularity
(cherry picked from commit b24eefc87375ff81a0218309931cc15c1aa91902)
2017-08-03 09:51:00 -07:00
James Lu
13baef08c1 example-conf: roughly sort config options by usefulness
Also, remove the example for the deprecated pylink:prefixes option.

(cherry picked from commit 1eb274342166bc11d1daa4b9880e7a3990f32df5)

Conflicts:
	example-conf.yml
2017-08-03 09:50:52 -07:00
James Lu
7df19bae5f example-conf: tweak whether some options are commented out by default or not
(cherry picked from commit dbc1e80def928528e60969739f041f01779a250b)
2017-08-03 09:49:26 -07:00
James Lu
4379ef68ef Migrate coremods.permissions to snake case 2017-08-02 22:24:23 +08:00
James Lu
f15f27168a services_support: log the reason as well when a service bot is killed 2017-08-02 22:15:19 +08:00
James Lu
d42eb82b62 bots: remove obsolete "Admin-only" tags from command help 2017-08-02 22:10:04 +08:00
James Lu
76b8932eeb bots: remove duplicate nick_to_uid call 2017-08-02 22:09:48 +08:00
James Lu
529d1d84be opercmds: remove obsolete "Oper/admin only" tags from commands
These have been long superseded by the permissions API.
2017-08-02 22:06:08 +08:00
James Lu
a55f60c6dc example-conf: spacing & comment tweaks 2017-08-02 22:02:35 +08:00
James Lu
b947afabd0 example-conf: reorder some config options 2017-08-02 22:02:23 +08:00
James Lu
1d6b692e14 relay: allow overriding tag_nicks per network
Closes #494.
2017-08-02 21:57:48 +08:00
James Lu
def1c0bfd9 opercmds: use irc.reply() instead of irc.msg(source, ...) 2017-08-02 21:48:05 +08:00
James Lu
acceb4e714 opercmds: reword checkban docs 2017-08-02 21:45:20 +08:00
James Lu
bf495a0aae opercmds.checkban: make maxresults configurable, up to a cap of 200 2017-08-02 21:44:17 +08:00
James Lu
981e6c508f Move _check_nick_collision to NetworkCoreWithUtils
This is useful for networks that emulate IRC as well, to prevent clashes between real clients and virtual ones.
2017-07-31 20:58:02 +08:00
James Lu
47f3977554 Move NetworkCore.parse_protocol_command to IRCNetwork.parse_irc_command
Also add a stub for handle_events.
2017-07-31 11:09:08 +08:00
James Lu
b9a5de16de user-modes.csv: fix wrong names for sno_serverconnects, sno_stats 2017-07-31 00:57:51 +08:00
James Lu
0a56ab662b channel/user-modes.csv: add RFC1459 & ngIRCd columns 2017-07-31 00:56:10 +08:00
James Lu
eae1425975 NetworkCore: remove nonexistent cmode +r from mode type definitions 2017-07-31 00:43:19 +08:00
James Lu
9345f2549b Merge branch 'master+faq-updates'
Sync docs/faq.md with the 2.x (devel) branch.
2017-07-31 00:34:29 +08:00
James Lu
7b281e4b04 faq: tweak wording
(cherry picked from commit 17a4bbea87686d814eb2292d0142f8a8fdf591b4)
2017-07-31 00:33:34 +08:00
James Lu
381d96552b faq: fix formatting in the connection troubleshooting guide
(cherry picked from commit f1f8f91bd7b6097c962b047b5b644b0c6bb9632b)
2017-07-31 00:33:32 +08:00
James Lu
5d000c9930 faq: add a section regarding #463 (sporadic SSL-related errors)
(cherry picked from commit 99acd06e02f8543f8d9196fa63913f57fe42a9a8)
2017-07-31 00:33:31 +08:00
James Lu
8e67017c81 faq: be slightly less excited
(cherry picked from commit cc4890184cc00ebc736697cbe5609c7e14bf386c)
2017-07-31 00:33:31 +08:00
James Lu
c9734cc0af faq: add "Relay users are missing" section
(cherry picked from commit 0d9c53a4f401f74ad9fdc369ab0c065f53d6b948)
2017-07-31 00:33:30 +08:00
James Lu
b41234ad79 faq: add a troubleshooting guide for connection failures
(cherry picked from commit bc5474a43a73a40303b6ba3a62c02825aa2a33c9)
2017-07-31 00:33:29 +08:00
James Lu
f18f7de9f7 faq: sort by subtopic
(cherry picked from commit 42ce9c83b5e6827cd1140ef4ff482248b78ccc11)
2017-07-31 00:33:28 +08:00
James Lu
c16ca14fba faq: reword "advantages over Janus" section
(cherry picked from commit 9dcc94129256e3d7f037514397fba11fc001d1e1)
2017-07-31 00:33:27 +08:00
James Lu
54dab5f107 faq: remove obsolete note about clientbot support
The answer is fairly obvious now, and it's even mentioned in the readme.

(cherry picked from commit 01dc2505e2c2aff9ca0a30a22bca63eed220287f)
2017-07-31 00:33:27 +08:00
James Lu
bed40cc10a faq: add point about maxnicklen misconfigurations and relay
(cherry picked from commit c7f300357c12e6c1aac1cf7fd7e76a42aef5f447)
2017-07-31 00:33:26 +08:00
James Lu
17a4bbea87 faq: tweak wording 2017-07-31 00:29:11 +08:00
James Lu
f1f8f91bd7 ffaq: fix formatting in the connection troubleshooting guide 2017-07-31 00:27:48 +08:00
James Lu
99acd06e02 faq: add a section regarding #463 (sporadic SSL-related errors) 2017-07-31 00:21:16 +08:00
James Lu
cc4890184c faq: be slightly less excited 2017-07-31 00:19:26 +08:00
James Lu
0d9c53a4f4 faq: add "Relay users are missing" section 2017-07-31 00:06:19 +08:00
James Lu
bc5474a43a faq: add a troubleshooting guide for connection failures 2017-07-31 00:01:07 +08:00
James Lu
b24eefc873 example-conf: roughly sort the plugins' order by usefulness/popularity 2017-07-30 23:44:27 +08:00
James Lu
42ce9c83b5 faq: sort by subtopic 2017-07-30 23:42:23 +08:00
James Lu
9dcc941292 faq: reword "advantages over Janus" section 2017-07-30 23:40:26 +08:00
James Lu
01dc2505e2 faq: remove obsolete note about clientbot support
The answer is fairly obvious now, and it's even mentioned in the readme.
2017-07-30 07:57:05 -05:00
James Lu
c7f300357c faq: add point about maxnicklen misconfigurations and relay 2017-07-30 07:54:58 -05:00
James Lu
02bd2035e4 opercmds: refactor checkban to use IRCParser, and implement --channel
--channel is really just a wrapper around the $and and $channel exttargets, but it exists separately for convenience. Closes #491.
2017-07-30 01:12:02 +08:00
James Lu
9b271efc89 opercmds: use %r instead of '%s' in formatting 2017-07-30 00:37:27 +08:00
James Lu
3eb2c6bbf7 service_support: raise KILLs to service bots to INFO 2017-07-30 00:32:59 +08:00
Ken Spencer
79d66ad94d .gitignore: ignore .idea/ due to PyCharm (#492)
* PyCharm keeps state inside of .idea/ which may contain sensitive information
2017-07-24 23:21:13 +08:00
James Lu
f53969a4b2 launcher: reword -n description to mention that it skips PID file checking as well 2017-07-20 21:39:36 +08:00
James Lu
9bec181cec launcher: restore -c/--check-pid as a no-op option for compatiblity 2017-07-20 21:39:29 +08:00
James Lu
dac0d5b234 core: Properly track whether we should actually remove the PID file
Previously, PyLink spuriously removed PID files even if -n/--no-check-pid was set or if PID file checking caused PyLink to quit

(cherry picked from commit 91659ea992e5be9fc645f8805a09c2dffef4148e)
2017-07-20 21:33:17 +08:00
James Lu
b90da19dfa control: log the name of the PID file on shutdown
(cherry picked from commit d57e141fbce83ea5c91f292d5db805af8b515e68)
2017-07-20 21:31:54 +08:00
James Lu
498a32a525 launcher: reword "PID exists" error 2017-07-20 21:22:01 +08:00
James Lu
1630d176d0 launcher: re-enable PID file checking by default 2017-07-20 21:19:00 +08:00
James Lu
819ac4d406 Move world.log_queue to world._log_queue 2017-07-20 21:16:44 +08:00
James Lu
91659ea992 core: Properly track whether we should actually remove the PID file
Previously, PyLink spuriously removed PID files even if -n/--no-check-pid was set or if PID file checking caused PyLink to quit
2017-07-20 21:13:01 +08:00
James Lu
2113f834a3 Rework the launcher to always call the installed copy of PyLink
This should prevent cryptic errors caused by mismatched PyLink core/launcher versions.
2017-07-20 21:01:16 +08:00
James Lu
63f52ca8b0 ircs2s_common: handle 'netadmin' umodes as an oper type
Also, make the Network Service opertype take precedence over the others.
2017-07-20 20:03:32 +08:00
James Lu
d707c243f4 Refresh mailmap 2017-07-20 18:46:35 +08:00
James Lu
b4ef0d1b16 PyLink 1.2.0-rc1 2017-07-20 18:41:53 +08:00
James Lu
19d41d7298 ngircd: add GLINE support
Also add IRCd notes regarding AllowRemoteOper and modeless channels.
2017-07-17 09:12:07 -07:00
James Lu
75e286fdc7 README: remove experimental tag for ngIRCd 2017-07-17 08:43:04 -07:00
James Lu
66576daf3f README: declare ChatIRCd support 2017-07-17 08:42:46 -07:00
James Lu
22e11d7811 README: add note about shared{} blocks on ts6 ircds 2017-07-17 08:40:35 -07:00
James Lu
b1d5ca36fb hybrid: add KLINE support (#139) 2017-07-17 08:37:43 -07:00
James Lu
f56fae4bc9 unreal: implement GLINE support (#139) 2017-07-17 08:13:28 -07:00
James Lu
7e8ff51646 conf: when config loading fails, show an error /before/ quitting
This fixes a regression from 2b346e3c01c094922e3d96411370c8394f5e38d4.
2017-07-17 07:56:23 -07:00
James Lu
cc9025a080 p10: add GLINE support (#139) 2017-07-17 07:50:48 -07:00
James Lu
410ade3b60 ts6: implement KLINE support (#139) 2017-07-17 06:29:44 -07:00
James Lu
ab8a922809 inspircd: add GLINE support (#139) 2017-07-17 06:12:12 -07:00
James Lu
c7c29f35e5 core: make message cutoff optional, and disable for inspircd
Closes #490.
2017-07-16 21:01:55 -07:00
James Lu
1f377adfee p10: also acknowledge our own kicks with a PART
(adapted from commit 1438f9e566a8f1778a448fd3fb3d1fc9da90a26b)
2017-07-16 07:34:45 -07:00
Mitchell Cooper
d1b321310e opercmds: add chghost, chgident, chgname commands (#488)
Closes #481.
2017-07-15 07:41:16 +08:00
James Lu
5a5a98c4ef services_support: migrate to conf.conf['pylink'], make the realname option optional 2017-07-14 05:51:40 -07:00
James Lu
06d3de354e utils: migrate to conf.conf['pylink'] 2017-07-14 05:51:29 -07:00
James Lu
b88830ba25 classes: migrate to conf.conf['pylink'] 2017-07-14 05:50:19 -07:00
James Lu
a164924ce5 Merge branch 'conf-host' of https://github.com/cooper/PyLink into devel 2017-07-14 05:38:45 -07:00
James Lu
880d0975db Merge branch 'type-to-isinstance' of https://github.com/cooper/PyLink into devel 2017-07-14 05:22:37 -07:00
James Lu
716ffd389b classes: mark reply_lock, init_vars as private 2017-07-14 05:22:05 -07:00
James Lu
fa4d831e44 NetworkCore: remove outdated, now misleading constructor description 2017-07-12 23:16:03 -07:00
James Lu
2e7fed84c1 IRCNetwork: mark connection_thread, pingTimer, socket, and queue as private 2017-07-12 22:56:30 -07:00
James Lu
2ef7df01e7 IRCNetwork: don't try to close the socket if none was ever initialized 2017-07-12 22:50:16 -07:00
James Lu
3cc6ea1e88 corecommands: remove duplicate error logging when REHASH fails 2017-07-12 22:44:51 -07:00
James Lu
2b346e3c01 conf: use Python logging when avaiable when the config file fails to load 2017-07-12 22:44:51 -07:00
James Lu
db778debb8 Fix error logging for validate_server_conf (#472) 2017-07-12 22:44:48 -07:00
James Lu
fceb2efce4 conf: remove ancient migration note from PyLink 0.9.x 2017-07-12 22:24:59 -07:00
James Lu
69bafedcca IRCNetwork: use a less confusing error than "No data received" 2017-07-12 22:22:08 -07:00
James Lu
d57e141fbc control: log the name of the PID file on shutdown 2017-07-12 22:22:08 -07:00
James Lu
561319bc57 networks: explicitly log successful network disconnects 2017-07-12 22:22:08 -07:00
James Lu
61db5d616d corecommands: use irc.get_hostmask instead of formatting the caller's hostname manually 2017-07-12 22:22:08 -07:00
James Lu
0e3d733a72 NetworkCore: set self.aborted to True as early as possible when launching a disconnect
This should prevent spurious "No data received" errors from popping up when using the 'disconnect' command.
2017-07-12 22:22:08 -07:00
James Lu
f85bdb3d8b IRCNetwork: suppress logging connection errors when PyLink is shutting down 2017-07-12 22:22:08 -07:00
James Lu
bb2b57f9dd control: mark signal handlers as private 2017-07-12 22:22:08 -07:00
James Lu
74f68c2176 control: mark rehash and shutdown as public 2017-07-12 22:22:08 -07:00
James Lu
22e6992770 core: consistently call die() with irc as a keyword argument 2017-07-12 22:22:08 -07:00
James Lu
b8a254167c p10: rename the 'p10_ircd' option to 'ircd', for consistency with ts6 2017-07-12 22:22:05 -07:00
James Lu
3d27e4a347 ts6: add support for ChatIRCd
This depreates the "use_elemental_modes" setting on ts6 networks, and replaces it with an "ircd" option targetting charybdis, elemental-ircd, or chatircd
Closes #339.
2017-07-12 22:21:34 -07:00
James Lu
b081270aa1 ts6: tweak some misplaced mode definitions
Charybdis provides deaf as umode +D and ssl as umode +Z
2017-07-12 22:21:34 -07:00
Mitchell Cooper
a1dfa14d20 allow realname to be specified the same way 2017-07-12 18:53:02 -04:00
Mitchell Cooper
33630e8f9d allow host to be specified in service bot block or per-network 2017-07-12 18:38:26 -04:00
Mitchell Cooper
3e356180a0 use isinstance() for conf values as well 2017-07-12 17:38:31 -04:00
Mitchell Cooper
7ab0e8f105 use isinstance() instead of type() where appropriate #410 2017-07-12 17:29:34 -04:00
Mitchell Cooper
87fe7693b0 ircs2s_common: use conf.validate() instead of assertion in validate_server_conf() (#485)
Closes #472.
2017-07-12 14:17:41 -07:00
Mitchell Cooper
c92bb1e33b relay: add server-specific server_suffix (closes #462) (#484) 2017-07-12 14:10:36 -07:00
James Lu
1eb2743421 example-conf: roughly sort config options by usefulness
Also, remove the example for the deprecated pylink:prefixes option.
2017-07-12 08:37:08 -07:00
James Lu
dbc1e80def example-conf: tweak whether some options are commented out by default or not 2017-07-12 08:33:28 -07:00
James Lu
cb368439cf inspircd: bring back extended WHOIS replies via a force_whois_extensions option 2017-07-12 08:28:32 -07:00
James Lu
0e4737e59d handlers: strip away '(on $network)' portions of relay oper types if the source and target netname are the same 2017-07-12 07:57:07 -07:00
James Lu
654df0889d relay: rename checkSendKey -> _check_send_key 2017-07-12 07:51:34 -07:00
James Lu
9a2bff25ee control: migrate to new log_setup() name 2017-07-12 07:50:34 -07:00
James Lu
50e7b0ab13 Limit signon time in WHOIS replies to service bot targets
Faking signon time for relay clients, etc. is misleading...
2017-07-12 07:49:19 -07:00
James Lu
556c2efb0a Make showing startup time an option (whois_show_startup_time) 2017-07-12 07:44:18 -07:00
James Lu
adaa6021f9 handlers: use conf.conf['pylink'] 2017-07-12 07:35:51 -07:00
James Lu
6a0859f56a handlers: send PyLink's connect time via 317 (RPL_WHOISIDLE) 2017-07-12 07:32:50 -07:00
James Lu
7c5f1533a0 handlers: fix weirdly named variables 2017-07-12 07:32:40 -07:00
James Lu
3fb563bb43 inspircd: get rid of IDLE->WHOIS hack
This is unnecessary as sending 0 for both the idle time and signon time will tell the IRCd to not show anything.

...But we track our startup time, so we can actually pass that as a value (Anope does this as well)
2017-07-12 07:23:00 -07:00
James Lu
1438f9e566 p10: also acknowledge our own kicks with a PART 2017-07-11 02:36:00 -07:00
James Lu
fbaa12de71 p10: use is_halfop_plus when checking whether we need to override 2017-07-11 02:27:44 -07:00
James Lu
508253af7e relay: switch to Channel.is_op_plus() 2017-07-11 02:23:13 -07:00
James Lu
1a8dcdfa3a NetworkCore: use the new validate_server_conf name 2017-07-11 02:22:01 -07:00
James Lu
f203abdeb0 relay: move iteration over all networks into a wrapper function
First part of #471.
2017-07-10 23:09:00 -07:00
James Lu
a43076e815 relay: rename isRelayClient to is_relay_client 2017-07-10 22:38:25 -07:00
James Lu
2f87aa63e9 relay: remove double iteration when firing the PYLINK_RELAY_JOIN hook 2017-07-10 22:36:43 -07:00
James Lu
5ed4f8bf85 ServiceBot: break when trying to alias a command to itself 2017-07-10 22:18:01 -07:00
James Lu
63ce7ea407 ServiceBot: tweak display format for command aliases
Specifically: bold the command lists, add a space before, and be more specific with "alias for X".
2017-07-10 22:12:53 -07:00
James Lu
bd19468825 automode: rename internal functions to snake case 2017-07-10 22:10:55 -07:00
James Lu
4df101c40c automode: fix alias definitions 2017-07-10 22:10:47 -07:00
James Lu
79db7b2124 automode: fix $ircop exttarget name in setacc examples
(cherry picked from commit 24caf36230849709707c867e6a33c1870bc630aa)
2017-07-10 22:07:11 -07:00
James Lu
59a4ecdcb9 automode: rewrap help for SET
(cherry picked from commit 499e94e0a52c91f6bd4e143fd1d221990e4ee3da)
2017-07-10 22:07:09 -07:00
James Lu
24caf36230 automode: fix $ircop exttarget name in setacc examples 2017-07-10 22:06:41 -07:00
James Lu
499e94e0a5 automode: rewrap help for SET 2017-07-10 22:06:08 -07:00
James Lu
6437721ec9 Merge branch 'hide-aliases' into devel 2017-07-10 22:00:29 -07:00
James Lu
bf24bac9c9 ServiceBot: replace 'alias' option with 'aliases' & condense multiple add_cmds calls into one 2017-07-10 21:59:29 -07:00
Mitchell Cooper
f0379d79ef mention that command is an alias or has aliases available in help command 2017-07-09 21:40:08 -04:00
Mitchell Cooper
20abac7461 hide aliases from command list 2017-07-09 21:23:52 -04:00
Mitchell Cooper
100089f6b8 add alias argument where appropriate in plugins 2017-07-09 21:19:08 -04:00
Mitchell Cooper
2299204efa add alias argument to add_cmd() 2017-07-09 21:18:45 -04:00
James Lu
b7466327db protocols: move S2S_BUFSIZE definition into a class variable 2017-07-07 20:14:26 -07:00
James Lu
1172ca7387 IRCNetwork: use \r\n as separator instead of \n
CRLF is the standard way of doing this per RFC1459
2017-07-07 20:14:26 -07:00
James Lu
51fb269d0d IRCNetwork: don't attempt to send more than 510 bytes per message
Some IRCds like ngIRCd will SQUIT you if you try to do so, though most just ignore this kind of overflow.
2017-07-07 20:14:23 -07:00
James Lu
3a42c8e835 protocols: add _check_oper_status_change abstraction
This condenses a large chunk of the code checking for oper ups, and adds support for the servprotect/admin umodes in an IRCd-independent manner. Closes #451.
2017-07-07 20:04:21 -07:00
James Lu
3bcf0092e9 NetworkCoreWithUtils: wrap irc.to_lower in a lru_cache 2017-07-07 14:40:27 -07:00
James Lu
f7dfc38688 relay: only initialize channels that are relevant to the called network in initialize_all()
Previously, this would quite often hit channel names that simply don't exist on the target network.
2017-07-07 14:20:24 -07:00
James Lu
3b091f9e20 relay: remove a useless logging line 2017-07-07 13:43:30 -07:00
James Lu
5955d3f90f p10: expand nick!user@host only for topic setters that are clients 2017-07-07 13:37:16 -07:00
James Lu
c2f12460da unreal, p10: condense topic_burst and topic together
Closes #480.
2017-07-07 13:35:30 -07:00
James Lu
d2d76baad8 relay: more detailed logging when the spawn_* condition times out 2017-07-07 13:32:04 -07:00
James Lu
67a36b7ebe inspircd: rename _operUp to _oper_up 2017-07-07 13:27:45 -07:00
James Lu
5d5c861a93 protocols: rename check_nick_collision to _check_nick_collision 2017-07-07 03:18:40 -07:00
James Lu
c5d06b2d41 ngircd: run check_nick_collision in handle_nick, per #375 2017-07-07 03:16:56 -07:00
James Lu
bd79c71b85 unreal: fix TypeError in mode() from accessing set items by index 2017-07-07 03:16:17 -07:00
James Lu
4b69edcbd7 README: declare support for ngircd 24+ 2017-07-07 02:56:28 -07:00
James Lu
28d2f89311 ngircd: implement KILL 2017-07-07 02:52:57 -07:00
James Lu
67a414fa2a ngircd: set slash-in-hosts, slash-in-nicks, underscore-in-hosts capabilities 2017-07-07 02:46:14 -07:00
James Lu
57c86c6d25 ngircd: implement update_client() 2017-07-07 02:41:20 -07:00
James Lu
fa2c5d928a IRCS2SProtocol: fix extraneous umode based AWAY messages 2017-07-06 22:17:47 -07:00
James Lu
6636a19a2b ngircd: implement handler for METADATA 2017-07-06 22:15:56 -07:00
James Lu
f29c95152b ngircd: remove has-ts from protocol capabilities 2017-07-06 21:49:51 -07:00
James Lu
b0eb1656a5 ngircd: add a stub for KNOCK 2017-07-06 21:47:03 -07:00
James Lu
6d3d2b239d IRCS2SProtocol: ignore attempts to ping the uplink before the link is ready 2017-07-06 21:45:51 -07:00
James Lu
d149576b4e protocols: move invite() into IRCS2SProtocol 2017-07-06 21:43:53 -07:00
James Lu
085b4cacbe protocols: handle usermode-based away (i.e. ngircd +a) 2017-07-06 20:19:52 -07:00
James Lu
b2b50371ab ngircd: fix setting umodes 2017-07-06 20:19:34 -07:00
James Lu
73464e516f ngircd: fill in mode definitions
Source: https://github.com/ngircd/ngircd/blob/master/doc/Modes.txt
2017-07-06 20:12:29 -07:00
James Lu
3d0ccadb76 ngircd: sort handler functions alphabetically 2017-07-06 18:38:25 -07:00
James Lu
28313fd478 ngircd: send burst modes after NJOIN, if there are any 2017-07-06 18:29:34 -07:00
James Lu
961e8ae991 ngircd: add outgoing MODE command 2017-07-06 18:22:56 -07:00
James Lu
4cd1ed5a7b ngircd: add an outgoing sjoin() function using NJOIN 2017-07-06 18:08:46 -07:00
James Lu
45dad63d5b Move handle_mode into IRCS2SProtocol
TODO: clean up protocols/unreal to use more of this code as well
2017-07-06 17:10:03 -07:00
James Lu
faa5b729d9 docs: update protocol-modules graphic 2017-07-05 03:28:28 -07:00
James Lu
694b5018fc Move numeric() into IRCS2SProtocol 2017-07-05 02:36:34 -07:00
James Lu
56c8b90362 IRCS2SProtocol: handle both killpath-based and preformatted kill reasons
Also drop the override in protocols/inspircd, as it is no longer needed.
2017-07-05 02:26:40 -07:00
James Lu
1e5985b608 Merge remote-tracking branch 'origin/beta' into wip/ngircd
Conflicts:
	protocols/ircs2s_common.py
	protocols/ts6.py
2017-07-05 02:26:40 -07:00
James Lu
58558c89ae ngircd: ignore KILLs not meant for us
ngIRCd sends QUIT after a successful KILL, so trying to remove the target twice is erroneous and will cause a crash.

TODO: what happens if an external KILL is never responded to for whatever reason?
2017-07-05 02:17:15 -07:00
James Lu
1acd654e6e ts6: fix 'ts' value type in handle_invite 2017-07-05 02:10:24 -07:00
James Lu
69f3ae52ec ts6: fix wrong argument count when parsing INVITE ts
(cherry picked from commit 7cfc63d6edcd218da82d75e2032359b50cc9891a)
2017-07-05 02:09:55 -07:00
James Lu
d2d176b6f9 IRCS2SProtocol: fix UnboundLocalError in "message coming from wrong way" warning
This fixes a regression from 69cf21c04e7fed2e3c3eff27e012f1b76e6a174b.
2017-07-05 02:08:41 -07:00
James Lu
30b9f47023 unreal: remove handle_kill override; unneeded as of aa4e9335aa7dac5884b8662fac49713c7dc221cc 2017-07-05 01:26:45 -07:00
James Lu
c2e65ff9c3 IRCCommonProtocol: alias topic_burst to topic by default (#480) 2017-07-05 00:56:34 -07:00
James Lu
db06ff4338 Move handle_topic to IRCS2SProtocol 2017-07-05 00:48:58 -07:00
James Lu
aa4e9335aa IRCS2SProtocol: expand nicks to UIDs in handle_kill
This allows this handler to work natively on ngIRCd.
2017-07-05 00:36:10 -07:00
James Lu
3729b23e43 Move KICK handlers to IRCS2SProtocol 2017-07-05 00:34:48 -07:00
James Lu
449b547a23 ngircd: properly track server tokens so that users spawn on the right servers
This brings in utils.PUIDGenerator once again for pseudo-SIDs; the counter numbers in these are used directly as server tokens
2017-07-05 00:18:13 -07:00
James Lu
4e082c2bbf PUIDGenerator: allow custom counter start values 2017-07-05 00:12:25 -07:00
James Lu
163f0099e7 IRCCommonProtocol: also expand PSIDs in _expandPUID 2017-07-04 23:56:12 -07:00
James Lu
5d4f2149e6 Move squit() to ircs2s_common 2017-07-04 23:41:00 -07:00
James Lu
9132556fd9 PyLinkNetworkCore: fix __repr__ definition 2017-07-04 23:32:41 -07:00
James Lu
b780070ee6 ngircd: implement nick changing 2017-07-04 23:26:05 -07:00
James Lu
42a25300c4 ngircd: don't leave user TS none in spawn_client 2017-07-04 23:25:49 -07:00
James Lu
759210a1e4 ngircd: add inbound & outbound JOIN, SERVER 2017-07-04 23:10:12 -07:00
James Lu
84a6cec732 p10: fix endburst_delay note 2017-07-04 23:09:48 -07:00
James Lu
37f0dcb456 ngircd: fix SQUIT user tracking 2017-07-04 23:09:13 -07:00
James Lu
970b38719d core: rename ping() to _ping_uplink(), and drop the unused source/target arguments 2017-07-04 22:09:50 -07:00
James Lu
43af9d1bac protocols: move ping() into IRCCommonProtocol 2017-07-04 22:00:22 -07:00
James Lu
276b0b251d protocols: move handle_pong to IRCCommonProtocol 2017-07-04 21:55:09 -07:00
James Lu
2e5fc2467f ngircd: handle CHANINFO (channel mode/topic bursts) and NJOIN (userlist bursts) 2017-07-03 14:24:57 -07:00
James Lu
7b2f93fd4c ngircd: send an UID hook in user introductions 2017-07-03 14:24:45 -07:00
James Lu
b6b1cbeb2d ngircd: send our own server negotiation info to complete the connection
Aside from the 376, none of this is /required/, but it's best to be consistent...
2017-07-03 13:35:43 -07:00
James Lu
4cdae540b5 IRCCommonProtocol: fix type of maxnicklen 2017-07-03 13:07:38 -07:00
James Lu
66af57e74f IRCCommonProtocol: handle EXCEPTS, INVEX, NICKLEN, DEAF, CALLERID in 005 2017-07-03 13:05:47 -07:00
James Lu
06d69aadf7 clientbot: fix self.connected.set() order 2017-07-03 13:04:43 -07:00
James Lu
a5e7d76341 IRCCommonProtocol: only update the same tokens once per connection 2017-07-03 12:49:38 -07:00
James Lu
ec308acfcb protocols: move 005 handling code to IRCCommonProtocol
Also enable extended server negotiation for ngIRCd, which really just passes 005 between servers (nifty!)
2017-07-03 12:45:39 -07:00
James Lu
e9d7ac39ea ngircd: remove duplicate function 2017-07-03 12:21:44 -07:00
James Lu
091c763a0f Initial ngIRCd protocol stub 2017-07-03 00:24:26 -07:00
James Lu
8bf65f3820 ircs2s_common: implicitly expand PUIDs in _send_with_prefix 2017-07-03 00:13:17 -07:00
James Lu
78034096a8 protocols: merge _expandPUID into ircs2s_common 2017-07-03 00:11:49 -07:00
James Lu
8ddcc4d9a6 Move part, quit, message, notice, topic, _send_with_prefix, _expandPUID to ircs2s_common 2017-07-03 00:05:58 -07:00
James Lu
f163d7ddde protocols: remove extraneous "Error: " from exception messages 2017-07-02 22:52:46 -07:00
James Lu
c9272c25ce IRCS2SProtocol: skip implicit message sender fetching if the first arg starts with a : 2017-07-02 22:44:57 -07:00
James Lu
640e903dd6 Move _get_SID/_get_UID to IRCCommonProtocol 2017-07-02 22:36:47 -07:00
James Lu
bbc4dec8dd NetworkCoreWithUtils: shortcut _get_SID/UID if the target already exists 2017-07-02 22:35:39 -07:00
James Lu
e866e9eb7b NetworkCore: demote "stopping connect loop" messages to DEBUG 2017-07-02 22:26:28 -07:00
James Lu
e9a6328566 protocols: remove unnecessary handle_squit overrides 2017-07-02 22:20:52 -07:00
James Lu
d4260734dc clientbot: make sure incoming server messages don't clash with a PSID/PUID 2017-07-02 22:13:57 -07:00
James Lu
1e39fb78db clientbot: rename _validateNick -> _check_puid_collision 2017-07-02 22:11:40 -07:00
James Lu
ce2852bdc0 clientbot: get rid of _get_SID in message parsing
It isn't needed because any external server messages are sent raw.
2017-07-02 22:09:22 -07:00
James Lu
990a928602 relay: re-add 'CLAIM #channel -'
This was mistakenly removed in d51c39935118223a3e7087c821af50e52ff152fd due to a merge conflict, oops...

(cherry picked from commit 62669c085dad8b6e9ea057fe2b130535af1b0a52)
2017-07-02 22:07:58 -07:00
James Lu
62669c085d relay: re-add 'CLAIM #channel -'
This was mistakenly removed in d51c39935118223a3e7087c821af50e52ff152fd due to a merge conflict, oops...
2017-07-02 22:06:50 -07:00
James Lu
51d8d3b3b8 clientbot: store external server names raw instead of using server name mangling 2017-07-02 21:59:40 -07:00
James Lu
ee5a884328 clientbot: fix imports for PyLink 2.0 2017-07-02 21:59:29 -07:00
James Lu
883f9199ec control: move rehash signal to SIGUSR1, and shutdown on SIGHUP (terminal close)
PyLink technically isn't a daemon, so it's a bit odd to have it linger around after the controlling terminal has died.
2017-07-02 21:19:04 -07:00
James Lu
f0fab0c0ad games: remove 'fml'
This is a blocking command which can potentially freeze the server given enough network interruption.
It will likely be reintroduced later on in some sort of "Websites" plugin, possibly in the contrib repository.
2017-07-02 21:18:32 -07:00
James Lu
f800c9f7c2 Merge branch 'wip/irc-explosion-2' into devel (#475)
This brings in a major refactor of the IRC/protocol stack, to start off 2.0-dev.
2017-07-02 21:16:06 -07:00
James Lu
5158497125 Bump version to 2.0-dev 2017-07-02 21:15:15 -07:00
James Lu
2c32269b7f PyLink 1.2-beta1 2017-07-02 13:19:49 -07:00
James Lu
f2b644e2bb relay: be more verbose in 'grabbing spawnlocks' messages 2017-07-02 12:36:33 -07:00
James Lu
60788e4ba5 relay_clientbot: remove dark blue from the random colours list
It's difficult to read on clients configurated to use a dark background. Reported by @MrBenC
2017-07-01 18:06:50 -07:00
James Lu
06ef421578 classes: clean up references to deprecated classes/methods 2017-06-30 21:49:29 -07:00
James Lu
54d7fe6dc5 protocols: convert IrcServer usage to Server 2017-06-30 21:45:10 -07:00
James Lu
a204d2b2db core: convert IrcUser calls to User 2017-06-30 21:44:31 -07:00
James Lu
b81a03fda9 protocols: Channel.removeuser -> Channel.remove_user 2017-06-30 21:40:50 -07:00
James Lu
7d68c03101 various: convert sortPrefixes/getPrefixModes calls to snake case 2017-06-30 21:40:05 -07:00
James Lu
6d7e2c667d DeprecatedAttributesObject: don't clobber __ variables 2017-06-30 21:38:50 -07:00
James Lu
a73300e864 classes.Channel: migrate to snake case 2017-06-30 21:34:08 -07:00
James Lu
61ed209abb coremods, plugins: migrate to snake case for protocol communication 2017-06-30 21:30:20 -07:00
James Lu
927fa9aac9 protocols: updateClient -> update_client 2017-06-30 21:29:38 -07:00
James Lu
f38b9c9a2c protocols: topicBurst -> topic_burst 2017-06-30 21:29:11 -07:00
James Lu
d0846170c4 protocols: spawnServer -> spawn_server 2017-06-30 21:27:15 -07:00
James Lu
f60dc8fa37 protocols: spawnClient -> spawn_client 2017-06-30 21:25:58 -07:00
James Lu
ea455436c1 control: fix REHASH for 2.x protocol modules 2017-06-30 00:41:29 -07:00
James Lu
0c7fb861f1 classes, relay, ircs2s_c: tweak/remove various debug statements 2017-06-29 23:19:21 -07:00
James Lu
8e9a99f90c ServiceBot: migrate to snake case 2017-06-29 23:02:34 -07:00
James Lu
10bca676fc coremods, plugins: migrate to snake case 2017-06-29 23:01:39 -07:00
James Lu
a4e321522b protocols: migrate away from camel case 2017-06-29 22:56:14 -07:00
James Lu
3913a909ef utils: remove parseModes, applyModes wrappers
These have been deprecated since 0.8-alpha2.
2017-06-29 22:43:29 -07:00
James Lu
5647229c05 CamelCaseToSnakeCase: add deprecation warnings 2017-06-29 22:41:18 -07:00
James Lu
3f240bd9e8 p10: mark check_cloak_change as private 2017-06-29 22:21:08 -07:00
James Lu
930a7e19f1 unreal: checkCloakChange -> _check_cloak_change 2017-06-29 22:20:30 -07:00
James Lu
741528b0b3 Merge handle_invite into IRCS2SProtocol (#454) 2017-06-29 22:17:46 -07:00
James Lu
2034bfcc83 IRCS2SProtocol: sort handle_* methods alphabetically 2017-06-29 22:17:46 -07:00
James Lu
7cfc63d6ed ts6: fix wrong argument count when parsing INVITE ts 2017-06-29 22:17:46 -07:00
James Lu
d01e797219 Merge handle_part into IRCS2SProtocol (#454) 2017-06-29 22:17:43 -07:00
James Lu
58a4215690 ratbox: fix support for merged Irc/proto 2017-06-29 21:55:52 -07:00
James Lu
67347935b5 ircs2s_common: add missing ProtocolError import 2017-06-29 21:55:33 -07:00
James Lu
c9c01def8c ts6_common: continue using self.irc in TS6SIDGenerator 2017-06-29 21:51:02 -07:00
James Lu
963d5e11cc Merge 'utils: add a default to DeprecatedAttributesObject so that it works as is'
Merge commit 'ed33c8d5804387245b6b012cd1aaabaca173262a' into devel
2017-06-29 18:14:15 -07:00
James Lu
85fbc9ea9d relay_clientbot: use isinstance(obj, dict) & cleanup imports (#410) 2017-06-29 18:08:41 -07:00
James Lu
8eebcb0b06 relay_clientbot: drop colour from network names by default 2017-06-29 18:07:40 -07:00
James Lu
77357b765e clientbot: rename various private functions
* capEnd -> _do_cap_end
* requestNewCaps -> _request_ircv3_caps
* saslAuth -> _try_sasl_auth
* sendAuthChunk -> _send_auth_chunk
* parseMessageTags -> parse_message_tags
2017-06-27 22:28:31 -07:00
James Lu
310f3f23b8 protocols: rename various parse* functions (no migration stub)
Renamed to camel case:
- parseArgs -> parse_args
- parsePrefixedArgs -> parse_prefixed_args

Renamed to show that we're specifically parsing ISUPPORT data:
- parseCapabilities -> parse_isupport
- parsePrefixes -> parse_isupport_prefixes
2017-06-27 17:16:46 -07:00
James Lu
6684f9bf08 utils.CC2SC: slightly reword the "missing attribute" error 2017-06-27 16:26:53 -07:00
James Lu
91fe7e0ca7 utils.CC2SC: use self.__class__ to get the name of the current subclass
This is the intended behaviour instead of showing "CamelCaseToSnakeCase" in attribute errors.
2017-06-27 16:25:40 -07:00
James Lu
56275c5a3b NetworkCore: rename removeClient -> _remove_client (no migration stub) 2017-06-27 16:21:30 -07:00
James Lu
5e7529dae4 Move some functions back into NetworkCore
Things like is_internal_client() are specific to the way we track users, so it doesn't make much sense to override these per protocol. It can *still* be done though, but there's little point...
2017-06-27 16:17:28 -07:00
James Lu
56f1c9e919 NetworkCore: fix irc.protoname definition 2017-06-27 16:15:37 -07:00
James Lu
ad2d5a5ae0 Move ts_lock definition into PyLinkNetworkCoreWithUtils 2017-06-27 16:12:45 -07:00
James Lu
928dbf80bb Move more IRC-specific attributes into IRCNetwork.init_vars() 2017-06-27 16:05:58 -07:00
James Lu
62784a63e4 IRCNetwork: error when attempting to start multiple connection threads for a network 2017-06-27 15:58:55 -07:00
James Lu
c3cdf63253 Move some IRC-specific attributes to IRCNetwork 2017-06-27 15:58:38 -07:00
James Lu
fb34392fca IRCNetwork: mark schedule_ping, process_queue as private 2017-06-27 02:53:09 -07:00
James Lu
710a276c45 IRCNetwork: rename run() -> _run_irc(), this is a private function 2017-06-27 01:44:26 -07:00
James Lu
04c18f0bd5 docs: get rid of self.irc, self.proto, irc.proto 2017-06-25 02:10:03 -07:00
James Lu
4696519bad plugins: migrate irc.proto calls to irc 2017-06-25 02:09:59 -07:00
James Lu
748c1bc158 coremods: migrate irc.proto calls to irc 2017-06-25 02:09:55 -07:00
James Lu
7814914a05 classes, protocols: convert self.irc usage to self 2017-06-25 02:09:52 -07:00
James Lu
eef7a73ce9 classes: migrate self.proto calls to self 2017-06-25 02:09:41 -07:00
James Lu
d0209f720a Rewrite network intitialization bits
- Move protocols.connect -> protocols.post_connect to fix namespace conflict
- Starting an IRC connection is now explicit (via irc.connect instead of __init__)
2017-06-25 01:12:58 -07:00
James Lu
8acf39cad6 protocols: rename _send to _send_with_prefix to avoid clashing with process_queue 2017-06-24 23:47:30 -07:00
James Lu
df18e318a8 WIP: merge IRCNetwork and Protocol classes together
Eventually, the goal is to have both of these hotswappable with inheritance, so this distinction becomes moot.
2017-06-24 23:27:24 -07:00
James Lu
f8155ff74c protocols: sed -i 's/_getSid/_get_SID/g' 2017-06-16 17:13:30 -07:00
James Lu
a60d746e3b protocols: sed -i 's/_getUid/_get_UID/g' 2017-06-16 17:13:30 -07:00
James Lu
7ca98eb965 Split IRC-specific code from classes.Protocol into a new IRCCommonProtocol (#454) 2017-06-16 17:13:26 -07:00
James Lu
45ae1dd67e Merge ts6 and p10 handle_events, handle_privmsg into ircs2s_common (#454) 2017-06-16 17:00:22 -07:00
James Lu
37d8e8ad43 Irc: break protocol-agnostic [dis]connect code into _pre/_post functions (#371) 2017-06-16 16:53:23 -07:00
James Lu
2a978c498e Rename PyLinkIRCNetwork -> IRCNetwork
The "PyLink" prefix is sort of redundant here...
2017-06-15 21:55:08 -07:00
James Lu
c4f6d626d5 Drop Irc prefix from IrcServer/User/Channel classes 2017-06-15 21:54:40 -07:00
James Lu
d98d522387 Move Irc.runline => PyLinkNetworkCore.parse_protocol_command 2017-06-15 21:45:04 -07:00
James Lu
47e36a9249 classes: break Irc into three classes: PyLinkNetworkCore, PyLinkNetworkCoreWithUtils, PyLinkIRCNetwork (aliased to Irc) 2017-06-15 21:38:52 -07:00
James Lu
8dae235b8d Merge branch 'devel' into wip/irc-explosion 2017-06-15 21:13:49 -07:00
James Lu
28cb7168b1 Merge branch 'master' into devel
Sync README and CI configuration with master.

Conflicts:
	protocols/nefarious.py
2017-06-14 07:04:15 -07:00
James Lu
7794171d62 README: update with new nightly build repositories for Debian/Ubuntu 2017-06-06 17:30:11 -07:00
James Lu
6e3e188fd6 Merge branch 'master+travis-production' 2017-06-06 17:13:41 -07:00
James Lu
f8a4f003f1 setup.py: explicitly parse README.md as markdown_github 2017-06-06 17:12:34 -07:00
James Lu
805aa52f59 travis: Move to Ubuntu 14.04; it has a newer pandoc version which better supports GitHub flavoured markdown 2017-06-06 17:12:34 -07:00
James Lu
963e8e7180 travis: rm skip_upload_docs rule
This doesn't seem to do anything useful?
2017-06-06 17:12:27 -07:00
James Lu
41c0191cf9 setup.py: update comments and my email 2017-06-06 12:54:48 -07:00
James Lu
d4fae02540 Irc: migrate functions to camel case 2017-06-02 23:17:14 -07:00
James Lu
8f82b92a6a utils: add CamelCaseToSnakeCase class, which wraps missing attributes from camel case names to snake case 2017-06-02 23:16:51 -07:00
James Lu
ed33c8d580 utils: add a default to DeprecatedAttributesObject so that it works as is 2017-06-02 17:26:54 -07:00
James Lu
2217306ca1 p10: acknowledge incoming KICKs with a PART
Per https://github.com/evilnet/nefarious2/blob/ed12d64/doc/p10.txt#L611-L616. This fixes autorejoin-on-kick not working with prefix modes because the remote verifies whether the KICK has been acknowledged properly. Closes #465.

(backported from commit 1996b86e85aff5ba0465445574f9ff7e91b18034)
2017-06-02 09:13:52 -07:00
James Lu
f97db31533 Irc: show the current encoding setting in fullVersion() 2017-06-02 08:46:55 -07:00
James Lu
f80c5df971 control: log the start and end of rehash to INFO 2017-06-02 08:46:27 -07:00
James Lu
60a0bcdc7a Rename config option log:stdout -> log:console
Closes #386.
2017-06-02 08:42:32 -07:00
James Lu
caade5a308 log: flush the log queue AFTER setting up file loggers
This makes sure that messages sent during the config phase are logged to files, not just the console.

TODO: We should actually be extending this to log to IRC too...
2017-06-02 08:39:49 -07:00
James Lu
6e8f618f80 control: don't duplicate config validation 2017-06-02 08:34:59 -07:00
James Lu
9ea9f66dd7 conf: actually pass the logger object to _log in validateConf 2017-06-02 08:34:43 -07:00
James Lu
1996b86e85 p10: acknowledge incoming KICKs with a PART
Per https://github.com/evilnet/nefarious2/blob/ed12d64/doc/p10.txt#L611-L616. This fixes autorejoin-on-kick not working with prefix modes because the remote verifies whether the KICK has been acknowledged properly. Closes #465.
2017-06-02 08:09:19 -07:00
James Lu
1ce1f7b3e5 ircs2s_common: don't clobber the case of prefixmsg prefixes on RFC1459 networks
Fixes #464.
2017-06-02 07:56:33 -07:00
James Lu
6ef3bab0fc unreal: remove handle_privmsg/handle_notice override 2017-06-02 07:56:22 -07:00
James Lu
a32d937b91 Merge branch 'wip/protocol-cleanup' into devel 2017-06-02 07:40:08 -07:00
James Lu
3a934ef5b8 Merge branch 'wip/configurable-encoding' into devel 2017-06-02 07:32:07 -07:00
James Lu
b9aee6ae85 Irc: only apply encoding settings on connect
Changing the encoding after a connection has been established is somewhat dangerous, because it's possible to corrupt channel/user state if characters in the old encoding are no longer valid.

Also, mark this option as experimental.
2017-06-02 07:31:49 -07:00
James Lu
1ff027152a Irc: remove outdated cert/keyfile comment 2017-05-28 20:09:26 -07:00
James Lu
2737b6bbfc Irc: simplify _send() code and replace unencodable characters 2017-05-27 02:21:12 -07:00
James Lu
1246edaf2c Irc: initial work on encoding support (#101) 2017-05-27 01:27:09 -07:00
James Lu
9ec3cccaee example-conf: minor tweaks to the pylink: block
- Make the "pylink:prefixes" deprecation notice more visible
2017-05-21 20:20:03 -07:00
James Lu
297087c620 example-conf: update plugins notes
- Remove obsolete note about opercmds permissions
- Reword and rewrap most other descriptions
2017-05-21 20:15:48 -07:00
James Lu
69cf21c04e Merge ts6 and p10 handle_events, handle_privmsg into ircs2s_common 2017-05-20 19:41:19 -07:00
James Lu
76ecc60675 servprotect: only track kills and saves to PyLink clients
why wasn't this done before...
2017-05-20 15:02:04 -07:00
James Lu
ead20f5be9 Irc: log full tracebacks when disconnecting due to an error 2017-05-16 16:30:03 -07:00
James Lu
89f9b46ec0 relay: demote "PM from server" warnings to debug
InspIRCd's m_chanlog.so sends these on purpose, so it's best not to warn about this "feature" endlessly...
2017-05-15 21:32:41 -07:00
James Lu
b2643a0ac8 adv-relay-conf: add a missing $ 2017-05-15 17:22:58 -07:00
James Lu
a5b3011ea4 networks: clear the 'remote command used' state and break if overriding account/reply target fails 2017-05-12 19:58:03 -07:00
James Lu
cfec70730f clientbot: reinitialize PUID generators on connect
Closes #448.
2017-05-12 19:57:24 -07:00
James Lu
fd3236ddb7 Irc: fix another CPU loop on 'disconnect' 2017-05-12 19:52:40 -07:00
James Lu
084f58b499 automode: remove extraneous +'s from mode lists
Closes #447.
2017-05-12 19:41:55 -07:00
James Lu
04f88df385 Actually use 'irc' in main() and die() as a keyword argument (per docs) 2017-05-12 19:19:52 -07:00
James Lu
998beb51b5 control: move plugin shutdown & pidfile cleanup routines to atexit
It is possible for PyLink to shutdown indirectly by disconnecting all networks. In these cases, the shutdown routines never ran at all...
2017-05-12 19:13:02 -07:00
James Lu
5b73e0a691 service_support: fix service respawn on KILL
(cherry picked from commit 7e51d3a7f5b184e3edafdd55c5f834177fe44b3a)
2017-05-12 18:51:34 -07:00
James Lu
7e51d3a7f5 service_support: fix service respawn on KILL 2017-05-12 18:45:27 -07:00
James Lu
457325024a service_support: reuse existing internal clients for service bots if one exists
Potential fix for #458.
2017-05-12 18:27:54 -07:00
James Lu
daa6593534 Irc: block when the queue is empty instead of needlessly polling it
Rework Irc.processQueue() to block when the queue is full, and abort if the item "None" is sent to it.
To make sure that the None isn't caught by a full queue or pushed back by other elements, this modifies queue.Queue's underlying deque instance directly.

Closes #459.
2017-05-12 17:55:24 -07:00
James Lu
06d49f4433 Revert "Irc: only disconnect the write portion of the socket"
This reverts commit f4babc6f285eba7f49e538815512d15c355c6af4.
2017-05-12 17:19:16 -07:00
James Lu
1bd6149ee2 Merge branch 'devel+next' into devel 2017-05-12 16:57:32 -07:00
James Lu
7daef0000b Irc: break out of processQueue properly when a network disconnects 2017-05-09 23:31:20 -07:00
James Lu
08c0082430 protocols: rename checkCollision -> check_nick_collision() (#454) 2017-05-09 20:44:48 -07:00
James Lu
8f14cb238b p10: rename checkCloakChange -> check_cloak_change (consistency) (#454) 2017-05-09 20:36:43 -07:00
James Lu
c898da7378 p10: move command tokens dict into a class variable & drop _getCommand() 2017-05-09 20:15:23 -07:00
James Lu
701f01fa4f Irc: reword error message from last commit to be more concise 2017-05-09 18:19:35 -07:00
James Lu
bac6dc36b4 Irc: log socket.send() errors with a proper traceback 2017-05-09 18:06:51 -07:00
James Lu
4ca8667669 Irc: fix 62aea23879f61d82522bfb91c0eb4fd9d3059740 (lowercase queue) 2017-05-07 17:34:04 -07:00
James Lu
a3df47e88e ServiceBot: fix ce77f2cbd42037935365e0e087a4bcdbbcb7bf74 2017-05-07 17:33:15 -07:00
James Lu
62aea23879 Irc: fix throttle_time not actually blocking for the defined amount of time
Passing the timeout to queue.Queue.get is invalid because it'll only block if there ISN'T any text to send.
2017-05-07 17:31:31 -07:00
James Lu
ce77f2cbd4 ServiceBot: minor fix of logging syntax 2017-05-07 17:11:51 -07:00
James Lu
24b5fd92ef relay: don't error if the pylink service is gone (e.g. during shutdown) 2017-05-07 13:58:11 -07:00
James Lu
b83aba0b13 inspircd: stop ENDBURST timers when irc.aborted gets set 2017-05-07 13:56:55 -07:00
James Lu
15ed251ed7 Irc: refuse to queue send data if aborted is set 2017-05-07 13:46:25 -07:00
James Lu
de67fe0d37 Irc: log socket shutdown errors to debug 2017-05-07 13:46:15 -07:00
James Lu
f4babc6f28 Irc: only disconnect the write portion of the socket
Per https://docs.python.org/3/howto/sockets.html#disconnecting
2017-05-07 13:46:06 -07:00
James Lu
59dd8d3bf8 control: print remaining threads on forced shutdowns as well 2017-05-07 13:39:46 -07:00
James Lu
5c7752a203 relay: stop execution if spawn lock acquire fails
Also, make the lock timeout a consistent, global variable.

(partial merge of commit e24bc54bbcefc40bf73a197de2dc24f7cd42cf79)
2017-05-04 21:18:02 -07:00
James Lu
fd15600d80 docs: split exttargets info into a new page 2017-05-04 20:05:57 -07:00
James Lu
5d629f7331 matchHost: extend negation via "!" to regular hostmasks as well as exttargets 2017-05-04 19:04:03 -07:00
James Lu
d51c399351 Revert "relay: add locks in db read/writes (thread safety)"
Unfortunately, this made relay prone to freezing the entire PyLink server.

This reverts commit 2b4943a7801149f27d48cd798d7c5fe31cdcace5.
2017-05-04 18:53:07 -07:00
James Lu
1358fedca6 exttargets: add $and exttarget (#334) 2017-05-04 18:00:32 -07:00
James Lu
dbeacf9249 exttargets: add a $network target (#334) 2017-05-04 17:16:50 -07:00
James Lu
bab6a71a2e example-conf: fix reversed option description for password encryption
(cherry picked from commit e69e1f5f038deca72f2f1108818d231c4664792f)
2017-04-29 00:09:01 -07:00
James Lu
5cea0fa73a commands: remove extraneous private=True from showchan
(cherry picked from commit 79e2d20d9d113398deedba1297c07a9e65711193)
2017-04-29 00:09:01 -07:00
James Lu
e69e1f5f03 example-conf: fix reversed option description for password encryption 2017-04-29 00:08:07 -07:00
James Lu
79e2d20d9d commands: remove extraneous private=True from showchan 2017-04-27 07:28:26 -07:00
James Lu
4205694a65 PyLink 1.2-alpha1 2017-04-20 13:25:18 -07:00
James Lu
94de51ad53 README: rewrap steps in "Installing from source" 2017-04-16 01:25:50 -07:00
James Lu
f6d97374c5 README: Update Charybdis link to GitHub
http://charybdis.io/ has been dead (NXDOMAIN) for a while now...
2017-04-16 01:22:28 -07:00
James Lu
18c13c735e Merge branch 'master' into devel
Conflicts:
	VERSION
2017-04-10 15:14:48 -07:00
James Lu
2feb93aaf0 clientbot: generate PUIDs/PSIDs with the nick or server name as prefix 2017-04-09 15:32:13 -07:00
James Lu
0e6d33a668 networks.remote: suppress errors if restoring remoteirc.pseudoclient.account
This should rarely happen, but can be purposely caused by causing the remote network to disconnect through 'remote'.
2017-04-09 15:21:19 -07:00
James Lu
3f6501fa88 clientbot: make sure incoming nicks don't clash with a PUID/PSID
This should really never happen, but it might break quite a few things if it does.
2017-04-09 15:12:52 -07:00
James Lu
cc9ffd47b1 clientbot: fix misleading comment 2017-04-09 15:01:49 -07:00
James Lu
709b1d2ead commands: show TS on networks without has-ts as well 2017-04-09 14:54:16 -07:00
James Lu
bf1d7812e2 clientbot: track channel modes and TS on join
Closes #345.
2017-04-09 14:49:19 -07:00
James Lu
6d96dd21ac updateTS: remove usage of mutable as function default argument
This may subtly break things: https://docs.quantifiedcode.com/python-anti-patterns/correctness/mutable_default_value_as_argument.html
2017-04-09 14:45:12 -07:00
James Lu
539ad668cf PyLink 1.1.2 2017-04-06 15:33:28 -07:00
James Lu
e2ae091723 .travis.yml: bring PyPI deployment to production 2017-04-06 15:30:46 -07:00
James Lu
0eda9a9d95 Merge branch 'master+sasl-backport'
This merges in backported SASL fixes from 1.2-dev.
2017-04-05 23:20:15 -07:00
James Lu
22ceb3f699 clientbot: make SASL timeout configurable & raise default to 15 secs
(cherry picked from commit 9d50a4363be5ed2ca17b9bdf563017220ffba1bd)
2017-04-05 23:19:08 -07:00
James Lu
84448e9803 clientbot: time out CAP/SASL after 5 seconds
Closes #424.

(cherry picked from commit 47f0b7626f8d17b99fa4dd8cfd93a89f81bb7753)
2017-04-05 23:19:08 -07:00
James Lu
b14ea4f051 clientbot: send CAP LS before NICK/USER so that it consistently gets a response before connect
Previously, SASL was failing on networks like freenode, as the connection completed before a CAP response was received.

(cherry picked from commit 9420f21680058e50a8134eed7a050bb4abb9ae73)
2017-04-05 23:19:08 -07:00
James Lu
9d50a4363b clientbot: make SASL timeout configurable & raise default to 15 secs 2017-04-05 23:08:17 -07:00
James Lu
69c25b5954 channel-modes: document UnrealIRCd +D and +V 2017-04-02 14:04:44 -07:00
James Lu
2e5cccc381 relay: fix incrementing changes to modedelta 2017-04-01 12:50:32 -07:00
James Lu
13be8ff6d0 relay: apply modedelta rules on SJOIN as well 2017-04-01 12:50:32 -07:00
James Lu
4312983ca5 relay: initial modedelta implementation 2017-04-01 12:50:32 -07:00
James Lu
4daa94c014 Merge branch 'master' into devel 2017-04-01 12:42:00 -07:00
James Lu
df4acbf5d5 unreal: expand PUIDs in outgoing channel modes
This fixes things like relay modes / automode targets not working.
2017-04-01 12:39:38 -07:00
James Lu
8465edd5af ts6_common: fix outbound kicks to PUIDS not updating the state
This was previously trying to update the state based on the user's nick, but we use PUIDs internally. In other words, make sure that we don't replace the internal target when sending the outgoing text...
2017-04-01 12:26:08 -07:00
James Lu
fccec3a195 unreal: fix userlist parsing breaking with Unreal 3.2 nicks starting with a symbol 2017-04-01 12:10:19 -07:00
James Lu
d94bd3f28f rm pylink-contribdl, superseded by plugin loading from any folder 2017-03-31 18:57:07 -07:00
James Lu
348572bcb6 Irc: rewrite sendq to use queue.Queue, and add an upper bound (maxsendq)
Closes #430. Closes #442.
2017-03-31 17:41:56 -07:00
James Lu
ad4fe1924b networks: show an error instead of silently failing if the command is empty 2017-03-31 17:13:04 -07:00
James Lu
0749a42ef6 networks.remote: add recursion checks to prevent bad queries from crashing the server
For example, 'remote net1 remote net2 echo hi' was problematic if the source network was 'net1'.

It's a good thing this command is restricted by default...
2017-03-31 16:40:26 -07:00
James Lu
9d9b01839c Split Irc.reply() into _reply() to make 'networks.remote' actually thread-safe
Previously, the Irc.reply_lock check was in the reply() function itself: replacing it with another function checking for the same lock would delay execution,
but then run the wrong reply() code if another module used irc.reply() while 'remote' was executing.
2017-03-31 16:25:28 -07:00
James Lu
40fa4f71bc corecommands: add a 'clearqueue' command to force clear queue muckups (#441) 2017-03-28 22:39:11 -07:00
James Lu
ae6c68018b core: half the default throttle time (from 0.01 to 0.005) 2017-03-28 22:38:54 -07:00
James Lu
029bb38af8 protocols: skip queuing when responding to PING 2017-03-28 22:30:33 -07:00
James Lu
5d10ee39be ServiceBot: make displaying unknown command errors optional
Closes #441.
2017-03-28 22:18:51 -07:00
James Lu
4cd71d12ef fantasy: don't trigger when the fantasy prefix is followed by a space
This prevents false positives such as "& that", "@ person", etc.
2017-03-28 22:00:41 -07:00
James Lu
d425cb9d47 relay: fix case sensitivity in channel TS check 2017-03-28 08:08:39 -07:00
James Lu
5c8ddef60b Merge branch 'master' into devel
Conflicts:
	classes.py
2017-03-27 16:13:58 -07:00
James Lu
1d6da68963 Add Travis CI configuration
Squashed commit of the following:

commit b03e5f82405343d54015a40c1655d54f0fc5ce90
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Mon Mar 27 16:04:51 2017 -0700

    .travis: install pandoc, disable docs and email

commit 25cd4ad2a07686f7a196b151070efe3fd31a94e3
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Mon Mar 27 15:55:36 2017 -0700

    wrap python as a subprocess

commit 749177ac7b13921f8a04b023b54c1716ff0ce0b0
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Mon Mar 27 15:53:37 2017 -0700

    oops, do proper matching

commit 98f90c15fd5be91b70ecf70bdff3a005d1fedc94
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Mon Mar 27 15:52:15 2017 -0700

    i am not dealing with this shell.

commit 8b3dcb6c59617e7ca85363bf485dc295ff22a012
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Mon Mar 27 15:37:28 2017 -0700

    More .travis.yml tweaks

commit 49346bef1b7794eefd251d27e846a184c0569dad
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Mon Mar 27 15:28:30 2017 -0700

    .travis.yml: add missing directory to compileall

commit 7eb1d464481c958ef007b51234045a13f1a8c06e
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Mon Mar 27 15:23:21 2017 -0700

    Make travis do syntax verification on all modules

commit 0331356e91e22cd85859a4ebbaf1973f21db1650
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Mon Mar 27 15:19:24 2017 -0700

    Revert ".travis.yml: remove duplicate python version restriction"

    This reverts commit 51c74cedff78aa204813743e1a738e3aa63fe3c6.

commit 51c74cedff78aa204813743e1a738e3aa63fe3c6
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Wed Feb 22 16:23:14 2017 -0800

    .travis.yml: remove duplicate python version restriction

commit 04087af09a29e2f825545cdc36be55f86a7c7d10
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Wed Feb 22 16:07:51 2017 -0800

    Test travis-ci deployment
2017-03-27 16:13:05 -07:00
James Lu
657ed82958 launcher: catch all errors when loading plugins, not just ImportError / OSError 2017-03-27 15:09:27 -07:00
James Lu
ff0eda1fba networks: flip **kwargs position to fix Python 3.4 support
Reported by @koaxirc.
2017-03-27 15:00:01 -07:00
James Lu
d70ca9fa3b Irc: simplify runloop error catching, adding RuntimeError and SystemExit (closes #438)
socket.error is aliased to OSError since Python 3.3, and ConnectionError is actually a subclass of OSError.
So, it makes more sense to just catch the more generic type here.

Also, make ProtocolError derive from RuntimeError instead of Exception.

(cherry picked from commit 397df48efde42d702f49529801f55b6912e9caed)
2017-03-26 14:46:51 -07:00
James Lu
55873416a1 control: raise KeyboardInterrupt to speed up forced shutdowns? 2017-03-26 14:44:04 -07:00
James Lu
d6243d9f89 protocols: raise NotImplementedError with proper reasons 2017-03-26 14:32:54 -07:00
James Lu
938a1fb9d7 Irc: reorder comments from last commit 2017-03-26 14:25:02 -07:00
James Lu
397df48efd Irc: simplify runloop error catching, adding RuntimeError and SystemExit (closes #438)
socket.error is aliased to OSError since Python 3.3, and ConnectionError is actually a subclass of OSError.
So, it makes more sense to just catch the more generic type here.

Also, make ProtocolError derive from RuntimeError instead of Exception.
2017-03-26 14:17:37 -07:00
James Lu
ce0c84266e fantasy: make responding to nick a per-service configuration option (#343)
This also renames the "respondtonick" option to "respond_to_nick", deprecating the former name.
2017-03-26 14:03:31 -07:00
James Lu
f497044777 corecommands: use utils.PLUGIN_PREFIX in 'unload' 2017-03-25 14:08:37 -07:00
James Lu
36f9bef450 docs/t: refresh protocol-modules graphic 2017-03-25 13:47:16 -07:00
James Lu
7ff5f47064 protocols: tweak mode type definitions 2017-03-25 13:47:16 -07:00
James Lu
8fec778b9b Merge branch 'wip/generic-p10-support' into devel
Closes #330. Really fixes #329.
2017-03-25 13:47:16 -07:00
James Lu
7acfd80387 Update named mode definitions 2017-03-25 13:47:16 -07:00
James Lu
45729d48cc p10: mode definition tweaks
- rename cmode +d to "had_delayjoin" (from "had_delayjoins")
- add cmode +R for ircu
-
2017-03-25 13:47:16 -07:00
James Lu
1994d1171c p10: add support for ircu proper 2017-03-25 13:47:16 -07:00
James Lu
7f923ba047 README: update supported IRCds notes 2017-03-25 13:47:16 -07:00
James Lu
e27095dd67 p10: set mode +x on targets when changing vHost 2017-03-25 13:47:15 -07:00
James Lu
6a32ae94fc p10: implement FAKE and SETHOST IRCd checks 2017-03-25 13:47:15 -07:00
James Lu
235b296a69 Rename protocols/nefarious.py to protocols/p10.py 2017-03-25 13:47:15 -07:00
James Lu
fa6120a563 nefarious: add a snircd mode set, fix nef2 umode +Dd definitions 2017-03-25 13:47:15 -07:00
James Lu
205053aebe example-conf: account-based cloaking is not nefarious specific 2017-03-25 13:47:15 -07:00
James Lu
bb63d859c9 example-conf: refresh P10 config arguments
The new p10_ircd option shown here isn't implemented yet.
2017-03-25 13:47:15 -07:00
James Lu
6154a7fb09 nefarious: mark cmode +A and +U as type B (#330)
This is consistent with snircd as of 58af1fc4d0
2017-03-25 13:47:15 -07:00
James Lu
e3c0bf6a1b Irc: break out of processQueue as soon as self.aborted is set 2017-03-24 18:40:02 -07:00
James Lu
af3b350498 networks.remote: fix "unknown service" error format 2017-03-24 01:10:56 -07:00
James Lu
6c4e042307 networks.remote: break if the target network is the same as the source (#437)
_remote_reply() otherwise gets sent into a loop when remoteirc and irc is the same here.
2017-03-24 01:08:01 -07:00
James Lu
490f21ff9f networks.remote: override remoteirc.reply() to send replies back to the caller
Closes #437.
2017-03-24 00:59:48 -07:00
James Lu
99d3780773 Irc: add locking for reply() calls (#437) 2017-03-24 00:57:21 -07:00
James Lu
0f472c8959 networks.remote: add an optional --service option to call commands for other services. 2017-03-24 00:25:19 -07:00
James Lu
9e0e47064a hybrid, ratbox: re-disable slash-in-hosts 2017-03-24 00:24:06 -07:00
James Lu
c894beed2e Merge branch 'wip/protocol-caps' into devel 2017-03-23 23:54:46 -07:00
James Lu
90d3ac3cf6 relay: skip message prefixing when forwarding a message for a service client (#403) 2017-03-23 23:53:48 -07:00
James Lu
feb9cce4ee core: Drop fake ServiceBot client stubs on Clientbot (#403) 2017-03-23 23:53:43 -07:00
James Lu
f188b29911 plugins: migrate to server capabilities 2017-03-23 23:37:24 -07:00
James Lu
ad00fdfa53 relay: migrate normalizeHost, normalizeNick to protocol capabilities 2017-03-23 23:12:59 -07:00
James Lu
21670a5d51 relay: migrate most protocol checks to protocol capabilities
Some things I left out include modesync and op status checking on LINK, since these are pretty specific to Clientbot IRC.
2017-03-23 22:52:11 -07:00
James Lu
bde27b0dde nefarious: define protocol_caps in the right class... 2017-03-23 22:46:12 -07:00
James Lu
ff6d961922 Protocol: add missing self in hasCap() 2017-03-23 22:41:12 -07:00
James Lu
c19ea74fb4 ServiceBot: ignore attempts to call empty commands 2017-03-20 22:22:43 -07:00
James Lu
efe468b0d2 protocols: declare slash-in-nicks, slash-in-hosts, underscore-in-hosts as necessary (#337) 2017-03-15 23:58:51 -07:00
James Lu
936535786e clientbot: declare clear-channels-on-leave capability 2017-03-15 23:47:32 -07:00
James Lu
f512ae1b33 Protocol: add a hasCap() wrapper function (#337) 2017-03-15 23:46:13 -07:00
James Lu
2ca0cf05a0 Start work on protocol capabilities (#337) 2017-03-15 23:32:47 -07:00
James Lu
61cf48c37c pmodule-spec: various fixes
- Corrected the location of the `self.irc.cmodes/umodes/prefixmodes` attributes
- Mention `self.conf_keys` as a special variable for completeness
2017-03-15 23:11:44 -07:00
James Lu
a8c1a46e3d core: queue messages for logging when 'log' isn't available during init
Closes #428.
2017-03-15 23:00:26 -07:00
James Lu
7892e37bfa Documentation tweaks
- Add advanced-service-config to TOC
- Move "matching SERVICE name" note to docs/t/services-api.md
- Various wording / grammar tweaks
2017-03-13 13:50:16 -07:00
James Lu
9ccd11b6d5 Merge pull request #433 from IotaSpencer/devel
docs: Add services config guide and mention matching config<->register

Closes #427.
2017-03-13 13:42:50 -07:00
James Lu
4910355711 Merge branch 'wip/global' into devel (#434)
Closes #398.
2017-03-13 13:41:10 -07:00
James Lu
787b254840 permissions-reference: document global.global permission 2017-03-13 13:40:09 -07:00
James Lu
3ffbbe60ff global: configurable output format 2017-03-13 13:39:30 -07:00
James Lu
cfe72e2cd0 global: set loopback=False on messages to prevent duplicating them via relay 2017-03-13 13:29:04 -07:00
James Lu
0a57c084bb global: only send to connected networks 2017-03-13 13:23:22 -07:00
James Lu
a3dff204d3 global: remove extraneous metadata
Core plugins do not need to track authors and version, because git does that for us.
2017-03-13 13:21:11 -07:00
Ken Spencer
7e6ad089c0 plugins: add global notice plugin 2017-03-13 12:30:06 -04:00
Ken Spencer
9a3ef0122b docs: Add services config guide and mention matching config<->register 2017-03-13 11:35:32 -04:00
James Lu
a6e38e7e20 pylink-opers: minor wording tweaks 2017-03-12 22:03:28 -07:00
James Lu
2e0c7db4e3 using-ircparser.md: minor tweaks and reordering
Thanks again to @IotaSpencer for writing this article :)
2017-03-12 20:45:42 -07:00
James Lu
75ea743b4a docs/t: link to using-ircparser.md in contents 2017-03-12 20:38:58 -07:00
James Lu
c8f945d40f writing-plugins.md: mention that IRCParser is a 1.2+ feature 2017-03-12 20:37:53 -07:00
James Lu
8c4a13fdf0 Merge pull request #431 from IotaSpencer/devel
Document utils.IRCParser() (closes #420)
2017-03-12 20:35:19 -07:00
Ken Spencer
d6d330bec6 Add docs/technical/using-ircparser.md guide. 2017-03-12 23:08:56 -04:00
Ken Spencer
ed7a117247 doc/technical/writing-plugins.md: mention and link to using-ircparser.md 2017-03-12 21:55:22 -04:00
James Lu
afc5cc26fa Irc: more compact __repr__ for IrcChannel/IrcServer/IrcUser 2017-03-11 22:49:48 -08:00
James Lu
716ac681b6 exec: add 'pieval' and 'peval' to evaluate expressions pretty-printed 2017-03-11 22:47:30 -08:00
James Lu
c67c0aa2e6 rehash: fix resetting the wrong autoconnect multiplier variable 2017-03-11 01:05:07 -08:00
James Lu
2028cab04c core: Grow autoconnect delays by a configurable factor whenever connections fail
Closes #348.
2017-03-11 00:21:30 -08:00
James Lu
9e50c5e69a Irc: try to make breaking out of autoconnect loops faster 2017-03-10 23:57:37 -08:00
James Lu
0526e96dc5 control: remove an extraneous, obsolete comment 2017-03-10 23:57:37 -08:00
James Lu
bf42109d81 Split fantasy prefix definitions into service-specific blocks
Closes #426.

This makes the pylink::prefix (aka bot::prefix) option only affect the main PyLink bot, and deprecates the pylink::prefixes::<...> options.
As the bot: config block is no longer checked, this commit depends on commit 45ed5b962e215763f7659631d1b98a5031dbab92 (ref #343) to alias it to conf::pylink.
2017-03-10 23:47:07 -08:00
James Lu
fb626c8a97 services_support: remove workarounds for the PyLink service nick & ident being in conf::bot (#343) 2017-03-10 23:47:07 -08:00
James Lu
dc298b3182 conf: join conf::bot and conf::pylink so that they mean the same thing (#343) 2017-03-10 23:47:07 -08:00
James Lu
3096c54bb8 Irc: return in msg() if the main client is missing and no explicit source is set 2017-03-10 23:47:07 -08:00
James Lu
b09565a723 bots, opercmds: add a "Done" reply to most commands as they finish 2017-03-09 20:50:14 -08:00
James Lu
ebd5b77576 relay: normalize channel case in 'link'
(regression from commit 93c9b6289c50ebc89043c866d06eb0d360ea6102)

Reported by @koaxirc.
2017-03-09 20:42:38 -08:00
James Lu
b23a887edd login: remove all__vary_rounds setting (deprecated in Passlib 1.7) 2017-03-08 22:58:17 -08:00
James Lu
f6d9765f87 core: implement module loading from user-defined directories
Closes #350.
2017-03-08 22:31:57 -08:00
James Lu
805a0502d2 utils: add an alias (utils.IRCParser.REMAINDER) to argparse.REMAINDER
Reported by @IotaSpencer.
2017-03-08 10:02:46 -08:00
James Lu
225b0ac8b2 ServiceBot: catch InvalidArgumentsError for prettier error display on IRC 2017-03-06 16:30:43 -08:00
James Lu
ca93d1ad70 IRCParser: show an error when using "command --help" instead of silently outputting help text in the console 2017-03-06 16:30:43 -08:00
James Lu
76d74ab9bb SECURITY: prevent DoS when calling --help on commands using IRCParser
argparse's default behaviour is to exit after displaying --help and --version information. However, doing so freezes the current IRC listener and essentially allows for DoS via IRC...
This bug does not affect any released (stable) version of PyLInk - only commits after 93c9b6289c50ebc89043c866d06eb0d360ea6102
2017-03-06 16:11:04 -08:00
James Lu
2d03ceebc8 Merge branch 'wip/attr-deprecations' into devel
Closes #273.
2017-03-05 00:15:03 -08:00
James Lu
9a9e0b2c20 Irc: deprecate the botdata field as well (#273) 2017-03-05 00:14:43 -08:00
James Lu
e8bf1d08bc handlers, fantasy: migrate away from irc.botdata (#273) 2017-03-05 00:10:33 -08:00
James Lu
d318fbac77 protocols: migrate away from irc.botdata (#273) 2017-03-05 00:09:01 -08:00
James Lu
b4f70bdece Irc: remove duplicate botdata assignment 2017-03-05 00:06:49 -08:00
James Lu
4284853a4a Irc: remove internal use of 'conf' and 'botdata' (#273) 2017-03-05 00:06:44 -08:00
James Lu
7f070448b7 utils, Irc: add abstraction to warn on deprecated attribute usage (#273) 2017-03-05 00:00:26 -08:00
James Lu
47f0b7626f clientbot: time out CAP/SASL after 5 seconds
Closes #424.
2017-03-04 23:54:16 -08:00
James Lu
733d7d7c87 exec: add ieval command using the isolated local scope 2017-03-04 22:05:03 -08:00
James Lu
42ba1775d7 exec: add irc, source, and args to isolated locals scopes
This allows basic things like irc.reply() to work in 'iexec'.
2017-03-04 21:59:42 -08:00
James Lu
9420f21680 clientbot: send CAP LS before NICK/USER so that it consistently gets a response before connect
Previously, SASL was failing on networks like freenode, as the connection completed before a CAP response was received.
2017-03-03 15:39:28 -08:00
James Lu
3281e1e8c2 pylink-contribdl: normalize exit codes 2017-02-27 07:28:37 -08:00
James Lu
8a773dea4e core: normalize exit codes 2017-02-27 07:26:29 -08:00
James Lu
7a1a4d9161 example-conf: document service-specific autojoin channels (from #423) 2017-02-26 18:16:15 -08:00
James Lu
21c0c617c1 Merge branch 'server-service-channels' of https://github.com/IotaSpencer/PyLink into devel
Closes #423.
2017-02-26 18:12:34 -08:00
James Lu
ae56ed6a32 services_support: fix service-specific key name 2017-02-26 18:11:20 -08:00
Ken Spencer
b3ec8a6790 service_support: allow server:service_channels for per service autojoin 2017-02-26 21:05:33 -05:00
James Lu
5fe277f90d Irc: mention CIDR matching (#411) and casemappings in matchHost() desc. 2017-02-25 22:26:37 -08:00
James Lu
4df8567fa6 Irc: move PYLINK_DISCONNECT firing and connected.clear() into disconnect() (#421)
This may prevent extra irc.connected.clear() calls from messing with the was_connected state.
2017-02-25 22:06:43 -08:00
James Lu
6fcb129ad6 hooks-reference: document the 'was_successful' key in PYLINK_DISCONNECT 2017-02-24 22:27:51 -08:00
James Lu
7c0cb92696 Irc: hack in CIDR support in matchHost() (#411) 2017-02-24 22:27:26 -08:00
James Lu
04fa0520a6 Irc: make was_successful check the last Irc.connected state 2017-02-24 22:09:41 -08:00
James Lu
cd65da75c6 relay: only announce disconnects if the last connection was successful 2017-02-24 21:42:58 -08:00
James Lu
b30d696e3a Irc: add "was_successful" data key to PYLINK_DISCONNECT
This stores whether the network was actually connected before this disconnect message fired (i.e. the disconnect wasn't caused by a configuration error, etc.)
2017-02-24 21:42:44 -08:00
James Lu
cd3d795296 relay: implement optional network disconnect announcements
Closes #421.
2017-02-24 21:15:40 -08:00
James Lu
0ebb52e64f conf: simplify newlogins checks & allow missing permissions blocks if an old login is also present 2017-02-24 21:07:28 -08:00
James Lu
c03f2d772c relay: allow dropping messages from user-less clients
This adds two new options, both defaulting to True:
- relay:accept_weird_senders (global)
- servers:<name>:relay_weird_senders (per-server)

Closes #404.
2017-02-24 19:16:01 -08:00
James Lu
3d9f69dba7 Irc: deprecate checkAuthenticated() 2017-02-24 18:42:58 -08:00
James Lu
50ff330734 Merge branch 'master' into devel 2017-02-24 18:31:45 -08:00
James Lu
e07974f803 utils: remove reference to checkAuthenticated() in NotAuthorizedError 2017-02-24 18:31:33 -08:00
James Lu
a7280d2943 docs/t: more notes regarding the permissions API 2017-02-24 18:31:26 -08:00
James Lu
027c35b75a PyLink 1.1.1 2017-02-24 17:56:43 -08:00
James Lu
6dd08e7dcb corecommands: remove extraneous irc.checkAuthenticated() call
(cherry picked from commit fe3fa2872db4ef3692ce36416e75ff6e7ab8b665)
2017-02-24 17:50:20 -08:00
James Lu
fe3fa2872d corecommands: remove extraneous irc.checkAuthenticated() call 2017-02-24 17:49:54 -08:00
James Lu
21cbcb8cf6 Irc: fix / simplify defaults in msg(), reply, error() 2017-02-24 16:28:23 -08:00
James Lu
46b18512cf relay: less ambiguous error if a relay channel doesn't exist on the caller network
(cherry picked from commit 0b0efbaf9f49757c58aa9ec6a060f966e15e7677)
2017-02-22 15:42:52 -08:00
James Lu
f432f6f082 relay: don't allow linking to channels when the home network is down
This check can be overridden via --force, and should stop unreliable TS checks from appearing instead

Closes #419.
2017-02-21 21:58:32 -08:00
James Lu
93c9b6289c relay: switch to IRCParser in 'link' and add a --force option to skip TS checks
Closes #416.
2017-02-21 21:52:22 -08:00
James Lu
bf702575be servprotect: fix a syntax error 2017-02-21 21:49:41 -08:00
James Lu
0125c544ee utils: add an IRCParser class based off argparse, modified from @IotaSpencer's code
Closes #6.
2017-02-21 21:45:43 -08:00
James Lu
02faa3fcb6 relay: more verbosity in TS-related link errors 2017-02-21 21:14:03 -08:00
James Lu
84d62fc540 example-conf: update comment to match the last commit 2017-02-21 21:06:55 -08:00
Ken Spencer
b92ee03525 servprotect: match key (example-conf.yml) to grabbed key (servprotect.py) (#418) 2017-02-21 18:14:48 -08:00
James Lu
2e92f65782 example-conf: mention that servprotect::max_age needs a plugin reload to update 2017-02-21 17:07:32 -08:00
James Lu
0706b6cf78 Style/spacing fixes for last commit 2017-02-21 17:04:59 -08:00
Ken Spencer
a8fe353ba4 servprotect: make length and age configurable (#417)
Fixes #395
2017-02-21 17:02:26 -08:00
Ken Spencer
b3075d3414 conf: change asserts to validations (#414) 2017-02-21 10:10:54 -08:00
James Lu
f83a81242a conf: reuse already-fetched newlogins value 2017-02-20 19:31:31 -08:00
Ken Spencer
07ac649763 conf: check for permissions block, per my own experience (#413) 2017-02-20 19:27:15 -08:00
James Lu
4577bde05c example-conf: make the "permissions:" block migration note more prominent 2017-02-19 21:40:00 -08:00
James Lu
0c88602d1f fantasy: check for nick prefix case insensitively 2017-02-18 21:21:32 -08:00
James Lu
12bb59d257 Irc: more parseArgs tweaks
- Make parsePrefixedArgs() a class method
- Split the input if parseArgs() is given a raw string instead of a list
2017-02-18 19:58:24 -08:00
James Lu
03fc16dd5a Irc: rewrite parseArgs to be more efficient 2017-02-18 19:47:36 -08:00
James Lu
01dd209647 inspircd: major->important 2017-02-18 14:45:23 -08:00
James Lu
cda5d15e31 inspircd: work around OPERTYPE changes in InspIRCd 3.x 2017-02-18 14:28:28 -08:00
James Lu
a9d2a2c4bc relay, handlers: rewrite oper WHOIS replies to show the target's home network 2017-02-18 13:51:45 -08:00
James Lu
3c98ef172e relay: use the new Irc.getFullNetworkName() where applicable 2017-02-18 13:33:35 -08:00
James Lu
b3161d6d5d Irc: add a getFullNetworkName() function 2017-02-18 13:32:48 -08:00
James Lu
75b5be5baf ServiceBot: implement global and per-service spawn_service(s) options (#403) 2017-02-18 12:54:26 -08:00
James Lu
a776aab897 utils: ignore missing services in unregisterService instead of raising an error
This is a prerequisite for the next commit (service spawn toggle options). (#403)
2017-02-18 12:54:08 -08:00
James Lu
a0ed43bf64 example-conf: describe how some options are common to all service bots 2017-02-18 12:42:36 -08:00
James Lu
a3f122fee4 control: try a more stable force-shutdown routine 2017-02-18 12:19:53 -08:00
James Lu
050721af5b example-conf: describe more clearly the pylink_nick/ident options in the clientbot block 2017-02-18 12:09:00 -08:00
James Lu
223dd3bf7b nefarious: fix a typo causing crash on user mode change
(cherry picked from commit 3e4a980ea64652239aea0eab4aa39ee35a9e10cb)
2017-02-17 22:28:11 -08:00
James Lu
8424870ec3 clientbot: abort when receiving a QUIT from uplink (#405) 2017-02-17 22:27:38 -08:00
James Lu
3e4a980ea6 nefarious: fix a typo causing crash on user mode change 2017-02-17 22:27:38 -08:00
James Lu
75158c47e2 clientbot: block PRIVMSG/NOTICE from being routed the wrong way (#405) 2017-02-17 22:27:38 -08:00
James Lu
f1fddefeac protocols: catch S2S messages if they're being routed the wrong way (#405) 2017-02-17 22:27:38 -08:00
James Lu
2f968aca80 Irc: allow defaulting to private command replies (Closes #409)
Squashed commit of the following:

commit c168500235b65f833b1d7fe49ebde674159683ee
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Thu Feb 16 17:33:36 2017 -0800

    ServiceBot: default notice and private to None

    This is so that it respects the changes from the last commit.

commit f685f3ef522f7f0ee356082c3c1b8b5a4e34b211
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Thu Feb 16 15:10:33 2017 -0800

    Irc: implement a prefer_private_replies option (#409)
2017-02-16 17:41:07 -08:00
James Lu
ad873cfd7b ServiceBot: be more flexible in help formatting
* Fix text after indented docstring line breaks not showing (thanks @IotaSpencer for noticing this)
* Update formatting so that multiple consecutive newlines in a docstring are shown:
    - 2 newlines => 1 displayed new line
    - 3 newlines => 2 displayed new lines, and so on...
2017-02-15 17:06:16 -08:00
James Lu
902b246f96 bots: allow specifying channel prefixes (e.g. @+) in 'join'
This functionality should really be merged with what ServiceBot does, but whatever...
2017-02-13 17:01:09 -08:00
James Lu
f70e771000 unreal: ignore userpairs with only a prefix and no user
How is this even possible?!

Reported by @koaxirc.

(cherry picked from commit 9fac7cb1f321e154f3b99038b5bc2c66fef15e31)
2017-02-06 18:00:39 -08:00
James Lu
9fac7cb1f3 unreal: ignore userpairs with only a prefix and no user
How is this even possible?!

Reported by @koaxirc.
2017-02-05 22:23:20 -08:00
James Lu
0b0efbaf9f relay: less ambiguous error if a relay channel doesn't exist on the caller network 2017-02-05 21:47:11 -08:00
James Lu
6e94375ed9 relay: clarify/revise help text for 'claim' 2017-02-05 21:43:53 -08:00
James Lu
f7768a00a0 inspircd: work around extraneous letters sometimes sent in FJOIN TS
Anope 1.8 potentially sends a trailing 'd' after the timestamp, which causes int() to error. This is technically valid in InspIRCd S2S because atoi() ignores non-digit characters, but it's strange behaviour either way:
<- :3AX FJOIN #monitor 1485462109d + :,3AXAAAAAK

Thansk to @koaxirc for reporting.

(cherry picked from commit 663e657bf5a82d9c2d1a1f3e9a02709118dc796b)
2017-02-05 21:13:23 -08:00
James Lu
663e657bf5 inspircd: work around extraneous letters sometimes sent in FJOIN TS
Anope 1.8 potentially sends a trailing 'd' after the timestamp, which causes int() to error. This is technically valid in InspIRCd S2S because atoi() ignores non-digit characters, but it's strange behaviour either way:
<- :3AX FJOIN #monitor 1485462109d + :,3AXAAAAAK

Thansk to @koaxirc for reporting.
2017-02-05 21:06:42 -08:00
James Lu
18826ad5c6 exec: add 'iexec' to run code in an isolated, persistent local scope 2017-02-05 20:26:40 -08:00
James Lu
a3a5569156 exec: print a "Done" after executing code 2017-02-05 20:14:30 -08:00
James Lu
dcacfb0c10 relay: catch RuntimeError in spawnRelayServer as well
This is raised when a network runs out of SIDs, for example.
2017-02-03 18:06:49 -08:00
James Lu
b5cf2e8a4e stats: add an --all option to 'uptime', and check for disconnected networks 2017-01-30 00:18:50 -08:00
James Lu
efded33f4a stats: oops, use the right Irc object 2017-01-30 00:08:40 -08:00
James Lu
de99be720e stats: prettier formatting for the uptime command
Closes #381.
2017-01-30 00:06:42 -08:00
James Lu
1c19d82f53 Merge branch 'wip/stats' into devel 2017-01-29 23:31:43 -08:00
James Lu
8901ed72ee relay: s/spawnIfMissing/spawn_if_missing/g 2017-01-29 22:18:05 -08:00
James Lu
fa30d3c732 relay: rework fallback message routing to be less annoying (#384)
- PRIVMSGs from users not spawned on a network are sent via the main PyLink client in the format "<$orignick/$network> <$text>"
    - <PyLink> <user/net> blah blah
- NOTICEs from users use the same format above, and are routed from the relay subserver representing the network that the message originated from
    - Notice(somenet.relay): <user/net> blah blah
- PRIVMSGs from servers are blocked, because they aren't valid on all IRCds and are fairly obscure anyways (suggestsions/improvements welcome)
- NOTICEs from servers are forwarded as raw text, from the relay subserver representing the origin network
    - Notice(somenet.relay): some server announcement
2017-01-29 20:21:45 -08:00
James Lu
e936b9cfd2 relay: add a spawn_if_missing option to get_remote_sid() 2017-01-29 20:20:39 -08:00
James Lu
359132045d protocols: allow forwarding NOTICE from servers (#384) 2017-01-29 19:49:37 -08:00
James Lu
8fdcb9d2bb Bump version to 1.2-dev 2017-01-29 18:11:31 -08:00
James Lu
0d99bc62d2 -mkpasswd: fetch password via getpass instead of requiring command line entry
This gives better security, since running programs and their command lines are visible in 'ps'.
2017-01-29 00:12:05 -08:00
James Lu
62c4b79e04 setup.py: install pylink-mkpasswd as a script 2017-01-28 23:27:24 -08:00
James Lu
e036449c72 README: move Apt / PPA instructions to after source builds and pip
These aren't tested as thoroughly.
2017-01-28 19:58:32 -08:00
James Lu
69b112eb44 PyLink 1.1.0.1
This is identical to 1.1.0 - bumping the version due to a botched PyPI upload.
2017-01-24 20:52:01 -08:00
James Lu
c7ac8eeafb PyLink 1.1.0 2017-01-24 20:42:45 -08:00
James Lu
b18b2fff17 setup.py: fix last commit 2017-01-22 17:00:27 -08:00
James Lu
39d2243b11 setup.py: use extras_require for expiringdict and passlib 2017-01-22 16:44:42 -08:00
James Lu
9daa452f8e Update dependencies in setup.py & README 2017-01-22 16:42:52 -08:00
James Lu
d6a6d069bc Move 'mkpasswd' to the commands plugin 2017-01-22 16:42:46 -08:00
James Lu
fd12a5d919 core: make passlib an optional dependency 2017-01-22 16:42:46 -08:00
James Lu
354a3022a4 services-api.md: remove extra_channels argument per d31d09ce7ec1b3be8062e5e18f3572832f7f50b7 2017-01-22 00:55:37 -08:00
James Lu
ce742d40eb docs/technical: fix capitalization of ircd-ratbox 2017-01-21 13:13:53 -08:00
James Lu
c20c144222 automode, relay: remove unused imports 2017-01-21 12:01:41 -08:00
James Lu
170738ee10 Revert "automode: bandaid fix for "service already registered" errors on first load"
This reverts commit e2a853c98ed5487344149ecf611e2171d9e1bf68.
2017-01-21 11:59:43 -08:00
James Lu
3dd35ba5a1 Add an example services plugin
Closes #399.
2017-01-21 11:59:43 -08:00
James Lu
d33eb22ca3 ServiceBot: verify that the service name is a valid nick 2017-01-21 11:59:43 -08:00
James Lu
d31d09ce7e ServiceBot: remove extra_channels argument from the constructor
This is unused and a poor thing to hardcode anyways.
2017-01-21 11:59:43 -08:00
James Lu
1fe64cca04 example.py: update to reflect 1.1.x docstring changes (#307) 2017-01-21 11:59:43 -08:00
James Lu
7bbe77fe4a Irc: remove unused "bot_clients" attribute
Thanks to @IotaSpencer for pointing this out!
2017-01-21 11:08:37 -08:00
James Lu
d749fbb2ab ctcp: -that 2017-01-20 22:32:32 -08:00
James Lu
b7470c3c42 ctcp: remove puns 2017-01-20 22:31:46 -08:00
James Lu
5cd7e2e14c PyLink 1.1-beta2 2017-01-15 01:13:21 -08:00
James Lu
39db5aee04 ts6_common: properly handle KICK without a reason
This field is optional in TS6.
2017-01-14 11:08:24 -08:00
James Lu
7245e978cd unreal: in MODE, also wrap to 12 modes per line
Closes #393. Really fixes #253.
2017-01-13 23:58:11 -08:00
Mitchell Cooper
5e79ea908d inspircd: fix comment that says FIDENT in the FHOST handler (#392) 2017-01-12 21:00:24 -08:00
James Lu
ca8b025f9a Merge branch 'master' into devel
Conflicts:
	RELNOTES.md
	VERSION
2017-01-12 20:59:41 -08:00
James Lu
4f0ca6367c unreal: fix math error
len(":SIDAAAAAA ") is 11, not 9
2017-01-12 19:54:47 -08:00
James Lu
b1918f5392 Initial pass at a Stats plugin (#121, #381) 2017-01-11 23:35:35 -08:00
James Lu
165f8fa4a7 world: start tracking daemon start time (#381) 2017-01-11 23:35:10 -08:00
James Lu
487a07671b ts6: implement line wrapping in SJOIN ban bursts (#253) 2017-01-11 23:09:25 -08:00
James Lu
702ba84956 wrapArguments: add a max_args_per_line option (#253) 2017-01-11 23:09:25 -08:00
James Lu
4147af6546 hooks-reference: remove redundant Introduction heading 2017-01-09 22:25:16 -08:00
James Lu
fb9144a715 clientbot: always add channels to users' channel lists in names reply
Do this regardless of whether the user is already added to the channel's user list/kick queue, since those are tracked separately.

Closes #388.
2017-01-09 22:23:59 -08:00
James Lu
ccfc2f601d clientbot: explicitly send /names after join (#388) 2017-01-09 22:23:59 -08:00
James Lu
602f35cb70 clientbot: fix message recognition treating nick prefixes without ident@host as servers 2017-01-09 22:23:59 -08:00
James Lu
7fe65e1f8a Refresh protocol spec for 1.0-beta1+
- Replace utils.parseModes() with irc.parseModes()
- Update source code links to 1.0-beta1
2017-01-09 22:23:35 -08:00
James Lu
70deb5a285 ts6: rewrite MODE wrapping to check message length and argument count (#253) 2017-01-08 21:20:42 -08:00
James Lu
e2c0877e9b wrapModes: optionally check for max. modes per line (#253) 2017-01-08 21:19:26 -08:00
James Lu
aafd734e3a ts6: remove leftover mode filtering in mode() 2017-01-08 20:43:08 -08:00
James Lu
74755db6e1 example-conf: log settings no longer need a restart to update 2017-01-08 17:34:29 -08:00
James Lu
7594933550 parseModes: reorder logic so that -k * workarounds work again
Thanks @cooper for noticing this.
2017-01-08 17:31:50 -08:00
James Lu
b2286157ef core: update stdout log level on REHASH 2017-01-07 00:12:35 -08:00
James Lu
a2e7a35998 relay: remove incorrect network name in logging for blocked kicks 2017-01-06 22:51:50 -08:00
James Lu
e0bda6b850 relay: also filter out low TS values in burst relaying 2017-01-06 22:48:11 -08:00
James Lu
8878f77636 updateTS: don't save any broken TS values lower than 750000
Workaround for #385, and other related timestamp issues caused by the TS value getting cut off in mode messages (#283)

(cherry picked from commit ba330bbfac5c542bb2fc0661d01e1fd203253308)
2017-01-06 22:35:16 -08:00
James Lu
d4b4cfb32e wrap* funcs: convert input args to a list, so that pop(0) always works 2017-01-06 22:13:27 -08:00
James Lu
ba330bbfac updateTS: don't save any broken TS values lower than 750000
Workaround for #385, and other related timestamp issues caused by the TS value getting cut off in mode messages (#283)
2017-01-06 21:56:22 -08:00
James Lu
ef4e1ecbab unreal: count the server prefix in SJOIN line wrap (#253) 2017-01-06 18:29:47 -08:00
James Lu
4183a580d2 nefarious: implement text wrapping in BURST (#253)
Some of this is totally hacky, but it still works from my initial testing...
2017-01-06 18:24:32 -08:00
James Lu
92dcf3c28e nefarious: implement text wrapping in outgoing MODE (#253) 2017-01-06 18:08:21 -08:00
James Lu
3e706366bd relay: on Clientbot networks, don't relay prefix mode changes for the relayer bot
Closes #366.
2017-01-06 16:45:48 -08:00
James Lu
43002d466e nefarious: fix misplaced log line from efe38264efa8ffb29228d917d58db859156603ef 2017-01-06 16:43:10 -08:00
James Lu
94779326bc example-conf: mention what changehost is NOT meant to do 2017-01-03 07:30:35 -08:00
James Lu
31ddc37db4 example-conf: be more clear that network names (e.g. "inspnet:") should be changed 2017-01-02 12:30:38 -08:00
James Lu
a3ff32c22e core: implement bind host support
Closes #379.
2017-01-02 12:30:24 -08:00
James Lu
9a01a5285f unreal: handle user mode changes via MODE 2017-01-02 12:20:20 -08:00
James Lu
fc3ee8d402 unreal: implement line wrapping for outgoing MODE 2017-01-02 12:16:35 -08:00
James Lu
d1d3c3ef15 Irc: oops, wrapModes() needs to be a classmethod 2017-01-02 12:15:44 -08:00
James Lu
5843eebba2 Irc: add wrapModes() abstraction 2017-01-02 12:08:22 -08:00
James Lu
12c33fcddf utils: remove incomplete example text for wrapArguments() 2017-01-02 11:08:55 -08:00
James Lu
efe38264ef nefarious: remove extraneous userlist assignment in handle_burst 2017-01-02 10:25:37 -08:00
James Lu
605d242677 automode: remove repeated "Error:" in error messages 2017-01-02 10:11:55 -08:00
James Lu
4020c3dea1 ircs2s_common: fix last commit (missing import)
(cherry picked from commit 6a90401d563567ead0823be0a9a5b6b77f9f4378)
2017-01-01 20:39:46 -08:00
James Lu
6a90401d56 ircs2s_common: fix last commit (missing import) 2017-01-01 20:39:34 -08:00
James Lu
d5eb01b724 protocols: move handle_pong into ircs2s_common, be less strict about the ping argument
This fixes issues on UnrealIRCd where PONGs get ignored if the argument doesn't match the server name entirely (e.g. different case).
Treating all PONGs from the uplink as valid is totally fine, as all we care about is that the uplink is alive.

(cherry picked from commit 38350465c1dcc3f45fafc5706aaacb37e534b9bb)
2017-01-01 20:28:26 -08:00
James Lu
38350465c1 protocols: move handle_pong into ircs2s_common, be less strict about the ping argument
This fixes issues on UnrealIRCd where PONGs get ignored if the argument doesn't match the server name entirely (e.g. different case).
Treating all PONGs from the uplink as valid is totally fine, as all we care about is that the uplink is alive.
2017-01-01 20:24:34 -08:00
James Lu
945fc8f0f9 unreal: normalize whitespace in SJOIN as well
Sometimes there is an extra space after the mode list, so the query looks like

<- :000 SJOIN 1234567890 #channel +ntf [10t]:5  :000AAAAAA 000AAAAAB

instead of

<- :000 SJOIN 1234567890 #channel +ntf [10t]:5 :000AAAAAA 000AAAAAB
2017-01-01 19:55:29 -08:00
James Lu
4a519832e0 unreal: normalize nicks to UIDs in SJOIN handling
These can still be used by old Unreal 3.2 links.
2017-01-01 13:48:47 -08:00
James Lu
71bd5583fa unreal: parse mode parameters in SJOIN as well!
Thanks to kevin for pointing this out.
2017-01-01 11:32:44 -08:00
James Lu
1a1dff7609 IrcChannel: don't assume +nt on new channels
Revert "classes.IrcChannel: default modes to +nt on join"

This reverts commit 1062e47b725a001ced44783632d691e1979c74b0.
2017-01-01 00:37:12 -08:00
James Lu
dc11638eb9 ts6 (and derivatives): don't burst bans that were already set 2017-01-01 00:28:55 -08:00
James Lu
803ccf7708 unreal: add SJOIN to required caps 2017-01-01 00:21:37 -08:00
James Lu
69be532c3c unreal: actually send the remote's modes in SJOIN hooks 2017-01-01 00:20:29 -08:00
James Lu
0b8b4dc3cf inspircd, nefarious: stop applying remote modes on sjoin
I have no clue why this code exists, but it looks wrong and probably is wrong.
2017-01-01 00:19:10 -08:00
James Lu
f851dc8ac1 unreal: implement modes in SJOIN (SJ3), respect S2S message length limits
Closes #378. Ref #253
2017-01-01 00:00:01 -08:00
James Lu
eafec9d4ad utils: add wrapArguments() to deal with S2S message cutoffs
Ref: #253, #378
2016-12-31 23:35:27 -08:00
James Lu
278339b5e2 unreal: actually enable the SJOIN cap, handle ban bursts properly 2016-12-31 22:15:42 -08:00
James Lu
a340ab15e1 relay: local channel in 'link' is optional
Thanks to Digerati for noticing this.
2016-12-29 08:47:35 -08:00
James Lu
e566b99b75 clientbot: don't crash if we receive /who for someone we don't know 2016-12-27 22:16:12 -08:00
James Lu
f1da5c57e8 clientbot: don't repeat KICK hooks if the source is internal
This prevents KICK events from being relayed twice to Clientbot links, when the kicked user is also a Clientbot user.
2016-12-27 22:09:16 -08:00
James Lu
59f232d69f clientbot: fix SASL PLAIN auth on Python 3.4
A strange bug causes "TypeError: unsupported operand type(s) for %: 'bytes' and 'tuple'" when formatting multiple args into a byte string using %b.
2016-12-27 18:16:15 -08:00
James Lu
f6f951fba8 PyLink 1.1-beta1 2016-12-26 08:14:05 -08:00
James Lu
af6b191164 Merge remote-tracking branch 'origin/disable-default-pidcheck' into devel 2016-12-25 11:19:37 -08:00
James Lu
6728627f78 docs: document the permissions API for developers
Closes #368.
2016-12-25 11:17:10 -08:00
James Lu
e53c758471 docs: reorganize & include a list of all PyLink permissions
Closes #365.
2016-12-25 00:41:41 -08:00
James Lu
614c029538 relay: add missing 'relay.linked' permissions check 2016-12-25 00:31:38 -08:00
James Lu
ec13bae7e6 opercmds: remove pointless source argument from 'kick' and 'kill' 2016-12-24 10:58:03 -08:00
James Lu
3e6550b8ad example-conf: mention Clientbot mode sync's behaviour with CLAIM
Closes #344.
2016-12-22 00:38:39 -08:00
James Lu
7ee963f66e Document advanced relay config & custom clientbot formatting
Closes #335.
2016-12-22 00:27:20 -08:00
James Lu
77dd8224ae relay: use built-in hash() for colorizing text
This is way faster than md5.
2016-12-21 23:48:40 -08:00
James Lu
c66c85bc9a hooks-reference: refresh for 1.1.x 2016-12-21 23:26:30 -08:00
James Lu
930443f4cd PyLink 1.0.4 2016-12-19 22:29:25 -08:00
James Lu
3595e5fdbf changehost: limit 'applyhosts' to those with the changehost.applyhosts perm
(cherry picked from commit 12a85092990743d46808544e35a787e5e048ab8c)
2016-12-19 01:29:54 -08:00
James Lu
a96bb0ce11 clientbot: make unattended SASL reauth optional 2016-12-19 01:06:49 -08:00
James Lu
68c618887f clientbot: auto-attempt SASL when it is introduced in CAP NEW 2016-12-19 00:54:20 -08:00
James Lu
f42d49b8eb cleintbot: only CAP END if we haven't registered yet 2016-12-19 00:52:28 -08:00
James Lu
880714b2f2 clientbot: implement CAP DEL, CAP NEW 2016-12-19 00:40:19 -08:00
James Lu
fdda28799c clientbot: fix message tag parsing 2016-12-19 00:18:15 -08:00
James Lu
3a8710540c Merge branch 'devel' into wip/ircv3 2016-12-19 00:10:07 -08:00
James Lu
1765d61973 Merge branch 'wip/relay/better-normalizehost' into devel
Conflicts:
	plugins/relay.py
2016-12-19 00:01:16 -08:00
James Lu
c09fce344d relay: whitelist _ on InspIRCd, UnrealIRCd, Nefarious, clientbot 2016-12-19 00:00:24 -08:00
James Lu
ef8ec03e41 relay: re-add / in hosts for networks that support it 2016-12-18 23:56:47 -08:00
James Lu
5f6337a734 Merge branch 'master' into devel
Conflicts:
	plugins/networks.py
2016-12-18 00:15:24 -08:00
James Lu
194a62fcea protocols: implement basic nick collision detection in UID handlers
Closes #285. Closes #375.
2016-12-18 00:13:42 -08:00
James Lu
3339bf0fe6 relay: rewrite normalizeHost() to whitelist characters instead 2016-12-17 23:47:26 -08:00
James Lu
3a852393bc relay: remove _ from hosts on ts6, ratbox 2016-12-17 23:03:28 -08:00
James Lu
de3d9bb5c9 example-conf: add a SASL login example 2016-12-17 16:18:11 -08:00
James Lu
9bfa0c9bb8 clientbot: whitelist supported SASL mechanisms, and abort on invalid ones 2016-12-17 16:18:11 -08:00
James Lu
90e10f948e clientbot: rename conf value sasl_mech -> sasl_mechanism 2016-12-17 16:18:11 -08:00
James Lu
0cc1ff8fa3 clientbot: log CAP REQ, CAP NAK events 2016-12-17 16:18:11 -08:00
James Lu
8b0b4bfcc4 clientbot: log SASL failures to warning instead of info 2016-12-17 16:18:11 -08:00
James Lu
651752d23d clientbot: send CAP END if SASL is disabled 2016-12-17 16:18:11 -08:00
James Lu
9cc817d544 clientbot: require SSL for SASL external, better grammar in SASL misconfiguration errors 2016-12-17 16:18:11 -08:00
James Lu
a6b889c469 Irc: fix whitespace 2016-12-17 16:18:11 -08:00
James Lu
ffed5e3378 clientbot: also CAP END on 906 (SASL aborted) 2016-12-17 16:18:11 -08:00
James Lu
8666151189 clientbot: properly verify ACKed/NAKed caps, add support for SASL PLAIN & EXTERNAL 2016-12-17 16:17:36 -08:00
James Lu
3bc9b1bc55 clientbot: implement IRCv3.2 CAP and IRCv3.1 multi-prefix (#290) 2016-12-16 22:28:40 -08:00
James Lu
cbc7f438d2 clientbot: implement preliminary message tags parsing
Untested so far...
2016-12-16 22:28:22 -08:00
James Lu
231fd13429 launcher: disable pid check by default
I'm postponing this until PyLink 2.0 to make migration from 1.0.x to 1.1 less of a nuisance.

Closes #364.
2016-12-16 20:55:09 -08:00
James Lu
7e37a90c80 clientbot: delete channels on kick / part, or if a channel becomes empty after parting
Closes #314.
2016-12-16 20:50:36 -08:00
James Lu
e7a005b685 control: log remaining threads on shutdown, for debugging freezes 2016-12-16 19:42:12 -08:00
James Lu
95b58fc2c4 utils: abstract protocol/plugin import prefixes, and implement filtering by plugin in 'list'
Closes #369.
2016-12-16 19:25:41 -08:00
James Lu
7b5fcc3219 corecommands: use irc.error() in login fail wrapper 2016-12-16 19:06:33 -08:00
James Lu
f9adaa85ca commands: implement 'logout'
Closes #370.
2016-12-16 19:05:08 -08:00
James Lu
2be8d5b282 README: supported->possible for unreal 3.2 notes
UnrealIRCd 3.2 is EOL Dec. 31, so it's not really worth actively "supporting" it.
2016-12-16 19:04:44 -08:00
James Lu
1fd7aba3b0 example-conf: add migration notes for login:user/password deprecation 2016-12-16 18:40:27 -08:00
James Lu
045abfa9c1 commands: add permissions checks to echo, showuser, showchan, and status 2016-12-16 18:31:19 -08:00
James Lu
2de36caea0 commands: fix previous commit 2016-12-09 22:13:32 -08:00
IotaSpencer
b0296933a1 plugins/commands.py: change over to permissions API 2016-12-09 22:13:32 -08:00
IotaSpencer
9199186309 plugins/opercmds.py: change over to permissions API 2016-12-09 22:13:32 -08:00
IotaSpencer
69083cfcfd plugins/bots.py: change over to permissions API 2016-12-09 22:13:32 -08:00
James Lu
12a8509299 changehost: limit 'applyhosts' to those with the changehost.applyhosts perm 2016-12-09 22:13:32 -08:00
James Lu
611b07e6bd networks: update help for 'disconnect'
The 'disconnect' command has been changed to disable autoconnect, but this text was never updated.

(cherry picked from commit 34ad79744a816e5ac6bf41963954f1e83c352274)
2016-12-09 21:56:25 -08:00
James Lu
60ca5af813 networks: port to the permissions API (#367) 2016-12-09 21:55:47 -08:00
James Lu
fb463e3b26 networks.remote: override the login to a valid user, so that permissions can match it 2016-12-09 21:53:54 -08:00
James Lu
34ad79744a networks: update help for 'disconnect'
The 'disconnect' command has been changed to disable autoconnect, but this text was never updated.
2016-12-09 21:51:57 -08:00
James Lu
01f2a4ca20 servermaps: fix wrong permission name 2016-12-09 21:43:58 -08:00
James Lu
3d4cb02ce2 exec: rewrap docstirngs 2016-12-09 21:43:58 -08:00
James Lu
9e2598612a exec: port to permissions API (#367) 2016-12-09 21:43:58 -08:00
James Lu
5a066bfde4 corecommands: port to use the permissions API (#367) 2016-12-09 21:43:58 -08:00
James Lu
1e7210a6db Refresh permissions examples, adding a stub in the main example conf for admin access 2016-12-09 20:57:49 -08:00
James Lu
57aa844fcb permissions: limit "login = admin access" to old-style (< 1.1) login blocks 2016-12-09 20:57:01 -08:00
James Lu
506467193e Import RELNOTES for 1.1-alpha1 2016-12-09 20:14:26 -08:00
James Lu
1761effa28 VERSION: bump to 1.1-alpha1 2016-12-09 18:37:54 -08:00
James Lu
a2e0bb19dc launcher: set terminal title on launch (windows & posix) 2016-12-09 18:10:47 -08:00
James Lu
b5f244009a relay: rename camel case functions to lowercase with underscores
The exception is isRelayClient(), which is aliased only to is_relay_client() to be consistent with isXYZ() functions elsewhere in the framework
Also, getRemoteChan() was renamed to get_remote_channel()
2016-12-09 18:02:30 -08:00
James Lu
2b4943a780 relay: add locks in db read/writes (thread safety) 2016-12-09 17:44:11 -08:00
James Lu
e40b2f6529 relay: add 'purge' command to remove all relays involving a network
Closes #356.
2016-12-09 17:34:51 -08:00
James Lu
8855ef2a41 relay: don't break in removeChannel if irc.pseudoclient isn't set 2016-12-09 17:34:34 -08:00
James Lu
e7e2f2c98e core: update "missing dependencies" errors
Closes #363.
2016-12-09 17:15:53 -08:00
James Lu
638b9dc84a relay: rewrap help for LINKACL
More formatting changes to come.
2016-12-05 23:33:42 -08:00
James Lu
03766b9f89 ServiceBot: implement docstring rewrapping per #307.
Closes #307.
2016-12-05 23:33:03 -08:00
James Lu
b3387f2d41 conf: fix deprecation warnings crashing because log is unavailable
This allows conf methods to access to global logger by via an optional 'logger' argument. However,
the caveat is that the logging facilities are still unavailable on first start, because log can
only be imported *after* the configuration is loaded.
2016-12-05 22:43:01 -08:00
James Lu
62a4bc01ce Merge branch 'master' into devel 2016-12-05 00:59:41 -08:00
James Lu
664bd3a89f pylink-contribdl: link to contrib modules list if no argument is given 2016-12-05 00:45:53 -08:00
James Lu
3f4bf72248 pylink-contribdl: implement --yes 2016-12-05 00:39:09 -08:00
James Lu
c479dd1753 Add a contrib module downloader (pylink-contribdl)
Fixes #350.
2016-12-05 00:36:11 -08:00
James Lu
f5633329f8 utils: drop loadModuleFromFolder; it is unused since bcc84b8618f641c77b0a599de9ab9d24432b8c29 2016-12-04 23:35:16 -08:00
Ken Spencer
e59d829973 update mailmap with my (IotaSpencer) aliases
(cherry picked from commit 2c1b8c09529dd229d570eb464cb3105cede146e6)
2016-11-22 22:52:05 -08:00
Ken Spencer
a173e2a61f pylink: Warn users that configuration is changing. (closes #361) 2016-11-22 22:49:23 -08:00
James Lu
e4b2ea60ec Irc: demote unknown user errors in parseModes() to DEBUG
Some ancient services like Anope 1.8 set SVS2MODE +d on users when they connect, even if the user quits right after. Due to lag we may receive the MODE after the QUIT instead of before.

(cherry picked from commit ec4e71c8cfb86105a28e7bc0b63d91dcc8c37a60)
2016-11-22 16:27:49 -08:00
James Lu
1888f24ef0 Irc: fix matchHost not using realhost properly
(cherry picked from commit f9e798cf936a64bbd6d19341f444ad4ce6bcca36)
2016-11-20 13:41:46 -08:00
James Lu
a57f194123 changehost: add options to match users by IP and realhost 2016-11-20 12:34:11 -08:00
James Lu
476f84a181 changehost: implement enforce exceptions 2016-11-20 12:33:58 -08:00
James Lu
f9e798cf93 Irc: fix matchHost not using realhost [rp[er;y 2016-11-20 12:29:51 -08:00
James Lu
d90f44c510 changehost: explicitly ignore PyLink internal clients 2016-11-20 12:04:30 -08:00
James Lu
501647805c changehost: add optional vHost enforcement 2016-11-20 11:53:55 -08:00
James Lu
ffc271a53a login: Use a slightly faster CryptContext 2016-11-19 17:47:55 -08:00
James Lu
c4351d61c6 Add command line mkpasswd utility & encrypted password example 2016-11-19 17:21:45 -08:00
James Lu
b1e4b34b79 Switch 'identify' to use the new login backend, add passlib to README dependencies
This new backend supports optional encryption (sha256_crypt / sha512_crypt via passlib). Closes #322.
2016-11-19 17:21:45 -08:00
James Lu
69066029f1 Simplify/rewrite the login module 2016-11-19 17:01:05 -08:00
James Lu
3308db0cd2 Import coremods/login.py (abstracted login checking) from @IotaSpencer's repo 2016-11-19 17:01:00 -08:00
James Lu
797476f7ad Version bump to 1.1-dev1 (1.1.x milestone 1) 2016-11-18 23:18:07 -08:00
James Lu
048367d6c5 AUTHORS: add @IotaSpencer and update my email 2016-11-18 23:16:50 -08:00
James Lu
c77ad6faa9 bots: PseudoClient->client in help text & error messages 2016-11-18 23:13:56 -08:00
Ken Spencer
d467d27ecd plugins: change remaining plugins over irc.error() use 2016-11-18 23:11:44 -08:00
Ken Spencer
02dfe5aeab classes: pass force_privmsg_in_private and private booleans to irc.reply() in irc.error() 2016-11-18 23:10:45 -08:00
Ken Spencer
a1bbb4fdb9 utils: style points! WOOOO 2016-11-18 23:10:45 -08:00
Ken Spencer
289ab78052 plugins/bots: change over to irc.error() use 2016-11-18 23:10:45 -08:00
Ken Spencer
940430b075 plugins/automode: change errors over to irc.error() based use 2016-11-18 23:10:45 -08:00
Ken Spencer
fdd4135632 utils: add error() to use classes.py's irc.error() 2016-11-18 23:10:45 -08:00
Ken Spencer
c450d71f84 classes: add irc.error() for easier error replies 2016-11-18 23:10:45 -08:00
James Lu
57f7acc124 pylink: fix last commit (args.no_check_pid) 2016-11-16 21:52:07 -08:00
James Lu
7c93a6cdfc pylink: tidy up --no-check-pid setting 2016-11-14 21:38:02 -08:00
James Lu
8e7cffc016 docs/automode: update perms list & caveat section
- Automode remote management and its relevant permissions were added in the last commit (8ff292bd1f314d50c2a2f8355ac2ca171f62b7f2).
- Autoop on join for services bots was implemented in 0cce6ca4885c986e09b4afba78a416cf9016538b.
2016-11-12 12:24:00 -08:00
James Lu
8ff292bd1f automode: support remote channel manipulation in the form netname#channel
Closes #352.
2016-11-12 12:20:25 -08:00
James Lu
9dbf124e36 docs/automode: fix typo 2016-11-12 12:18:41 -08:00
James Lu
691a8178b2 relay: implement 'showchan' with links info
Closes #353.
2016-11-12 10:43:55 -08:00
James Lu
e570779a03 Remove update.sh helper script 2016-11-12 10:39:11 -08:00
James Lu
c077f9aebb DataStore: log the name of the current database implementation to DEBUG 2016-11-09 22:56:57 -08:00
James Lu
b94e11930e structures: directly retrieve DB save delay in DataStore (#303) 2016-11-09 22:53:13 -08:00
James Lu
b0636b40ab Finish the plugin migration to DataStore
Closes #303.
2016-11-09 22:47:22 -08:00
James Lu
089dce3853 structures: revise DataStore a bit (#303) 2016-11-09 22:19:30 -08:00
James Lu
377df413ed Irc: s/isServiceBot/getServiceBot/g (#355)
This function is renamed to better reflect its return value (ServiceBot object instead of boolean True).
2016-11-09 19:09:59 -08:00
James Lu
08fa64c3cc Irc, services_support: store service name in IrcUser objects (#355) 2016-11-09 19:07:01 -08:00
James Lu
0815df1bca Irc: rewrite isInternalClient to use getServer & return a boolean (#355) 2016-11-09 18:55:53 -08:00
James Lu
ddd0436937 Irc: rewrite getServer() to look for IrcUser.server attribute (#355)
The relevant attribute was introduced in c57fabc9ef2f9acecc70130e6f48c17ab435dc6d.
2016-11-09 18:55:08 -08:00
James Lu
c57fabc9ef core, protocols: add server argument to IrcUser (#355) 2016-11-09 18:40:16 -08:00
James Lu
b0bd5d47ae relay: fix logging format in spawnRelayUser error 2016-11-07 22:04:34 -08:00
James Lu
44743d860e relay: don't break autoconnect anymore when there's a server conflict
This reverts most of commits 5c7524b and f2a5e1d.
2016-11-07 21:53:52 -08:00
James Lu
4246a3d113 relay: work on sane fallbacks when a network's SID goes missing (#354) 2016-11-07 21:47:53 -08:00
James Lu
b79e693808 exec: Drop 'raw' text logging to DEBUG for security purposes
Closes #347.
2016-11-07 21:25:57 -08:00
James Lu
2d20256ed8 Relay: rework to use the permission system
This defines the following permissions:

Granted to opers by default:
- relay.create
- relay.destroy
- relay.claim
- relay.link
- relay.delink
- relay.linkacl.view
- relay.linkacl

Granted to all users by default:
- relay.linked

And the following which is not explicitly granted:
- relay.savedb

Closes #325.
2016-11-07 21:22:52 -08:00
James Lu
93ca62aa49 Revamp configuration to support multiple accounts (#319) 2016-11-07 21:01:28 -08:00
James Lu
e8cc7227a8 PyLink 1.0.3 2016-11-05 21:40:40 -07:00
James Lu
28b88a3840 README: add Ubuntu PPA instructions 2016-11-05 21:31:50 -07:00
James Lu
34fd45dce9 Merge remote-tracking branch 'origin/master' into devel 2016-11-05 21:27:50 -07:00
James Lu
d2f95acd2e Proofread and edit the example-conf 2016-11-04 18:13:15 -07:00
James Lu
6ad40c91b4 Irc: explicitly kill connect loop threads after an Irc object has been removed
Possible fix for #351.
2016-11-02 22:34:02 -07:00
James Lu
e977c95520 Merge branch 'master' into devel
Conflicts:
	VERSION
	example-conf.yml
2016-11-02 22:28:39 -07:00
James Lu
9f43c0fe17 automode: fix typo in DB error message 2016-11-02 22:27:01 -07:00
James Lu
9950e0948f conf: split off absolute paths in confname
This fixes invalid database names such as "automode-/tmp/test.db" from being generated when PyLink is started with an absolute path to its config.

Closes #358.
2016-11-02 22:23:45 -07:00
James Lu
441bf5f048 README: link to utopia repository site 2016-10-28 20:05:35 -07:00
James Lu
e0802192f6 README: add Debian repository instructions 2016-10-28 20:04:39 -07:00
James Lu
72818412e1 faq: fix link to dependencies list 2016-10-27 20:50:10 -07:00
James Lu
522e6df73c Actually, use LF line endings for everything
(cherry picked from commit e49d36d8246d008abe6fe1a695d9952341fc354a)
2016-10-27 18:43:57 -07:00
James Lu
9f4c68f114 relay: skip channel TS check for Clientbot
(cherry picked from commit d230af1d5e3e14a12dc267bbd66b1bcf219e0ae9)
2016-10-22 20:59:34 -07:00
James Lu
d230af1d5e relay: skip channel TS check for Clientbot 2016-10-22 20:45:43 -07:00
James Lu
a5d97c15e7 relay: disable nick collide messages to debug 2016-10-20 20:13:17 -07:00
James Lu
2ed8b68c44 relay: allow configuring custom relay server suffixes
Closes #333.

(cherry picked from commit 39008334584023ddc540f7f62e45e7910ecd6783)
2016-10-20 19:10:47 -07:00
James Lu
4f3fa04cde docs/t/services-api: mention ServiceBot.join() 2016-10-16 18:48:29 -07:00
James Lu
c4e0923fb5 pylink: add a migration note for pid checking 2016-10-15 14:34:21 -07:00
James Lu
b750bd4d15 coremods/control: ignore errors when removing PID file 2016-10-15 14:31:13 -07:00
James Lu
774f30c940 pylink: exit with a non-zero code if pid check fails; reword error to be more helpful 2016-10-15 14:23:29 -07:00
Ken Spencer
bc4be815e4 coremods: make _shutdown remove running 'config'.pid 2016-10-15 16:50:25 -04:00
Ken Spencer
a30942669a pylink: Stop daemon if pid file exists and we're checking 2016-10-15 16:43:14 -04:00
Ken Spencer
aaadb63137 Add PID file checking 2016-10-15 16:09:35 -04:00
James Lu
ac270c200c PyLink 1.0.2 2016-10-14 22:51:33 -07:00
James Lu
a09ec494f6 README: cherry-pick updates from the devel branch
Squashed commit of the following:

commit 00279a148abe5f1f5413116b7736f758883c8287
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Mon Oct 10 15:55:36 2016 -0700

    README: update clientbot notes
    (cherry picked from commit 0db194726318ce2bb46b454ebe792b3896f4b80c)

commit 33cd90c2553977f17e61006f29288abbe9fe5cac
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Mon Oct 10 15:54:30 2016 -0700

    README: update IRCd notes
    (cherry picked from commit 44d139f610f51d51bd59b00015fa75ad9548ee4c)

commit 639e185977e8cfe98932310d132751ea9d720609
Author: James Lu <GLolol@overdrivenetworks.com>
Date:   Sun Sep 25 19:32:00 2016 -0700

    README: briefly mention protocols/clientbot

    (cherry picked from commit 1fbd9edc3b9601841b06d94aca907ea27fcab677)
2016-10-14 22:39:28 -07:00
James Lu
405b886ba2 clientbot: overload _getUid() to deal with nick collisions between virtual clients and Clientbot users
Closes #327.

(cherry picked from commit 05e2d6d060de5cc3a04712c74df7f377cafbf2d5)
2016-10-14 22:33:29 -07:00
James Lu
eb64190228 Clientbot: use a more specific realname fallback
(cherry picked from commit 288a2fffd79d2f34a3af36fbf222bf8fd9d7641c)

This is cherry-picked as a prerequisite for the next commit.
2016-10-14 22:33:10 -07:00
James Lu
05e2d6d060 clientbot: overload _getUid() to deal with nick collisions between virtual clients and Clientbot users
Closes #327.
2016-10-14 22:29:13 -07:00
James Lu
9e6e30324f setup: reword fallback version format & warnings
Also, change the suffix from -dirty to -nogit.

(cherry picked from commit a4e1f2a4ab78bd8511378088ef785a397e76ad02)
2016-10-14 21:30:54 -07:00
James Lu
a4e1f2a4ab setup: reword fallback version format & warnings
Also, change the suffix from -dirty to -nogit.
2016-10-14 21:30:43 -07:00
James Lu
0db1947263 README: update clientbot notes 2016-10-10 15:55:36 -07:00
James Lu
44d139f610 README: update IRCd notes 2016-10-10 15:54:30 -07:00
James Lu
7917502799 clientbot: make oper status tracking a network-specific option 2016-10-09 19:04:59 -07:00
James Lu
288a2fffd7 Clientbot: use a more specific realname fallback 2016-10-07 21:08:55 -07:00
James Lu
5c2e7e9324 Clientbot: unconditionally block MODE hooks if we're the sender
This is another check to prevent possible infinite loops in MODE syncing.
2016-10-07 20:54:52 -07:00
James Lu
72ca41df33 Irc, clientbot: disallow unsetting bans that don't exist
This fixes an infinite loop when:
- Clientbot modesync is enabled
- 2 or more clientbot linked networks show unsetting modes that weren't enabled (e.g. charybdis)
- A user removes a ban

The workaround in clientbot prevents this process from triggering an infinite loop when a mode change acknowledgement is received for unsetting a non-existant ban,
though multiple -b mode changes may still be seen due to race conditions in updating the various networks' states.
2016-10-07 20:54:52 -07:00
James Lu
386c71475a servermaps: removing leading - from entries 2016-10-07 18:51:31 -07:00
James Lu
e2e5de009b Merge branch 'master' into devel
Bump version to 1.1-dev
2016-10-05 20:31:16 -07:00
James Lu
8bffffa000 example-conf: comment out filerotation: so it doesn't become an empty, null-valued block 2016-10-05 20:30:32 -07:00
James Lu
e22d75d1a1 setup: install README.md as well ... 2016-10-05 19:22:23 -07:00
James Lu
738f027f33 PyLink 1.0.1 2016-10-05 19:13:00 -07:00
James Lu
613bece13a .gitattributes: force .py and .md to LF
(cherry picked from commit 8acdfc81c1d40250f2eac2986a6f15f06a57303f)
2016-10-05 19:08:07 -07:00
James Lu
8486d7c85b setup.py: Ship the VERSION file as well 2016-10-05 19:07:37 -07:00
James Lu
ec4e71c8cf Irc: demote unknown user errors in parseModes() to DEBUG
Some ancient services like Anope 1.8 set SVS2MODE +d on users when they connect, even if the user quits right after. Due to lag we may receive the MODE after the QUIT instead of before.
2016-10-02 22:09:33 -07:00
James Lu
844a4d5f19 README: add missing notes for ratbox 2016-10-01 16:45:46 -07:00
James Lu
aa0d1596b0 ts6: don't crash when CHGHOST target is a nick instead of UID
(cherry picked from commit 4dcbc85a816eee8d509940dfefea9bdd078bb56d)
2016-10-01 13:43:18 -07:00
James Lu
9997fa9306 docs/t: update protocol modules list 2016-10-01 13:42:48 -07:00
James Lu
36e18929de docs, hybrid, ratbox: Mode definition updates 2016-10-01 13:41:11 -07:00
James Lu
eb79f77bd2 ratbox: fixes for mode name consistency 2016-10-01 13:40:10 -07:00
James Lu
e4e00b4cd3 README: add ratbox to supported IRCds 2016-10-01 13:00:20 -07:00
James Lu
19c5a8c64f ratbox: update mode definitions 2016-10-01 13:00:04 -07:00
James Lu
a4c6a72a9c README: move nefarious to Primary support 2016-10-01 12:44:18 -07:00
James Lu
4dd0709f66 protocols, core: remove unused imports 2016-10-01 12:43:12 -07:00
James Lu
625e7b8aae ratbox: handle ENCAP LOGIN (#338) 2016-10-01 12:39:37 -07:00
James Lu
d943a8286f ratbox: fix typo in outgoing REALHOST 2016-10-01 00:40:20 -07:00
James Lu
4dcbc85a81 ts6: don't crash when CHGHOST target is a nick instead of UID 2016-10-01 00:34:38 -07:00
James Lu
bd9885182e ratbox: stub updateClient to prevent hostname desyncs
In ratbox, arbitrary host changing via CHGHOST is not supported.
2016-10-01 00:20:54 -07:00
James Lu
39987b6dcc ts6: supply SAVETS_100 capability for ratbox 2016-10-01 00:11:50 -07:00
James Lu
d6cb5c1ed0 ratbox: implement REALHOST (#338) 2016-09-30 23:46:23 -07:00
James Lu
f618feea26 Initial protocol support for Ratbox (#338) 2016-09-30 23:33:27 -07:00
James Lu
d9fdd9dfcb ts6: modularize required capabilities 2016-09-30 23:33:04 -07:00
James Lu
1cb320f5f4 clientbot: only send MODE if there are modes left after filtering 2016-09-25 20:21:01 -07:00
James Lu
57b566286d relay: make clientbot modesync more configurable (#287) 2016-09-25 20:07:16 -07:00
James Lu
8dd0cb19af clientbot: fix outgoing mode filtering 2016-09-25 20:07:16 -07:00
James Lu
1fbd9edc3b README: briefly mention protocols/clientbot 2016-09-25 20:07:16 -07:00
James Lu
4222cc30a8 relay, clientbot: implement clientbot mode sync
Closes #287.
2016-09-25 20:07:16 -07:00
James Lu
13a42c17b2 servermaps: skip clientbot networks (servers aren't properly tracked) 2016-09-24 16:36:28 -07:00
James Lu
438838f81d ServiceBot: handle autojoin additions even if bots haven't spawned yet 2016-09-24 12:33:57 -07:00
James Lu
1c60ad7251 services_support: honour joinmodes settings on kick-rejoin 2016-09-24 12:22:12 -07:00
James Lu
7d20b70d33 relay_cb: fix UnboundLocalError when kicking a service bot 2016-09-24 12:20:18 -07:00
James Lu
b9d8ec5039 relay: listen to PYLINK_SERVICE_JOIN from services_support 2016-09-24 12:13:33 -07:00
James Lu
eb18a6cf67 Bump VERSION to 1.0.0-dev 2016-09-24 12:08:46 -07:00
James Lu
dfa75f6606 servermaps: simplify output format
Drawing all the |'s and `'s is a rather complex process, so just replace this with bullet-point output.
2016-09-24 12:02:43 -07:00
James Lu
60b595ea6f servermaps: fix help for 'localmap' 2016-09-24 11:58:28 -07:00
James Lu
595bceda2e servermaps: implement network maps over relay!
Local-only map is available too using the 'localmap' command.
2016-09-24 11:56:37 -07:00
James Lu
68c247f764 New servermaps plugin: displays network /map's from the PyLink server's perspective 2016-09-24 11:19:33 -07:00
James Lu
d59732f6dd automode: simplify join routines to use ServiceBot.join() (#326) 2016-09-23 23:43:27 -07:00
James Lu
a040c3c7d2 ServiceBot: modularize join() for explicit channel joining (#326) 2016-09-23 23:43:07 -07:00
James Lu
0cce6ca488 service_support: allow specifying modes to join with for each service (#326)
This updates the example config to, by default, join Automode bots as op in channels.
2016-09-23 23:10:38 -07:00
James Lu
3900833458 relay: allow configuring custom relay server suffixes
Closes #333.
2016-09-23 22:49:04 -07:00
James Lu
d05917222d relay: clobber colour codes in hosts
(cherry picked from commit b467da13b12b87b972714d89129e7e93f11e7137)
2016-09-20 07:01:16 -07:00
James Lu
df50b7b137 bots: allow JOIN/NICK/QUIT on ServiceBot clients
(cherry picked from commit 1c86f3200302c3398a448436be79e507601b2f9c)
2016-09-20 07:01:16 -07:00
James Lu
b467da13b1 relay: clobber colour codes in hosts 2016-09-20 06:58:04 -07:00
James Lu
e0f56a157d relay_cb: remove dark gray from colours index
It's too hard to read on a dark background.
2016-09-19 21:56:12 -07:00
James Lu
8acdfc81c1 .gitattributes: force .py and .md to LF 2016-09-19 18:23:25 -07:00
James Lu
1c86f32003 bots: allow JOIN/NICK/QUIT on ServiceBot clients 2016-09-19 18:23:11 -07:00
James Lu
c62dd272d6 relay_clientbot: display the remote channel in join/kick/etc relay, not the local channel 2016-09-19 17:40:50 -07:00
James Lu
e60c020634 relay_clientbot: redo color hashing to be more unique 2016-09-19 17:40:33 -07:00
James Lu
fe5a40d632 ts6: handle legacy UID by wrapping around EUID 2016-09-18 14:13:05 -07:00
James Lu
a7662f8327 PyLink 1.0.0 2016-09-16 22:25:09 -07:00
James Lu
c94a92bd5f Document the PyLink release process 2016-09-16 22:01:10 -07:00
James Lu
c690916da9 example-conf: fix inverted config desc. for relay:show_netsplits 2016-09-16 21:07:32 -07:00
James Lu
b9d6efa677 docs: refresh Automode guide with permissions info and more (#284)
Also... fix the line endings to LF
2016-09-12 20:21:34 -07:00
James Lu
e3627e4721 Normalize line endings 2016-09-12 20:16:56 -07:00
James Lu
8589feaedf protocols: fix incomplete renames from c5c77eeb9784101d690eb4252988787273ad8ea4 2016-09-12 20:16:56 -07:00
James Lu
da24b85ccd example-conf: more consistent default nick for Automode 2016-09-12 20:16:52 -07:00
James Lu
eaa09d68ce Automode: limit 'list' permissions on all channels to opers 2016-09-12 11:26:31 -07:00
James Lu
1eb48c63e5 README: actually, let's make these links more topical 2016-09-10 14:31:21 -07:00
James Lu
9adab9f6f4 README: add badges pointing to PyPI & webchat
Closes #332.
2016-09-10 14:28:31 -07:00
James Lu
9bb7cfc81b README: badges! 2016-09-09 07:28:53 -07:00
James Lu
b43d714011 clientbot: ignore WHO replies for clients we don't know
This fixes various KeyErrors, etc. when processing the WHO reply later on, as Clientbot doesn't track state for anyone who doesn't share a channel with or talks to the bot.

Reported by Kev`Bz via IRC.
2016-09-08 18:45:34 -07:00
James Lu
d8990e8439 example-conf: add debug log example 2016-09-08 18:02:55 -07:00
James Lu
1ffb1bb1ec example-conf: remove extraneous example line 2016-09-08 18:00:03 -07:00
James Lu
90f1999c6d clientbot: downgrade bad updateClient() calls to warning 2016-09-08 17:44:02 -07:00
James Lu
838ea6bfc4 relay: skip iterating over modetype definitions during reverse mode lookup
This fixes a bug when setting +l on UnrealIRCd, where the type C mode definition is coincidentally also equal to 'l'. Reported by kevin via IRC.
2016-09-07 20:37:57 -07:00
James Lu
ffa89f1e01 nefarious: fix UnboundLocalError when no modes are given on user introduction
Reported by Kev`Bz via IRC.
2016-09-07 06:57:03 -07:00
James Lu
bbdffc797d relay_clientbot: Implement relaying of text sent from service bots 2016-09-06 20:11:08 -07:00
James Lu
dd083b9b8d relay: remove extraneous comment 2016-09-06 20:11:08 -07:00
James Lu
a12ed6ad35 services_support: hack around nick clashes between service clients & real users in Clientbot
For #327. Essentially what this does is tack on a nick prefix to all service bots introduced on a Clientbot network, using characters invalid for regular nicks.
2016-09-06 20:04:19 -07:00
James Lu
aacc3149ce fantasy: don't error when bots are removed while processing (e.g. on shutdown) 2016-09-06 18:06:29 -07:00
James Lu
e2747839d1 1.0-beta1 2016-09-03 00:48:33 -07:00
James Lu
c5c77eeb97 protocols: chandata->channeldata for MODE and JOIN hooks 2016-09-02 17:52:19 -07:00
James Lu
4a80b2ce1e setup.py: install example-permissions.yml as a data file 2016-09-02 17:45:01 -07:00
James Lu
d1e2dfcf61 clientbot: only call spawnClient for new message sources after irc.pseudoclient is set 2016-09-01 13:08:20 -07:00
James Lu
4d4dbb7764 Revert "clientbot: return existing PUIDs in spawnClient if nick exists"
This reverts commit 113fbf9eb8d401d75b21507b5d0cca53b2454f7a.

Incomplete fix: would confuse virtual service bots and external users if they had the same nick.
2016-09-01 13:00:27 -07:00
James Lu
113fbf9eb8 clientbot: return existing PUIDs in spawnClient if nick exists
This fixes some silly UID duplication with FNC handling.
2016-08-31 23:22:24 -07:00
James Lu
ae63f72cf9 clientbot: decouple inbound and outbound nick changes 2016-08-31 23:12:45 -07:00
James Lu
9bda4094e4 clientbot: handle pre-auth FNC better
Closes #321.
2016-08-31 23:05:36 -07:00
James Lu
be960bf27b clientbot: handle numerics 463 to 465 as fatal error 2016-08-31 22:46:46 -07:00
James Lu
cf5898fb45 clientbot: only send updateClient() hooks if something changes
Closes #323.
2016-08-31 22:32:12 -07:00
James Lu
9b38ca7d68 automode: join modebot client on setacc
Reported by kevin via IRC.
2016-08-31 22:23:55 -07:00
James Lu
a25a6b7e8d Merge branch 'master' into devel 2016-08-31 22:08:47 -07:00
James Lu
a99cab9130 Merge branch 'master' into devel 2016-08-31 22:07:32 -07:00
James Lu
eeca2c080d README: update webchat link 2016-08-31 22:07:13 -07:00
James Lu
c4a68d2f73 README: fix formatting 2016-08-31 22:05:32 -07:00
James Lu
02f3c71e8b README: more thorough description of branches & installation 2016-08-31 22:03:43 -07:00
James Lu
e85cc684a1 relay_cb: respect allow_clientbot_pms in 'rpm' (#292) 2016-08-31 19:51:52 -07:00
James Lu
7eb5e59842 relay_cb: implement outgoing PMs from clientbot networks via an 'rpm' command
Closes #292.
2016-08-31 19:48:17 -07:00
James Lu
7817898c14 relay_cb: distinguish between PM and private notice, switch to irc.msg() 2016-08-31 18:30:51 -07:00
James Lu
0a436cdf4c Irc: make loopback hook in msg() optional 2016-08-31 18:28:13 -07:00
James Lu
e06a6ae5bd example-conf: add an autoperform example 2016-08-31 18:10:51 -07:00
James Lu
663d03ed2c relay/clientbot: begin work on outgoing PMs to clientbot users (#318)
This still needs to implement PMs going the other way around, and should eventually distinguish between PMs and private notices.
2016-08-31 14:08:28 -07:00
James Lu
7bae4062b1 automode: log successful sync/add/remove/clear events
Closes #320.
2016-08-31 13:32:50 -07:00
James Lu
e903a8226a permissions: move admin login clause into checkPermissions()
This is more persistent.
2016-08-31 13:32:42 -07:00
James Lu
53de6542f6 clientbot: in nick(), make sure irc.pseudoclient exists 2016-08-27 20:39:35 -07:00
James Lu
42a104534a unreal: ensure type safety from last commit 2016-08-27 19:19:47 -07:00
James Lu
8f8cd95395 unreal: use umode +xt instead of SETHOST in spawnClient
This is to ensure vHosts for all PyLink clients are respected.
2016-08-27 19:14:37 -07:00
James Lu
1c4cb94a13 relay: block /OJOIN in claim 2016-08-27 19:09:02 -07:00
James Lu
ae94bec6b8 protocols: add a chandata key to SJOIN hook payloads 2016-08-27 18:56:36 -07:00
James Lu
3c7b201f57 protocols: rename 'oldchan' in MODE payloads to 'chandata' 2016-08-27 17:46:35 -07:00
James Lu
87757a60a3 Irc: rename 'chandata' in SQUIT payload to 'channeldata' 2016-08-27 17:42:07 -07:00
James Lu
c68c941c1d Irc.msg: break on empty text strings
Closes #306.
2016-08-27 09:52:01 -07:00
James Lu
9e7d0a50ca Revert "ServiceBot: fall back to a space in 'help' when stripped text is empty"
This reverts commit 7a3c8ab637d9b49e5b9dca5a3f4ec8c226b85573.
2016-08-27 09:49:59 -07:00
James Lu
556b388a4e core: Rehashable permissions; import coremods on start 2016-08-25 12:07:55 -07:00
James Lu
6af8e77ee1 permissions: apply add/removeDefaultPermissions on the right list 2016-08-25 12:07:36 -07:00
James Lu
f890ddac1b permissions, automode: work on default permissions & add example permissions config (#190)
- Fix possible type errors in add/removeDefaultPermissions by converting permlist values to sets.
- Fix wrong permission string being checked in automode.<command>.#channel
- automode: register and unregister default permissions on load/unload.
- permissions: add an 'also_show' argument to checkPermissions(), to display alternative permissions that weren't directly checked.
2016-08-25 11:45:57 -07:00
James Lu
03a780f397 automode: reorder functions in a way that makes more sense 2016-08-25 11:10:55 -07:00
James Lu
06cbbbb019 ServiceBot: display the NotAuthorizedError argument raw 2016-08-25 00:58:19 -07:00
James Lu
b4b772354c permissions: fix inverted permissions list lookup 2016-08-25 00:58:19 -07:00
James Lu
104c0cef4b automode: switch from irc.checkAuthenticated to new-style permissions 2016-08-25 00:58:19 -07:00
James Lu
91e39b7df9 WIP Permissions API (#190) 2016-08-25 00:45:05 -07:00
James Lu
5908776a86 API CHANGE: Rename NotAuthenticatedError -> NotAuthorizedError 2016-08-25 00:43:44 -07:00
James Lu
7d2b22630d ServiceBot: display custom error messages for NotAuthenticatedError 2016-08-25 00:41:53 -07:00
James Lu
945818e1d8 Refresh protocol module spec for 0.10 (#284)
Reword various things, and update links to source code references. Also, mention the following:

- conf_keys validation (#282)
- Topic acceptance rules (or rather, the lack thereof: #277)
2016-08-24 23:25:36 -07:00
James Lu
3e2c6ea1b7 launcher: show VCS version in "pylink -v"
(cherry picked from commit a04bf6119e69343f69d37f75e1382d99f2c952db)
2016-08-24 22:58:19 -07:00
James Lu
441f2244a9 relay: fix overzealous host normalization due to variable replacement during iteration
(cherry picked from commit 09c98f66ff34d98127b8fa82aa91b6ae70f491b2)
2016-08-24 22:58:19 -07:00
James Lu
814c714145 clientbot: fix nick() using the wrong arguments
I should learn to proofread...

Also, outgoing NICK changes should not implicitly update the state; we should wait for the IRCd's acknowledgement instead.
2016-08-24 22:54:53 -07:00
James Lu
a04bf6119e launcher: show VCS version in "pylink -v" 2016-08-23 07:28:48 -07:00
James Lu
09c98f66ff relay: fix overzealous host normalization due to variable replacement during iteration 2016-08-21 18:06:53 -07:00
James Lu
29bfe108fe clientbot: only send SQUIT payload if nicks are affected 2016-08-21 17:43:10 -07:00
James Lu
5444b808b1 Revert "relay_clientbot: lowercase network name (stylistic choice)"
This reverts commit 42da216f5db4f11fe9305b1025e59f9f3aa5a7ec.
2016-08-21 17:28:33 -07:00
James Lu
a546bae341 Irc: make throttle time configurable per server (defaults to 0.01s) 2016-08-21 17:25:09 -07:00
James Lu
7a5b64bdc9 Irc: implement basic message queueing (1 message sent per X seconds)
Ref #293.
2016-08-21 17:12:51 -07:00
James Lu
583e4c65c8 Bump version to 0.10-alpha1 2016-08-21 17:12:50 -07:00
James Lu
e171708aba PyLink 0.9.2 2016-08-21 16:58:07 -07:00
James Lu
689c0dd45b relay: {} are valid nick chars too...
(cherry picked from commit b572c58223c6d9b2e7ec0045f809767bebffe625)
2016-08-20 20:54:42 -07:00
James Lu
8cf771be2f ts6: fix typo 2016-08-20 17:36:56 -07:00
James Lu
b572c58223 relay: {} are valid nick chars too... 2016-08-20 17:36:24 -07:00
James Lu
affe54b47f log: configurable file rotation (size & backup count) 2016-08-17 22:01:00 -07:00
James Lu
3b93a521d6 log: import 'conf' in a way that works with REHASH 2016-08-17 22:00:34 -07:00
James Lu
126a07bdf6 core: rehashable file loggers with log rotation support
Closes #176. Closes #315.
2016-08-17 21:42:18 -07:00
James Lu
06ecc89603 Merge remote-tracking branch 'origin/master' into devel
Conflicts:
	README.md
	docs/faq.md
	protocols/unreal.py
2016-08-17 21:22:31 -07:00
James Lu
e9088792af unreal: support TSCTL alltime
Closes #228.
2016-08-17 21:21:18 -07:00
James Lu
a1e7c89027 FAQ: update link to dependencies list 2016-08-17 13:59:33 -07:00
James Lu
9d582661b7 README: reformat, add more verbose setup guide
This mentions multiple ways of installation now: via PyPI or source.

Closes #310.
2016-08-17 13:58:07 -07:00
James Lu
44744fa510 FAQ: reorder issues based on perceived importance 2016-08-14 18:06:10 -07:00
James Lu
bf36d58612 faq: mention errors caused by tabs in config (#316) 2016-08-14 18:03:50 -07:00
James Lu
2d89fd44cd faq: reformat, add "one-way clientbot" issue 2016-08-14 11:34:25 -07:00
James Lu
e1fab8c153 relay: fix a typo in comment 2016-08-13 11:40:01 -07:00
James Lu
1c83f59baa relay: rewrap some poorly placed comments 2016-08-13 11:03:58 -07:00
James Lu
c20dd07a2d inspircd: implement /ALLTIME support (#228) 2016-08-12 19:36:40 -07:00
James Lu
94aee8f05c Implement /TIME support (#228) 2016-08-12 19:19:09 -07:00
James Lu
a76bd8c5b2 core: make hostmask fetching a shared function 2016-08-12 19:18:56 -07:00
James Lu
63f40c9565 relay: delay RELAY_JOIN hooks to a second loop. Closes #311. 2016-08-12 18:47:12 -07:00
James Lu
17127dd131 clientbot: send hook payloads for external updateClient
Closes #300.
2016-08-12 18:47:12 -07:00
James Lu
6828d032b4 Irc: RFC-standard umode +s does not take an argument 2016-08-11 18:14:35 -07:00
James Lu
e75c0d075f relay: on clientbot, explain _why_ calling LINK twice is needed 2016-08-11 12:51:08 -07:00
James Lu
b4e83e7b1a clientbot: basic prefix mode detection (halfop, admin, owner) 2016-08-11 12:46:05 -07:00
James Lu
226089cc3c clientbot: forward SJOIN on the main client as JOIN (#299) 2016-08-11 11:24:23 -07:00
James Lu
3a0a2c7f1c clientbot: don't update state on join()
Wait for NAMES instead to make sure that the join attempt actually succeeded. #299
2016-08-11 11:23:41 -07:00
James Lu
7a0a013c43 clientbot: suppress mode change hooks for internal clients 2016-08-10 21:07:20 -07:00
James Lu
84eee41734 example-conf: rewrap clientbot sample, add autoconnect entry 2016-08-10 13:24:57 -07:00
James Lu
ffdc313376 Irc: make hostname field optional in version()
Reported by Rascle via IRC.
2016-08-10 13:23:24 -07:00
James Lu
d1dccf8dcc example-conf: update examples for name resolving
This is tested now. It works fine.
2016-08-10 11:28:35 -07:00
James Lu
f7b93e474c unreal: request VHP capability so cloaked hosts for Unreal3.2 users are sent
(cherry picked from commit 671d6d1893629fbd05b09b301aa8c134a9772c78)
2016-08-09 18:48:14 -07:00
James Lu
84daec0f07 unreal: fix services login tracking for UnrealIRCd 3.2 + Anope 1.8
More specifically, if the +d argument in SVS2MODE is a non-zero integer, ignore it and use the user's nick as account name instead.

(cherry picked from commit 7dcabf072b7c84b269e4c2885525aad02038b434)
2016-08-09 18:48:14 -07:00
James Lu
8341667bdd relay: convert / to . in hostnames on hybrid
Also, switch this check to a whitelist instead of a blacklist for better compatibility.

(cherry picked from commit 990f24938b765f29d0674888c5ac09f155fc5609)
2016-08-09 18:48:14 -07:00
James Lu
bdadb27466 relay: reverse getPrefixModes() output so the right mode order is passed to sjoin()
This prevents users from bursted as "+@~UID" instead of "~@+UID", for example.
2016-08-09 18:39:13 -07:00
James Lu
1660705658 exec: repr() eval output for proper formatting
This ensures that results that are an empty string, for example, are properly formatted as ''.
2016-08-09 10:57:31 -07:00
James Lu
6f0be9400b docs/t: add clientbot to protocol-modules 2016-08-09 00:07:27 -07:00
James Lu
6dd882a69b README: it's better if I actually finish my sentences... 2016-08-09 00:03:01 -07:00
James Lu
48680051e1 Refresh README (IRCd notes), FAQ 2016-08-09 00:01:19 -07:00
James Lu
8ac014709b inspircd: track MODSUPPORT and required modules for updateClient()
m_chgident.so, m_chghost.so, and m_chgname.so are required for ident, host, and real name setting to work, respectively.
2016-08-08 23:40:48 -07:00
James Lu
72f9019300 clientbot: remove self.irc.users print 2016-08-08 23:40:24 -07:00
James Lu
c09ff6a706 relay_clientbot: fix config key name 2016-08-08 22:23:42 -07:00
James Lu
1b747bf09d relay_clientbot: skip relaying non-PRIVMSGs for X seconds after connect
This can be configured via the option relay::clientbot_startup_delay, and defaults to 5 seconds.
2016-08-08 22:20:31 -07:00
James Lu
df4277e530 hooks-reference: update keys for SJOIN 2016-08-08 21:06:49 -07:00
James Lu
3878ae9e0d relay_clientbot: skip to next channel for SQUIT payloads if no nicks are affected 2016-08-08 21:05:55 -07:00
James Lu
8636280b91 relay_clientbot: use channel specific user lists when relaying SQUIT
Closes #312.
2016-08-08 20:59:15 -07:00
James Lu
3633a41e4f Protocol: return a mapping of channels->lists of nicks in SQUIT payloads 2016-08-08 20:58:34 -07:00
James Lu
b096c7a2e3 relay_clientbot: fix wrong key for clientbot_styles 2016-08-08 18:50:36 -07:00
James Lu
12e1a0edff Move handle_error() to the generic protocol 2016-08-08 18:12:07 -07:00
James Lu
73a70d6952 unreal: declare support for ESVID
This capability declares support for account name arguments in service stamps (+d argument in SVS2MODE).

Realistically this doesn't appear to affect any S2S communication, because services packages only check for ESVID support on their uplink, while SVS2MODE is passed raw from services->IRCd->PyLink.
2016-08-08 17:28:45 -07:00
James Lu
671d6d1893 unreal: request VHP capability so cloaked hosts for Unreal3.2 users are sent 2016-08-08 17:05:28 -07:00
James Lu
7dcabf072b unreal: fix services login tracking for UnrealIRCd 3.2 + Anope 1.8
More specifically, if the +d argument in SVS2MODE is a non-zero integer, ignore it and use the user's nick as account name instead.
2016-08-08 16:35:10 -07:00
James Lu
e8fd97d3ba example-conf: mention relay_clientbot 2016-08-07 22:08:44 -07:00
James Lu
990f24938b relay: convert / to . in hostnames on hybrid
Also, switch this check to a whitelist instead of a blacklist for better compatibility.
2016-08-07 15:16:37 -07:00
James Lu
c40895e221 Merge branch 'master' into devel 2016-08-06 20:04:18 -07:00
James Lu
05bc7e292f Release 0.9.1 2016-08-06 20:00:57 -07:00
James Lu
8b59d7d7a2 Merge branch 'wip/pypi-prep' into HEAD 2016-08-06 20:00:49 -07:00
James Lu
81ffa93bad relay: block attempts to LINK if local TS is lower than remote
Closes #301.
2016-08-04 13:04:32 -07:00
James Lu
9448bc86fd Protocol: abort when receiving SQUIT to our own server 2016-08-04 12:55:24 -07:00
James Lu
acd0c673cb relay_cb: use safe_substitute, rename some fields, add SQUIT/SJOIN support
Closes #294. Closes #304. This renames the "nick" field to "sender", and "identhost" to "sender_identhost", to be less ambiguous when a message sender is a server.
2016-08-04 12:50:38 -07:00
James Lu
b36ce36451 protocols: send old IrcServer object (serverdata) in SQUIT hooks 2016-08-04 12:50:04 -07:00
James Lu
1c0900b29d clientbot: introduce virtual SQUIT and SJOIN hooks 2016-08-04 11:48:57 -07:00
James Lu
62099e6078 clientbot: move virtual ENDBURST hook to end of MOTD
Possible fix for #299.
2016-08-04 11:10:51 -07:00
James Lu
72da00d23c relay: block clientbot networks from hosting relays 2016-08-04 10:55:00 -07:00
James Lu
7a0fd1caa3 relay: rework "target in channel" requirements for clientbot links
Closes #305.
2016-08-04 10:47:06 -07:00
James Lu
e50684e367 Merge branch 'master' into devel 2016-08-04 10:29:17 -07:00
James Lu
a9fe0499e6 services_support: ignore invalid autojoin channels
Reported by @rvzm on IRC.

Closes #308.
2016-08-04 10:26:00 -07:00
James Lu
347ddc112d setup: refresh classifiers 2016-08-04 00:27:50 -07:00
James Lu
1e4044fdf7 setup.py: convert Markdown to RST using pypandoc 2016-08-04 00:27:33 -07:00
James Lu
66067c272f relay: limit hosts to 63 chars, not 64 2016-08-03 00:21:30 -07:00
James Lu
5e1cb67dcd Bump version to 0.10-dev2 2016-08-02 23:59:55 -07:00
James Lu
197d9fc14a commands: show topic only if it exists, disable TS output on clientbot 2016-08-02 23:57:18 -07:00
James Lu
0fc0f104bd clientbot: pass realhost and IP options to IrcUser 2016-08-02 23:36:20 -07:00
James Lu
a6258dd973 changehost: more friendly error when an expansion field is unavailable 2016-08-02 23:04:46 -07:00
James Lu
1cda5023c8 changehost: copy args before cloning, make $host expansion optional 2016-08-02 22:56:59 -07:00
James Lu
1ffbd0eea8 example-conf: fix typo 2016-08-02 22:27:14 -07:00
James Lu
02b5f0b7e6 changehost: explicitly forbid $host from being expanded
This will cause recursion whenever applyhost is ran and a user matches a mask based on real host or IP, which haven't changed. Any suffix or prefix applied to $host will be reapplied, often leading to invalid hosts that are too long.
2016-08-02 22:17:23 -07:00
James Lu
592ae4053e Merge branch 'master' into devel 2016-08-02 21:11:00 -07:00
James Lu
d855c6b2ea example-conf: support passwordless UnrealIRCd links by setting recv/sendpass to *
(cherry picked from commit a018dd19b58286441bff315c187b5dcfd22a62b3)
2016-08-02 21:10:55 -07:00
James Lu
a37eb57d6c example-conf: update default SID 2016-08-02 21:10:10 -07:00
James Lu
524c55e6f1 example-conf: less confusing channels: description 2016-08-02 21:08:18 -07:00
James Lu
3126560fb9 ServiceBot: use newline-agnostic str.splitlines() to split up docstrings 2016-08-02 12:38:15 -07:00
James Lu
7a3c8ab637 ServiceBot: fall back to a space in 'help' when stripped text is empty
This makes sure things like newlines actually show when displaying docstrings. Closes #306.
2016-08-02 12:32:06 -07:00
James Lu
d5c5a34467 Merge branch 'master' into devel
Conflicts:
	protocols/ts6_common.py
2016-08-01 18:07:34 -07:00
James Lu
f60e17907a hybrid, unreal: switch to irc.toLower() for channels 2016-08-01 18:06:38 -07:00
James Lu
8e5c58d897 commands: explicitly sort mode lists in 'showuser' and 'showchan'
(cherry picked from commit eb032eb7f96dbab4546bad563089eb53f863a148)
2016-08-01 17:59:31 -07:00
James Lu
c9ce4d1507 Irc: make sorting in joinModes() an option, and explicitly enable it in WHOIS output
This prevents mode lists from being sorted when they really shouldn't be, such as when relaying mode changes ("+qo nick nick" became +oq nick nick").

(cherry picked from commit 4b27ebbee4f06ba64bdd0c25489051ebe2a7ff8d)
2016-08-01 17:59:31 -07:00
James Lu
eb2bc68c07 relay: catch errors on remove_network() and ignore them
(cherry picked from commit f2a5e1dc78a31b66df58aa3fb65854fe2d87dd21)
2016-08-01 17:59:31 -07:00
James Lu
9a59c68370 protocols: strip leading prefix modes before checking whether msg target is a channel
(cherry picked from commit 2c7b5669bd761672544035ccf7679db0899a6ab0)
2016-08-01 17:59:31 -07:00
James Lu
f061a2fc68 ts6: replace str.lower() with irc.toLower() for channel names
(cherry picked from commit 791b124cf70eb63b21aa99f7f4a88038e5cd3f64)
2016-08-01 17:59:31 -07:00
James Lu
eb032eb7f9 commands: explicitly sort mode lists in 'showuser' and 'showchan' 2016-07-31 21:46:33 -07:00
James Lu
4b27ebbee4 Irc: make sorting in joinModes() an option, and explicitly enable it in WHOIS output
This prevents mode lists from being sorted when they really shouldn't be, such as when relaying mode changes ("+qo nick nick" became +oq nick nick").
2016-07-31 21:45:38 -07:00
James Lu
ac358f4199 ts6: add CHW to required capabilities
This is implicitly used by relay when relaying @#channel messages.
2016-07-31 20:42:34 -07:00
James Lu
2ca3dfe689 ts6_common: rewrite =#channel messages to @#channel (charybdis +z support) 2016-07-31 20:38:25 -07:00
James Lu
f2a5e1dc78 relay: catch errors on remove_network() and ignore them 2016-07-31 20:36:27 -07:00
James Lu
2c7b5669bd protocols: strip leading prefix modes before checking whether msg target is a channel 2016-07-31 20:30:51 -07:00
James Lu
77a93b17ac ts6: handle ETB 2016-07-31 20:25:17 -07:00
James Lu
791b124cf7 ts6: replace str.lower() with irc.toLower() for channel names 2016-07-31 20:22:12 -07:00
James Lu
d911045dca Merge branch 'devel' of github.com:GLolol/PyLink into devel 2016-07-31 20:11:06 -07:00
James Lu
0cc405f51e classes: more tweaks to updateTS()
Really fixes #295. Closes #298.

(cherry picked from commit 168f9f972d8851bac72aa8adf6ff2254f3b45c13)
2016-07-30 21:25:37 -07:00
James Lu
168f9f972d classes: more tweaks to updateTS()
Really fixes #295. Closes #298.
2016-07-30 21:25:28 -07:00
James Lu
1ef89560e2 core: rename IrcUser.identified attribute to IrcUser.account 2016-07-29 20:16:05 -07:00
James Lu
afb035ae6b README: add inspircd 2.2 link, update juno version 2016-07-29 19:52:07 -07:00
James Lu
891039dba2 Merge branch 'master' into devel 2016-07-29 14:44:54 -07:00
James Lu
2326f8b818 Irc: fix TS handling comparing against the wrong TS on outgoing sjoin()
Closes #295.
2016-07-29 14:44:25 -07:00
James Lu
358e03f6ce ts6: don't unconditionally apply modes in outgoing sjoin() 2016-07-29 14:44:25 -07:00
James Lu
63a81b87d8 setup: explicitly forbid installing on Python 2 (#297)
(cherry picked from commit e1d857653e2f16b0620c1a25872a2c0ab758cbba)
2016-07-29 10:14:58 -07:00
James Lu
e1d857653e setup: explicitly forbid installing on Python 2 (#297) 2016-07-29 10:12:56 -07:00
James Lu
accd5b70b3 relay: show reasons when clientbot is force parted or kicked 2016-07-29 01:08:14 -07:00
James Lu
8eed60a8f5 nefarious: fix nameerror on start 2016-07-29 01:02:18 -07:00
James Lu
2bc066bacb clientbot: don't send duplicate operups (#289) 2016-07-29 00:56:14 -07:00
James Lu
3ad9362247 clientbot: fix oper tracking, add deoper tracking
Closes #289.
2016-07-29 00:54:47 -07:00
James Lu
6bc3191077 relay: only block cmodes from relaying on clientbot (#289) 2016-07-29 00:54:29 -07:00
James Lu
f457018f89 unreal: remove mixed_link option; this is now implied
pylink<->unreal4<->unreal3.2 links are stable enough.
2016-07-29 00:37:31 -07:00
James Lu
a018dd19b5 example-conf: support passwordless UnrealIRCd links by setting recv/sendpass to * 2016-07-29 00:32:02 -07:00
James Lu
f92cb55eb1 example-conf: add sample Clientbot config 2016-07-28 22:49:25 -07:00
James Lu
13c0e50358 Irc: make certfile/keyfile optional
This was never required for S2S links to work... Why did I think that?
2016-07-28 22:49:05 -07:00
James Lu
f4922743fc core: SID and hostname options are now optional (#282)
Hostname defaults to a fallback hardcoded in world.fallback_hostname, while SID defaults to None (protocol modules have to deal with this themselves)
2016-07-28 22:23:59 -07:00
James Lu
f45cb3a583 classes: Drop FakeIRC, FakeProto 2016-07-28 22:03:44 -07:00
James Lu
87c558537f unreal: refactor to use utils.PUIDGenerator
Closes #238.
2016-07-28 21:59:56 -07:00
James Lu
852bd74c3b core: enumerate our server after calling proto.connect()
This gives protocol modules a chance to manipulate their SID before it's added to the internal server list, replacing hacks previously used by the Nefarious and Clientbot modules.

This is the first step to sid-less servers :)
2016-07-28 21:50:51 -07:00
James Lu
c410de2fad Make server config validation protocol specific
Closes #282.
2016-07-28 21:34:00 -07:00
James Lu
85e786904c pylink: show a better error if the protocol module is missing 2016-07-28 21:33:13 -07:00
James Lu
c36c32082f inspircd: support SAKICK 2016-07-28 21:05:59 -07:00
James Lu
a56e464eb6 unreal: handle umode +r on burst as services login, even when no explicit accountname is set
Ref #296.

(cherry picked from commit 5800594d55a60787792073c120c16da02d1678f7)
2016-07-28 20:20:35 -07:00
James Lu
4fbb7b1791 unreal: remove obsolete comment about cloaked hosts
(cherry picked from commit e7ccfc9156f0f4734a9caefc30253164e5eac70f)
2016-07-28 20:20:35 -07:00
James Lu
94ae457a82 nefarious: fix usermode changes internally applying on the wrong target
A regression from fca23c7d5594ba098324c0236bd5f6f62488e93b overwrote the target UID with a nick before passing it into applyModes(), which raises an "unknown mode target" warning and causes the mode change to be dropped.

(cherry picked from commit af210638342651dcb86c83319231dbb5ed4de98d)
2016-07-28 20:20:35 -07:00
James Lu
bc5a508cd7 unreal: rework services login handling
This should now support SVS2MODE without account info, used by Anope versions < 2.0? Also, the protocol module no longer stores umode +r as an actual user mode, as doing so isn't needed.

Closes #296.

(cherry picked from commit 97d09c5015865e4236e13e855628beab9373338f)
2016-07-28 20:20:35 -07:00
James Lu
4e3d209831 Irc: rework TS handling again
(cherry picked from commit 0e535a916c3b72cd6f44668b1058ec5d77de5726)
2016-07-28 20:20:35 -07:00
James Lu
9233a94379 nefarious: forward CMODE and KICK through the server if the sender isn't opped
This prevents mode bounces, kick failures, and the HACK server notices from showing up.

(cherry picked from commit fca23c7d5594ba098324c0236bd5f6f62488e93b)
2016-07-28 20:20:35 -07:00
James Lu
5800594d55 unreal: handle umode +r on burst as services login, even when no explicit accountname is set
Ref #296.
2016-07-27 17:38:37 -07:00
James Lu
e7ccfc9156 unreal: remove obsolete comment about cloaked hosts 2016-07-27 17:38:09 -07:00
James Lu
8047186c58 plugins, coremods: remove usage of irc.conf (#273) 2016-07-27 17:02:04 -07:00
James Lu
af21063834 nefarious: fix usermode changes internally applying on the wrong target
A regression from fca23c7d5594ba098324c0236bd5f6f62488e93b overwrote the target UID with a nick before passing it into applyModes(), which raises an "unknown mode target" warning and causes the mode change to be dropped.
2016-07-27 16:55:59 -07:00
James Lu
97d09c5015 unreal: rework services login handling
This should now support SVS2MODE without account info, used by Anope versions < 2.0? Also, the protocol module no longer stores umode +r as an actual user mode, as doing so isn't needed.

Closes #296.
2016-07-27 16:55:59 -07:00
James Lu
0e535a916c Irc: rework TS handling again 2016-07-26 23:30:55 -07:00
James Lu
4002fb9da5 clientbot: only send AWAY when polling if the message has changed 2016-07-26 23:30:26 -07:00
James Lu
fca23c7d55 nefarious: forward CMODE and KICK through the server if the sender isn't opped
This prevents mode bounces, kick failures, and the HACK server notices from showing up.
2016-07-26 18:31:23 -07:00
James Lu
42da216f5d relay_clientbot: lowercase network name (stylistic choice) 2016-07-26 16:41:15 -07:00
James Lu
0d563eaa57 control/utils: unload the PyLink service bot on shutdown 2016-07-26 16:26:01 -07:00
James Lu
a4dbd8d09f control: handle SIGINT for clean shutdown on ctrl-c
Closes #280.
2016-07-26 16:16:23 -07:00
James Lu
fa1ce45bf3 relay: add configurable list of nick globs to always tag
Closes #276.
2016-07-26 16:01:42 -07:00
James Lu
7d0cd1d454 Merge branch 'master' into devel
Conflicts:
	VERSION
2016-07-25 11:15:11 -07:00
James Lu
8ce0ceee37 clientbot: properly track oper 2016-07-25 11:02:25 -07:00
James Lu
db9f362857 clientbot: use colon for realname in USER 2016-07-25 11:02:08 -07:00
James Lu
af027e2288 networks: support protocol module reloading
Closes #177.
2016-07-25 11:00:56 -07:00
James Lu
fcf364f958 clientbot: initialize users with umode +i by default 2016-07-25 00:00:27 -07:00
James Lu
fd8ba5edfc 0.9.0 2016-07-24 22:49:05 -07:00
James Lu
3c1090cb46 opercmds: make 'kick' treat channels case insensitively
(cherry picked from commit d008814de9deccb574a9edfb33a0443de28f05d7)
2016-07-24 22:15:48 -07:00
James Lu
3ba59f7c54 opercmds: make 'jupe' admin only
(cherry picked from commit a2c5b11194e2608c999af6b7c49f450c02188640)
2016-07-24 22:15:48 -07:00
James Lu
a28405c9ff exec: make pylinkirc and importlib accessible
(cherry picked from commit bcf5f612cc242e416c88ec7c4af0c05db6fcee7f)
2016-07-24 22:15:48 -07:00
James Lu
02bac384df commands: mention the VCS version as well
(cherry picked from commit 04e37ebd043e86483220987abaa611b42cefb7f6)
2016-07-24 22:15:48 -07:00
James Lu
b9e00c7261 setup: remove rolling package versions
This makes 'reload' after commit impossible, for example, because each version is installed in a separate folder. Also, versions from 'git describe' aren't compatible with PEP 440.

(cherry picked from commit bda39b48384fce4ff5b35ff224fe6686d326e25a)
2016-07-24 22:15:48 -07:00
James Lu
d008814de9 opercmds: make 'kick' treat channels case insensitively 2016-07-24 16:40:31 -07:00
James Lu
a2c5b11194 opercmds: make 'jupe' admin only 2016-07-24 16:40:23 -07:00
James Lu
c8ec2d9600 handlers: remove obsolete comment about WHOIS 2016-07-24 13:48:03 -07:00
James Lu
a905f74800 relay/clientbot: rework KICK handling to send the right thing at the right times
Some of the logic was inverted so that the Clientbot would try to kick invalid things like GLolol/ovd, and send clientbot KICK payloads when it's supposed to forward an actual kick.

This also fixes KICKs from servers not being relayed by clientbot.
2016-07-24 11:55:46 -07:00
James Lu
05972e500c clientbot: fix marking of internal/external servers 2016-07-24 11:52:34 -07:00
James Lu
bcf5f612cc exec: make pylinkirc and importlib accessible 2016-07-24 11:51:44 -07:00
James Lu
8fc310eb24 clientbot: clear who / kick state on connect 2016-07-24 11:15:33 -07:00
James Lu
832f22e7f0 relay: remove unnecessary "Relay plugin unloaded" quits 2016-07-24 11:13:51 -07:00
James Lu
dc364b3e9e relay: explicitly don't sync modes originating from clientbot 2016-07-24 11:11:54 -07:00
James Lu
62f78a60a9 commands: refactor showchan to use IrcChannel.getPrefixModes() 2016-07-24 11:03:23 -07:00
James Lu
04e37ebd04 commands: mention the VCS version as well 2016-07-24 10:59:25 -07:00
James Lu
e226846446 0.10-dev1 2016-07-23 22:46:14 -07:00
James Lu
bda39b4838 setup: remove rolling package versions
This makes 'reload' after commit impossible, for example, because each version is installed in a separate folder. Also, versions from 'git describe' aren't compatible with PEP 440.
2016-07-23 22:46:08 -07:00
James Lu
8dd0a904d2 relay_cb: use the full network name in prefixes 2016-07-23 22:30:25 -07:00
James Lu
05a5ec7e19 relay: don't show "real server" for clientbot users, it's not accurately tracked 2016-07-23 22:30:10 -07:00
James Lu
cf804ca84e example-conf: mention automode 2016-07-23 22:15:54 -07:00
James Lu
e342889937 corecommands: really fix 'reload'
(cherry picked from commit 150bfe8c663387f58d4fff820ead19a215191cae)
2016-07-23 22:02:08 -07:00
James Lu
150bfe8c66 corecommands: really fix 'reload' 2016-07-23 21:59:25 -07:00
James Lu
d3ca49ceeb Revert "automode: bandaid fix for "service already registered" errors on first load"
This reverts commit 3b62d0af3ec95085ea9683fe4d66bc01298acb66.
2016-07-23 21:56:08 -07:00
James Lu
795c163a69 Irc: use int values for ircmatch casemapping
The ircmatch version with casemapping variables hasn't been released on pip yet.

(cherry picked from commit 39675e15e870efb890d6923af71e395a925447ca)
2016-07-23 21:52:23 -07:00
James Lu
3b62d0af3e automode: bandaid fix for "service already registered" errors on first load
(cherry picked from commit e2a853c98ed5487344149ecf611e2171d9e1bf68)
2016-07-23 21:52:21 -07:00
James Lu
39675e15e8 Irc: use int values for ircmatch casemapping
The ircmatch version with casemapping variables hasn't been released on pip yet.
2016-07-23 21:51:26 -07:00
James Lu
e2a853c98e automode: bandaid fix for "service already registered" errors on first load 2016-07-23 21:46:55 -07:00
James Lu
38793654ea relay: error when someone attempts to PM a clientbot user
PMs aren't routed through Clientbot yet.
2016-07-23 21:09:25 -07:00
James Lu
51dcfb7d1e clientbot: don't leak private messages via the relay 2016-07-23 21:09:15 -07:00
James Lu
da9ab6ac90 Merge branch 'master' into devel 2016-07-23 20:57:26 -07:00
James Lu
b376aba591 docs/t: add Services API description
Closes #224.
2016-07-23 20:56:51 -07:00
James Lu
e56c9640dc ServiceBot: remove request/remove stubs for now 2016-07-23 19:57:05 -07:00
James Lu
7f7c8a8b51 Doxygen configuration & autorun script 2016-07-23 19:42:17 -07:00
James Lu
cadf0a336c relay: limit auto-rejoin-on-part to relay channels 2016-07-23 19:06:35 -07:00
James Lu
08525e8eba relay: treat forced parts to clientbot as clearchan 2016-07-23 19:03:07 -07:00
James Lu
0db9d4e69e relay/clientbot: support relaying KICK, QUIT, NICK, CTCP ACTION, and NOTICE 2016-07-23 18:48:27 -07:00
James Lu
3ec11680ea clientbot: handle nick conflicts on connect (43x) 2016-07-23 12:48:26 -07:00
James Lu
a662f93e15 clientbot: fix part/kick handling to only keep state for internal clients 2016-07-23 12:36:13 -07:00
James Lu
3e91118644 relay/clientbot: implement kick, join, part relaying 2016-07-23 12:25:52 -07:00
James Lu
fdaed4f700 relay_clientbot: configurable formatting, with defaults 1000x prettier than janus 2016-07-23 12:05:22 -07:00
James Lu
a402d1057e clientbot: send CLIENTBOT_KICK payloads for external clients 2016-07-23 12:04:32 -07:00
James Lu
a9be5d0dc7 relay: treat kicks to the service bot on clientbot networks as clear channel
This is so if the bot is kickbanned from a channel, it will remove all users to prevent ghosts on the channel.

* cbot.relay has kicked ChanServ/cbot from #test ((xdxdxd.xdxdxd/cbot) Clientbot was kicked from channel.)
2016-07-23 11:20:52 -07:00
James Lu
88e510b4cf clientbot: begin work on special hooks & move event relaying to a separate plugin 2016-07-23 01:16:26 -07:00
James Lu
7eaf074019 clientbot: poll WHO once every pingfreq 2016-07-23 00:06:11 -07:00
James Lu
91310164be clientbot: more descriptive default hostmasks 2016-07-22 22:52:06 -07:00
James Lu
734db841f1 clientbot: track failed KICKs with /NAMES & a timer
Suggested by @cooper.

This tracking system solves the problem of failed relay kicks causing channel desyncs, because what's seen as a successful KICK when PyLink is linked as a service might not forward
successfully on clientbot network. This can be caused due to the clientbot not being opped, the target being immune or having higher access than the clientbot, etc. When a NAMES reply
(delayed in this case) is received for any channel where an initial /WHO has already been received, a JOIN hook will be sent for any users on the kick queue to rejoin them on the relay.
2016-07-22 22:36:34 -07:00
James Lu
d41d123969 clientbot: send away hook and remove extraneous stub assignment 2016-07-22 00:45:37 -07:00
James Lu
ff807b3d47 clientbot: basic WHO handling, implement away() stub 2016-07-22 00:22:56 -07:00
James Lu
04ed7a8f5d clientbot: add MODE handling 2016-07-21 19:12:05 -07:00
James Lu
3d7e8f8420 clientbot: fix wrong args to _squit 2016-07-21 19:11:57 -07:00
James Lu
c6fcdd854c parseModes: give prefix modes precedence over mode types 2016-07-21 19:11:19 -07:00
James Lu
2d886367b5 clientbot: handle prefix modes in NAMES 2016-07-21 18:49:01 -07:00
James Lu
deddbde2b0 clientbot: implement updateClient for tracking client ident/host 2016-07-21 18:49:01 -07:00
James Lu
3e8ed35aea clientbot: stub kill() and numeric() 2016-07-21 18:16:15 -07:00
James Lu
95ee94e747 clientbot: implement outgoing INVITE 2016-07-21 18:14:23 -07:00
James Lu
a2043d6762 clientbot: make services clients manipulatable 2016-07-21 18:13:17 -07:00
James Lu
1507a87f31 clientbot: wait for KICK acknowledgement before updating state 2016-07-21 18:04:36 -07:00
James Lu
09ce38a53d clientbot: fix wrong variable 2016-07-21 00:11:15 -07:00
James Lu
dac043a1b6 clientbot: stub updateClient too 2016-07-21 00:04:49 -07:00
James Lu
357e8327c4 pylink: less ambiguous error when module isn't installed 2016-07-21 00:02:37 -07:00
James Lu
7a93c1bbb1 clientbot: capability negotiation (cmodes,umodes,prefixmodes,casemapping) 2016-07-20 23:59:22 -07:00
James Lu
eef54cd77d classes: Move ISUPPORT-style PREFIX and capabilities handling to Protocol 2016-07-20 23:59:22 -07:00
James Lu
75d88224f1 clientbot: document some functions 2016-07-20 23:59:22 -07:00
James Lu
d2a3a64293 clientbot: use rfc2812-style USER, kthx 2016-07-20 23:59:22 -07:00
James Lu
eb24e01619 relay: allow slashes for clientbot 2016-07-20 23:55:34 -07:00
James Lu
f970f760aa clientbot: *untested* autopreform support 2016-07-20 23:55:34 -07:00
James Lu
8fb3310d33 clientbot: properly stub a lot more functions 2016-07-20 23:55:34 -07:00
James Lu
dd0c5c7e63 clientbot: format kick reasons with the sender prefix 2016-07-20 23:55:34 -07:00
James Lu
e7ae6ddbff clientbot: in conditionals, don't break if irc.pseudoclient isn't set yet 2016-07-20 23:55:34 -07:00
James Lu
de618393c0 Fix SQUIT handling from 2f1a338 2016-07-20 23:55:34 -07:00
James Lu
387d47808c clientbot: add nick, kick handling; squit and sjoin stubs 2016-07-20 23:55:34 -07:00
James Lu
154421ffde Move SQUIT abstraction into core 2016-07-20 23:55:34 -07:00
James Lu
8b4b08f589 clientbot: names, join, part, quit handling 2016-07-20 23:55:34 -07:00
James Lu
027dfe46a4 clientbot: handle notice, privmsg
This is literally the world's most useless IRC bot now.
2016-07-20 23:55:34 -07:00
James Lu
b1b13a5c63 clientbot: fix args list 2016-07-20 23:55:34 -07:00
James Lu
8981d71fd0 protocols: rename _getOutgoingNick() -> _expandPUID() 2016-07-20 23:55:34 -07:00
James Lu
e0b254e6ad clientbot: ping/pong support 2016-07-20 23:55:34 -07:00
James Lu
58fa769ba0 clientbot: implement JOIN, uplink enumeration 2016-07-20 23:55:34 -07:00
James Lu
c6ed06ba61 utils: new splitHostmask function 2016-07-20 23:55:34 -07:00
James Lu
c52d542ed8 Initial Clientbot stub, with very rudimentary user handling (#144) 2016-07-20 23:55:34 -07:00
James Lu
1770058e1b Move ts6_common.parseTS6Args to ts6_common.parsePrefixedArgs 2016-07-20 23:55:34 -07:00
James Lu
0a0e19d4c2 conf: remove key checks for password fields
TODO: replace this with more verbose checks in protocol modules
2016-07-20 23:55:34 -07:00
James Lu
47a0a7f8c6 Make plugins/ and protocols/ namespaces 2016-07-20 23:55:26 -07:00
James Lu
f3b6e8f7ea setup.py: add ircmatch as requirement 2016-07-19 18:03:43 -07:00
James Lu
fab61729b9 conf: throw a specific error when PyYAML is missing 2016-07-19 17:47:39 -07:00
James Lu
3381dda884 Throw specific errors for missing ircmatch/setuptools libraries 2016-07-19 17:44:22 -07:00
James Lu
166adcf44d conf: add migration warning for the config file rename 2016-07-19 17:40:22 -07:00
James Lu
1629533242 pmodule-spec: add modes argument to sjoin() 2016-07-17 22:25:01 -07:00
James Lu
5d8f3036e7 README: add setuptools dependency & clarify --user
Thanks to kevin on KoaxIRC for reporting.
2016-07-17 20:26:32 -07:00
James Lu
8169a2b751 automode: fix typo in loadDB() 2016-07-17 15:19:48 -07:00
James Lu
916817443e docs: add Automode tutorial (#224) 2016-07-17 11:35:58 -07:00
James Lu
40197e5f57 automode: fix help syntax 2016-07-17 10:54:46 -07:00
James Lu
d7538bee1b automode: remove mentions to "setacc #channel mask -" 2016-07-17 10:50:18 -07:00
James Lu
fd3c2b521e faq: remove periods after links that might confuse editors 2016-07-17 10:50:18 -07:00
James Lu
f51a882d23 exttargets: verify target is in channel before calling getPrefixModes 2016-07-17 10:49:42 -07:00
James Lu
8e1e92c564 update.sh: pass arguments to pylink 2016-07-16 21:58:39 -07:00
James Lu
94f2422a1e relay: show secret channels in LINKED to those in the channel
Closes #278.
2016-07-16 21:07:08 -07:00
James Lu
b79f391be6 corecommands: fix no-identify-in-channel logic 2016-07-13 19:32:39 -07:00
James Lu
0c8397e940 core: Better VERSION handling (closes #279) 2016-07-13 19:29:41 -07:00
James Lu
88281a3a54 docs/technical: purge ancient autogen docs 2016-07-13 19:23:09 -07:00
James Lu
9256500a23 corecommands: replace irc.msg() with irc.reply()
Thanks to @Techman- for pointing this out.
2016-07-13 19:20:11 -07:00
James Lu
cf040f5df3 Release 0.9-beta1 2016-07-13 19:05:29 -07:00
James Lu
1a785d83c2 Release notes for 0.9-beta1 2016-07-13 19:05:07 -07:00
James Lu
af0b14bf58 setup.py: remove -dirty version marking 2016-07-13 19:04:51 -07:00
James Lu
b7852b1d01 relay: hint at JUPE being a reason why server spawning might fail 2016-07-13 18:56:55 -07:00
James Lu
72eb04ebc6 docs/t: add SVSNICK to hooks reference 2016-07-13 18:53:51 -07:00
James Lu
5c7524bcf3 relay: use the "official" method of removing bad networks 2016-07-13 18:49:51 -07:00
James Lu
31bf984996 Revert "relay: don't try to force network disconnects"
This reverts commit 77edd9870c4f0d9617cf78b071e2f42fa0ca657c.
2016-07-13 18:43:22 -07:00
James Lu
51457f3550 Irc: treat unicode case sensitively in toLower() 2016-07-13 13:38:56 -07:00
James Lu
35a9d7ccba Irc: move self.aborted.clear() to connect() 2016-07-13 00:38:19 -07:00
James Lu
ad5cd51189 automode: fix wrong indent in modebot_uid check 2016-07-12 22:16:24 -07:00
James Lu
58d71b0907 classes.Protocol: use a lock with updateTS to ensure thread-safety
Closes #274.
2016-07-12 22:08:01 -07:00
James Lu
6598d56400 automode: fix join handler, make match() take multiple users instead of one 2016-07-12 21:58:08 -07:00
James Lu
76554dccd1 automode: refactor to send only one MODE per channel (#275) 2016-07-12 21:58:08 -07:00
James Lu
80d7be8c7d nefarious: add (untested) SVSNICK handler based on P10 docs
Closes #269.
2016-07-12 21:58:08 -07:00
James Lu
db93db7f4e docs/t: update pmodule-spec.md 2016-07-12 00:32:57 -07:00
James Lu
c5176b7386 Add last commit's image to docs contents 2016-07-12 00:29:14 -07:00
James Lu
a726352c44 Merge remote-tracking branch 'origin/master' into devel 2016-07-12 00:28:58 -07:00
James Lu
0823fb2a1e docs/technical: Add protocol modules inheritance graph 2016-07-12 00:28:13 -07:00
James Lu
256801c0b4 automode: send one MODE per user (first part of #275)
If a user matches multiple DB entries, only one is sent now. However, this still needs to be changed so if multiple people are being checked for entries at once, one MODE command is sent for the entire channel.
2016-07-12 00:08:11 -07:00
James Lu
d45aa6ae87 example-conf: mark relay:tag_nicks as experimental 2016-07-11 23:38:00 -07:00
James Lu
92466e4a00 relay: make tag_nicks default to True 2016-07-11 23:35:16 -07:00
James Lu
bced9506c3 ts6: add RSFNC to supported caps 2016-07-11 23:29:44 -07:00
James Lu
59f6c861e0 relay: treat SVSNICK as a cue to tag nicks
Closes #116.
2016-07-11 23:23:26 -07:00
James Lu
21d03e7b69 protocols: implement SVSNICK hooks for InspIRCd, Charybdis, UnrealIRCd (#269)
More testing still needs to be done with this on Nefarious, as atheme (what I'm testing against) doesn't use P10 SVSNICK yet.
2016-07-11 23:21:08 -07:00
James Lu
bc369bf6a6 relay: force tags when a nick collision happens 2016-07-11 21:54:48 -07:00
James Lu
95ff33876c relay: handle KILLs to untagged clients as a cue to tag them (#116) 2016-07-11 21:43:43 -07:00
James Lu
5bfba0a411 relay: make default nick tagging and separator global options (#116) 2016-07-11 21:43:43 -07:00
James Lu
4a756843ed README: fix typo 2016-07-11 20:17:17 -07:00
James Lu
1b0829f401 ctcp: totally not an easter egg update 2016-07-11 16:59:44 -07:00
James Lu
cf40a38c14 corecommands: actually assign reload() output to update the module 2016-07-11 16:53:32 -07:00
James Lu
6904ba9606 commands: remove reference to 'signon time' 2016-07-11 16:49:08 -07:00
James Lu
7c1a80708b unreal: demote mode bounce notices to DEBUG 2016-07-11 16:37:01 -07:00
James Lu
77edd9870c relay: don't try to force network disconnects
This doesn't work, and seems to cause PyLink to freeze instead.
2016-07-11 16:35:35 -07:00
James Lu
a6d9016464 automode: add clearacc, syncacc commands
Closes #262. Closes #263.
2016-07-11 16:29:17 -07:00
James Lu
cb7e7abec5 automode: treat channels case insensitively in 'delacc' 2016-07-11 16:29:02 -07:00
James Lu
64dd7e9387 unreal: fix server name of the uplink not being saved
Closes #268.
2016-07-11 16:20:10 -07:00
James Lu
2b60cbd59c handlers: fix wrong server name in WHOIS output (8e29e16 regression) 2016-07-11 16:14:05 -07:00
James Lu
c04f9d1879 protocols: move handlers for AWAY, VERSION, WHOIS, and QUIT to ircs2s_common 2016-07-11 16:08:46 -07:00
James Lu
d91589c4da protocols: move handle_whois to ts6_common 2016-07-11 16:00:08 -07:00
James Lu
6a1349847f handlers: handle WHOIS requests to unknown nicks instead of warning
"/whois pylink.server badnick" is allowed everywhere except InspIRCd.
2016-07-11 15:58:18 -07:00
James Lu
8e29e16144 handlers: always send WHOIS reply numerics from our server
This fixes "/whois pylink.server TARGET" syntax sending replies from bad sources, when the target is NOT a PyLink client.
2016-07-11 15:49:23 -07:00
James Lu
1cf560c465 handlers: only show the highest prefix mode for channels in WHOIS output
Closes #270.
2016-07-11 15:46:32 -07:00
James Lu
2a08ae98b0 Irc: consistently sort getPrefixModes output 2016-07-11 15:21:17 -07:00
James Lu
2c656341e2 handlers: Optionally disable extended WHOIS replies for users marked as a bot
Closes #271. This adds a new option, "whois_show_extensions_to_bots", to the bot: section of the config.
2016-07-11 15:01:34 -07:00
James Lu
ba20016a83 example-conf: drop references to use_experimental_whois 2016-07-10 22:39:16 -07:00
James Lu
9c2bec7a3d relay: don't create relay clones with the user's original TS
There's no point of doing this. Relay should purposely lose nick collisions anyways because it can simply switch to the next one.
2016-07-10 22:25:01 -07:00
James Lu
84452bec2e inspircd: always make PyLink handle WHOIS (closes #267)
This removes the use_experimental_whois option, which is always enabled now.

The rationale behind this is that PyLink cannot accurately track signon and idle times for things like relay users, without forwarding WHOIS requests between servers in a way the hooks system is really not optimized to do. However, no IDLE response means that no WHOIS data is ever sent back to the calling user, so this workaround is probably the best solution, aside from faking values.
2016-07-10 22:08:56 -07:00
James Lu
8f78205406 handlers: remove idle and signon time in WHOIS output (#267)
We can't accurately track this for things like relay users without forwarding WHOIS replies like Janus does. I don't really like faking values either, so let's just remove this entirely
2016-07-10 22:06:02 -07:00
James Lu
5d251d511a corecommands: fix wrong plugin module name causing 'unload' to not work
Plugin modules are now named 'pylinkirc.plugins.abcd' instead of just 'abcd', since the import system is absolute now.
2016-07-10 21:41:08 -07:00
James Lu
c1cd6f42a0 updateTS: Fix mode tracking again
It looks like we actually do need to track whether we're sending or receiving modes. This time, do so /properly/.
2016-07-10 21:36:18 -07:00
James Lu
fa59e2bded ts6_common: force nick TS to 100 in SAVE 2016-07-10 21:20:47 -07:00
James Lu
a06b478a2e ts6_common: update nick TS on SAVE as well (#267) 2016-07-10 21:14:12 -07:00
James Lu
16b162ffbe protocols: update nick TS on nick change (#267) 2016-07-10 21:10:57 -07:00
James Lu
ade0fa707e nefarious: fix wrong variable in last commit 2016-07-10 21:01:01 -07:00
James Lu
8b04a51daf nefarious: only send EOB_ACK to uplink
Closes #266.
2016-07-10 20:37:21 -07:00
James Lu
1675c43841 ts6: the @cooper fix
Validate that incoming TS is valid, because bad user timestamps like 0 will cause InspIRCd networks to SQUIT if forwarded over Relay.
2016-07-10 20:37:07 -07:00
James Lu
b90d7c3bec 0.9-alpha1 2016-07-09 00:26:22 -07:00
James Lu
ca638f77e3 docs/t: drop update-autogen, it's broken now 2016-07-09 00:25:58 -07:00
James Lu
0d0cccea63 Merge branch 'staging' 2016-07-09 00:20:45 -07:00
James Lu
7cfcef7c0f Relicense docs under CC-BY-SA-4.0 2016-07-09 00:14:24 -07:00
James Lu
b88b9614f6 automode: join channels where automode is enabled (#264)
Still a WIP: when automode is killed, it won't join any relay leaf channels. Perhaps Relay needs to learn how to queue channels for services bots when they join a remote channel, and then remove them when that channel is delinked, etc.
2016-07-08 13:01:56 -07:00
James Lu
c1476dda59 setup.py: drop py_modules entry
It looks like this ended up installing the main PyLink modules twice.
2016-07-08 12:57:35 -07:00
James Lu
7d321e2e6c inspircd: only send services_login hooks if the user exists
With SASL, services_login can be called before the user is registered. We should ignore that because the actual accountname is still bursted later.
2016-07-08 12:08:13 -07:00
James Lu
de7533a46e automode: more explicit check for the modebot UID being available 2016-07-07 22:57:31 -07:00
James Lu
ba53d63d37 ServiceBot: make sure all the defaultdict(set) instances are SEPARATE
Bit of a Python oddity here - If you initialize a class like defaultdict in a class constructor, the same instance is used for all instances of the class?

This fixes all service bots joining the same channel, when they really shouldn't be.
2016-07-07 22:56:05 -07:00
James Lu
7a7b590295 corecommands: quick fix for 'reload' not updating things
The real issue is the 'unload' doesn't seem to cleanly unload things anymore. (Even though that was a pretty bad hack in the first place)

That needs to be cleaned up in the next release.
2016-07-07 22:48:38 -07:00
James Lu
f97f2d2fda automode: call unregisterService() on unload 2016-07-07 22:41:51 -07:00
James Lu
4f26a29196 utils: unregisterService should be case insensitive 2016-07-07 22:41:39 -07:00
James Lu
df41dabb71 relay: drop prefix modes if the target doesn't have a relay clone yet
This fixes modes like ('+v', None) from being accidentally sent by Relay, when Automode happens to set modes before all relay clients are spawned.
2016-07-07 22:16:34 -07:00
James Lu
00766041f3 plugins: make DB save delay configurable 2016-07-07 22:16:21 -07:00
James Lu
db3517b43a automode: only those with admin login can change access entries 2016-07-07 21:57:36 -07:00
James Lu
4999ecd11f Integrate services_support and automode
New hook: PYLINK_SERVICE_JOIN
2016-07-07 21:54:59 -07:00
James Lu
b3310eaf78 relay: also send internal hook for services login, for integration with automode 2016-07-07 21:43:25 -07:00
James Lu
0d502095c5 relay: integrate with automode by sending relay JOINs as a hook 2016-07-07 21:38:12 -07:00
James Lu
2be4811673 automode: separate matching into a separate function & listen for services login changes 2016-07-07 21:31:36 -07:00
James Lu
552070132d automode: implement JOIN handler for a new working ACL system!
Closes #170.
2016-07-07 21:23:06 -07:00
James Lu
d365f04199 Automode plugin stub (#204)
This supports adding/removing/listing entries and saving them to a DB, but no modes are set yet.
2016-07-07 21:06:28 -07:00
James Lu
14b30b26c0 exttargets: $pylinkacc matcher
Closes #170.
2016-07-07 12:10:09 -07:00
James Lu
1cec67725a exttargets: add $channel matcher (#170) 2016-07-07 12:00:23 -07:00
James Lu
1a8976afb6 exttargets: add $server matcher (#170) 2016-07-07 11:26:11 -07:00
James Lu
50d30d4e20 Irc: implement exttarget inversion (#170) 2016-07-07 11:18:06 -07:00
James Lu
ae2eefc73e opercmds: actually make 'checkban' oper-only 2016-07-07 11:18:06 -07:00
James Lu
7dda8ebe58 exttargets: add $ircop matcher (#170) 2016-07-07 11:18:06 -07:00
James Lu
724ab0a45e exttargets: match account names case insensitively 2016-07-07 11:18:06 -07:00
James Lu
aba1f4cf24 opercmds: fix typo in help.
(cherry picked from commit 37e1c7d5382af52cc84fd1781a8d6e42ae3a097a)
2016-07-07 10:11:31 -07:00
James Lu
3e28856944 exttargets: fix typo in comment 2016-07-07 00:41:31 -07:00
James Lu
c034877d04 exttargets: $account scenario 3 matching should require logged in status 2016-07-07 00:29:52 -07:00
James Lu
183a4cbd75 core: add extban support in matchHost, and $account matching (#170)
The following forms are supported in $account, with groups separated by a
literal colon. All account and network name matching is currently case sensitive:

$account -> Returns True (a match) if the target is registered.
$account:accountname -> Returns True if the target's account name matches the one given, and the target is connected to the local network..
$account:accountname:netname -> Returns True if both the target's account name and origin network name match the ones given.
$account:*:netname -> Matches all logged in users on the given network.
2016-07-07 00:26:52 -07:00
James Lu
2b88c8d630 opercmds/changehost: actually commit changes mentioned in last commit 2016-07-06 23:47:31 -07:00
James Lu
d3877b0194 Irc: introduce matchHost() wrapper around ircmatch
This makes the latter a core dependency. Refactor changehost and opercmds plugins to take advantage of this new core function.
2016-07-06 23:11:36 -07:00
James Lu
37e1c7d538 opercmds: fix typo in help. 2016-07-06 22:36:29 -07:00
James Lu
ec0f2714e2 opercmds: format KILL reasons properly in kill() 2016-07-06 22:36:06 -07:00
James Lu
e0f050c195 Irc: add getFriendlyName() abstraction
Closes #260.
2016-07-06 22:30:21 -07:00
James Lu
3f1ad01ac6 Add update.sh: Updates a locally installed copy of PyLink and runs it. 2016-07-05 13:29:01 -07:00
James Lu
9f0121e102 .gitignore: add __init__.py 2016-07-05 13:28:45 -07:00
James Lu
3b80802d1a protocols: rewrite encapsulated commands implicitly
Closes #182.
2016-07-05 13:27:31 -07:00
James Lu
ed6293e54a nefarious: define access_sort() as a static method 2016-07-05 00:42:17 -07:00
James Lu
f8ca65cabc Irc: warn when applyModes target doesn't exist
This can happen when a network disconnects while spawnClient is setting modes on a newly spawned client, for example...
2016-07-05 00:42:00 -07:00
James Lu
5ef135d888 nefarious: use handle_kill (with kill path support) from ircs2s_common
Ref #239.
2016-07-05 00:31:24 -07:00
James Lu
bfa69815b4 protocols: split things common between nefarious and ts6_common into a new ircs2s_common module 2016-07-05 00:24:23 -07:00
James Lu
26f4a9c276 nefarious: fix 'changedmodes' type inconsistency causing crashes 2016-07-05 00:13:33 -07:00
James Lu
d549e2ae47 protocols: format kills properly in ts6_common.handle_kill() (#239)
This separates the kill handling for InspIRCd and other TS6 protocols, as InspIRCd pre-formats kills when they are sent.
2016-07-05 00:08:02 -07:00
James Lu
3457da16bd protocols: Move kill() into ts6_common, and make unreal.py use it
Ref #239. protocols/unreal was hardcoding a killpath, lazy me...
2016-07-04 23:43:11 -07:00
James Lu
d0b7d44f1a ts6: implement kill paths in outgoing KILL (#239) 2016-07-04 23:43:11 -07:00
James Lu
81fca49738 relay: hide disconnected networks from LINKED output
Closes #258.
2016-07-03 12:40:23 -07:00
James Lu
08b2eb7c45 0.9.0-dev1 2016-07-03 12:09:22 -07:00
James Lu
7c5b87f4d2 setup: unconditionally write __init__.py; mark builds with unretrievable version as -dirty
Ref #259.
2016-07-03 00:43:06 -07:00
James Lu
a9a6c22044 setup.py: fix fallback version import path 2016-07-03 00:29:31 -07:00
James Lu
72c48502c6 Generate __init__.py with package version on runtime
Closes #259. This removes world.version and replaces it with pylinkirc.__version__ where the former was used.
2016-07-03 00:26:03 -07:00
James Lu
e63a1bc739 Irc: remove leading 'pylinkirc.protocols.' from protoname values 2016-07-03 00:12:23 -07:00
James Lu
bcc84b8618 core: remove references to plugins/protocols_folder (#259) 2016-07-02 23:58:21 -07:00
James Lu
c58c4d4a9c example-conf: capitalize our nick by default 2016-07-02 22:18:10 -07:00
James Lu
8704114fa0 control: copy world.networkobjects to prevent RuntimeError
i.e.: RuntimeError: dictionary changed size during iteration
2016-07-01 22:20:24 -07:00
James Lu
7c6daa839c relay: don't error if servers for current net were already removed 2016-07-01 22:20:10 -07:00
James Lu
28cf85bc33 README: add juno-ircd (make @cooper happy) and links to IRCd pages 2016-07-01 22:03:49 -07:00
James Lu
408ce701b1 service_support: support optionally setting servprotect on service bots
Closes #243.
2016-07-01 21:25:58 -07:00
James Lu
017d6a4651 coremods: remove references to 'coreplugin' in logging 2016-07-01 21:08:50 -07:00
James Lu
e3170cda83 relay: remove another _ in opertype handling 2016-07-01 21:07:07 -07:00
James Lu
6c8731a55a inspircd: refactor opertype handling and opertypes for Services
This gives all protected service clients with +k a special "Network Service" opertype.
2016-07-01 21:03:10 -07:00
James Lu
55e0da96c3 Merge remote-tracking branch 'origin/master' into devel 2016-07-01 20:42:46 -07:00
James Lu
4bd621f47e control: when rehashing, reconnect networks without autoconnect that failed to connect the first time 2016-07-01 20:33:00 -07:00
James Lu
847a98755f core: use a shared function for disconnecting + removing networks 2016-07-01 20:14:31 -07:00
James Lu
013891bebc networks: drop 'connect' command, it's useless 2016-07-01 19:54:37 -07:00
James Lu
da4da91ef9 networks: unconditionally remove disconnected IRC objects 2016-07-01 19:54:19 -07:00
James Lu
fde7860fc7 ts6: fix wrong args in TB handling
I'm not sure where I got that example from. It's wrong.
2016-07-01 19:45:13 -07:00
James Lu
eb7aae9634 example-conf: mention what's NOT valid for a relay separator 2016-07-01 19:27:00 -07:00
James Lu
1a0983b8e9 relay: prefix nicks starting with - (this is invalid) 2016-07-01 19:19:11 -07:00
James Lu
c9b6695f90 relay: make sure normalized nicks have no invalid characters
This affects the separator option too: if someone puts something invalid in that field, this will coerse bad chars to | instead of letting the IRCd deal with it.
2016-07-01 19:16:47 -07:00
James Lu
042a173d87 relay: use a 5 second timeout when acquiring all threading locks 2016-07-01 18:54:35 -07:00
James Lu
06ee35dcfc relay: catch various errors if a relay client is killed while commands are called from it 2016-07-01 18:54:07 -07:00
James Lu
cc40cacb7a Irc.joinModes(): sort mode list before formatting 2016-06-30 19:52:06 -07:00
James Lu
4cd49296e5 Merge remote-tracking branch 'origin/master' into devel 2016-06-30 19:00:39 -07:00
James Lu
d34ab6db8b ServiceBot: show an "End of help" line after featured command listing 2016-06-30 18:55:37 -07:00
James Lu
d2b5fd7b6e ServiceBot: implement short form help for featured command lists
Suggestion from @cooper.
2016-06-30 18:52:35 -07:00
James Lu
91a663d5c7 commands, relay: use irc.reply() with private=True instead of irc.msg()
This is more flexible, etc.
2016-06-30 18:43:56 -07:00
James Lu
5c90cbe01f ServiceBot: always show featured commands list in private, to prevent channel floods 2016-06-30 18:37:14 -07:00
James Lu
1ac1e3eca6 Irc: fix wrong target for reply() 2016-06-30 18:36:40 -07:00
James Lu
02405c36b5 core, fantasy: redo handling of noticed and/or private replies
New behaviour for command responses in general: FANTASY commands reply in channel as PRIVMSG, while all commands sent in PM reply as private notices.

- The old irc.called_by is now irc.called_in (PLACE last command was called)
- irc.called_by is now used to store the CALLER of the last command
- notice=True/False toggle is dropped from ServiceBot.call_cmd()
- New private=True/False option added to ServiceBot.reply() and irc.reply(), which controls whether replies should be sent privately or not.
2016-06-30 18:22:45 -07:00
James Lu
14f569fd7c relay: improve command help, add featured command definitions 2016-06-30 18:05:27 -07:00
James Lu
0922f7cefc ServiceBot: sort featured commands list 2016-06-30 18:05:12 -07:00
James Lu
e730909a46 utils: pass featured argument to ServiceBot in add_cmd() 2016-06-30 17:57:40 -07:00
James Lu
1637193a53 service_support: add description for main PyLink bot 2016-06-30 17:45:05 -07:00
James Lu
b2b4f33fe8 games: add service description & featured commands 2016-06-30 17:45:05 -07:00
James Lu
7210161ece ServiceBot: use a shared function for showing command help 2016-06-30 17:39:53 -07:00
James Lu
a9f8b05419 ServiceBot: support service descriptions, featured commands
Closes #256. Closes #255.
2016-06-30 17:30:44 -07:00
James Lu
5f2da1c8c3 relay: cap waiting time for irc.connected to 5 seconds 2016-06-29 18:12:50 -07:00
James Lu
e8ecc1c775 ts6: 10 modes per line, not 9
I must've made it shorter just to be safe, but that isn't necessary.
2016-06-27 23:40:58 -07:00
James Lu
982e7c43f2 ts6: cut off BMASK at 12 args per line to prevent message cutoff
Ref #253.
2016-06-27 23:35:56 -07:00
James Lu
12f1cce6a9 ts6: 12 users max are allowed in each SJOIN message, not 10 2016-06-27 23:13:39 -07:00
James Lu
8b39635fa8 relay: don't expect that serverdata['channels'] is always present 2016-06-27 23:00:39 -07:00
James Lu
dda1d6865d example-conf: correct wrong default pingfreq
(cherry picked from commit 0460df0e51c94f015ab14cea3933f17b957691e6)
2016-06-27 22:49:31 -07:00
James Lu
01d462c97f example-conf: Formatting, remove pingfreq and maxnicklen from examples 2016-06-27 22:49:03 -07:00
James Lu
0460df0e51 example-conf: correct wrong default pingfreq 2016-06-27 22:46:53 -07:00
James Lu
e4b400042e core: make maxnicklen optional, defaulting it to 30 2016-06-27 22:39:18 -07:00
James Lu
f458a40e1c inspircd: new use_experimental_whois option, which forces PyLink to handle WHOIS requests locally 2016-06-27 22:28:37 -07:00
James Lu
57afa806e3 inspircd: implement raw numeric sending 2016-06-27 22:28:00 -07:00
James Lu
9374bccb6f Merge remote-tracking branch 'origin/master' into devel 2016-06-27 21:47:55 -07:00
James Lu
b1e138d9c5 nefarious: fix wrong variable in "/join 0" handling causing crashes
(cherry picked from commit 5cb550afd9a01b61a229c6ab3fb42f8b8657b80c)
2016-06-26 11:38:09 -07:00
James Lu
0fbf9e165c Irc: forcibly disable SSLv2 and SSLv3 2016-06-26 10:02:27 -07:00
James Lu
2c0a09271c conf: re-add dummy conf / confname so that dependant modules are still importable 2016-06-25 14:37:06 -07:00
James Lu
5a363c22af conf: remove checks for 'channels' in server: blocks
This key is no longer mandatory as of ceed9346c08aa44decf4c60f256b32413199de75.
2016-06-25 14:25:57 -07:00
James Lu
8af4b0c06d relay: use extra_channels to persistently join the PyLink bot to relay channels
Closes #247.
2016-06-25 14:21:18 -07:00
James Lu
b90c69eead ServiceBot: actually, make extra_channels network specific 2016-06-25 14:21:13 -07:00
James Lu
a0d1f627ec example-conf: update notes on autojoin and log channels 2016-06-25 14:14:19 -07:00
James Lu
ceed9346c0 ServiceBot: allow configuring extra channels that bots will join 2016-06-25 14:14:19 -07:00
James Lu
9d7fb4ed70 updateTS: fix typo in error message 2016-06-25 14:00:26 -07:00
James Lu
170de377ca coremods: Move ServiceBot kill/kick/message handling into the right module
This also fixes the kill handler erroneously calling a spawn_service() that was never imported.
2016-06-25 13:58:59 -07:00
James Lu
fbeb3a3747 protocols: fix ts actually defaulting to None in sjoin() 2016-06-25 13:56:24 -07:00
James Lu
1ce6102ae8 relay: allow toggling netsplit hiding 2016-06-25 13:47:59 -07:00
James Lu
a3b9c55de6 example-conf: missing "on" in description 2016-06-25 13:47:48 -07:00
James Lu
d2956c3d00 protocols: return uplink field in SQUIT handlers 2016-06-25 13:34:43 -07:00
James Lu
1a6bb714ac relay: drop spawn_servers toggle, remove "Relay network lost connection" quits
Relay server spawning is now always on - there's no real reason why it shouldn't work.

Closes #237.
2016-06-25 13:27:24 -07:00
James Lu
9ea6769c54 parseModes: allow type str as modestring 2016-06-25 13:08:49 -07:00
James Lu
5e16eeea41 updateTS: skip applying modes if there aren't any 2016-06-25 12:54:56 -07:00
James Lu
2b0dd0f746 ctcp: allow PING with multiple arguments 2016-06-25 12:18:50 -07:00
James Lu
3461216d20 ctcp: add PING, easter egg 2016-06-25 12:11:04 -07:00
James Lu
f009a739bc Merge remote-tracking branch 'origin/master' into devel 2016-06-25 11:36:55 -07:00
James Lu
51f1506f90 updateTS: remove mentions of 'outbound' variable from logging 2016-06-25 11:34:14 -07:00
James Lu
5cb550afd9 nefarious: fix wrong variable in "/join 0" handling causing crashes 2016-06-25 11:33:56 -07:00
James Lu
f445f7baef relay: be more tolerant to users sending messages to channels they're not in
This extends the routing of messages from servers across the relay via the main PyLink client to users too.
This allows atheme's GameServ, various commit announcers, etc. to work over relay regardless of whether +n is set.
2016-06-25 11:31:04 -07:00
James Lu
e966fe7e56 relay: typo in comment 2016-06-25 10:43:07 -07:00
James Lu
504a9be7d6 relay: attempt to eliminate freezing when networks are down 2016-06-24 18:41:13 -07:00
James Lu
4c00479d78 Irc: lower ping frequency & timeout to 90/180
180*3 seconds is equivalent to 9 minutes. That's a long time for a dead network to stay connected.
2016-06-24 18:14:03 -07:00
James Lu
167963ddc4 updateTS: fix handling for outbound modes
Outgoing and incoming modes should be handled the same way - we're only dealing with a "received TS" which could originate from PyLink (sjoin() in protocols) OR the uplink.
2016-06-24 13:24:44 -07:00
James Lu
7f829ba0ec updateTS: apply TS on channel regardless of whether mode changes pass 2016-06-24 13:15:26 -07:00
James Lu
be19f88e86 pylink, world: default tests mode to False, simplify imports 2016-06-23 22:51:40 -07:00
James Lu
77e13bce03 inspircd: fix logic for tracking existing bans in sjoin() 2016-06-23 22:36:22 -07:00
James Lu
6555ba2e6a nefarious: burst bans according to the P10 standard
Closes #250.
2016-06-23 22:36:22 -07:00
James Lu
f6edf997c5 README: fix typo 2016-06-23 20:32:23 -07:00
James Lu
2936e94f50 unreal: warn about mode bounces instead of fighting with the uplink 2016-06-22 22:37:53 -07:00
James Lu
f2b139c828 core: make inbound SJOINs also respect the updateTS() rules 2016-06-22 22:28:15 -07:00
James Lu
90ee20ee8b relay: revert changes to mode handling that only bursts modes from the owning network on connect 2016-06-22 21:41:04 -07:00
James Lu
6b8e80cd5b core/protocols: add modes option in sjoin(), TS6 BMASK, and mode TS rules in updateTS()
Closes #249.
Closes #250.
2016-06-22 21:34:16 -07:00
James Lu
6fc5fa3130 relay: split relayModes() into getSupportedCmodes() for better reusability
First part of #248.
2016-06-22 19:49:49 -07:00
James Lu
377c09fe66 pylink: oops, re-add world.testing = False 2016-06-22 19:23:06 -07:00
James Lu
8969cfb74e Merge branch 'master' into devel
Conflicts:
	plugins/networks.py
2016-06-22 19:10:59 -07:00
James Lu
6060a8857f ts6: fix incorrect WHOIS syntax
Thanks to @cooper for reporting.
2016-06-22 18:34:17 -07:00
James Lu
1ad8b2e539 networks: reintroduce networks properly in 'connect'
Closes #245.
2016-06-22 10:39:22 -07:00
James Lu
dd08c01791 Irc: default pingTimer to None before starting connections
(cherry picked from commit 42ec6f2502b9dfcf775cbab0755dcda04809d14d)
2016-06-21 20:34:48 -07:00
James Lu
cfa2cda885 relay: only reverse modes for CLAIM if there are any to reverse 2016-06-21 20:29:48 -07:00
James Lu
101bd9664e unreal: fix typo in comment 2016-06-21 17:03:23 -07:00
James Lu
dce768017a [WIP] conf: re-add 'fname' variable (used by REHASH) 2016-06-21 11:31:39 -07:00
James Lu
3a57e8d595 Split coreplugin.py into coremods/
Closes #240.
2016-06-21 11:25:47 -07:00
James Lu
75ae50e03a pylink: implement --version, --no-pid
Closes #242.
2016-06-21 11:03:28 -07:00
James Lu
cb633cfba5 Default config file location is now pylink.yml 2016-06-21 10:59:03 -07:00
James Lu
2f188dc60d core: Remove load-conf-on-import, implement basic command line options via argparse
Ref #242.
2016-06-21 10:55:42 -07:00
James Lu
42ec6f2502 Irc: default pingTimer to None before starting connections 2016-06-21 10:54:07 -07:00
James Lu
d0bd064eda pylink: better error message if PyLink isn't properly installed 2016-06-21 10:27:36 -07:00
James Lu
53ce5e26e0 Update installation instructions 2016-06-20 18:40:13 -07:00
James Lu
04de033454 log: remove references to script directory
This is wrong when log.py is present in ~/.local/lib/... or /usr/lib/...
2016-06-20 18:23:05 -07:00
James Lu
c6968fa2b8 .gitignore: ignore dist/ and egg-related data 2016-06-20 18:19:37 -07:00
James Lu
481d70eff8 New import paths for properly installed pylinkirc 2016-06-20 18:18:54 -07:00
James Lu
5f9c14ca18 WIP: Very basic setup.py
Import paths, etc. in the PyLink app still need to be adjusted!
2016-06-20 17:48:14 -07:00
James Lu
1caf42e8c3 Remove CPUlimit wrapper scripts 2016-06-20 17:11:27 -07:00
James Lu
58e3c6626c Remove awfully maintained test cases 2016-06-20 17:11:14 -07:00
James Lu
0b9691c3c6 Merge branch 'master' into devel 2016-06-20 16:38:33 -07:00
James Lu
3e19e9c3f1 unreal: add missing inviteonly (+i) definition 2016-06-19 21:18:35 -07:00
James Lu
e057df2ae9 channel-modes: fix definition for private (+p)
Charybdis and Elemental-IRCd implement +p as noknock, not the RFC1459-style private.
2016-06-19 21:14:10 -07:00
James Lu
26df48c26d ts6: add missing definition for cmode +i 2016-06-19 21:13:14 -07:00
James Lu
87cbbc9c57 relay: clearer error message when DESTROY'ing a channel you didn't create 2016-06-19 12:32:27 -07:00
James Lu
d3e207d653 relay: axe unused variable 2016-06-19 12:32:12 -07:00
James Lu
bcc754cf0b relay: allow forcing slashes on unsupported IRCds
This adds an undocumented option "relay_force_slashes" to allow slashes in nicks anyways, for IRCds that are, for example, TS6 or P10 variations that don't validate remote nicks.
2016-06-19 12:32:03 -07:00
James Lu
e47738c27f relay: forbid linking two channels on the same network 2016-06-19 12:14:09 -07:00
James Lu
9132bfcb3a pylink-opers: mention DESTROY, DELINK 2016-06-19 12:12:17 -07:00
James Lu
9732d01a9e relay: default DB to {} so it doesn't flip out on 'load relay'
(cherry picked from commit db56513ac7ff76b21f4907d4608f2a1e24a4c961)
2016-06-17 07:19:13 -07:00
James Lu
db56513ac7 relay: default DB to {} so it doesn't flip out on 'load relay' 2016-06-16 21:55:50 -07:00
James Lu
fa3d230ac9 IncrementalUIDGenerator: normalize SID to strings 2016-06-16 21:55:32 -07:00
James Lu
11bbbfba19 Ctcp plugin: handles basic VERSION requests
Closes #236.
2016-06-15 11:31:40 -07:00
James Lu
d362063e8a ServiceBot: ignore invalid commands beginning with \x01 (CTCP) 2016-06-15 11:26:30 -07:00
James Lu
f555a484f9 Irc: unconditionally call disconnect() on errors or ping timeouts
This prevents duplicated ping timer threads from spawning, I think...
2016-06-15 10:55:47 -07:00
James Lu
b1a5f864d8 Irc: resolve hostnames when connecting
Cloeses #158.
2016-06-11 11:29:11 -07:00
James Lu
cb30aca750 docs/t/writing-plugins: mention main() and die()
Closes #113.
2016-06-11 11:03:16 -07:00
James Lu
77fa3573a1 ServiceBot: remove defaults for nick and ident
This fallback is handled by coreplugin already.
2016-06-11 10:54:07 -07:00
James Lu
bbcddceaf9 core: configurable nicks for services (per-net and global)
Closes #220.
Closes #229.
2016-06-11 10:44:14 -07:00
James Lu
675489489a Merge branch 'master' into devel 2016-06-11 10:42:02 -07:00
James Lu
cd4bf55629 example-conf: fix wrong key for ident setting 2016-06-11 10:36:01 -07:00
James Lu
26a5809582 Merge remote-tracking branch 'origin/bump-timeouts' into devel 2016-06-11 10:19:57 -07:00
James Lu
f59b307238 pylink-opers: dos2unix, mention LINKACL and CLAIM
Closes #234.
2016-06-11 10:19:26 -07:00
James Lu
9952f08c8f opercmds: switch to irc.parseModes() 2016-06-08 16:40:00 -07:00
James Lu
e1d3003683 relay: less accusatory message for links blocked by LINKACL 2016-06-08 16:00:25 -07:00
James Lu
3869c06991 Add .codeclimate.yml 2016-06-08 07:56:10 -07:00
James Lu
8f2dadb247 .gitignore: don't ignore dotfile .yml confs 2016-06-08 07:56:10 -07:00
James Lu
eeca570cc4 classes: make pingfreq consistent, bump timeouts 2016-06-06 07:34:48 -07:00
James Lu
88f55fe493 example-conf: mention short name config in servers 2016-06-04 20:31:25 -07:00
James Lu
0edb516ac1 example-conf: fix syntax error
Thanks to kevin and fedxguy for finding this.
2016-06-04 20:17:00 -07:00
James Lu
633f0a45e7 case sensitivity is annoying 2016-06-04 18:51:22 -07:00
James Lu
a2016175a5 README: mention the faq 2016-06-04 18:49:27 -07:00
James Lu
172698dfb2 docs: add FAQ, refresh TOC 2016-06-04 18:47:23 -07:00
James Lu
61cd215e29 clarify that example link blocks are examples 2016-06-04 18:39:18 -07:00
James Lu
229c36381e docs/t: update links to include user-modes.csv
Closes #200.
2016-05-31 20:57:18 -07:00
James Lu
85a97ce86b docs/t: add usermodes list (#200) 2016-05-31 20:57:18 -07:00
James Lu
92de5e81b2 docs/t: remove + from entries in channel-modes 2016-05-31 20:57:18 -07:00
James Lu
5d5d4acd55 hybrid: rename debug umode (+g) to sno_debug 2016-05-31 20:57:17 -07:00
James Lu
d18e9c82f9 ts6: recognize umode +p (override) 2016-05-31 20:57:17 -07:00
134 changed files with 25400 additions and 10760 deletions

25
.dockerignore Normal file
View File

@ -0,0 +1,25 @@
*.yml
*.yaml
# Git, CI, etc. config files
.*
test/
# Automatically generated by setup.py
/__init__.py
env/
build/
__pycache__/
.idea/
*.py[cod]
*.bak
*~
*#
*.save*
*.db
*.pid
*.pem
.eggs
*.egg-info/
dist/
log/

30
.drone-write-tags.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
# Write Docker tags for Drone CI: version-YYMMDD, version, major version, latest
VERSION="$1"
if test -z "$VERSION"; then
echo "Reading version from VERSION file" >&2
VERSION=$(<VERSION)
fi
if [[ "$VERSION" == *"alpha"* || "$VERSION" == *"dev"* ]]; then
# This should never trigger if reference based tagging is enabled
echo "ERROR: Pushing alpha / dev tags is not supported"
exit 1
fi
major_version="$(printf '%s' "$VERSION" | cut -d . -f 1)"
# Date based tag
printf '%s' "$VERSION-$(date +%Y%m%d),"
# Program version
printf '%s' "$VERSION,"
if [[ "$VERSION" == *"beta"* ]]; then
printf '%s' "$major_version-beta,"
printf '%s' "latest-beta"
else # Stable or rc build
printf '%s' "$major_version,"
printf '%s' "latest"
fi

94
.drone.jsonnet Normal file
View File

@ -0,0 +1,94 @@
local test(py_version) = {
"kind": "pipeline",
"type": "docker",
"name": "test-" + py_version,
"steps": [
{
"name": "test",
"image": "python:" + py_version,
"commands": [
"git submodule update --recursive --remote --init",
"pip install -r requirements-docker.txt",
"python3 setup.py install",
"python3 -m unittest discover test/ --verbose"
]
}
]
};
local build_docker(py_version) = {
"kind": "pipeline",
"type": "docker",
"name": "build_docker",
"steps": [
{
"name": "set Docker image tags",
"image": "bash",
"commands": [
"bash .drone-write-tags.sh $DRONE_TAG > .tags",
"# Will build the following tags:",
"cat .tags"
]
},
{
"name": "build Docker image",
"image": "plugins/docker",
"settings": {
"repo": "jlu5/pylink",
"username": {
"from_secret": "docker_user"
},
"password": {
"from_secret": "docker_token"
}
}
}
],
"trigger": {
"event": [
"push"
],
"branch": ["release"],
},
"depends_on": ["test-" + py_version]
};
local deploy_pypi(py_version) = {
"kind": "pipeline",
"type": "docker",
"name": "deploy_pypi",
"steps": [
{
"name": "pypi_publish",
"image": "plugins/pypi",
"settings": {
"username": "__token__",
"password": {
"from_secret": "pypi_token"
}
}
}
],
"trigger": {
"event": [
"tag"
],
"ref": {
"exclude": [
"refs/tags/*alpha*",
"refs/tags/*beta*",
"refs/tags/*dev*"
]
}
},
"depends_on": ["test-" + py_version]
};
[
test("3.7"),
test("3.8"),
test("3.9"),
test("3.10"),
deploy_pypi("3.10"),
build_docker("3.10"),
]

4
.gitattributes vendored Normal file
View File

@ -0,0 +1,4 @@
* eol=lf
*.png binary
*.jpg binary

13
.gitignore vendored
View File

@ -1,14 +1,27 @@
# Ignore config files except the example ones # Ignore config files except the example ones
*.yml *.yml
!example-*.yml !example-*.yml
!.*.yml
# Generated from .drone.jsonnet
.drone.yml
# Automatically generated by setup.py
/__init__.py
env/ env/
build/ build/
__pycache__/ __pycache__/
.idea/
*.py[cod] *.py[cod]
*.bak *.bak
*~ *~
*#
*.save* *.save*
*.db *.db
*.pid *.pid
*.pem *.pem
.eggs
*.egg-info/
dist/
log/

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "test/parser_tests"]
path = test/parser-tests
url = https://github.com/ircdocs/parser-tests

2
.isort.cfg Normal file
View File

@ -0,0 +1,2 @@
[settings]
line_length=100

View File

@ -1,2 +1,7 @@
James Lu <GLolol@overdrivenetworks.com> <GLolol1@hotmail.com> James Lu <james@overdrivenetworks.com> <GLolol@overdrivenetworks.com>
James Lu <GLolol@overdrivenetworks.com> <GLolol@overdrive.pw> James Lu <james@overdrivenetworks.com> <bitflip3+github@gmail.com>
James Lu <james@overdrivenetworks.com> <GLolol1@hotmail.com>
James Lu <james@overdrivenetworks.com> <GLolol@overdrive.pw>
Ken Spencer <ken@electrocode.net> <kspencer@electrocode.net>
Ken Spencer <ken@electrocode.net> <iota@electrocode.net>
Ken Spencer <ken@electrocode.net> <iota@e-code.in>

4
.pylintrc Normal file
View File

@ -0,0 +1,4 @@
[FORMAT]
max-line-length=120
good-names=ip,f,i

View File

@ -1,4 +1,5 @@
The following people have contributed substantially to PyLink: The following people have contributed substantially to PyLink:
James Lu <glolol@overdrivenetworks.com> James Lu <james@overdrivenetworks.com>
Daniel Oaks <daniel@danieloaks.net> Daniel Oaks <daniel@danieloaks.net>
Ken Spencer <iota@electrocode.net>

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
FROM python:3-alpine
RUN adduser -D -H -u 10000 pylink
VOLUME /pylink
COPY . /pylink-src
RUN cd /pylink-src && pip3 install --no-cache-dir -r requirements-docker.txt
RUN cd /pylink-src && python3 setup.py install
RUN rm -r /pylink-src
USER pylink
WORKDIR /pylink
# Run in no-PID file mode by default
CMD ["pylink", "-n"]

167
LICENSE.CC-BY-SA-4.0 Normal file
View File

@ -0,0 +1,167 @@
Attribution-ShareAlike 4.0 International
=======================================================================
Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.
Using Creative Commons Public Licenses
Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.
Considerations for licensors: Our public licenses are
intended for use by those authorized to give the public
permission to use material in ways otherwise restricted by
copyright and certain other rights. Our licenses are
irrevocable. Licensors should read and understand the terms
and conditions of the license they choose before applying it.
Licensors should also secure all rights necessary before
applying our licenses so that the public can reuse the
material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-
licensed material, or material used under an exception or
limitation to copyright. More considerations for licensors:
wiki.creativecommons.org/Considerations_for_licensors
Considerations for the public: By using one of our public
licenses, a licensor grants the public permission to use the
licensed material under specified terms and conditions. If
the licensor's permission is not necessary for any reason--for
example, because of any applicable exception or limitation to
copyright--then that use is not regulated by the license. Our
licenses grant only permissions under copyright and certain
other rights that a licensor has authority to grant. Use of
the licensed material may still be restricted for other
reasons, including because others have copyright or other
rights in the material. A licensor may make special requests,
such as asking that all changes be marked or described.
Although not required by our licenses, you are encouraged to
respect those requests where reasonable. More_considerations
for the public:
wiki.creativecommons.org/Considerations_for_licensees
=======================================================================
Creative Commons Attribution-ShareAlike 4.0 International Public
License
By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-ShareAlike 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.
Section 1 -- Definitions.
a. Adapted Material means material subject to Copyright and Similar
Rights that is derived from or based upon the Licensed Material
and in which the Licensed Material is translated, altered,
arranged, transformed, or otherwise modified in a manner requiring
permission under the Copyright and Similar Rights held by the
Licensor. For purposes of this Public License, where the Licensed
Material is a musical work, performance, or sound recording,
Adapted Material is always produced where the Licensed Material is
synched in timed relation with a moving image.
b. Adapter's License means the license You apply to Your Copyright
and Similar Rights in Your contributions to Adapted Material in
accordance with the terms and conditions of this Public License.
c. BY-SA Compatible License means a license listed at
creativecommons.org/compatiblelicenses, approved by Creative
Commons as essentially the equivalent of this Public License.
d. Copyright and Similar Rights means copyright and/or similar rights
closely related to copyright including, without limitation,
performance, broadcast, sound recording, and Sui Generis Database
Rights, without regard to how the rights are labeled or
categorized. For purposes of this Public License, the rights
specified in Section 2(b)(1)-(2) are not Copyright and Similar
Rights.
e. Effective Technological Measures means those measures that, in the
absence of proper authority, may not be circumvented under laws
fulfilling obligations under Article 11 of the WIPO Copyright
Treaty adopted on December 20, 1996, and/or similar international
agreements.
f. Exceptions and Limitations means fair use, fair dealing, and/or
any other exception or limitation to Copyright and Similar Rights
that applies to Your use of the Licensed Material.
g. License Elements means the license attributes listed in the name
of a Creative Commons Public License. The License Elements of this
Public License are Attribution and ShareAlike.
h. Licensed Material means the artistic or literary work, database,
or other material to which the Licensor applied this Public
License.
i. Licensed Rights means the rights granted to You subject to the
terms and conditions of this Public License, which are limited to
all Copyright and Similar Rights that apply to Your use of the
Licensed Material and that the Licensor has authority to license.
j. Licensor means the individual(s) or entity(ies) granting rights
under this Public License.
k. Share means to provide material to the public by any means or
process that requires permission under the Licensed Rights, such
as reproduction, public display, public performance, distribution,
dissemination, communication, or importation, and to make material
available to the public including in ways that members of the
public may access the material from a place and at a time
individually chosen by them.
l. Sui Generis Database Rights means rights other than copyright
resulting from Directive 96/9/EC of the European Parliament and of
the Council of 11 March 1996 on the legal protection of databases,
as amended and/or succeeded, as well as other essentially
equivalent rights anywhere in the world.
m. You means the individual or entity exercising the Licensed Rights
under this Public License. Your has a corresponding meaning.
Section 2 -- Scope.
a. License grant.
1. Subject to the terms and conditions of this Public License,
the Licensor hereby grants You a worldwide, royalty-free,
non-sublicensable, non-exclusive, irrevocable license to
exercise the Licensed Rights in the Licensed Material to:
a. reproduce and Share the Licensed Material, in whole or
in part; and
b. produce, reproduce, and Share Adapted Material.
2. Exceptions and Limitations. For the avoidance of doubt, where
Exceptions and Limitations apply to Your use, this Public
License does not apply, and You do not need to comply with
its terms and conditions.
3. Term. The term of this Public License is specified in Section
6(a).
4. Media and formats; technical modifications allowed. The
Licensor authorizes You to exercise the Licensed Rights in
all media and formats whether now known or h

146
README.md
View File

@ -1,51 +1,141 @@
# PyLink # PyLink IRC Services
## END OF LIFE NOTICE: This project is no longer maintained. So long and thanks for all the fish.
<!--
[![Latest stable release](https://img.shields.io/github/v/tag/jlu5/pylink?label=stable&color=1a1)](https://github.com/PyLink/PyLink/tree/master)
[![PyPI version](https://img.shields.io/pypi/v/pylinkirc.svg?maxAge=2592000)](https://pypi.python.org/pypi/pylinkirc/)
[![Docker image version](https://img.shields.io/docker/v/jlu5/pylink/latest?label=docker)](https://hub.docker.com/r/jlu5/pylink)
[![Supported Python versions](https://img.shields.io/badge/python-3.7%20and%20later-50e)](https://www.python.org/downloads/)
-->
PyLink is an extensible, plugin-based IRC services framework written in Python. It aims to be: PyLink is an extensible, plugin-based IRC services framework written in Python. It aims to be:
1) a replacement for the now-defunct Janus. 1) a transparent server-side relayer between IRC networks.
2) a versatile framework and gateway to IRC. 2) a versatile framework for developing IRC services.
## Support PyLink is licensed under the Mozilla Public License, version 2.0 ([LICENSE.MPL2](LICENSE.MPL2)). The [corresponding documentation](docs/) is licensed under the Creative Attribution-ShareAlike 4.0 International License. ([LICENSE.CC-BY-SA-4.0](LICENSE.CC-BY-SA-4.0))
Please report any bugs you find to the [issue tracker](https://github.com/GLolol/PyLink/issues). Pull requests are open if you'd like to contribute, though new stuff generally goes to the **devel** branch. ## Getting help
You can also find support via our IRC channels: `#PyLink @ irc.overdrivenetworks.com `([webchat](https://webchat.overdrivenetworks.com/?channels=PyLink,dev)) or `#PyLink @ chat.freenode.net`. Ask your questions and be patient for a response. **First, MAKE SURE you've read the [FAQ](docs/faq.md)!**
## Dependencies **When upgrading between major versions, remember to read the [release notes](RELNOTES.md) for any breaking changes!**
* Python 3.4+ Please report any bugs you find to the [issue tracker](https://github.com/PyLink/PyLink/issues). Pull requests are likewise welcome.
* PyYAML (`pip install pyyaml`)
* *For the servprotect plugin*: [expiringdict](https://github.com/mailgun/expiringdict) (note: unfortunately, installation is broken in pip due to [mailgun/expiringdict#13](https://github.com/mailgun/expiringdict/issues/13)) ## Installation
* *For the changehost and opercmds plugins*: [ircmatch](https://github.com/mammon-ircd/ircmatch) (`pip install ircmatch`)
### Pre-requisites
* Python 3.7 or above - prefer the newest Python 3.x when available
* A Unix-like operating system: PyLink is actively developed on Linux only, so we cannot guarantee that things will work properly on other systems.
If you are a developer and want to help make PyLink more portable, patches are welcome.
### Installing from source
1) First, make sure the following dependencies are met:
* Setuptools (`pip3 install setuptools`)
* PyYAML (`pip3 install pyyaml`)
* cachetools (`pip3 install cachetools`)
* *For hashed password support*: Passlib >= 1.7.0 (`pip3 install passlib`)
* *For Unicode support in Relay*: unidecode (`pip3 install Unidecode`)
* *For extended PID file tracking (i.e. removing stale PID files after a crash)*: psutil (`pip3 install psutil`)
2) Clone the repository: `git clone https://github.com/PyLink/PyLink && cd PyLink`
- Previously there was a *devel* branch for testing versions of PyLink - this practice has since been discontinued.
3) Install PyLink using `python3 setup.py install` (global install) or `python3 setup.py install --user` (local install)
* Note: `--user` is a *literal* string; *do not* replace it with your username.
* **Whenever you switch branches or update PyLink's sources via `git pull`, you will need to re-run this command for changes to apply!**
### Installing via Docker
As of PyLink 3.0 there is a Docker image available on Docker Hub: [jlu5/pylink](https://hub.docker.com/r/jlu5/pylink)
It supports the following tags:
- Rolling tags: **`latest`** (latest stable/RC release), **`latest-beta`** (latest beta snapshot)
- Pinned to a major branch: e.g. **`3`** (latest 3.x stable release), **`3-beta`** (latest 3.x beta snapshot)
- Pinned to a specific version: e.g. **`3.0.0`**
To use this image you should mount your configuration/DB folder into `/pylink`. **Make sure this directory is writable by UID 10000.**
```bash
$ docker run -v $HOME/pylink:/pylink jlu5/pylink
```
### Installing via PyPI (stable branch only)
1) Make sure you're running the right pip command: on most distros, pip for Python 3 uses the command `pip3`.
2) Run `pip3 install pylinkirc` to download and install PyLink. pip will automatically resolve dependencies.
3) Download or copy https://github.com/PyLink/PyLink/blob/master/example-conf.yml for an example configuration.
## Configuration
1) Rename `example-conf.yml` to `pylink.yml` (or a similarly named `.yml` file) and configure your instance there.
2) Run `pylink` from the command line. PyLink will load its configuration from `pylink.yml` by default, but you can override this by running `pylink` with a config argument (e.g. `pylink mynet.yml`).
## Supported IRCds ## Supported IRCds
### Primary support ### Primary support
These IRCds are frequently tested and well supported. If any issues occur, please file a bug on the issue tracker. These IRCds (in alphabetical order) are frequently tested and well supported. If any issues occur, please file a bug on the issue tracker.
* charybdis (3.5.x / git master) - module `ts6` * [InspIRCd](http://www.inspircd.org/) (2.0 - 3.x) - module `inspircd`
* InspIRCd 2.0.x - module `inspircd` - Set the `target_version` option to `insp3` to target InspIRCd 3.x (default), or `insp20` to target InspIRCd 2.0 (legacy).
* UnrealIRCd 4.x - module `unreal` - For vHost setting to work, `m_chghost.so` must be loaded. For ident and realname changing support, `m_chgident.so` and `m_chgname.so` must be loaded respectively.
- Note: Support for mixed UnrealIRCd 3.2/4.0 networks is experimental, and requires you to enable a `mixed_link` option in the configuration. This may in turn void your support. - Supported channel, user, and prefix modes are negotiated on connect, but hotloading modules that change these is not supported. After changing module configuration, it is recommended to SQUIT PyLink to force a protocol renegotiation.
* [Nefarious IRCu](https://github.com/evilnet/nefarious2) (2.0.0+) - module `p10`
- Note: Both account cloaks (user and oper) and hashed IP cloaks are optionally supported (`HOST_HIDING_STYLE` settings 0 to 3). Make sure you configure PyLink to match your IRCd settings.
* [UnrealIRCd](https://www.unrealircd.org/) (4.2.x - 5.0.x) - module `unreal`
- Supported channel, user, and prefix modes are negotiated on connect, but hotloading modules that change these is not supported. After changing module configuration, it is recommended to SQUIT PyLink to force a protocol renegotiation.
### Extended support ### Extended support
Support for these IRCds exist, but are not tested as frequently and thoroughly. Bugs should be filed if there are any issues, though they may not be always be fixed in a timely fashion. Support for these IRCds exist, but are not tested as frequently and thoroughly. Bugs should be filed if there are any issues, though they may not always be fixed in a timely fashion.
* Elemental-IRCd (6.6.x / git master) - module `ts6` * [charybdis](https://github.com/charybdis-ircd/charybdis) (3.5+) - module `ts6`
* InspIRCd 2.2 (git master) - module `inspircd` - For KLINE support to work, a `shared{}` block should be added for PyLink on all servers.
* IRCd-Hybrid (8.2.x / svn trunk) - module `hybrid` * [ChatIRCd](http://www.chatlounge.net/software) (1.2.x / git master) - module `ts6`
- Note: for host changing support and optimal functionality, a `service{}` block / U-line should be added for PyLink on every IRCd across your network. - For KLINE support to work, a `shared{}` block should be added for PyLink on all servers.
* Nefarious IRCu (2.0.0+) - module `nefarious` * [juno-ircd](https://github.com/cooper/juno) (13.x / ava) - module `ts6` (see [configuration example](https://github.com/cooper/juno/blob/master/doc/ts6.md#pylink))
- Note: Both account cloaks (user and oper) and hashed IP cloaks are optionally supported (HOST_HIDING_STYLE settings 0 to 3). Make sure you configure PyLink to match your IRCd settings. * [ngIRCd](https://ngircd.barton.de/) (24+) - module `ngircd`
- For optimal functionality (mode overrides in relay, etc.), a `UWorld{}` block / U-line should be added for every server that PyLink spawns. To make this easier, you may want to turn relay's spawn_servers off, so that all relay users originate from one virtual server. - For GLINEs to propagate, the `AllowRemoteOper` option must be enabled in ngIRCd.
- `+` (modeless) channels are not supported, and should be disabled for PyLink to function correctly.
- For use with Relay, the `CloakHostModeX` setting will work fine but `CloakHost` and `CloakUserToNick` are *not* supported.
## Setup ### Legacy extended support
1) Rename `example-conf.yml` to `config.yml` and configure your instance there. Note that the configuration format isn't finalized yet - this means that your configuration may break in an update! Support for these IRCds was added at some point but is no longer actively maintained, either due to inactive upstream development or a perceived lack of interest. We recommend migrating to an IRCd in the above two sections.
2) Run `./pylink` from the command line. * [beware-ircd](http://ircd.bircd.org/) (1.6.3) - module `p10`
- Because bircd disallows BURST after ENDBURST for regular servers, U-lines are required for all PyLink servers. Fortunately, wildcards are supported in U-lines, so you can add something along the lines of `U:<your pylink server>:` and `U:*.relay:` (adjust accordingly for your relay server suffix).
- Use `ircd: snircd` as the target IRCd.
- Halfops, `sethost` (`+h`), and account-based cloaking (`VHostStyle=1`) are supported. Crypted IPs and static hosts (`VHostStyle` 2 and 3) are NOT.
* [Elemental-IRCd](https://github.com/Elemental-IRCd/elemental-ircd) (6.6.x / git master) - module `ts6`
- For KLINE support to work, a `shared{}` block should be added for PyLink on all servers.
* [IRCd-Hybrid](http://www.ircd-hybrid.org/) (8.2.x / svn trunk) - module `hybrid`
- For host changing support and optimal functionality, a `service{}` block / U-line should be added for PyLink on every IRCd across your network.
- For KLINE support to work, a `shared{}` block should also be added for PyLink on all servers.
* [ircd-ratbox](http://www.ratbox.org/) (3.x) - module `ts6`
- Host changing is not supported.
- On ircd-ratbox, all known IPs of users will be shown in `/whois`, even if the client is e.g. a cloaked relay client. If you're paranoid about this, turn off Relay IP forwarding on the ratbox network(s).
- For KLINE support to work, a `shared{}` block should be added for PyLink on all servers.
* [IRCu](http://coder-com.undernet.org/) (u2.10.12.16+) - module `p10`
- Host changing (changehost, relay) is not supported.
* [snircd](https://development.quakenet.org/) (1.3.x+) - module `p10`
- Outbound host changing (i.e. for the `changehost` plugin) is not supported.
3) Profit??? ### Clientbot
PyLink supports connecting to IRCds as a relay bot and forwarding users back as virtual clients, similar to Janus' Clientbot. This can be useful if the IRCd a network used isn't supported, or if you want to relay certain channels without fully linking with a network.
For Relay to work properly with Clientbot, be sure to load the `relay_clientbot` plugin in conjunction with `relay`.
Note: **Clientbot links can only be used as a leaf for Relay links - they CANNOT be used to host channels!** This means that Relay does not support having all your networks be Clientbot - in those cases you are better off using a classic relay bot, like [RelayNext for Limnoria](https://github.com/jlu5/SupyPlugins/tree/master/RelayNext).

1803
RELNOTES.md Normal file

File diff suppressed because it is too large Load Diff

1
VERSION Normal file
View File

@ -0,0 +1 @@
3.1.0

2687
classes.py

File diff suppressed because it is too large Load Diff

195
conf.py
View File

@ -1,102 +1,153 @@
""" """
conf.py - PyLink configuration core. conf.py - PyLink configuration core.
This module is used to access the complete configuration for the current This module is used to access the configuration of the current PyLink instance.
PyLink instance. It will load the config on first import, taking the It provides simple checks for validating and loading YAML-format configurations from arbitrary files.
configuration file name from the first command-line argument, but defaulting
to 'config.yml' if this isn't given.
If world.testing is set to True, it will return a preset testing configuration
instead.
This module also provides simple checks for validating and loading YAML-format
configurations from arbitrary files.
""" """
import yaml try:
import yaml
except ImportError:
raise ImportError("PyLink requires PyYAML to function; please install it and try again.")
import logging
import os.path
import sys import sys
from collections import defaultdict from collections import defaultdict
import world from . import world
global testconf __all__ = ['ConfigurationError', 'conf', 'confname', 'validate', 'load_conf',
testconf = {'bot': 'get_database_name']
class ConfigurationError(RuntimeError):
"""Error when config conditions aren't met."""
conf = {'bot':
{ {
'nick': 'PyLink', 'nick': 'PyLink',
'user': 'pylink', 'user': 'pylink',
'realname': 'PyLink Service Client', 'realname': 'PyLink Service Client',
'serverdesc': 'PyLink unit tests' 'serverdesc': 'Unconfigured PyLink'
}, },
'logging': 'logging':
{ {
# Suppress logging in the test output for the most part. 'console': 'INFO'
'stdout': 'CRITICAL'
}, },
'servers': 'servers':
# Wildcard defaultdict! This means that # Wildcard defaultdict! This means that
# any network name you try will work and return # any network name you try will work and return
# this basic template: # this basic template:
defaultdict(lambda: { defaultdict(lambda: {'ip': '0.0.0.0',
'ip': '0.0.0.0', 'port': 7000,
'port': 7000, 'recvpass': "unconfigured",
'recvpass': "abcd", 'sendpass': "unconfigured",
'sendpass': "chucknorris", 'protocol': "null",
'protocol': "null", 'hostname': "pylink.unconfigured",
'hostname': "pylink.unittest", 'sid': "000",
'sid': "9PY", 'maxnicklen': 20,
'channels': ["#pylink"], 'sidrange': '0##'
'maxnicklen': 20, })
'sidrange': '8##' }
}) conf['pylink'] = conf['bot']
} confname = 'unconfigured'
def validateConf(conf): def validate(condition, errmsg):
"""Raises ConfigurationError with errmsg unless the given condition is met."""
if not condition:
raise ConfigurationError(errmsg)
def _log(level, text, *args, logger=None, **kwargs):
if logger:
logger.log(level, text, *args, **kwargs)
else:
world._log_queue.append((level, text))
def _validate_conf(conf, logger=None):
"""Validates a parsed configuration dict.""" """Validates a parsed configuration dict."""
assert type(conf) == dict, "Invalid configuration given: should be type dict, not %s." % type(conf).__name__ validate(isinstance(conf, dict),
"Invalid configuration given: should be type dict, not %s."
% type(conf).__name__)
for section in ('bot', 'servers', 'login', 'logging'): if 'pylink' in conf and 'bot' in conf:
assert conf.get(section), "Missing %r section in config." % section _log(logging.WARNING, "Since PyLink 1.2, the 'pylink:' and 'bot:' configuration sections have been condensed "
"into one. You should merge any options under these sections into one 'pylink:' block.", logger=logger)
for netname, serverblock in conf['servers'].items(): new_block = conf['bot'].copy()
for section in ('ip', 'port', 'recvpass', 'sendpass', 'hostname', new_block.update(conf['pylink'])
'sid', 'sidrange', 'protocol', 'maxnicklen'): conf['bot'] = conf['pylink'] = new_block
assert serverblock.get(section), "Missing %r in server block for %r." % (section, netname) elif 'pylink' in conf:
conf['bot'] = conf['pylink']
elif 'bot' in conf:
conf['pylink'] = conf['bot']
# TODO: add a migration warning in the next release.
assert type(serverblock.get('channels')) == list, "'channels' option in " \ for section in ('pylink', 'servers', 'login', 'logging'):
"server block for %s must be a list, not %s." % (netname, type(serverblock['channels']).__name__) validate(conf.get(section), "Missing %r section in config." % section)
assert type(conf['login'].get('password')) == type(conf['login'].get('user')) == str and \ # Make sure at least one form of authentication is valid.
conf['login']['password'] != "changeme", "You have not set the login details correctly!" # Also we'll warn them that login:user/login:password is deprecated
if conf['login'].get('password') or conf['login'].get('user'):
_log(logging.WARNING, "The 'login:user' and 'login:password' options are deprecated since PyLink 1.1. "
"Please switch to the new 'login:accounts' format as outlined in the example config.", logger=logger)
old_login_valid = isinstance(conf['login'].get('password'), str) and isinstance(conf['login'].get('user'), str)
newlogins = conf['login'].get('accounts', {})
validate(old_login_valid or newlogins, "No accounts were set, aborting!")
for account, block in newlogins.items():
validate(isinstance(account, str), "Bad username format %s" % account)
validate(isinstance(block.get('password'), str), "Bad password %s for account %s" % (block.get('password'), account))
validate(conf['login'].get('password') != "changeme", "You have not set the login details correctly!")
if newlogins and not old_login_valid:
validate(conf.get('permissions'), "New-style accounts enabled but no permissions block was found. You will not be able to administrate your PyLink instance!")
if conf['logging'].get('stdout'):
_log(logging.WARNING, 'The log:stdout option is deprecated since PyLink 1.2 in favour of '
'(a more correctly named) log:console. Please update your '
'configuration accordingly!', logger=logger)
return conf return conf
def loadConf(fname, errors_fatal=True): def load_conf(filename, errors_fatal=True, logger=None):
"""Loads a PyLink configuration file from the filename given.""" """Loads a PyLink configuration file from the filename given."""
with open(fname, 'r') as f: global confname, conf, fname
try: # Note: store globally the last loaded conf filename, for REHASH in coremods/control.
conf = yaml.load(f) fname = filename
except Exception as e: # For the internal config name, strip off any .yml extensions and absolute paths
print('ERROR: Failed to load config from %r: %s: %s' % (fname, type(e).__name__, e)) confname = os.path.splitext(os.path.basename(filename))[0]
if errors_fatal:
sys.exit(4)
raise
else:
return conf
if world.testing:
conf = testconf
confname = 'testconf'
fname = None
else:
try: try:
# Get the config name from the command line, falling back to config.yml with open(filename, 'r') as f:
# if not given. conf = yaml.safe_load(f)
fname = sys.argv[1] conf = _validate_conf(conf, logger=logger)
confname = fname.split('.', 1)[0] except Exception as e:
except IndexError: e = 'Failed to load config from %r: %s: %s' % (filename, type(e).__name__, e)
# confname is used for logging and PID writing, so that each
# instance uses its own files. fname is the actual name of the file if logger: # Prefer using the Python logger when available
# we load. logger.exception(e)
confname = 'pylink' else: # Otherwise, fall back to a print() call.
fname = 'config.yml' print('ERROR: %s' % e, file=sys.stderr)
conf = validateConf(loadConf(fname))
if errors_fatal:
sys.exit(1)
raise
else:
return conf
def get_database_name(dbname):
"""
Returns a database filename with the given base DB name appropriate for the
current PyLink instance.
This returns '<dbname>.db' if the running config name is PyLink's default
(pylink.yml), and '<dbname>-<config name>.db' for anything else. For example,
if this is called from an instance running as 'pylink testing.yml', it
would return '<dbname>-testing.db'."""
if confname != 'pylink':
dbname += '-%s' % confname
dbname += '.db'
return dbname

3
coremods/__init__.py Normal file
View File

@ -0,0 +1,3 @@
# Note: Service support has to be imported first, so that utils.add_cmd() works for corecommands,
# etc.
from . import service_support, permissions, control, handlers, corecommands, exttargets

162
coremods/control.py Normal file
View File

@ -0,0 +1,162 @@
"""
control.py - Implements SHUTDOWN and REHASH functionality.
"""
import atexit
import os
import signal
import threading
from pylinkirc import conf, utils, world # Do not import classes, it'll import loop
from pylinkirc.log import _get_console_log_level, _make_file_logger, _stop_file_loggers, log
from . import login
__all__ = ['remove_network', 'shutdown', 'rehash']
def remove_network(ircobj):
"""Removes a network object from the pool."""
# Disable autoconnect first by setting the delay negative.
ircobj.serverdata['autoconnect'] = -1
ircobj.disconnect()
del world.networkobjects[ircobj.name]
def _print_remaining_threads():
log.debug('shutdown(): Remaining threads: %s', ['%s/%s' % (t.name, t.ident) for t in threading.enumerate()])
def _remove_pid():
pidfile = "%s.pid" % conf.confname
if world._should_remove_pid:
# Remove our pid file.
log.info("Removing PID file %r.", pidfile)
try:
os.remove(pidfile)
except OSError:
log.exception("Failed to remove PID file %r, ignoring..." % pidfile)
else:
log.debug('Not removing PID file %s as world._should_remove_pid is False.' % pidfile)
def _kill_plugins(irc=None):
if not world.plugins:
# No plugins were loaded or we were in a pre-initialized state, ignore.
return
log.info("Shutting down plugins.")
for name, plugin in world.plugins.items():
# Before closing connections, tell all plugins to shutdown cleanly first.
if hasattr(plugin, 'die'):
log.debug('coremods.control: Running die() on plugin %s due to shutdown.', name)
try:
plugin.die(irc=irc)
except: # But don't allow it to crash the server.
log.exception('coremods.control: Error occurred in die() of plugin %s, skipping...', name)
# We use atexit to register certain functions so that when PyLink cleans up after itself if it
# shuts down because all networks have been disconnected.
atexit.register(_remove_pid)
atexit.register(_kill_plugins)
def shutdown(irc=None):
"""Shuts down the Pylink daemon."""
if world.shutting_down.is_set(): # We froze on shutdown last time, so immediately abort.
_print_remaining_threads()
raise KeyboardInterrupt("Forcing shutdown.")
world.shutting_down.set()
# HACK: run the _kill_plugins trigger with the current IRC object. XXX: We should really consider removing this
# argument, since no plugins actually use it to do anything.
atexit.unregister(_kill_plugins)
_kill_plugins(irc=irc)
# Remove our main PyLink bot as well.
utils.unregister_service('pylink')
for ircobj in world.networkobjects.copy().values():
# Disconnect all our networks.
try:
remove_network(ircobj)
except NotImplementedError:
continue
log.info("Waiting for remaining threads to stop; this may take a few seconds. If PyLink freezes "
"at this stage, press Ctrl-C to force a shutdown.")
_print_remaining_threads()
# Done.
def _sigterm_handler(signo, stack_frame):
"""Handles SIGTERM and SIGINT gracefully by shutting down the PyLink daemon."""
log.info("Shutting down on signal %s." % signo)
shutdown()
signal.signal(signal.SIGTERM, _sigterm_handler)
signal.signal(signal.SIGINT, _sigterm_handler)
def rehash():
"""Rehashes the PyLink daemon."""
log.info('Reloading PyLink configuration...')
old_conf = conf.conf.copy()
fname = conf.fname
new_conf = conf.load_conf(fname, errors_fatal=False, logger=log)
conf.conf = new_conf
# Reset any file logger options.
_stop_file_loggers()
files = new_conf['logging'].get('files')
if files:
for filename, config in files.items():
_make_file_logger(filename, config.get('loglevel'))
log.debug('rehash: updating console log level')
world.console_handler.setLevel(_get_console_log_level())
login._make_cryptcontext() # refresh password hashing settings
for network, ircobj in world.networkobjects.copy().items():
# Server was removed from the config file, disconnect them.
log.debug('rehash: checking if %r is still in new conf.', network)
if ircobj.has_cap('virtual-server') or hasattr(ircobj, 'virtual_parent'):
log.debug('rehash: not removing network %r since it is a virtual server.', network)
continue
if network not in new_conf['servers']:
log.debug('rehash: removing connection to %r (removed from config).', network)
remove_network(ircobj)
else:
# XXX: we should really just add abstraction to Irc to update config settings...
ircobj.serverdata = new_conf['servers'][network]
ircobj.autoconnect_active_multiplier = 1
# Clear the IRC object's channel loggers and replace them with
# new ones by re-running log_setup().
while ircobj.loghandlers:
log.removeHandler(ircobj.loghandlers.pop())
ircobj.log_setup()
utils._reset_module_dirs()
for network, sdata in new_conf['servers'].items():
# Connect any new networks or disconnected networks if they aren't already.
if network not in world.networkobjects:
try:
proto = utils._get_protocol_module(sdata['protocol'])
# API note: 2.0.x style of starting network connections
world.networkobjects[network] = newirc = proto.Class(network)
newirc.connect()
except:
log.exception('Failed to initialize network %r, skipping it...', network)
log.info('Finished reloading PyLink configuration.')
if os.name == 'posix':
# Only register SIGHUP/SIGUSR1 on *nix.
def _sighup_handler(signo, _stack_frame):
"""Handles SIGHUP/SIGUSR1 by rehashing the PyLink daemon."""
log.info("Signal %s received, reloading config." % signo)
rehash()
signal.signal(signal.SIGHUP, _sighup_handler)
signal.signal(signal.SIGUSR1, _sighup_handler)

167
coremods/corecommands.py Normal file
View File

@ -0,0 +1,167 @@
"""
corecommands.py - Implements core PyLink commands.
"""
import gc
import sys
from pylinkirc import utils, world
from pylinkirc.log import log
from . import control, permissions
__all__ = []
# Essential, core commands go here so that the "commands" plugin with less-important,
# but still generic functions can be reloaded.
@utils.add_cmd
def shutdown(irc, source, args):
"""takes no arguments.
Exits PyLink by disconnecting all networks."""
permissions.check_permissions(irc, source, ['core.shutdown'])
log.info('(%s) SHUTDOWN requested by %s, exiting...', irc.name, irc.get_hostmask(source))
control.shutdown(irc=irc)
@utils.add_cmd
def load(irc, source, args):
"""<plugin name>.
Loads a plugin from the plugin folder."""
# Note: reload capability is acceptable here, because all it actually does is call
# load after unload.
permissions.check_permissions(irc, source, ['core.load', 'core.reload'])
try:
name = args[0]
except IndexError:
irc.reply("Error: Not enough arguments. Needs 1: plugin name.")
return
if name in world.plugins:
irc.reply("Error: %r is already loaded." % name)
return
log.info('(%s) Loading plugin %r for %s', irc.name, name, irc.get_hostmask(source))
try:
world.plugins[name] = pl = utils._load_plugin(name)
except ImportError as e:
if str(e) == ('No module named %r' % name):
log.exception('Failed to load plugin %r: The plugin could not be found.', name)
else:
log.exception('Failed to load plugin %r: ImportError.', name)
raise
else:
if hasattr(pl, 'main'):
log.debug('Calling main() function of plugin %r', pl)
pl.main(irc=irc)
irc.reply("Loaded plugin %r." % name)
@utils.add_cmd
def unload(irc, source, args):
"""<plugin name>.
Unloads a currently loaded plugin."""
permissions.check_permissions(irc, source, ['core.unload', 'core.reload'])
try:
name = args[0]
except IndexError:
irc.reply("Error: Not enough arguments. Needs 1: plugin name.")
return
# Since we're using absolute imports in 0.9.x+, the module name differs from the actual plugin
# name.
modulename = utils.PLUGIN_PREFIX + name
if name in world.plugins:
log.info('(%s) Unloading plugin %r for %s', irc.name, name, irc.get_hostmask(source))
pl = world.plugins[name]
log.debug('sys.getrefcount of plugin %s is %s', pl, sys.getrefcount(pl))
# Remove any command functions defined by the plugin.
for cmdname, cmdfuncs in world.services['pylink'].commands.copy().items():
log.debug('cmdname=%s, cmdfuncs=%s', cmdname, cmdfuncs)
for cmdfunc in cmdfuncs:
log.debug('__module__ of cmdfunc %s is %s', cmdfunc, cmdfunc.__module__)
if cmdfunc.__module__ == modulename:
log.debug("Removing %s from world.services['pylink'].commands[%s]", cmdfunc, cmdname)
world.services['pylink'].commands[cmdname].remove(cmdfunc)
# If the cmdfunc list is empty, remove it.
if not cmdfuncs:
log.debug("Removing world.services['pylink'].commands[%s] (it's empty now)", cmdname)
del world.services['pylink'].commands[cmdname]
# Remove any command hooks set by the plugin.
for hookname, hookpairs in world.hooks.copy().items():
for hookpair in hookpairs:
hookfunc = hookpair[1]
if hookfunc.__module__ == modulename:
log.debug('Trying to remove hook func %s (%s) from plugin %s', hookfunc, hookname, modulename)
world.hooks[hookname].remove(hookpair)
# If the hookfuncs list is empty, remove it.
if not hookpairs:
del world.hooks[hookname]
# Call the die() function in the plugin, if present.
if hasattr(pl, 'die'):
try:
pl.die(irc=irc)
except: # But don't allow it to crash the server.
log.exception('(%s) Error occurred in die() of plugin %s, skipping...', irc.name, pl)
# Delete it from memory (hopefully).
del world.plugins[name]
for n in (name, modulename):
if n in sys.modules:
del sys.modules[n]
if n in globals():
del globals()[n]
# Garbage collect.
gc.collect()
irc.reply("Unloaded plugin %r." % name)
return True # We succeeded, make it clear (this status is used by reload() below)
else:
irc.reply("Unknown plugin %r." % name)
@utils.add_cmd
def reload(irc, source, args):
"""<plugin name>.
Loads a plugin from the plugin folder."""
try:
name = args[0]
except IndexError:
irc.reply("Error: Not enough arguments. Needs 1: plugin name.")
return
# Note: these functions do permission checks, so there are none needed here.
if unload(irc, source, args):
load(irc, source, args)
@utils.add_cmd
def rehash(irc, source, args):
"""takes no arguments.
Reloads the configuration file for PyLink, (dis)connecting added/removed networks.
Note: plugins must be manually reloaded."""
permissions.check_permissions(irc, source, ['core.rehash'])
try:
control.rehash()
except Exception as e: # Something went wrong, abort.
irc.reply("Error loading configuration file: %s: %s" % (type(e).__name__, e))
return
else:
irc.reply("Done.")
@utils.add_cmd
def clearqueue(irc, source, args):
"""takes no arguments.
Clears the outgoing text queue for the current connection."""
permissions.check_permissions(irc, source, ['core.clearqueue'])
irc._queue.queue.clear()

229
coremods/exttargets.py Normal file
View File

@ -0,0 +1,229 @@
"""
exttargets.py - Implements extended targets like $account:xyz, $oper, etc.
"""
from pylinkirc import world
from pylinkirc.log import log
__all__ = []
def bind(func):
"""
Binds an exttarget with the given name.
"""
world.exttarget_handlers[func.__name__] = func
return func
@bind
def account(irc, host, uid):
"""
$account exttarget handler. The following forms are supported, with groups separated by a
literal colon. Account matching is case insensitive, while network name matching IS case
sensitive.
$account -> Returns True (a match) if the target is registered.
$account:accountname -> Returns True if the target's account name matches the one given, and the
target is connected to the local network.
$account:accountname:netname -> Returns True if both the target's account name and origin
network name match the ones given.
$account:*:netname -> Matches all logged in users on the given network.
"""
userobj = irc.users[uid]
homenet = irc.name
if hasattr(userobj, 'remote'):
# User is a PyLink Relay pseudoclient. Use their real services account on their
# origin network.
homenet, realuid = userobj.remote
log.debug('(%s) exttargets.account: Changing UID of relay client %s to %s/%s', irc.name,
uid, homenet, realuid)
try:
userobj = world.networkobjects[homenet].users[realuid]
except KeyError: # User lookup failed. Bail and return False.
log.exception('(%s) exttargets.account: KeyError finding %s/%s:', irc.name,
homenet, realuid)
return False
slogin = irc.to_lower(str(userobj.services_account))
# Split the given exttarget host into parts, so we know how many to look for.
groups = host.split(':')
log.debug('(%s) exttargets.account: groups to match: %s', irc.name, groups)
if len(groups) == 1:
# First scenario. Return True if user is logged in.
return bool(slogin)
elif len(groups) == 2:
# Second scenario. Return True if the user's account matches the one given.
return slogin == irc.to_lower(groups[1]) and homenet == irc.name
else:
# Third or fourth scenario. If there are more than 3 groups, the rest are ignored.
# In other words: Return True if the user is logged in, the query matches either '*' or the
# user's login, and the user is connected on the network requested.
return slogin and (irc.to_lower(groups[1]) in ('*', slogin)) and (homenet == groups[2])
@bind
def ircop(irc, host, uid):
"""
$ircop exttarget handler. The following forms are supported, with groups separated by a
literal colon. Oper types are matched case insensitively.
$ircop -> Returns True (a match) if the target is opered.
$ircop:*admin* -> Returns True if the target's is opered and their opertype matches the glob
given.
"""
groups = host.split(':')
log.debug('(%s) exttargets.ircop: groups to match: %s', irc.name, groups)
if len(groups) == 1:
# 1st scenario.
return irc.is_oper(uid)
else:
# 2nd scenario. Match the opertype glob to the opertype.
return irc.match_text(groups[1], irc.users[uid].opertype)
@bind
def server(irc, host, uid):
"""
$server exttarget handler. The following forms are supported, with groups separated by a
literal colon. Server names are matched case insensitively, but SIDs ARE case sensitive.
$server:server.name -> Returns True (a match) if the target is connected on the given server.
$server:server.glob -> Returns True (a match) if the target is connected on a server matching the glob.
$server:1XY -> Returns True if the target's is connected on the server with the given SID.
"""
groups = host.split(':')
log.debug('(%s) exttargets.server: groups to match: %s', irc.name, groups)
if len(groups) >= 2:
sid = irc.get_server(uid)
query = groups[1]
# Return True if the SID matches the query or the server's name glob matches it.
return sid == query or irc.match_text(query, irc.get_friendly_name(sid))
# $server alone is invalid. Don't match anything.
return False
@bind
def channel(irc, host, uid):
"""
$channel exttarget handler. The following forms are supported, with groups separated by a
literal colon. Channel names are matched case insensitively.
$channel:#channel -> Returns True if the target is in the given channel.
$channel:#channel:op -> Returns True if the target is in the given channel, and is opped.
Any other supported prefix (owner, admin, op, halfop, voice) can be given, but only one at a
time.
"""
groups = host.split(':')
log.debug('(%s) exttargets.channel: groups to match: %s', irc.name, groups)
try:
channel = groups[1]
except IndexError: # No channel given, abort.
return False
if channel not in irc.channels:
# Channel doesn't even exist...
return False
if len(groups) == 2:
# Just #channel was given as query
return uid in irc.channels[channel].users
elif len(groups) >= 3:
# For things like #channel:op, check if the query is in the user's prefix modes.
return (uid in irc.channels[channel].users) and (groups[2].lower() in irc.channels[channel].get_prefix_modes(uid))
@bind
def pylinkacc(irc, host, uid):
"""
$pylinkacc (PyLink account) exttarget handler. The following forms are supported, with groups
separated by a literal colon. Account matching is case insensitive.
$pylinkacc -> Returns True if the target is logged in to PyLink.
$pylinkacc:accountname -> Returns True if the target's PyLink login matches the one given.
"""
login = irc.to_lower(irc.users[uid].account)
groups = list(map(irc.to_lower, host.split(':')))
log.debug('(%s) exttargets.pylinkacc: groups to match: %s', irc.name, groups)
if len(groups) == 1:
# First scenario. Return True if user is logged in.
return bool(login)
elif len(groups) == 2:
# Second scenario. Return True if the user's login matches the one given.
return login == groups[1]
@bind
def network(irc, host, uid):
"""
$network exttarget handler. This exttarget takes one argument: a network name, and returns
a match for all users on that network.
Note: network names are case sensitive.
"""
try:
targetnet = host.split(':')[1]
except IndexError: # No network arg given, bail.
return False
userobj = irc.users[uid]
if hasattr(userobj, 'remote'):
# User is a PyLink Relay client; set the correct network name.
homenet = userobj.remote[0]
else:
homenet = irc.name
return homenet == targetnet
# Note: "and" can't be a function name so we use this.
def exttarget_and(irc, host, uid):
"""
$and exttarget handler. This exttarget takes a series of exttargets (or hostmasks) joined with
a "+", and returns True if all sub exttargets match.
Examples:
$and:($ircop:*admin*+$network:ovd) -> Matches all opers on the network ovd.
$and:($account+$pylinkirc) -> Matches all users logged in to both services and PyLink.
$and:(*!*@localhost+$ircop) -> Matches all opers with the host `localhost`.
$and:(*!*@*.mibbit.com+!$ircop+!$account) -> Matches all mibbit users that aren't opered or logged in to services.
"""
targets = host.split(':', 1)[-1]
# For readability, this requires that the exttarget list be wrapped in brackets.
if not (targets.startswith('(') and targets.endswith(')')):
return False
targets = targets[1:-1]
targets = list(filter(None, targets.split('+')))
log.debug('exttargets_and: using raw subtargets list %r (original query=%r)', targets, host)
# Wrap every subtarget into irc.match_host and return True if all subtargets return True.
return all(map(lambda sub_exttarget: irc.match_host(sub_exttarget, uid), targets))
world.exttarget_handlers['and'] = exttarget_and
@bind
def realname(irc, host, uid):
"""
$realname exttarget handler. This takes one argument: a glob, which is compared case-insensitively to the user's real name.
Examples:
$realname:*James* -> matches anyone with "James" in their real name.
"""
groups = host.split(':')
if len(groups) >= 2:
return irc.match_text(groups[1], irc.users[uid].realname)
@bind
def service(irc, host, uid):
"""
$service exttarget handler. This takes one optional argument: a glob, which is compared case-insensitively to the target user's service name (if present).
Examples:
$service -> Matches any PyLink service bot.
$service:automode -> Matches the Automode service bot.
"""
if not irc.users[uid].service:
return False
groups = host.split(':')
if len(groups) >= 2:
return irc.match_text(groups[1], irc.users[uid].service)
return True # It *is* a service bot because of the check at the top.

215
coremods/handlers.py Normal file
View File

@ -0,0 +1,215 @@
"""
handlers.py - Implements miscellaneous IRC command handlers (WHOIS, services login, etc.)
"""
import time
from pylinkirc import conf, utils
from pylinkirc.log import log
__all__ = []
def handle_whois(irc, source, command, args):
"""Handle WHOIS queries."""
target = args['target']
user = irc.users.get(target)
f = lambda num, source, text: irc.numeric(irc.sid, num, source, text)
# Get the server that the target is on.
server = irc.get_server(target)
if user is None: # User doesn't exist
# <- :42X 401 7PYAAAAAB jlu5- :No such nick/channel
nick = target
f(401, source, "%s :No such nick/channel" % nick)
else:
nick = user.nick
source_is_oper = ('o', None) in irc.users[source].modes
source_is_bot = (irc.umodes.get('bot'), None) in irc.users[source].modes
# Get the full network name.
netname = irc.serverdata.get('netname', irc.name)
# https://www.alien.net.au/irc/irc2numerics.html
# 311: sends nick!user@host information
f(311, source, "%s %s %s * :%s" % (nick, user.ident, user.host, user.realname))
# 319: RPL_WHOISCHANNELS; Show public channels of the target, respecting
# hidechans umodes for non-oper callers.
isHideChans = (irc.umodes.get('hidechans'), None) in user.modes
if (not isHideChans) or (isHideChans and source_is_oper):
public_chans = []
for chan in user.channels:
c = irc.channels[chan]
# Here, we'll want to hide secret/private channels from non-opers
# who are not in them.
if ((irc.cmodes.get('secret'), None) in c.modes or \
(irc.cmodes.get('private'), None) in c.modes) \
and not (source_is_oper or source in c.users):
continue
# Show the highest prefix mode like a regular IRCd does, if there are any.
prefixes = c.get_prefix_modes(target)
if prefixes:
highest = prefixes[0]
# Fetch the prefix mode letter from the named mode.
modechar = irc.cmodes[highest]
# Fetch and prepend the prefix character (@, +, etc.), given the mode letter.
chan = irc.prefixmodes[modechar] + chan
public_chans.append(chan)
if public_chans: # Only send the line if the person is in any visible channels...
f(319, source, '%s :%s' % (nick, ' '.join(public_chans)))
# 312: sends the server the target is on, and its server description.
f(312, source, "%s %s :%s" % (nick, irc.servers[server].name,
irc.servers[server].desc))
# 313: sends a string denoting the target's operator privilege if applicable.
if ('o', None) in user.modes:
# Check hideoper status. Require that either:
# 1) +H is not set
# 2) +H is set, but the caller is oper
# 3) +H is set, but whois_use_hideoper is disabled in config
isHideOper = (irc.umodes.get('hideoper'), None) in user.modes
if (not isHideOper) or (isHideOper and source_is_oper) or \
(isHideOper and not conf.conf['pylink'].get('whois_use_hideoper', True)):
opertype = user.opertype
# Let's be gramatically correct. (If the opertype starts with a vowel,
# write "an Operator" instead of "a Operator")
n = 'n' if opertype[0].lower() in 'aeiou' else ''
# Remove the "(on $network)" bit in relay oper types if the target network is the
# same - this prevents duplicate text such as "jlu5/ovd is a Network Administrator
# (on OVERdrive-IRC) on OVERdrive-IRC" from showing.
# XXX: does this post-processing really belong here?
opertype = opertype.replace(' (on %s)' % irc.get_full_network_name(), '')
f(313, source, "%s :is a%s %s" % (nick, n, opertype))
# 379: RPL_WHOISMODES, used by UnrealIRCd and InspIRCd to show user modes.
# Only show this to opers!
if source_is_oper:
f(378, source, "%s :is connecting from %s@%s %s" % (nick, user.ident, user.realhost, user.ip))
f(379, source, '%s :is using modes %s' % (nick, irc.join_modes(user.modes, sort=True)))
# 301: used to show away information if present
away_text = user.away
log.debug('(%s) coremods.handlers.handle_whois: away_text for %s is %r', irc.name, target, away_text)
if away_text:
f(301, source, '%s :%s' % (nick, away_text))
if (irc.umodes.get('bot'), None) in user.modes:
# Show botmode info in WHOIS.
f(335, source, "%s :is a bot" % nick)
# :charybdis.midnight.vpn 317 jlu5 jlu5 1946 1499867833 :seconds idle, signon time
if irc.get_service_bot(target) and conf.conf['pylink'].get('whois_show_startup_time', True):
f(317, source, "%s 0 %s :seconds idle (placeholder), signon time" % (nick, irc.start_ts))
# Call custom WHOIS handlers via the PYLINK_CUSTOM_WHOIS hook, unless the
# caller is marked a bot and the whois_show_extensions_to_bots option is False
if (source_is_bot and conf.conf['pylink'].get('whois_show_extensions_to_bots')) or (not source_is_bot):
irc.call_hooks([source, 'PYLINK_CUSTOM_WHOIS', {'target': target, 'server': server}])
else:
log.debug('(%s) coremods.handlers.handle_whois: skipping custom whois handlers because '
'caller %s is marked as a bot', irc.name, source)
# 318: End of WHOIS.
f(318, source, "%s :End of /WHOIS list" % nick)
utils.add_hook(handle_whois, 'WHOIS')
def handle_mode(irc, source, command, args):
"""Protect against forced deoper attempts."""
target = args['target']
modes = args['modes']
# If the sender is not a PyLink client, and the target IS a protected
# client, revert any forced deoper attempts.
if irc.is_internal_client(target) and not irc.is_internal_client(source):
if ('-o', None) in modes and (target == irc.pseudoclient.uid or not irc.is_manipulatable_client(target)):
irc.mode(irc.sid, target, {('+o', None)})
utils.add_hook(handle_mode, 'MODE')
def handle_operup(irc, source, command, args):
"""Logs successful oper-ups on networks."""
otype = args.get('text', 'IRC Operator')
log.debug("(%s) Successful oper-up (opertype %r) from %s", irc.name, otype, irc.get_hostmask(source))
irc.users[source].opertype = otype
utils.add_hook(handle_operup, 'CLIENT_OPERED')
def handle_services_login(irc, source, command, args):
"""Sets services login status for users."""
try:
irc.users[source].services_account = args['text']
except KeyError: # User doesn't exist
log.debug("(%s) Ignoring early account name setting for %s (UID hasn't been sent yet)", irc.name, source)
utils.add_hook(handle_services_login, 'CLIENT_SERVICES_LOGIN')
def handle_version(irc, source, command, args):
"""Handles requests for the PyLink server version."""
# 351 syntax is usually "<server version>. <server hostname> :<anything else you want to add>
fullversion = irc.version()
irc.numeric(irc.sid, 351, source, fullversion)
utils.add_hook(handle_version, 'VERSION')
def handle_time(irc, source, command, args):
"""Handles requests for the PyLink server time."""
timestring = time.ctime()
irc.numeric(irc.sid, 391, source, '%s :%s' % (irc.hostname(), timestring))
utils.add_hook(handle_time, 'TIME')
def _state_cleanup_core(irc, source, channel):
"""
Handles PART and KICK on clientbot-like networks (where only the users and channels we see are available)
by deleting channels when we leave and users when they leave all shared channels.
"""
if irc.has_cap('visible-state-only'):
# Delete channels that we were removed from.
if irc.pseudoclient and source == irc.pseudoclient.uid:
log.debug('(%s) state_cleanup: removing channel %s since we have left', irc.name, channel)
del irc._channels[channel]
# Delete external users no longer sharing a channel with us.
if (not irc.users[source].channels) and (not irc.is_internal_client(source)):
log.debug('(%s) state_cleanup: removing external user %s/%s who no longer shares a channel with us',
irc.name, source, irc.users[source].nick)
irc._remove_client(source)
# Clear empty non-permanent channels.
if channel in irc.channels and not (irc._channels[channel].users or ((irc.cmodes.get('permanent'), None) \
in irc._channels[channel].modes)):
log.debug('(%s) state_cleanup: removing empty channel %s', irc.name, channel)
del irc._channels[channel]
def _state_cleanup_part(irc, source, command, args):
for channel in args['channels']:
_state_cleanup_core(irc, source, channel)
utils.add_hook(_state_cleanup_part, 'PART', priority=-100)
def _state_cleanup_kick(irc, source, command, args):
_state_cleanup_core(irc, args['target'], args['channel'])
utils.add_hook(_state_cleanup_kick, 'KICK', priority=-100)
def _state_cleanup_mode(irc, source, command, args):
"""
Cleans up and removes empty channels when -P (permanent mode) is removed from them.
"""
target = args['target']
if target in irc.channels and 'permanent' in irc.cmodes:
c = irc.channels[target]
mode = '-%s' % irc.cmodes['permanent']
if (not c.users) and (mode, None) in args['modes']:
log.debug('(%s) _state_cleanup_mode: deleting empty channel %s as %s was set', irc.name, target, mode)
del irc._channels[target]
return False # Block further hooks from running
utils.add_hook(_state_cleanup_mode, 'MODE', priority=10000)

139
coremods/login.py Normal file
View File

@ -0,0 +1,139 @@
"""
login.py - Implement core login abstraction.
"""
from pylinkirc import conf, utils
from pylinkirc.log import log
__all__ = ['pwd_context', 'check_login', 'verify_hash']
# PyLink's global password context
pwd_context = None
_DEFAULT_CRYPTCONTEXT_SETTINGS = {
'schemes': ["pbkdf2_sha256", "sha512_crypt"]
}
def _make_cryptcontext():
try:
from passlib.context import CryptContext
except ImportError:
log.warning("Hashed passwords are disabled because passlib is not installed. Please install "
"it (pip3 install passlib) and rehash for this feature to work.")
return
context_settings = conf.conf.get('login', {}).get('cryptcontext_settings') or _DEFAULT_CRYPTCONTEXT_SETTINGS
global pwd_context
if pwd_context is None:
log.debug("Initialized new CryptContext with settings: %s", context_settings)
pwd_context = CryptContext(**context_settings)
else:
log.debug("Updated CryptContext with settings: %s", context_settings)
pwd_context.update(**context_settings)
_make_cryptcontext() # This runs at startup and in rehash (control.py)
def _get_account(accountname):
"""
Returns the login data block for the given account name (case-insensitive), or False if none
exists.
"""
accounts = {k.lower(): v for k, v in
conf.conf['login'].get('accounts', {}).items()}
try:
return accounts[accountname.lower()]
except KeyError:
return False
def check_login(user, password):
"""Checks whether the given user and password is a valid combination."""
account = _get_account(user)
if account:
passhash = account.get('password')
if not passhash:
# No password given, return. XXX: we should allow plugins to override
# this in the future.
return False
# Hashing in account passwords is optional.
if account.get('encrypted', False):
return verify_hash(password, passhash)
else:
return password == passhash
return False
def verify_hash(password, passhash):
"""Checks whether the password given matches the hash."""
if password:
if not pwd_context:
raise utils.NotAuthorizedError("Cannot log in to an account with a hashed password "
"because passlib is not installed.")
return pwd_context.verify(password, passhash)
return False # No password given!
def _irc_try_login(irc, source, username, skip_checks=False):
"""Internal function to process logins via IRC."""
if irc.is_internal_client(source):
irc.error("Cannot use 'identify' via a command proxy.")
return
if not skip_checks:
logindata = _get_account(username)
network_filter = logindata.get('networks')
require_oper = logindata.get('require_oper', False)
hosts_filter = logindata.get('hosts', [])
if network_filter and irc.name not in network_filter:
log.warning("(%s) Failed login to %r from %s (wrong network: networks filter says %r but we got %r)",
irc.name, username, irc.get_hostmask(source), ', '.join(network_filter), irc.name)
raise utils.NotAuthorizedError("Account is not authorized to login on this network.")
elif require_oper and not irc.is_oper(source):
log.warning("(%s) Failed login to %r from %s (needs oper)", irc.name, username, irc.get_hostmask(source))
raise utils.NotAuthorizedError("You must be opered.")
elif hosts_filter and not any(irc.match_host(host, source) for host in hosts_filter):
log.warning("(%s) Failed login to %r from %s (hostname mismatch)", irc.name, username, irc.get_hostmask(source))
raise utils.NotAuthorizedError("Hostname mismatch.")
irc.users[source].account = username
irc.reply('Successfully logged in as %s.' % username)
log.info("(%s) Successful login to %r by %s",
irc.name, username, irc.get_hostmask(source))
return True
def identify(irc, source, args):
"""<username> <password>
Logs in to PyLink using the configured administrator account."""
if irc.is_channel(irc.called_in):
irc.reply('Error: This command must be sent in private. '
'(Would you really type a password inside a channel?)')
return
try:
username, password = args[0], args[1]
except IndexError:
irc.reply('Error: Not enough arguments.')
return
# Process new-style accounts.
if check_login(username, password):
_irc_try_login(irc, source, username)
return
# Process legacy logins (login:user).
if username.lower() == conf.conf['login'].get('user', '').lower() and password == conf.conf['login'].get('password'):
realuser = conf.conf['login']['user']
_irc_try_login(irc, source, realuser, skip_checks=True)
return
# Username not found or password incorrect.
log.warning("(%s) Failed login to %r from %s", irc.name, username, irc.get_hostmask(source))
raise utils.NotAuthorizedError('Bad username or password.')
utils.add_cmd(identify, aliases=('login', 'id'))

67
coremods/permissions.py Normal file
View File

@ -0,0 +1,67 @@
"""
permissions.py - Permissions Abstraction for PyLink IRC Services.
"""
from collections import defaultdict
from pylinkirc import conf, utils
from pylinkirc.log import log
__all__ = ['default_permissions', 'add_default_permissions',
'remove_default_permissions', 'check_permissions']
# Global variables: these store mappings of hostmasks/exttargets to lists of permissions each target has.
default_permissions = defaultdict(set)
def add_default_permissions(perms):
"""Adds default permissions to the index."""
global default_permissions
for target, permlist in perms.items():
default_permissions[target] |= set(permlist)
addDefaultPermissions = add_default_permissions
def remove_default_permissions(perms):
"""Remove default permissions from the index."""
global default_permissions
for target, permlist in perms.items():
default_permissions[target] -= set(permlist)
removeDefaultPermissions = remove_default_permissions
def check_permissions(irc, uid, perms, also_show=[]):
"""
Checks permissions of the caller. If the caller has any of the permissions listed in perms,
this function returns True. Otherwise, NotAuthorizedError is raised.
"""
# For old (< 1.1 login blocks):
# If the user is logged in, they automatically have all permissions.
olduser = conf.conf['login'].get('user')
if olduser and irc.match_host('$pylinkacc:%s' % olduser, uid):
log.debug('permissions: overriding permissions check for old-style admin user %s',
irc.get_hostmask(uid))
return True
permissions = defaultdict(set)
# Enumerate the configured permissions list.
for k, v in (conf.conf.get('permissions') or {}).items():
permissions[k] |= set(v)
# Merge in default permissions if enabled.
if conf.conf.get('permissions_merge_defaults', True):
for k, v in default_permissions.items():
permissions[k] |= v
for host, permlist in permissions.items():
log.debug('permissions: permlist for %s: %s', host, permlist)
if irc.match_host(host, uid):
# Now, iterate over all the perms we are looking for.
for perm in permlist:
# Use irc.match_host to expand globs in an IRC-case insensitive and wildcard
# friendly way. e.g. 'xyz.*.#Channel\' will match 'xyz.manage.#channel|' on IRCds
# using the RFC1459 casemapping.
log.debug('permissions: checking if %s glob matches anything in %s', perm, permlist)
if any(irc.match_host(perm, p) for p in perms):
return True
raise utils.NotAuthorizedError("You are missing one of the following permissions: %s" %
(', '.join(perms+also_show)))
checkPermissions = check_permissions

191
coremods/service_support.py Normal file
View File

@ -0,0 +1,191 @@
"""
service_support.py - Implements handlers for the PyLink ServiceBot API.
"""
from pylinkirc import conf, utils, world
from pylinkirc.log import log
__all__ = []
def spawn_service(irc, source, command, args):
"""Handles new service bot introductions."""
if not irc.connected.is_set():
return
# Service name
name = args['name']
if name != 'pylink' and not irc.has_cap('can-spawn-clients'):
log.debug("(%s) Not spawning service %s because the server doesn't support spawning clients",
irc.name, name)
return
# Get the ServiceBot object.
sbot = world.services[name]
old_userobj = irc.users.get(sbot.uids.get(irc.name))
if old_userobj and old_userobj.service:
# A client already exists, so don't respawn it.
log.debug('(%s) spawn_service: Not respawning service %r as service client %r already exists.', irc.name, name,
irc.pseudoclient.nick)
return
if name == 'pylink' and irc.pseudoclient:
# irc.pseudoclient already exists, reuse values from it but
# spawn a new client. This is used for protocols like Clientbot,
# so that they can override the main service nick, among other things.
log.debug('(%s) spawn_service: Using existing nick %r for service %r', irc.name, irc.pseudoclient.nick, name)
userobj = irc.pseudoclient
userobj.opertype = "PyLink Service"
userobj.manipulatable = sbot.manipulatable
else:
# No client exists, spawn a new one
nick = sbot.get_nick(irc)
ident = sbot.get_ident(irc)
host = sbot.get_host(irc)
realname = sbot.get_realname(irc)
# Spawning service clients with these umodes where supported. servprotect usage is a
# configuration option.
preferred_modes = ['oper', 'hideoper', 'hidechans', 'invisible', 'bot']
modes = []
if conf.conf['pylink'].get('protect_services'):
preferred_modes.append('servprotect')
for mode in preferred_modes:
mode = irc.umodes.get(mode)
if mode:
modes.append((mode, None))
# Track the service's UIDs on each network.
log.debug('(%s) spawn_service: Spawning new client %s for service %s', irc.name, nick, name)
userobj = irc.spawn_client(nick, ident, host, modes=modes, opertype="PyLink Service",
realname=realname, manipulatable=sbot.manipulatable)
# Store the service name in the User object for easier access.
userobj.service = name
sbot.uids[irc.name] = u = userobj.uid
# Special case: if this is the main PyLink client being spawned,
# assign this as irc.pseudoclient.
if name == 'pylink' and not irc.pseudoclient:
log.debug('(%s) spawn_service: irc.pseudoclient set to UID %s', irc.name, u)
irc.pseudoclient = userobj
# Enumerate & join network defined channels.
sbot.join(irc, sbot.get_persistent_channels(irc))
utils.add_hook(spawn_service, 'PYLINK_NEW_SERVICE')
def handle_disconnect(irc, source, command, args):
"""Handles network disconnections."""
for name, sbot in world.services.items():
try:
del sbot.uids[irc.name]
log.debug("coremods.service_support: removing uids[%s] from service bot %s", irc.name, sbot.name)
except KeyError:
continue
utils.add_hook(handle_disconnect, 'PYLINK_DISCONNECT')
def handle_endburst(irc, source, command, args):
"""Handles network bursts."""
if source == irc.uplink:
log.debug('(%s): spawning service bots now.', irc.name)
# We just connected. Burst all our registered services.
for name, sbot in world.services.items():
spawn_service(irc, source, command, {'name': name})
utils.add_hook(handle_endburst, 'ENDBURST', priority=500)
def handle_kill(irc, source, command, args):
"""Handle KILLs to PyLink service bots, respawning them as needed."""
target = args['target']
if irc.pseudoclient and target == irc.pseudoclient.uid:
irc.pseudoclient = None
userdata = args.get('userdata')
sbot = irc.get_service_bot(target)
servicename = None
if userdata and hasattr(userdata, 'service'): # Look for the target's service name attribute
servicename = userdata.service
elif sbot: # Or their service bot instance
servicename = sbot.name
if servicename:
log.info('(%s) Received kill to service %r (nick: %r) from %s (reason: %r).', irc.name, servicename,
userdata.nick if userdata else irc.users[target].nick, irc.get_hostmask(source), args.get('text'))
spawn_service(irc, source, command, {'name': servicename})
utils.add_hook(handle_kill, 'KILL')
def handle_join(irc, source, command, args):
"""Monitors channel joins for dynamic service bot joining."""
if irc.has_cap('visible-state-only'):
# No-op on bot-only servers.
return
channel = args['channel']
users = irc.channels[channel].users
for servicename, sbot in world.services.items():
if channel in sbot.get_persistent_channels(irc) and \
sbot.uids.get(irc.name) not in users:
log.debug('(%s) Dynamically joining service %r to channel %r.', irc.name, servicename, channel)
sbot.join(irc, channel)
utils.add_hook(handle_join, 'JOIN')
utils.add_hook(handle_join, 'PYLINK_SERVICE_JOIN')
def _services_dynamic_part(irc, channel):
"""Dynamically removes service bots from empty channels."""
if irc.has_cap('visible-state-only'):
# No-op on bot-only servers.
return
if irc.serverdata.get('join_empty_channels', conf.conf['pylink'].get('join_empty_channels', False)):
return
# If all remaining users in the channel are service bots, make them all part.
if all(irc.get_service_bot(u) for u in irc.channels[channel].users):
for u in irc.channels[channel].users.copy():
sbot = irc.get_service_bot(u)
if sbot:
log.debug('(%s) Dynamically parting service %r from channel %r.', irc.name, sbot.name, channel)
irc.part(u, channel)
return True
def handle_part(irc, source, command, args):
"""Monitors channel joins for dynamic service bot joining."""
for channel in args['channels']:
_services_dynamic_part(irc, channel)
utils.add_hook(handle_part, 'PART')
def handle_kick(irc, source, command, args):
"""Handle KICKs to the PyLink service bots, rejoining channels as needed."""
channel = args['channel']
# Skip autorejoin routines if the channel is now empty.
if not _services_dynamic_part(irc, channel):
kicked = args['target']
sbot = irc.get_service_bot(kicked)
if sbot and channel in sbot.get_persistent_channels(irc):
sbot.join(irc, channel)
utils.add_hook(handle_kick, 'KICK')
def handle_commands(irc, source, command, args):
"""Handle commands sent to the PyLink service bots (PRIVMSG)."""
target = args['target']
text = args['text']
sbot = irc.get_service_bot(target)
if sbot:
sbot.call_cmd(irc, source, text)
utils.add_hook(handle_commands, 'PRIVMSG')
# Register the main PyLink service. All command definitions MUST go after this!
# TODO: be more specific in description, and possibly allow plugins to modify this to mention
# their features?
mydesc = "\x02PyLink\x02 provides extended network services for IRC."
utils.register_service('pylink', default_nick="PyLink", desc=mydesc, manipulatable=True)

View File

@ -1,470 +0,0 @@
"""
coreplugin.py - Implements core PyLink functions as a plugin.
"""
import gc
import sys
import signal
import os
import utils
import conf
import classes
from log import log
import world
def _shutdown(irc=None):
"""Shuts down the Pylink daemon."""
for name, plugin in world.plugins.items():
# Before closing connections, tell all plugins to shutdown cleanly first.
if hasattr(plugin, 'die'):
log.debug('coreplugin: Running die() on plugin %s due to shutdown.', name)
try:
plugin.die(irc)
except: # But don't allow it to crash the server.
log.exception('coreplugin: Error occurred in die() of plugin %s, skipping...', name)
for ircobj in world.networkobjects.values():
# Disconnect all our networks. Disable auto-connect first by setting
# the time to negative.
ircobj.serverdata['autoconnect'] = -1
ircobj.disconnect()
def sigterm_handler(_signo, _stack_frame):
"""Handles SIGTERM gracefully by shutting down the PyLink daemon."""
log.info("Shutting down on SIGTERM.")
_shutdown()
signal.signal(signal.SIGTERM, sigterm_handler)
def handle_kill(irc, source, command, args):
"""Handle KILLs to PyLink service bots, respawning them as needed."""
target = args['target']
sbot = irc.isServiceBot(target)
if sbot:
spawn_service(irc, source, command, {'name': sbot.name})
return
utils.add_hook(handle_kill, 'KILL')
def handle_kick(irc, source, command, args):
"""Handle KICKs to the PyLink service bots, rejoining channels as needed."""
kicked = args['target']
channel = args['channel']
if irc.isServiceBot(kicked):
irc.proto.join(kicked, channel)
utils.add_hook(handle_kick, 'KICK')
def handle_commands(irc, source, command, args):
"""Handle commands sent to the PyLink service bots (PRIVMSG)."""
target = args['target']
text = args['text']
sbot = irc.isServiceBot(target)
if sbot:
sbot.call_cmd(irc, source, text)
utils.add_hook(handle_commands, 'PRIVMSG')
def handle_whois(irc, source, command, args):
"""Handle WHOIS queries, for IRCds that send them across servers (charybdis, UnrealIRCd; NOT InspIRCd)."""
target = args['target']
user = irc.users.get(target)
if user is None:
log.warning('(%s) Got a WHOIS request for %r from %r, but the target '
'doesn\'t exist in irc.users!', irc.name, target, source)
return
f = irc.proto.numeric
server = irc.getServer(target) or irc.sid
nick = user.nick
sourceisOper = ('o', None) in irc.users[source].modes
# Get the full network name.
netname = irc.serverdata.get('netname', irc.name)
# https://www.alien.net.au/irc/irc2numerics.html
# 311: sends nick!user@host information
f(server, 311, source, "%s %s %s * :%s" % (nick, user.ident, user.host, user.realname))
# 319: RPL_WHOISCHANNELS; Show public channels of the target, respecting
# hidechans umodes for non-oper callers.
isHideChans = (irc.umodes.get('hidechans'), None) in user.modes
if (not isHideChans) or (isHideChans and sourceisOper):
public_chans = []
for chan in user.channels:
c = irc.channels[chan]
# Here, we'll want to hide secret/private channels from non-opers
# who are not in them.
if ((irc.cmodes.get('secret'), None) in c.modes or \
(irc.cmodes.get('private'), None) in c.modes) \
and not (sourceisOper or source in c.users):
continue
# Show prefix modes like a regular IRCd does.
for prefixmode in c.getPrefixModes(target):
modechar = irc.cmodes[prefixmode]
chan = irc.prefixmodes[modechar] + chan
public_chans.append(chan)
if public_chans: # Only send the line if the person is in any visible channels...
f(server, 319, source, '%s :%s' % (nick, ' '.join(public_chans)))
# 312: sends the server the target is on, and its server description.
f(server, 312, source, "%s %s :%s" % (nick, irc.servers[server].name,
irc.servers[server].desc))
# 313: sends a string denoting the target's operator privilege if applicable.
if ('o', None) in user.modes:
# Check hideoper status. Require that either:
# 1) +H is not set
# 2) +H is set, but the caller is oper
# 3) +H is set, but whois_use_hideoper is disabled in config
isHideOper = (irc.umodes.get('hideoper'), None) in user.modes
if (not isHideOper) or (isHideOper and sourceisOper) or \
(isHideOper and not irc.botdata.get('whois_use_hideoper', True)):
# Let's be gramatically correct. (If the opertype starts with a vowel,
# write "an Operator" instead of "a Operator")
n = 'n' if user.opertype[0].lower() in 'aeiou' else ''
# I want to normalize the syntax: PERSON is an OPERTYPE on NETWORKNAME.
# This is the only syntax InspIRCd supports, but for others it doesn't
# really matter since we're handling the WHOIS requests by ourselves.
f(server, 313, source, "%s :is a%s %s on %s" % (nick, n, user.opertype, netname))
# 379: RPL_WHOISMODES, used by UnrealIRCd and InspIRCd to show user modes.
# Only show this to opers!
if sourceisOper:
f(server, 378, source, "%s :is connecting from %s@%s %s" % (nick, user.ident, user.realhost, user.ip))
f(server, 379, source, '%s :is using modes %s' % (nick, irc.joinModes(user.modes)))
# 301: used to show away information if present
away_text = user.away
log.debug('(%s) coreplugin/handle_whois: away_text for %s is %r', irc.name, target, away_text)
if away_text:
f(server, 301, source, '%s :%s' % (nick, away_text))
# 317: shows idle and signon time. However, we don't track the user's real
# idle time, so we simply return 0.
# <- 317 GL GL 15 1437632859 :seconds idle, signon time
f(server, 317, source, "%s 0 %s :seconds idle, signon time" % (nick, user.ts))
if (irc.umodes.get('bot'), None) in user.modes:
# Show botmode info in WHOIS.
f(server, 335, source, "%s :is a bot" % nick)
# Call custom WHOIS handlers via the PYLINK_CUSTOM_WHOIS hook.
irc.callHooks([source, 'PYLINK_CUSTOM_WHOIS', {'target': target, 'server': server}])
# 318: End of WHOIS.
f(server, 318, source, "%s :End of /WHOIS list" % nick)
utils.add_hook(handle_whois, 'WHOIS')
def handle_mode(irc, source, command, args):
"""Protect against forced deoper attempts."""
target = args['target']
modes = args['modes']
# If the sender is not a PyLink client, and the target IS a protected
# client, revert any forced deoper attempts.
if irc.isInternalClient(target) and not irc.isInternalClient(source):
if ('-o', None) in modes and (target == irc.pseudoclient.uid or not irc.isManipulatableClient(target)):
irc.proto.mode(irc.sid, target, {('+o', None)})
utils.add_hook(handle_mode, 'MODE')
def handle_operup(irc, source, command, args):
"""Logs successful oper-ups on networks."""
otype = args.get('text', 'IRC Operator')
log.debug("(%s) Successful oper-up (opertype %r) from %s", irc.name, otype, irc.getHostmask(source))
irc.users[source].opertype = otype
utils.add_hook(handle_operup, 'CLIENT_OPERED')
def handle_services_login(irc, source, command, args):
"""Sets services login status for users."""
try:
irc.users[source].services_account = args['text']
except KeyError: # User doesn't exist
log.debug("(%s) Ignoring early account name setting for %s (UID hasn't been sent yet)", irc.name, source)
utils.add_hook(handle_services_login, 'CLIENT_SERVICES_LOGIN')
def handle_version(irc, source, command, args):
"""Handles requests for the PyLink server version."""
# 351 syntax is usually "<server version>. <server hostname> :<anything else you want to add>
fullversion = irc.version()
irc.proto.numeric(irc.sid, 351, source, fullversion)
utils.add_hook(handle_version, 'VERSION')
def spawn_service(irc, source, command, args):
"""Handles new service bot introductions."""
if not irc.connected.is_set():
return
name = args['name']
# TODO: make this configurable?
host = irc.serverdata["hostname"]
# Prefer spawning service clients with umode +io, plus hideoper and
# hidechans if supported.
modes = []
for mode in ('oper', 'hideoper', 'hidechans', 'invisible', 'bot'):
mode = irc.umodes.get(mode)
if mode:
modes.append((mode, None))
# Track the service's UIDs on each network.
sbot = world.services[name]
userobj = irc.proto.spawnClient(sbot.nick, sbot.ident,
host, modes=modes, opertype="PyLink Service",
manipulatable=sbot.manipulatable)
sbot.uids[irc.name] = u = userobj.uid
# Special case: if this is the main PyLink client being spawned,
# assign this as irc.pseudoclient.
if name == 'pylink':
irc.pseudoclient = userobj
# TODO: channels should be tracked in a central database, not hardcoded
# in conf.
for chan in irc.serverdata['channels']:
irc.proto.join(u, chan)
utils.add_hook(spawn_service, 'PYLINK_NEW_SERVICE')
def handle_disconnect(irc, source, command, args):
"""Handles network disconnections."""
for name, sbot in world.services.items():
try:
del sbot.uids[irc.name]
log.debug("coreplugin: removing uids[%s] from service bot %s", irc.name, sbot.name)
except KeyError:
continue
utils.add_hook(handle_disconnect, 'PYLINK_DISCONNECT')
def handle_endburst(irc, source, command, args):
"""Handles network bursts."""
if source == irc.uplink:
log.debug('(%s): spawning service bots now.', irc.name)
# We just connected. Burst all our registered services.
for name, sbot in world.services.items():
spawn_service(irc, source, command, {'name': name})
utils.add_hook(handle_endburst, 'ENDBURST')
# Register the main PyLink service. All command definitions MUST go after this!
mynick = conf.conf['bot'].get("nick", "PyLink")
myident = conf.conf['bot'].get("ident", "pylink")
utils.registerService('pylink', nick=mynick, ident=myident, manipulatable=True)
# Essential, core commands go here so that the "commands" plugin with less-important,
# but still generic functions can be reloaded.
@utils.add_cmd
def identify(irc, source, args):
"""<username> <password>
Logs in to PyLink using the configured administrator account."""
if utils.isChannel(irc.called_by):
irc.reply('Error: This command must be sent in private. '
'(Would you really type a password inside a channel?)')
return
try:
username, password = args[0], args[1]
except IndexError:
irc.msg(source, 'Error: Not enough arguments.')
return
# Usernames are case-insensitive, passwords are NOT.
if username.lower() == irc.conf['login']['user'].lower() and password == irc.conf['login']['password']:
realuser = irc.conf['login']['user']
irc.users[source].identified = realuser
irc.msg(source, 'Successfully logged in as %s.' % realuser)
log.info("(%s) Successful login to %r by %s",
irc.name, username, irc.getHostmask(source))
else:
irc.msg(source, 'Error: Incorrect credentials.')
u = irc.users[source]
log.warning("(%s) Failed login to %r from %s",
irc.name, username, irc.getHostmask(source))
@utils.add_cmd
def shutdown(irc, source, args):
"""takes no arguments.
Exits PyLink by disconnecting all networks."""
irc.checkAuthenticated(source, allowOper=False)
u = irc.users[source]
log.info('(%s) SHUTDOWN requested by "%s!%s@%s", exiting...', irc.name, u.nick,
u.ident, u.host)
_shutdown(irc)
def load(irc, source, args):
"""<plugin name>.
Loads a plugin from the plugin folder."""
irc.checkAuthenticated(source, allowOper=False)
try:
name = args[0]
except IndexError:
irc.reply("Error: Not enough arguments. Needs 1: plugin name.")
return
if name in world.plugins:
irc.reply("Error: %r is already loaded." % name)
return
log.info('(%s) Loading plugin %r for %s', irc.name, name, irc.getHostmask(source))
try:
world.plugins[name] = pl = utils.loadModuleFromFolder(name, world.plugins_folder)
except ImportError as e:
if str(e) == ('No module named %r' % name):
log.exception('Failed to load plugin %r: The plugin could not be found.', name)
else:
log.exception('Failed to load plugin %r: ImportError.', name)
raise
else:
if hasattr(pl, 'main'):
log.debug('Calling main() function of plugin %r', pl)
pl.main(irc)
irc.reply("Loaded plugin %r." % name)
utils.add_cmd(load)
def unload(irc, source, args):
"""<plugin name>.
Unloads a currently loaded plugin."""
irc.checkAuthenticated(source, allowOper=False)
try:
name = args[0]
except IndexError:
irc.reply("Error: Not enough arguments. Needs 1: plugin name.")
return
if name in world.plugins:
log.info('(%s) Unloading plugin %r for %s', irc.name, name, irc.getHostmask(source))
pl = world.plugins[name]
log.debug('sys.getrefcount of plugin %s is %s', pl, sys.getrefcount(pl))
# Remove any command functions defined by the plugin.
for cmdname, cmdfuncs in world.services['pylink'].commands.copy().items():
log.debug('cmdname=%s, cmdfuncs=%s', cmdname, cmdfuncs)
for cmdfunc in cmdfuncs:
log.debug('__module__ of cmdfunc %s is %s', cmdfunc, cmdfunc.__module__)
if cmdfunc.__module__ == name:
log.debug("Removing %s from world.services['pylink'].commands[%s]", cmdfunc, cmdname)
world.services['pylink'].commands[cmdname].remove(cmdfunc)
# If the cmdfunc list is empty, remove it.
if not cmdfuncs:
log.debug("Removing world.services['pylink'].commands[%s] (it's empty now)", cmdname)
del world.services['pylink'].commands[cmdname]
# Remove any command hooks set by the plugin.
for hookname, hookfuncs in world.hooks.copy().items():
for hookfunc in hookfuncs:
if hookfunc.__module__ == name:
world.hooks[hookname].remove(hookfunc)
# If the hookfuncs list is empty, remove it.
if not hookfuncs:
del world.hooks[hookname]
# Call the die() function in the plugin, if present.
if hasattr(pl, 'die'):
try:
pl.die(irc)
except: # But don't allow it to crash the server.
log.exception('(%s) Error occurred in die() of plugin %s, skipping...', irc.name, pl)
# Delete it from memory (hopefully).
del world.plugins[name]
if name in sys.modules:
del sys.modules[name]
if name in globals():
del globals()[name]
# Garbage collect.
gc.collect()
irc.reply("Unloaded plugin %r." % name)
return True # We succeeded, make it clear (this status is used by reload() below)
else:
irc.reply("Unknown plugin %r." % name)
utils.add_cmd(unload)
@utils.add_cmd
def reload(irc, source, args):
"""<plugin name>.
Loads a plugin from the plugin folder."""
try:
name = args[0]
except IndexError:
irc.reply("Error: Not enough arguments. Needs 1: plugin name.")
return
if unload(irc, source, args):
load(irc, source, args)
def _rehash():
"""Rehashes the PyLink daemon."""
old_conf = conf.conf.copy()
fname = conf.fname
new_conf = conf.loadConf(fname, errors_fatal=False)
new_conf = conf.validateConf(new_conf)
conf.conf = new_conf
for network, ircobj in world.networkobjects.copy().items():
# Server was removed from the config file, disconnect them.
log.debug('rehash: checking if %r is in new conf still.', network)
if network not in new_conf['servers']:
log.debug('rehash: removing connection to %r (removed from config).', network)
# Disable autoconnect first.
ircobj.serverdata['autoconnect'] = -1
ircobj.disconnect()
del world.networkobjects[network]
else:
ircobj.conf = new_conf
ircobj.serverdata = new_conf['servers'][network]
ircobj.botdata = new_conf['bot']
# Clear the IRC object's channel loggers and replace them with
# new ones by re-running logSetup().
while ircobj.loghandlers:
log.removeHandler(ircobj.loghandlers.pop())
ircobj.logSetup()
# TODO: update file loggers here too.
for network, sdata in new_conf['servers'].items():
# New server was added. Connect them if not already connected.
if network not in world.networkobjects:
proto = utils.getProtocolModule(sdata['protocol'])
world.networkobjects[network] = classes.Irc(network, proto, new_conf)
if os.name == 'posix':
# Only register SIGHUP on *nix.
def sighup_handler(_signo, _stack_frame):
"""Handles SIGHUP by rehashing the PyLink daemon."""
log.info("SIGHUP received, reloading config.")
_rehash()
signal.signal(signal.SIGHUP, sighup_handler)
@utils.add_cmd
def rehash(irc, source, args):
"""takes no arguments.
Reloads the configuration file for PyLink, (dis)connecting added/removed networks.
Plugins must be manually reloaded."""
irc.checkAuthenticated(source, allowOper=False)
try:
_rehash()
except Exception as e: # Something went wrong, abort.
log.exception("Error REHASHing config: ")
irc.reply("Error loading configuration file: %s: %s" % (type(e).__name__, e))
return
else:
irc.reply("Done.")

View File

@ -4,5 +4,20 @@ This folder contains general documentation for PyLink IRC services.
## Contents ## Contents
- [Opering with PyLink Relay](pylink-opers.md) - [PyLink FAQ (Frequently Asked Questions)](faq.md)
- [PyLink Relay Quick Start Guide](relay-quickstart.md)
----
- [Automode Tutorial](automode.md)
- [Advanced Relay Configuration](advanced-relay-config.md)
- [Advanced Services Configuration](advanced-services-config.md)
- [Extended Targets (Exttargets) Guide](exttargets.md)
- [PyLink Permissions Reference](permissions-reference.md)
----
- [PyLink named modes tables](modelists/)
- [Developer documentation](technical/) - [Developer documentation](technical/)
There is also a Doxygen-powered API reference at https://pylink.github.io/

View File

@ -0,0 +1,93 @@
# Advanced Configuration for PyLink Relay
PyLink Relay provides a few configuration options not documented in the example configuration, either because they have limited use or are too complicated to be described briefly.
**This guide assumes that you are relatively familiar with the way YAML syntax works (lists, named arrays/dicts, etc.).** In this document, configuration options will be referred to in the format `a::b::c`, which represents the "`c`" option inside a "`b`" config block, all within an "`a`" config block.
In actual YAML, that translates to this:
```yaml
a:
b:
c: "some value"
```
### Custom Clientbot Styles
Custom Clientbot styles can be applied for any of Clientbot's supported events, by defining keys in the format `relay::clientbot_styles::<event name>`. As of PyLink 2.1, you can also override options per network by defining them in the form `servers::<network name>::relay_clientbot_styles::<event names>`
See below for a list of supported events and their default values (as of PyLink 2.1).
A common use case for this feature is to turn off or adjust colors/formatting; this is explicitly documented [below](#disabling-colorscontrol-codes).
These options take template strings as documented here: https://docs.python.org/3/library/string.html#template-strings. Supported substitution values differ by event, but usually include the [hook values for each](technical/hooks-reference.md#irc-command-hooks), *plus* the following:
- For all events:
- `$netname`: origin network name
- `$sender`: nick of sender
- `$sender_identhost`: ident@host string of the sender
- `$colored_sender`: color hashed version of `$sender`
- `$colored_netname`: color hashed version of `$netname`
- For KICK, and other events that have a `$target` field corresponding to a user:
- `$target_nick`: the nick of the target (as opposed to `$target`, which is an user ID)
- For events that have a `$channel` field attached (e.g. JOIN, PART):
- `$local_channel`: the *local* channel name (i.e. the channel on the clientbot network)
- `$channel`: the real channel name on the sender's network
- `$mode_prefix`: the highest prefix mode of the sender, if they are a user. This is normally either empty or one of (common prefix modes) `~&!@%+`.
- For SJOIN, SQUIT:
- `$nicks`: a comma-joined list of nicks that were bursted
- `$colored_nicks`: a comma-joined list of each bursted nick, color hashed
To disable relaying for any specific event, set the template string to an empty string (`''`).
#### List of supported events
|Event name|Default value|
| :---: | :--- |
MESSAGE | \x02[$netname]\x02 <$colored\_sender> $text
KICK | \x02[$netname]\x02 - $colored_sender$sender\_identhost has kicked $target_nick from $channel ($text)
PART | \x02[$netname]\x02 - $colored_sender$sender\_identhost has left $channel ($text)
JOIN | \x02[$netname]\x02 - $colored_sender$sender\_identhost has joined $channel
NICK | \x02[$netname]\x02 - $colored_sender$sender\_identhost is now known as $newnick
QUIT | \x02[$netname]\x02 - $colored_sender$sender\_identhost has quit ($text)
ACTION | \x02[$netname]\x02 * $colored\_sender $text
NOTICE | \x02[$netname]\x02 - Notice from $colored\_sender: $text
SQUIT | \x02[$netname]\x02 - Netsplit lost users: $colored\_nicks
SJOIN | \x02[$netname]\x02 - Netjoin gained users: $colored\_nicks
MODE | \x02[$netname]\x02 - $colored_sender$sender_identhost sets mode $modes on $channel
PM | PM from $sender on $netname: $text
PNOTICE | <$sender> $text
- Note: the `PM` and `PNOTICE` events represent private messages and private notices respectively, when they're relayed to users behind a Clientbot link.
- Note 2: as of 1.1.x, all public channel events are sent to channels as PRIVMSG, while `PM` and `PNOTICE` are relayed privately as NOTICE.
#### Disabling Colors/Control Codes
If you don't want the messages PyLink sends for clientbot messages to be emboldened or colored,
remove all escape sequences (e.g. `\x02`) from the format template and replace the colored variants
of applicable substitutions with their non-colored versions.
This is a example clientbot_styles config block, which you can copy *into* your `relay` configuration block.
(*Do not* make multiple `relay` config blocks, or duplicate any config blocks with the same name!)
```yaml
clientbot_styles:
ACTION: "[$netname] * $sender $text"
JOIN: "[$netname] - $sender$sender_identhost has joined $channel"
KICK: "[$netname] - $sender$sender_identhost has kicked $target_nick from $channel ($text)"
MESSAGE: "[$netname] <$sender> $text"
MODE: "[$netname] - $sender$sender_identhost sets mode $modes on $channel"
NICK: "[$netname] - $sender$sender_identhost is now known as $newnick"
NOTICE: "[$netname] - Notice from $sender: $text"
PART: "[$netname] - $sender$sender_identhost has left $channel ($text)"
PM: "PM from $sender on $netname: $text"
PNOTICE: "<$sender> $text"
QUIT: "[$netname] - $sender$sender_identhost has quit ($text)"
SJOIN: "[$netname] - Netjoin gained users: $nicks"
SQUIT: "[$netname] - Netsplit lost users: $nicks"
```
### Misc. options
- `relay::clientbot_startup_delay`: Defines the amount of seconds Clientbot should wait after startup, before relaying any non-PRIVMSG events. This is used to prevent excess floods when the bot connects. Defaults to 5 seconds.
- `servers::NETNAME::relay_force_slashes`: This network specific option forces Relay to use `/` in nickname separators. You should only use this option on TS6 or P10 variants that are less strict with nickname validation, as **it will cause protocol violations** on most IRCds. UnrealIRCd and InspIRCd users do not need to set this either, as `/` in nicks is automatically enabled.
- `servers::NETNAME::relay_endburst_delay`: InspIRCd networks only: sets the endburst delay for relay subservers. If relay server bursts are causing +j (join flood) protection to trigger, raising this value can work around the issue.

View File

@ -0,0 +1,61 @@
# Advanced Services Configuration
There are some service configuration options that you may want to be aware of.
#### Nick / Ident
You can override the `nick` or `ident` of a service bot using a directive liek this:
```yaml
servers:
somenet:
# ...
SERVICE_nick: OTHERNICK
SERVICE_ident: OTHERIDENT
```
You can also set an arbitrary nick/ident using a per-**service** directive.
```yaml
SERVICE:
nick: OTHERNICK
ident: OTHERIDENT
```
#### joinmodes
By default, service bots join channels without giving themselves any modes. You can configure what modes a service bot joins channels with using this directive:
```yaml
SERVICE:
joinmodes: 'o'
```
This would request the mode 'o' (op on most IRCds) when joining the channel.
Technically any mode can be put here, but if an IRCd in question doesn't support
the mode then it will be ignored.
You can also use combinations of modes, such as 'ao' (usually admin/protect + op)
```yaml
SERVICE:
joinmodes: 'ao'
```
Combinations should work provided an IRCd in question supports it.
#### Fantasy prefix
You can also set the service bot's fantasy prefix; of course this is only
applicable if the `fantasy` plugin is loaded.
The setting allows for one or more characters to be set as the prefix.
```yaml
SERVICE:
prefix: './'
```
The above is perfectly valid, as is any other string.

37
docs/automode.md Normal file
View File

@ -0,0 +1,37 @@
# Automode Tutorial
The Automode plugin was introduced in PyLink 0.9 as a simple mechanism to manage channel access. That said, it is not designed to entirely replace traditional IRC services such as ChanServ.
## Starting steps
Upon loading the `automode` plugin, you should see an Automode service bot connect, using the name that you defined (this guide uses the default, `Automode`). This service provides the commands used to manage access.
For a list of commands:
- `/msg Automode help`
Adding access to a channel:
- `/msg Automode setacc #channel [MASK] [MODE LIST]`
- The mask can be a simple `nick!user@host` hostmask or any of the extended targets (exttargets) mentioned below. MODE LIST is a string of any prefix modes that you want to set (no leading `+` needed): e.g. `qo`, `h`, or `ov`.
Removing access from a channel:
- `/msg Automode delacc #channel [MASK]`
Listing access entries on a channel:
- `/msg Automode listacc #channel`
Applying all access entries on a channel (sync):
- `/msg Automode syncacc #channel`
Clearing all access entries on a channel:
- `/msg Automode clearacc #channel`
## Supported masks and extended targets
Automode supports any hostmask or PyLink extended target; see the [Exttargets Guide](exttargets.md) for more details.
## Permissions
See the [Permissions Reference](permissions-reference.md#automode) for a list of permissions defined by Automode.
## Caveats
- Service bot joining and Relay don't always behave consistently: see https://github.com/jlu5/PyLink/issues/265

72
docs/exttargets.md Normal file
View File

@ -0,0 +1,72 @@
# Exttargets Guide
**Extended targets** or **exttargets** extend regular hostmask matching by checking users against specific conditions. PyLink exttargets are supported by most plugins in the place of `nick!user@host` masks (provided they use `IRCNetwork.match_host()` as their backend).
Exttargets were introduced in PyLink 0.9 alongside [Automode](automode.md), with the goal of making user/ACL matching more versatile. As of PyLink 2.0, the following exttargets are supported:
### The "$account" target (PyLink 0.9+)
Used to match users by their services account.
- `$account` -> Returns True (a match) if the target is registered.
- `$account:accountname` -> Returns True if the target's account name matches the one given, and the target is connected to the local network. Account names are case insensitive.
- `$account:accountname:netname` -> Returns True if both the target's account name and origin network name match the ones given. Account names are case insensitive, but network names ARE case sensitive.
- `$account:*:netname` -> Matches all logged in users on the given network. Globs are not supported here; only a literal `*`.
### The "$channel" target (PyLink 0.9+)
Used to match users in certain channels. Channel names are matched case insensitively.
- `$channel:#channel` -> Returns True if the target is in the given channel.
- `$channel:#channel:PREFIXMODE` -> Returns True if the target is in the given channel, and is opped. Any supported prefix mode (owner, admin, op, halfop, voice) can be used for the last part, but only one at a time.
### The "$ircop" target (PyLink 0.9+)
Used to match users by IRCop status.
- `$ircop` -> Returns True (a match) if the target is opered.
- `$ircop:*admin*` -> Returns True if the target's is opered and their oper type matches the glob given (case insensitive).
### Target inversion (PyLink 1.2+)
In PyLink 1.2 and above, all targets and hostmasks can be inverted by placing a `!` before the target:
- `!$account` -> Matches all users not registered with services.
- `!$ircop` -> Matches all non-opers.
- `!*!*@localhost` -> Matches all users not connecting from localhost.
- `!*!*@*:*` -> Matches all non-IPv6 users.
For users on PyLink version **before 1.2**, target inversion is *only* supported with exttargets (i.e. `!$account` will work, but not `!*!*@localhost`.
### The "$network" target (PyLink 1.2+)
Used to match users on specific networks.
- `$network:netname` -> Returns True if the target user originates from the given network (this supports and looks up the home network of Relay users).
### The "$and" target (PyLink 1.2+)
The `$and` target is slightly more complex, and involves chaining together multiple exttargets or hosts with a `+` between each. Note that parentheses are required around the list of targets to match.
- `$and:($ircop:*admin*+$network:ovd)` -> Matches all opers on the network ovd.
- `$and:($account+$pylinkirc)` -> Matches all users logged in to both services and PyLink.
- `$and:(*!*@localhost+$ircop)` -> Matches all opers with the host `localhost`.
- `$and:(*!*@*.mibbit.com+!$ircop+!$account)` -> Matches all (non-CGIIRC) Mibbit users that aren't opered or logged in to services.
### The "$server" target (PyLink 0.9+)
Used to match users on specific IRC servers.
- `$server:server.name` -> Returns True (a match) if the target is connected on the given server. Server names are matched case insensitively.
- `$server:*server.glob*` -> Returns True (a match) if the target is connected on a server matching the glob.
- `$server:1XY` -> Returns True if the target's is connected on the server with the given SID. Note: SIDs ARE case sensitive.
### The "$pylinkacc" target (PyLink 0.9+)
Used to match users logged in to *PyLink* (i.e. via the `identify` command). **As of PyLink 2.0, The "$pylinkirc:" prefix is implied if you specify a PyLink account name without it.**
- `$pylinkacc` -> Returns True if the target is logged in to PyLink.
- `$pylinkacc:accountname` -> Returns True if the target's PyLink login matches the one given (case insensitive).
### The "$realname" target (PyLink 2.0+)
Used to match users with certain realnames.
- `$realname:*James*`: matches anyone with "James" in their real name (case insensitive).
### The "$service" target (PyLink 2.0+)
Used to match service bots. This exttarget takes one optional argument: a glob, which is compared case-insensitively to the target user's service name if present.
- `$service`: matches any PyLink service bot.
- `$service:automode`: matches the Automode service bot.

144
docs/faq.md Normal file
View File

@ -0,0 +1,144 @@
# PyLink FAQ
## Startup errors
### I get errors like "ImportError: No module named 'yaml'" when I start PyLink
You are missing dependencies - re-read https://github.com/jlu5/PyLink/blob/master/README.md#installation
### I get errors like "yaml.scanner.ScannerError: while scanning for the next token, found character '\t' that cannot start any token"
You must use **spaces** and not tabs to indent your configuration file! (`\t` is the escaped code for a tab, which is not allowed in YAML)
### I get errors like "ParserError: while parsing a block mapping ... expected &lt;block end&gt;, but found '&lt;block sequence start&gt;'
This likely indicates an indentation issue. When you create a list in YAML (PyLink's config format), all entries must be indented consistently. For example, this is **bad**:
```yaml
# This will cause an error!
someblock:
- abcd
- def
- ghi
```
This is good:
```yaml
someblock:
- abcd
- def
- ghi
```
### I keep getting YAML / syntax errors trying to set up my instance!
Take a few minutes to familiarize yourself with YAML, the markup language we use for the config file.
[CraftIRC](https://github.com/Animosity/CraftIRC/wiki/Complete-idiot%27s-introduction-to-yaml), [Ansible](https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html), and [Wikipedia](https://en.wikipedia.org/wiki/YAML) all provide excellent guides (with examples) on its basic structure.
A common misconception is that the YAML format is something specific to Python or PyLink, but this is not the case! YAML is a programming language-independent standard which *happens* to use indents for structures like Python does, but [parsers for it exist just about everywhere](http://yaml.org/).
The reason I (James) chose it for this project is essentially a restatement of its common benefits:
- It's compact and human readable (compared to raw JSON or XML)
- It's powerful, supporting everything from nested config blocks to multi-line strings
- It fits well in the Python landscape, compared to a flat .ini or C-style config format
- It's language independent, which means it's not a giant pain if we decide to rewrite the project in some other language one day... 🙃
## Linking / Connection issues
### PyLink won't connect to my network!
As a general guide, you should check the following before asking for help:
- Is the target network's IRCd showing failed connection attempts?
- If not:
1) Is PyLink connecting to the right port (i.e. one the IRCd is listening on?)
2) Is the target network's IRCd actually binding to the port you're trying to use? If there is a port conflict with another program, the IRCd may fail to bind to specific ports but *still start* on others which are free.
3) Is the target port firewalled on the target machine?
4) Is there a working connection between the source and target servers? Use ping to test this, as routing issues between providers can cause servers to become unreachable.
- If your servers purposely block ping, you're going to have to figure this one out yourself... 😬
- If so:
1) Check for recvpass/sendpass/server hostname/IP mismatches - usually the IRCd will tell you if you're running into one of these, provided you have the right server notices enabled (consult your IRCd documentation for how to enable these).
2) Make sure you're not connecting with SSL on a non-SSL port, or vice versa.
If these steps haven't helped you so far, maybe you've found a bug...?
### My networks keep disconnecting with SSL errors!
See https://github.com/jlu5/PyLink/issues/463 - the problem appears to be caused somewhere in Python's SSL stack and/or OpenSSL, and not directly by our code.
Unfortunately, the only workarounds so far are to either disable SSL/TLS, or wrap a plain IRC connection in an external service (stunnel, OpenVPN, etc.)
### I turned autoconnect for PyLink on, and now I'm getting errors!
PyLink does not support inbound connections - much like regular services such as Atheme or Anope, it only connects outwards *to* IRCds. (If you don't understand what this means, it means you should turn autoconnect **off** for PyLink)
## Relay issues
### Does everyone need to install PyLink Relay for it to work?
**No!** Only the PyLink administrator needs to host a PyLink instance with the `relay` plugin loaded, as each instance can connect to multiple networks. Everyone else only needs to add a link block on their IRCd.
InterJanus-style links between PyLink daemons are not supported yet; see https://github.com/jlu5/PyLink/issues/99 for any progress regarding that.
### What are PyLink's advantages over Janus?
PyLink provides, in no particular order:
- More complete support for modern IRCds (UnrealIRCd 4.x, InspIRCd 2.0, charybdis 4, Nefarious IRCu, etc.).
- A flexible, maintainable codebase extensible beyond Relay.
- Proper protocol negotiation leading to fewer SQUIT/DoS possibilities:
- Better support for channel modes such as +fjMOR, etc.
- Proper support for nick length limits with relayed users.
### My IRCd SQUITs the Relay server with errors like "Bad nickname introduced"!
First, check whether the SQUIT message includes the nick that triggered the netsplit. If this nick includes any characters not allowed in regular IRC, such as the slash ("/"), or is otherwise an invalid nick (e.g. beginning with a hyphen or number), this likely indicates a bug in PyLink Relay. These problems should be reported on the issue tracker.
However, if the nick mentioned is legal on IRC, this issue is likely caused by a max nick length misconfiguration: i.e. the Relay server is introducing nicks too long for the target network. This can be fixed by setting the `maxnicklen` option in the affected network's PyLink `server:` block to the same value as that network's `005` `NICKLEN` (that is, the `NICKLEN=<num>` value in `/raw version`).
### Clientbot doesn't relay both ways!
Load the `relay_clientbot` plugin. https://github.com/jlu5/PyLink/blob/1.3-beta1/example-conf.yml#L465-L468
### How do I turn off colors in Clientbot?
See https://github.com/jlu5/PyLink/blob/master/docs/advanced-relay-config.md#custom-clientbot-styles, especially the section "Disabling Colors/Control Codes".
### Relay is occasionally dropping users from channels!
This usually indicates a serious bug in either Relay or PyLink's protocol modules, and should be reported as an issue. When asking for help, please state which IRCds your PyLink instance is linking to: specifically, which IRCd the missing users are *from* and which IRCd the users are missing *on*. Also, be prepared to send debug logs as you reproduce the issue!
- Another tip in debugging this is to run `showchan` on the affected channels. If PyLink shows users in `showchan` that aren't in the actual user list, this is most likely a protocol module issue. If `showchan`'s output is correct, it is instead probably a Relay issue where users aren't spawning correctly.
### Does Relay support mode +R, +M, etc.? How does Relay handle modes supported on one IRCd but not on another?
Essentially, PyLink maps IRCd modes together by name, so modes that use different characters on different IRCds can be recognized as the same "mode". Tables of supported channel modes, user modes, and extbans (in 2.0+) can be found at https://github.com/jlu5/PyLink/tree/devel/docs/modelists. Note that third party/contrib modules implementing modes are generally *not* tested / supported.
Relay in particular uses whitelists to determine which modes are safe to relay: for 2.0.0, this is https://github.com/jlu5/PyLink/blob/71a24b8/plugins/relay.py#L903-L969. **Most *channel* modes recognized by PyLink are whitelisted and usable with Relay**, with the following exceptions:
- "registered" channel / user modes (InspIRCd, UnrealIRCd **+r**) - this is to prevent conflicts with local networks's services.
- "permanent" channel modes (commonly **+P**) - it's not necessary for remote networks' channels to also be permanent locally.
- Flood protection modes are only relayed between networks running the same IRCd (UnrealIRCd <-> UnrealIRCd or InspIRCd <-> InspIRCd).
- Modes and extbans that specify a forwarding channel - mangling channel names between networks is far too complicated and desync prone.
- InspIRCd's m_ojoin **+Y** and m_operprefix **+y** are ignored by Relay.
- auditorium (InspIRCd **+u**), delayjoin (UnrealIRCd, P10, InspIRCd **+D**), and any other modes affecting join visibilites are not supported.
Support for user modes is not as complete:
- Filter type modes such as callerid (**+g**), regonly (**+R**), noctcp (UnrealIRCd **+T**) are *not yet* supported by Relay.
- Service protection modes (UnrealIRCD **+S**, InspIRCd **+k**, etc.) are not forwarded by Relay to prevent abuse.
### How does Relay handle kills?
See https://github.com/jlu5/PyLink/blob/devel/docs/relay-quickstart.md#kill-handling
### How does Relay handle KLINE/GLINE/ZLINE?
It doesn't. https://github.com/jlu5/PyLink/issues/521#issuecomment-352316396 explains my reasons for skipping over this:
* The weakest link, whether this be a malicious/compromised/mistaken oper or a misconfigured services instance, can easily wreak havoc by banning something they shouldn't.
* KLINE relaying goes against the concept of partial network links and creates serious animosity when opers disagree on policy. If KLINEs are shared, opers are essentially shared as well, and this is not the goal of Relay.
## Services issues
### Service bots aren't spawning on my network, even though PyLink connects
This indicates either a bug in PyLink's protocol module or (less commonly) a bug in your IRCd. Hint: ENDBURST is likely not being sent or received properly, which causes service bot spawning to never trigger.
Make sure you're using an [officially supported IRCd](https://github.com/jlu5/PyLink#supported-ircds) before requesting help, as custom IRCd code can potentially trigger S2S bugs and is not something we can support.

5
docs/modelists/README.md Normal file
View File

@ -0,0 +1,5 @@
This folder contains tables of named modes defined by PyLink modules. The following are HTML versions of the raw .csv data:
- [Supported named channel modes](https://raw.githack.com/jlu5/PyLink/devel/docs/modelists/channel-modes.html)
- [Supported named user modes](https://raw.githack.com/jlu5/PyLink/devel/docs/modelists/user-modes.html)
- [Supported extbans](https://raw.githack.com/jlu5/PyLink/devel/docs/modelists/extbans.html)

View File

@ -0,0 +1,71 @@
Channel Mode / IRCd,rfc1459,hybrid,inspircd/insp20,inspircd/insp3,ngircd,p10/ircu,p10/nefarious,p10/snircd,ts6/charybdis,ts6/chatircd,ts6/elemental,ts6/ratbox,unreal
admin,,,"a (m_customprefix, m_chanprotect)",a (customprefix),a,,,,,a (when enabled),a (when enabled),,a
adminonly,,,,,,,a,,A (ext/chm_adminonly),A (ext/chm_adminonly),A (ext/chm_adminonly.so),,
allowinvite,,,A (m_allowinvite),A (allowinvite),,,,,g,g,g,,
auditorium,,,u (m_auditorium),u (auditorium),,,,,,,,,
autoop,,,w (m_autoop),w (autoop),,,,,,,,,
ban,b,b,b,b,b,b,b,b,b,b,b,b,b
banexception,,e,e (m_banexception),e (banexception),e,,e,,e,e,e,e,e
blockcaps,,,B (m_blockcaps),"B (anticaps, blockcaps)",,,,,,,G (ext/chm_nocaps.so),,
blockcolor,,c,c (m_blockcolor),c (blockcolor),,c,c,c,,,,,c (chanmodes/nocolor)
blockhighlight,,,V (contrib/m_blockhighlight),V (contrib/blockhighlight),,,,,,,,,
censor,,,G (m_censor),G (censor),,,,,,,,,G (chanmodes/censor)
delayjoin,,,D (m_delayjoin),D (delayjoin),,D,D,D,,,,,D (chanmodes/delayjoin)
delaymsg,,,d (m_delaymsg),d (delaymsg),,,,,,,,,
exemptchanops,,,X (m_exemptchanops),X (exemptchanops),,,,,,,,,
filter,,,g (m_filter),g (filter),,,,,,,,,(via extban ~T:block:)
flood,,,f (m_messageflood),f (messageflood),,,,,,,,,
flood_unreal,,,,,,,,,,,,,f (chanmodes/floodprot)
freetarget,,,,,,,,,F,F,F,,
had_delayjoin,,,,,,d,d,d,,,,,
halfop,,h,"h (m_customprefix, m_halfop)",h (customprefix),h,,,,,h (when enabled),h (when enabled),,h
hiddenbans,,,,,,,,,,,u,,
hidequits,,,,,,,Q,u,,,,,
history,,,H (m_chanhistory),H (chanhistory),,,,,,,,,
invex,,I,I (m_inviteexception),I (inviteexception),I,,,,I,I,I,I,I
inviteonly,i,i,i,i,i,i,i,i,i,i,i,i,i
issecure,,,,,,,,,,,,,Z (chanmodes/issecure)
joinflood,,,j (m_joinflood),j (joinflood),,,,,j,j,j,,
key,k,k,k,k,k,k,k,k,k,k,k,k,k
kicknorejoin,,,,,,,,,,,J,,
kicknorejoin_insp,,,J (m_kicknorejoin),J (kicknorejoin),,,,,,,,,
largebanlist,,,,,,,,,L,L,L,,
limit,l,l,l,l,l,l,l,l,l,l,l,l,l
moderated,m,m,m,m,m,m,m,m,m,m,m,m,m
netadminonly,,,,,,,,,,N (ext/chm_netadminonly),,,
nickflood,,,F (m_nickflood),F (nickflood),,,,,,,,,
noamsg,,,,,,,T,T,,,,,
noctcp,,C,C (m_noctcp),C (noctcp),,C,C,C,C,C,C,,C (chanmodes/noctcp)
noextmsg,n,n,n,n,n,n,n,n,n,n,n,n,n
noforwards,,,,,,,,,Q,Q,Q,,
noinvite,,,,,V,,,,,,,,V (chanmodes/noinvite)
nokick,,,Q (m_nokicks),Q (nokicks),Q,,,,,,E,,Q (chanmodes/nokick)
noknock,,p*,K (m_knock),K (knock),,,,,p*,p*,p*,p*,K (chanmodes/noknock)
nonick,,,N (m_nonicks),N (nonicks),N,,,,,,d,,N (chanmodes/nonickchange)
nonotice,,,T (m_nonotice),T (nonotice),,,N,N,T (ext/chm_nonotice),T (ext/chm_nonotice),T,,T (chanmodes/nonotice)
official-join,,,Y (m_ojoin),Y (ojoin),,,,,,,,,
op,o,o,o,o,o,o,o,o,o,o,o,o,o
operonly,,O,O (m_operchans),O (operchans),O,,O,,O (ext/chm_operonly),O (ext/chm_operonly),O (ext/chm_operonly.so),,O (chanmodes/operonly)
oplevel_apass,,,,,,A,A,A,,,,,
oplevel_upass,,,,,,U,U,U,,,,,
opmoderated,,,U (contrib/m_opmoderated),,,,,,z,z,z,,
owner,,,"q (m_customprefix, m_chanprotect)",q (customprefix),q,,,,,y (when enabled),y (when enabled),,q
paranoia,,p*,,,,,,,,,,,
permanent,,,P (m_permchannels),P (permchannels),P,,z,,P,P,P,,P (chanmodes/permanent)
private,p,p*,p,p,p,p,p,p,p*,p*,p*,p*,p
quiet,,,(via extban m:),(via extban m:),,,(via extban ~q:),,q,q,q,,(via extban ~q:)
redirect,,,L (m_redirect),L (redirect),,,L,,f,f,f,,L (chanmodes/link)
registered,,r,r (m_services_account),r (services_account),r,R,R,R,,,,,r
regmoderated,,M,M (m_services_account),M (services_account),M,,M,M,,,,,M (chanmodes/regonlyspeak)
regonly,,R,R (m_services_account),R (services_account),R,r,r,r,r,r,r,r,R (chanmodes/regonly)
repeat,,,,,,,,,,,K (ext/chm_norepeat.c),,
repeat_insp,,,,E (repeat),,,,,,,,,
secret,s,s,s,s,s,s,s,s,s,s,s,s,s
sslonly,,S,z (m_sslmodes),z (sslmodes),z,,,,S (ext/chm_sslonly),S (ext/chm_sslonly),S (ext/chm_sslonly.c),S,z (chanmodes/secureonly)
stripcolor,,,S (m_stripcolor),S (stripcolor),,,S,,c,c,c,,S (chanmodes/stripcolor)
topiclock,t,t,t,t,t,t,t,t,t,t,t,t,t
voice,v,v,v,v,v,v,v,v,v,v,v,v,v
,,,,,,,,,,,,,
----,,,,,,,,,,,,,
<b>Note</b>: Channel modes for InspIRCd and UnrealIRCd are automatically negotiated on connect; this may not be a complete list.,,,,,,,,,,,,,
"* Mode +p corresponds to both “noknock” and “private” on TS6 IRCds, as well as “paranoia” on hybrid.",,,,,,,,,,,,,
1 Channel Mode / IRCd rfc1459 hybrid inspircd/insp20 inspircd/insp3 ngircd p10/ircu p10/nefarious p10/snircd ts6/charybdis ts6/chatircd ts6/elemental ts6/ratbox unreal
2 admin a (m_customprefix, m_chanprotect) a (customprefix) a a (when enabled) a (when enabled) a
3 adminonly a A (ext/chm_adminonly) A (ext/chm_adminonly) A (ext/chm_adminonly.so)
4 allowinvite A (m_allowinvite) A (allowinvite) g g g
5 auditorium u (m_auditorium) u (auditorium)
6 autoop w (m_autoop) w (autoop)
7 ban b b b b b b b b b b b b b
8 banexception e e (m_banexception) e (banexception) e e e e e e e
9 blockcaps B (m_blockcaps) B (anticaps, blockcaps) G (ext/chm_nocaps.so)
10 blockcolor c c (m_blockcolor) c (blockcolor) c c c c (chanmodes/nocolor)
11 blockhighlight V (contrib/m_blockhighlight) V (contrib/blockhighlight)
12 censor G (m_censor) G (censor) G (chanmodes/censor)
13 delayjoin D (m_delayjoin) D (delayjoin) D D D D (chanmodes/delayjoin)
14 delaymsg d (m_delaymsg) d (delaymsg)
15 exemptchanops X (m_exemptchanops) X (exemptchanops)
16 filter g (m_filter) g (filter) (via extban ~T:block:)
17 flood f (m_messageflood) f (messageflood)
18 flood_unreal f (chanmodes/floodprot)
19 freetarget F F F
20 had_delayjoin d d d
21 halfop h h (m_customprefix, m_halfop) h (customprefix) h h (when enabled) h (when enabled) h
22 hiddenbans u
23 hidequits Q u
24 history H (m_chanhistory) H (chanhistory)
25 invex I I (m_inviteexception) I (inviteexception) I I I I I I
26 inviteonly i i i i i i i i i i i i i
27 issecure Z (chanmodes/issecure)
28 joinflood j (m_joinflood) j (joinflood) j j j
29 key k k k k k k k k k k k k k
30 kicknorejoin J
31 kicknorejoin_insp J (m_kicknorejoin) J (kicknorejoin)
32 largebanlist L L L
33 limit l l l l l l l l l l l l l
34 moderated m m m m m m m m m m m m m
35 netadminonly N (ext/chm_netadminonly)
36 nickflood F (m_nickflood) F (nickflood)
37 noamsg T T
38 noctcp C C (m_noctcp) C (noctcp) C C C C C C C (chanmodes/noctcp)
39 noextmsg n n n n n n n n n n n n n
40 noforwards Q Q Q
41 noinvite V V (chanmodes/noinvite)
42 nokick Q (m_nokicks) Q (nokicks) Q E Q (chanmodes/nokick)
43 noknock p* K (m_knock) K (knock) p* p* p* p* K (chanmodes/noknock)
44 nonick N (m_nonicks) N (nonicks) N d N (chanmodes/nonickchange)
45 nonotice T (m_nonotice) T (nonotice) N N T (ext/chm_nonotice) T (ext/chm_nonotice) T T (chanmodes/nonotice)
46 official-join Y (m_ojoin) Y (ojoin)
47 op o o o o o o o o o o o o o
48 operonly O O (m_operchans) O (operchans) O O O (ext/chm_operonly) O (ext/chm_operonly) O (ext/chm_operonly.so) O (chanmodes/operonly)
49 oplevel_apass A A A
50 oplevel_upass U U U
51 opmoderated U (contrib/m_opmoderated) z z z
52 owner q (m_customprefix, m_chanprotect) q (customprefix) q y (when enabled) y (when enabled) q
53 paranoia p*
54 permanent P (m_permchannels) P (permchannels) P z P P P P (chanmodes/permanent)
55 private p p* p p p p p p p* p* p* p* p
56 quiet (via extban m:) (via extban m:) (via extban ~q:) q q q (via extban ~q:)
57 redirect L (m_redirect) L (redirect) L f f f L (chanmodes/link)
58 registered r r (m_services_account) r (services_account) r R R R r
59 regmoderated M M (m_services_account) M (services_account) M M M M (chanmodes/regonlyspeak)
60 regonly R R (m_services_account) R (services_account) R r r r r r r r R (chanmodes/regonly)
61 repeat K (ext/chm_norepeat.c)
62 repeat_insp E (repeat)
63 secret s s s s s s s s s s s s s
64 sslonly S z (m_sslmodes) z (sslmodes) z S (ext/chm_sslonly) S (ext/chm_sslonly) S (ext/chm_sslonly.c) S z (chanmodes/secureonly)
65 stripcolor S (m_stripcolor) S (stripcolor) S c c c S (chanmodes/stripcolor)
66 topiclock t t t t t t t t t t t t t
67 voice v v v v v v v v v v v v v
68
69 ----
70 <b>Note</b>: Channel modes for InspIRCd and UnrealIRCd are automatically negotiated on connect; this may not be a complete list.
71 * Mode +p corresponds to both “noknock” and “private” on TS6 IRCds, as well as “paranoia” on hybrid.

View File

@ -0,0 +1,281 @@
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<meta name=viewport content="width=device-width, initial-scale=1">
<head>
<title>Supported Channel Modes for PyLink</title>
<style>
html {
background-color: white;
}
.note {
color: #555555;
}
/* (╮°-°)╮┳━┳ */
table, th, td {
border: 1px solid black;
}
td, th {
text-align: center;
padding: 3px;
}
td:first-child, th[scope="row"] {
text-align: left;
}
/* Table cells */
.tablecell-yes {
background-color: #A7F2A5
}
.tablecell-no {
background-color: #F08496
}
.tablecell-na {
background-color: #F0F0F0
}
.tablecell-planned, .tablecell-yes2 {
background-color: #B1FCDE
}
.tablecell-partial {
background-color: #EDE8A4
}
.tablecell-special {
background-color: #DCB1FC
}
</style>
</head>
<body>
<table><tr>
<th scope="col">Channel Mode / IRCd</th>
<th scope="col">rfc1459</th>
<th scope="col">hybrid</th>
<th scope="col">inspircd/insp20</th>
<th scope="col">inspircd/insp3</th>
<th scope="col">ngircd</th>
<th scope="col">p10/ircu</th>
<th scope="col">p10/nefarious</th>
<th scope="col">p10/snircd</th>
<th scope="col">ts6/charybdis</th>
<th scope="col">ts6/chatircd</th>
<th scope="col">ts6/elemental</th>
<th scope="col">ts6/ratbox</th>
<th scope="col">unreal</th>
</tr>
<tr>
<th scope="row">admin</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+a<br><span class="note">(m_customprefix, m_chanprotect)</span></td><td class="tablecell-yes2">+a<br><span class="note">(customprefix)</span></td><td class="tablecell-yes">+a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+a<br><span class="note">(when enabled)</span></td><td class="tablecell-yes2">+a<br><span class="note">(when enabled)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+a</td></tr>
<tr>
<th scope="row">adminonly</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+A<br><span class="note">(ext/chm_adminonly)</span></td><td class="tablecell-yes2">+A<br><span class="note">(ext/chm_adminonly)</span></td><td class="tablecell-yes2">+A<br><span class="note">(ext/chm_adminonly.so)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">allowinvite</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+A<br><span class="note">(m_allowinvite)</span></td><td class="tablecell-yes2">+A<br><span class="note">(allowinvite)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+g</td><td class="tablecell-yes">+g</td><td class="tablecell-yes">+g</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">auditorium</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+u<br><span class="note">(m_auditorium)</span></td><td class="tablecell-yes2">+u<br><span class="note">(auditorium)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">autoop</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+w<br><span class="note">(m_autoop)</span></td><td class="tablecell-yes2">+w<br><span class="note">(autoop)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban</th>
<td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td><td class="tablecell-yes">+b</td></tr>
<tr>
<th scope="row">banexception</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+e</td><td class="tablecell-yes2">+e<br><span class="note">(m_banexception)</span></td><td class="tablecell-yes2">+e<br><span class="note">(banexception)</span></td><td class="tablecell-yes">+e</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+e</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+e</td><td class="tablecell-yes">+e</td><td class="tablecell-yes">+e</td><td class="tablecell-yes">+e</td><td class="tablecell-yes">+e</td></tr>
<tr>
<th scope="row">blockcaps</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+B<br><span class="note">(m_blockcaps)</span></td><td class="tablecell-yes2">+B<br><span class="note">(anticaps, blockcaps)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+G<br><span class="note">(ext/chm_nocaps.so)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">blockcolor</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+c</td><td class="tablecell-yes2">+c<br><span class="note">(m_blockcolor)</span></td><td class="tablecell-yes2">+c<br><span class="note">(blockcolor)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+c</td><td class="tablecell-yes">+c</td><td class="tablecell-yes">+c</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+c<br><span class="note">(chanmodes/nocolor)</span></td></tr>
<tr>
<th scope="row">blockhighlight</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+V<br><span class="note">(contrib/m_blockhighlight)</span></td><td class="tablecell-yes2">+V<br><span class="note">(contrib/blockhighlight)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">censor</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+G<br><span class="note">(m_censor)</span></td><td class="tablecell-yes2">+G<br><span class="note">(censor)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+G<br><span class="note">(chanmodes/censor)</span></td></tr>
<tr>
<th scope="row">delayjoin</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+D<br><span class="note">(m_delayjoin)</span></td><td class="tablecell-yes2">+D<br><span class="note">(delayjoin)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+D</td><td class="tablecell-yes">+D</td><td class="tablecell-yes">+D</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+D<br><span class="note">(chanmodes/delayjoin)</span></td></tr>
<tr>
<th scope="row">delaymsg</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+d<br><span class="note">(m_delaymsg)</span></td><td class="tablecell-yes2">+d<br><span class="note">(delaymsg)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">exemptchanops</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+X<br><span class="note">(m_exemptchanops)</span></td><td class="tablecell-yes2">+X<br><span class="note">(exemptchanops)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">filter</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+g<br><span class="note">(m_filter)</span></td><td class="tablecell-yes2">+g<br><span class="note">(filter)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-partial">(via extban ~T:block:)</td></tr>
<tr>
<th scope="row">flood</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+f<br><span class="note">(m_messageflood)</span></td><td class="tablecell-yes2">+f<br><span class="note">(messageflood)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">flood_unreal</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+f<br><span class="note">(chanmodes/floodprot)</span></td></tr>
<tr>
<th scope="row">freetarget</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+F</td><td class="tablecell-yes">+F</td><td class="tablecell-yes">+F</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">had_delayjoin</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+d</td><td class="tablecell-yes">+d</td><td class="tablecell-yes">+d</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">halfop</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+h</td><td class="tablecell-yes2">+h<br><span class="note">(m_customprefix, m_halfop)</span></td><td class="tablecell-yes2">+h<br><span class="note">(customprefix)</span></td><td class="tablecell-yes">+h</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+h<br><span class="note">(when enabled)</span></td><td class="tablecell-yes2">+h<br><span class="note">(when enabled)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+h</td></tr>
<tr>
<th scope="row">hiddenbans</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+u</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">hidequits</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+Q</td><td class="tablecell-yes">+u</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">history</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+H<br><span class="note">(m_chanhistory)</span></td><td class="tablecell-yes2">+H<br><span class="note">(chanhistory)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">invex</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+I</td><td class="tablecell-yes2">+I<br><span class="note">(m_inviteexception)</span></td><td class="tablecell-yes2">+I<br><span class="note">(inviteexception)</span></td><td class="tablecell-yes">+I</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+I</td><td class="tablecell-yes">+I</td><td class="tablecell-yes">+I</td><td class="tablecell-yes">+I</td><td class="tablecell-yes">+I</td></tr>
<tr>
<th scope="row">inviteonly</th>
<td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td></tr>
<tr>
<th scope="row">issecure</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+Z<br><span class="note">(chanmodes/issecure)</span></td></tr>
<tr>
<th scope="row">joinflood</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+j<br><span class="note">(m_joinflood)</span></td><td class="tablecell-yes2">+j<br><span class="note">(joinflood)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+j</td><td class="tablecell-yes">+j</td><td class="tablecell-yes">+j</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">key</th>
<td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td></tr>
<tr>
<th scope="row">kicknorejoin</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+J</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">kicknorejoin_insp</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+J<br><span class="note">(m_kicknorejoin)</span></td><td class="tablecell-yes2">+J<br><span class="note">(kicknorejoin)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">largebanlist</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+L</td><td class="tablecell-yes">+L</td><td class="tablecell-yes">+L</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">limit</th>
<td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td></tr>
<tr>
<th scope="row">moderated</th>
<td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td><td class="tablecell-yes">+m</td></tr>
<tr>
<th scope="row">netadminonly</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+N<br><span class="note">(ext/chm_netadminonly)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">nickflood</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+F<br><span class="note">(m_nickflood)</span></td><td class="tablecell-yes2">+F<br><span class="note">(nickflood)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">noamsg</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+T</td><td class="tablecell-yes">+T</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">noctcp</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+C</td><td class="tablecell-yes2">+C<br><span class="note">(m_noctcp)</span></td><td class="tablecell-yes2">+C<br><span class="note">(noctcp)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+C</td><td class="tablecell-yes">+C</td><td class="tablecell-yes">+C</td><td class="tablecell-yes">+C</td><td class="tablecell-yes">+C</td><td class="tablecell-yes">+C</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+C<br><span class="note">(chanmodes/noctcp)</span></td></tr>
<tr>
<th scope="row">noextmsg</th>
<td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td></tr>
<tr>
<th scope="row">noforwards</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+Q</td><td class="tablecell-yes">+Q</td><td class="tablecell-yes">+Q</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">noinvite</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+V</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+V<br><span class="note">(chanmodes/noinvite)</span></td></tr>
<tr>
<th scope="row">nokick</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+Q<br><span class="note">(m_nokicks)</span></td><td class="tablecell-yes2">+Q<br><span class="note">(nokicks)</span></td><td class="tablecell-yes">+Q</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+E</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+Q<br><span class="note">(chanmodes/nokick)</span></td></tr>
<tr>
<th scope="row">noknock</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-special">+p*</td><td class="tablecell-yes2">+K<br><span class="note">(m_knock)</span></td><td class="tablecell-yes2">+K<br><span class="note">(knock)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-special">+p*</td><td class="tablecell-special">+p*</td><td class="tablecell-special">+p*</td><td class="tablecell-special">+p*</td><td class="tablecell-yes2">+K<br><span class="note">(chanmodes/noknock)</span></td></tr>
<tr>
<th scope="row">nonick</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+N<br><span class="note">(m_nonicks)</span></td><td class="tablecell-yes2">+N<br><span class="note">(nonicks)</span></td><td class="tablecell-yes">+N</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+d</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+N<br><span class="note">(chanmodes/nonickchange)</span></td></tr>
<tr>
<th scope="row">nonotice</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+T<br><span class="note">(m_nonotice)</span></td><td class="tablecell-yes2">+T<br><span class="note">(nonotice)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+N</td><td class="tablecell-yes">+N</td><td class="tablecell-yes2">+T<br><span class="note">(ext/chm_nonotice)</span></td><td class="tablecell-yes2">+T<br><span class="note">(ext/chm_nonotice)</span></td><td class="tablecell-yes">+T</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+T<br><span class="note">(chanmodes/nonotice)</span></td></tr>
<tr>
<th scope="row">official-join</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+Y<br><span class="note">(m_ojoin)</span></td><td class="tablecell-yes2">+Y<br><span class="note">(ojoin)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">op</th>
<td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td></tr>
<tr>
<th scope="row">operonly</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+O</td><td class="tablecell-yes2">+O<br><span class="note">(m_operchans)</span></td><td class="tablecell-yes2">+O<br><span class="note">(operchans)</span></td><td class="tablecell-yes">+O</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+O</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+O<br><span class="note">(ext/chm_operonly)</span></td><td class="tablecell-yes2">+O<br><span class="note">(ext/chm_operonly)</span></td><td class="tablecell-yes2">+O<br><span class="note">(ext/chm_operonly.so)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+O<br><span class="note">(chanmodes/operonly)</span></td></tr>
<tr>
<th scope="row">oplevel_apass</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+A</td><td class="tablecell-yes">+A</td><td class="tablecell-yes">+A</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">oplevel_upass</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+U</td><td class="tablecell-yes">+U</td><td class="tablecell-yes">+U</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">opmoderated</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+U<br><span class="note">(contrib/m_opmoderated)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+z</td><td class="tablecell-yes">+z</td><td class="tablecell-yes">+z</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">owner</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+q<br><span class="note">(m_customprefix, m_chanprotect)</span></td><td class="tablecell-yes2">+q<br><span class="note">(customprefix)</span></td><td class="tablecell-yes">+q</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+y<br><span class="note">(when enabled)</span></td><td class="tablecell-yes2">+y<br><span class="note">(when enabled)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+q</td></tr>
<tr>
<th scope="row">paranoia</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-special">+p*</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">permanent</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+P<br><span class="note">(m_permchannels)</span></td><td class="tablecell-yes2">+P<br><span class="note">(permchannels)</span></td><td class="tablecell-yes">+P</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+z</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+P</td><td class="tablecell-yes">+P</td><td class="tablecell-yes">+P</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+P<br><span class="note">(chanmodes/permanent)</span></td></tr>
<tr>
<th scope="row">private</th>
<td class="tablecell-yes">+p</td><td class="tablecell-special">+p*</td><td class="tablecell-yes">+p</td><td class="tablecell-yes">+p</td><td class="tablecell-yes">+p</td><td class="tablecell-yes">+p</td><td class="tablecell-yes">+p</td><td class="tablecell-yes">+p</td><td class="tablecell-special">+p*</td><td class="tablecell-special">+p*</td><td class="tablecell-special">+p*</td><td class="tablecell-special">+p*</td><td class="tablecell-yes">+p</td></tr>
<tr>
<th scope="row">quiet</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-partial">(via extban m:)</td><td class="tablecell-partial">(via extban m:)</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-partial">(via extban ~q:)</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+q</td><td class="tablecell-yes">+q</td><td class="tablecell-yes">+q</td><td class="tablecell-na note">n/a</td><td class="tablecell-partial">(via extban ~q:)</td></tr>
<tr>
<th scope="row">redirect</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+L<br><span class="note">(m_redirect)</span></td><td class="tablecell-yes2">+L<br><span class="note">(redirect)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+L</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+f</td><td class="tablecell-yes">+f</td><td class="tablecell-yes">+f</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+L<br><span class="note">(chanmodes/link)</span></td></tr>
<tr>
<th scope="row">registered</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+r</td><td class="tablecell-yes2">+r<br><span class="note">(m_services_account)</span></td><td class="tablecell-yes2">+r<br><span class="note">(services_account)</span></td><td class="tablecell-yes">+r</td><td class="tablecell-yes">+R</td><td class="tablecell-yes">+R</td><td class="tablecell-yes">+R</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+r</td></tr>
<tr>
<th scope="row">regmoderated</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+M</td><td class="tablecell-yes2">+M<br><span class="note">(m_services_account)</span></td><td class="tablecell-yes2">+M<br><span class="note">(services_account)</span></td><td class="tablecell-yes">+M</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+M</td><td class="tablecell-yes">+M</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+M<br><span class="note">(chanmodes/regonlyspeak)</span></td></tr>
<tr>
<th scope="row">regonly</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+R</td><td class="tablecell-yes2">+R<br><span class="note">(m_services_account)</span></td><td class="tablecell-yes2">+R<br><span class="note">(services_account)</span></td><td class="tablecell-yes">+R</td><td class="tablecell-yes">+r</td><td class="tablecell-yes">+r</td><td class="tablecell-yes">+r</td><td class="tablecell-yes">+r</td><td class="tablecell-yes">+r</td><td class="tablecell-yes">+r</td><td class="tablecell-yes">+r</td><td class="tablecell-yes2">+R<br><span class="note">(chanmodes/regonly)</span></td></tr>
<tr>
<th scope="row">repeat</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+K<br><span class="note">(ext/chm_norepeat.c)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">repeat_insp</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+E<br><span class="note">(repeat)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">secret</th>
<td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td></tr>
<tr>
<th scope="row">sslonly</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+S</td><td class="tablecell-yes2">+z<br><span class="note">(m_sslmodes)</span></td><td class="tablecell-yes2">+z<br><span class="note">(sslmodes)</span></td><td class="tablecell-yes">+z</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+S<br><span class="note">(ext/chm_sslonly)</span></td><td class="tablecell-yes2">+S<br><span class="note">(ext/chm_sslonly)</span></td><td class="tablecell-yes2">+S<br><span class="note">(ext/chm_sslonly.c)</span></td><td class="tablecell-yes">+S</td><td class="tablecell-yes2">+z<br><span class="note">(chanmodes/secureonly)</span></td></tr>
<tr>
<th scope="row">stripcolor</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+S<br><span class="note">(m_stripcolor)</span></td><td class="tablecell-yes2">+S<br><span class="note">(stripcolor)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+S</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+c</td><td class="tablecell-yes">+c</td><td class="tablecell-yes">+c</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+S<br><span class="note">(chanmodes/stripcolor)</span></td></tr>
<tr>
<th scope="row">topiclock</th>
<td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td><td class="tablecell-yes">+t</td></tr>
<tr>
<th scope="row">voice</th>
<td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td><td class="tablecell-yes">+v</td></tr>
<p><b>Note</b>: Channel modes for InspIRCd and UnrealIRCd are automatically negotiated on connect; this may not be a complete list.</p><p>* Mode +p corresponds to both “noknock” and “private” on TS6 IRCds, as well as “paranoia” on hybrid.</p>
</table>
</body>
</html>

View File

@ -0,0 +1,40 @@
Extban / IRCd,inspircd,p10/nefarious,ts6/charybdis,unreal
ban_account,R:,~a:,$a:,~a:
ban_account_legacy,,,,~R:
ban_all_opers,,,$o,
ban_all_registered,,,$a,
ban_all_ssl,,,$z,
ban_banshare,,~j:,$j:,
ban_blockcaps,B:,,,
ban_blockcolor,c:,,,
ban_certfp,z:,,,~S:
ban_extgecos,,,$x:,
ban_inchannel,j:,~c:,$c:,~c:
ban_invites,A:,,,
ban_mark,,~m:,,
ban_noctcp,C:,,,
ban_nojoins,,,,~j:
ban_nokicks,Q:,,,
ban_nonick,N:,~n:,,~n:
ban_nonotice,T:,,,~m:notice: (+e only)
ban_not_account,,,$~a:,
ban_not_banshare,,,$~j:,
ban_not_extgecos,,,$~x:,
ban_not_inchannel,,,$~c:,
ban_not_opers,,,$~o,
ban_not_realname,,,$~r:,
ban_not_server,,,$~s:,
ban_not_ssl,,,$~z,
ban_opertype,O:,,,~O:
ban_partmsgs,p:,,,
ban_realname,r:,~r:,$r:,~r:
ban_server,s:,,$s:,
ban_stripcolor,S:,,,~m:color: (+e only)
ban_unregistered,,,$~a,
ban_unregistered_mark,,~M:,,
ban_unregistered_matching,U:,,,
msgbypass_external,,,,~m:external:
msgbypass_censor,,,,~m:censor:
msgbypass_moderated,,,,~m:moderated:
quiet,m:,~q:,(via cmode +q),~q:
timedban_unreal,,,,~t:
1 Extban / IRCd inspircd p10/nefarious ts6/charybdis unreal
2 ban_account R: ~a: $a: ~a:
3 ban_account_legacy ~R:
4 ban_all_opers $o
5 ban_all_registered $a
6 ban_all_ssl $z
7 ban_banshare ~j: $j:
8 ban_blockcaps B:
9 ban_blockcolor c:
10 ban_certfp z: ~S:
11 ban_extgecos $x:
12 ban_inchannel j: ~c: $c: ~c:
13 ban_invites A:
14 ban_mark ~m:
15 ban_noctcp C:
16 ban_nojoins ~j:
17 ban_nokicks Q:
18 ban_nonick N: ~n: ~n:
19 ban_nonotice T: ~m:notice: (+e only)
20 ban_not_account $~a:
21 ban_not_banshare $~j:
22 ban_not_extgecos $~x:
23 ban_not_inchannel $~c:
24 ban_not_opers $~o
25 ban_not_realname $~r:
26 ban_not_server $~s:
27 ban_not_ssl $~z
28 ban_opertype O: ~O:
29 ban_partmsgs p:
30 ban_realname r: ~r: $r: ~r:
31 ban_server s: $s:
32 ban_stripcolor S: ~m:color: (+e only)
33 ban_unregistered $~a
34 ban_unregistered_mark ~M:
35 ban_unregistered_matching U:
36 msgbypass_external ~m:external:
37 msgbypass_censor ~m:censor:
38 msgbypass_moderated ~m:moderated:
39 quiet m: ~q: (via cmode +q) ~q:
40 timedban_unreal ~t:

191
docs/modelists/extbans.html Normal file
View File

@ -0,0 +1,191 @@
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<meta name=viewport content="width=device-width, initial-scale=1">
<head>
<title>Supported Extbans for PyLink</title>
<style>
html {
background-color: white;
}
.note {
color: #555555;
}
/* (╮°-°)╮┳━┳ */
table, th, td {
border: 1px solid black;
}
td, th {
text-align: center;
padding: 3px;
}
td:first-child, th[scope="row"] {
text-align: left;
}
/* Table cells */
.tablecell-yes {
background-color: #A7F2A5
}
.tablecell-no {
background-color: #F08496
}
.tablecell-na {
background-color: #F0F0F0
}
.tablecell-planned, .tablecell-yes2 {
background-color: #B1FCDE
}
.tablecell-partial {
background-color: #EDE8A4
}
.tablecell-special {
background-color: #DCB1FC
}
</style>
</head>
<body>
<table><tr>
<th scope="col">Extban / IRCd</th>
<th scope="col">inspircd</th>
<th scope="col">p10/nefarious</th>
<th scope="col">ts6/charybdis</th>
<th scope="col">unreal</th>
</tr>
<tr>
<th scope="row">ban_account</th>
<td class="tablecell-yes">R:</td><td class="tablecell-yes">~a:</td><td class="tablecell-yes">$a:</td><td class="tablecell-yes">~a:</td></tr>
<tr>
<th scope="row">ban_account_legacy</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">~R:</td></tr>
<tr>
<th scope="row">ban_all_opers</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$o</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_all_registered</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_all_ssl</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$z</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_banshare</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">~j:</td><td class="tablecell-yes">$j:</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_blockcaps</th>
<td class="tablecell-yes">B:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_blockcolor</th>
<td class="tablecell-yes">c:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_certfp</th>
<td class="tablecell-yes">z:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">~S:</td></tr>
<tr>
<th scope="row">ban_extgecos</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$x:</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_inchannel</th>
<td class="tablecell-yes">j:</td><td class="tablecell-yes">~c:</td><td class="tablecell-yes">$c:</td><td class="tablecell-yes">~c:</td></tr>
<tr>
<th scope="row">ban_invites</th>
<td class="tablecell-yes">A:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_mark</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">~m:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_noctcp</th>
<td class="tablecell-yes">C:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_nojoins</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">~j:</td></tr>
<tr>
<th scope="row">ban_nokicks</th>
<td class="tablecell-yes">Q:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_nonick</th>
<td class="tablecell-yes">N:</td><td class="tablecell-yes">~n:</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">~n:</td></tr>
<tr>
<th scope="row">ban_nonotice</th>
<td class="tablecell-yes">T:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">~m:notice:<br><span class="note">(+e only)</span></td></tr>
<tr>
<th scope="row">ban_not_account</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$~a:</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_not_banshare</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$~j:</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_not_extgecos</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$~x:</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_not_inchannel</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$~c:</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_not_opers</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$~o</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_not_realname</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$~r:</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_not_server</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$~s:</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_not_ssl</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$~z</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_opertype</th>
<td class="tablecell-yes">O:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">~O:</td></tr>
<tr>
<th scope="row">ban_partmsgs</th>
<td class="tablecell-yes">p:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_realname</th>
<td class="tablecell-yes">r:</td><td class="tablecell-yes">~r:</td><td class="tablecell-yes">$r:</td><td class="tablecell-yes">~r:</td></tr>
<tr>
<th scope="row">ban_server</th>
<td class="tablecell-yes">s:</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$s:</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_stripcolor</th>
<td class="tablecell-yes">S:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">~m:color:<br><span class="note">(+e only)</span></td></tr>
<tr>
<th scope="row">ban_unregistered</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">$~a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_unregistered_mark</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">~M:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">ban_unregistered_matching</th>
<td class="tablecell-yes">U:</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">msgbypass_external</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">~m:external:</td></tr>
<tr>
<th scope="row">msgbypass_censor</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">~m:censor:</td></tr>
<tr>
<th scope="row">msgbypass_moderated</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">~m:moderated:</td></tr>
<tr>
<th scope="row">quiet</th>
<td class="tablecell-yes">m:</td><td class="tablecell-yes">~q:</td><td class="tablecell-partial">(via cmode +q)</td><td class="tablecell-yes">~q:</td></tr>
<tr>
<th scope="row">timedban_unreal</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">~t:</td></tr>
</table>
</body>
</html>

View File

@ -0,0 +1,142 @@
#!/usr/bin/env python3
"""
Generates HTML versions of the mode list .csv definitions.
"""
import csv
import os
import os.path
os.chdir(os.path.dirname(__file__))
FILES = {
'user-modes.csv': 'Supported User Modes for PyLink',
'channel-modes.csv': 'Supported Channel Modes for PyLink',
'extbans.csv': 'Supported Extbans for PyLink',
}
def _write(outf, text):
print(text, end='')
outf.write(text)
def _format(articlename, text):
# More formatting
if text:
if text.startswith('('):
text = '<td class="tablecell-partial">%s</td>' % text
else:
if 'modes' in articlename:
text = '+' + text
try:
text, note = text.split(' ', 1)
except ValueError:
if text.endswith('*'):
text = '<td class="tablecell-special">%s</td>' % text
else:
text = '<td class="tablecell-yes">%s</td>' % text
else:
text = '%s<br><span class="note">%s</span>' % (text, note)
text = '<td class="tablecell-yes2">%s</td>' % text
else:
text = '<td class="tablecell-na note">n/a</td>'
return text
for fname, title in FILES.items():
outfname = os.path.splitext(fname)[0] + '.html'
print('Generating HTML for %s to %s:' % (fname, outfname))
with open(fname) as csvfile:
csvdata = csv.reader(csvfile)
with open(outfname, 'w') as outf:
# CSS in HTML in Python, how lovely...
_write(outf, """
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<meta name=viewport content="width=device-width, initial-scale=1">
<head>
<title>%s</title>
<style>
html {
background-color: white;
}
.note {
color: #555555;
}
/* (°-°) */
table, th, td {
border: 1px solid black;
}
td, th {
text-align: center;
padding: 3px;
}
td:first-child, th[scope="row"] {
text-align: left;
}
/* Table cells */
.tablecell-yes {
background-color: #A7F2A5
}
.tablecell-no {
background-color: #F08496
}
.tablecell-na {
background-color: #F0F0F0
}
.tablecell-planned, .tablecell-yes2 {
background-color: #B1FCDE
}
.tablecell-partial {
background-color: #EDE8A4
}
.tablecell-special {
background-color: #DCB1FC
}
</style>
</head>
<body>
<table>""" % title)
notes = False
for idx, row in enumerate(csvdata):
if not any(row): # Empty row
continue
elif row[0] == '----':
notes = True
continue
if notes:
_write(outf, "<p>%s</p>" % row[0])
continue
_write(outf, "<tr>\n")
for colidx, coltext in enumerate(row):
if idx == 0:
text = '<th scope="col">%s</th>\n' % coltext
elif colidx == 0:
text = '<th scope="row">%s</th>\n' % coltext
else:
text = _format(fname, coltext)
_write(outf, text)
_write(outf, "</tr>\n")
_write(outf, """
</table>
</body>
</html>""")

View File

@ -0,0 +1,55 @@
User Mode / IRCd,RFC 1459,hybrid,inspircd,ngircd,p10/ircu,p10/nefarious,p10/snircd,ts6/charybdis,ts6/chatircd,ts6/elemental,ts6/ratbox,unreal
admin,,a,,,,a,,a,a,a,a,
away,,,,a,,,,,,,,
bot,,,B (botmode),B,,B,,,B,B,,B (usermodes/bot)
callerid,,g,g (callerid),,,,,g,g,g,g,
censor,,,,,,,,,,,,G (usermodes/censor)
cloak,,x,x (cloaking),x,x,x,x,x,x,x,,x
cloak_fakehost,,,,,,f,,,,,,
cloak_hashedhost,,,,,,C,,,,,,
cloak_hashedip,,,,,,c,,,,,,
cloak_sethost,,,,,,h,h,,,,,
deaf,,D,d (deaf),,d,d,d,D,D,D,D,d
deaf_commonchan,,G,c (commonchans),C,,q,,,,,,
debug,,d,,,,,,,,,,
filter,,,,,,,,,,,,G
floodexempt,,,,F,,,,,,,,
helpop,,,h (helpop),,,,,,,,,
hidechans,,p,I (hidechans),I,,n,n,,,I,,p (usermodes/privacy)
hideidle,,q,,,,I,I,,,,,I
hideoper,,H,H (hideoper),,,H,,,,,,H
invisible,i,i,i,i,i,i,i,i,i,i,i,i
locops,,l,,,O,O,O,l,l,l,l,
netadmin,,,,,,,,,N,,,
noctcp,,,,,,,,,,C,,T (usermodes/noctcp)
noforward,,,L (redirect),,,L,,Q,Q,Q,,
noinvite,,,,,,,,,,V,,
oper,o,o,o,o,o,o,o,o,o,o,o,o
operwall,,,,,,,,z,z,z,z,
override,,,,,,X,X,p,p,p,,
privdeaf,,,,b,,D,,,,,,D (usermodes/privdeaf)
protected,,,,,,,,,,,,q (usermodes/nokick)
regdeaf,,R,R (services_account),,,R,R,R,R,R,,R (usermodes/regonlymsg)
registered,,r,r (services_account),R,r,r,r,,,,,r
restricted,,,,r,,,,,,,,
servprotect,,,k (servprotect),q,k,k,k,S,S,S,S,S (usermodes/servicebot)
showwhois,,,W (showwhois),,,W,,,,,,W (usermodes/showwhois)
sno_badclientconnections,,u,,,,,,,,,u,
sno_botfloods,,b,,,,,,,,,b,
sno_clientconnections,,c,,c,,,,,,,c,
sno_debug,,,,,g,g,g,,,,d,
sno_extclientconnections,,,,,,,,,,,C,
sno_fullauthblock,,f,,,,,,,,,f,
sno_nickchange,,n,,,,,,,,,,
sno_rejectedclients,,j,,,,,,,,,r,
sno_remoteclientconnections,,F,,,,,,,,,,
sno_serverconnects,,e,,,,,,,,,x,
sno_skill,,k,,,,,,,,,k,
sno_stats,,y,,,,,,,,,y,
snomask,s,s,s,s,s,s,s,s,s,s,s,s
ssl,,S,,,,z,,,,,,z
sslonlymsg,,,,,,,,,t,,,Z (usermodes/secureonlymsg)
stripcolor,,,S (stripcolor),,,,,,,,,
vhost,,,,,,,,,,,,t
wallops,w,w,w,w,w,w,w,w,w,w,w,w
webirc,,W,,,,,,,,,,
1 User Mode / IRCd RFC 1459 hybrid inspircd ngircd p10/ircu p10/nefarious p10/snircd ts6/charybdis ts6/chatircd ts6/elemental ts6/ratbox unreal
2 admin a a a a a a
3 away a
4 bot B (botmode) B B B B B (usermodes/bot)
5 callerid g g (callerid) g g g g
6 censor G (usermodes/censor)
7 cloak x x (cloaking) x x x x x x x x
8 cloak_fakehost f
9 cloak_hashedhost C
10 cloak_hashedip c
11 cloak_sethost h h
12 deaf D d (deaf) d d d D D D D d
13 deaf_commonchan G c (commonchans) C q
14 debug d
15 filter G
16 floodexempt F
17 helpop h (helpop)
18 hidechans p I (hidechans) I n n I p (usermodes/privacy)
19 hideidle q I I I
20 hideoper H H (hideoper) H H
21 invisible i i i i i i i i i i i i
22 locops l O O O l l l l
23 netadmin N
24 noctcp C T (usermodes/noctcp)
25 noforward L (redirect) L Q Q Q
26 noinvite V
27 oper o o o o o o o o o o o o
28 operwall z z z z
29 override X X p p p
30 privdeaf b D D (usermodes/privdeaf)
31 protected q (usermodes/nokick)
32 regdeaf R R (services_account) R R R R R R (usermodes/regonlymsg)
33 registered r r (services_account) R r r r r
34 restricted r
35 servprotect k (servprotect) q k k k S S S S S (usermodes/servicebot)
36 showwhois W (showwhois) W W (usermodes/showwhois)
37 sno_badclientconnections u u
38 sno_botfloods b b
39 sno_clientconnections c c c
40 sno_debug g g g d
41 sno_extclientconnections C
42 sno_fullauthblock f f
43 sno_nickchange n
44 sno_rejectedclients j r
45 sno_remoteclientconnections F
46 sno_serverconnects e x
47 sno_skill k k
48 sno_stats y y
49 snomask s s s s s s s s s s s s
50 ssl S z z
51 sslonlymsg t Z (usermodes/secureonlymsg)
52 stripcolor S (stripcolor)
53 vhost t
54 wallops w w w w w w w w w w w w
55 webirc W

View File

@ -0,0 +1,244 @@
<!DOCTYPE html>
<html lang="en">
<meta charset="UTF-8">
<meta name=viewport content="width=device-width, initial-scale=1">
<head>
<title>Supported User Modes for PyLink</title>
<style>
html {
background-color: white;
}
.note {
color: #555555;
}
/* (╮°-°)╮┳━┳ */
table, th, td {
border: 1px solid black;
}
td, th {
text-align: center;
padding: 3px;
}
td:first-child, th[scope="row"] {
text-align: left;
}
/* Table cells */
.tablecell-yes {
background-color: #A7F2A5
}
.tablecell-no {
background-color: #F08496
}
.tablecell-na {
background-color: #F0F0F0
}
.tablecell-planned, .tablecell-yes2 {
background-color: #B1FCDE
}
.tablecell-partial {
background-color: #EDE8A4
}
.tablecell-special {
background-color: #DCB1FC
}
</style>
</head>
<body>
<table><tr>
<th scope="col">User Mode / IRCd</th>
<th scope="col">RFC 1459</th>
<th scope="col">hybrid</th>
<th scope="col">inspircd</th>
<th scope="col">ngircd</th>
<th scope="col">p10/ircu</th>
<th scope="col">p10/nefarious</th>
<th scope="col">p10/snircd</th>
<th scope="col">ts6/charybdis</th>
<th scope="col">ts6/chatircd</th>
<th scope="col">ts6/elemental</th>
<th scope="col">ts6/ratbox</th>
<th scope="col">unreal</th>
</tr>
<tr>
<th scope="row">admin</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+a</td><td class="tablecell-yes">+a</td><td class="tablecell-yes">+a</td><td class="tablecell-yes">+a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">away</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">bot</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+B<br><span class="note">(botmode)</span></td><td class="tablecell-yes">+B</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+B</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+B</td><td class="tablecell-yes">+B</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+B<br><span class="note">(usermodes/bot)</span></td></tr>
<tr>
<th scope="row">callerid</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+g</td><td class="tablecell-yes2">+g<br><span class="note">(callerid)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+g</td><td class="tablecell-yes">+g</td><td class="tablecell-yes">+g</td><td class="tablecell-yes">+g</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">censor</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+G<br><span class="note">(usermodes/censor)</span></td></tr>
<tr>
<th scope="row">cloak</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+x</td><td class="tablecell-yes2">+x<br><span class="note">(cloaking)</span></td><td class="tablecell-yes">+x</td><td class="tablecell-yes">+x</td><td class="tablecell-yes">+x</td><td class="tablecell-yes">+x</td><td class="tablecell-yes">+x</td><td class="tablecell-yes">+x</td><td class="tablecell-yes">+x</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+x</td></tr>
<tr>
<th scope="row">cloak_fakehost</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+f</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">cloak_hashedhost</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+C</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">cloak_hashedip</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+c</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">cloak_sethost</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+h</td><td class="tablecell-yes">+h</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">deaf</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+D</td><td class="tablecell-yes2">+d<br><span class="note">(deaf)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+d</td><td class="tablecell-yes">+d</td><td class="tablecell-yes">+d</td><td class="tablecell-yes">+D</td><td class="tablecell-yes">+D</td><td class="tablecell-yes">+D</td><td class="tablecell-yes">+D</td><td class="tablecell-yes">+d</td></tr>
<tr>
<th scope="row">deaf_commonchan</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+G</td><td class="tablecell-yes2">+c<br><span class="note">(commonchans)</span></td><td class="tablecell-yes">+C</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+q</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">debug</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+d</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">filter</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+G</td></tr>
<tr>
<th scope="row">floodexempt</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+F</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">helpop</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+h<br><span class="note">(helpop)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">hidechans</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+p</td><td class="tablecell-yes2">+I<br><span class="note">(hidechans)</span></td><td class="tablecell-yes">+I</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+n</td><td class="tablecell-yes">+n</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+I</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+p<br><span class="note">(usermodes/privacy)</span></td></tr>
<tr>
<th scope="row">hideidle</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+q</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+I</td><td class="tablecell-yes">+I</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+I</td></tr>
<tr>
<th scope="row">hideoper</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+H</td><td class="tablecell-yes2">+H<br><span class="note">(hideoper)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+H</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+H</td></tr>
<tr>
<th scope="row">invisible</th>
<td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td><td class="tablecell-yes">+i</td></tr>
<tr>
<th scope="row">locops</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+l</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+O</td><td class="tablecell-yes">+O</td><td class="tablecell-yes">+O</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-yes">+l</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">netadmin</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+N</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">noctcp</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+C</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+T<br><span class="note">(usermodes/noctcp)</span></td></tr>
<tr>
<th scope="row">noforward</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+L<br><span class="note">(redirect)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+L</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+Q</td><td class="tablecell-yes">+Q</td><td class="tablecell-yes">+Q</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">noinvite</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+V</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">oper</th>
<td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td><td class="tablecell-yes">+o</td></tr>
<tr>
<th scope="row">operwall</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+z</td><td class="tablecell-yes">+z</td><td class="tablecell-yes">+z</td><td class="tablecell-yes">+z</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">override</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+X</td><td class="tablecell-yes">+X</td><td class="tablecell-yes">+p</td><td class="tablecell-yes">+p</td><td class="tablecell-yes">+p</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">privdeaf</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+b</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+D</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+D<br><span class="note">(usermodes/privdeaf)</span></td></tr>
<tr>
<th scope="row">protected</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+q<br><span class="note">(usermodes/nokick)</span></td></tr>
<tr>
<th scope="row">regdeaf</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+R</td><td class="tablecell-yes2">+R<br><span class="note">(services_account)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+R</td><td class="tablecell-yes">+R</td><td class="tablecell-yes">+R</td><td class="tablecell-yes">+R</td><td class="tablecell-yes">+R</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+R<br><span class="note">(usermodes/regonlymsg)</span></td></tr>
<tr>
<th scope="row">registered</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+r</td><td class="tablecell-yes2">+r<br><span class="note">(services_account)</span></td><td class="tablecell-yes">+R</td><td class="tablecell-yes">+r</td><td class="tablecell-yes">+r</td><td class="tablecell-yes">+r</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+r</td></tr>
<tr>
<th scope="row">restricted</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+r</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">servprotect</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+k<br><span class="note">(servprotect)</span></td><td class="tablecell-yes">+q</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+k</td><td class="tablecell-yes">+S</td><td class="tablecell-yes">+S</td><td class="tablecell-yes">+S</td><td class="tablecell-yes">+S</td><td class="tablecell-yes2">+S<br><span class="note">(usermodes/servicebot)</span></td></tr>
<tr>
<th scope="row">showwhois</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+W<br><span class="note">(showwhois)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+W</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+W<br><span class="note">(usermodes/showwhois)</span></td></tr>
<tr>
<th scope="row">sno_badclientconnections</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+u</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+u</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_botfloods</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+b</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+b</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_clientconnections</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+c</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+c</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+c</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_debug</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+g</td><td class="tablecell-yes">+g</td><td class="tablecell-yes">+g</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+d</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_extclientconnections</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+C</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_fullauthblock</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+f</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+f</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_nickchange</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+n</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_rejectedclients</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+j</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+r</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_remoteclientconnections</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+F</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_serverconnects</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+e</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+x</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_skill</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+k</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+k</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">sno_stats</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+y</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+y</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">snomask</th>
<td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td><td class="tablecell-yes">+s</td></tr>
<tr>
<th scope="row">ssl</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+S</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+z</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+z</td></tr>
<tr>
<th scope="row">sslonlymsg</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+t</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+Z<br><span class="note">(usermodes/secureonlymsg)</span></td></tr>
<tr>
<th scope="row">stripcolor</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes2">+S<br><span class="note">(stripcolor)</span></td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
<tr>
<th scope="row">vhost</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-yes">+t</td></tr>
<tr>
<th scope="row">wallops</th>
<td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td><td class="tablecell-yes">+w</td></tr>
<tr>
<th scope="row">webirc</th>
<td class="tablecell-na note">n/a</td><td class="tablecell-yes">+W</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td><td class="tablecell-na note">n/a</td></tr>
</table>
</body>
</html>

View File

@ -0,0 +1,121 @@
# PyLink Permissions Reference
Below is a list of all the permissions defined by PyLink and its official plugins.
## PyLink Core
- `core.clearqueue` - Grants access to the `clearqueue` command.
- `core.load` - Grants access to the `load` command.
- `core.rehash` - Grants access to the `rehash` command.
- `core.reload` - Grants access to the `reload`, `load`, and `unload` commands. (This implies access to `load` and `unload` because `reload` is really just those two commands combined.)
- `core.shutdown` - Grants access to the `shutdown` command.
- `core.unload` - Grants access to the `unload` command.
## Automode
By default, Automode integrates with Relay by only allowing access lists to be created / manipulated on channels that are owned by a network via Relay.
- `automode.manage` OR `automode.manage.*`: ability to manage Automode (use `setacc` and `delacc`) on all channels on the network where the user is connected.
- `automode.manage.relay_owned`: ability to manage Automode on channels owned by the current network in Relay. If Relay isn't loaded or the channel in question isn't shared via Relay, this permission check FAILS. **With the default permissions set, this is granted to all opers.**
- `automode.manage.#channel`: ability to manage Automode on the specific given channel.
- `automode.list` OR `automode.list.*`: ability to list Automode on all channels. **With the default permissions set, this is granted to all opers.**
- `automode.list.relay_owned`: ability to list automode on channels owned via Relay. If Relay isn't loaded or the channel in question isn't shared via Relay, this permission check FAILS.
- `automode.list.#channel`: ability to list Automode access entries on the specific given channel.
- `automode.sync` OR `automode.sync.*`: ability to sync automode on all channels.
- `automode.sync.relay_owned`: ability to sync automode on channels owned via Relay. If Relay isn't loaded or the channel in question isn't shared via Relay, this permission check FAILS. **With the default permissions set, this is granted to all opers.**
- `automode.sync.#channel`: ability to sync automode on the specific given channel.
- `automode.clear` OR `automode.clear.*`: ability to clear automode on all channels.
- `automode.clear.relay_owned`: ability to clear automode on channels owned via Relay. If Relay isn't loaded or the channel in question isn't shared via Relay, this permission check FAILS.
- `automode.clear.#channel`: ability to clear automode on the specific given channel.
- `automode.savedb`: ability to save the automode DB.
Remote versions of the `manage`, `list`, `sync`, and `clear` commands also exist for cross-network manipulation (e.g. `automode.remotemanage.*`)
## Bots
- `bots.join` - Grants access to the `join` command. `bots.joinclient` is a deprecated alias for this, retained for compatibility with PyLink < 2.0-rc1.
- `bots.msg` - Grants access to the `msg` command.
- `bots.nick` - Grants access to the `nick` command.
- `bots.part` - Grants access to the `part` command.
- `bots.quit` - Grants access to the `quit` command.
- `bots.spawnclient` - Grants access to the `spawnclient` command.
## Changehost
- `changehost.applyhosts` - Grants access to the `applyhosts` command.
## Commands
- `commands.echo` - Grants access to the `echo` command.
- `commands.loglevel` - Grants access to the `loglevel` command.
- `commands.logout.force` - Allows forcing logouts on other users via the `logout` command.
- `commands.showchan` - Grants access to the `showchan` command. **With the default permissions set, this is granted to all users.**
- `commands.shownet` - Grants access to the `shownet` command (basic info including netname, protocol module, and encoding). **With the default permissions set, this is granted to all users.**
- `commands.shownet.extended` - Grants access to extended info in `shownet`, including connected status, target IP:port, and configured PyLink hostname / SID.
- `commands.showuser` - Grants access to the `showuser` command. **With the default permissions set, this is granted to all users.**
- `commands.status` - Grants access to the `status` command. **With the default permissions set, this is granted to all users.**
## Exec
- `exec.exec` - Grants access to the `exec` and `iexec` commands.
- `exec.eval` - Grants access to the `eval`, `ieval`, `peval`, and `pieval` commands.
- `exec.inject` - Grants access to the `inject` command.
- `exec.threadinfo` - Grants access to the `threadinfo` command.
## Global
- `global.global` - Grants access to the `global` command.
## Networks
- `networks.autoconnect` - Grants access to the `autoconnect` command.
- `networks.disconnect` - Grants access to the `disconnect` command.
- `networks.reloadproto` - Grants access to the `reloadproto` command.
- `networks.remote` - Grants access to the `remote` command.
## Opercmds
- `opercmds.checkban` - Grants access to the `checkban` command.
- `opercmds.checkban.re` - Grants access to the `checkbanre` command **if** the caller also has `opercmds.checkban`.
- `opercmds.chghost` - Grants access to the `chghost` command.
- `opercmds.chgident` - Grants access to the `chgident` command.
- `opercmds.chgname` - Grants access to the `chgname` command.
- `opercmds.jupe` - Grants access to the `jupe` command.
- `opercmds.kick` - Grants access to the `kick` command.
- `opercmds.kill` - Grants access to the `kill` command.
- `opercmds.massban` - Grants access to the `massban` command.
- `opercmds.massban.re` - Grants access to the `massbanre` command **if** the caller also has `opercmds.massban`.
- `opercmds.mode` - Grants access to the `mode` command.
- `opercmds.topic` - Grants access to the `topic` command.
## Raw
- `raw.raw` - Grants access to the `raw` command. `exec.raw` is equivalent to this and retained for compatibility with PyLink 1.x.
- `raw.raw.unsupported_network` - Allows use of the `raw` command on servers other than Clientbot.
## Relay
These permissions are granted to all opers when the `relay::allow_free_oper_links` option is set (this is the default):
- `relay.chandesc.remove` - Allows removing channel descriptions via the `chandesc` command.
- `relay.chandesc.set` - Allows setting / updating channel descriptions via the `chandesc` command.
- `relay.claim` - Grants access to the `claim` command.
- `relay.create` - Grants access to the `create` command.
- `relay.delink` - Grants access to the `delink` command.
- `relay.destroy` - Grants access to the `destroy` command.
- `relay.link` - Grants access to the `link` command.
These permissions are always granted to all opers:
- `relay.linkacl` - Allows managing LINKACL entries via the `linkacl` command.
- `relay.linkacl.view` - Allows viewing LINKACL entries via the `linkacl` command.
These permissions are not granted to anyone by default:
- `relay.destroy.remote` - Allows destroying remote channels.
- `relay.link.force_ts` - Grants access to the `link` command's `--force-ts` option (skip TS and target network is connected checks).
- `relay.linked` - Grants access to the `link` command. **With the default permissions set, this is granted to all users.**
- `relay.purge` - Grants access to the `purge` command.
- `relay.savedb` - Grants access to the `savedb` command.
## Servermaps
- `servermaps.localmap` - Grants access to the `localmap` command.
- `servermaps.map` - Grants access to the `map` command.
## Stats
- `stats.c`, `stats.o`, `stats.u` - Grants access to remote `/stats` calls with the corresponding letter.
- `stats.uptime` - Grants access to the `stats` command.

View File

@ -1,49 +1 @@
# Opering with PyLink Relay Moved to [relay-quickstart.md](relay-quickstart.md).
*This guide was written for the OVERdrive-IRC network, but may be applicable elsewhere.*
PyLink Relay behaves much like Janus, an extended service used to relay channels together. This guide goes over some of the basic oper commands in Relay, along with the best ways to handle channel emergencies.
## How nick suffixing work
When joining a relay channel, every user from another network will have a network tag attached to their name. The purpose of this is to prevent nick collisions from the same nick being used on multiple nets, and ensure that different networks' registered nicks remain separate.
How is this relevant? Firstly, it means that you **cannot ban users from entire networks** using banmasks such as `*/net1!*@*`! The nick suffix is something PyLink adds artificially; on `net1`'s IRCd, which is checking the bans locally, the nick suffix simply doesn't exist.
However, this *does* mean that you can effectively give access to remote users via services, by specifying masks such as `*/net1@someident@someperson.opers.somenet.org`. Just don't make masks too wide, or you risk getting channel takeovers.
## Basic linking commands
The concept of relay channels in PyLink is greatly inspired from the original Janus implementation, though with a few differences in command syntax.
To create a channel:
- `/msg PyLink create #channelname`
To link to a channel already created on a different network:
- `/msg PyLink link othernet #channelname`
You can also link remote channels to take a different name on your network. (This is the third argument to the LINK command)
- `/msg PyLink link othernet #lobby #othernet-lobby`
Also, to list the available channels:
- `/msg PyLink linked`
## Dealing with channel emergencies
PyLink is not designed with the ability to forward KILLs, G:Lines, or any network bans. **The best thing to do in the case of emergencies is to delink the problem networks / channels!** Kills are actively blocked by the PyLink daemon (user is just respawned), while X:Lines are simply ignored, as there isn't any code to handle them yet.
To delink another network from a channel your network owns:
- `/msg PyLink delink #yourchannel badnetwork`
To delink your network from a bad network's channel:
- `/msg PyLink delink #badchannel`
Basically, only one of the two above commands will work for one specific channel. Almost always, the network that owns a channel should be the one who has it registered via their services. You can see a list of channels by typing `/msg PyLink linked`.
## When a network starts causing disconnect spam
Juping an individual `net.relay` server will likely cause PyLink Relay to break or disconnect completely. When a network starts acting up and disconnecting frequently (and causing netsplit/quit floods), you should disable autoconnect for this network:
- `/msg PyLink autoconnect badnetwork -1` (setting autoconnect to 0 or below will cause it to be disabled)

146
docs/relay-quickstart.md Normal file
View File

@ -0,0 +1,146 @@
# PyLink Relay Quick Start
## What is Relay?
PyLink Relay is a plugin that provides transparent relays between channels on different networks. On participating networks, PyLink connects as a services server and mirrors messages as well as user lists from relayed channels, the latter by creating "puppet" service clients for all remote users in common channels. Relay offers an alternative to classic IRC linking, letting networks share channels on demand while retaining their services, policies, and distinct branding. By default, Relay also secures channels from remote oper overrides via a CLAIM feature, which restricts /kick, /mode, and /topic changes from un-opped users unless they are granted permissions via CLAIM.
Relay shares many ideas from its predecessor Janus, but is a complete rewrite in Python. This guide goes over some of the basic commands in Relay, as well as some must-know gotchas.
## Important notes (READ FIRST!)
### How nick suffixing work
By default, Relay will automatically tag users from other networks with a suffix such as `/net`. This prevents confusing nick collisions if the same nick is used on multiple linked networks, and ensure that nicks from remote networks are all isolated into their own namespaces.
How is this relevant to an operator? It means that you **cannot ban users** using banmasks such as `*/net1!*@*`! The nick suffix is something PyLink adds artificially; on `net1`'s IRCd, which check the bans locally, the nick suffix doesn't exist and will therefore *not* match anyone.
### Services compatibility
While PyLink is generally able to run independently of individual networks' services, there are some gotchas. This list briefly details services features that have been known to cause problems with Relay. **Using any of these features in conjunction with Relay is *not* supported.**
- Anope, Atheme: **Clones prevention should be DISABLED** (or at a minimum, set to use G/KLINE instead of KILL)
- Rationale: it is common for a person to want to connect to multiple networks in a Relay instance, because they are still independent entities. You can still use IRCd-side clones prevention, which sanely blocks connections instead of killing / banning everyone involved.
- Anope: **SQLINE nicks should NOT be used**
- Rationale: Anope falls back to killing target clients matching a SQLINE, which will obviously cause conflicts with other services.
- Atheme: **The ChanFix service should be disabled**
- Rationale: ChanFix is incompatible with Relay CLAIM because it overrides ops on relay channels whenever they appear "opless". This basic op check is unable to consider the case of remote channel services not being set to join channels, and will instead cause join/message/part spam as CLAIM reverts the ChanFix service's mode changes.
- *Any*: **Do NOT register a relayed channel on multiple networks**
- Rationale: It is very easy for this to start kick or mode wars. (Bad akick mask? Secure ops enabled?)
- Clientbot is an exception to this, though you may want to add Clientbot networks to CLAIM so that PyLink doesn't try to reverse modes set by services on the Clientbot network.
- *Any*: **Do NOT jupe virtual Relay servers** (e.g. `net.relay`)
- Rationale: This will just make PyLink split off - you should instead [delink any problem networks / channels](#dealing-with-disputes-and-emergencies).
- Multiple PyLink Relay instances:
- **Do NOT connect a network twice to any PyLink instance**.
- **Do NOT connect a network to 2+ separate PyLink instances if there is another network already acting as a hub for them**.
- Not following these rules means that it's very easy for the Relay instances to go in a loop should an operator run the wrong command, which will hammer your CPU and relentlessly spam your channels.
Note: P10-specific services packages have not been particularly tested - your feedback is welcome.
## Relay commands
The basic steps for setting up a relay is to first CREATE the channel with PyLink on the network that owns it, and run LINK from each network that wants to link to it. In most cases, you want to run CREATE on the network where the channel is registered with services.
Importantly, this means that CREATE and LINK have to be run on different networks for any particular channel, and that you should only run CREATE once for each distinct channel! This setup is intended to allow individual network admins to pick and choose channels they want to participate in.
First, to list all available channels:
- `/msg PyLink linked`
To create a channel on Relay:
- `/msg PyLink create #channelname`
- Note: **you can only create channels on full IRCd links - this will NOT work with Clientbot.**
- A channel created on a particular network is considered to be _owned_ by that network; this affects how CLAIM works for instance (see the next section)
To link to a channel already created on a different network:
- `/msg PyLink link othernet #channelname`
- You should replace `othernet` with the *short name* for the network that owns the channel.
- Note: network names are case sensitive!
You can also link remote channels while using a different name for it on your network. (This is the third argument to the LINK command)
- `/msg PyLink link othernet #lobby #othernet-lobby`
To completely remove a relay channel (on the network that created it):
- `/msg PyLink destroy #channelname`
To delink a channel *linked to another network*:
- `/msg PyLink delink #localchannelname`
To delink one of *your* channels from another network:
- `/msg PyLink delink #yourchannelname <name-of-other-network>`
Then, to list all available channels:
- `/msg PyLink linked`
### Claiming channels
Channel claiming is a feature which prevents oper override (MODE, KICK, TOPIC, KILL, OJOIN, ...) by other networks' operators from affecting your channels. By default, CLAIM is enabled for all new channels, though this can be configured via the [`relay::enable_default_claim` option](https://github.com/jlu5/PyLink/blob/3.0.0/example-conf.yml#L828-L831). Unless the claimed network list of a channel is _empty__, oper override will only be allowed from networks on the CLAIM list (plus the network that owns the channel).
Note: these commands must be run from the network which owns the channel in question!
To set a claim:
- `/msg PyLink claim #channel yournet,net2,net3` (the last parameter is a case-sensitive comma-separated list of networks)
To list claim networks on a channel:
- `/msg PyLink claim #channel`
To clear the claim list for a channel:
- `/msg PyLink claim #channel -`
### Access control for links (LINKACL)
LINKACL allows you to allow or deny networks from linking to your channel. New channels are created using a blacklist by default, though this can be configured via the [`relay::linkacl_use_whitelist` option](https://github.com/jlu5/PyLink/blob/3.0.0/example-conf.yml#L823-L826).
To change between blacklist and whitelist mode:
- `/msg PyLink linkacl whitelist #channel true/false`
- Note that when you switch between LINKACL modes, the LINKACL entries from the previous mode are stored and stashed away. This means that you will get an empty LINKACL list in the new LINKACL mode if you haven't used it already, and that you can reload the previous LINKACL mode's entries by switching back to it at any point.
To view the LINKACL networks for a channel:
- `/msg PyLink linkacl #channel list`
To add a network to the whitelist **OR** remove a network from the blacklist:
- `/msg PyLink linkacl #channel allow goodnet`
To remove a network from the whitelist **OR** add a network to the blacklist:
- `/msg PyLink linkacl #channel deny badnet`
### Adding channel descriptions
Starting with PyLink 2.0, you can annotate your channels with a description to use in LINKED:
To view the description for a channel:
- `/msg PyLink chandesc #channel`
To change the description for a channel:
- `/msg PyLink chandesc #channel your text goes here`
To remove the description for a channel:
- `/msg PyLink chandesc #channel -`
## Dealing with disputes and emergencies
The best thing to do in the event of a dispute is to delink the problem networks / channels. In order for individual networks to maintain their autonomy, KILLs and network bans (K/G/ZLINE) will most often *not* behave the way you expect them to.
### Kill handling
Special kill handling was introduced in PyLink 2.0, while in previous versions they were always bounced:
1) If the sender was a server and not a client, reject the kill. (This prevents services messups from wreaking havoc across the relay)
2) If the target and source networks share a [kill share pool](https://github.com/jlu5/PyLink/blob/3.0.0/example-conf.yml#L782-L792), relay the kill as-is.
3) Otherwise, check every channel that the kill target is in:
- If the sender is opped or has claim access in a channel, forward the KILL as a kick in that channel.
- Otherwise, bounce the kill silently (i.e. rejoin the user immediately).
### Network bans (K/G/ZLINE)
Network bans are purposely not supported; see https://github.com/jlu5/PyLink/issues/521#issuecomment-352316396.
### Delinking channels
To delink another network from a channel your network owns:
- `/msg PyLink delink #yourchannel badnetwork`
To delink your network from a bad network's channel:
- `/msg PyLink delink #badchannel`
Basically, only one of the two above commands will work for one specific channel. Almost always, the network that owns a channel should be the one who has it registered via their services. You can see a list of channels by typing `/msg PyLink linked`.

View File

@ -1,26 +1,28 @@
# PyLink Developer Documentation # PyLink Developer Documentation
Please note that as PyLink is still in its development phase, its APIs are subject to change. This documentation is provided for reference only, and may not always be up to date as APIs change.
Any documentation here is provided for reference only. Patches are welcome if something looks wrong or *is* wrong. In such cases, consulting the source code is probably your best bet.
The docs are also really incomplete (contributors welcome!) The docs are also really incomplete (contributions are appreciated!)
## Introduction ## Introduction
PyLink is an a modular, plugin-based IRC services framework. It uses swappable protocol modules and a hooks system for calling plugins, allowing them to function regardless of the IRCd used. PyLink is an a modular, plugin-based IRC services framework. It uses swappable protocol modules and a hooks system for calling plugins, allowing them to function regardless of the IRCd used.
<img src="core-structure.png" width="50%" height="50%">
## Contents ## Contents
- [Writing plugins for PyLink](writing-plugins.md) - [Writing plugins for PyLink](writing-plugins.md)
- [PyLink protocol module specification](pmodule-spec.md)
- [PyLink hooks reference](hooks-reference.md) - [PyLink hooks reference](hooks-reference.md)
- [Supported named channel modes](channel-modes.csv)
- [API reference: utils.py](autogen/utils.html)
- [API reference: classes.py](autogen/classes.html)
### Future topics (not yet available)
- [Writing tests for PyLink modules](writing-tests.md)
- [Supported named user modes](user-modes.csv)
- [Services bot API/Creating your own service bots](services-api.md) - [Services bot API/Creating your own service bots](services-api.md)
- [Permissions API Introduction](permissions-api.md)
- [Using `utils.IRCParser()`](using-ircparser.md)
----
- [PyLink protocol module specification](pmodule-spec.md)
----
- [Release Process for PyLink](release-process.md)
![Graph of protocol module inheritance tree](protocol-modules.svg)

View File

@ -1,569 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module classes</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>classes</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/classes.py">/home/gl/pylink/classes.py</a></font></td></tr></table>
<p><tt>classes.py&nbsp;-&nbsp;Base&nbsp;classes&nbsp;for&nbsp;PyLink&nbsp;IRC&nbsp;Services.<br>
&nbsp;<br>
This&nbsp;module&nbsp;contains&nbsp;the&nbsp;base&nbsp;classes&nbsp;used&nbsp;by&nbsp;PyLink,&nbsp;including&nbsp;threaded&nbsp;IRC<br>
connections&nbsp;and&nbsp;objects&nbsp;used&nbsp;to&nbsp;represent&nbsp;IRC&nbsp;servers,&nbsp;users,&nbsp;and&nbsp;channels.<br>
&nbsp;<br>
Here&nbsp;be&nbsp;dragons.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="hashlib.html">hashlib</a><br>
<a href="inspect.html">inspect</a><br>
<a href="logging.html">logging</a><br>
</td><td width="25%" valign=top><a href="os.html">os</a><br>
<a href="socket.html">socket</a><br>
<a href="ssl.html">ssl</a><br>
</td><td width="25%" valign=top><a href="structures.html">structures</a><br>
<a href="sys.html">sys</a><br>
<a href="threading.html">threading</a><br>
</td><td width="25%" valign=top><a href="time.html">time</a><br>
<a href="utils.html">utils</a><br>
<a href="world.html">world</a><br>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="builtins.html#Exception">builtins.Exception</a>(<a href="builtins.html#BaseException">builtins.BaseException</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="classes.html#ProtocolError">ProtocolError</a>
</font></dt></dl>
</dd>
<dt><font face="helvetica, arial"><a href="builtins.html#object">builtins.object</a>
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="classes.html#Irc">Irc</a>
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="classes.html#FakeIRC">FakeIRC</a>
</font></dt></dl>
</dd>
<dt><font face="helvetica, arial"><a href="classes.html#IrcChannel">IrcChannel</a>
</font></dt><dt><font face="helvetica, arial"><a href="classes.html#IrcServer">IrcServer</a>
</font></dt><dt><font face="helvetica, arial"><a href="classes.html#IrcUser">IrcUser</a>
</font></dt><dt><font face="helvetica, arial"><a href="classes.html#Protocol">Protocol</a>
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="classes.html#FakeProto">FakeProto</a>
</font></dt></dl>
</dd>
</dl>
</dd>
</dl>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="FakeIRC">class <strong>FakeIRC</strong></a>(<a href="classes.html#Irc">Irc</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Fake&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>&nbsp;used&nbsp;for&nbsp;unit&nbsp;tests.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="classes.html#FakeIRC">FakeIRC</a></dd>
<dd><a href="classes.html#Irc">Irc</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="FakeIRC-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Runs&nbsp;the&nbsp;connect&nbsp;loop&nbsp;for&nbsp;the&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>.&nbsp;This&nbsp;is&nbsp;usually&nbsp;called&nbsp;by<br>
__init__&nbsp;in&nbsp;a&nbsp;separate&nbsp;thread&nbsp;to&nbsp;allow&nbsp;multiple&nbsp;concurrent&nbsp;connections.</tt></dd></dl>
<dl><dt><a name="FakeIRC-run"><strong>run</strong></a>(self, data)</dt><dd><tt>Queues&nbsp;a&nbsp;message&nbsp;to&nbsp;the&nbsp;fake&nbsp;IRC&nbsp;server.</tt></dd></dl>
<dl><dt><a name="FakeIRC-send"><strong>send</strong></a>(self, data)</dt><dd><tt>Sends&nbsp;raw&nbsp;text&nbsp;to&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="FakeIRC-takeCommands"><strong>takeCommands</strong></a>(self, msgs)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;commands&nbsp;parsed&nbsp;from&nbsp;the&nbsp;output&nbsp;of&nbsp;<a href="#FakeIRC-takeMsgs">takeMsgs</a>().</tt></dd></dl>
<dl><dt><a name="FakeIRC-takeHooks"><strong>takeHooks</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;hook&nbsp;arguments&nbsp;sent&nbsp;by&nbsp;the&nbsp;protocol&nbsp;module&nbsp;since<br>
the&nbsp;last&nbsp;<a href="#FakeIRC-takeHooks">takeHooks</a>()&nbsp;call.</tt></dd></dl>
<dl><dt><a name="FakeIRC-takeMsgs"><strong>takeMsgs</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;messages&nbsp;sent&nbsp;by&nbsp;the&nbsp;protocol&nbsp;module&nbsp;since<br>
the&nbsp;last&nbsp;<a href="#FakeIRC-takeMsgs">takeMsgs</a>()&nbsp;call,&nbsp;so&nbsp;we&nbsp;can&nbsp;track&nbsp;what&nbsp;has&nbsp;been&nbsp;sent.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Irc">Irc</a>:<br>
<dl><dt><a name="FakeIRC-__init__"><strong>__init__</strong></a>(self, netname, proto, conf)</dt><dd><tt>Initializes&nbsp;an&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>.&nbsp;This&nbsp;takes&nbsp;3&nbsp;variables:&nbsp;the&nbsp;network&nbsp;name<br>
(a&nbsp;string),&nbsp;the&nbsp;name&nbsp;of&nbsp;the&nbsp;protocol&nbsp;module&nbsp;to&nbsp;use&nbsp;for&nbsp;this&nbsp;connection,<br>
and&nbsp;a&nbsp;configuration&nbsp;<a href="builtins.html#object">object</a>.</tt></dd></dl>
<dl><dt><a name="FakeIRC-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;repr(self).</tt></dd></dl>
<dl><dt><a name="FakeIRC-applyModes"><strong>applyModes</strong></a>(self, target, changedmodes)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;parsed&nbsp;IRC&nbsp;modes,&nbsp;and&nbsp;applies&nbsp;them&nbsp;on&nbsp;the&nbsp;given&nbsp;target.<br>
&nbsp;<br>
The&nbsp;target&nbsp;can&nbsp;be&nbsp;either&nbsp;a&nbsp;channel&nbsp;or&nbsp;a&nbsp;user;&nbsp;this&nbsp;is&nbsp;handled&nbsp;automatically.</tt></dd></dl>
<dl><dt><a name="FakeIRC-callCommand"><strong>callCommand</strong></a>(self, source, text)</dt><dd><tt>Calls&nbsp;a&nbsp;PyLink&nbsp;bot&nbsp;command.&nbsp;source&nbsp;is&nbsp;the&nbsp;caller's&nbsp;UID,&nbsp;and&nbsp;text&nbsp;is&nbsp;the<br>
full,&nbsp;unparsed&nbsp;text&nbsp;of&nbsp;the&nbsp;message.</tt></dd></dl>
<dl><dt><a name="FakeIRC-callHooks"><strong>callHooks</strong></a>(self, hook_args)</dt><dd><tt>Calls&nbsp;a&nbsp;hook&nbsp;function&nbsp;with&nbsp;the&nbsp;given&nbsp;hook&nbsp;args.</tt></dd></dl>
<dl><dt><a name="FakeIRC-checkAuthenticated"><strong>checkAuthenticated</strong></a>(self, uid, allowAuthed=True, allowOper=True)</dt><dd><tt>Checks&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;has&nbsp;operator&nbsp;status&nbsp;on&nbsp;PyLink,&nbsp;raising<br>
NotAuthenticatedError&nbsp;and&nbsp;logging&nbsp;the&nbsp;access&nbsp;denial&nbsp;if&nbsp;not.</tt></dd></dl>
<dl><dt><a name="FakeIRC-disconnect"><strong>disconnect</strong></a>(self)</dt><dd><tt>Handle&nbsp;disconnects&nbsp;from&nbsp;the&nbsp;remote&nbsp;server.</tt></dd></dl>
<dl><dt><a name="FakeIRC-getHostmask"><strong>getHostmask</strong></a>(self, user, realhost=False, ip=False)</dt><dd><tt>Returns&nbsp;the&nbsp;hostmask&nbsp;of&nbsp;the&nbsp;given&nbsp;user,&nbsp;if&nbsp;present.&nbsp;If&nbsp;the&nbsp;realhost&nbsp;option<br>
is&nbsp;given,&nbsp;return&nbsp;the&nbsp;real&nbsp;host&nbsp;of&nbsp;the&nbsp;user&nbsp;instead&nbsp;of&nbsp;the&nbsp;displayed&nbsp;host.<br>
If&nbsp;the&nbsp;ip&nbsp;option&nbsp;is&nbsp;given,&nbsp;return&nbsp;the&nbsp;IP&nbsp;address&nbsp;of&nbsp;the&nbsp;user&nbsp;(this&nbsp;overrides<br>
realhost).</tt></dd></dl>
<dl><dt><a name="FakeIRC-getServer"><strong>getServer</strong></a>(self, numeric)</dt><dd><tt>Finds&nbsp;the&nbsp;SID&nbsp;of&nbsp;the&nbsp;server&nbsp;a&nbsp;user&nbsp;is&nbsp;on.</tt></dd></dl>
<dl><dt><a name="FakeIRC-initVars"><strong>initVars</strong></a>(self)</dt><dd><tt>(Re)sets&nbsp;an&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>&nbsp;to&nbsp;its&nbsp;default&nbsp;state.&nbsp;This&nbsp;should&nbsp;be&nbsp;called&nbsp;when<br>
an&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>&nbsp;is&nbsp;first&nbsp;created,&nbsp;and&nbsp;on&nbsp;every&nbsp;reconnection&nbsp;to&nbsp;a&nbsp;network.</tt></dd></dl>
<dl><dt><a name="FakeIRC-isInternalClient"><strong>isInternalClient</strong></a>(self, numeric)</dt><dd><tt>Checks&nbsp;whether&nbsp;the&nbsp;given&nbsp;numeric&nbsp;is&nbsp;a&nbsp;PyLink&nbsp;Client,<br>
returning&nbsp;the&nbsp;SID&nbsp;of&nbsp;the&nbsp;server&nbsp;it's&nbsp;on&nbsp;if&nbsp;so.</tt></dd></dl>
<dl><dt><a name="FakeIRC-isInternalServer"><strong>isInternalServer</strong></a>(self, sid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;SID&nbsp;is&nbsp;an&nbsp;internal&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="FakeIRC-isManipulatableClient"><strong>isManipulatableClient</strong></a>(self, uid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;is&nbsp;marked&nbsp;as&nbsp;an&nbsp;internal,&nbsp;manipulatable<br>
client.&nbsp;Usually,&nbsp;automatically&nbsp;spawned&nbsp;services&nbsp;clients&nbsp;should&nbsp;have&nbsp;this<br>
set&nbsp;True&nbsp;to&nbsp;prevent&nbsp;interactions&nbsp;with&nbsp;opers&nbsp;(like&nbsp;mode&nbsp;changes)&nbsp;from<br>
causing&nbsp;desyncs.</tt></dd></dl>
<dl><dt><a name="FakeIRC-isOper"><strong>isOper</strong></a>(self, uid, allowAuthed=True, allowOper=True)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;has&nbsp;operator&nbsp;status&nbsp;on&nbsp;PyLink.&nbsp;This&nbsp;can&nbsp;be&nbsp;achieved<br>
by&nbsp;either&nbsp;identifying&nbsp;to&nbsp;PyLink&nbsp;as&nbsp;admin&nbsp;(if&nbsp;allowAuthed&nbsp;is&nbsp;True),<br>
or&nbsp;having&nbsp;user&nbsp;mode&nbsp;+o&nbsp;set&nbsp;(if&nbsp;allowOper&nbsp;is&nbsp;True).&nbsp;At&nbsp;least&nbsp;one&nbsp;of<br>
allowAuthed&nbsp;or&nbsp;allowOper&nbsp;must&nbsp;be&nbsp;True&nbsp;for&nbsp;this&nbsp;to&nbsp;give&nbsp;any&nbsp;meaningful<br>
results.</tt></dd></dl>
<dl><dt><a name="FakeIRC-isServiceBot"><strong>isServiceBot</strong></a>(self, uid)</dt><dd><tt>Checks&nbsp;whether&nbsp;the&nbsp;given&nbsp;UID&nbsp;is&nbsp;a&nbsp;registered&nbsp;service&nbsp;bot.&nbsp;If&nbsp;True,<br>
returns&nbsp;the&nbsp;cooresponding&nbsp;ServiceBot&nbsp;<a href="builtins.html#object">object</a>.</tt></dd></dl>
<dl><dt><a name="FakeIRC-logSetup"><strong>logSetup</strong></a>(self)</dt><dd><tt>Initializes&nbsp;any&nbsp;channel&nbsp;loggers&nbsp;defined&nbsp;for&nbsp;the&nbsp;current&nbsp;network.</tt></dd></dl>
<dl><dt><a name="FakeIRC-msg"><strong>msg</strong></a>(self, target, text, notice=False, source=None)</dt><dd><tt>Handy&nbsp;function&nbsp;to&nbsp;send&nbsp;messages/notices&nbsp;to&nbsp;clients.&nbsp;Source<br>
is&nbsp;optional,&nbsp;and&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink&nbsp;client&nbsp;if&nbsp;not&nbsp;specified.</tt></dd></dl>
<dl><dt><a name="FakeIRC-nickToUid"><strong>nickToUid</strong></a>(self, nick)</dt><dd><tt>Looks&nbsp;up&nbsp;the&nbsp;UID&nbsp;of&nbsp;a&nbsp;user&nbsp;with&nbsp;the&nbsp;given&nbsp;nick,&nbsp;if&nbsp;one&nbsp;is&nbsp;present.</tt></dd></dl>
<dl><dt><a name="FakeIRC-parseModes"><strong>parseModes</strong></a>(self, target, args)</dt><dd><tt>Parses&nbsp;a&nbsp;modestring&nbsp;list&nbsp;into&nbsp;a&nbsp;list&nbsp;of&nbsp;(mode,&nbsp;argument)&nbsp;tuples.<br>
['+mitl-o',&nbsp;'3',&nbsp;'person']&nbsp;=&gt;&nbsp;[('+m',&nbsp;None),&nbsp;('+i',&nbsp;None),&nbsp;('+t',&nbsp;None),&nbsp;('+l',&nbsp;'3'),&nbsp;('-o',&nbsp;'person')]</tt></dd></dl>
<dl><dt><a name="FakeIRC-reply"><strong>reply</strong></a>(self, text, notice=False, source=None)</dt><dd><tt>Replies&nbsp;to&nbsp;the&nbsp;last&nbsp;caller&nbsp;in&nbsp;the&nbsp;right&nbsp;context&nbsp;(channel&nbsp;or&nbsp;PM).</tt></dd></dl>
<dl><dt><a name="FakeIRC-reverseModes"><strong>reverseModes</strong></a>(self, target, modes, oldobj=None)</dt><dd><tt>Reverses/Inverts&nbsp;the&nbsp;mode&nbsp;string&nbsp;or&nbsp;mode&nbsp;list&nbsp;given.<br>
&nbsp;<br>
Optionally,&nbsp;an&nbsp;oldobj&nbsp;argument&nbsp;can&nbsp;be&nbsp;given&nbsp;to&nbsp;look&nbsp;at&nbsp;an&nbsp;earlier&nbsp;state&nbsp;of<br>
a&nbsp;channel/user&nbsp;<a href="builtins.html#object">object</a>,&nbsp;e.g.&nbsp;for&nbsp;checking&nbsp;the&nbsp;op&nbsp;status&nbsp;of&nbsp;a&nbsp;mode&nbsp;setter<br>
before&nbsp;their&nbsp;modes&nbsp;are&nbsp;processed&nbsp;and&nbsp;added&nbsp;to&nbsp;the&nbsp;channel&nbsp;state.<br>
&nbsp;<br>
This&nbsp;function&nbsp;allows&nbsp;both&nbsp;mode&nbsp;strings&nbsp;or&nbsp;mode&nbsp;lists.&nbsp;Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;"+mi-lk&nbsp;test&nbsp;=&gt;&nbsp;"-mi+lk&nbsp;test"<br>
&nbsp;&nbsp;&nbsp;&nbsp;"mi-k&nbsp;test&nbsp;=&gt;&nbsp;"-mi+k&nbsp;test"<br>
&nbsp;&nbsp;&nbsp;&nbsp;[('+m',&nbsp;None),&nbsp;('+r',&nbsp;None),&nbsp;('+l',&nbsp;'3'),&nbsp;('-o',&nbsp;'person')<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&gt;&nbsp;{('-m',&nbsp;None),&nbsp;('-r',&nbsp;None),&nbsp;('-l',&nbsp;None),&nbsp;('+o',&nbsp;'person')})<br>
&nbsp;&nbsp;&nbsp;&nbsp;{('s',&nbsp;None),&nbsp;('+o',&nbsp;'whoever')&nbsp;=&gt;&nbsp;{('-s',&nbsp;None),&nbsp;('-o',&nbsp;'whoever')})</tt></dd></dl>
<dl><dt><a name="FakeIRC-runline"><strong>runline</strong></a>(self, line)</dt><dd><tt>Sends&nbsp;a&nbsp;command&nbsp;to&nbsp;the&nbsp;protocol&nbsp;module.</tt></dd></dl>
<dl><dt><a name="FakeIRC-schedulePing"><strong>schedulePing</strong></a>(self)</dt><dd><tt>Schedules&nbsp;periodic&nbsp;pings&nbsp;in&nbsp;a&nbsp;loop.</tt></dd></dl>
<dl><dt><a name="FakeIRC-toLower"><strong>toLower</strong></a>(self, text)</dt><dd><tt>Returns&nbsp;a&nbsp;lowercase&nbsp;representation&nbsp;of&nbsp;text&nbsp;based&nbsp;on&nbsp;the&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>'s<br>
casemapping&nbsp;(rfc1459&nbsp;or&nbsp;ascii).</tt></dd></dl>
<dl><dt><a name="FakeIRC-version"><strong>version</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;detailed&nbsp;version&nbsp;string&nbsp;including&nbsp;the&nbsp;PyLink&nbsp;daemon&nbsp;version,<br>
the&nbsp;protocol&nbsp;module&nbsp;in&nbsp;use,&nbsp;and&nbsp;the&nbsp;server&nbsp;hostname.</tt></dd></dl>
<hr>
Static methods inherited from <a href="classes.html#Irc">Irc</a>:<br>
<dl><dt><a name="FakeIRC-joinModes"><strong>joinModes</strong></a>(modes)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;(mode,&nbsp;arg)&nbsp;tuples&nbsp;in&nbsp;<a href="#FakeIRC-parseModes">parseModes</a>()&nbsp;format,&nbsp;and<br>
joins&nbsp;them&nbsp;into&nbsp;a&nbsp;string.<br>
&nbsp;<br>
See&nbsp;testJoinModes&nbsp;in&nbsp;tests/test_utils.py&nbsp;for&nbsp;some&nbsp;examples.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Irc">Irc</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="FakeProto">class <strong>FakeProto</strong></a>(<a href="classes.html#Protocol">Protocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Dummy&nbsp;protocol&nbsp;module&nbsp;for&nbsp;testing&nbsp;purposes.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="classes.html#FakeProto">FakeProto</a></dd>
<dd><a href="classes.html#Protocol">Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="FakeProto-connect"><strong>connect</strong></a>(self)</dt></dl>
<dl><dt><a name="FakeProto-handle_events"><strong>handle_events</strong></a>(self, data)</dt></dl>
<dl><dt><a name="FakeProto-join"><strong>join</strong></a>(self, client, channel)</dt></dl>
<dl><dt><a name="FakeProto-spawnClient"><strong>spawnClient</strong></a>(self, nick, *args, **kwargs)</dt></dl>
<hr>
Data and other attributes defined here:<br>
<dl><dt><strong>Class</strong> = &lt;class 'classes.FakeProto'&gt;<dd><tt>Dummy&nbsp;protocol&nbsp;module&nbsp;for&nbsp;testing&nbsp;purposes.</tt></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">Protocol</a>:<br>
<dl><dt><a name="FakeProto-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="FakeProto-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="FakeProto-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="FakeProto-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="Irc">class <strong>Irc</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="Irc-__init__"><strong>__init__</strong></a>(self, netname, proto, conf)</dt><dd><tt>Initializes&nbsp;an&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>.&nbsp;This&nbsp;takes&nbsp;3&nbsp;variables:&nbsp;the&nbsp;network&nbsp;name<br>
(a&nbsp;string),&nbsp;the&nbsp;name&nbsp;of&nbsp;the&nbsp;protocol&nbsp;module&nbsp;to&nbsp;use&nbsp;for&nbsp;this&nbsp;connection,<br>
and&nbsp;a&nbsp;configuration&nbsp;<a href="builtins.html#object">object</a>.</tt></dd></dl>
<dl><dt><a name="Irc-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;repr(self).</tt></dd></dl>
<dl><dt><a name="Irc-applyModes"><strong>applyModes</strong></a>(self, target, changedmodes)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;parsed&nbsp;IRC&nbsp;modes,&nbsp;and&nbsp;applies&nbsp;them&nbsp;on&nbsp;the&nbsp;given&nbsp;target.<br>
&nbsp;<br>
The&nbsp;target&nbsp;can&nbsp;be&nbsp;either&nbsp;a&nbsp;channel&nbsp;or&nbsp;a&nbsp;user;&nbsp;this&nbsp;is&nbsp;handled&nbsp;automatically.</tt></dd></dl>
<dl><dt><a name="Irc-callCommand"><strong>callCommand</strong></a>(self, source, text)</dt><dd><tt>Calls&nbsp;a&nbsp;PyLink&nbsp;bot&nbsp;command.&nbsp;source&nbsp;is&nbsp;the&nbsp;caller's&nbsp;UID,&nbsp;and&nbsp;text&nbsp;is&nbsp;the<br>
full,&nbsp;unparsed&nbsp;text&nbsp;of&nbsp;the&nbsp;message.</tt></dd></dl>
<dl><dt><a name="Irc-callHooks"><strong>callHooks</strong></a>(self, hook_args)</dt><dd><tt>Calls&nbsp;a&nbsp;hook&nbsp;function&nbsp;with&nbsp;the&nbsp;given&nbsp;hook&nbsp;args.</tt></dd></dl>
<dl><dt><a name="Irc-checkAuthenticated"><strong>checkAuthenticated</strong></a>(self, uid, allowAuthed=True, allowOper=True)</dt><dd><tt>Checks&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;has&nbsp;operator&nbsp;status&nbsp;on&nbsp;PyLink,&nbsp;raising<br>
NotAuthenticatedError&nbsp;and&nbsp;logging&nbsp;the&nbsp;access&nbsp;denial&nbsp;if&nbsp;not.</tt></dd></dl>
<dl><dt><a name="Irc-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Runs&nbsp;the&nbsp;connect&nbsp;loop&nbsp;for&nbsp;the&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>.&nbsp;This&nbsp;is&nbsp;usually&nbsp;called&nbsp;by<br>
__init__&nbsp;in&nbsp;a&nbsp;separate&nbsp;thread&nbsp;to&nbsp;allow&nbsp;multiple&nbsp;concurrent&nbsp;connections.</tt></dd></dl>
<dl><dt><a name="Irc-disconnect"><strong>disconnect</strong></a>(self)</dt><dd><tt>Handle&nbsp;disconnects&nbsp;from&nbsp;the&nbsp;remote&nbsp;server.</tt></dd></dl>
<dl><dt><a name="Irc-getHostmask"><strong>getHostmask</strong></a>(self, user, realhost=False, ip=False)</dt><dd><tt>Returns&nbsp;the&nbsp;hostmask&nbsp;of&nbsp;the&nbsp;given&nbsp;user,&nbsp;if&nbsp;present.&nbsp;If&nbsp;the&nbsp;realhost&nbsp;option<br>
is&nbsp;given,&nbsp;return&nbsp;the&nbsp;real&nbsp;host&nbsp;of&nbsp;the&nbsp;user&nbsp;instead&nbsp;of&nbsp;the&nbsp;displayed&nbsp;host.<br>
If&nbsp;the&nbsp;ip&nbsp;option&nbsp;is&nbsp;given,&nbsp;return&nbsp;the&nbsp;IP&nbsp;address&nbsp;of&nbsp;the&nbsp;user&nbsp;(this&nbsp;overrides<br>
realhost).</tt></dd></dl>
<dl><dt><a name="Irc-getServer"><strong>getServer</strong></a>(self, numeric)</dt><dd><tt>Finds&nbsp;the&nbsp;SID&nbsp;of&nbsp;the&nbsp;server&nbsp;a&nbsp;user&nbsp;is&nbsp;on.</tt></dd></dl>
<dl><dt><a name="Irc-initVars"><strong>initVars</strong></a>(self)</dt><dd><tt>(Re)sets&nbsp;an&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>&nbsp;to&nbsp;its&nbsp;default&nbsp;state.&nbsp;This&nbsp;should&nbsp;be&nbsp;called&nbsp;when<br>
an&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>&nbsp;is&nbsp;first&nbsp;created,&nbsp;and&nbsp;on&nbsp;every&nbsp;reconnection&nbsp;to&nbsp;a&nbsp;network.</tt></dd></dl>
<dl><dt><a name="Irc-isInternalClient"><strong>isInternalClient</strong></a>(self, numeric)</dt><dd><tt>Checks&nbsp;whether&nbsp;the&nbsp;given&nbsp;numeric&nbsp;is&nbsp;a&nbsp;PyLink&nbsp;Client,<br>
returning&nbsp;the&nbsp;SID&nbsp;of&nbsp;the&nbsp;server&nbsp;it's&nbsp;on&nbsp;if&nbsp;so.</tt></dd></dl>
<dl><dt><a name="Irc-isInternalServer"><strong>isInternalServer</strong></a>(self, sid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;SID&nbsp;is&nbsp;an&nbsp;internal&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="Irc-isManipulatableClient"><strong>isManipulatableClient</strong></a>(self, uid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;is&nbsp;marked&nbsp;as&nbsp;an&nbsp;internal,&nbsp;manipulatable<br>
client.&nbsp;Usually,&nbsp;automatically&nbsp;spawned&nbsp;services&nbsp;clients&nbsp;should&nbsp;have&nbsp;this<br>
set&nbsp;True&nbsp;to&nbsp;prevent&nbsp;interactions&nbsp;with&nbsp;opers&nbsp;(like&nbsp;mode&nbsp;changes)&nbsp;from<br>
causing&nbsp;desyncs.</tt></dd></dl>
<dl><dt><a name="Irc-isOper"><strong>isOper</strong></a>(self, uid, allowAuthed=True, allowOper=True)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;has&nbsp;operator&nbsp;status&nbsp;on&nbsp;PyLink.&nbsp;This&nbsp;can&nbsp;be&nbsp;achieved<br>
by&nbsp;either&nbsp;identifying&nbsp;to&nbsp;PyLink&nbsp;as&nbsp;admin&nbsp;(if&nbsp;allowAuthed&nbsp;is&nbsp;True),<br>
or&nbsp;having&nbsp;user&nbsp;mode&nbsp;+o&nbsp;set&nbsp;(if&nbsp;allowOper&nbsp;is&nbsp;True).&nbsp;At&nbsp;least&nbsp;one&nbsp;of<br>
allowAuthed&nbsp;or&nbsp;allowOper&nbsp;must&nbsp;be&nbsp;True&nbsp;for&nbsp;this&nbsp;to&nbsp;give&nbsp;any&nbsp;meaningful<br>
results.</tt></dd></dl>
<dl><dt><a name="Irc-isServiceBot"><strong>isServiceBot</strong></a>(self, uid)</dt><dd><tt>Checks&nbsp;whether&nbsp;the&nbsp;given&nbsp;UID&nbsp;is&nbsp;a&nbsp;registered&nbsp;service&nbsp;bot.&nbsp;If&nbsp;True,<br>
returns&nbsp;the&nbsp;cooresponding&nbsp;ServiceBot&nbsp;<a href="builtins.html#object">object</a>.</tt></dd></dl>
<dl><dt><a name="Irc-logSetup"><strong>logSetup</strong></a>(self)</dt><dd><tt>Initializes&nbsp;any&nbsp;channel&nbsp;loggers&nbsp;defined&nbsp;for&nbsp;the&nbsp;current&nbsp;network.</tt></dd></dl>
<dl><dt><a name="Irc-msg"><strong>msg</strong></a>(self, target, text, notice=False, source=None)</dt><dd><tt>Handy&nbsp;function&nbsp;to&nbsp;send&nbsp;messages/notices&nbsp;to&nbsp;clients.&nbsp;Source<br>
is&nbsp;optional,&nbsp;and&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink&nbsp;client&nbsp;if&nbsp;not&nbsp;specified.</tt></dd></dl>
<dl><dt><a name="Irc-nickToUid"><strong>nickToUid</strong></a>(self, nick)</dt><dd><tt>Looks&nbsp;up&nbsp;the&nbsp;UID&nbsp;of&nbsp;a&nbsp;user&nbsp;with&nbsp;the&nbsp;given&nbsp;nick,&nbsp;if&nbsp;one&nbsp;is&nbsp;present.</tt></dd></dl>
<dl><dt><a name="Irc-parseModes"><strong>parseModes</strong></a>(self, target, args)</dt><dd><tt>Parses&nbsp;a&nbsp;modestring&nbsp;list&nbsp;into&nbsp;a&nbsp;list&nbsp;of&nbsp;(mode,&nbsp;argument)&nbsp;tuples.<br>
['+mitl-o',&nbsp;'3',&nbsp;'person']&nbsp;=&gt;&nbsp;[('+m',&nbsp;None),&nbsp;('+i',&nbsp;None),&nbsp;('+t',&nbsp;None),&nbsp;('+l',&nbsp;'3'),&nbsp;('-o',&nbsp;'person')]</tt></dd></dl>
<dl><dt><a name="Irc-reply"><strong>reply</strong></a>(self, text, notice=False, source=None)</dt><dd><tt>Replies&nbsp;to&nbsp;the&nbsp;last&nbsp;caller&nbsp;in&nbsp;the&nbsp;right&nbsp;context&nbsp;(channel&nbsp;or&nbsp;PM).</tt></dd></dl>
<dl><dt><a name="Irc-reverseModes"><strong>reverseModes</strong></a>(self, target, modes, oldobj=None)</dt><dd><tt>Reverses/Inverts&nbsp;the&nbsp;mode&nbsp;string&nbsp;or&nbsp;mode&nbsp;list&nbsp;given.<br>
&nbsp;<br>
Optionally,&nbsp;an&nbsp;oldobj&nbsp;argument&nbsp;can&nbsp;be&nbsp;given&nbsp;to&nbsp;look&nbsp;at&nbsp;an&nbsp;earlier&nbsp;state&nbsp;of<br>
a&nbsp;channel/user&nbsp;<a href="builtins.html#object">object</a>,&nbsp;e.g.&nbsp;for&nbsp;checking&nbsp;the&nbsp;op&nbsp;status&nbsp;of&nbsp;a&nbsp;mode&nbsp;setter<br>
before&nbsp;their&nbsp;modes&nbsp;are&nbsp;processed&nbsp;and&nbsp;added&nbsp;to&nbsp;the&nbsp;channel&nbsp;state.<br>
&nbsp;<br>
This&nbsp;function&nbsp;allows&nbsp;both&nbsp;mode&nbsp;strings&nbsp;or&nbsp;mode&nbsp;lists.&nbsp;Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;"+mi-lk&nbsp;test&nbsp;=&gt;&nbsp;"-mi+lk&nbsp;test"<br>
&nbsp;&nbsp;&nbsp;&nbsp;"mi-k&nbsp;test&nbsp;=&gt;&nbsp;"-mi+k&nbsp;test"<br>
&nbsp;&nbsp;&nbsp;&nbsp;[('+m',&nbsp;None),&nbsp;('+r',&nbsp;None),&nbsp;('+l',&nbsp;'3'),&nbsp;('-o',&nbsp;'person')<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&gt;&nbsp;{('-m',&nbsp;None),&nbsp;('-r',&nbsp;None),&nbsp;('-l',&nbsp;None),&nbsp;('+o',&nbsp;'person')})<br>
&nbsp;&nbsp;&nbsp;&nbsp;{('s',&nbsp;None),&nbsp;('+o',&nbsp;'whoever')&nbsp;=&gt;&nbsp;{('-s',&nbsp;None),&nbsp;('-o',&nbsp;'whoever')})</tt></dd></dl>
<dl><dt><a name="Irc-run"><strong>run</strong></a>(self)</dt><dd><tt>Main&nbsp;IRC&nbsp;loop&nbsp;which&nbsp;listens&nbsp;for&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="Irc-runline"><strong>runline</strong></a>(self, line)</dt><dd><tt>Sends&nbsp;a&nbsp;command&nbsp;to&nbsp;the&nbsp;protocol&nbsp;module.</tt></dd></dl>
<dl><dt><a name="Irc-schedulePing"><strong>schedulePing</strong></a>(self)</dt><dd><tt>Schedules&nbsp;periodic&nbsp;pings&nbsp;in&nbsp;a&nbsp;loop.</tt></dd></dl>
<dl><dt><a name="Irc-send"><strong>send</strong></a>(self, data)</dt><dd><tt>Sends&nbsp;raw&nbsp;text&nbsp;to&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="Irc-toLower"><strong>toLower</strong></a>(self, text)</dt><dd><tt>Returns&nbsp;a&nbsp;lowercase&nbsp;representation&nbsp;of&nbsp;text&nbsp;based&nbsp;on&nbsp;the&nbsp;IRC&nbsp;<a href="builtins.html#object">object</a>'s<br>
casemapping&nbsp;(rfc1459&nbsp;or&nbsp;ascii).</tt></dd></dl>
<dl><dt><a name="Irc-version"><strong>version</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;detailed&nbsp;version&nbsp;string&nbsp;including&nbsp;the&nbsp;PyLink&nbsp;daemon&nbsp;version,<br>
the&nbsp;protocol&nbsp;module&nbsp;in&nbsp;use,&nbsp;and&nbsp;the&nbsp;server&nbsp;hostname.</tt></dd></dl>
<hr>
Static methods defined here:<br>
<dl><dt><a name="Irc-joinModes"><strong>joinModes</strong></a>(modes)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;(mode,&nbsp;arg)&nbsp;tuples&nbsp;in&nbsp;<a href="#Irc-parseModes">parseModes</a>()&nbsp;format,&nbsp;and<br>
joins&nbsp;them&nbsp;into&nbsp;a&nbsp;string.<br>
&nbsp;<br>
See&nbsp;testJoinModes&nbsp;in&nbsp;tests/test_utils.py&nbsp;for&nbsp;some&nbsp;examples.</tt></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="IrcChannel">class <strong>IrcChannel</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>PyLink&nbsp;IRC&nbsp;channel&nbsp;class.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="IrcChannel-__init__"><strong>__init__</strong></a>(self, name=None)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="IrcChannel-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;repr(self).</tt></dd></dl>
<dl><dt><a name="IrcChannel-deepcopy"><strong>deepcopy</strong></a>(self)</dt><dd><tt>Returns&nbsp;a&nbsp;deep&nbsp;copy&nbsp;of&nbsp;the&nbsp;channel&nbsp;<a href="builtins.html#object">object</a>.</tt></dd></dl>
<dl><dt><a name="IrcChannel-getPrefixModes"><strong>getPrefixModes</strong></a>(self, uid, prefixmodes=None)</dt><dd><tt>Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;all&nbsp;named&nbsp;prefix&nbsp;modes&nbsp;the&nbsp;given&nbsp;user&nbsp;has&nbsp;in&nbsp;the&nbsp;channel.<br>
&nbsp;<br>
Optionally,&nbsp;a&nbsp;prefixmodes&nbsp;argument&nbsp;can&nbsp;be&nbsp;given&nbsp;to&nbsp;look&nbsp;at&nbsp;an&nbsp;earlier&nbsp;state&nbsp;of<br>
the&nbsp;channel's&nbsp;prefix&nbsp;modes&nbsp;mapping,&nbsp;e.g.&nbsp;for&nbsp;checking&nbsp;the&nbsp;op&nbsp;status&nbsp;of&nbsp;a&nbsp;mode<br>
setter&nbsp;before&nbsp;their&nbsp;modes&nbsp;are&nbsp;processed&nbsp;and&nbsp;added&nbsp;to&nbsp;the&nbsp;channel&nbsp;state.</tt></dd></dl>
<dl><dt><a name="IrcChannel-isAdmin"><strong>isAdmin</strong></a>(self, uid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;is&nbsp;admin&nbsp;(&amp;)&nbsp;in&nbsp;the&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="IrcChannel-isHalfop"><strong>isHalfop</strong></a>(self, uid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;is&nbsp;halfop&nbsp;in&nbsp;the&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="IrcChannel-isHalfopPlus"><strong>isHalfopPlus</strong></a>(self, uid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;is&nbsp;halfop&nbsp;or&nbsp;above&nbsp;in&nbsp;the&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="IrcChannel-isOp"><strong>isOp</strong></a>(self, uid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;is&nbsp;op&nbsp;in&nbsp;the&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="IrcChannel-isOpPlus"><strong>isOpPlus</strong></a>(self, uid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;is&nbsp;op&nbsp;or&nbsp;above&nbsp;in&nbsp;the&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="IrcChannel-isOwner"><strong>isOwner</strong></a>(self, uid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;is&nbsp;owner&nbsp;(~)&nbsp;in&nbsp;the&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="IrcChannel-isVoice"><strong>isVoice</strong></a>(self, uid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;is&nbsp;voice&nbsp;in&nbsp;the&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="IrcChannel-isVoicePlus"><strong>isVoicePlus</strong></a>(self, uid)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;user&nbsp;is&nbsp;voice&nbsp;or&nbsp;above&nbsp;in&nbsp;the&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="IrcChannel-removeuser"><strong>removeuser</strong></a>(self, target)</dt><dd><tt>Removes&nbsp;a&nbsp;user&nbsp;from&nbsp;a&nbsp;channel.</tt></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="IrcServer">class <strong>IrcServer</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>PyLink&nbsp;IRC&nbsp;server&nbsp;class.<br>
&nbsp;<br>
uplink:&nbsp;The&nbsp;SID&nbsp;of&nbsp;this&nbsp;<a href="#IrcServer">IrcServer</a>&nbsp;instance's&nbsp;uplink.&nbsp;This&nbsp;is&nbsp;set&nbsp;to&nbsp;None<br>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for&nbsp;the&nbsp;main&nbsp;PyLink&nbsp;PseudoServer!<br>
name:&nbsp;The&nbsp;name&nbsp;of&nbsp;the&nbsp;server.<br>
internal:&nbsp;Whether&nbsp;the&nbsp;server&nbsp;is&nbsp;an&nbsp;internal&nbsp;PyLink&nbsp;PseudoServer.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="IrcServer-__init__"><strong>__init__</strong></a>(self, uplink, name, internal=False, desc='(None given)')</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="IrcServer-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;repr(self).</tt></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="IrcUser">class <strong>IrcUser</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>PyLink&nbsp;IRC&nbsp;user&nbsp;class.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="IrcUser-__init__"><strong>__init__</strong></a>(self, nick, ts, uid, ident='null', host='null', realname='PyLink dummy client', realhost='null', ip='0.0.0.0', manipulatable=False, opertype='IRC Operator')</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="IrcUser-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return&nbsp;repr(self).</tt></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="Protocol">class <strong>Protocol</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;<a href="#Protocol">Protocol</a>&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="Protocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="Protocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="Protocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="Protocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="ProtocolError">class <strong>ProtocolError</strong></a>(<a href="builtins.html#Exception">builtins.Exception</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Common&nbsp;base&nbsp;class&nbsp;for&nbsp;all&nbsp;non-exit&nbsp;exceptions.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="classes.html#ProtocolError">ProtocolError</a></dd>
<dd><a href="builtins.html#Exception">builtins.Exception</a></dd>
<dd><a href="builtins.html#BaseException">builtins.BaseException</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<hr>
Methods inherited from <a href="builtins.html#Exception">builtins.Exception</a>:<br>
<dl><dt><a name="ProtocolError-__init__"><strong>__init__</strong></a>(self, /, *args, **kwargs)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="ProtocolError-__new__"><strong>__new__</strong></a>(*args, **kwargs)<font color="#909090"><font face="helvetica, arial"> from <a href="builtins.html#type">builtins.type</a></font></font></dt><dd><tt>Create&nbsp;and&nbsp;return&nbsp;a&nbsp;new&nbsp;<a href="builtins.html#object">object</a>.&nbsp;&nbsp;See&nbsp;help(type)&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<hr>
Methods inherited from <a href="builtins.html#BaseException">builtins.BaseException</a>:<br>
<dl><dt><a name="ProtocolError-__delattr__"><strong>__delattr__</strong></a>(self, name, /)</dt><dd><tt>Implement&nbsp;delattr(self,&nbsp;name).</tt></dd></dl>
<dl><dt><a name="ProtocolError-__getattribute__"><strong>__getattribute__</strong></a>(self, name, /)</dt><dd><tt>Return&nbsp;getattr(self,&nbsp;name).</tt></dd></dl>
<dl><dt><a name="ProtocolError-__reduce__"><strong>__reduce__</strong></a>(...)</dt><dd><tt>helper&nbsp;for&nbsp;pickle</tt></dd></dl>
<dl><dt><a name="ProtocolError-__repr__"><strong>__repr__</strong></a>(self, /)</dt><dd><tt>Return&nbsp;repr(self).</tt></dd></dl>
<dl><dt><a name="ProtocolError-__setattr__"><strong>__setattr__</strong></a>(self, name, value, /)</dt><dd><tt>Implement&nbsp;setattr(self,&nbsp;name,&nbsp;value).</tt></dd></dl>
<dl><dt><a name="ProtocolError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
<dl><dt><a name="ProtocolError-__str__"><strong>__str__</strong></a>(self, /)</dt><dd><tt>Return&nbsp;str(self).</tt></dd></dl>
<dl><dt><a name="ProtocolError-with_traceback"><strong>with_traceback</strong></a>(...)</dt><dd><tt><a href="builtins.html#Exception">Exception</a>.<a href="#ProtocolError-with_traceback">with_traceback</a>(tb)&nbsp;--<br>
set&nbsp;self.<strong>__traceback__</strong>&nbsp;to&nbsp;tb&nbsp;and&nbsp;return&nbsp;self.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="builtins.html#BaseException">builtins.BaseException</a>:<br>
<dl><dt><strong>__cause__</strong></dt>
<dd><tt>exception&nbsp;cause</tt></dd>
</dl>
<dl><dt><strong>__context__</strong></dt>
<dd><tt>exception&nbsp;context</tt></dd>
</dl>
<dl><dt><strong>__dict__</strong></dt>
</dl>
<dl><dt><strong>__suppress_context__</strong></dt>
</dl>
<dl><dt><strong>__traceback__</strong></dt>
</dl>
<dl><dt><strong>args</strong></dt>
</dl>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>conf</strong> = {'bot': {'nick': 'PyLink', 'realname': 'PyLink Service Client', 'serverdesc': 'PyLink unit tests', 'user': 'pylink'}, 'logging': {'stdout': 'CRITICAL'}, 'servers': defaultdict(&lt;function &lt;lambda&gt; at 0x7fcfaf78de18&gt;, {})}<br>
<strong>confname</strong> = 'testconf'<br>
<strong>curdir</strong> = '/home/gl/pylink'<br>
<strong>files</strong> = None<br>
<strong>log</strong> = &lt;logging.RootLogger object&gt;<br>
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
<strong>logformatter</strong> = &lt;logging.Formatter object&gt;<br>
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
</body></html>

View File

@ -1,54 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module conf</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>conf</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/conf.py">/home/gl/pylink/conf.py</a></font></td></tr></table>
<p><tt>conf.py&nbsp;-&nbsp;PyLink&nbsp;configuration&nbsp;core.<br>
&nbsp;<br>
This&nbsp;module&nbsp;is&nbsp;used&nbsp;to&nbsp;access&nbsp;the&nbsp;complete&nbsp;configuration&nbsp;for&nbsp;the&nbsp;current<br>
PyLink&nbsp;instance.&nbsp;It&nbsp;will&nbsp;load&nbsp;the&nbsp;config&nbsp;on&nbsp;first&nbsp;import,&nbsp;taking&nbsp;the<br>
configuration&nbsp;file&nbsp;name&nbsp;from&nbsp;the&nbsp;first&nbsp;command-line&nbsp;argument,&nbsp;but&nbsp;defaulting<br>
to&nbsp;'config.yml'&nbsp;if&nbsp;this&nbsp;isn't&nbsp;given.<br>
&nbsp;<br>
If&nbsp;world.testing&nbsp;is&nbsp;set&nbsp;to&nbsp;True,&nbsp;it&nbsp;will&nbsp;return&nbsp;a&nbsp;preset&nbsp;testing&nbsp;configuration<br>
instead.<br>
&nbsp;<br>
This&nbsp;module&nbsp;also&nbsp;provides&nbsp;simple&nbsp;checks&nbsp;for&nbsp;validating&nbsp;and&nbsp;loading&nbsp;YAML-format<br>
configurations&nbsp;from&nbsp;arbitrary&nbsp;files.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="sys.html">sys</a><br>
</td><td width="25%" valign=top><a href="world.html">world</a><br>
</td><td width="25%" valign=top><a href="yaml.html">yaml</a><br>
</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#eeaa77">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt><a name="-loadConf"><strong>loadConf</strong></a>(fname, errors_fatal=True)</dt><dd><tt>Loads&nbsp;a&nbsp;PyLink&nbsp;configuration&nbsp;file&nbsp;from&nbsp;the&nbsp;filename&nbsp;given.</tt></dd></dl>
<dl><dt><a name="-validateConf"><strong>validateConf</strong></a>(conf)</dt><dd><tt>Validates&nbsp;a&nbsp;parsed&nbsp;configuration&nbsp;dict.</tt></dd></dl>
</td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>conf</strong> = {'bot': {'nick': 'PyLink', 'realname': 'PyLink Service Client', 'serverdesc': 'PyLink unit tests', 'user': 'pylink'}, 'logging': {'stdout': 'CRITICAL'}, 'servers': defaultdict(&lt;function &lt;lambda&gt; at 0x7fee74c229d8&gt;, {})}<br>
<strong>confname</strong> = 'testconf'<br>
<strong>fname</strong> = None<br>
<strong>testconf</strong> = {'bot': {'nick': 'PyLink', 'realname': 'PyLink Service Client', 'serverdesc': 'PyLink unit tests', 'user': 'pylink'}, 'logging': {'stdout': 'CRITICAL'}, 'servers': defaultdict(&lt;function &lt;lambda&gt; at 0x7fee74c229d8&gt;, {})}</td></tr></table>
</body></html>

View File

@ -1,77 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module coreplugin</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>coreplugin</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/coreplugin.py">/home/gl/pylink/coreplugin.py</a></font></td></tr></table>
<p><tt>coreplugin.py&nbsp;-&nbsp;Implements&nbsp;core&nbsp;PyLink&nbsp;functions&nbsp;as&nbsp;a&nbsp;plugin.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="classes.html">classes</a><br>
<a href="conf.html">conf</a><br>
</td><td width="25%" valign=top><a href="gc.html">gc</a><br>
<a href="os.html">os</a><br>
</td><td width="25%" valign=top><a href="signal.html">signal</a><br>
<a href="sys.html">sys</a><br>
</td><td width="25%" valign=top><a href="utils.html">utils</a><br>
<a href="world.html">world</a><br>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#eeaa77">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt><a name="-handle_commands"><strong>handle_commands</strong></a>(irc, source, command, args)</dt><dd><tt>Handle&nbsp;commands&nbsp;sent&nbsp;to&nbsp;the&nbsp;PyLink&nbsp;service&nbsp;bots&nbsp;(PRIVMSG).</tt></dd></dl>
<dl><dt><a name="-handle_disconnect"><strong>handle_disconnect</strong></a>(irc, source, command, args)</dt><dd><tt>Handles&nbsp;network&nbsp;disconnections.</tt></dd></dl>
<dl><dt><a name="-handle_endburst"><strong>handle_endburst</strong></a>(irc, source, command, args)</dt><dd><tt>Handles&nbsp;network&nbsp;bursts.</tt></dd></dl>
<dl><dt><a name="-handle_kick"><strong>handle_kick</strong></a>(irc, source, command, args)</dt><dd><tt>Handle&nbsp;KICKs&nbsp;to&nbsp;the&nbsp;PyLink&nbsp;service&nbsp;bots,&nbsp;rejoining&nbsp;channels&nbsp;as&nbsp;needed.</tt></dd></dl>
<dl><dt><a name="-handle_kill"><strong>handle_kill</strong></a>(irc, source, command, args)</dt><dd><tt>Handle&nbsp;KILLs&nbsp;to&nbsp;PyLink&nbsp;service&nbsp;bots,&nbsp;respawning&nbsp;them&nbsp;as&nbsp;needed.</tt></dd></dl>
<dl><dt><a name="-handle_mode"><strong>handle_mode</strong></a>(irc, source, command, args)</dt><dd><tt>Protect&nbsp;against&nbsp;forced&nbsp;deoper&nbsp;attempts.</tt></dd></dl>
<dl><dt><a name="-handle_operup"><strong>handle_operup</strong></a>(irc, source, command, args)</dt><dd><tt>Logs&nbsp;successful&nbsp;oper-ups&nbsp;on&nbsp;networks.</tt></dd></dl>
<dl><dt><a name="-handle_services_login"><strong>handle_services_login</strong></a>(irc, source, command, args)</dt><dd><tt>Sets&nbsp;services&nbsp;login&nbsp;status&nbsp;for&nbsp;users.</tt></dd></dl>
<dl><dt><a name="-handle_version"><strong>handle_version</strong></a>(irc, source, command, args)</dt><dd><tt>Handles&nbsp;requests&nbsp;for&nbsp;the&nbsp;PyLink&nbsp;server&nbsp;version.</tt></dd></dl>
<dl><dt><a name="-handle_whois"><strong>handle_whois</strong></a>(irc, source, command, args)</dt><dd><tt>Handle&nbsp;WHOIS&nbsp;queries,&nbsp;for&nbsp;IRCds&nbsp;that&nbsp;send&nbsp;them&nbsp;across&nbsp;servers&nbsp;(charybdis,&nbsp;UnrealIRCd;&nbsp;NOT&nbsp;InspIRCd).</tt></dd></dl>
<dl><dt><a name="-identify"><strong>identify</strong></a>(irc, source, args)</dt><dd><tt>&lt;username&gt;&nbsp;&lt;password&gt;<br>
&nbsp;<br>
Logs&nbsp;in&nbsp;to&nbsp;PyLink&nbsp;using&nbsp;the&nbsp;configured&nbsp;administrator&nbsp;account.</tt></dd></dl>
<dl><dt><a name="-load"><strong>load</strong></a>(irc, source, args)</dt><dd><tt>&lt;plugin&nbsp;name&gt;.<br>
&nbsp;<br>
Loads&nbsp;a&nbsp;plugin&nbsp;from&nbsp;the&nbsp;plugin&nbsp;folder.</tt></dd></dl>
<dl><dt><a name="-rehash"><strong>rehash</strong></a>(irc, source, args)</dt><dd><tt>takes&nbsp;no&nbsp;arguments.<br>
&nbsp;<br>
Reloads&nbsp;the&nbsp;configuration&nbsp;file&nbsp;for&nbsp;PyLink,&nbsp;(dis)connecting&nbsp;added/removed&nbsp;networks.<br>
Plugins&nbsp;must&nbsp;be&nbsp;manually&nbsp;reloaded.</tt></dd></dl>
<dl><dt><a name="-reload"><strong>reload</strong></a>(irc, source, args)</dt><dd><tt>&lt;plugin&nbsp;name&gt;.<br>
&nbsp;<br>
Loads&nbsp;a&nbsp;plugin&nbsp;from&nbsp;the&nbsp;plugin&nbsp;folder.</tt></dd></dl>
<dl><dt><a name="-shutdown"><strong>shutdown</strong></a>(irc, source, args)</dt><dd><tt>takes&nbsp;no&nbsp;arguments.<br>
&nbsp;<br>
Exits&nbsp;PyLink&nbsp;by&nbsp;disconnecting&nbsp;all&nbsp;networks.</tt></dd></dl>
<dl><dt><a name="-sighup_handler"><strong>sighup_handler</strong></a>(_signo, _stack_frame)</dt><dd><tt>Handles&nbsp;SIGHUP&nbsp;by&nbsp;rehashing&nbsp;the&nbsp;PyLink&nbsp;daemon.</tt></dd></dl>
<dl><dt><a name="-sigterm_handler"><strong>sigterm_handler</strong></a>(_signo, _stack_frame)</dt><dd><tt>Handles&nbsp;SIGTERM&nbsp;gracefully&nbsp;by&nbsp;shutting&nbsp;down&nbsp;the&nbsp;PyLink&nbsp;daemon.</tt></dd></dl>
<dl><dt><a name="-spawn_service"><strong>spawn_service</strong></a>(irc, source, command, args)</dt><dd><tt>Handles&nbsp;new&nbsp;service&nbsp;bot&nbsp;introductions.</tt></dd></dl>
<dl><dt><a name="-unload"><strong>unload</strong></a>(irc, source, args)</dt><dd><tt>&lt;plugin&nbsp;name&gt;.<br>
&nbsp;<br>
Unloads&nbsp;a&nbsp;currently&nbsp;loaded&nbsp;plugin.</tt></dd></dl>
</td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>log</strong> = &lt;logging.RootLogger object&gt;<br>
<strong>myident</strong> = 'pylink'<br>
<strong>mynick</strong> = 'PyLink'</td></tr></table>
</body></html>

View File

@ -1,470 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module hybrid</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>hybrid</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/protocols/hybrid.py">/home/gl/pylink/protocols/hybrid.py</a></font></td></tr></table>
<p></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="hashlib.html">hashlib</a><br>
<a href="inspect.html">inspect</a><br>
<a href="logging.html">logging</a><br>
<a href="os.html">os</a><br>
</td><td width="25%" valign=top><a href="re.html">re</a><br>
<a href="socket.html">socket</a><br>
<a href="ssl.html">ssl</a><br>
<a href="string.html">string</a><br>
</td><td width="25%" valign=top><a href="structures.html">structures</a><br>
<a href="sys.html">sys</a><br>
<a href="threading.html">threading</a><br>
<a href="time.html">time</a><br>
</td><td width="25%" valign=top><a href="utils.html">utils</a><br>
<a href="world.html">world</a><br>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="ts6.html#TS6Protocol">ts6.TS6Protocol</a>(<a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="hybrid.html#HybridProtocol">HybridProtocol</a>
</font></dt></dl>
</dd>
</dl>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><strong>Class</strong> = <a name="Class">class HybridProtocol</a>(<a href="ts6.html#TS6Protocol">ts6.TS6Protocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;Protocol&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="hybrid.html#HybridProtocol">HybridProtocol</a></dd>
<dd><a href="ts6.html#TS6Protocol">ts6.TS6Protocol</a></dd>
<dd><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="HybridProtocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_capab"><strong>handle_capab</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;CAPAB&nbsp;command,&nbsp;used&nbsp;for&nbsp;TS6&nbsp;capability&nbsp;negotiation.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_eob"><strong>handle_eob</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="HybridProtocol-handle_svsmode"><strong>handle_svsmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;SVSMODE,&nbsp;which&nbsp;is&nbsp;used&nbsp;for&nbsp;sending&nbsp;services&nbsp;metadata<br>
(vhosts,&nbsp;account&nbsp;logins),&nbsp;and&nbsp;other&nbsp;forced&nbsp;usermode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_tburst"><strong>handle_tburst</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;topic&nbsp;burst&nbsp;(TBURST)&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_uid"><strong>handle_uid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;Hybrid-style&nbsp;UID&nbsp;commands&nbsp;(user&nbsp;introduction).&nbsp;This&nbsp;is&nbsp;INCOMPATIBLE<br>
with&nbsp;standard&nbsp;TS6&nbsp;implementations,&nbsp;as&nbsp;the&nbsp;arguments&nbsp;are&nbsp;slightly&nbsp;different.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-spawnClient"><strong>spawnClient</strong></a>(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None, manipulatable=False)</dt><dd><tt>Spawns&nbsp;a&nbsp;new&nbsp;client&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
&nbsp;<br>
Note:&nbsp;No&nbsp;nick&nbsp;collision&nbsp;/&nbsp;valid&nbsp;nickname&nbsp;checks&nbsp;are&nbsp;done&nbsp;here;&nbsp;it&nbsp;is<br>
up&nbsp;to&nbsp;plugins&nbsp;to&nbsp;make&nbsp;sure&nbsp;they&nbsp;don't&nbsp;introduce&nbsp;anything&nbsp;invalid.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;topic&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;This&nbsp;is&nbsp;usually&nbsp;used&nbsp;on&nbsp;burst.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates&nbsp;the&nbsp;ident,&nbsp;host,&nbsp;or&nbsp;realname&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="ts6.html#TS6Protocol">ts6.TS6Protocol</a>:<br>
<dl><dt><a name="HybridProtocol-handle_472"><strong>handle_472</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;incoming&nbsp;472&nbsp;numeric.<br>
&nbsp;<br>
472&nbsp;is&nbsp;sent&nbsp;to&nbsp;us&nbsp;when&nbsp;one&nbsp;of&nbsp;our&nbsp;clients&nbsp;tries&nbsp;to&nbsp;set&nbsp;a&nbsp;mode&nbsp;the&nbsp;uplink<br>
server&nbsp;doesn't&nbsp;support.&nbsp;In&nbsp;this&nbsp;case,&nbsp;we'll&nbsp;raise&nbsp;a&nbsp;warning&nbsp;to&nbsp;alert<br>
the&nbsp;administrator&nbsp;that&nbsp;certain&nbsp;extensions&nbsp;should&nbsp;be&nbsp;loaded&nbsp;for&nbsp;the&nbsp;best<br>
compatibility.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_bmask"><strong>handle_bmask</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;BMASK&nbsp;commands&nbsp;(ban&nbsp;propagation&nbsp;on&nbsp;burst).</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;CHGHOST&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_encap"><strong>handle_encap</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;ENCAP&nbsp;command&nbsp;-&nbsp;encapsulated&nbsp;TS6&nbsp;commands&nbsp;with&nbsp;a&nbsp;variety&nbsp;of<br>
subcommands&nbsp;used&nbsp;for&nbsp;different&nbsp;purposes.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_euid"><strong>handle_euid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;EUID&nbsp;commands&nbsp;(user&nbsp;introduction).</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;INVITEs.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;channel&nbsp;JOINs.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;user&nbsp;mode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_pass"><strong>handle_pass</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;PASS&nbsp;command,&nbsp;used&nbsp;to&nbsp;send&nbsp;the&nbsp;server's&nbsp;SID&nbsp;and&nbsp;negotiate<br>
passwords&nbsp;on&nbsp;connect.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PING&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PONG&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;1)&nbsp;incoming&nbsp;legacy&nbsp;(no&nbsp;SID)&nbsp;server&nbsp;introductions,<br>
2)&nbsp;Sending&nbsp;server&nbsp;data&nbsp;in&nbsp;initial&nbsp;connection.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;server&nbsp;introductions.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SJOIN&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_tb"><strong>handle_tb</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;topic&nbsp;burst&nbsp;(TB)&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_tmode"><strong>handle_tmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TMODE&nbsp;commands&nbsp;(channel&nbsp;mode&nbsp;change).</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;WHOIS&nbsp;commands.<br>
&nbsp;<br>
Note:&nbsp;The&nbsp;core&nbsp;of&nbsp;WHOIS&nbsp;handling&nbsp;is&nbsp;done&nbsp;by&nbsp;coreplugin.py<br>
(IRCd-independent),&nbsp;and&nbsp;not&nbsp;here.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends&nbsp;an&nbsp;INVITE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client..</tt></dd></dl>
<dl><dt><a name="HybridProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins&nbsp;a&nbsp;PyLink&nbsp;client&nbsp;to&nbsp;a&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends&nbsp;a&nbsp;kill&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;KNOCK&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends&nbsp;mode&nbsp;changes&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends&nbsp;a&nbsp;PING&nbsp;to&nbsp;a&nbsp;target&nbsp;server.&nbsp;Periodic&nbsp;PINGs&nbsp;are&nbsp;sent&nbsp;to&nbsp;our&nbsp;uplink<br>
automatically&nbsp;by&nbsp;the&nbsp;Irc()&nbsp;internals;&nbsp;plugins&nbsp;shouldn't&nbsp;have&nbsp;to&nbsp;use&nbsp;this.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends&nbsp;an&nbsp;SJOIN&nbsp;for&nbsp;a&nbsp;group&nbsp;of&nbsp;users&nbsp;to&nbsp;a&nbsp;channel.<br>
&nbsp;<br>
The&nbsp;sender&nbsp;should&nbsp;always&nbsp;be&nbsp;a&nbsp;Server&nbsp;ID&nbsp;(SID).&nbsp;TS&nbsp;is&nbsp;optional,&nbsp;and&nbsp;defaults<br>
to&nbsp;the&nbsp;one&nbsp;we've&nbsp;stored&nbsp;in&nbsp;the&nbsp;channel&nbsp;state&nbsp;if&nbsp;not&nbsp;given.<br>
&lt;users&gt;&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;(prefix&nbsp;mode,&nbsp;UID)&nbsp;pairs:<br>
&nbsp;<br>
Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Class-sjoin">sjoin</a>('100',&nbsp;'#test',&nbsp;[('',&nbsp;'100AAABBC'),&nbsp;('o',&nbsp;100AAABBB'),&nbsp;('v',&nbsp;'100AAADDD')])<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Class-sjoin">sjoin</a>(self.<strong>irc</strong>.sid,&nbsp;'#test',&nbsp;[('o',&nbsp;self.<strong>irc</strong>.pseudoclient.uid)])</tt></dd></dl>
<hr>
Methods inherited from <a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>:<br>
<dl><dt><a name="HybridProtocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;ERROR&nbsp;messages&nbsp;-&nbsp;these&nbsp;mean&nbsp;that&nbsp;our&nbsp;uplink&nbsp;has&nbsp;disconnected&nbsp;us!</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;TS6&nbsp;protocols.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions<br>
elsewhere&nbsp;defined&nbsp;protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes<br>
from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;UIDs&nbsp;and&nbsp;SIDs&nbsp;respectively,<br>
whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;will&nbsp;have&nbsp;them&nbsp;set&nbsp;to<br>
the&nbsp;SID&nbsp;of&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;NICK&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_notice"><strong>handle_notice</strong></a> = handle_privmsg(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PART&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUIT&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SAVE&nbsp;messages,&nbsp;used&nbsp;to&nbsp;handle&nbsp;nick&nbsp;collisions.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SQUITs&nbsp;(netsplits).</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TOPIC&nbsp;changes&nbsp;from&nbsp;clients.&nbsp;For&nbsp;topic&nbsp;bursts,<br>
TB&nbsp;(TS6/charybdis)&nbsp;and&nbsp;FTOPIC&nbsp;(InspIRCd)&nbsp;are&nbsp;used&nbsp;instead.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;requests&nbsp;for&nbsp;the&nbsp;PyLink&nbsp;server&nbsp;version.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client,&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar&nbsp;to&nbsp;<a href="#Class-parseArgs">parseArgs</a>(),&nbsp;but&nbsp;stripping&nbsp;leading&nbsp;colons&nbsp;from&nbsp;the&nbsp;first&nbsp;argument<br>
of&nbsp;a&nbsp;line&nbsp;(usually&nbsp;the&nbsp;sender&nbsp;field).</tt></dd></dl>
<dl><dt><a name="HybridProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
Note:&nbsp;TS6&nbsp;doesn't&nbsp;use&nbsp;a&nbsp;specific&nbsp;ENDBURST&nbsp;command,&nbsp;so&nbsp;the&nbsp;endburst_delay<br>
option&nbsp;will&nbsp;be&nbsp;ignored&nbsp;if&nbsp;given.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="HybridProtocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="HybridProtocol">class <strong>HybridProtocol</strong></a>(<a href="ts6.html#TS6Protocol">ts6.TS6Protocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;Protocol&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="hybrid.html#HybridProtocol">HybridProtocol</a></dd>
<dd><a href="ts6.html#TS6Protocol">ts6.TS6Protocol</a></dd>
<dd><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="HybridProtocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_capab"><strong>handle_capab</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;CAPAB&nbsp;command,&nbsp;used&nbsp;for&nbsp;TS6&nbsp;capability&nbsp;negotiation.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_eob"><strong>handle_eob</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="HybridProtocol-handle_svsmode"><strong>handle_svsmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;SVSMODE,&nbsp;which&nbsp;is&nbsp;used&nbsp;for&nbsp;sending&nbsp;services&nbsp;metadata<br>
(vhosts,&nbsp;account&nbsp;logins),&nbsp;and&nbsp;other&nbsp;forced&nbsp;usermode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_tburst"><strong>handle_tburst</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;topic&nbsp;burst&nbsp;(TBURST)&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_uid"><strong>handle_uid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;Hybrid-style&nbsp;UID&nbsp;commands&nbsp;(user&nbsp;introduction).&nbsp;This&nbsp;is&nbsp;INCOMPATIBLE<br>
with&nbsp;standard&nbsp;TS6&nbsp;implementations,&nbsp;as&nbsp;the&nbsp;arguments&nbsp;are&nbsp;slightly&nbsp;different.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-spawnClient"><strong>spawnClient</strong></a>(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None, manipulatable=False)</dt><dd><tt>Spawns&nbsp;a&nbsp;new&nbsp;client&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
&nbsp;<br>
Note:&nbsp;No&nbsp;nick&nbsp;collision&nbsp;/&nbsp;valid&nbsp;nickname&nbsp;checks&nbsp;are&nbsp;done&nbsp;here;&nbsp;it&nbsp;is<br>
up&nbsp;to&nbsp;plugins&nbsp;to&nbsp;make&nbsp;sure&nbsp;they&nbsp;don't&nbsp;introduce&nbsp;anything&nbsp;invalid.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;topic&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;This&nbsp;is&nbsp;usually&nbsp;used&nbsp;on&nbsp;burst.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates&nbsp;the&nbsp;ident,&nbsp;host,&nbsp;or&nbsp;realname&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="ts6.html#TS6Protocol">ts6.TS6Protocol</a>:<br>
<dl><dt><a name="HybridProtocol-handle_472"><strong>handle_472</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;incoming&nbsp;472&nbsp;numeric.<br>
&nbsp;<br>
472&nbsp;is&nbsp;sent&nbsp;to&nbsp;us&nbsp;when&nbsp;one&nbsp;of&nbsp;our&nbsp;clients&nbsp;tries&nbsp;to&nbsp;set&nbsp;a&nbsp;mode&nbsp;the&nbsp;uplink<br>
server&nbsp;doesn't&nbsp;support.&nbsp;In&nbsp;this&nbsp;case,&nbsp;we'll&nbsp;raise&nbsp;a&nbsp;warning&nbsp;to&nbsp;alert<br>
the&nbsp;administrator&nbsp;that&nbsp;certain&nbsp;extensions&nbsp;should&nbsp;be&nbsp;loaded&nbsp;for&nbsp;the&nbsp;best<br>
compatibility.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_bmask"><strong>handle_bmask</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;BMASK&nbsp;commands&nbsp;(ban&nbsp;propagation&nbsp;on&nbsp;burst).</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;CHGHOST&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_encap"><strong>handle_encap</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;ENCAP&nbsp;command&nbsp;-&nbsp;encapsulated&nbsp;TS6&nbsp;commands&nbsp;with&nbsp;a&nbsp;variety&nbsp;of<br>
subcommands&nbsp;used&nbsp;for&nbsp;different&nbsp;purposes.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_euid"><strong>handle_euid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;EUID&nbsp;commands&nbsp;(user&nbsp;introduction).</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;INVITEs.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;channel&nbsp;JOINs.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;user&nbsp;mode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_pass"><strong>handle_pass</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;PASS&nbsp;command,&nbsp;used&nbsp;to&nbsp;send&nbsp;the&nbsp;server's&nbsp;SID&nbsp;and&nbsp;negotiate<br>
passwords&nbsp;on&nbsp;connect.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PING&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PONG&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;1)&nbsp;incoming&nbsp;legacy&nbsp;(no&nbsp;SID)&nbsp;server&nbsp;introductions,<br>
2)&nbsp;Sending&nbsp;server&nbsp;data&nbsp;in&nbsp;initial&nbsp;connection.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;server&nbsp;introductions.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SJOIN&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_tb"><strong>handle_tb</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;topic&nbsp;burst&nbsp;(TB)&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_tmode"><strong>handle_tmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TMODE&nbsp;commands&nbsp;(channel&nbsp;mode&nbsp;change).</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;WHOIS&nbsp;commands.<br>
&nbsp;<br>
Note:&nbsp;The&nbsp;core&nbsp;of&nbsp;WHOIS&nbsp;handling&nbsp;is&nbsp;done&nbsp;by&nbsp;coreplugin.py<br>
(IRCd-independent),&nbsp;and&nbsp;not&nbsp;here.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends&nbsp;an&nbsp;INVITE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client..</tt></dd></dl>
<dl><dt><a name="HybridProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins&nbsp;a&nbsp;PyLink&nbsp;client&nbsp;to&nbsp;a&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends&nbsp;a&nbsp;kill&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;KNOCK&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends&nbsp;mode&nbsp;changes&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends&nbsp;a&nbsp;PING&nbsp;to&nbsp;a&nbsp;target&nbsp;server.&nbsp;Periodic&nbsp;PINGs&nbsp;are&nbsp;sent&nbsp;to&nbsp;our&nbsp;uplink<br>
automatically&nbsp;by&nbsp;the&nbsp;Irc()&nbsp;internals;&nbsp;plugins&nbsp;shouldn't&nbsp;have&nbsp;to&nbsp;use&nbsp;this.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends&nbsp;an&nbsp;SJOIN&nbsp;for&nbsp;a&nbsp;group&nbsp;of&nbsp;users&nbsp;to&nbsp;a&nbsp;channel.<br>
&nbsp;<br>
The&nbsp;sender&nbsp;should&nbsp;always&nbsp;be&nbsp;a&nbsp;Server&nbsp;ID&nbsp;(SID).&nbsp;TS&nbsp;is&nbsp;optional,&nbsp;and&nbsp;defaults<br>
to&nbsp;the&nbsp;one&nbsp;we've&nbsp;stored&nbsp;in&nbsp;the&nbsp;channel&nbsp;state&nbsp;if&nbsp;not&nbsp;given.<br>
&lt;users&gt;&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;(prefix&nbsp;mode,&nbsp;UID)&nbsp;pairs:<br>
&nbsp;<br>
Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#HybridProtocol-sjoin">sjoin</a>('100',&nbsp;'#test',&nbsp;[('',&nbsp;'100AAABBC'),&nbsp;('o',&nbsp;100AAABBB'),&nbsp;('v',&nbsp;'100AAADDD')])<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#HybridProtocol-sjoin">sjoin</a>(self.<strong>irc</strong>.sid,&nbsp;'#test',&nbsp;[('o',&nbsp;self.<strong>irc</strong>.pseudoclient.uid)])</tt></dd></dl>
<hr>
Methods inherited from <a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>:<br>
<dl><dt><a name="HybridProtocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;ERROR&nbsp;messages&nbsp;-&nbsp;these&nbsp;mean&nbsp;that&nbsp;our&nbsp;uplink&nbsp;has&nbsp;disconnected&nbsp;us!</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;TS6&nbsp;protocols.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions<br>
elsewhere&nbsp;defined&nbsp;protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes<br>
from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;UIDs&nbsp;and&nbsp;SIDs&nbsp;respectively,<br>
whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;will&nbsp;have&nbsp;them&nbsp;set&nbsp;to<br>
the&nbsp;SID&nbsp;of&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;NICK&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_notice"><strong>handle_notice</strong></a> = handle_privmsg(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PART&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUIT&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SAVE&nbsp;messages,&nbsp;used&nbsp;to&nbsp;handle&nbsp;nick&nbsp;collisions.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SQUITs&nbsp;(netsplits).</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TOPIC&nbsp;changes&nbsp;from&nbsp;clients.&nbsp;For&nbsp;topic&nbsp;bursts,<br>
TB&nbsp;(TS6/charybdis)&nbsp;and&nbsp;FTOPIC&nbsp;(InspIRCd)&nbsp;are&nbsp;used&nbsp;instead.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;requests&nbsp;for&nbsp;the&nbsp;PyLink&nbsp;server&nbsp;version.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client,&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar&nbsp;to&nbsp;<a href="#HybridProtocol-parseArgs">parseArgs</a>(),&nbsp;but&nbsp;stripping&nbsp;leading&nbsp;colons&nbsp;from&nbsp;the&nbsp;first&nbsp;argument<br>
of&nbsp;a&nbsp;line&nbsp;(usually&nbsp;the&nbsp;sender&nbsp;field).</tt></dd></dl>
<dl><dt><a name="HybridProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
Note:&nbsp;TS6&nbsp;doesn't&nbsp;use&nbsp;a&nbsp;specific&nbsp;ENDBURST&nbsp;command,&nbsp;so&nbsp;the&nbsp;endburst_delay<br>
option&nbsp;will&nbsp;be&nbsp;ignored&nbsp;if&nbsp;given.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="HybridProtocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="HybridProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>conf</strong> = {'bot': {'nick': 'PyLink', 'realname': 'PyLink Service Client', 'serverdesc': 'PyLink unit tests', 'user': 'pylink'}, 'logging': {'stdout': 'CRITICAL'}, 'servers': defaultdict(&lt;function &lt;lambda&gt; at 0x7f737f074378&gt;, {})}<br>
<strong>confname</strong> = 'testconf'<br>
<strong>curdir</strong> = '/home/gl/pylink'<br>
<strong>files</strong> = None<br>
<strong>log</strong> = &lt;logging.RootLogger object&gt;<br>
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
<strong>logformatter</strong> = &lt;logging.Formatter object&gt;<br>
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
</body></html>

View File

@ -1,462 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module inspircd</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>inspircd</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/protocols/inspircd.py">/home/gl/pylink/protocols/inspircd.py</a></font></td></tr></table>
<p><tt>inspircd.py:&nbsp;InspIRCd&nbsp;2.x&nbsp;protocol&nbsp;module&nbsp;for&nbsp;PyLink.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="hashlib.html">hashlib</a><br>
<a href="inspect.html">inspect</a><br>
<a href="logging.html">logging</a><br>
<a href="os.html">os</a><br>
</td><td width="25%" valign=top><a href="re.html">re</a><br>
<a href="socket.html">socket</a><br>
<a href="ssl.html">ssl</a><br>
<a href="string.html">string</a><br>
</td><td width="25%" valign=top><a href="structures.html">structures</a><br>
<a href="sys.html">sys</a><br>
<a href="threading.html">threading</a><br>
<a href="time.html">time</a><br>
</td><td width="25%" valign=top><a href="utils.html">utils</a><br>
<a href="world.html">world</a><br>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>(<a href="classes.html#Protocol">classes.Protocol</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="inspircd.html#InspIRCdProtocol">InspIRCdProtocol</a>
</font></dt></dl>
</dd>
</dl>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><strong>Class</strong> = <a name="Class">class InspIRCdProtocol</a>(<a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;Protocol&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="inspircd.html#InspIRCdProtocol">InspIRCdProtocol</a></dd>
<dd><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="InspIRCdProtocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_capab"><strong>handle_capab</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;CAPAB&nbsp;command,&nbsp;used&nbsp;for&nbsp;capability&nbsp;negotiation&nbsp;with&nbsp;our<br>
uplink.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_encap"><strong>handle_encap</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;encapsulated&nbsp;commands&nbsp;(ENCAP).&nbsp;Hook&nbsp;arguments<br>
returned&nbsp;by&nbsp;this&nbsp;should&nbsp;have&nbsp;a&nbsp;parse_as&nbsp;field,&nbsp;that&nbsp;sets&nbsp;the&nbsp;correct<br>
hook&nbsp;name&nbsp;for&nbsp;the&nbsp;message.<br>
&nbsp;<br>
For&nbsp;InspIRCd,&nbsp;the&nbsp;only&nbsp;ENCAP&nbsp;command&nbsp;we&nbsp;handle&nbsp;right&nbsp;now&nbsp;is&nbsp;KNOCK.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_endburst"><strong>handle_endburst</strong></a>(self, numeric, command, args)</dt><dd><tt>ENDBURST&nbsp;handler;&nbsp;sends&nbsp;a&nbsp;hook&nbsp;with&nbsp;empty&nbsp;contents.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_fhost"><strong>handle_fhost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;FHOST,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;hostname&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_fident"><strong>handle_fident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;FIDENT,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;ident&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_fjoin"><strong>handle_fjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;FJOIN&nbsp;commands&nbsp;(InspIRCd&nbsp;equivalent&nbsp;of&nbsp;JOIN/SJOIN).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_fmode"><strong>handle_fmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;FMODE&nbsp;command,&nbsp;used&nbsp;for&nbsp;channel&nbsp;mode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_fname"><strong>handle_fname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;FNAME,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;real&nbsp;name/gecos&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_ftopic"><strong>handle_ftopic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;FTOPIC&nbsp;(sets&nbsp;topic&nbsp;on&nbsp;burst).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_idle"><strong>handle_idle</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;IDLE&nbsp;command,&nbsp;sent&nbsp;between&nbsp;servers&nbsp;in&nbsp;remote&nbsp;WHOIS&nbsp;queries.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;INVITEs.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_metadata"><strong>handle_metadata</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;METADATA&nbsp;command,&nbsp;used&nbsp;by&nbsp;servers&nbsp;to&nbsp;send&nbsp;metadata&nbsp;(services<br>
login&nbsp;name,&nbsp;certfp&nbsp;data,&nbsp;etc.)&nbsp;for&nbsp;clients.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;user&nbsp;mode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_opertype"><strong>handle_opertype</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;OPERTYPE,&nbsp;which&nbsp;is&nbsp;used&nbsp;to&nbsp;denote&nbsp;an&nbsp;oper&nbsp;up.<br>
&nbsp;<br>
This&nbsp;calls&nbsp;the&nbsp;internal&nbsp;hook&nbsp;CLIENT_OPERED,&nbsp;sets&nbsp;the&nbsp;internal<br>
opertype&nbsp;of&nbsp;the&nbsp;client,&nbsp;and&nbsp;assumes&nbsp;setting&nbsp;user&nbsp;mode&nbsp;+o&nbsp;on&nbsp;the&nbsp;caller.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PING&nbsp;commands,&nbsp;so&nbsp;we&nbsp;don't&nbsp;time&nbsp;out.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PONG&nbsp;commands.<br>
&nbsp;<br>
This&nbsp;is&nbsp;used&nbsp;to&nbsp;keep&nbsp;track&nbsp;of&nbsp;whether&nbsp;the&nbsp;uplink&nbsp;is&nbsp;alive&nbsp;by&nbsp;the&nbsp;Irc()<br>
internals&nbsp;-&nbsp;a&nbsp;server&nbsp;that&nbsp;fails&nbsp;to&nbsp;reply&nbsp;to&nbsp;our&nbsp;PINGs&nbsp;eventually<br>
times&nbsp;out&nbsp;and&nbsp;is&nbsp;disconnected.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_rsquit"><strong>handle_rsquit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;RSQUIT&nbsp;command,&nbsp;which&nbsp;is&nbsp;sent&nbsp;by&nbsp;opers&nbsp;to&nbsp;SQUIT&nbsp;remote<br>
servers.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SERVER&nbsp;commands&nbsp;(introduction&nbsp;of&nbsp;servers).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_svstopic"><strong>handle_svstopic</strong></a> = <a href="#InspIRCdProtocol-handle_ftopic">handle_ftopic</a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="InspIRCdProtocol-handle_uid"><strong>handle_uid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;UID&nbsp;commands&nbsp;(user&nbsp;introduction).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Stub&nbsp;VERSION&nbsp;handler&nbsp;(does&nbsp;nothing)&nbsp;to&nbsp;override&nbsp;the&nbsp;one&nbsp;in&nbsp;ts6_common.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends&nbsp;an&nbsp;INVITE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client..</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins&nbsp;a&nbsp;PyLink&nbsp;client&nbsp;to&nbsp;a&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends&nbsp;a&nbsp;kill&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;KNOCK&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends&nbsp;mode&nbsp;changes&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client,&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends&nbsp;a&nbsp;PING&nbsp;to&nbsp;a&nbsp;target&nbsp;server.&nbsp;Periodic&nbsp;PINGs&nbsp;are&nbsp;sent&nbsp;to&nbsp;our&nbsp;uplink<br>
automatically&nbsp;by&nbsp;the&nbsp;Irc()&nbsp;internals;&nbsp;plugins&nbsp;shouldn't&nbsp;have&nbsp;to&nbsp;use&nbsp;this.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends&nbsp;an&nbsp;SJOIN&nbsp;for&nbsp;a&nbsp;group&nbsp;of&nbsp;users&nbsp;to&nbsp;a&nbsp;channel.<br>
&nbsp;<br>
The&nbsp;sender&nbsp;should&nbsp;always&nbsp;be&nbsp;a&nbsp;Server&nbsp;ID&nbsp;(SID).&nbsp;TS&nbsp;is&nbsp;optional,&nbsp;and&nbsp;defaults<br>
to&nbsp;the&nbsp;one&nbsp;we've&nbsp;stored&nbsp;in&nbsp;the&nbsp;channel&nbsp;state&nbsp;if&nbsp;not&nbsp;given.<br>
&lt;users&gt;&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;(prefix&nbsp;mode,&nbsp;UID)&nbsp;pairs:<br>
&nbsp;<br>
Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Class-sjoin">sjoin</a>('100',&nbsp;'#test',&nbsp;[('',&nbsp;'100AAABBC'),&nbsp;('qo',&nbsp;100AAABBB'),&nbsp;('h',&nbsp;'100AAADDD')])<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Class-sjoin">sjoin</a>(self.<strong>irc</strong>.sid,&nbsp;'#test',&nbsp;[('o',&nbsp;self.<strong>irc</strong>.pseudoclient.uid)])</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-spawnClient"><strong>spawnClient</strong></a>(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator', manipulatable=False)</dt><dd><tt>Spawns&nbsp;a&nbsp;new&nbsp;client&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
&nbsp;<br>
Note:&nbsp;No&nbsp;nick&nbsp;collision&nbsp;/&nbsp;valid&nbsp;nickname&nbsp;checks&nbsp;are&nbsp;done&nbsp;here;&nbsp;it&nbsp;is<br>
up&nbsp;to&nbsp;plugins&nbsp;to&nbsp;make&nbsp;sure&nbsp;they&nbsp;don't&nbsp;introduce&nbsp;anything&nbsp;invalid.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
If&nbsp;endburst_delay&nbsp;is&nbsp;set&nbsp;greater&nbsp;than&nbsp;zero,&nbsp;the&nbsp;sending&nbsp;of&nbsp;ENDBURST<br>
will&nbsp;be&nbsp;delayed&nbsp;by&nbsp;the&nbsp;amount&nbsp;given.&nbsp;This&nbsp;can&nbsp;be&nbsp;used&nbsp;to&nbsp;prevent<br>
pseudoserver&nbsp;bursts&nbsp;from&nbsp;triggering&nbsp;IRCd&nbsp;join-flood&nbsp;preventions,<br>
and&nbsp;prevent&nbsp;connections&nbsp;from&nbsp;filling&nbsp;up&nbsp;the&nbsp;snomasks&nbsp;too&nbsp;much.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;topic&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;This&nbsp;is&nbsp;usually&nbsp;used&nbsp;on&nbsp;burst.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates&nbsp;the&nbsp;ident,&nbsp;host,&nbsp;or&nbsp;realname&nbsp;of&nbsp;any&nbsp;connected&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>:<br>
<dl><dt><a name="InspIRCdProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;ERROR&nbsp;messages&nbsp;-&nbsp;these&nbsp;mean&nbsp;that&nbsp;our&nbsp;uplink&nbsp;has&nbsp;disconnected&nbsp;us!</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;TS6&nbsp;protocols.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions<br>
elsewhere&nbsp;defined&nbsp;protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes<br>
from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;UIDs&nbsp;and&nbsp;SIDs&nbsp;respectively,<br>
whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;will&nbsp;have&nbsp;them&nbsp;set&nbsp;to<br>
the&nbsp;SID&nbsp;of&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;NICK&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_notice"><strong>handle_notice</strong></a> = handle_privmsg(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PART&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUIT&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SAVE&nbsp;messages,&nbsp;used&nbsp;to&nbsp;handle&nbsp;nick&nbsp;collisions.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SQUITs&nbsp;(netsplits).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TOPIC&nbsp;changes&nbsp;from&nbsp;clients.&nbsp;For&nbsp;topic&nbsp;bursts,<br>
TB&nbsp;(TS6/charybdis)&nbsp;and&nbsp;FTOPIC&nbsp;(InspIRCd)&nbsp;are&nbsp;used&nbsp;instead.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar&nbsp;to&nbsp;<a href="#Class-parseArgs">parseArgs</a>(),&nbsp;but&nbsp;stripping&nbsp;leading&nbsp;colons&nbsp;from&nbsp;the&nbsp;first&nbsp;argument<br>
of&nbsp;a&nbsp;line&nbsp;(usually&nbsp;the&nbsp;sender&nbsp;field).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="InspIRCdProtocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="InspIRCdProtocol">class <strong>InspIRCdProtocol</strong></a>(<a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;Protocol&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="inspircd.html#InspIRCdProtocol">InspIRCdProtocol</a></dd>
<dd><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="InspIRCdProtocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_capab"><strong>handle_capab</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;CAPAB&nbsp;command,&nbsp;used&nbsp;for&nbsp;capability&nbsp;negotiation&nbsp;with&nbsp;our<br>
uplink.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_encap"><strong>handle_encap</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;encapsulated&nbsp;commands&nbsp;(ENCAP).&nbsp;Hook&nbsp;arguments<br>
returned&nbsp;by&nbsp;this&nbsp;should&nbsp;have&nbsp;a&nbsp;parse_as&nbsp;field,&nbsp;that&nbsp;sets&nbsp;the&nbsp;correct<br>
hook&nbsp;name&nbsp;for&nbsp;the&nbsp;message.<br>
&nbsp;<br>
For&nbsp;InspIRCd,&nbsp;the&nbsp;only&nbsp;ENCAP&nbsp;command&nbsp;we&nbsp;handle&nbsp;right&nbsp;now&nbsp;is&nbsp;KNOCK.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_endburst"><strong>handle_endburst</strong></a>(self, numeric, command, args)</dt><dd><tt>ENDBURST&nbsp;handler;&nbsp;sends&nbsp;a&nbsp;hook&nbsp;with&nbsp;empty&nbsp;contents.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_fhost"><strong>handle_fhost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;FHOST,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;hostname&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_fident"><strong>handle_fident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;FIDENT,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;ident&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_fjoin"><strong>handle_fjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;FJOIN&nbsp;commands&nbsp;(InspIRCd&nbsp;equivalent&nbsp;of&nbsp;JOIN/SJOIN).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_fmode"><strong>handle_fmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;FMODE&nbsp;command,&nbsp;used&nbsp;for&nbsp;channel&nbsp;mode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_fname"><strong>handle_fname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;FNAME,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;real&nbsp;name/gecos&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_ftopic"><strong>handle_ftopic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;FTOPIC&nbsp;(sets&nbsp;topic&nbsp;on&nbsp;burst).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_idle"><strong>handle_idle</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;IDLE&nbsp;command,&nbsp;sent&nbsp;between&nbsp;servers&nbsp;in&nbsp;remote&nbsp;WHOIS&nbsp;queries.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;INVITEs.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_metadata"><strong>handle_metadata</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;METADATA&nbsp;command,&nbsp;used&nbsp;by&nbsp;servers&nbsp;to&nbsp;send&nbsp;metadata&nbsp;(services<br>
login&nbsp;name,&nbsp;certfp&nbsp;data,&nbsp;etc.)&nbsp;for&nbsp;clients.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;user&nbsp;mode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_opertype"><strong>handle_opertype</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;OPERTYPE,&nbsp;which&nbsp;is&nbsp;used&nbsp;to&nbsp;denote&nbsp;an&nbsp;oper&nbsp;up.<br>
&nbsp;<br>
This&nbsp;calls&nbsp;the&nbsp;internal&nbsp;hook&nbsp;CLIENT_OPERED,&nbsp;sets&nbsp;the&nbsp;internal<br>
opertype&nbsp;of&nbsp;the&nbsp;client,&nbsp;and&nbsp;assumes&nbsp;setting&nbsp;user&nbsp;mode&nbsp;+o&nbsp;on&nbsp;the&nbsp;caller.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PING&nbsp;commands,&nbsp;so&nbsp;we&nbsp;don't&nbsp;time&nbsp;out.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PONG&nbsp;commands.<br>
&nbsp;<br>
This&nbsp;is&nbsp;used&nbsp;to&nbsp;keep&nbsp;track&nbsp;of&nbsp;whether&nbsp;the&nbsp;uplink&nbsp;is&nbsp;alive&nbsp;by&nbsp;the&nbsp;Irc()<br>
internals&nbsp;-&nbsp;a&nbsp;server&nbsp;that&nbsp;fails&nbsp;to&nbsp;reply&nbsp;to&nbsp;our&nbsp;PINGs&nbsp;eventually<br>
times&nbsp;out&nbsp;and&nbsp;is&nbsp;disconnected.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_rsquit"><strong>handle_rsquit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;RSQUIT&nbsp;command,&nbsp;which&nbsp;is&nbsp;sent&nbsp;by&nbsp;opers&nbsp;to&nbsp;SQUIT&nbsp;remote<br>
servers.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SERVER&nbsp;commands&nbsp;(introduction&nbsp;of&nbsp;servers).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_svstopic"><strong>handle_svstopic</strong></a> = <a href="#InspIRCdProtocol-handle_ftopic">handle_ftopic</a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="InspIRCdProtocol-handle_uid"><strong>handle_uid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;UID&nbsp;commands&nbsp;(user&nbsp;introduction).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Stub&nbsp;VERSION&nbsp;handler&nbsp;(does&nbsp;nothing)&nbsp;to&nbsp;override&nbsp;the&nbsp;one&nbsp;in&nbsp;ts6_common.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends&nbsp;an&nbsp;INVITE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client..</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins&nbsp;a&nbsp;PyLink&nbsp;client&nbsp;to&nbsp;a&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends&nbsp;a&nbsp;kill&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;KNOCK&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends&nbsp;mode&nbsp;changes&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client,&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends&nbsp;a&nbsp;PING&nbsp;to&nbsp;a&nbsp;target&nbsp;server.&nbsp;Periodic&nbsp;PINGs&nbsp;are&nbsp;sent&nbsp;to&nbsp;our&nbsp;uplink<br>
automatically&nbsp;by&nbsp;the&nbsp;Irc()&nbsp;internals;&nbsp;plugins&nbsp;shouldn't&nbsp;have&nbsp;to&nbsp;use&nbsp;this.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends&nbsp;an&nbsp;SJOIN&nbsp;for&nbsp;a&nbsp;group&nbsp;of&nbsp;users&nbsp;to&nbsp;a&nbsp;channel.<br>
&nbsp;<br>
The&nbsp;sender&nbsp;should&nbsp;always&nbsp;be&nbsp;a&nbsp;Server&nbsp;ID&nbsp;(SID).&nbsp;TS&nbsp;is&nbsp;optional,&nbsp;and&nbsp;defaults<br>
to&nbsp;the&nbsp;one&nbsp;we've&nbsp;stored&nbsp;in&nbsp;the&nbsp;channel&nbsp;state&nbsp;if&nbsp;not&nbsp;given.<br>
&lt;users&gt;&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;(prefix&nbsp;mode,&nbsp;UID)&nbsp;pairs:<br>
&nbsp;<br>
Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#InspIRCdProtocol-sjoin">sjoin</a>('100',&nbsp;'#test',&nbsp;[('',&nbsp;'100AAABBC'),&nbsp;('qo',&nbsp;100AAABBB'),&nbsp;('h',&nbsp;'100AAADDD')])<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#InspIRCdProtocol-sjoin">sjoin</a>(self.<strong>irc</strong>.sid,&nbsp;'#test',&nbsp;[('o',&nbsp;self.<strong>irc</strong>.pseudoclient.uid)])</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-spawnClient"><strong>spawnClient</strong></a>(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator', manipulatable=False)</dt><dd><tt>Spawns&nbsp;a&nbsp;new&nbsp;client&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
&nbsp;<br>
Note:&nbsp;No&nbsp;nick&nbsp;collision&nbsp;/&nbsp;valid&nbsp;nickname&nbsp;checks&nbsp;are&nbsp;done&nbsp;here;&nbsp;it&nbsp;is<br>
up&nbsp;to&nbsp;plugins&nbsp;to&nbsp;make&nbsp;sure&nbsp;they&nbsp;don't&nbsp;introduce&nbsp;anything&nbsp;invalid.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
If&nbsp;endburst_delay&nbsp;is&nbsp;set&nbsp;greater&nbsp;than&nbsp;zero,&nbsp;the&nbsp;sending&nbsp;of&nbsp;ENDBURST<br>
will&nbsp;be&nbsp;delayed&nbsp;by&nbsp;the&nbsp;amount&nbsp;given.&nbsp;This&nbsp;can&nbsp;be&nbsp;used&nbsp;to&nbsp;prevent<br>
pseudoserver&nbsp;bursts&nbsp;from&nbsp;triggering&nbsp;IRCd&nbsp;join-flood&nbsp;preventions,<br>
and&nbsp;prevent&nbsp;connections&nbsp;from&nbsp;filling&nbsp;up&nbsp;the&nbsp;snomasks&nbsp;too&nbsp;much.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;topic&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;This&nbsp;is&nbsp;usually&nbsp;used&nbsp;on&nbsp;burst.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates&nbsp;the&nbsp;ident,&nbsp;host,&nbsp;or&nbsp;realname&nbsp;of&nbsp;any&nbsp;connected&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>:<br>
<dl><dt><a name="InspIRCdProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;ERROR&nbsp;messages&nbsp;-&nbsp;these&nbsp;mean&nbsp;that&nbsp;our&nbsp;uplink&nbsp;has&nbsp;disconnected&nbsp;us!</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;TS6&nbsp;protocols.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions<br>
elsewhere&nbsp;defined&nbsp;protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes<br>
from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;UIDs&nbsp;and&nbsp;SIDs&nbsp;respectively,<br>
whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;will&nbsp;have&nbsp;them&nbsp;set&nbsp;to<br>
the&nbsp;SID&nbsp;of&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;NICK&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_notice"><strong>handle_notice</strong></a> = handle_privmsg(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PART&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUIT&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SAVE&nbsp;messages,&nbsp;used&nbsp;to&nbsp;handle&nbsp;nick&nbsp;collisions.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SQUITs&nbsp;(netsplits).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TOPIC&nbsp;changes&nbsp;from&nbsp;clients.&nbsp;For&nbsp;topic&nbsp;bursts,<br>
TB&nbsp;(TS6/charybdis)&nbsp;and&nbsp;FTOPIC&nbsp;(InspIRCd)&nbsp;are&nbsp;used&nbsp;instead.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar&nbsp;to&nbsp;<a href="#InspIRCdProtocol-parseArgs">parseArgs</a>(),&nbsp;but&nbsp;stripping&nbsp;leading&nbsp;colons&nbsp;from&nbsp;the&nbsp;first&nbsp;argument<br>
of&nbsp;a&nbsp;line&nbsp;(usually&nbsp;the&nbsp;sender&nbsp;field).</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="InspIRCdProtocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="InspIRCdProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>conf</strong> = {'bot': {'nick': 'PyLink', 'realname': 'PyLink Service Client', 'serverdesc': 'PyLink unit tests', 'user': 'pylink'}, 'logging': {'stdout': 'CRITICAL'}, 'servers': defaultdict(&lt;function &lt;lambda&gt; at 0x7ff146c496a8&gt;, {})}<br>
<strong>confname</strong> = 'testconf'<br>
<strong>curdir</strong> = '/home/gl/pylink'<br>
<strong>files</strong> = None<br>
<strong>log</strong> = &lt;logging.RootLogger object&gt;<br>
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
<strong>logformatter</strong> = &lt;logging.Formatter object&gt;<br>
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
</body></html>

View File

@ -1,166 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module log</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>log</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/log.py">/home/gl/pylink/log.py</a></font></td></tr></table>
<p><tt>log.py&nbsp;-&nbsp;PyLink&nbsp;logging&nbsp;module.<br>
&nbsp;<br>
This&nbsp;module&nbsp;contains&nbsp;the&nbsp;logging&nbsp;portion&nbsp;of&nbsp;the&nbsp;PyLink&nbsp;framework.&nbsp;Plugins&nbsp;can<br>
access&nbsp;the&nbsp;global&nbsp;logger&nbsp;object&nbsp;by&nbsp;importing&nbsp;"log"&nbsp;from&nbsp;this&nbsp;module<br>
(from&nbsp;log&nbsp;import&nbsp;log).</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="logging.html">logging</a><br>
</td><td width="25%" valign=top><a href="os.html">os</a><br>
</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
</td><td width="25%" valign=top><a href="world.html">world</a><br>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="logging.html#Handler">logging.Handler</a>(<a href="logging.html#Filterer">logging.Filterer</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="log.html#PyLinkChannelLogger">PyLinkChannelLogger</a>
</font></dt></dl>
</dd>
</dl>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="PyLinkChannelLogger">class <strong>PyLinkChannelLogger</strong></a>(<a href="logging.html#Handler">logging.Handler</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Log&nbsp;handler&nbsp;to&nbsp;log&nbsp;to&nbsp;channels&nbsp;in&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="log.html#PyLinkChannelLogger">PyLinkChannelLogger</a></dd>
<dd><a href="logging.html#Handler">logging.Handler</a></dd>
<dd><a href="logging.html#Filterer">logging.Filterer</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="PyLinkChannelLogger-__init__"><strong>__init__</strong></a>(self, irc, channel, level=None)</dt><dd><tt>Initializes&nbsp;the&nbsp;instance&nbsp;-&nbsp;basically&nbsp;setting&nbsp;the&nbsp;formatter&nbsp;to&nbsp;None<br>
and&nbsp;the&nbsp;filter&nbsp;list&nbsp;to&nbsp;empty.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-emit"><strong>emit</strong></a>(self, record)</dt><dd><tt>Logs&nbsp;a&nbsp;record&nbsp;to&nbsp;the&nbsp;configured&nbsp;channels&nbsp;for&nbsp;the&nbsp;network&nbsp;given.</tt></dd></dl>
<hr>
Methods inherited from <a href="logging.html#Handler">logging.Handler</a>:<br>
<dl><dt><a name="PyLinkChannelLogger-acquire"><strong>acquire</strong></a>(self)</dt><dd><tt>Acquire&nbsp;the&nbsp;I/O&nbsp;thread&nbsp;lock.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-close"><strong>close</strong></a>(self)</dt><dd><tt>Tidy&nbsp;up&nbsp;any&nbsp;resources&nbsp;used&nbsp;by&nbsp;the&nbsp;handler.<br>
&nbsp;<br>
This&nbsp;version&nbsp;removes&nbsp;the&nbsp;handler&nbsp;from&nbsp;an&nbsp;internal&nbsp;map&nbsp;of&nbsp;handlers,<br>
_handlers,&nbsp;which&nbsp;is&nbsp;used&nbsp;for&nbsp;handler&nbsp;lookup&nbsp;by&nbsp;name.&nbsp;Subclasses<br>
should&nbsp;ensure&nbsp;that&nbsp;this&nbsp;gets&nbsp;called&nbsp;from&nbsp;overridden&nbsp;<a href="#PyLinkChannelLogger-close">close</a>()<br>
methods.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-createLock"><strong>createLock</strong></a>(self)</dt><dd><tt>Acquire&nbsp;a&nbsp;thread&nbsp;lock&nbsp;for&nbsp;serializing&nbsp;access&nbsp;to&nbsp;the&nbsp;underlying&nbsp;I/O.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-flush"><strong>flush</strong></a>(self)</dt><dd><tt>Ensure&nbsp;all&nbsp;logging&nbsp;output&nbsp;has&nbsp;been&nbsp;flushed.<br>
&nbsp;<br>
This&nbsp;version&nbsp;does&nbsp;nothing&nbsp;and&nbsp;is&nbsp;intended&nbsp;to&nbsp;be&nbsp;implemented&nbsp;by<br>
subclasses.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-format"><strong>format</strong></a>(self, record)</dt><dd><tt>Format&nbsp;the&nbsp;specified&nbsp;record.<br>
&nbsp;<br>
If&nbsp;a&nbsp;formatter&nbsp;is&nbsp;set,&nbsp;use&nbsp;it.&nbsp;Otherwise,&nbsp;use&nbsp;the&nbsp;default&nbsp;formatter<br>
for&nbsp;the&nbsp;module.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-get_name"><strong>get_name</strong></a>(self)</dt></dl>
<dl><dt><a name="PyLinkChannelLogger-handle"><strong>handle</strong></a>(self, record)</dt><dd><tt>Conditionally&nbsp;emit&nbsp;the&nbsp;specified&nbsp;logging&nbsp;record.<br>
&nbsp;<br>
Emission&nbsp;depends&nbsp;on&nbsp;filters&nbsp;which&nbsp;may&nbsp;have&nbsp;been&nbsp;added&nbsp;to&nbsp;the&nbsp;handler.<br>
Wrap&nbsp;the&nbsp;actual&nbsp;emission&nbsp;of&nbsp;the&nbsp;record&nbsp;with&nbsp;acquisition/release&nbsp;of<br>
the&nbsp;I/O&nbsp;thread&nbsp;lock.&nbsp;Returns&nbsp;whether&nbsp;the&nbsp;filter&nbsp;passed&nbsp;the&nbsp;record&nbsp;for<br>
emission.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-handleError"><strong>handleError</strong></a>(self, record)</dt><dd><tt>Handle&nbsp;errors&nbsp;which&nbsp;occur&nbsp;during&nbsp;an&nbsp;<a href="#PyLinkChannelLogger-emit">emit</a>()&nbsp;call.<br>
&nbsp;<br>
This&nbsp;method&nbsp;should&nbsp;be&nbsp;called&nbsp;from&nbsp;handlers&nbsp;when&nbsp;an&nbsp;exception&nbsp;is<br>
encountered&nbsp;during&nbsp;an&nbsp;<a href="#PyLinkChannelLogger-emit">emit</a>()&nbsp;call.&nbsp;If&nbsp;raiseExceptions&nbsp;is&nbsp;false,<br>
exceptions&nbsp;get&nbsp;silently&nbsp;ignored.&nbsp;This&nbsp;is&nbsp;what&nbsp;is&nbsp;mostly&nbsp;wanted<br>
for&nbsp;a&nbsp;logging&nbsp;system&nbsp;-&nbsp;most&nbsp;users&nbsp;will&nbsp;not&nbsp;care&nbsp;about&nbsp;errors&nbsp;in<br>
the&nbsp;logging&nbsp;system,&nbsp;they&nbsp;are&nbsp;more&nbsp;interested&nbsp;in&nbsp;application&nbsp;errors.<br>
You&nbsp;could,&nbsp;however,&nbsp;replace&nbsp;this&nbsp;with&nbsp;a&nbsp;custom&nbsp;handler&nbsp;if&nbsp;you&nbsp;wish.<br>
The&nbsp;record&nbsp;which&nbsp;was&nbsp;being&nbsp;processed&nbsp;is&nbsp;passed&nbsp;in&nbsp;to&nbsp;this&nbsp;method.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-release"><strong>release</strong></a>(self)</dt><dd><tt>Release&nbsp;the&nbsp;I/O&nbsp;thread&nbsp;lock.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-setFormatter"><strong>setFormatter</strong></a>(self, fmt)</dt><dd><tt>Set&nbsp;the&nbsp;formatter&nbsp;for&nbsp;this&nbsp;handler.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-setLevel"><strong>setLevel</strong></a>(self, level)</dt><dd><tt>Set&nbsp;the&nbsp;logging&nbsp;level&nbsp;of&nbsp;this&nbsp;handler.&nbsp;&nbsp;level&nbsp;must&nbsp;be&nbsp;an&nbsp;int&nbsp;or&nbsp;a&nbsp;str.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-set_name"><strong>set_name</strong></a>(self, name)</dt></dl>
<hr>
Data descriptors inherited from <a href="logging.html#Handler">logging.Handler</a>:<br>
<dl><dt><strong>name</strong></dt>
</dl>
<hr>
Methods inherited from <a href="logging.html#Filterer">logging.Filterer</a>:<br>
<dl><dt><a name="PyLinkChannelLogger-addFilter"><strong>addFilter</strong></a>(self, filter)</dt><dd><tt>Add&nbsp;the&nbsp;specified&nbsp;filter&nbsp;to&nbsp;this&nbsp;handler.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-filter"><strong>filter</strong></a>(self, record)</dt><dd><tt>Determine&nbsp;if&nbsp;a&nbsp;record&nbsp;is&nbsp;loggable&nbsp;by&nbsp;consulting&nbsp;all&nbsp;the&nbsp;filters.<br>
&nbsp;<br>
The&nbsp;default&nbsp;is&nbsp;to&nbsp;allow&nbsp;the&nbsp;record&nbsp;to&nbsp;be&nbsp;logged;&nbsp;any&nbsp;filter&nbsp;can&nbsp;veto<br>
this&nbsp;and&nbsp;the&nbsp;record&nbsp;is&nbsp;then&nbsp;dropped.&nbsp;Returns&nbsp;a&nbsp;zero&nbsp;value&nbsp;if&nbsp;a&nbsp;record<br>
is&nbsp;to&nbsp;be&nbsp;dropped,&nbsp;else&nbsp;non-zero.<br>
&nbsp;<br>
..&nbsp;versionchanged::&nbsp;3.2<br>
&nbsp;<br>
&nbsp;&nbsp;&nbsp;Allow&nbsp;filters&nbsp;to&nbsp;be&nbsp;just&nbsp;callables.</tt></dd></dl>
<dl><dt><a name="PyLinkChannelLogger-removeFilter"><strong>removeFilter</strong></a>(self, filter)</dt><dd><tt>Remove&nbsp;the&nbsp;specified&nbsp;filter&nbsp;from&nbsp;this&nbsp;handler.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="logging.html#Filterer">logging.Filterer</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#eeaa77">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt><a name="-makeFileLogger"><strong>makeFileLogger</strong></a>(filename, level=None)</dt><dd><tt>Initializes&nbsp;a&nbsp;file&nbsp;logging&nbsp;target&nbsp;with&nbsp;the&nbsp;given&nbsp;filename&nbsp;and&nbsp;level.</tt></dd></dl>
</td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>conf</strong> = {'bot': {'nick': 'PyLink', 'realname': 'PyLink Service Client', 'serverdesc': 'PyLink unit tests', 'user': 'pylink'}, 'logging': {'stdout': 'CRITICAL'}, 'servers': defaultdict(&lt;function &lt;lambda&gt; at 0x7f496c510e18&gt;, {})}<br>
<strong>confname</strong> = 'testconf'<br>
<strong>curdir</strong> = '/home/gl/pylink'<br>
<strong>files</strong> = None<br>
<strong>log</strong> = &lt;logging.RootLogger object&gt;<br>
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
<strong>logformatter</strong> = &lt;logging.Formatter object&gt;<br>
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
</body></html>

View File

@ -1,475 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module nefarious</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>nefarious</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/protocols/nefarious.py">/home/gl/pylink/protocols/nefarious.py</a></font></td></tr></table>
<p><tt>nefarious.py:&nbsp;Nefarious&nbsp;IRCu&nbsp;protocol&nbsp;module&nbsp;for&nbsp;PyLink.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="base64.html">base64</a><br>
<a href="hashlib.html">hashlib</a><br>
<a href="inspect.html">inspect</a><br>
<a href="logging.html">logging</a><br>
</td><td width="25%" valign=top><a href="os.html">os</a><br>
<a href="socket.html">socket</a><br>
<a href="ssl.html">ssl</a><br>
<a href="struct.html">struct</a><br>
</td><td width="25%" valign=top><a href="structures.html">structures</a><br>
<a href="sys.html">sys</a><br>
<a href="threading.html">threading</a><br>
<a href="time.html">time</a><br>
</td><td width="25%" valign=top><a href="utils.html">utils</a><br>
<a href="world.html">world</a><br>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="builtins.html#object">builtins.object</a>
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="nefarious.html#P10SIDGenerator">P10SIDGenerator</a>
</font></dt></dl>
</dd>
<dt><font face="helvetica, arial"><a href="classes.html#Protocol">classes.Protocol</a>(<a href="builtins.html#object">builtins.object</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="nefarious.html#P10Protocol">P10Protocol</a>
</font></dt></dl>
</dd>
<dt><font face="helvetica, arial"><a href="utils.html#IncrementalUIDGenerator">utils.IncrementalUIDGenerator</a>(<a href="builtins.html#object">builtins.object</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="nefarious.html#P10UIDGenerator">P10UIDGenerator</a>
</font></dt></dl>
</dd>
</dl>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><strong>Class</strong> = <a name="Class">class P10Protocol</a>(<a href="classes.html#Protocol">classes.Protocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;<a href="classes.html#Protocol">Protocol</a>&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="nefarious.html#P10Protocol">P10Protocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="P10Protocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="P10Protocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="P10Protocol-checkCloakChange"><strong>checkCloakChange</strong></a>(self, uid)</dt><dd><tt>Checks&nbsp;for&nbsp;cloak&nbsp;changes&nbsp;on&nbsp;the&nbsp;given&nbsp;UID.</tt></dd></dl>
<dl><dt><a name="P10Protocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_account"><strong>handle_account</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;services&nbsp;account&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_burst"><strong>handle_burst</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;BURST&nbsp;command,&nbsp;used&nbsp;for&nbsp;bursting&nbsp;channels&nbsp;on&nbsp;link.<br>
&nbsp;<br>
This&nbsp;is&nbsp;equivalent&nbsp;to&nbsp;SJOIN&nbsp;on&nbsp;most&nbsp;IRCds.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_clearmode"><strong>handle_clearmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;CLEARMODE,&nbsp;which&nbsp;is&nbsp;used&nbsp;to&nbsp;clear&nbsp;a&nbsp;channel's&nbsp;modes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_create"><strong>handle_create</strong></a> = <a href="#P10Protocol-handle_join">handle_join</a>(self, source, command, args)</dt></dl>
<dl><dt><a name="P10Protocol-handle_end_of_burst"><strong>handle_end_of_burst</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;end&nbsp;of&nbsp;burst&nbsp;from&nbsp;our&nbsp;uplink.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;the&nbsp;P10&nbsp;protocol.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions&nbsp;defined&nbsp;elsewhere&nbsp;in&nbsp;the<br>
protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes&nbsp;from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;P10<br>
"numeric&nbsp;nicks",&nbsp;whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;are&nbsp;treated&nbsp;as&nbsp;originating&nbsp;from&nbsp;the&nbsp;uplink<br>
server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_fake"><strong>handle_fake</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;FAKE&nbsp;hostmask&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_invite"><strong>handle_invite</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;INVITEs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_join"><strong>handle_join</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;JOINs&nbsp;and&nbsp;channel&nbsp;creations.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_kill"><strong>handle_kill</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_mode"><strong>handle_mode</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;mode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_nick"><strong>handle_nick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;NICK&nbsp;command,&nbsp;used&nbsp;for&nbsp;user&nbsp;introductions&nbsp;and&nbsp;nick&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_notice"><strong>handle_notice</strong></a> = <a href="#P10Protocol-handle_privmsg">handle_privmsg</a>(self, source, command, args)</dt></dl>
<dl><dt><a name="P10Protocol-handle_opmode"><strong>handle_opmode</strong></a> = <a href="#P10Protocol-handle_mode">handle_mode</a>(self, source, command, args)</dt></dl>
<dl><dt><a name="P10Protocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;user&nbsp;parts.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_pass"><strong>handle_pass</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;authentication&nbsp;with&nbsp;our&nbsp;uplink.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PING&nbsp;requests.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PONGs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUITs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_server"><strong>handle_server</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;server&nbsp;introductions.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SQUITs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_topic"><strong>handle_topic</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;TOPIC&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;requests&nbsp;for&nbsp;the&nbsp;PyLink&nbsp;server&nbsp;version.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;WHOIS&nbsp;requests.</tt></dd></dl>
<dl><dt><a name="P10Protocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends&nbsp;INVITEs&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins&nbsp;a&nbsp;PyLink&nbsp;client&nbsp;to&nbsp;a&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="P10Protocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends&nbsp;a&nbsp;kill&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt></dl>
<dl><dt><a name="P10Protocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends&nbsp;mode&nbsp;changes&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client.&nbsp;This&nbsp;is&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="P10Protocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends&nbsp;a&nbsp;PING&nbsp;to&nbsp;a&nbsp;target&nbsp;server.&nbsp;Periodic&nbsp;PINGs&nbsp;are&nbsp;sent&nbsp;to&nbsp;our&nbsp;uplink<br>
automatically&nbsp;by&nbsp;the&nbsp;Irc()&nbsp;internals;&nbsp;plugins&nbsp;shouldn't&nbsp;have&nbsp;to&nbsp;use&nbsp;this.</tt></dd></dl>
<dl><dt><a name="P10Protocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends&nbsp;an&nbsp;SJOIN&nbsp;for&nbsp;a&nbsp;group&nbsp;of&nbsp;users&nbsp;to&nbsp;a&nbsp;channel.<br>
&nbsp;<br>
The&nbsp;sender&nbsp;should&nbsp;always&nbsp;be&nbsp;a&nbsp;Server&nbsp;ID&nbsp;(SID).&nbsp;TS&nbsp;is&nbsp;optional,&nbsp;and&nbsp;defaults<br>
to&nbsp;the&nbsp;one&nbsp;we've&nbsp;stored&nbsp;in&nbsp;the&nbsp;channel&nbsp;state&nbsp;if&nbsp;not&nbsp;given.<br>
&lt;users&gt;&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;(prefix&nbsp;mode,&nbsp;UID)&nbsp;pairs:<br>
&nbsp;<br>
Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Class-sjoin">sjoin</a>('100',&nbsp;'#test',&nbsp;[('',&nbsp;'100AAABBC'),&nbsp;('o',&nbsp;100AAABBB'),&nbsp;('v',&nbsp;'100AAADDD')])<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Class-sjoin">sjoin</a>(self.<strong>irc</strong>.sid,&nbsp;'#test',&nbsp;[('o',&nbsp;self.<strong>irc</strong>.pseudoclient.uid)])</tt></dd></dl>
<dl><dt><a name="P10Protocol-spawnClient"><strong>spawnClient</strong></a>(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator', manipulatable=False)</dt><dd><tt>Spawns&nbsp;a&nbsp;new&nbsp;client&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
&nbsp;<br>
Note:&nbsp;No&nbsp;nick&nbsp;collision&nbsp;/&nbsp;valid&nbsp;nickname&nbsp;checks&nbsp;are&nbsp;done&nbsp;here;&nbsp;it&nbsp;is<br>
up&nbsp;to&nbsp;plugins&nbsp;to&nbsp;make&nbsp;sure&nbsp;they&nbsp;don't&nbsp;introduce&nbsp;anything&nbsp;invalid.</tt></dd></dl>
<dl><dt><a name="P10Protocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
Note:&nbsp;TS6&nbsp;doesn't&nbsp;use&nbsp;a&nbsp;specific&nbsp;ENDBURST&nbsp;command,&nbsp;so&nbsp;the&nbsp;endburst_delay<br>
option&nbsp;will&nbsp;be&nbsp;ignored&nbsp;if&nbsp;given.</tt></dd></dl>
<dl><dt><a name="P10Protocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates&nbsp;the&nbsp;ident&nbsp;or&nbsp;host&nbsp;of&nbsp;any&nbsp;connected&nbsp;client.</tt></dd></dl>
<hr>
Static methods defined here:<br>
<dl><dt><a name="P10Protocol-decode_p10_ip"><strong>decode_p10_ip</strong></a>(ip)</dt><dd><tt>Decodes&nbsp;a&nbsp;P10&nbsp;IP.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="P10Protocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="P10Protocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="P10Protocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="P10Protocol">class <strong>P10Protocol</strong></a>(<a href="classes.html#Protocol">classes.Protocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;<a href="classes.html#Protocol">Protocol</a>&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="nefarious.html#P10Protocol">P10Protocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="P10Protocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="P10Protocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="P10Protocol-checkCloakChange"><strong>checkCloakChange</strong></a>(self, uid)</dt><dd><tt>Checks&nbsp;for&nbsp;cloak&nbsp;changes&nbsp;on&nbsp;the&nbsp;given&nbsp;UID.</tt></dd></dl>
<dl><dt><a name="P10Protocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_account"><strong>handle_account</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;services&nbsp;account&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_burst"><strong>handle_burst</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;BURST&nbsp;command,&nbsp;used&nbsp;for&nbsp;bursting&nbsp;channels&nbsp;on&nbsp;link.<br>
&nbsp;<br>
This&nbsp;is&nbsp;equivalent&nbsp;to&nbsp;SJOIN&nbsp;on&nbsp;most&nbsp;IRCds.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_clearmode"><strong>handle_clearmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;CLEARMODE,&nbsp;which&nbsp;is&nbsp;used&nbsp;to&nbsp;clear&nbsp;a&nbsp;channel's&nbsp;modes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_create"><strong>handle_create</strong></a> = <a href="#P10Protocol-handle_join">handle_join</a>(self, source, command, args)</dt></dl>
<dl><dt><a name="P10Protocol-handle_end_of_burst"><strong>handle_end_of_burst</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;end&nbsp;of&nbsp;burst&nbsp;from&nbsp;our&nbsp;uplink.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;the&nbsp;P10&nbsp;protocol.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions&nbsp;defined&nbsp;elsewhere&nbsp;in&nbsp;the<br>
protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes&nbsp;from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;P10<br>
"numeric&nbsp;nicks",&nbsp;whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;are&nbsp;treated&nbsp;as&nbsp;originating&nbsp;from&nbsp;the&nbsp;uplink<br>
server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_fake"><strong>handle_fake</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;FAKE&nbsp;hostmask&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_invite"><strong>handle_invite</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;INVITEs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_join"><strong>handle_join</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;JOINs&nbsp;and&nbsp;channel&nbsp;creations.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_kill"><strong>handle_kill</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_mode"><strong>handle_mode</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;mode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_nick"><strong>handle_nick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;NICK&nbsp;command,&nbsp;used&nbsp;for&nbsp;user&nbsp;introductions&nbsp;and&nbsp;nick&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_notice"><strong>handle_notice</strong></a> = <a href="#P10Protocol-handle_privmsg">handle_privmsg</a>(self, source, command, args)</dt></dl>
<dl><dt><a name="P10Protocol-handle_opmode"><strong>handle_opmode</strong></a> = <a href="#P10Protocol-handle_mode">handle_mode</a>(self, source, command, args)</dt></dl>
<dl><dt><a name="P10Protocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;user&nbsp;parts.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_pass"><strong>handle_pass</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;authentication&nbsp;with&nbsp;our&nbsp;uplink.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PING&nbsp;requests.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PONGs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUITs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_server"><strong>handle_server</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;server&nbsp;introductions.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SQUITs.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_topic"><strong>handle_topic</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;TOPIC&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;requests&nbsp;for&nbsp;the&nbsp;PyLink&nbsp;server&nbsp;version.</tt></dd></dl>
<dl><dt><a name="P10Protocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;WHOIS&nbsp;requests.</tt></dd></dl>
<dl><dt><a name="P10Protocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends&nbsp;INVITEs&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins&nbsp;a&nbsp;PyLink&nbsp;client&nbsp;to&nbsp;a&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="P10Protocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends&nbsp;a&nbsp;kill&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt></dl>
<dl><dt><a name="P10Protocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends&nbsp;mode&nbsp;changes&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client.&nbsp;This&nbsp;is&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="P10Protocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends&nbsp;a&nbsp;PING&nbsp;to&nbsp;a&nbsp;target&nbsp;server.&nbsp;Periodic&nbsp;PINGs&nbsp;are&nbsp;sent&nbsp;to&nbsp;our&nbsp;uplink<br>
automatically&nbsp;by&nbsp;the&nbsp;Irc()&nbsp;internals;&nbsp;plugins&nbsp;shouldn't&nbsp;have&nbsp;to&nbsp;use&nbsp;this.</tt></dd></dl>
<dl><dt><a name="P10Protocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends&nbsp;an&nbsp;SJOIN&nbsp;for&nbsp;a&nbsp;group&nbsp;of&nbsp;users&nbsp;to&nbsp;a&nbsp;channel.<br>
&nbsp;<br>
The&nbsp;sender&nbsp;should&nbsp;always&nbsp;be&nbsp;a&nbsp;Server&nbsp;ID&nbsp;(SID).&nbsp;TS&nbsp;is&nbsp;optional,&nbsp;and&nbsp;defaults<br>
to&nbsp;the&nbsp;one&nbsp;we've&nbsp;stored&nbsp;in&nbsp;the&nbsp;channel&nbsp;state&nbsp;if&nbsp;not&nbsp;given.<br>
&lt;users&gt;&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;(prefix&nbsp;mode,&nbsp;UID)&nbsp;pairs:<br>
&nbsp;<br>
Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#P10Protocol-sjoin">sjoin</a>('100',&nbsp;'#test',&nbsp;[('',&nbsp;'100AAABBC'),&nbsp;('o',&nbsp;100AAABBB'),&nbsp;('v',&nbsp;'100AAADDD')])<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#P10Protocol-sjoin">sjoin</a>(self.<strong>irc</strong>.sid,&nbsp;'#test',&nbsp;[('o',&nbsp;self.<strong>irc</strong>.pseudoclient.uid)])</tt></dd></dl>
<dl><dt><a name="P10Protocol-spawnClient"><strong>spawnClient</strong></a>(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator', manipulatable=False)</dt><dd><tt>Spawns&nbsp;a&nbsp;new&nbsp;client&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
&nbsp;<br>
Note:&nbsp;No&nbsp;nick&nbsp;collision&nbsp;/&nbsp;valid&nbsp;nickname&nbsp;checks&nbsp;are&nbsp;done&nbsp;here;&nbsp;it&nbsp;is<br>
up&nbsp;to&nbsp;plugins&nbsp;to&nbsp;make&nbsp;sure&nbsp;they&nbsp;don't&nbsp;introduce&nbsp;anything&nbsp;invalid.</tt></dd></dl>
<dl><dt><a name="P10Protocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
Note:&nbsp;TS6&nbsp;doesn't&nbsp;use&nbsp;a&nbsp;specific&nbsp;ENDBURST&nbsp;command,&nbsp;so&nbsp;the&nbsp;endburst_delay<br>
option&nbsp;will&nbsp;be&nbsp;ignored&nbsp;if&nbsp;given.</tt></dd></dl>
<dl><dt><a name="P10Protocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="P10Protocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="P10Protocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates&nbsp;the&nbsp;ident&nbsp;or&nbsp;host&nbsp;of&nbsp;any&nbsp;connected&nbsp;client.</tt></dd></dl>
<hr>
Static methods defined here:<br>
<dl><dt><a name="P10Protocol-decode_p10_ip"><strong>decode_p10_ip</strong></a>(ip)</dt><dd><tt>Decodes&nbsp;a&nbsp;P10&nbsp;IP.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="P10Protocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="P10Protocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="P10Protocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="P10SIDGenerator">class <strong>P10SIDGenerator</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="P10SIDGenerator-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="P10SIDGenerator-next_sid"><strong>next_sid</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;next&nbsp;available&nbsp;SID.</tt></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="P10UIDGenerator">class <strong>P10UIDGenerator</strong></a>(<a href="utils.html#IncrementalUIDGenerator">utils.IncrementalUIDGenerator</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Implements&nbsp;an&nbsp;incremental&nbsp;P10&nbsp;UID&nbsp;Generator.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="nefarious.html#P10UIDGenerator">P10UIDGenerator</a></dd>
<dd><a href="utils.html#IncrementalUIDGenerator">utils.IncrementalUIDGenerator</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="P10UIDGenerator-__init__"><strong>__init__</strong></a>(self, sid)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<hr>
Methods inherited from <a href="utils.html#IncrementalUIDGenerator">utils.IncrementalUIDGenerator</a>:<br>
<dl><dt><a name="P10UIDGenerator-increment"><strong>increment</strong></a>(self, pos=None)</dt><dd><tt>Increments&nbsp;the&nbsp;UID&nbsp;generator&nbsp;to&nbsp;the&nbsp;next&nbsp;available&nbsp;UID.</tt></dd></dl>
<dl><dt><a name="P10UIDGenerator-next_uid"><strong>next_uid</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;next&nbsp;unused&nbsp;UID&nbsp;for&nbsp;the&nbsp;server.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="utils.html#IncrementalUIDGenerator">utils.IncrementalUIDGenerator</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#eeaa77">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt><a name="-p10b64encode"><strong>p10b64encode</strong></a>(num, length=2)</dt><dd><tt>Encodes&nbsp;a&nbsp;given&nbsp;numeric&nbsp;using&nbsp;P10&nbsp;Base64&nbsp;numeric&nbsp;nicks,&nbsp;as&nbsp;documented&nbsp;at<br>
https://github.com/evilnet/nefarious2/blob/a29b63144/doc/p10.txt#L69-L92</tt></dd></dl>
</td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>conf</strong> = {'bot': {'nick': 'PyLink', 'realname': 'PyLink Service Client', 'serverdesc': 'PyLink unit tests', 'user': 'pylink'}, 'logging': {'stdout': 'CRITICAL'}, 'servers': defaultdict(&lt;function &lt;lambda&gt; at 0x7fcf7b6db510&gt;, {})}<br>
<strong>confname</strong> = 'testconf'<br>
<strong>curdir</strong> = '/home/gl/pylink'<br>
<strong>files</strong> = None<br>
<strong>log</strong> = &lt;logging.RootLogger object&gt;<br>
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
<strong>logformatter</strong> = &lt;logging.Formatter object&gt;<br>
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
</body></html>

View File

@ -1,194 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module structures</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>structures</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/structures.py">/home/gl/pylink/structures.py</a></font></td></tr></table>
<p><tt>structures.py&nbsp;-&nbsp;PyLink&nbsp;data&nbsp;structures&nbsp;module.<br>
&nbsp;<br>
This&nbsp;module&nbsp;contains&nbsp;custom&nbsp;data&nbsp;structures&nbsp;that&nbsp;may&nbsp;be&nbsp;useful&nbsp;in&nbsp;various&nbsp;situations.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
</td><td width="25%" valign=top><a href="json.html">json</a><br>
</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="builtins.html#object">builtins.object</a>
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="structures.html#DataStore">DataStore</a>
</font></dt></dl>
</dd>
<dt><font face="helvetica, arial"><a href="collections.html#defaultdict">collections.defaultdict</a>(<a href="builtins.html#dict">builtins.dict</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="structures.html#KeyedDefaultdict">KeyedDefaultdict</a>
</font></dt></dl>
</dd>
</dl>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="DataStore">class <strong>DataStore</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="DataStore-__contains__"><strong>__contains__</strong></a>(self, key)</dt><dd><tt>#&nbsp;single&nbsp;keys</tt></dd></dl>
<dl><dt><a name="DataStore-__init__"><strong>__init__</strong></a>(self, name, filename, db_format='json', save_frequency={'seconds': 30})</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="DataStore-create_or_load"><strong>create_or_load</strong></a>(self)</dt></dl>
<dl><dt><a name="DataStore-delete"><strong>delete</strong></a>(self, key)</dt></dl>
<dl><dt><a name="DataStore-delete_keys"><strong>delete_keys</strong></a>(self, prefix)</dt><dd><tt>Delete&nbsp;all&nbsp;keys&nbsp;with&nbsp;the&nbsp;given&nbsp;prefix.</tt></dd></dl>
<dl><dt><a name="DataStore-get"><strong>get</strong></a>(self, key, default=None)</dt></dl>
<dl><dt><a name="DataStore-list_keys"><strong>list_keys</strong></a>(self, prefix=None)</dt><dd><tt>Return&nbsp;all&nbsp;key&nbsp;names.&nbsp;If&nbsp;prefix&nbsp;given,&nbsp;return&nbsp;only&nbsp;keys&nbsp;that&nbsp;start&nbsp;with&nbsp;it.</tt></dd></dl>
<dl><dt><a name="DataStore-put"><strong>put</strong></a>(self, key, value)</dt></dl>
<dl><dt><a name="DataStore-save"><strong>save</strong></a>(self)</dt></dl>
<dl><dt><a name="DataStore-save_callback"><strong>save_callback</strong></a>(self, starting=False)</dt><dd><tt>Start&nbsp;the&nbsp;DB&nbsp;save&nbsp;loop.</tt></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<hr>
Data and other attributes defined here:<br>
<dl><dt><strong>initial_version</strong> = 1</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="KeyedDefaultdict">class <strong>KeyedDefaultdict</strong></a>(<a href="collections.html#defaultdict">collections.defaultdict</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Subclass&nbsp;of&nbsp;<a href="collections.html#defaultdict">defaultdict</a>&nbsp;allowing&nbsp;the&nbsp;key&nbsp;to&nbsp;be&nbsp;passed&nbsp;to&nbsp;the&nbsp;default&nbsp;factory.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="structures.html#KeyedDefaultdict">KeyedDefaultdict</a></dd>
<dd><a href="collections.html#defaultdict">collections.defaultdict</a></dd>
<dd><a href="builtins.html#dict">builtins.dict</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="KeyedDefaultdict-__missing__"><strong>__missing__</strong></a>(self, key)</dt><dd><tt><a href="#KeyedDefaultdict-__missing__">__missing__</a>(key)&nbsp;#&nbsp;Called&nbsp;by&nbsp;__getitem__&nbsp;for&nbsp;missing&nbsp;key;&nbsp;pseudo-code:<br>
if&nbsp;self.<strong>default_factory</strong>&nbsp;is&nbsp;None:&nbsp;raise&nbsp;KeyError((key,))<br>
self[key]&nbsp;=&nbsp;value&nbsp;=&nbsp;self.<a href="#KeyedDefaultdict-default_factory">default_factory</a>()<br>
return&nbsp;value</tt></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<hr>
Methods inherited from <a href="collections.html#defaultdict">collections.defaultdict</a>:<br>
<dl><dt><a name="KeyedDefaultdict-__copy__"><strong>__copy__</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-copy">copy</a>()&nbsp;-&gt;&nbsp;a&nbsp;shallow&nbsp;copy&nbsp;of&nbsp;D.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__getattribute__"><strong>__getattribute__</strong></a>(self, name, /)</dt><dd><tt>Return&nbsp;getattr(self,&nbsp;name).</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__init__"><strong>__init__</strong></a>(self, /, *args, **kwargs)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__reduce__"><strong>__reduce__</strong></a>(...)</dt><dd><tt>Return&nbsp;state&nbsp;information&nbsp;for&nbsp;pickling.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__repr__"><strong>__repr__</strong></a>(self, /)</dt><dd><tt>Return&nbsp;repr(self).</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-copy"><strong>copy</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-copy">copy</a>()&nbsp;-&gt;&nbsp;a&nbsp;shallow&nbsp;copy&nbsp;of&nbsp;D.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="collections.html#defaultdict">collections.defaultdict</a>:<br>
<dl><dt><strong>default_factory</strong></dt>
<dd><tt>Factory&nbsp;for&nbsp;default&nbsp;value&nbsp;called&nbsp;by&nbsp;__missing__().</tt></dd>
</dl>
<hr>
Methods inherited from <a href="builtins.html#dict">builtins.dict</a>:<br>
<dl><dt><a name="KeyedDefaultdict-__contains__"><strong>__contains__</strong></a>(self, key, /)</dt><dd><tt>True&nbsp;if&nbsp;D&nbsp;has&nbsp;a&nbsp;key&nbsp;k,&nbsp;else&nbsp;False.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__delitem__"><strong>__delitem__</strong></a>(self, key, /)</dt><dd><tt>Delete&nbsp;self[key].</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__eq__"><strong>__eq__</strong></a>(self, value, /)</dt><dd><tt>Return&nbsp;self==value.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__ge__"><strong>__ge__</strong></a>(self, value, /)</dt><dd><tt>Return&nbsp;self&gt;=value.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__getitem__"><strong>__getitem__</strong></a>(...)</dt><dd><tt>x.<a href="#KeyedDefaultdict-__getitem__">__getitem__</a>(y)&nbsp;&lt;==&gt;&nbsp;x[y]</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__gt__"><strong>__gt__</strong></a>(self, value, /)</dt><dd><tt>Return&nbsp;self&gt;value.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__iter__"><strong>__iter__</strong></a>(self, /)</dt><dd><tt>Implement&nbsp;iter(self).</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__le__"><strong>__le__</strong></a>(self, value, /)</dt><dd><tt>Return&nbsp;self&lt;=value.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__len__"><strong>__len__</strong></a>(self, /)</dt><dd><tt>Return&nbsp;len(self).</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__lt__"><strong>__lt__</strong></a>(self, value, /)</dt><dd><tt>Return&nbsp;self&lt;value.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__ne__"><strong>__ne__</strong></a>(self, value, /)</dt><dd><tt>Return&nbsp;self!=value.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__new__"><strong>__new__</strong></a>(*args, **kwargs)<font color="#909090"><font face="helvetica, arial"> from <a href="builtins.html#type">builtins.type</a></font></font></dt><dd><tt>Create&nbsp;and&nbsp;return&nbsp;a&nbsp;new&nbsp;<a href="builtins.html#object">object</a>.&nbsp;&nbsp;See&nbsp;help(type)&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__setitem__"><strong>__setitem__</strong></a>(self, key, value, /)</dt><dd><tt>Set&nbsp;self[key]&nbsp;to&nbsp;value.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-__sizeof__">__sizeof__</a>()&nbsp;-&gt;&nbsp;size&nbsp;of&nbsp;D&nbsp;in&nbsp;memory,&nbsp;in&nbsp;bytes</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-clear"><strong>clear</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-clear">clear</a>()&nbsp;-&gt;&nbsp;None.&nbsp;&nbsp;Remove&nbsp;all&nbsp;items&nbsp;from&nbsp;D.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-fromkeys"><strong>fromkeys</strong></a>(iterable, value=None, /)<font color="#909090"><font face="helvetica, arial"> from <a href="builtins.html#type">builtins.type</a></font></font></dt><dd><tt>Returns&nbsp;a&nbsp;new&nbsp;dict&nbsp;with&nbsp;keys&nbsp;from&nbsp;iterable&nbsp;and&nbsp;values&nbsp;equal&nbsp;to&nbsp;value.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-get"><strong>get</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-get">get</a>(k[,d])&nbsp;-&gt;&nbsp;D[k]&nbsp;if&nbsp;k&nbsp;in&nbsp;D,&nbsp;else&nbsp;d.&nbsp;&nbsp;d&nbsp;defaults&nbsp;to&nbsp;None.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-items"><strong>items</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-items">items</a>()&nbsp;-&gt;&nbsp;a&nbsp;set-like&nbsp;<a href="builtins.html#object">object</a>&nbsp;providing&nbsp;a&nbsp;view&nbsp;on&nbsp;D's&nbsp;items</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-keys"><strong>keys</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-keys">keys</a>()&nbsp;-&gt;&nbsp;a&nbsp;set-like&nbsp;<a href="builtins.html#object">object</a>&nbsp;providing&nbsp;a&nbsp;view&nbsp;on&nbsp;D's&nbsp;keys</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-pop"><strong>pop</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-pop">pop</a>(k[,d])&nbsp;-&gt;&nbsp;v,&nbsp;remove&nbsp;specified&nbsp;key&nbsp;and&nbsp;return&nbsp;the&nbsp;corresponding&nbsp;value.<br>
If&nbsp;key&nbsp;is&nbsp;not&nbsp;found,&nbsp;d&nbsp;is&nbsp;returned&nbsp;if&nbsp;given,&nbsp;otherwise&nbsp;KeyError&nbsp;is&nbsp;raised</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-popitem"><strong>popitem</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-popitem">popitem</a>()&nbsp;-&gt;&nbsp;(k,&nbsp;v),&nbsp;remove&nbsp;and&nbsp;return&nbsp;some&nbsp;(key,&nbsp;value)&nbsp;pair&nbsp;as&nbsp;a<br>
2-tuple;&nbsp;but&nbsp;raise&nbsp;KeyError&nbsp;if&nbsp;D&nbsp;is&nbsp;empty.</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-setdefault"><strong>setdefault</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-setdefault">setdefault</a>(k[,d])&nbsp;-&gt;&nbsp;D.<a href="#KeyedDefaultdict-get">get</a>(k,d),&nbsp;also&nbsp;set&nbsp;D[k]=d&nbsp;if&nbsp;k&nbsp;not&nbsp;in&nbsp;D</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-update"><strong>update</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-update">update</a>([E,&nbsp;]**F)&nbsp;-&gt;&nbsp;None.&nbsp;&nbsp;Update&nbsp;D&nbsp;from&nbsp;dict/iterable&nbsp;E&nbsp;and&nbsp;F.<br>
If&nbsp;E&nbsp;is&nbsp;present&nbsp;and&nbsp;has&nbsp;a&nbsp;.<a href="#KeyedDefaultdict-keys">keys</a>()&nbsp;method,&nbsp;then&nbsp;does:&nbsp;&nbsp;for&nbsp;k&nbsp;in&nbsp;E:&nbsp;D[k]&nbsp;=&nbsp;E[k]<br>
If&nbsp;E&nbsp;is&nbsp;present&nbsp;and&nbsp;lacks&nbsp;a&nbsp;.<a href="#KeyedDefaultdict-keys">keys</a>()&nbsp;method,&nbsp;then&nbsp;does:&nbsp;&nbsp;for&nbsp;k,&nbsp;v&nbsp;in&nbsp;E:&nbsp;D[k]&nbsp;=&nbsp;v<br>
In&nbsp;either&nbsp;case,&nbsp;this&nbsp;is&nbsp;followed&nbsp;by:&nbsp;for&nbsp;k&nbsp;in&nbsp;F:&nbsp;&nbsp;D[k]&nbsp;=&nbsp;F[k]</tt></dd></dl>
<dl><dt><a name="KeyedDefaultdict-values"><strong>values</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-values">values</a>()&nbsp;-&gt;&nbsp;an&nbsp;<a href="builtins.html#object">object</a>&nbsp;providing&nbsp;a&nbsp;view&nbsp;on&nbsp;D's&nbsp;values</tt></dd></dl>
<hr>
Data and other attributes inherited from <a href="builtins.html#dict">builtins.dict</a>:<br>
<dl><dt><strong>__hash__</strong> = None</dl>
</td></tr></table></td></tr></table>
</body></html>

View File

@ -1,448 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module ts6</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>ts6</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/protocols/ts6.py">/home/gl/pylink/protocols/ts6.py</a></font></td></tr></table>
<p><tt>ts6.py:&nbsp;PyLink&nbsp;protocol&nbsp;module&nbsp;for&nbsp;TS6-based&nbsp;IRCds&nbsp;(charybdis,&nbsp;elemental-ircd).</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="hashlib.html">hashlib</a><br>
<a href="inspect.html">inspect</a><br>
<a href="logging.html">logging</a><br>
<a href="os.html">os</a><br>
</td><td width="25%" valign=top><a href="re.html">re</a><br>
<a href="socket.html">socket</a><br>
<a href="ssl.html">ssl</a><br>
<a href="string.html">string</a><br>
</td><td width="25%" valign=top><a href="structures.html">structures</a><br>
<a href="sys.html">sys</a><br>
<a href="threading.html">threading</a><br>
<a href="time.html">time</a><br>
</td><td width="25%" valign=top><a href="utils.html">utils</a><br>
<a href="world.html">world</a><br>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>(<a href="classes.html#Protocol">classes.Protocol</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="ts6.html#TS6Protocol">TS6Protocol</a>
</font></dt></dl>
</dd>
</dl>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><strong>Class</strong> = <a name="Class">class TS6Protocol</a>(<a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;Protocol&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="ts6.html#TS6Protocol">TS6Protocol</a></dd>
<dd><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="TS6Protocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_472"><strong>handle_472</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;incoming&nbsp;472&nbsp;numeric.<br>
&nbsp;<br>
472&nbsp;is&nbsp;sent&nbsp;to&nbsp;us&nbsp;when&nbsp;one&nbsp;of&nbsp;our&nbsp;clients&nbsp;tries&nbsp;to&nbsp;set&nbsp;a&nbsp;mode&nbsp;the&nbsp;uplink<br>
server&nbsp;doesn't&nbsp;support.&nbsp;In&nbsp;this&nbsp;case,&nbsp;we'll&nbsp;raise&nbsp;a&nbsp;warning&nbsp;to&nbsp;alert<br>
the&nbsp;administrator&nbsp;that&nbsp;certain&nbsp;extensions&nbsp;should&nbsp;be&nbsp;loaded&nbsp;for&nbsp;the&nbsp;best<br>
compatibility.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_bmask"><strong>handle_bmask</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;BMASK&nbsp;commands&nbsp;(ban&nbsp;propagation&nbsp;on&nbsp;burst).</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_capab"><strong>handle_capab</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;CAPAB&nbsp;command,&nbsp;used&nbsp;for&nbsp;TS6&nbsp;capability&nbsp;negotiation.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;CHGHOST&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_encap"><strong>handle_encap</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;ENCAP&nbsp;command&nbsp;-&nbsp;encapsulated&nbsp;TS6&nbsp;commands&nbsp;with&nbsp;a&nbsp;variety&nbsp;of<br>
subcommands&nbsp;used&nbsp;for&nbsp;different&nbsp;purposes.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_euid"><strong>handle_euid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;EUID&nbsp;commands&nbsp;(user&nbsp;introduction).</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;INVITEs.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;channel&nbsp;JOINs.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;user&nbsp;mode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_pass"><strong>handle_pass</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;PASS&nbsp;command,&nbsp;used&nbsp;to&nbsp;send&nbsp;the&nbsp;server's&nbsp;SID&nbsp;and&nbsp;negotiate<br>
passwords&nbsp;on&nbsp;connect.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PING&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PONG&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;1)&nbsp;incoming&nbsp;legacy&nbsp;(no&nbsp;SID)&nbsp;server&nbsp;introductions,<br>
2)&nbsp;Sending&nbsp;server&nbsp;data&nbsp;in&nbsp;initial&nbsp;connection.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;server&nbsp;introductions.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SJOIN&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_tb"><strong>handle_tb</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;topic&nbsp;burst&nbsp;(TB)&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_tmode"><strong>handle_tmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TMODE&nbsp;commands&nbsp;(channel&nbsp;mode&nbsp;change).</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_uid"><strong>handle_uid</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="TS6Protocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;WHOIS&nbsp;commands.<br>
&nbsp;<br>
Note:&nbsp;The&nbsp;core&nbsp;of&nbsp;WHOIS&nbsp;handling&nbsp;is&nbsp;done&nbsp;by&nbsp;coreplugin.py<br>
(IRCd-independent),&nbsp;and&nbsp;not&nbsp;here.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends&nbsp;an&nbsp;INVITE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client..</tt></dd></dl>
<dl><dt><a name="TS6Protocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins&nbsp;a&nbsp;PyLink&nbsp;client&nbsp;to&nbsp;a&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends&nbsp;a&nbsp;kill&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;KNOCK&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends&nbsp;mode&nbsp;changes&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends&nbsp;a&nbsp;PING&nbsp;to&nbsp;a&nbsp;target&nbsp;server.&nbsp;Periodic&nbsp;PINGs&nbsp;are&nbsp;sent&nbsp;to&nbsp;our&nbsp;uplink<br>
automatically&nbsp;by&nbsp;the&nbsp;Irc()&nbsp;internals;&nbsp;plugins&nbsp;shouldn't&nbsp;have&nbsp;to&nbsp;use&nbsp;this.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends&nbsp;an&nbsp;SJOIN&nbsp;for&nbsp;a&nbsp;group&nbsp;of&nbsp;users&nbsp;to&nbsp;a&nbsp;channel.<br>
&nbsp;<br>
The&nbsp;sender&nbsp;should&nbsp;always&nbsp;be&nbsp;a&nbsp;Server&nbsp;ID&nbsp;(SID).&nbsp;TS&nbsp;is&nbsp;optional,&nbsp;and&nbsp;defaults<br>
to&nbsp;the&nbsp;one&nbsp;we've&nbsp;stored&nbsp;in&nbsp;the&nbsp;channel&nbsp;state&nbsp;if&nbsp;not&nbsp;given.<br>
&lt;users&gt;&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;(prefix&nbsp;mode,&nbsp;UID)&nbsp;pairs:<br>
&nbsp;<br>
Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Class-sjoin">sjoin</a>('100',&nbsp;'#test',&nbsp;[('',&nbsp;'100AAABBC'),&nbsp;('o',&nbsp;100AAABBB'),&nbsp;('v',&nbsp;'100AAADDD')])<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Class-sjoin">sjoin</a>(self.<strong>irc</strong>.sid,&nbsp;'#test',&nbsp;[('o',&nbsp;self.<strong>irc</strong>.pseudoclient.uid)])</tt></dd></dl>
<dl><dt><a name="TS6Protocol-spawnClient"><strong>spawnClient</strong></a>(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator', manipulatable=False)</dt><dd><tt>Spawns&nbsp;a&nbsp;new&nbsp;client&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
&nbsp;<br>
Note:&nbsp;No&nbsp;nick&nbsp;collision&nbsp;/&nbsp;valid&nbsp;nickname&nbsp;checks&nbsp;are&nbsp;done&nbsp;here;&nbsp;it&nbsp;is<br>
up&nbsp;to&nbsp;plugins&nbsp;to&nbsp;make&nbsp;sure&nbsp;they&nbsp;don't&nbsp;introduce&nbsp;anything&nbsp;invalid.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;topic&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;This&nbsp;is&nbsp;usually&nbsp;used&nbsp;on&nbsp;burst.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates&nbsp;the&nbsp;hostname&nbsp;of&nbsp;any&nbsp;connected&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>:<br>
<dl><dt><a name="TS6Protocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;ERROR&nbsp;messages&nbsp;-&nbsp;these&nbsp;mean&nbsp;that&nbsp;our&nbsp;uplink&nbsp;has&nbsp;disconnected&nbsp;us!</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;TS6&nbsp;protocols.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions<br>
elsewhere&nbsp;defined&nbsp;protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes<br>
from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;UIDs&nbsp;and&nbsp;SIDs&nbsp;respectively,<br>
whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;will&nbsp;have&nbsp;them&nbsp;set&nbsp;to<br>
the&nbsp;SID&nbsp;of&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;NICK&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_notice"><strong>handle_notice</strong></a> = handle_privmsg(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PART&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUIT&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SAVE&nbsp;messages,&nbsp;used&nbsp;to&nbsp;handle&nbsp;nick&nbsp;collisions.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SQUITs&nbsp;(netsplits).</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TOPIC&nbsp;changes&nbsp;from&nbsp;clients.&nbsp;For&nbsp;topic&nbsp;bursts,<br>
TB&nbsp;(TS6/charybdis)&nbsp;and&nbsp;FTOPIC&nbsp;(InspIRCd)&nbsp;are&nbsp;used&nbsp;instead.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;requests&nbsp;for&nbsp;the&nbsp;PyLink&nbsp;server&nbsp;version.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client,&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar&nbsp;to&nbsp;<a href="#Class-parseArgs">parseArgs</a>(),&nbsp;but&nbsp;stripping&nbsp;leading&nbsp;colons&nbsp;from&nbsp;the&nbsp;first&nbsp;argument<br>
of&nbsp;a&nbsp;line&nbsp;(usually&nbsp;the&nbsp;sender&nbsp;field).</tt></dd></dl>
<dl><dt><a name="TS6Protocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
Note:&nbsp;TS6&nbsp;doesn't&nbsp;use&nbsp;a&nbsp;specific&nbsp;ENDBURST&nbsp;command,&nbsp;so&nbsp;the&nbsp;endburst_delay<br>
option&nbsp;will&nbsp;be&nbsp;ignored&nbsp;if&nbsp;given.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="TS6Protocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="TS6Protocol">class <strong>TS6Protocol</strong></a>(<a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;Protocol&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="ts6.html#TS6Protocol">TS6Protocol</a></dd>
<dd><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="TS6Protocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_472"><strong>handle_472</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;incoming&nbsp;472&nbsp;numeric.<br>
&nbsp;<br>
472&nbsp;is&nbsp;sent&nbsp;to&nbsp;us&nbsp;when&nbsp;one&nbsp;of&nbsp;our&nbsp;clients&nbsp;tries&nbsp;to&nbsp;set&nbsp;a&nbsp;mode&nbsp;the&nbsp;uplink<br>
server&nbsp;doesn't&nbsp;support.&nbsp;In&nbsp;this&nbsp;case,&nbsp;we'll&nbsp;raise&nbsp;a&nbsp;warning&nbsp;to&nbsp;alert<br>
the&nbsp;administrator&nbsp;that&nbsp;certain&nbsp;extensions&nbsp;should&nbsp;be&nbsp;loaded&nbsp;for&nbsp;the&nbsp;best<br>
compatibility.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_bmask"><strong>handle_bmask</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;BMASK&nbsp;commands&nbsp;(ban&nbsp;propagation&nbsp;on&nbsp;burst).</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_capab"><strong>handle_capab</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;CAPAB&nbsp;command,&nbsp;used&nbsp;for&nbsp;TS6&nbsp;capability&nbsp;negotiation.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;CHGHOST&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_encap"><strong>handle_encap</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;ENCAP&nbsp;command&nbsp;-&nbsp;encapsulated&nbsp;TS6&nbsp;commands&nbsp;with&nbsp;a&nbsp;variety&nbsp;of<br>
subcommands&nbsp;used&nbsp;for&nbsp;different&nbsp;purposes.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_euid"><strong>handle_euid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;EUID&nbsp;commands&nbsp;(user&nbsp;introduction).</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;INVITEs.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;channel&nbsp;JOINs.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;user&nbsp;mode&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_pass"><strong>handle_pass</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;PASS&nbsp;command,&nbsp;used&nbsp;to&nbsp;send&nbsp;the&nbsp;server's&nbsp;SID&nbsp;and&nbsp;negotiate<br>
passwords&nbsp;on&nbsp;connect.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PING&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PONG&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;1)&nbsp;incoming&nbsp;legacy&nbsp;(no&nbsp;SID)&nbsp;server&nbsp;introductions,<br>
2)&nbsp;Sending&nbsp;server&nbsp;data&nbsp;in&nbsp;initial&nbsp;connection.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;server&nbsp;introductions.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SJOIN&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_tb"><strong>handle_tb</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;topic&nbsp;burst&nbsp;(TB)&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_tmode"><strong>handle_tmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TMODE&nbsp;commands&nbsp;(channel&nbsp;mode&nbsp;change).</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_uid"><strong>handle_uid</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="TS6Protocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;WHOIS&nbsp;commands.<br>
&nbsp;<br>
Note:&nbsp;The&nbsp;core&nbsp;of&nbsp;WHOIS&nbsp;handling&nbsp;is&nbsp;done&nbsp;by&nbsp;coreplugin.py<br>
(IRCd-independent),&nbsp;and&nbsp;not&nbsp;here.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends&nbsp;an&nbsp;INVITE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client..</tt></dd></dl>
<dl><dt><a name="TS6Protocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins&nbsp;a&nbsp;PyLink&nbsp;client&nbsp;to&nbsp;a&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends&nbsp;a&nbsp;kill&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;KNOCK&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends&nbsp;mode&nbsp;changes&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends&nbsp;a&nbsp;PING&nbsp;to&nbsp;a&nbsp;target&nbsp;server.&nbsp;Periodic&nbsp;PINGs&nbsp;are&nbsp;sent&nbsp;to&nbsp;our&nbsp;uplink<br>
automatically&nbsp;by&nbsp;the&nbsp;Irc()&nbsp;internals;&nbsp;plugins&nbsp;shouldn't&nbsp;have&nbsp;to&nbsp;use&nbsp;this.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends&nbsp;an&nbsp;SJOIN&nbsp;for&nbsp;a&nbsp;group&nbsp;of&nbsp;users&nbsp;to&nbsp;a&nbsp;channel.<br>
&nbsp;<br>
The&nbsp;sender&nbsp;should&nbsp;always&nbsp;be&nbsp;a&nbsp;Server&nbsp;ID&nbsp;(SID).&nbsp;TS&nbsp;is&nbsp;optional,&nbsp;and&nbsp;defaults<br>
to&nbsp;the&nbsp;one&nbsp;we've&nbsp;stored&nbsp;in&nbsp;the&nbsp;channel&nbsp;state&nbsp;if&nbsp;not&nbsp;given.<br>
&lt;users&gt;&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;(prefix&nbsp;mode,&nbsp;UID)&nbsp;pairs:<br>
&nbsp;<br>
Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#TS6Protocol-sjoin">sjoin</a>('100',&nbsp;'#test',&nbsp;[('',&nbsp;'100AAABBC'),&nbsp;('o',&nbsp;100AAABBB'),&nbsp;('v',&nbsp;'100AAADDD')])<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#TS6Protocol-sjoin">sjoin</a>(self.<strong>irc</strong>.sid,&nbsp;'#test',&nbsp;[('o',&nbsp;self.<strong>irc</strong>.pseudoclient.uid)])</tt></dd></dl>
<dl><dt><a name="TS6Protocol-spawnClient"><strong>spawnClient</strong></a>(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator', manipulatable=False)</dt><dd><tt>Spawns&nbsp;a&nbsp;new&nbsp;client&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
&nbsp;<br>
Note:&nbsp;No&nbsp;nick&nbsp;collision&nbsp;/&nbsp;valid&nbsp;nickname&nbsp;checks&nbsp;are&nbsp;done&nbsp;here;&nbsp;it&nbsp;is<br>
up&nbsp;to&nbsp;plugins&nbsp;to&nbsp;make&nbsp;sure&nbsp;they&nbsp;don't&nbsp;introduce&nbsp;anything&nbsp;invalid.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;topic&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;This&nbsp;is&nbsp;usually&nbsp;used&nbsp;on&nbsp;burst.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates&nbsp;the&nbsp;hostname&nbsp;of&nbsp;any&nbsp;connected&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>:<br>
<dl><dt><a name="TS6Protocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;ERROR&nbsp;messages&nbsp;-&nbsp;these&nbsp;mean&nbsp;that&nbsp;our&nbsp;uplink&nbsp;has&nbsp;disconnected&nbsp;us!</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;TS6&nbsp;protocols.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions<br>
elsewhere&nbsp;defined&nbsp;protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes<br>
from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;UIDs&nbsp;and&nbsp;SIDs&nbsp;respectively,<br>
whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;will&nbsp;have&nbsp;them&nbsp;set&nbsp;to<br>
the&nbsp;SID&nbsp;of&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;NICK&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_notice"><strong>handle_notice</strong></a> = handle_privmsg(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PART&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUIT&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SAVE&nbsp;messages,&nbsp;used&nbsp;to&nbsp;handle&nbsp;nick&nbsp;collisions.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SQUITs&nbsp;(netsplits).</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TOPIC&nbsp;changes&nbsp;from&nbsp;clients.&nbsp;For&nbsp;topic&nbsp;bursts,<br>
TB&nbsp;(TS6/charybdis)&nbsp;and&nbsp;FTOPIC&nbsp;(InspIRCd)&nbsp;are&nbsp;used&nbsp;instead.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;requests&nbsp;for&nbsp;the&nbsp;PyLink&nbsp;server&nbsp;version.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client,&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar&nbsp;to&nbsp;<a href="#TS6Protocol-parseArgs">parseArgs</a>(),&nbsp;but&nbsp;stripping&nbsp;leading&nbsp;colons&nbsp;from&nbsp;the&nbsp;first&nbsp;argument<br>
of&nbsp;a&nbsp;line&nbsp;(usually&nbsp;the&nbsp;sender&nbsp;field).</tt></dd></dl>
<dl><dt><a name="TS6Protocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
Note:&nbsp;TS6&nbsp;doesn't&nbsp;use&nbsp;a&nbsp;specific&nbsp;ENDBURST&nbsp;command,&nbsp;so&nbsp;the&nbsp;endburst_delay<br>
option&nbsp;will&nbsp;be&nbsp;ignored&nbsp;if&nbsp;given.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="TS6Protocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="TS6Protocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>conf</strong> = {'bot': {'nick': 'PyLink', 'realname': 'PyLink Service Client', 'serverdesc': 'PyLink unit tests', 'user': 'pylink'}, 'logging': {'stdout': 'CRITICAL'}, 'servers': defaultdict(&lt;function &lt;lambda&gt; at 0x7f3f91da9488&gt;, {})}<br>
<strong>confname</strong> = 'testconf'<br>
<strong>curdir</strong> = '/home/gl/pylink'<br>
<strong>files</strong> = None<br>
<strong>log</strong> = &lt;logging.RootLogger object&gt;<br>
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
<strong>logformatter</strong> = &lt;logging.Formatter object&gt;<br>
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
</body></html>

View File

@ -1,249 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module ts6_common</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>ts6_common</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/protocols/ts6_common.py">/home/gl/pylink/protocols/ts6_common.py</a></font></td></tr></table>
<p><tt>ts6_common.py:&nbsp;Common&nbsp;base&nbsp;protocol&nbsp;class&nbsp;with&nbsp;functions&nbsp;shared&nbsp;by&nbsp;the&nbsp;UnrealIRCd,&nbsp;InspIRCd,&nbsp;and&nbsp;TS6&nbsp;protocol&nbsp;modules.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="hashlib.html">hashlib</a><br>
<a href="inspect.html">inspect</a><br>
<a href="logging.html">logging</a><br>
<a href="os.html">os</a><br>
</td><td width="25%" valign=top><a href="socket.html">socket</a><br>
<a href="ssl.html">ssl</a><br>
<a href="string.html">string</a><br>
<a href="structures.html">structures</a><br>
</td><td width="25%" valign=top><a href="sys.html">sys</a><br>
<a href="threading.html">threading</a><br>
<a href="time.html">time</a><br>
<a href="utils.html">utils</a><br>
</td><td width="25%" valign=top><a href="world.html">world</a><br>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="builtins.html#object">builtins.object</a>
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="ts6_common.html#TS6SIDGenerator">TS6SIDGenerator</a>
</font></dt></dl>
</dd>
<dt><font face="helvetica, arial"><a href="classes.html#Protocol">classes.Protocol</a>(<a href="builtins.html#object">builtins.object</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="ts6_common.html#TS6BaseProtocol">TS6BaseProtocol</a>
</font></dt></dl>
</dd>
<dt><font face="helvetica, arial"><a href="utils.html#IncrementalUIDGenerator">utils.IncrementalUIDGenerator</a>(<a href="builtins.html#object">builtins.object</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="ts6_common.html#TS6UIDGenerator">TS6UIDGenerator</a>
</font></dt></dl>
</dd>
</dl>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="TS6BaseProtocol">class <strong>TS6BaseProtocol</strong></a>(<a href="classes.html#Protocol">classes.Protocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;<a href="classes.html#Protocol">Protocol</a>&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="ts6_common.html#TS6BaseProtocol">TS6BaseProtocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="TS6BaseProtocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;ERROR&nbsp;messages&nbsp;-&nbsp;these&nbsp;mean&nbsp;that&nbsp;our&nbsp;uplink&nbsp;has&nbsp;disconnected&nbsp;us!</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;TS6&nbsp;protocols.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions<br>
elsewhere&nbsp;defined&nbsp;protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes<br>
from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;UIDs&nbsp;and&nbsp;SIDs&nbsp;respectively,<br>
whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;will&nbsp;have&nbsp;them&nbsp;set&nbsp;to<br>
the&nbsp;SID&nbsp;of&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;NICK&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_notice"><strong>handle_notice</strong></a> = <a href="#TS6BaseProtocol-handle_privmsg">handle_privmsg</a>(self, source, command, args)</dt></dl>
<dl><dt><a name="TS6BaseProtocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PART&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUIT&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SAVE&nbsp;messages,&nbsp;used&nbsp;to&nbsp;handle&nbsp;nick&nbsp;collisions.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SQUITs&nbsp;(netsplits).</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;TOPIC&nbsp;changes&nbsp;from&nbsp;clients.&nbsp;For&nbsp;topic&nbsp;bursts,<br>
TB&nbsp;(TS6/charybdis)&nbsp;and&nbsp;FTOPIC&nbsp;(InspIRCd)&nbsp;are&nbsp;used&nbsp;instead.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;requests&nbsp;for&nbsp;the&nbsp;PyLink&nbsp;server&nbsp;version.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client,&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar&nbsp;to&nbsp;<a href="#TS6BaseProtocol-parseArgs">parseArgs</a>(),&nbsp;but&nbsp;stripping&nbsp;leading&nbsp;colons&nbsp;from&nbsp;the&nbsp;first&nbsp;argument<br>
of&nbsp;a&nbsp;line&nbsp;(usually&nbsp;the&nbsp;sender&nbsp;field).</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
Note:&nbsp;TS6&nbsp;doesn't&nbsp;use&nbsp;a&nbsp;specific&nbsp;ENDBURST&nbsp;command,&nbsp;so&nbsp;the&nbsp;endburst_delay<br>
option&nbsp;will&nbsp;be&nbsp;ignored&nbsp;if&nbsp;given.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="TS6BaseProtocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="TS6BaseProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="TS6SIDGenerator">class <strong>TS6SIDGenerator</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>TS6&nbsp;SID&nbsp;Generator.&nbsp;&lt;query&gt;&nbsp;is&nbsp;a&nbsp;3&nbsp;character&nbsp;string&nbsp;with&nbsp;any&nbsp;combination&nbsp;of<br>
uppercase&nbsp;letters,&nbsp;digits,&nbsp;and&nbsp;#'s.&nbsp;it&nbsp;must&nbsp;contain&nbsp;at&nbsp;least&nbsp;one&nbsp;#,<br>
which&nbsp;are&nbsp;used&nbsp;by&nbsp;the&nbsp;generator&nbsp;as&nbsp;a&nbsp;wildcard.&nbsp;On&nbsp;every&nbsp;<a href="#TS6SIDGenerator-next_sid">next_sid</a>()&nbsp;call,<br>
the&nbsp;first&nbsp;available&nbsp;wildcard&nbsp;character&nbsp;(from&nbsp;the&nbsp;right)&nbsp;will&nbsp;be<br>
incremented&nbsp;to&nbsp;generate&nbsp;the&nbsp;next&nbsp;SID.<br>
&nbsp;<br>
When&nbsp;there&nbsp;are&nbsp;no&nbsp;more&nbsp;available&nbsp;SIDs&nbsp;left&nbsp;(SIDs&nbsp;are&nbsp;not&nbsp;reused,&nbsp;only<br>
incremented),&nbsp;RuntimeError&nbsp;is&nbsp;raised.<br>
&nbsp;<br>
Example&nbsp;queries:<br>
&nbsp;&nbsp;&nbsp;&nbsp;"1#A"&nbsp;would&nbsp;give:&nbsp;10A,&nbsp;11A,&nbsp;12A&nbsp;...&nbsp;19A,&nbsp;1AA,&nbsp;1BA&nbsp;...&nbsp;1ZA&nbsp;(36&nbsp;total&nbsp;results)<br>
&nbsp;&nbsp;&nbsp;&nbsp;"#BQ"&nbsp;would&nbsp;give:&nbsp;0BQ,&nbsp;1BQ,&nbsp;2BQ&nbsp;...&nbsp;9BQ&nbsp;(10&nbsp;total&nbsp;results)<br>
&nbsp;&nbsp;&nbsp;&nbsp;"6##"&nbsp;would&nbsp;give:&nbsp;600,&nbsp;601,&nbsp;602,&nbsp;...&nbsp;60Y,&nbsp;60Z,&nbsp;610,&nbsp;611,&nbsp;...&nbsp;6ZZ&nbsp;(1296&nbsp;total&nbsp;results)<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="TS6SIDGenerator-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="TS6SIDGenerator-increment"><strong>increment</strong></a>(self, pos=2)</dt><dd><tt>Increments&nbsp;the&nbsp;SID&nbsp;generator&nbsp;to&nbsp;the&nbsp;next&nbsp;available&nbsp;SID.</tt></dd></dl>
<dl><dt><a name="TS6SIDGenerator-next_sid"><strong>next_sid</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;next&nbsp;unused&nbsp;TS6&nbsp;SID&nbsp;for&nbsp;the&nbsp;server.</tt></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="TS6UIDGenerator">class <strong>TS6UIDGenerator</strong></a>(<a href="utils.html#IncrementalUIDGenerator">utils.IncrementalUIDGenerator</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Implements&nbsp;an&nbsp;incremental&nbsp;TS6&nbsp;UID&nbsp;Generator.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="ts6_common.html#TS6UIDGenerator">TS6UIDGenerator</a></dd>
<dd><a href="utils.html#IncrementalUIDGenerator">utils.IncrementalUIDGenerator</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="TS6UIDGenerator-__init__"><strong>__init__</strong></a>(self, sid)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<hr>
Methods inherited from <a href="utils.html#IncrementalUIDGenerator">utils.IncrementalUIDGenerator</a>:<br>
<dl><dt><a name="TS6UIDGenerator-increment"><strong>increment</strong></a>(self, pos=None)</dt><dd><tt>Increments&nbsp;the&nbsp;UID&nbsp;generator&nbsp;to&nbsp;the&nbsp;next&nbsp;available&nbsp;UID.</tt></dd></dl>
<dl><dt><a name="TS6UIDGenerator-next_uid"><strong>next_uid</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;next&nbsp;unused&nbsp;UID&nbsp;for&nbsp;the&nbsp;server.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="utils.html#IncrementalUIDGenerator">utils.IncrementalUIDGenerator</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>conf</strong> = {'bot': {'nick': 'PyLink', 'realname': 'PyLink Service Client', 'serverdesc': 'PyLink unit tests', 'user': 'pylink'}, 'logging': {'stdout': 'CRITICAL'}, 'servers': defaultdict(&lt;function &lt;lambda&gt; at 0x7f1e3a24e488&gt;, {})}<br>
<strong>confname</strong> = 'testconf'<br>
<strong>curdir</strong> = '/home/gl/pylink'<br>
<strong>files</strong> = None<br>
<strong>log</strong> = &lt;logging.RootLogger object&gt;<br>
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
<strong>logformatter</strong> = &lt;logging.Formatter object&gt;<br>
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
</body></html>

View File

@ -1,455 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module unreal</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>unreal</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/protocols/unreal.py">/home/gl/pylink/protocols/unreal.py</a></font></td></tr></table>
<p><tt>unreal.py:&nbsp;UnrealIRCd&nbsp;4.0&nbsp;protocol&nbsp;module&nbsp;for&nbsp;PyLink.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="codecs.html">codecs</a><br>
<a href="hashlib.html">hashlib</a><br>
<a href="inspect.html">inspect</a><br>
<a href="logging.html">logging</a><br>
</td><td width="25%" valign=top><a href="os.html">os</a><br>
<a href="re.html">re</a><br>
<a href="socket.html">socket</a><br>
<a href="ssl.html">ssl</a><br>
</td><td width="25%" valign=top><a href="string.html">string</a><br>
<a href="structures.html">structures</a><br>
<a href="sys.html">sys</a><br>
<a href="threading.html">threading</a><br>
</td><td width="25%" valign=top><a href="time.html">time</a><br>
<a href="utils.html">utils</a><br>
<a href="world.html">world</a><br>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>(<a href="classes.html#Protocol">classes.Protocol</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="unreal.html#UnrealProtocol">UnrealProtocol</a>
</font></dt></dl>
</dd>
</dl>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><strong>Class</strong> = <a name="Class">class UnrealProtocol</a>(<a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;Protocol&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="unreal.html#UnrealProtocol">UnrealProtocol</a></dd>
<dd><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="UnrealProtocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-checkCloakChange"><strong>checkCloakChange</strong></a>(self, uid, parsedmodes)</dt><dd><tt>Checks&nbsp;whether&nbsp;+x/-x&nbsp;was&nbsp;set&nbsp;in&nbsp;the&nbsp;mode&nbsp;query,&nbsp;and&nbsp;changes&nbsp;the<br>
hostname&nbsp;of&nbsp;the&nbsp;user&nbsp;given&nbsp;to&nbsp;or&nbsp;from&nbsp;their&nbsp;cloaked&nbsp;host&nbsp;if&nbsp;True.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;CHGHOST,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;hostname&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_chgident"><strong>handle_chgident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;CHGIDENT,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;ident&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_chgname"><strong>handle_chgname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;CHGNAME,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;real&nbsp;name/gecos&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_eos"><strong>handle_eos</strong></a>(self, numeric, command, args)</dt><dd><tt>EOS&nbsp;is&nbsp;used&nbsp;to&nbsp;denote&nbsp;end&nbsp;of&nbsp;burst.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;INVITEs.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;UnrealIRCd&nbsp;JOIN&nbsp;command.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_kill"><strong>handle_kill</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;NICK&nbsp;changes,&nbsp;and&nbsp;legacy&nbsp;NICK&nbsp;introductions&nbsp;from&nbsp;pre-4.0&nbsp;servers.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_notice"><strong>handle_notice</strong></a> = <a href="#UnrealProtocol-handle_privmsg">handle_privmsg</a>(self, source, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_pass"><strong>handle_pass</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_ping"><strong>handle_ping</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_protoctl"><strong>handle_protoctl</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;protocol&nbsp;negotiation.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;SERVER&nbsp;command,&nbsp;which&nbsp;is&nbsp;used&nbsp;for&nbsp;both&nbsp;authentication&nbsp;and<br>
introducing&nbsp;legacy&nbsp;(non-SID)&nbsp;servers.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_sethost"><strong>handle_sethost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;CHGHOST,&nbsp;used&nbsp;for&nbsp;self&nbsp;hostname&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_setident"><strong>handle_setident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;SETIDENT,&nbsp;used&nbsp;for&nbsp;self&nbsp;ident&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_setname"><strong>handle_setname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;SETNAME,&nbsp;used&nbsp;for&nbsp;self&nbsp;real&nbsp;name/gecos&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;SID&nbsp;command,&nbsp;used&nbsp;for&nbsp;introducing&nbsp;remote&nbsp;servers&nbsp;by&nbsp;our&nbsp;uplink.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;UnrealIRCd&nbsp;SJOIN&nbsp;command.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;SQUIT&nbsp;command.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_svs2mode"><strong>handle_svs2mode</strong></a>(self, sender, command, args)</dt><dd><tt>Handles&nbsp;SVS2MODE,&nbsp;which&nbsp;sets&nbsp;services&nbsp;login&nbsp;information,&nbsp;and&nbsp;user&nbsp;modes&nbsp;on<br>
the&nbsp;given&nbsp;target.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_svsmode"><strong>handle_svsmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;SVSMODE,&nbsp;used&nbsp;by&nbsp;services&nbsp;for&nbsp;setting&nbsp;user&nbsp;modes&nbsp;on&nbsp;others.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;TOPIC&nbsp;command.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_uid"><strong>handle_uid</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_umode2"><strong>handle_umode2</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;UMODE2,&nbsp;used&nbsp;to&nbsp;set&nbsp;user&nbsp;modes&nbsp;on&nbsp;oneself.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;WHOIS&nbsp;queries.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends&nbsp;an&nbsp;INVITE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client..</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins&nbsp;a&nbsp;PyLink&nbsp;client&nbsp;to&nbsp;a&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends&nbsp;a&nbsp;kill&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;KNOCK&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends&nbsp;mode&nbsp;changes&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.&nbsp;The&nbsp;mode&nbsp;list&nbsp;should&nbsp;be<br>
a&nbsp;list&nbsp;of&nbsp;(mode,&nbsp;arg)&nbsp;tuples,&nbsp;i.e.&nbsp;the&nbsp;format&nbsp;of&nbsp;utils.parseModes()&nbsp;output.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends&nbsp;a&nbsp;PING&nbsp;to&nbsp;a&nbsp;target&nbsp;server.&nbsp;Periodic&nbsp;PINGs&nbsp;are&nbsp;sent&nbsp;to&nbsp;our&nbsp;uplink<br>
automatically&nbsp;by&nbsp;the&nbsp;Irc()&nbsp;internals;&nbsp;plugins&nbsp;shouldn't&nbsp;have&nbsp;to&nbsp;use&nbsp;this.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends&nbsp;an&nbsp;SJOIN&nbsp;for&nbsp;a&nbsp;group&nbsp;of&nbsp;users&nbsp;to&nbsp;a&nbsp;channel.<br>
&nbsp;<br>
The&nbsp;sender&nbsp;should&nbsp;always&nbsp;be&nbsp;a&nbsp;server&nbsp;(SID).&nbsp;TS&nbsp;is&nbsp;optional,&nbsp;and&nbsp;defaults<br>
to&nbsp;the&nbsp;one&nbsp;we've&nbsp;stored&nbsp;in&nbsp;the&nbsp;channel&nbsp;state&nbsp;if&nbsp;not&nbsp;given.<br>
&lt;users&gt;&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;(prefix&nbsp;mode,&nbsp;UID)&nbsp;pairs:<br>
&nbsp;<br>
Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Class-sjoin">sjoin</a>('100',&nbsp;'#test',&nbsp;[('',&nbsp;'100AAABBC'),&nbsp;('o',&nbsp;100AAABBB'),&nbsp;('v',&nbsp;'100AAADDD')])<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#Class-sjoin">sjoin</a>(self.<strong>irc</strong>.sid,&nbsp;'#test',&nbsp;[('o',&nbsp;self.<strong>irc</strong>.pseudoclient.uid)])<br>
&nbsp;<br>
Note&nbsp;that&nbsp;for&nbsp;UnrealIRCd,&nbsp;no&nbsp;mode&nbsp;data&nbsp;is&nbsp;sent&nbsp;in&nbsp;an&nbsp;SJOIN&nbsp;command,&nbsp;only<br>
The&nbsp;channel&nbsp;name,&nbsp;TS,&nbsp;and&nbsp;user&nbsp;list.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-spawnClient"><strong>spawnClient</strong></a>(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator', manipulatable=False)</dt><dd><tt>Spawns&nbsp;a&nbsp;new&nbsp;client&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
&nbsp;<br>
Note:&nbsp;No&nbsp;nick&nbsp;collision&nbsp;/&nbsp;valid&nbsp;nickname&nbsp;checks&nbsp;are&nbsp;done&nbsp;here;&nbsp;it&nbsp;is<br>
up&nbsp;to&nbsp;plugins&nbsp;to&nbsp;make&nbsp;sure&nbsp;they&nbsp;don't&nbsp;introduce&nbsp;anything&nbsp;invalid.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates&nbsp;the&nbsp;ident,&nbsp;host,&nbsp;or&nbsp;realname&nbsp;of&nbsp;any&nbsp;connected&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>:<br>
<dl><dt><a name="UnrealProtocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;ERROR&nbsp;messages&nbsp;-&nbsp;these&nbsp;mean&nbsp;that&nbsp;our&nbsp;uplink&nbsp;has&nbsp;disconnected&nbsp;us!</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;TS6&nbsp;protocols.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions<br>
elsewhere&nbsp;defined&nbsp;protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes<br>
from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;UIDs&nbsp;and&nbsp;SIDs&nbsp;respectively,<br>
whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;will&nbsp;have&nbsp;them&nbsp;set&nbsp;to<br>
the&nbsp;SID&nbsp;of&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PART&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUIT&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SAVE&nbsp;messages,&nbsp;used&nbsp;to&nbsp;handle&nbsp;nick&nbsp;collisions.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;requests&nbsp;for&nbsp;the&nbsp;PyLink&nbsp;server&nbsp;version.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client,&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar&nbsp;to&nbsp;<a href="#Class-parseArgs">parseArgs</a>(),&nbsp;but&nbsp;stripping&nbsp;leading&nbsp;colons&nbsp;from&nbsp;the&nbsp;first&nbsp;argument<br>
of&nbsp;a&nbsp;line&nbsp;(usually&nbsp;the&nbsp;sender&nbsp;field).</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
Note:&nbsp;TS6&nbsp;doesn't&nbsp;use&nbsp;a&nbsp;specific&nbsp;ENDBURST&nbsp;command,&nbsp;so&nbsp;the&nbsp;endburst_delay<br>
option&nbsp;will&nbsp;be&nbsp;ignored&nbsp;if&nbsp;given.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="UnrealProtocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="UnrealProtocol">class <strong>UnrealProtocol</strong></a>(<a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Base&nbsp;Protocol&nbsp;module&nbsp;class&nbsp;for&nbsp;PyLink.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="unreal.html#UnrealProtocol">UnrealProtocol</a></dd>
<dd><a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a></dd>
<dd><a href="classes.html#Protocol">classes.Protocol</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Methods defined here:<br>
<dl><dt><a name="UnrealProtocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-checkCloakChange"><strong>checkCloakChange</strong></a>(self, uid, parsedmodes)</dt><dd><tt>Checks&nbsp;whether&nbsp;+x/-x&nbsp;was&nbsp;set&nbsp;in&nbsp;the&nbsp;mode&nbsp;query,&nbsp;and&nbsp;changes&nbsp;the<br>
hostname&nbsp;of&nbsp;the&nbsp;user&nbsp;given&nbsp;to&nbsp;or&nbsp;from&nbsp;their&nbsp;cloaked&nbsp;host&nbsp;if&nbsp;True.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes&nbsp;a&nbsp;connection&nbsp;to&nbsp;a&nbsp;server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;CHGHOST,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;hostname&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_chgident"><strong>handle_chgident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;CHGIDENT,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;ident&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_chgname"><strong>handle_chgname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;CHGNAME,&nbsp;used&nbsp;for&nbsp;denoting&nbsp;real&nbsp;name/gecos&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_eos"><strong>handle_eos</strong></a>(self, numeric, command, args)</dt><dd><tt>EOS&nbsp;is&nbsp;used&nbsp;to&nbsp;denote&nbsp;end&nbsp;of&nbsp;burst.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;INVITEs.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;UnrealIRCd&nbsp;JOIN&nbsp;command.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_kill"><strong>handle_kill</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KILLs.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;NICK&nbsp;changes,&nbsp;and&nbsp;legacy&nbsp;NICK&nbsp;introductions&nbsp;from&nbsp;pre-4.0&nbsp;servers.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_notice"><strong>handle_notice</strong></a> = <a href="#UnrealProtocol-handle_privmsg">handle_privmsg</a>(self, source, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_pass"><strong>handle_pass</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_ping"><strong>handle_ping</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PRIVMSG/NOTICE.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_protoctl"><strong>handle_protoctl</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;protocol&nbsp;negotiation.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;SERVER&nbsp;command,&nbsp;which&nbsp;is&nbsp;used&nbsp;for&nbsp;both&nbsp;authentication&nbsp;and<br>
introducing&nbsp;legacy&nbsp;(non-SID)&nbsp;servers.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_sethost"><strong>handle_sethost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;CHGHOST,&nbsp;used&nbsp;for&nbsp;self&nbsp;hostname&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_setident"><strong>handle_setident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;SETIDENT,&nbsp;used&nbsp;for&nbsp;self&nbsp;ident&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_setname"><strong>handle_setname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;SETNAME,&nbsp;used&nbsp;for&nbsp;self&nbsp;real&nbsp;name/gecos&nbsp;changes.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;SID&nbsp;command,&nbsp;used&nbsp;for&nbsp;introducing&nbsp;remote&nbsp;servers&nbsp;by&nbsp;our&nbsp;uplink.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;UnrealIRCd&nbsp;SJOIN&nbsp;command.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;SQUIT&nbsp;command.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_svs2mode"><strong>handle_svs2mode</strong></a>(self, sender, command, args)</dt><dd><tt>Handles&nbsp;SVS2MODE,&nbsp;which&nbsp;sets&nbsp;services&nbsp;login&nbsp;information,&nbsp;and&nbsp;user&nbsp;modes&nbsp;on<br>
the&nbsp;given&nbsp;target.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_svsmode"><strong>handle_svsmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;SVSMODE,&nbsp;used&nbsp;by&nbsp;services&nbsp;for&nbsp;setting&nbsp;user&nbsp;modes&nbsp;on&nbsp;others.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;the&nbsp;TOPIC&nbsp;command.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_uid"><strong>handle_uid</strong></a>(self, numeric, command, args)</dt></dl>
<dl><dt><a name="UnrealProtocol-handle_umode2"><strong>handle_umode2</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;UMODE2,&nbsp;used&nbsp;to&nbsp;set&nbsp;user&nbsp;modes&nbsp;on&nbsp;oneself.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;WHOIS&nbsp;queries.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends&nbsp;an&nbsp;INVITE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client..</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins&nbsp;a&nbsp;PyLink&nbsp;client&nbsp;to&nbsp;a&nbsp;channel.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends&nbsp;a&nbsp;kill&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;KNOCK&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends&nbsp;mode&nbsp;changes&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.&nbsp;The&nbsp;mode&nbsp;list&nbsp;should&nbsp;be<br>
a&nbsp;list&nbsp;of&nbsp;(mode,&nbsp;arg)&nbsp;tuples,&nbsp;i.e.&nbsp;the&nbsp;format&nbsp;of&nbsp;utils.parseModes()&nbsp;output.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends&nbsp;a&nbsp;PING&nbsp;to&nbsp;a&nbsp;target&nbsp;server.&nbsp;Periodic&nbsp;PINGs&nbsp;are&nbsp;sent&nbsp;to&nbsp;our&nbsp;uplink<br>
automatically&nbsp;by&nbsp;the&nbsp;Irc()&nbsp;internals;&nbsp;plugins&nbsp;shouldn't&nbsp;have&nbsp;to&nbsp;use&nbsp;this.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends&nbsp;an&nbsp;SJOIN&nbsp;for&nbsp;a&nbsp;group&nbsp;of&nbsp;users&nbsp;to&nbsp;a&nbsp;channel.<br>
&nbsp;<br>
The&nbsp;sender&nbsp;should&nbsp;always&nbsp;be&nbsp;a&nbsp;server&nbsp;(SID).&nbsp;TS&nbsp;is&nbsp;optional,&nbsp;and&nbsp;defaults<br>
to&nbsp;the&nbsp;one&nbsp;we've&nbsp;stored&nbsp;in&nbsp;the&nbsp;channel&nbsp;state&nbsp;if&nbsp;not&nbsp;given.<br>
&lt;users&gt;&nbsp;is&nbsp;a&nbsp;list&nbsp;of&nbsp;(prefix&nbsp;mode,&nbsp;UID)&nbsp;pairs:<br>
&nbsp;<br>
Example&nbsp;uses:<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#UnrealProtocol-sjoin">sjoin</a>('100',&nbsp;'#test',&nbsp;[('',&nbsp;'100AAABBC'),&nbsp;('o',&nbsp;100AAABBB'),&nbsp;('v',&nbsp;'100AAADDD')])<br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#UnrealProtocol-sjoin">sjoin</a>(self.<strong>irc</strong>.sid,&nbsp;'#test',&nbsp;[('o',&nbsp;self.<strong>irc</strong>.pseudoclient.uid)])<br>
&nbsp;<br>
Note&nbsp;that&nbsp;for&nbsp;UnrealIRCd,&nbsp;no&nbsp;mode&nbsp;data&nbsp;is&nbsp;sent&nbsp;in&nbsp;an&nbsp;SJOIN&nbsp;command,&nbsp;only<br>
The&nbsp;channel&nbsp;name,&nbsp;TS,&nbsp;and&nbsp;user&nbsp;list.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-spawnClient"><strong>spawnClient</strong></a>(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype='IRC Operator', manipulatable=False)</dt><dd><tt>Spawns&nbsp;a&nbsp;new&nbsp;client&nbsp;with&nbsp;the&nbsp;given&nbsp;options.<br>
&nbsp;<br>
Note:&nbsp;No&nbsp;nick&nbsp;collision&nbsp;/&nbsp;valid&nbsp;nickname&nbsp;checks&nbsp;are&nbsp;done&nbsp;here;&nbsp;it&nbsp;is<br>
up&nbsp;to&nbsp;plugins&nbsp;to&nbsp;make&nbsp;sure&nbsp;they&nbsp;don't&nbsp;introduce&nbsp;anything&nbsp;invalid.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates&nbsp;the&nbsp;ident,&nbsp;host,&nbsp;or&nbsp;realname&nbsp;of&nbsp;any&nbsp;connected&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="ts6_common.html#TS6BaseProtocol">ts6_common.TS6BaseProtocol</a>:<br>
<dl><dt><a name="UnrealProtocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends&nbsp;an&nbsp;AWAY&nbsp;message&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.&nbsp;&lt;text&gt;&nbsp;can&nbsp;be&nbsp;an&nbsp;empty&nbsp;string<br>
to&nbsp;unset&nbsp;AWAY&nbsp;status.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;AWAY&nbsp;messages.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;ERROR&nbsp;messages&nbsp;-&nbsp;these&nbsp;mean&nbsp;that&nbsp;our&nbsp;uplink&nbsp;has&nbsp;disconnected&nbsp;us!</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event&nbsp;handler&nbsp;for&nbsp;TS6&nbsp;protocols.<br>
&nbsp;<br>
This&nbsp;passes&nbsp;most&nbsp;commands&nbsp;to&nbsp;the&nbsp;various&nbsp;handle_ABCD()&nbsp;functions<br>
elsewhere&nbsp;defined&nbsp;protocol&nbsp;modules,&nbsp;coersing&nbsp;various&nbsp;sender&nbsp;prefixes<br>
from&nbsp;nicks&nbsp;and&nbsp;server&nbsp;names&nbsp;to&nbsp;UIDs&nbsp;and&nbsp;SIDs&nbsp;respectively,<br>
whenever&nbsp;possible.<br>
&nbsp;<br>
Commands&nbsp;sent&nbsp;without&nbsp;an&nbsp;explicit&nbsp;sender&nbsp;prefix&nbsp;will&nbsp;have&nbsp;them&nbsp;set&nbsp;to<br>
the&nbsp;SID&nbsp;of&nbsp;the&nbsp;uplink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;KICKs.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;PART&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;QUIT&nbsp;commands.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;incoming&nbsp;SAVE&nbsp;messages,&nbsp;used&nbsp;to&nbsp;handle&nbsp;nick&nbsp;collisions.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles&nbsp;requests&nbsp;for&nbsp;the&nbsp;PyLink&nbsp;server&nbsp;version.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends&nbsp;kicks&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client/server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;PRIVMSG&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes&nbsp;the&nbsp;nick&nbsp;of&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;NOTICE&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends&nbsp;raw&nbsp;numerics&nbsp;from&nbsp;a&nbsp;server&nbsp;to&nbsp;a&nbsp;remote&nbsp;client,&nbsp;used&nbsp;for&nbsp;WHOIS<br>
replies.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar&nbsp;to&nbsp;<a href="#UnrealProtocol-parseArgs">parseArgs</a>(),&nbsp;but&nbsp;stripping&nbsp;leading&nbsp;colons&nbsp;from&nbsp;the&nbsp;first&nbsp;argument<br>
of&nbsp;a&nbsp;line&nbsp;(usually&nbsp;the&nbsp;sender&nbsp;field).</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends&nbsp;a&nbsp;part&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-spawnServer"><strong>spawnServer</strong></a>(self, name, sid=None, uplink=None, desc=None, endburst_delay=0)</dt><dd><tt>Spawns&nbsp;a&nbsp;server&nbsp;off&nbsp;a&nbsp;PyLink&nbsp;server.&nbsp;desc&nbsp;(server&nbsp;description)<br>
defaults&nbsp;to&nbsp;the&nbsp;one&nbsp;in&nbsp;the&nbsp;config.&nbsp;uplink&nbsp;defaults&nbsp;to&nbsp;the&nbsp;main&nbsp;PyLink<br>
server,&nbsp;and&nbsp;sid&nbsp;(the&nbsp;server&nbsp;ID)&nbsp;is&nbsp;automatically&nbsp;generated&nbsp;if&nbsp;not<br>
given.<br>
&nbsp;<br>
Note:&nbsp;TS6&nbsp;doesn't&nbsp;use&nbsp;a&nbsp;specific&nbsp;ENDBURST&nbsp;command,&nbsp;so&nbsp;the&nbsp;endburst_delay<br>
option&nbsp;will&nbsp;be&nbsp;ignored&nbsp;if&nbsp;given.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-squit"><strong>squit</strong></a>(self, source, target, text='No reason given')</dt><dd><tt>SQUITs&nbsp;a&nbsp;PyLink&nbsp;server.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends&nbsp;a&nbsp;TOPIC&nbsp;change&nbsp;from&nbsp;a&nbsp;PyLink&nbsp;client.</tt></dd></dl>
<hr>
Methods inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><a name="UnrealProtocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses&nbsp;a&nbsp;string&nbsp;of&nbsp;<a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style&nbsp;arguments&nbsp;split&nbsp;into&nbsp;a&nbsp;list,&nbsp;where&nbsp;":"&nbsp;may<br>
be&nbsp;used&nbsp;for&nbsp;multi-word&nbsp;arguments&nbsp;that&nbsp;last&nbsp;until&nbsp;the&nbsp;end&nbsp;of&nbsp;a&nbsp;line.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal&nbsp;function&nbsp;to&nbsp;remove&nbsp;a&nbsp;client&nbsp;from&nbsp;our&nbsp;internal&nbsp;state.</tt></dd></dl>
<dl><dt><a name="UnrealProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares&nbsp;the&nbsp;current&nbsp;TS&nbsp;of&nbsp;the&nbsp;channel&nbsp;given&nbsp;with&nbsp;the&nbsp;new&nbsp;TS,&nbsp;resetting<br>
all&nbsp;modes&nbsp;we&nbsp;have&nbsp;if&nbsp;the&nbsp;one&nbsp;given&nbsp;is&nbsp;older.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="classes.html#Protocol">classes.Protocol</a>:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>conf</strong> = {'bot': {'nick': 'PyLink', 'realname': 'PyLink Service Client', 'serverdesc': 'PyLink unit tests', 'user': 'pylink'}, 'logging': {'stdout': 'CRITICAL'}, 'servers': defaultdict(&lt;function &lt;lambda&gt; at 0x7f19895c4bf8&gt;, {})}<br>
<strong>confname</strong> = 'testconf'<br>
<strong>curdir</strong> = '/home/gl/pylink'<br>
<strong>files</strong> = None<br>
<strong>log</strong> = &lt;logging.RootLogger object&gt;<br>
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
<strong>logformatter</strong> = &lt;logging.Formatter object&gt;<br>
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
</body></html>

View File

@ -1,220 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module utils</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>utils</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/utils.py">/home/gl/pylink/utils.py</a></font></td></tr></table>
<p><tt>utils.py&nbsp;-&nbsp;PyLink&nbsp;utilities&nbsp;module.<br>
&nbsp;<br>
This&nbsp;module&nbsp;contains&nbsp;various&nbsp;utility&nbsp;functions&nbsp;related&nbsp;to&nbsp;IRC&nbsp;and/or&nbsp;the&nbsp;PyLink<br>
framework.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="collections.html">collections</a><br>
<a href="conf.html">conf</a><br>
</td><td width="25%" valign=top><a href="importlib.html">importlib</a><br>
<a href="os.html">os</a><br>
</td><td width="25%" valign=top><a href="re.html">re</a><br>
<a href="string.html">string</a><br>
</td><td width="25%" valign=top><a href="world.html">world</a><br>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ee77aa">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
<tr><td bgcolor="#ee77aa"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl>
<dt><font face="helvetica, arial"><a href="builtins.html#Exception">builtins.Exception</a>(<a href="builtins.html#BaseException">builtins.BaseException</a>)
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="utils.html#NotAuthenticatedError">NotAuthenticatedError</a>
</font></dt></dl>
</dd>
<dt><font face="helvetica, arial"><a href="builtins.html#object">builtins.object</a>
</font></dt><dd>
<dl>
<dt><font face="helvetica, arial"><a href="utils.html#IncrementalUIDGenerator">IncrementalUIDGenerator</a>
</font></dt><dt><font face="helvetica, arial"><a href="utils.html#ServiceBot">ServiceBot</a>
</font></dt></dl>
</dd>
</dl>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="IncrementalUIDGenerator">class <strong>IncrementalUIDGenerator</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt>Incremental&nbsp;UID&nbsp;Generator&nbsp;module,&nbsp;adapted&nbsp;from&nbsp;InspIRCd&nbsp;source:<br>
https://github.com/inspircd/inspircd/blob/f449c6b296ab/src/server.cpp#L85-L156<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="IncrementalUIDGenerator-__init__"><strong>__init__</strong></a>(self, sid)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="IncrementalUIDGenerator-increment"><strong>increment</strong></a>(self, pos=None)</dt><dd><tt>Increments&nbsp;the&nbsp;UID&nbsp;generator&nbsp;to&nbsp;the&nbsp;next&nbsp;available&nbsp;UID.</tt></dd></dl>
<dl><dt><a name="IncrementalUIDGenerator-next_uid"><strong>next_uid</strong></a>(self)</dt><dd><tt>Returns&nbsp;the&nbsp;next&nbsp;unused&nbsp;UID&nbsp;for&nbsp;the&nbsp;server.</tt></dd></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="NotAuthenticatedError">class <strong>NotAuthenticatedError</strong></a>(<a href="builtins.html#Exception">builtins.Exception</a>)</font></td></tr>
<tr bgcolor="#ffc8d8"><td rowspan=2><tt>&nbsp;&nbsp;&nbsp;</tt></td>
<td colspan=2><tt><a href="builtins.html#Exception">Exception</a>&nbsp;raised&nbsp;by&nbsp;checkAuthenticated()&nbsp;when&nbsp;a&nbsp;user&nbsp;fails&nbsp;authentication<br>
requirements.<br>&nbsp;</tt></td></tr>
<tr><td>&nbsp;</td>
<td width="100%"><dl><dt>Method resolution order:</dt>
<dd><a href="utils.html#NotAuthenticatedError">NotAuthenticatedError</a></dd>
<dd><a href="builtins.html#Exception">builtins.Exception</a></dd>
<dd><a href="builtins.html#BaseException">builtins.BaseException</a></dd>
<dd><a href="builtins.html#object">builtins.object</a></dd>
</dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<hr>
Methods inherited from <a href="builtins.html#Exception">builtins.Exception</a>:<br>
<dl><dt><a name="NotAuthenticatedError-__init__"><strong>__init__</strong></a>(self, /, *args, **kwargs)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;help(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="NotAuthenticatedError-__new__"><strong>__new__</strong></a>(*args, **kwargs)<font color="#909090"><font face="helvetica, arial"> from <a href="builtins.html#type">builtins.type</a></font></font></dt><dd><tt>Create&nbsp;and&nbsp;return&nbsp;a&nbsp;new&nbsp;<a href="builtins.html#object">object</a>.&nbsp;&nbsp;See&nbsp;help(type)&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<hr>
Methods inherited from <a href="builtins.html#BaseException">builtins.BaseException</a>:<br>
<dl><dt><a name="NotAuthenticatedError-__delattr__"><strong>__delattr__</strong></a>(self, name, /)</dt><dd><tt>Implement&nbsp;delattr(self,&nbsp;name).</tt></dd></dl>
<dl><dt><a name="NotAuthenticatedError-__getattribute__"><strong>__getattribute__</strong></a>(self, name, /)</dt><dd><tt>Return&nbsp;getattr(self,&nbsp;name).</tt></dd></dl>
<dl><dt><a name="NotAuthenticatedError-__reduce__"><strong>__reduce__</strong></a>(...)</dt><dd><tt>helper&nbsp;for&nbsp;pickle</tt></dd></dl>
<dl><dt><a name="NotAuthenticatedError-__repr__"><strong>__repr__</strong></a>(self, /)</dt><dd><tt>Return&nbsp;repr(self).</tt></dd></dl>
<dl><dt><a name="NotAuthenticatedError-__setattr__"><strong>__setattr__</strong></a>(self, name, value, /)</dt><dd><tt>Implement&nbsp;setattr(self,&nbsp;name,&nbsp;value).</tt></dd></dl>
<dl><dt><a name="NotAuthenticatedError-__setstate__"><strong>__setstate__</strong></a>(...)</dt></dl>
<dl><dt><a name="NotAuthenticatedError-__str__"><strong>__str__</strong></a>(self, /)</dt><dd><tt>Return&nbsp;str(self).</tt></dd></dl>
<dl><dt><a name="NotAuthenticatedError-with_traceback"><strong>with_traceback</strong></a>(...)</dt><dd><tt><a href="builtins.html#Exception">Exception</a>.<a href="#NotAuthenticatedError-with_traceback">with_traceback</a>(tb)&nbsp;--<br>
set&nbsp;self.<strong>__traceback__</strong>&nbsp;to&nbsp;tb&nbsp;and&nbsp;return&nbsp;self.</tt></dd></dl>
<hr>
Data descriptors inherited from <a href="builtins.html#BaseException">builtins.BaseException</a>:<br>
<dl><dt><strong>__cause__</strong></dt>
<dd><tt>exception&nbsp;cause</tt></dd>
</dl>
<dl><dt><strong>__context__</strong></dt>
<dd><tt>exception&nbsp;context</tt></dd>
</dl>
<dl><dt><strong>__dict__</strong></dt>
</dl>
<dl><dt><strong>__suppress_context__</strong></dt>
</dl>
<dl><dt><strong>__traceback__</strong></dt>
</dl>
<dl><dt><strong>args</strong></dt>
</dl>
</td></tr></table> <p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#ffc8d8">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#000000" face="helvetica, arial"><a name="ServiceBot">class <strong>ServiceBot</strong></a>(<a href="builtins.html#object">builtins.object</a>)</font></td></tr>
<tr><td bgcolor="#ffc8d8"><tt>&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%">Methods defined here:<br>
<dl><dt><a name="ServiceBot-__init__"><strong>__init__</strong></a>(self, name, default_help=True, default_request=False, default_list=True, nick=None, ident=None, manipulatable=False)</dt><dd><tt>Initialize&nbsp;self.&nbsp;&nbsp;See&nbsp;<a href="#ServiceBot-help">help</a>(type(self))&nbsp;for&nbsp;accurate&nbsp;signature.</tt></dd></dl>
<dl><dt><a name="ServiceBot-add_cmd"><strong>add_cmd</strong></a>(self, func, name=None)</dt><dd><tt>Binds&nbsp;an&nbsp;IRC&nbsp;command&nbsp;function&nbsp;to&nbsp;the&nbsp;given&nbsp;command&nbsp;name.</tt></dd></dl>
<dl><dt><a name="ServiceBot-call_cmd"><strong>call_cmd</strong></a>(self, irc, source, text, called_by=None, notice=True)</dt><dd><tt>Calls&nbsp;a&nbsp;PyLink&nbsp;bot&nbsp;command.&nbsp;source&nbsp;is&nbsp;the&nbsp;caller's&nbsp;UID,&nbsp;and&nbsp;text&nbsp;is&nbsp;the<br>
full,&nbsp;unparsed&nbsp;text&nbsp;of&nbsp;the&nbsp;message.</tt></dd></dl>
<dl><dt><a name="ServiceBot-help"><strong>help</strong></a>(self, irc, source, args)</dt><dd><tt>&lt;command&gt;<br>
&nbsp;<br>
Gives&nbsp;help&nbsp;for&nbsp;&lt;command&gt;,&nbsp;if&nbsp;it&nbsp;is&nbsp;available.</tt></dd></dl>
<dl><dt><a name="ServiceBot-listcommands"><strong>listcommands</strong></a>(self, irc, source, args)</dt><dd><tt>takes&nbsp;no&nbsp;arguments.<br>
&nbsp;<br>
Returns&nbsp;a&nbsp;list&nbsp;of&nbsp;available&nbsp;commands&nbsp;this&nbsp;service&nbsp;has&nbsp;to&nbsp;offer.</tt></dd></dl>
<dl><dt><a name="ServiceBot-remove"><strong>remove</strong></a>(self, irc, source, args)</dt></dl>
<dl><dt><a name="ServiceBot-reply"><strong>reply</strong></a>(self, irc, text)</dt><dd><tt>Replies&nbsp;to&nbsp;a&nbsp;message&nbsp;using&nbsp;the&nbsp;right&nbsp;service&nbsp;UID.</tt></dd></dl>
<dl><dt><a name="ServiceBot-request"><strong>request</strong></a>(self, irc, source, args)</dt></dl>
<dl><dt><a name="ServiceBot-spawn"><strong>spawn</strong></a>(self, irc=None)</dt></dl>
<hr>
Data descriptors defined here:<br>
<dl><dt><strong>__dict__</strong></dt>
<dd><tt>dictionary&nbsp;for&nbsp;instance&nbsp;variables&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
<dl><dt><strong>__weakref__</strong></dt>
<dd><tt>list&nbsp;of&nbsp;weak&nbsp;references&nbsp;to&nbsp;the&nbsp;object&nbsp;(if&nbsp;defined)</tt></dd>
</dl>
</td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#eeaa77">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
<tr><td bgcolor="#eeaa77"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><dl><dt><a name="-add_cmd"><strong>add_cmd</strong></a>(func, name=None)</dt><dd><tt>Binds&nbsp;an&nbsp;IRC&nbsp;command&nbsp;function&nbsp;to&nbsp;the&nbsp;given&nbsp;command&nbsp;name.</tt></dd></dl>
<dl><dt><a name="-add_hook"><strong>add_hook</strong></a>(func, command)</dt><dd><tt>Binds&nbsp;a&nbsp;hook&nbsp;function&nbsp;to&nbsp;the&nbsp;given&nbsp;command&nbsp;name.</tt></dd></dl>
<dl><dt><a name="-applyModes"><strong>applyModes</strong></a>(irc, target, changedmodes)</dt><dd><tt>Takes&nbsp;a&nbsp;list&nbsp;of&nbsp;parsed&nbsp;IRC&nbsp;modes,&nbsp;and&nbsp;applies&nbsp;them&nbsp;on&nbsp;the&nbsp;given&nbsp;target.<br>
&nbsp;<br>
The&nbsp;target&nbsp;can&nbsp;be&nbsp;either&nbsp;a&nbsp;channel&nbsp;or&nbsp;a&nbsp;user;&nbsp;this&nbsp;is&nbsp;handled&nbsp;automatically.<br>
&nbsp;<br>
This&nbsp;method&nbsp;is&nbsp;deprecated.&nbsp;Use&nbsp;irc.<a href="#-applyModes">applyModes</a>()&nbsp;instead.</tt></dd></dl>
<dl><dt><a name="-getDatabaseName"><strong>getDatabaseName</strong></a>(dbname)</dt><dd><tt>Returns&nbsp;a&nbsp;database&nbsp;filename&nbsp;with&nbsp;the&nbsp;given&nbsp;base&nbsp;DB&nbsp;name&nbsp;appropriate&nbsp;for&nbsp;the<br>
current&nbsp;PyLink&nbsp;instance.<br>
&nbsp;<br>
This&nbsp;returns&nbsp;'&lt;dbname&gt;.db'&nbsp;if&nbsp;the&nbsp;running&nbsp;config&nbsp;name&nbsp;is&nbsp;PyLink's&nbsp;default<br>
(config.yml),&nbsp;and&nbsp;'&lt;dbname&gt;-&lt;config&nbsp;name&gt;.db'&nbsp;for&nbsp;anything&nbsp;else.&nbsp;For&nbsp;example,<br>
if&nbsp;this&nbsp;is&nbsp;called&nbsp;from&nbsp;an&nbsp;instance&nbsp;running&nbsp;as&nbsp;'./pylink&nbsp;testing.yml',&nbsp;it<br>
would&nbsp;return&nbsp;'&lt;dbname&gt;-testing.db'.</tt></dd></dl>
<dl><dt><a name="-getProtocolModule"><strong>getProtocolModule</strong></a>(protoname)</dt><dd><tt>Imports&nbsp;and&nbsp;returns&nbsp;the&nbsp;protocol&nbsp;module&nbsp;requested.</tt></dd></dl>
<dl><dt><a name="-isChannel"><strong>isChannel</strong></a>(s)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;string&nbsp;given&nbsp;is&nbsp;a&nbsp;valid&nbsp;channel&nbsp;name.</tt></dd></dl>
<dl><dt><a name="-isHostmask"><strong>isHostmask</strong></a>(text)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;given&nbsp;text&nbsp;is&nbsp;a&nbsp;valid&nbsp;hostmask.</tt></dd></dl>
<dl><dt><a name="-isNick"><strong>isNick</strong></a>(s, nicklen=None)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;string&nbsp;given&nbsp;is&nbsp;a&nbsp;valid&nbsp;nick.</tt></dd></dl>
<dl><dt><a name="-isServerName"><strong>isServerName</strong></a>(s)</dt><dd><tt>Returns&nbsp;whether&nbsp;the&nbsp;string&nbsp;given&nbsp;is&nbsp;a&nbsp;valid&nbsp;IRC&nbsp;server&nbsp;name.</tt></dd></dl>
<dl><dt><a name="-loadModuleFromFolder"><strong>loadModuleFromFolder</strong></a>(name, folder)</dt><dd><tt>Imports&nbsp;and&nbsp;returns&nbsp;a&nbsp;module,&nbsp;if&nbsp;existing,&nbsp;from&nbsp;a&nbsp;specific&nbsp;folder.</tt></dd></dl>
<dl><dt><a name="-parseModes"><strong>parseModes</strong></a>(irc, target, args)</dt><dd><tt>Parses&nbsp;a&nbsp;modestring&nbsp;list&nbsp;into&nbsp;a&nbsp;list&nbsp;of&nbsp;(mode,&nbsp;argument)&nbsp;tuples.<br>
['+mitl-o',&nbsp;'3',&nbsp;'person']&nbsp;=&gt;&nbsp;[('+m',&nbsp;None),&nbsp;('+i',&nbsp;None),&nbsp;('+t',&nbsp;None),&nbsp;('+l',&nbsp;'3'),&nbsp;('-o',&nbsp;'person')]<br>
&nbsp;<br>
This&nbsp;method&nbsp;is&nbsp;deprecated.&nbsp;Use&nbsp;irc.<a href="#-parseModes">parseModes</a>()&nbsp;instead.</tt></dd></dl>
<dl><dt><a name="-registerService"><strong>registerService</strong></a>(name, *args, **kwargs)</dt><dd><tt>Registers&nbsp;a&nbsp;service&nbsp;bot.</tt></dd></dl>
<dl><dt><a name="-unregisterService"><strong>unregisterService</strong></a>(name)</dt><dd><tt>Unregisters&nbsp;an&nbsp;existing&nbsp;service&nbsp;bot.</tt></dd></dl>
</td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>hostmaskRe</strong> = re.compile('^\\S+!\\S+@\\S+$')<br>
<strong>log</strong> = &lt;logging.RootLogger object&gt;</td></tr></table>
</body></html>

View File

@ -1,41 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html><head><title>Python: module world</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head><body bgcolor="#f0f0f8">
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
<tr bgcolor="#7799ee">
<td valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial">&nbsp;<br><big><big><strong>world</strong></big></big></font></td
><td align=right valign=bottom
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:/home/gl/pylink/world.py">/home/gl/pylink/world.py</a></font></td></tr></table>
<p><tt>world.py:&nbsp;Stores&nbsp;global&nbsp;variables&nbsp;for&nbsp;PyLink,&nbsp;including&nbsp;lists&nbsp;of&nbsp;active&nbsp;IRC&nbsp;objects&nbsp;and&nbsp;plugins.</tt></p>
<p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#aa55cc">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
<tr><td bgcolor="#aa55cc"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="os.html">os</a><br>
</td><td width="25%" valign=top><a href="subprocess.html">subprocess</a><br>
</td><td width="25%" valign=top><a href="threading.html">threading</a><br>
</td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
<tr bgcolor="#55aa55">
<td colspan=3 valign=bottom>&nbsp;<br>
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
<tr><td bgcolor="#55aa55"><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</tt></td><td>&nbsp;</td>
<td width="100%"><strong>hooks</strong> = defaultdict(&lt;class 'list'&gt;, {})<br>
<strong>networkobjects</strong> = {}<br>
<strong>plugins</strong> = {}<br>
<strong>plugins_folder</strong> = '/home/gl/pylink/plugins'<br>
<strong>protocols_folder</strong> = '/home/gl/pylink/protocols'<br>
<strong>services</strong> = {}<br>
<strong>source</strong> = 'https://github.com/GLolol/PyLink'<br>
<strong>started</strong> = &lt;threading.Event object&gt;<br>
<strong>testing</strong> = True<br>
<strong>testing_ircd</strong> = 'inspircd'<br>
<strong>version</strong> = 'v0.8-alpha2-60-g8c55eb4'</td></tr></table>
</body></html>

View File

@ -1,59 +0,0 @@
Channel Mode / IRCd,InspIRCd,charybdis,Elemental-IRCd,UnrealIRCd,IRCd-Hybrid,Nefarious IRCu
admin,+a (m_customprefix/m_chanprotect),,+a (when enabled),+a,,
adminonly,,+A (extensions/chm_adminonly),+A (extensions/chm_adminonly.c),,,+a
allowinvite,+A (m_allowinvite),+g,+g,,,
autoop,+w (m_autoop),,,,,
ban,+b,+b,+b,+b,+b,+b
banexception,+e (m_banexception),+e,+e,+e,+e,+e
blockcaps,+B (m_blockcaps),,+G (extensions/chm_nocaps.c),,,
blockcolor,+c (m_blockcolor),,,+c,+c,+c
delayjoin,,,,,,+D
exemptchanops,+X (m_exemptchanops),,,,,
filter,+g (m_filter),,,,,
flood,+f (m_messageflood),,,,,
flood_unreal,,,,+f,,
freetarget,,+F,+F,,,
had_delayjoins,,,,,,+d
halfop,+h (m_customprefix/m_halfop),,+h (when enabled),+h,+h,
hiddenbans,,,+u,,,
hidequits,,,,,,+Q
history,+H (m_chanhistory),,,,,
invex,+I (m_inviteexception),+I,+I,+I,+I,
inviteonly,+i,+i,+i,+i,+i,+i
issecure,,,,+Z,,
joinflood,+j (m_joinflood),+j,+j,,,
key,+k,+k,+k,+k,+k,+k
kicknorejoin,+J (m_kicknorejoin),,+J,,,
largebanlist,,+L,+L,,,
limit,+l,+l,+l,+l,+l,+l
moderated,+m,+m,+m,+m,+m,+m
nickflood,+F (m_nickflood),,,,,
noamsg,,,,,,+T
noctcp,+C (m_noctcp),+C,+C,+C,+C,+C
noextmsg,+n,+n,+n,+n,+n,+n
noforwards,,+Q,+Q,,,
noinvite,,,,,,
nokick,+Q (m_nokicks),,+E,+Q,,
noknock,+K (m_knock),+p,,+K,+p,
nonick,+N (m_nonicks),,+d,+N,,
nonotice,+T (m_nonotice),+T (extensions/chm_nonotice),+T,+T,,+N
official-join,+Y (m_ojoin),,,,,
op,+o,+o,+o,+o,+o,+o
operonly,+O (m_operchans),+O (extensions/chm_operonly),+O (extensions/chm_operonly.c),+O,+O,+O
oplevel_apass,,,,,,+A
oplevel_upass,,,,,,+U
opmoderated,+U (extras/m_opmoderated),+z,+z,,,
owner,+q (m_customprefix/m_chanprotect),,+y (when enabled),+q,,
permanent,+P (m_permchannels),+P,+P,+P,,+z
private,+p,+p,+p,+p,,+p
quiet,,+q,+q,,,
redirect,+L (m_redirect),+f,+f,+L,,+L
registered,+r (m_services_account),,,+r,+r,+R
regmoderated,+M (m_services_account),,,+M,+M,+M
regonly,+R (m_services_account),+r,+r,+R,+R,+r
repeat,+E (m_repeat),,+K (extensions/chm_norepeat.c),,,
secret,+s,+s,+s,+s,+s,+s
sslonly,+z (m_sslmodes),+S (extensions/chm_sslonly),+S (extensions/chm_sslonly.c),+z,+S,
stripcolor,+S (m_stripcolor),+c,+c,+S,,+S
topiclock,+t,+t,+t,+t,+t,+t
voice,+v,+v,+v,+v,+v,+v
1 Channel Mode / IRCd InspIRCd charybdis Elemental-IRCd UnrealIRCd IRCd-Hybrid Nefarious IRCu
2 admin +a (m_customprefix/m_chanprotect) +a (when enabled) +a
3 adminonly +A (extensions/chm_adminonly) +A (extensions/chm_adminonly.c) +a
4 allowinvite +A (m_allowinvite) +g +g
5 autoop +w (m_autoop)
6 ban +b +b +b +b +b +b
7 banexception +e (m_banexception) +e +e +e +e +e
8 blockcaps +B (m_blockcaps) +G (extensions/chm_nocaps.c)
9 blockcolor +c (m_blockcolor) +c +c +c
10 delayjoin +D
11 exemptchanops +X (m_exemptchanops)
12 filter +g (m_filter)
13 flood +f (m_messageflood)
14 flood_unreal +f
15 freetarget +F +F
16 had_delayjoins +d
17 halfop +h (m_customprefix/m_halfop) +h (when enabled) +h +h
18 hiddenbans +u
19 hidequits +Q
20 history +H (m_chanhistory)
21 invex +I (m_inviteexception) +I +I +I +I
22 inviteonly +i +i +i +i +i +i
23 issecure +Z
24 joinflood +j (m_joinflood) +j +j
25 key +k +k +k +k +k +k
26 kicknorejoin +J (m_kicknorejoin) +J
27 largebanlist +L +L
28 limit +l +l +l +l +l +l
29 moderated +m +m +m +m +m +m
30 nickflood +F (m_nickflood)
31 noamsg +T
32 noctcp +C (m_noctcp) +C +C +C +C +C
33 noextmsg +n +n +n +n +n +n
34 noforwards +Q +Q
35 noinvite
36 nokick +Q (m_nokicks) +E +Q
37 noknock +K (m_knock) +p +K +p
38 nonick +N (m_nonicks) +d +N
39 nonotice +T (m_nonotice) +T (extensions/chm_nonotice) +T +T +N
40 official-join +Y (m_ojoin)
41 op +o +o +o +o +o +o
42 operonly +O (m_operchans) +O (extensions/chm_operonly) +O (extensions/chm_operonly.c) +O +O +O
43 oplevel_apass +A
44 oplevel_upass +U
45 opmoderated +U (extras/m_opmoderated) +z +z
46 owner +q (m_customprefix/m_chanprotect) +y (when enabled) +q
47 permanent +P (m_permchannels) +P +P +P +z
48 private +p +p +p +p +p
49 quiet +q +q
50 redirect +L (m_redirect) +f +f +L +L
51 registered +r (m_services_account) +r +r +R
52 regmoderated +M (m_services_account) +M +M +M
53 regonly +R (m_services_account) +r +r +R +R +r
54 repeat +E (m_repeat) +K (extensions/chm_norepeat.c)
55 secret +s +s +s +s +s +s
56 sslonly +z (m_sslmodes) +S (extensions/chm_sslonly) +S (extensions/chm_sslonly.c) +z +S
57 stripcolor +S (m_stripcolor) +c +c +S +S
58 topiclock +t +t +t +t +t +t
59 voice +v +v +v +v +v +v

View File

@ -1,27 +0,0 @@
/* Graph for the PyLink Application Structure:
* Update using: dot -Tpng core-structure.dot > core-structure.png
*/
digraph G {
ratio = 0.8; /* make the graph wider than tall */
subgraph cluster_core {
label="PyLink Application Structure";
style="filled";
node [style="filled",color="white"];
color="lightblue";
"IRC objects" -> "Protocol modules" [label="Data relayed"]
"Protocol modules" -> "PyLink hooks" -> Plugins;
"IRC objects" -> "PyLink hooks";
"Main program" -> "IRC objects" [color=indigo] [label="One per network\nspawned"] [fontcolor=indigo];
"Main program" -> "IRC objects" [color=indigo];
"Main program" -> "IRC objects" [color=indigo];
"Protocol modules" -> "IRC objects" [label="States updated"] [color=darkgreen] [fontcolor=darkgreen];
"Main program" -> Plugins [label="Plugin loaders"];
}
"Protocol modules" -> "IRCds" -> "Protocol modules";
Plugins -> "Protocol modules" [label="Communication via\nIRC command\nsenders"] [color=navyblue] [fontcolor=navyblue];
Plugins -> "Main program" [label="Registers commands\n& hook handlers"] [color=brown] [fontcolor=brown];
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

2427
docs/technical/doxygen.conf Normal file

File diff suppressed because it is too large Load Diff

15
docs/technical/doxygen.sh Executable file
View File

@ -0,0 +1,15 @@
#!/bin/bash
# Runs Doxygen on PyLink.
# Note: to change the outpuit path, doxygen.conf also has to be updated too!
OUTDIR="../../../pylink.github.io"
if [ ! -d "$OUTDIR" ]; then
echo "Git clone https://github.com/PyLink/pylink.github.io to $OUTDIR and then rerun this script."
exit 1
fi
CURDIR="$(pwd)"
doxygen doxygen.conf
cp -R html/* "$OUTDIR"
rm -r "html/"

View File

@ -1,13 +1,13 @@
# PyLink hooks reference # PyLink hooks reference
## Introduction ***Last updated for 3.1-dev (2021-06-13).***
In PyLink, protocol modules communicate with plugins through a system of hooks. This has the benefit of being IRCd-independent, allowing most plugins to function regardless of the IRCd being used. In PyLink, protocol modules communicate with plugins through a system of hooks. This has the benefit of being IRCd-independent, allowing most plugins to function regardless of the IRCd being used.
Each hook payload is formatted as a Python `list`, with three arguments: `numeric`, `command`, and `args`. Each hook payload is formatted as a Python `list`, with three arguments: `numeric`, `command`, and `args`.
1) **numeric**: The sender of the hook payload (normally a UID or SID). 1) **numeric**: The sender of the hook payload (normally a UID or SID).
2) **command**: The command name (hook name) of the payload. These are *always* UPPERCASE, and those starting with "PYLINK_" indicate hooks sent out by PyLink IRC objects themselves; i.e. they don't require protocol modules to handle them. 2) **command**: The command name (hook name) of the payload. These are *always* UPPERCASE, and those starting with "PYLINK_" indicate hooks sent out by PyLink IRC objects themselves (i.e. they don't require protocol modules to handle them).
3) **args**: The hook data (args), a Python `dict`, with different data keys and values depending on the command given. 3) **args**: The hook data (args), a Python `dict`, with different data keys and values depending on the command given.
@ -19,26 +19,17 @@ The command `:42XAAAAAB PRIVMSG #dev :test` would result in the following raw ho
- `['42XAAAAAB', 'PRIVMSG', {'target': '#dev', 'text': 'test', 'ts': 1451174041}]` - `['42XAAAAAB', 'PRIVMSG', {'target': '#dev', 'text': 'test', 'ts': 1451174041}]`
On UnrealIRCd, because SETHOST is mapped to CHGHOST, `:GL SETHOST blah` would return the raw hook data of this (with the nick converted into UID automatically by the protocol module): On UnrealIRCd, because SETHOST is mapped to CHGHOST, `:jlu5 SETHOST blah` would return the raw hook data of this (with the nick converted into UID automatically by the protocol module):
- `['001ZJZW01', 'CHGHOST', {'ts': 1451174512, 'target': '001ZJZW01', 'newhost': 'blah'}]` - `['001ZJZW01', 'CHGHOST', {'ts': 1451174512, 'target': '001ZJZW01', 'newhost': 'blah'}]`
Some hooks, like MODE, are more complex and can include the entire state of a channel! This will be further described later. `:GL MODE #chat +o PyLink-devel` is converted into (pretty-printed for readability): Some hooks, like MODE, are more complex and can include the entire state of a channel. This will be further described later. `:jlu5 MODE #chat +o PyLink-devel` is converted into (pretty-printed for readability):
``` ```
['001ZJZW01', ['001ZJZW01',
'MODE', 'MODE',
{'modes': [('+o', '38QAAAAAA')], {'modes': [('+o', '38QAAAAAA')],
'oldchan': IrcChannel({'modes': set(), 'channeldata': Channel(...),
'prefixmodes': {'admin': set(),
'halfop': set(),
'op': set(),
'owner': set(),
'voice': set()},
'topic': '',
'topicset': False,
'ts': 1451169448,
'users': {'38QAAAAAA', '001ZJZW01'}}),
'target': '#chat', 'target': '#chat',
'ts': 1451174702}] 'ts': 1451174702}]
``` ```
@ -52,8 +43,9 @@ These following hooks, sent with their correct data keys, are required for PyLin
- This payload should be sent whenever a server finishes its burst, with the SID of the bursted server as the sender. - This payload should be sent whenever a server finishes its burst, with the SID of the bursted server as the sender.
- The service bot API and plugins like relay use this to make sure networks are properly connected. Should ENDBURST not be sent or emulated, they will likely fail to spawn users entirely. - The service bot API and plugins like relay use this to make sure networks are properly connected. Should ENDBURST not be sent or emulated, they will likely fail to spawn users entirely.
- **PYLINK_DISCONNECT**: `{}` - **PYLINK_DISCONNECT**: `{'was_successful': False}`
- This is sent to plugins by IRC object instances whenever their network has disconnected. The sender here is always **None**. - This is sent to plugins by IRC object instances whenever their network has disconnected. The sender here is always **None**.
- The `was_successful` key shows whether the last connection before this message was successful (i.e. whether the disconnect wasn't caused by a configuration error, etc.)
## IRC command hooks ## IRC command hooks
@ -67,48 +59,53 @@ The following hooks represent regular IRC commands sent between servers.
- `modes` returns a list of parsed modes: `(mode character, mode argument)` tuples, where the mode argument is either `None` (for modes without arguments), or a string. - `modes` returns a list of parsed modes: `(mode character, mode argument)` tuples, where the mode argument is either `None` (for modes without arguments), or a string.
- The sender of this hook payload is IRCd-dependent, and is determined by whether the command was originally a SJOIN or regular JOIN - SJOIN is only sent by servers, and JOIN is only sent by users. - The sender of this hook payload is IRCd-dependent, and is determined by whether the command was originally a SJOIN or regular JOIN - SJOIN is only sent by servers, and JOIN is only sent by users.
- For IRCds that support joining multiple channels in one command (`/join #channel1,#channel2`), consecutive JOIN hook payloads of this format will be sent (one per channel). - For IRCds that support joining multiple channels in one command (`/join #channel1,#channel2`), consecutive JOIN hook payloads of this format will be sent (one per channel).
- For SJOIN, the `channeldata` key may also be sent, with a copy of the `classes.Channel` object *before* any mode changes from this burst command were processed.
- **KICK**: `{'channel': '#channel', 'target': 'UID1', 'text': 'some reason'}` - **KICK**: `{'channel': '#channel', 'target': 'UID1', 'text': 'some reason'}`
- `text` refers to the kick reason. The `target` and `channel` fields send the target's UID and the channel they were kicked from, and the sender of the hook payload is the kicker. - `text` refers to the kick reason. The `target` and `channel` fields send the target's UID and the channel they were kicked from, and the sender of the hook payload is the kicker.
- **KILL**: `{'target': killed, 'text': args[1], 'userdata': data}` - **KILL**: `{'target': killed, 'text': 'Killed (james (absolutely not))', 'userdata': User(...)}`
- `text` refers to the kill reason. `target` is the target's UID. - `text` refers to the kill reason. `target` is the target's UID.
- The `userdata` key may include an `IrcUser` instance, depending on the IRCd. On IRCds where QUITs are explicitly sent (InspIRCd), `userdata` will be `None`. Other IRCds do not explicitly send QUIT messages for KILLed clients, so the daemon must assume that they've quit, and deliver their last state to plugins that require this info. - `userdata` includes a `classes.User` instance, containing the information of the killed user.
- **MODE**: `{'target': '#channel', 'modes': [('+m', None), ('+i', None), ('+t', None), ('+l', '3'), ('-o', 'person')], 'oldchan': IrcChannel(...)}` - **MODE**: `{'target': '#channel', 'modes': [('+m', None), ('+i', None), ('+t', None), ('+l', '3'), ('-o', 'person')], 'channeldata': Channel(...)}`
- `target` is the target the mode is being set on: it may be either a channel (for channel modes) OR a UID (for user modes). - `target` is the target the mode is being set on: it may be either a channel (for channel modes) *or* a UID (for user modes).
- `modes` is a list of prefixed parsed modes: `(mode character, mode argument)` tuples, but with `+/-` prefixes to denote whether each mode is being set or unset. - `modes` is a list of prefixed parsed modes: `(mode character, mode argument)` tuples, but with `+/-` prefixes to denote whether each mode is being set or unset.
- For channels, the `oldchan` key is also sent, with the state of the channel BEFORE this MODE hook was processed. - For channels, the `channeldata` key is also sent, with a copy of the `classes.Channel` object *before* this MODE hook was processed.
- One such use for this is to prevent oper-override hacks: checks for whether a sender is opped have to be done before the MODE is processed; otherwise, someone can simply op themselves and circumvent this detection. - One use for this is to prevent oper-override hacks: checks for whether a sender is opped have to be done before the MODE is processed; otherwise, someone can simply op themselves and circumvent this detection.
- **NICK**: `{'newnick': 'Alakazam', 'oldnick': 'Abracadabra', 'ts': 1234567890}` - **NICK**: `{'newnick': 'Alakazam', 'oldnick': 'Abracadabra', 'ts': 1234567890}`
- **NOTICE**: `{'target': 'UID3', 'text': 'hi there!'}` - **NOTICE**: `{'target': 'UID3', 'text': 'hi there!'}`
- *Note:* `target` can not only be a channel or a UID, but also a channel with a prefix attached (e.g. `@#lounge`). These cases should not be overlooked! - STATUSMSG targets (e.g. `@#lounge`) are also allowed here.
- **PART**: `{'channels': ['#channel1', '#channel2'], 'text': 'some reason'}` - **PART**: `{'channels': ['#channel1', '#channel2'], 'text': 'some reason'}`
- `text` can also be an empty string, as part messages are *optional* on IRC. - `text` can also be an empty string, as part messages are *optional* on IRC.
- Unlike the JOIN hook, multiple channels can be specified in a list for PART. This means that a user PARTing one channel will cause a payload to be sent with `channels` as a one-length *list* with the channel name. - Unlike the JOIN hook, multiple channels can be specified in a list for PART. This means that a user parting one channel will cause a payload to be sent with `channels` as a one-length *list* with the channel name.
- **PRIVMSG**: `{'target': 'UID3', 'text': 'hi there!'}` - **PRIVMSG**: `{'target': 'UID3', 'text': 'hi there!'}`
- Ditto with NOTICE: `target` can be a channel or a UID, or a channel with a prefix attached (e.g. `@#lounge`). - Ditto with NOTICE: STATUSMSG targets (e.g. `@#lounge`) are also allowed here.
- **QUIT**: `{'text': 'Quit: Bye everyone!'}` - **QUIT**: `{'text': 'Quit: Bye everyone!', 'userdata': User(...)}`
- `text` corresponds to the quit reason. - `text` corresponds to the quit reason.
- `userdata` includes a `classes.User` instance, containing the information of the killed user.
- **SQUIT**: `{'target': '800', 'users': ['UID1', 'UID2', 'UID6'], 'name': 'some.server'}` - **SQUIT**: `{'target': '800', 'users': ['UID1', 'UID2', 'UID6'], 'name': 'some.server', 'uplink': '24X', 'nicks': {'#channel1: ['tester1', 'tester2'], '#channel3': ['somebot']}, 'serverdata': Server(...), 'affected_servers': ['SID1', 'SID2', 'SID3']`
- `target` is the SID of the server being split, while `name` is the server's name. - `target` is the SID of the server being split, while `name` is the server's name.
- `users` is a list of all UIDs affected by the netsplit. - `users` is a list of all UIDs affected by the netsplit. `nicks` maps channels to lists of nicks affected.
- `serverdata` provides the `classes.Server` object of the server that split off.
- `channeldata` provides the channel index of the network before the netsplit was processed, allowing plugins to track who was affected by a netsplit in a channel specific way.
- **TOPIC**: `{'channel': channel, 'setter': numeric, 'text': 'Welcome to #Lounge!, 'oldtopic': 'Welcome to#Lounge!'}` - **TOPIC**: `{'channel': channel, 'setter': numeric, 'text': 'Welcome to #Lounge!, 'oldtopic': 'Welcome to#Lounge!'}`
- `oldtopic` denotes the original topic, and `text` indicates the new one being set. - `oldtopic` denotes the original topic, and `text` indicates the new one being set.
- `setter` is the raw sender field given to us by the IRCd; it may be a `nick!user@host`, a UID, a SID, a server name, or a nick. This is not processed any further. - `setter` is the raw sender field given to us by the IRCd; it may be a `nick!user@host`, a UID, a SID, a server name, or a nick. This is not processed at the protocol level.
- **UID**: `{'uid': 'UID1', 'ts': 1234567891, 'nick': 'supercoder', 'realhost': 'localhost', 'host': 'admin.testnet.local', 'ident': ident, 'ip': '127.0.0.1'}` - **UID**: `{'uid': 'UID1', 'ts': 1234567891, 'nick': 'supercoder', 'realhost': 'localhost', 'host': 'admin.testnet.local', 'ident': ident, 'ip': '127.0.0.1', 'secure': True}`
- This command is used to introduce users; the sender of the message should be the server bursting or announcing the connection. - This command is used to introduce users; the sender of the message should be the server bursting or announcing the connection.
- `ts` refers to the user's signon time. - `ts` refers to the user's signon time.
- `secure` is a ternary value (True/False/None) that determines whether the user is connected over a secure connection (SSL/TLS). This value is only available on some IRCds: currently UnrealIRCd, P10, Charybdis TS6, and Hybrid; on other servers this will be `None`.
### Extra commands (where supported by the IRCd) ### Extra commands (where supported)
- **AWAY**: `{'text': text}` - **AWAY**: `{'text': text}`
- `text` denotes the away reason. It is an empty string (`''`) when a user is unsetting their away status. - `text` denotes the away reason. It is an empty string (`''`) when a user is unsetting their away status.
@ -122,21 +119,26 @@ The following hooks represent regular IRC commands sent between servers.
- **CHGNAME**: `{'target': 'UID2', 'newgecos': "I ain't telling you!"}` - **CHGNAME**: `{'target': 'UID2', 'newgecos': "I ain't telling you!"}`
- SETNAME and CHGNAME commands, where available, both share this hook name. - SETNAME and CHGNAME commands, where available, both share this hook name.
- **INVITE**: `{'target': 'UID3', 'channel': '#myroom'}` - **INVITE**: `{'target': 'UID3', 'channel': '#hello'}`
- **KNOCK**: `{'text': 'let me in please!', 'channel': '#myroom'}` - **KNOCK**: `{'text': 'let me in please!', 'channel': '#hello'}`
- This is not actually implemented by any protocol module as of writing. - This is not actually implemented by any protocol module as of writing.
- **SAVE**: `{'target': 'UID8', 'ts': 1234567892, 'oldnick': 'Abracadabra'}` - **SAVE**: `{'target': 'UID8', 'ts': 1234567892, 'oldnick': 'Abracadabra'}`
- For protocols that use TS6-style nick saving. During nick collisions, instead of killing the losing client, servers that support SAVE will send such a command targeting the losing client, which forces that user's nick to their UID. - For protocols that use TS6-style nick saving. During nick collisions, instead of killing the losing client, servers that support SAVE will send such a command targeting the losing client, which forces that user's nick to their UID.
- As of 1.1.x, SAVE is also used internally to alert plugins of nick collisions, when protocol modules receive a user introduction for a nick that already exists.
- **SVSNICK**: `{'target': 'UID1', 'newnick': 'abcd'}`
- PyLink does not comply with SVSNICK requests, but instead forwards it to plugins that listen for it.
- Relay, for example, treats SVSNICK as a cue to force tag nicks.
- **VERSION**: `{}` - **VERSION**: `{}`
- This is used for protocols that send VERSION requests between servers when a client requests it (e.g. `/raw version pylink.local`). - This is used for protocols that send VERSION requests between servers when a client requests it (e.g. `/raw version pylink.local`).
- `coreplugin` automatically handles this by responding with a 351 numeric, with the data being the output of `utils.fullVersion(irc)`. - `coremods/handlers.py` automatically handles this by responding with a 351 numeric, with the data being the output of `irc.version()`.
- **WHOIS**: `{'target': 'UID1'}` - **WHOIS**: `{'target': 'UID1'}`
- On protocols supporting it (everything except InspIRCd), the WHOIS command is sent between servers for remote WHOIS requests. - On protocols supporting it (everything except InspIRCd), the WHOIS command is sent between servers for remote WHOIS requests.
- This requires servers to respond with a complete WHOIS reply (using all the different numerics), as done in `coreplugin`. - PyLink has built-in handling for this via `coremods/handlers.py` - plugins wishing to add custom WHOIS text should use the PYLINK_CUSTOM_WHOIS hook below.
## Hooks that don't map to IRC commands ## Hooks that don't map to IRC commands
Some hooks do not map directly to IRC commands, but to events that protocol modules should handle. Some hooks do not map directly to IRC commands, but to events that protocol modules should handle.
@ -153,9 +155,26 @@ Some hooks do not map directly to IRC commands, but to events that protocol modu
- The sender here is always **None**. - The sender here is always **None**.
- **PYLINK_CUSTOM_WHOIS**: `{'target': UID1, 'server': SID1}` - **PYLINK_CUSTOM_WHOIS**: `{'target': UID1, 'server': SID1}`
- This hook is called by `coreplugin` during its WHOIS handling process, to allow plugins to provide custom WHOIS information. The `target` field represents the target UID, while the `server` field represents the SID that should be replying to the WHOIS request. The source of the payload is the user using `/whois`. - This hook is called by `coremods/handlers.py` during its WHOIS handling process, to allow plugins to provide custom WHOIS information. The `target` field represents the target UID, while the `server` field represents the SID that should be replying to the WHOIS request. The source of the payload is the user using `/whois`.
- Plugins wishing to implement this should use the standard WHOIS numerics, using `irc.proto.numeric()` to reply to the source from the given server. - Plugins wishing to implement this should use the standard WHOIS numerics, using `irc.numeric()` to reply to the source from the given server.
- This hook replaces the pre-0.8 fashion of defining custom WHOIS handlers, which was non-standard and poorly documented. - This hook replaces the pre-0.8.x fashion of defining custom WHOIS handlers, which was never standardized and poorly documented.
## Commands handled WITHOUT hooks ## Commands handled WITHOUT hooks
At this time, commands that are handled by protocol modules without returning any hook data include PING, PONG, and various commands sent during the initial server linking phase. At this time, commands that are handled by protocol modules without returning any hook data include PING, PONG, and various commands sent during the initial server linking phase.
## Changes
* 2021-06-13 (3.1-dev)
- Added the `secure` field to `UID` hooks.
* 2019-07-01 (2.1-alpha2)
- KILL and QUIT hooks now always include a non-empty `userdata` key. Now, if a QUIT message for a killed user is received before the corresponding KILL (or vice versa), only the first message received will have the corresponding hook payload broadcasted.
* 2018-12-27 (2.1-dev)
- Add the `affected_servers` argument to SQUIT hooks.
* 2018-07-11 (2.0.0)
- Version bump for 2.0 stable release; no meaningful content changes.
* 2018-01-13 (2.0-alpha2)
- Replace `IrcChannel`, `IrcUser`, and `IrcServer` with their new class names (`classes.Channel`, `classes.User`, and `classes.Server`)
- Replace `irc.fullVersion()` with `irc.version()`
- Various minor wording tweaks.
* 2017-02-24 (1.2-dev)
- The `was_successful` key was added to PYLINK_DISCONNECT.

View File

@ -0,0 +1,33 @@
# The Permissions API
Permissions were introduced in PyLink 1.0 as a way for plugins to manage command access, replacing the old `irc.checkAuthenticated()`. The permissions system in PyLink is fairly simple, globally assigning a list of permissions to each hostmask/exttarget.
Permissions conventionally take the format `pluginname.commandname.optional_extra_portion(s)`, and support globs† in matching. Permission nodes are case-insensitive and casemapping aware, but are defined as being all lowercase for consistency.
The permissions module is available as `pylinkirc.coremods.permissions`. Usually, plugins import it this format:
```python
from pylinkirc.coremods import permissions
```
† The globbing used by the permissions module is just generic IRC-style globbing. For example, anyone with `*`, `perm.*`, `perm.?`, `*.1`, etc. in their permissions list will be allowed to use a command checking for a permission named `perm.1`.
## Checking for permissions
Individual functions check for permissions using the `permissions.checkPermissions(irc, source, ['perm.1', 'perm.2'])` function, where the last argument is an OR'ed list of permissions matched against a list of permission string globs that a user may have. If the user has any of the permissions in the permission list, they will be allowed to call the command. This function returns `True` when a permission check passes, and raises `utils.NotAuthorizedError` when a check fails, automatically aborting the execution of the command function.
`utils.NotAuthorizedError` can be treated like any other exception, so it's possible to wrap it around `try:` / `except:` for more complex access checking ([example in the Automode plugin](https://github.com/jlu5/PyLink/blob/1.1.1/plugins/automode.py#L64-L68)).
## Assigning default permissions
Plugins are also allowed to assign default permissions to their commands, though this should be used sparingly to ensure maximum configurability (explicitly removing permissions isn't supported yet). Default permissions are specified as a `dict` mapping targets to permission lists.
Example of this in [Automode](https://github.com/jlu5/PyLink/blob/1.1-alpha1/plugins/automode.py#L38-L39):
```python
# The default set of Automode permissions.
default_permissions = {"$ircop": ['automode.manage.relay_owned', 'automode.sync.relay_owned',
'automode.list']}
```
Default permissions are registered in a plugin's `main()` function via `permissions.addDefaultPermissions(default_permissions_dict)`, and should always be erased on `die()` through `permissions.removeDefaultPermissions(default_permissions_dict)`.

View File

@ -1,48 +1,69 @@
# PyLink Protocol Module Specification # PyLink Protocol Module Specification
In PyLink, each protocol module is a single file consisting of a protocol class, and a global `Class` attribute that is set equal to it (e.g. `Class = InspIRCdProtocol`). These classes should be based off of either [`classes.Protocol`](https://github.com/GLolol/PyLink/blob/e4fb64aebaf542122c70a8f3a49061386a00b0ca/classes.py#L532), a boilerplate class that only defines a few basic things, or [`ts6_common.TS6BaseProtocol`](https://github.com/GLolol/PyLink/blob/0.5.0-dev/protocols/ts6_common.py), which includes elements of the TS6 protocol that are shared by the InspIRCd, UnrealIRCd, and TS6 protocols. IRC objects load protocol modules by creating an instance of its main class, and sends it commands accordingly. ***Last updated for 3.1-dev (2021-06-15).***
See also: [autogen/inspircd.html](autogen/inspircd.html) for auto-generated documentation the InspIRCd protocol module. Starting with PyLink 2.x, a *protocol module* is any module containing a class derived from `PyLinkNetworkCore` (e.g. `InspIRCdProtocol`), along with a global `Class` attribute set equal to it (e.g. `Class = InspIRCdProtocol`). These modules do everything from managing connections to providing plugins with an API to send and receive data. New protocol modules may be implemented based off any of the classes in the following inheritance tree, with each containing a different amount of abstraction.
## Tasks ![[Protocol module inheritence graph]](protocol-modules.svg)
Protocol modules have some very important jobs. If any of these aren't done correctly, you will be left with a broken, desynced services server: ## Starting Steps
1) Handle incoming commands from the uplink IRCd. **Before you proceed, we highly recommend protocol module coders to get in touch with us** (e.g. via IRC at `#PyLink @ irc.overdrivenetworks.com`). Letting us know what you are working on can help coordinate coding efforts and better prepare for potential API breaks.
2) Return [hook data](hooks-reference.md) for relevant commands, so that plugins can receive data from IRC. Note: The following notes in this section assume that you are working on some IRCd's server protocol, such that PyLink can spawn subservers and its own pseudoclients. If this is not the case, *virtual* clients and servers have to be spawned instead to emulate the correct state - the `clientbot` protocol module is a functional (though not very elegant) example of this.
3) Make sure channel/user states are kept correctly. Joins, quits, parts, kicks, mode changes, nick changes, etc. should all be handled accurately. When writing new protocol modules, it is recommended to subclass from one of the following classes:
4) Respond to both pings *and* pongs - the `irc.lastping` attribute **must** be set to the current time whenever a `PONG` is received from the uplink, so PyLink's doesn't [lag out the uplink thinking that it isn't responding to our pings](https://github.com/GLolol/PyLink/blob/e4fb64aebaf542122c70a8f3a49061386a00b0ca/classes.py#L309-L311). ### `classes.IRCNetwork`
5) Implement a series of outgoing command functions, used by plugins to send commands to IRC. See the `Outbound commands` section below for a list of which ones are needed. `IRCNetwork` is the base IRC class which includes the state checking utilities from `PyLinkNetworkCore`, the generic IRC utilities from `PyLinkNetworkCoreWithUtils`, along with abstraction for establishing IRC connections and pinging the uplink at a set interval.
6) Set the threading.Event object `irc.connected` (via `irc.connected.set()`) when the protocol negotiation with the uplink is complete. This is important for plugins like relay which must check that links are ready before spawning clients, and they will fail to work if this is not set. To use `classes.IRCNetwork`, the following functions must be defined:
7) Check to see that RECVPASS is correct. Always. - `handle_events(self, data)`: given a line of text containing an IRC command, parse it and return a hook payload as specified in the [PyLink hooks reference](hooks-reference.md).
- In all of the official PyLink modules so far, handling for specific commands is delegated into submethods via [`getattr()`](https://github.com/jlu5/PyLink/blob/3922d44173593e4bcceae1218bbc6f267caa9fc1/protocols/ircs2s_common.py#L409-L412), and unknown commands are ignored.
- `post_connect(self)`: This method sends the server introduction commands to the uplink IRC server. This method replaces the `connect()` function defined by protocol modules prior to PyLink 2.x.
- `_ping_uplink(self)`: Sends a ping command to the uplink. No return value is expected / used.
## Core functions This class offers the most flexibility because the protocol module can choose how it wants to handle any command. However, because most IRC server protocols use the same RFC 1459-style message format, rewriting the entire event handler is often not worth doing. Instead, it may be better to use `IRCS2SProtocol`, as documented below, which includes a `handle_events` method which handles most cases (TS5/6, P10, and TS-less protocols such as ngIRCd).
The following functions *must* be implemented by any protocol module within its main class, since they are used by the IRC object internals. - An exception to this general statement is `clientbot`, whose event handler also checks for unknown message senders and enumerates them when such a message is received.
- **`connect`**`(self)` - Initializes a connection to a server. ### `protocols.ircs2s_common.IRCCommonProtocol`
- **`handle_events`**`(self, line)` - Handles inbound data (lines of text) from the uplink IRC server. Normally, this will pass commands to other command handlers within the protocol module, while dropping commands that are unrecognized (wildcard handling). But, it's really up to you how to structure your modules. You will want to be able to parse command arguments properly into a list: many protocols send RFC1459-style commands that can be parsed using the [`Protocol.parseArgs()`](https://github.com/GLolol/PyLink/blob/e4fb64aebaf542122c70a8f3a49061386a00b0ca/classes.py#L539) function. `IRCCommonProtocol` (based off `IRCNetwork`) includes more IRC-specific methods such as parsers for ISUPPORT, as well as helper methods to parse arguments and recursively handle SQUIT. It also defines a default `_ping_uplink()` and incoming command handlers for commands that are the same across known protocols (AWAY, PONG, ERROR).
- **`ping`**`(self, source=None, target=None)` - Sends a PING to a target server. Periodic PINGs are sent to our uplink automatically by the [`Irc()` `IRCCommonProtocol` does *not*, however, define an `handle_events` method.
internals](https://github.com/GLolol/PyLink/blob/0.4.0-dev/classes.py#L267-L272); plugins shouldn't have to use this.
### Outgoing command functions ### `protocols.ircs2s_common.IRCS2SProtocol`
`IRCS2SProtocol` is the most complete base server class, including a generic `handle_events()` supporting most IRC S2S message styles (i.e. prefix-less messages, protocols with and without UIDs). It also defines some incoming and outgoing command functions that hardly vary between protocols: `invite()`, `kick()`, `message()`, `notice()`, `numeric()`, `part()`, `quit()`, `squit()`, and `topic()` as of PyLink 2.0. This list is subject to change in future releases.
- **`spawnClient`**`(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None, manipulatable=False)` - Spawns a client on the PyLink server. No nick collision / valid nickname checks are done by protocol modules, as it is up to plugins to make sure they don't introduce anything invalid. ### `classes.PyLinkNetworkCoreWithUtils`
- `modes` is a set of `(mode char, mode arg)` tuples in the form of [`utils.parseModes()` output](using-utils.md#parseModes).
- `ident` and `host` default to "null", while `realhost` defaults to the same things as `host` if not defined. `PyLinkNetworkCoreWithUtils` contains various state checking and IRC-related utility functions. Originally this abstraction was intended to support non-IRC protocols (Discord, Telegram, Slack, ...), but I (jlu5) no longer support this as a development focus. The main reason being is that in order to keep track of IRC server state correctly, PyLink makes a lot of assumptions specific to IRC (e.g. explicit join/part, mode formats, etc.). Trying to reconcile this with other platforms is a large undertaking and ideally requires a different, more generic protocol specification. (In PyLink 2.x there was a [Discord module](https://github.com/PyLink/pylink-discord) that is no longer supported - see https://jlu5.com/blog/the-trouble-with-pylink for a more in depth explanation as to why.)
- `realname` defaults to the real name specified in the PyLink config, if not given.
- `ts` defaults to the current time if not given. Subclassing one of the `PyLinkNetworkCore*` classes means that a protocol module only needs to define one method of entry: `connect()`, and must set up its own message handling stack. Protocol configuration validation checks and autoconnect must also be reimplemented. IRC-style utility functions (i.e. `PyLinkNetworkCoreWithUtils` methods) should also be reimplemented / overridden when applicable.
- `opertype` (the oper type name, if applicable) defaults to the simple text of `IRC Operator`.
(Unfortunately, this work is complicated, so please get in touch with us if you're stuck or want tips!)
### Other
For protocols that are closely related to existing ones, it may be wise to subclass off of an existing protocol class. For example, the `hybrid` and `ratbox` modules are based off of `ts6`. However, these protocol modules *do not guarantee API stability*, so we recommend letting us know of your intentions beforehand.
## Outgoing command functions
The methods defined below are integral to any protocol module, as they are needed by plugins to communicate with the rest of the world.
Unless otherwise noted, the camel-case variants of command functions (e.g. "`spawnClient`) are supported but deprecated. Protocol modules do *not* need to implement these aliases themselves; attempts to missing camel case functions are automatically coersed into their snake case variants via the [`structures.CamelCaseToSnakeCase`](https://github.com/jlu5/PyLink/blob/3922d44173593e4bcceae1218bbc6f267caa9fc1/structures.py#L172-L197) wrapper.
- **`spawn_client`**`(self, nick, ident='null', host='null', realhost=None, modes=set(), server=None, ip='0.0.0.0', realname=None, ts=None, opertype=None, manipulatable=False)` - Spawns a client on the PyLink server. No nick collision / valid nickname checks are done by protocol modules, as it is up to plugins to make sure they don't introduce anything invalid.
- `modes` is a list or set of `(mode char, mode arg)` tuples in the [PyLink mode format](#mode-formats).
- `ident` and `host` should default to "null", while `realhost` should default to the same things as `host` if not defined.
- `realname` should default to the real name specified in the PyLink config, if not given.
- `ts` should default to the current time if not given.
- `opertype` (the oper type name, if applicable) should default to the simple text of `IRC Operator`.
- The `manipulatable` option toggles whether the client spawned should be considered protected. Currently, all this does is prevent commands from plugins like `bots` from modifying these clients, but future client protections (anti-kill flood, etc.) may also depend on this. - The `manipulatable` option toggles whether the client spawned should be considered protected. Currently, all this does is prevent commands from plugins like `bots` from modifying these clients, but future client protections (anti-kill flood, etc.) may also depend on this.
- The `server` option optionally takes a SID of any PyLink server, and spawns the client on the one given. It will default to the root PyLink server. - The `server` option optionally takes a SID of any PyLink server, and spawns the client on the one given. It should default to the root PyLink server if not specified.
- **`join`**`(self, client, channel)` - Joins the given client UID given to a channel. - **`join`**`(self, client, channel)` - Joins the given client UID given to a channel.
@ -50,11 +71,11 @@ internals](https://github.com/GLolol/PyLink/blob/0.4.0-dev/classes.py#L267-L272)
- **`invite`**`(self, source, target, channel)` - Sends an INVITE from a PyLink client. - **`invite`**`(self, source, target, channel)` - Sends an INVITE from a PyLink client.
- **`kick`**`(self, source, channel, target, reason=None)` - Sends a kick from a PyLink client/server. - **`kick`**`(self, source, channel, target, reason=None)` - Sends a kick from a PyLink client/server. This should raise `NotImplementedError` if not supported by a protocol.
- **`kill`**`(self, source, target, reason)` - Sends a kill from a PyLink client/server. - **`kill`**`(self, source, target, reason)` - Sends a kill from a PyLink client/server. This should raise `NotImplementedError` if not supported by a protocol.
- **`knock`**`(self, source, target, text)` - Sends a KNOCK from a PyLink client. - **`knock`**`(self, source, target, text)` - Sends a KNOCK from a PyLink client. This should raise `NotImplementedError` if not supported by a protocol.
- **`message`**`(self, source, target, text)` - Sends a PRIVMSG from a PyLink client. - **`message`**`(self, source, target, text)` - Sends a PRIVMSG from a PyLink client.
@ -62,90 +83,218 @@ internals](https://github.com/GLolol/PyLink/blob/0.4.0-dev/classes.py#L267-L272)
- **`nick`**`(self, source, newnick)` - Changes the nick of a PyLink client. - **`nick`**`(self, source, newnick)` - Changes the nick of a PyLink client.
- **`notice`**`(self, source, target, text)` - Sends a NOTICE from a PyLink client. - **`oper_notice`**`(self, source, target)` - Sends a notice to all operators on the network.
- **`numeric`**`(self, source, numeric, target, text)` - Sends a raw numeric `numeric` with `text` from the `source` server to `target`. - **`notice`**`(self, source, target, text)` - Sends a NOTICE from a PyLink client or server.
- **`numeric`**`(self, source, numeric, target, text)` - Sends a raw numeric `numeric` with `text` from the `source` server to `target`. This should raise `NotImplementedError` if not supported on a protocol.
- **`part`**`(self, client, channel, reason=None)` - Sends a part from a PyLink client. - **`part`**`(self, client, channel, reason=None)` - Sends a part from a PyLink client.
- **`quit`**`(self, source, reason)` - Quits a PyLink client. - **`quit`**`(self, source, reason)` - Quits a PyLink client.
- **`sjoin`**`(self, server, channel, users, ts=None)` - Sends an SJOIN for a group of users to a channel. The sender should always be a Server ID (SID). TS is - **`sjoin`**`(self, server, channel, users, ts=None, modes=set())` - Sends an SJOIN for a group of users to a channel. The sender should always be a Server ID (SID). TS is
optional, and defaults to the one we've stored in the channel state if not given. `users` is a list of `(prefix mode, UID)` pairs. Example uses: optional, and defaults to the one we've stored in the channel state if not given. `users` is a list of `(prefix mode, UID)` pairs. Example uses:
- `sjoin('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')])` - `sjoin('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')])`
- `sjoin(self.irc.sid, '#test', [('o', self.irc.pseudoclient.uid)])` - `sjoin(self.sid, '#test', [('o', self.pseudoclient.uid)])`
- **`spawnServer`**`(self, name, sid=None, uplink=None, desc=None)` - Spawns a server off another PyLink server. `desc` (server description) defaults to the one in the config. `uplink` defaults to the main PyLink server, and `sid` (the server ID) is automatically generated if not given. Sanity checks for server name and SID validity ARE done by the protocol module here. - **`spawn_server`**`(self, name, sid=None, uplink=None, desc=None)` - Spawns a server off another PyLink server. `desc` (server description) defaults to the one in the config. `uplink` defaults to the main PyLink server, and `sid` (the server ID) is automatically generated if not given. Sanity checks for server name and SID validity ARE done by the protocol module here.
- **`squit`**`(self, source, target, text='No reason given')` - SQUITs a PyLink server. - **`squit`**`(self, source, target, text='No reason given')` - SQUITs a PyLink server.
- **`topic`**`(self, source, target, text)` - Sends a topic change from a PyLink client. - **`topic`**`(self, source, target, text)` - Sends a topic change from a PyLink *client.
- **`topicBurst`**`(self, source, target, text)` - Sends a topic change from a PyLink server. This is usually used on burst. - **`topic_burst`**`(self, source, target, text)` - Sends a topic change from a PyLink server. This is usually used on burst.
- **`updateClient`**`(self, source, field, text)` - Updates the ident, host, or realname of a PyLink client. `field` should be either "IDENT", "HOST", "GECOS", or - **`update_client`**`(self, source, field, text)` - Updates the ident, host, or realname of a PyLink client. `field` should be either "IDENT", "HOST", "GECOS", or "REALNAME". If changing the field given on the IRCd isn't supported, `NotImplementedError` should be raised.
"REALNAME". If changing the field given on the IRCd isn't supported, `NotImplementedError` should be raised.
## Things to note ## Special variables
### Special variables A protocol module should also set the following variables in each instance:
A protocol module should also set the following variables in their protocol class: - `self.casemapping`: a string (`'rfc1459'` or `'ascii'`) to determine which case mapping the IRCd uses.
- `self.hook_map`: this is a `dict`, which maps non-standard command names sent by the IRCd to those used by [PyLink hooks](hooks-reference.md).
- `self.casemapping`: set this to `rfc1459` (default) or `ascii` to determine which case mapping the IRCd uses. - Examples exist in the [UnrealIRCd](https://github.com/jlu5/PyLink/blob/1.0-beta1/protocols/unreal.py#L24-L27) and [InspIRCd](https://github.com/jlu5/PyLink/blob/1.0-beta1/protocols/inspircd.py#L25-L28) modules.
- `self.hook_map`: this is a `dict`, which maps non-standard command names sent by the IRCd to those that PyLink plugins use internally. - `self.conf_keys`: a set of strings determining which server configuration options a protocol module needs to function; see the [Configuration key validation](#configuration-key-validation) section below.
- Examples exist in the [UnrealIRCd](https://github.com/GLolol/PyLink/blob/0.5-dev/protocols/unreal.py#L22) and [InspIRCd](https://github.com/GLolol/PyLink/blob/0.5-dev/protocols/inspircd.py#L24) modules. - `self.cmodes` / `self.umodes`: These are mappings of named IRC modes (e.g. `inviteonly` or `moderated`) to a string list of mode letters, that should be either set during link negotiation or hardcoded into the protocol module. There are also special keys: `*A`, `*B`, `*C`, and `*D`, which **must** be set properly with a list of mode characters for that type of mode.
- `self.cmodes` / `self.umodes`: These are mappings of named IRC modes to mode letters, that should be either negotiated during link or preset in the `connect()` function of the protocol module. There are also special keys: `*A`, `*B`, `*C`, and `*D`, which should each be filled with a list of mode characters for that type of modes.
- Types of modes are defined as follows (from http://www.irc.org/tech_docs/005.html): - Types of modes are defined as follows (from http://www.irc.org/tech_docs/005.html):
- A = Mode that adds or removes a nick or address to a list. Always has a parameter. - A = Mode that adds or removes a nick or address to a list. Always has a parameter.
- B = Mode that changes a setting and always has a parameter. - B = Mode that changes a setting and always has a parameter.
- C = Mode that changes a setting and only has a parameter when set. - C = Mode that changes a setting and only has a parameter when set.
- D = Mode that changes a setting and never has a parameter. - D = Mode that changes a setting and never has a parameter.
- Examples in the TS6 protocol module: https://github.com/GLolol/PyLink/blob/cb3187c/protocols/ts6.py#L259-L300 - If not defined, these will default to modes defined by RFC 1459: https://github.com/jlu5/PyLink/blob/1.0-beta1/classes.py#L127-L152
- If not defined, these will default to modes defined by RFC 1459: https://github.com/GLolol/PyLink/blob/cb3187c/classes.py#L118-L152 - An example of mode mapping hardcoding can be found here: https://github.com/jlu5/PyLink/blob/1.0-beta1/protocols/ts6.py#L259-L311
- You can find a list of supported (named) channel modes [here](channel-modes.csv), and a list of user modes [here](user-modes.csv).
- `self.prefixmodes`: This defines a mapping of prefix modes (+o, +v, etc.) to their respective mode prefix. This will default to `{'o': '@', 'v': '+'}` (the standard op and voice) if not defined. - `self.prefixmodes`: This defines a mapping of prefix modes (+o, +v, etc.) to their respective mode prefix. This will default to `{'o': '@', 'v': '+'}` (the standard op and voice) if not defined.
- Example: `self.prefixmodes = {'o': '@', 'h': '%', 'v': '+'}` - Example: `self.prefixmodes = {'o': '@', 'h': '%', 'v': '+'}`
- `self.connected`: this is a `threading.Event` object that plugins use to determine if the network has finished bursting. Protocol modules should set this to True via `self.connected.set()` when ready.
### Topics ## PyLink Protocol capabilities
PyLink 1.2 introduced the concept of protocol-defined capabilities, so that plugins wishing to use IRCd-specific features don't have to hard code protocol modules by name. Protocol capabilities are defined in `self.protocol_caps` (a set of strings) and may be changed freely before `self.connected` is set. Individual capabilities are then checked by plugins via `irc.has_cap(capability_name)`.
When receiving or sending topics, there is a `topicset` attribute in the IRC channel (IrcChannel) object that should be set **True**. It simply denotes that a topic has been set in the channel at least once. As of writing, the following protocol capabilities (case-sensitive) are implemented:
(Relay uses this so it doesn't overwrite topics with empty ones during burst, when a relay channel initialize before the uplink has sent the topic for it) ### Supported protocol capabilities
- `can-host-relay` - whether servers using this protocol can host a relay channel (for sanity reasons, this should be off for anything that's not IRC S2S)
- `can-manage-bot-channels` - whether PyLink can manage which channels the bot itself is in. This is off for platforms such as Discord.
- `can-spawn-clients` - determines whether any spawned clients are real or virtual (mainly for `services_support`).
- `can-track-servers` - determines whether servers are accurately tracked (for `servermaps` and other statistics)
- `freeform-nicks` - if set, nicknames for PyLink's virtual clients are not subject to validity and nick collision checks. This implies the `slash-in-nicks` capability.
- Note: PyLink already allows incoming nicks to be freeform, provided they are encoded correctly and don't cause parsing conflicts (i.e. containing reserved chars on IRC)
- `has-irc-modes` - whether IRC style modes are supported
- `has-statusmsg` - whether STATUSMSG messages (e.g. `@#channel`) are supported
- `has-ts` - determines whether channel and user timestamps are tracked (and not spoofed)
- `slash-in-hosts` - determines whether `/` is allowed in hostnames
- `slash-in-nicks` - determines whether `/` is allowed in nicks
- `ssl-should-verify` - determines whether TLS certificates should be checked for validity by default - this should be enabled for any protocol modules needing to verify a remote server (e.g. Clientbot or a non-IRC API endpoint), and disabled for most IRC S2S links (where self-signed certs are widespread)
- `underscore-in-hosts` - determines whether `_` is allowed in client hostnames (yes, support for this actually varies by IRCd)
- `virtual-server` - marks the server as virtual, i.e. controlled by protocol module under a different server. Virtual servers are ignored by `rehash` and `disconnect` in the `networks` plugin.
- This is used by pylink-discord as of v0.2.0.
- `visible-state-only` - determines whether channels should be autocleared when the PyLink client leaves (for clientbot, etc.)
- Note: enabling this in a protocol module lets `coremods/handlers` automatically clean up old channels for you!
New protocol capabilities are generally added when needed - see https://github.com/jlu5/PyLink/issues/620
### Abstraction defaults
For reference, the `IRCS2SProtocol` class defines the following by default:
- `can-host-relay`
- `can-spawn-clients`
- `can-track-servers`
- `has-ts`
Whereas `PyLinkNetworkCore` defines no capabilities (i.e. an empty set) by default.
## PyLink structures
In this section, `self` refers to the network object/protocol module instance itself (i.e. from its own perspective).
### Server, User, Channel classes
PyLink defines classes named `Server`, `User`, and `Channel` in the `classes` module, and stores dictionaries of these in the `servers`, `users`, and `channels` attributes of a protocol object respectively.
- `self.servers` is a dictionary mapping server IDs (SIDs) to `Server` objects. If a protocol module does not use SIDs, servers are stored by server name instead.
- `self.users` is a dictionary mapping user IDs (UIDs) to `User` objects. If a protocol module does not use UIDs, a pseudo UID (PUID) generator such as [`classes.PUIDGenerator`](https://github.com/jlu5/PyLink/blob/3922d44173593e4bcceae1218bbc6f267caa9fc1/classes.py#L1710-L1726) *must* be used instead.
- The rationale behind this is because plugins tracking user lists are not designed to remove and re-add users when they change their nicks.
- When sending text back to the protocol module, it may be helpful to use the [`_expandPUID()`](https://github.com/jlu5/PyLink/blob/4a363aee509c5a0488a38b9e60f93ec59a274c3c/classes.py#L1213-L1231) function in `PyLinkNetworkCoreWithUtils` to expand these pseudo-UIDs back to regular nicks.
- `self._channels` and `self.channels` are [IRC case-insensitive dictionaries](https://github.com/jlu5/PyLink/blob/4a363aee509c5a0488a38b9e60f93ec59a274c3c/structures.py#L114-L116) mapping channel names to Channel objects.
- The key difference between these two dictionaries is that `_channels` is powered by `classes.ChannelState` and creates new channels *automatically* when they are accessed by index. This makes writing protocol modules easier, as they can assume that the channels they wish to modify always exist (no chance of `KeyError`!).
- `self.channels`, on the other hand, does *not* implicitly create channels and is thus better suited for plugins.
The `Channel`, `User`, and `Server` classes are initiated as follows:
- `Channel(self, name)` - First arg is the protocol object, second is the channel name.
- `User(self, nick, ts, uid, server, ident='null', host='null', realname='PyLink dummy client', realhost='null', ip='0.0.0.0', manipulatable=False, opertype='IRC Operator')` - These arguments are essentially the same as `spawn_client()`'s.
- `Server(self, uplink, name, internal=False, desc="(None given)")`
- The `uplink` (type `str`) option sets the SID of the uplink server, or *None* for both the main PyLink server and its uplink.
- The `name` option sets the server name.
- The `internal` boolean sets whether the server is an internal PyLink server.
- The `desc` option sets the server description, when applicable.
#### Statekeeping specifics
- When a user is introduced, their UID must be added to both `self.users` and to the `users` set in the `Server` object hosting the user (`self.servers[SID].users`). The latter list is used internally to track SQUITs.
- When a user joins a channel, the channel name is added to the User object's `channels` set (`self.users[UID].channels`), as well as the Channel object's user list (`self.channels[CHANNELNAME].users`)
- When a user disconnects, the `_remove_client` helper method can be called on their UID to automatically remove them from the relevant Server object, as well as all channels they were in.
- When a user leaves a channel, the `Channel.remove_user()` method can be used to easily remove them from the channel state, and vice versa.
### Mode formats ### Mode formats
Modes are stored a special format in PyLink, different from raw mode strings in order to make them easier to parse. Mode strings can be turned into mode *lists*, which are used to represent mode changes in hooks, and when storing modes internally. Modes are stored not stored as strings, but lists of mode pairs in order to ease parsing. These lists of mode pairs are used both to represent mode changes in hooks and store modes internally.
`utils.parseModes(irc, target, split_modestring)` is used to convert mode strings to mode lists, where `irc` is the IRC object, `target` is the channel name or UID the mode is being set on, and `split_modestring` is the string of modes to parse, *split at each space* (meaning that it's really a list). `self.parse_modes(target, modestring)` is used to convert mode strings to mode lists. `target` is the channel name/UID the mode is being set on, while `modestring` takes either a string or string split by spaces (really a list).
- `utils.parseModes(irc, '#chat', ['+tHIs', '*!*@is.sparta'])` would give: - `self.parse_modes('#chat', ['+tHIs', '*!*@is.sparta'])` would give:
- `[('+t', None), ('+H', None), ('+I', '*!*@is.sparta'), ('+s', None)]` - `[('+t', None), ('+H', None), ('+I', '*!*@is.sparta'), ('+s', None)]`
Also, `parseModes` will automatically convert prefix mode targets from nicks to UIDs, and drop invalid modes settings. `parse_modes()` will also automatically convert prefix mode targets from nicks to UIDs, and drop any duplicate (already set) or invalid (e.g. missing argument) modes.
- `utils.parseModes(irc, '#chat', ['+ol', 'invalidnick'])`: - `self.parse_modes('#chat', ['+ol invalidnick'])`:
- `[]` - `[]`
- `utils.parseModes(irc, '#chat', ['+o', 'GLolol'])`: - `self.parse_modes('#chat', ['+o jlu5'])`:
- `[('+o', '001ZJZW01')]` - `[('+o', '001ZJZW01')]`
Then, a parsed mode list can be applied to channel name or UID using `utils.applyModes(irc, target, parsed_modelist)`. Afterwords, a parsed mode list can be applied to channel name or UID using `self.apply_modes(target, parsed_modelist)`.
Internally, modes are stored in channel and user objects as sets: `(userobj or chanobj).modes`: **Note**: for protocols that accept or reject mode changes based on TS (i.e. practically every IRCd), you will want to use [`updateTS(...)`](https://github.com/jlu5/PyLink/blob/master/classes.py#L1484-L1487) instead to only apply the modes if the source TS is lower.
Internally, modes are stored in `Channel` and `User` objects as sets, **with the `+` prefixing each mode character omitted**. These sets are accessed via the `modes` attribute:
``` ```
<+GLolol> PyLink-devel, eval irc.users[source].modes <+jlu5> PyLink-devel, eval irc.users[source].modes
<@PyLink-devel> {('i', None), ('x', None), ('w', None), ('o', None)} <@PyLink-devel> {('i', None), ('x', None), ('w', None), ('o', None)}
<+GLolol> PyLink-devel, eval irc.channels['#chat'].modes <+jlu5> PyLink-devel, eval irc.channels['#chat'].modes
<@PyLink-devel> {('n', None), ('t', None)} <@PyLink-devel> {('n', None), ('t', None)}
``` ```
*With the exception of channel prefix modes* (op, voice, etc.), which are stored as a dict of sets in `chanobj.prefixmodes`: **Exception**: the owner, admin, op, halfop, and voice channel prefix modes are stored separately as a dict of sets in `Channel.prefixmodes`:
``` ```
<@GLolol> PyLink-devel, eval irc.channels['#chat'].prefixmodes <@jlu5> PyLink-devel, eval irc.channels['#chat'].prefixmodes
<+PyLink-devel> {'op': set(), 'halfop': set(), 'voice': {'38QAAAAAA'}, 'owner': set(), 'admin': set()} <+PyLink-devel> {'op': set(), 'halfop': set(), 'voice': {'38QAAAAAA'}, 'owner': set(), 'admin': set()}
``` ```
When a certain mode (e.g. owner) isn't supported on a network, the key still exists in `prefixmodes` but is simply unused. When a certain mode (e.g. owner) isn't supported on a network, the key still exists in `prefixmodes` but is simply unused.
You can see a list of supported (named) channel modes [here](channel-modes.csv). ### Topics
When receiving or sending topics, there is a `topicset` attribute in the `Channel` object that should be set to **True**. This boolean denotes that a topic has been set in the channel at least once; Relay uses it to know not to overwrite topics with empty ones during startup, when topics have not been received from all networks yet.
*Caveat:* Topic handlers on the current protocol modules do not follow TS rules (which vary by IRCd), and blindly accept data. See issue https://github.com/jlu5/PyLink/issues/277
## Configuration key validation
Starting with PyLink 1.x, protocol modules can specify which config values within a server block they need in order to work. This is done by adjusting the `self.conf_keys` attribute, usually in the protocol module's `__init__()` method. The default set, defined in [`Classes.Protocol`](https://github.com/jlu5/PyLink/blob/1.0-beta1/classes.py#L1202-L1204), includes `{'ip', 'port', 'hostname', 'sid', 'sidrange', 'protocol', 'sendpass', 'recvpass'}`. Should any of these keys be missing from a server block, PyLink will bail with a configuration error.
As an example, one protocol module that tweaks this is [`Clientbot`](https://github.com/jlu5/PyLink/blob/1.0-beta1/protocols/clientbot.py#L17-L18), which removes all options except `ip`, `protocol`, and `port`.
## The final checklist
In short, protocol modules have some very important jobs. If any of these aren't done correctly, you will be left with a very broken, desynced services server:
1) Handle incoming commands from the uplink.
2) Return [hook data](hooks-reference.md) for relevant commands, so that plugins can receive data from the uplink.
3) Make sure channel/user states are kept correctly. Joins, quits, parts, kicks, mode changes, nick changes, etc. should all be handled accurately where relevant.
4) Implement the specified outgoing command functions, which are used by plugins to send commands to the uplink.
5) Set the `threading.Event` instance `self.connected` to True (via `self.connected.set()`) when the connection with the uplink is fully established. This is important for Relay and the services API, which will refuse to initialize if the connection is not marked ready.
6) Check that `recvpass` is correct when applicable, and raise `ProtocolError` with a relevant error message if not.
7) Declare the correct set of protocol module capabilities to prevent confusing PyLink's plugins.
## Changes to this document
* 2021-06-15 (3.1-dev)
- Added `oper_notice()` function to send notices to opers (GLOBOPS / OPERWALL on most IRCds)
- Update notes about non-IRC protocols and PyLinkNetworkCoreWithUtils
* 2019-11-02 (2.1-beta1)
- Added protocol capability: `can-manage-bot-channels`
* 2019-10-10 (2.1-beta1)
- Added protocol capability: `has-irc-modes`
* 2019-06-23 (2.1-alpha2)
- Added new protocol capabilities: `virtual-server` and `freeform-nicks`
* 2018-07-11 (2.0.0)
- Version bump for 2.0 stable release; no meaningful content changes.
* 2018-06-26 (2.0-beta1)
- Added documentation for PyLink protocol capabilities
- Wording tweaks, restructured headings
- Consistently refer to protocol module attributes as `self.<whatever>` instead of `irc.<whatever>`
* 2018-05-09 (2.0-alpha3)
- `kill` and `kick` implementations should raise `NotImplementedError` if not supported (anti-desync measure).
- Future PyLink versions will further standardize which functions should be stubbed (no-op) when not available and which should raise an error.
* 2017-10-05 (2.0-alpha1)
- Added notes on user statekeeping and the tracking/helper functions used.
- Mention the `post_connect()` function that must be defined by protocols inheriting from IRCNetwork.
* 2017-08-30 (2.0-dev)
- Rewritten specification for the IRC-protocol class convergence in PyLink 2.0.
- Updated the spec for 2.0 method renames and class restructures.
- Added a proper "Starting Steps" section detailing which new classes inherit from and when.
- Explicitly document the Server, User, and Channel classes.
* 2017-03-15 (1.2-dev)
- Corrected the location of `self.cmodes/umodes/prefixmodes` attributes
- Mention `self.conf_keys` as a special variable for completeness
* 2017-01-29 (1.2-dev)
- NOTICE can now be sent from servers.
- This section was added.

View File

@ -0,0 +1,47 @@
/* Graph showing inheritance with the current PyLink protocol modules:
* Update using: dot -Tsvg protocol-modules.dot > protocol-modules.svg
*/
digraph G {
edge [ penwidth=0.75, color="#111111CC" ];
subgraph cluster_core {
label="Core classes (pylinkirc.classes)";
style="filled";
node [style="filled",color="white"];
color="#90EE90";
"PyLinkNetworkCore" -> "PyLinkNetworkCoreWithUtils" -> "IRCNetwork";
}
subgraph cluster_helper {
label="Protocol module helpers\n(pylinkirc.protocols.ircs2s_common)";
style="filled";
node [style="filled",color="white"];
color="lightblue";
"IRCNetwork" -> "IRCCommonProtocol" -> "IRCS2SProtocol" -> "TS6BaseProtocol";
subgraph cluster_helper {
label="pylinkirc.protocols.ts6_common";
style="filled";
color="lightcyan";
"TS6BaseProtocol";
}
}
subgraph cluster_pluggable {
label="Complete protocol modules (pylinkirc.protocols.*)";
style="filled";
node [style="filled",color="white"];
color="khaki";
"IRCS2SProtocol" -> "p10";
"IRCS2SProtocol" -> "ngircd";
"TS6BaseProtocol" -> "ts6" -> "hybrid";
"TS6BaseProtocol" -> "inspircd";
"TS6BaseProtocol" -> "unreal";
"IRCCommonProtocol" -> "clientbot";
}
}

View File

@ -0,0 +1,155 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.38.0 (20140413.2041)
-->
<!-- Title: G Pages: 1 -->
<svg width="530pt" height="651pt"
viewBox="0.00 0.00 530.00 651.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 647)">
<title>G</title>
<polygon fill="white" stroke="none" points="-4,4 -4,-647 526,-647 526,4 -4,4"/>
<g id="clust1" class="cluster"><title>cluster_core</title>
<polygon fill="#90ee90" stroke="#90ee90" points="77,-416 77,-635 333,-635 333,-416 77,-416"/>
<text text-anchor="middle" x="205" y="-619.8" font-family="Times,serif" font-size="14.00">Core classes (pylinkirc.classes)</text>
</g>
<g id="clust2" class="cluster"><title>cluster_helper</title>
<polygon fill="lightblue" stroke="lightblue" points="83,-163 83,-408 302,-408 302,-163 83,-163"/>
<text text-anchor="middle" x="192.5" y="-392.8" font-family="Times,serif" font-size="14.00">Protocol module helpers</text>
<text text-anchor="middle" x="192.5" y="-377.8" font-family="Times,serif" font-size="14.00">(pylinkirc.protocols.ircs2s_common)</text>
</g>
<g id="clust3" class="cluster"><title>cluster_helper</title>
<polygon fill="lightcyan" stroke="lightcyan" points="91,-171 91,-246 285,-246 285,-171 91,-171"/>
<text text-anchor="middle" x="188" y="-230.8" font-family="Times,serif" font-size="14.00">pylinkirc.protocols.ts6_common</text>
</g>
<g id="clust4" class="cluster"><title>cluster_pluggable</title>
<polygon fill="khaki" stroke="khaki" points="8,-8 8,-155 514,-155 514,-8 8,-8"/>
<text text-anchor="middle" x="261" y="-139.8" font-family="Times,serif" font-size="14.00">Complete protocol modules (pylinkirc.protocols.*)</text>
</g>
<!-- PyLinkNetworkCore -->
<g id="node1" class="node"><title>PyLinkNetworkCore</title>
<ellipse fill="white" stroke="white" cx="205" cy="-586" rx="84.485" ry="18"/>
<text text-anchor="middle" x="205" y="-582.3" font-family="Times,serif" font-size="14.00">PyLinkNetworkCore</text>
</g>
<!-- PyLinkNetworkCoreWithUtils -->
<g id="node2" class="node"><title>PyLinkNetworkCoreWithUtils</title>
<ellipse fill="white" stroke="white" cx="205" cy="-514" rx="119.679" ry="18"/>
<text text-anchor="middle" x="205" y="-510.3" font-family="Times,serif" font-size="14.00">PyLinkNetworkCoreWithUtils</text>
</g>
<!-- PyLinkNetworkCore&#45;&gt;PyLinkNetworkCoreWithUtils -->
<g id="edge1" class="edge"><title>PyLinkNetworkCore&#45;&gt;PyLinkNetworkCoreWithUtils</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M205,-567.697C205,-559.983 205,-550.712 205,-542.112"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="208.5,-542.104 205,-532.104 201.5,-542.104 208.5,-542.104"/>
</g>
<!-- IRCNetwork -->
<g id="node3" class="node"><title>IRCNetwork</title>
<ellipse fill="white" stroke="white" cx="205" cy="-442" rx="55.7903" ry="18"/>
<text text-anchor="middle" x="205" y="-438.3" font-family="Times,serif" font-size="14.00">IRCNetwork</text>
</g>
<!-- PyLinkNetworkCoreWithUtils&#45;&gt;IRCNetwork -->
<g id="edge2" class="edge"><title>PyLinkNetworkCoreWithUtils&#45;&gt;IRCNetwork</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M205,-495.697C205,-487.983 205,-478.712 205,-470.112"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="208.5,-470.104 205,-460.104 201.5,-470.104 208.5,-470.104"/>
</g>
<!-- IRCCommonProtocol -->
<g id="node4" class="node"><title>IRCCommonProtocol</title>
<ellipse fill="white" stroke="white" cx="205" cy="-344" rx="89.0842" ry="18"/>
<text text-anchor="middle" x="205" y="-340.3" font-family="Times,serif" font-size="14.00">IRCCommonProtocol</text>
</g>
<!-- IRCNetwork&#45;&gt;IRCCommonProtocol -->
<g id="edge3" class="edge"><title>IRCNetwork&#45;&gt;IRCCommonProtocol</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M205,-423.837C205,-409.503 205,-388.807 205,-372.216"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="208.5,-372.014 205,-362.014 201.5,-372.014 208.5,-372.014"/>
</g>
<!-- IRCS2SProtocol -->
<g id="node5" class="node"><title>IRCS2SProtocol</title>
<ellipse fill="white" stroke="white" cx="214" cy="-272" rx="69.5877" ry="18"/>
<text text-anchor="middle" x="214" y="-268.3" font-family="Times,serif" font-size="14.00">IRCS2SProtocol</text>
</g>
<!-- IRCCommonProtocol&#45;&gt;IRCS2SProtocol -->
<g id="edge4" class="edge"><title>IRCCommonProtocol&#45;&gt;IRCS2SProtocol</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M207.225,-325.697C208.217,-317.983 209.408,-308.712 210.514,-300.112"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="213.997,-300.469 211.801,-290.104 207.054,-299.576 213.997,-300.469"/>
</g>
<!-- clientbot -->
<g id="node13" class="node"><title>clientbot</title>
<ellipse fill="white" stroke="white" cx="464" cy="-106" rx="41.6928" ry="18"/>
<text text-anchor="middle" x="464" y="-102.3" font-family="Times,serif" font-size="14.00">clientbot</text>
</g>
<!-- IRCCommonProtocol&#45;&gt;clientbot -->
<g id="edge12" class="edge"><title>IRCCommonProtocol&#45;&gt;clientbot</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M233.699,-326.923C251.178,-316.837 273.771,-303.27 293,-290 319.052,-272.023 326.338,-268.098 349,-246 386.799,-209.142 424.679,-160.559 446.146,-131.67"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="449.147,-133.498 452.263,-123.372 443.512,-129.345 449.147,-133.498"/>
</g>
<!-- TS6BaseProtocol -->
<g id="node6" class="node"><title>TS6BaseProtocol</title>
<ellipse fill="white" stroke="white" cx="187" cy="-197" rx="72.2875" ry="18"/>
<text text-anchor="middle" x="187" y="-193.3" font-family="Times,serif" font-size="14.00">TS6BaseProtocol</text>
</g>
<!-- IRCS2SProtocol&#45;&gt;TS6BaseProtocol -->
<g id="edge5" class="edge"><title>IRCS2SProtocol&#45;&gt;TS6BaseProtocol</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M207.738,-254.069C204.49,-245.287 200.448,-234.36 196.798,-224.49"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="200.073,-223.257 193.322,-215.092 193.508,-225.685 200.073,-223.257"/>
</g>
<!-- p10 -->
<g id="node7" class="node"><title>p10</title>
<ellipse fill="white" stroke="white" cx="293" cy="-106" rx="27" ry="18"/>
<text text-anchor="middle" x="293" y="-102.3" font-family="Times,serif" font-size="14.00">p10</text>
</g>
<!-- IRCS2SProtocol&#45;&gt;p10 -->
<g id="edge6" class="edge"><title>IRCS2SProtocol&#45;&gt;p10</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M269.936,-261.101C277.361,-257.496 284.089,-252.611 289,-246 313.082,-213.582 307.4,-164.181 300.569,-133.833"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="303.928,-132.834 298.152,-123.952 297.129,-134.497 303.928,-132.834"/>
</g>
<!-- ngircd -->
<g id="node8" class="node"><title>ngircd</title>
<ellipse fill="white" stroke="white" cx="371" cy="-106" rx="33.2948" ry="18"/>
<text text-anchor="middle" x="371" y="-102.3" font-family="Times,serif" font-size="14.00">ngircd</text>
</g>
<!-- IRCS2SProtocol&#45;&gt;ngircd -->
<g id="edge7" class="edge"><title>IRCS2SProtocol&#45;&gt;ngircd</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M276.712,-264.047C289.461,-260.278 302.009,-254.58 312,-246 345.593,-217.151 360.541,-165.861 366.824,-134.301"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="370.276,-134.882 368.637,-124.415 363.391,-133.62 370.276,-134.882"/>
</g>
<!-- ts6 -->
<g id="node9" class="node"><title>ts6</title>
<ellipse fill="white" stroke="white" cx="43" cy="-106" rx="27" ry="18"/>
<text text-anchor="middle" x="43" y="-102.3" font-family="Times,serif" font-size="14.00">ts6</text>
</g>
<!-- TS6BaseProtocol&#45;&gt;ts6 -->
<g id="edge8" class="edge"><title>TS6BaseProtocol&#45;&gt;ts6</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M137.801,-183.792C118.234,-177.355 96.3078,-168.023 79,-155 70.5984,-148.678 63.2254,-139.88 57.3761,-131.54"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="60.2708,-129.572 51.8527,-123.138 54.4215,-133.417 60.2708,-129.572"/>
</g>
<!-- inspircd -->
<g id="node11" class="node"><title>inspircd</title>
<ellipse fill="white" stroke="white" cx="209" cy="-106" rx="38.9931" ry="18"/>
<text text-anchor="middle" x="209" y="-102.3" font-family="Times,serif" font-size="14.00">inspircd</text>
</g>
<!-- TS6BaseProtocol&#45;&gt;inspircd -->
<g id="edge10" class="edge"><title>TS6BaseProtocol&#45;&gt;inspircd</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M191.242,-178.84C194.377,-166.158 198.707,-148.64 202.307,-134.077"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="205.769,-134.655 204.771,-124.107 198.974,-132.975 205.769,-134.655"/>
</g>
<!-- unreal -->
<g id="node12" class="node"><title>unreal</title>
<ellipse fill="white" stroke="white" cx="120" cy="-106" rx="32.4942" ry="18"/>
<text text-anchor="middle" x="120" y="-102.3" font-family="Times,serif" font-size="14.00">unreal</text>
</g>
<!-- TS6BaseProtocol&#45;&gt;unreal -->
<g id="edge11" class="edge"><title>TS6BaseProtocol&#45;&gt;unreal</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M174.398,-179.26C164.099,-165.579 149.413,-146.071 137.895,-130.772"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="140.682,-128.653 131.871,-122.769 135.089,-132.863 140.682,-128.653"/>
</g>
<!-- hybrid -->
<g id="node10" class="node"><title>hybrid</title>
<ellipse fill="white" stroke="white" cx="50" cy="-34" rx="33.5952" ry="18"/>
<text text-anchor="middle" x="50" y="-30.3" font-family="Times,serif" font-size="14.00">hybrid</text>
</g>
<!-- ts6&#45;&gt;hybrid -->
<g id="edge9" class="edge"><title>ts6&#45;&gt;hybrid</title>
<path fill="none" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" d="M44.7303,-87.6966C45.5017,-79.9827 46.4288,-70.7125 47.2888,-62.1124"/>
<polygon fill="#111111" fill-opacity="0.800000" stroke="#111111" stroke-width="0.75" stroke-opacity="0.800000" points="50.7771,-62.403 48.2896,-52.1043 43.8118,-61.7064 50.7771,-62.403"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,13 @@
# Release Process for PyLink
This document documents the steps that I (James) use to release updates to PyLink.
1) Draft the next release's changelog in `RELNOTES.md`
2) Bump the version in the [`VERSION`](VERSION) file.
3) Commit the changes to `VERSION` and `RELNOTES.md`, and tag+sign this commit as the new release. Do not prefix version numbers with "v".
4) Publish the release via the GitHub release page, using the same changelog content as `RELNOTES.md`.
5) For stable releases, ~~also upload to PyPI: `python3 setup.py sdist upload`~~ PyPI uploads are handled automatically via Travis-CI.

View File

@ -0,0 +1,101 @@
# PyLink Services Bot API
The goal of PyLink's Services API is to make writing custom services easier. It is able to automatically spawn service bots on connect, handle rejoins on kill and kick, and expose a way for plugins to bind commands to various services bots. It also handles U-line servprotect modes when enabled and supported on a particular network (i.e. the `protect_services` option).
## Basic service creation
Services can be registered and created using code similar to the following in a plugin:
```python
from pylinkirc import utils, world
# Description is optional (though recommended), and usually around a sentence or two.
desc = "Optional description of servicenick, in sentence form."
# First argument is the internal service name.
# utils.register_service() returns a utils.ServiceBot instance, which is also stored
# as world.services['myservice'].
myservice = utils.register_service('myservice', desc=desc)
```
`utils.register_service()` passes its arguments directly to the `utils.ServiceBot` class constructor, which in turn supports the following options:
- **`name`** - defines the service name (mandatory)
- `default_help` - Determines whether the default HELP command should be used for the service. Defaults to True.
- `default_list` - Determines whether the default LIST command should be used for the service. Defaults to True.
- `default_nick` - Sets the default nick this service should use if the user doesn't provide it. Defaults to the same as the service name.
- `manipulatable` - Determines whether the bot is marked manipulatable. Only manipulatable clients can be force joined, etc. using PyLink commands. Defaults to False.
- `desc` - Sets the command description of the service. This is shown in the default HELP command if enabled.
**NOTE**: It is convention for the service name in `utils.register_service('SERVICE')` to match your plugin name, as the services API implicitly loads [configuration options](../advanced-services-config.md) from config blocks named `SERVICE:` (which you may want to put plugin options in as well).
Implementation note: if the `spawn_service` option is disabled (either globally or for your service bot), `register_service` will return the main PyLink `ServiceBot` instance (i.e. `world.services['pylink']`), which you can modify as usual. `unregister_service` calls to your service name will be silently ignored as no `ServiceBot` instance is actually registered for that name. Altogether, this allows service-spawning plugins to function normally regardless of the `spawn_service` value.
### Getting the UID of a bot
To obtain the UID of a service bot on a specific network, use `myservice.uids.get(irc.name)` (where `irc` is the network object).
### Removing services on unload
All plugins using the services API should have a `die()` function that unregisters all service bots that they've created. A simple example would be in the `games` plugin:
```python
def die(irc=None):
utils.unregister_service('games')
```
Should your service bot define any persistent channels, you will also want to clear them on unload via `myservice.clear_persistent_channels(None, 'your-namespace', ...)`
## Persistent channel joining
Since PyLink 2.0-alpha3, persistent channels are handled in a plugin specific manner. For any service bot on any network, a plugin can register a list of channels that the bot should join persistently (i.e. through kicks and kills). Instead of removing channels from service bots directly, plugins then "request" parts through the services API, which succeed only if no other plugins still mark the channel as persistent. This rework fixes [edge-case desyncs](https://github.com/jlu5/PyLink/issues/265) in earlier versions when multiple plugins change a service bot's channel list, and replaces the `ServiceBot.extra_channels` attribute (which is no longer supported).
Note: Autojoin channels defined in a network's server block are always treated as persistent on that network.
### Channel management methods
Channels, persistent and otherwise are managed through the following functions implemented by `ServiceBot`. While namespaces for channel registrations can technically be any string, it is preferable to keep them close (or equal) to your plugin name.
- `myservice.add_persistent_channel(irc, namespace, channel, try_join=True)`: Adds a persistent channel to the service bot on the given network and namespace.
- `try_join` determines whether the service bot should try to join the channel immediately; you can disable this if you prefer to manage joins by yourself.
- `myservice.remove_persistent_channel(irc, namespace, channel, try_part=True, part_reason='')`: Removes a persistent channel from the service bot on the given network and namespace.
- `try_part` determines whether a part should be requested from the channel immediately. (`part_reason` is ignored if this is False)
- `myservice.get_persistent_channels(irc, namespace=None)`: Returns a set of persistent channels for the IRC network, optionally filtering by namespace is one is given. The channels defined in the network's server block are also included because they are always treated as persistent.
- `myservice.clear_persistent_channels(irc, namespace, try_part=True, part_reason='')`: Clears all the persistent channels defined by a namespace. `irc` can also be `None` to clear persistent channels for all networks in this namespace.
- `myservice.join(irc, channels, ignore_empty=True)`: Joins the given service bot to the given channel(s). `channels` can be an iterable of channel names or the name of a single channel (type `str`).
- The `ignore_empty` option sets whether we should skip joining empty channels and join them later when we see someone else join (if it is marked persistent). This option is automatically *disabled* on networks where we cannot monitor channels we're not in (e.g. on Clientbot).
- Before 2.0-alpha3, this function implicitly marks channels it receives to be persistent - this is no longer the case!
- `myservice.part(irc, channels, reason='')`: Requests a part from the given channel(s) - that is, leave only if no other plugins still register it as a persistent channel.
- `channels` can be an iterable of channel names or the name of a single channel (type `str`).
### A note on dynamicness
As of PyLink 2.0-alpha3, persistent channels are also "dynamic" in the sense that PyLink service bots will part channels marked persistent when they become empty, and rejoin when they are recreated. This feature will hopefully be more fine-tunable in future releases.
Dynamic channels are disabled on networks with the [`visible-state-only` protocol capability](pmodule-spec.md#pylink-protocol-capabilities) (e.g. Clientbot), where it is impossible to monitor the state of channels the bot is not in.
## Service bots and commands
Commands for service bots and commands for the main PyLink bot have two main differences.
1) Commands for service bots are bound using `myservice.add_cmd(cmdfunc, 'cmdname')` instead of `utils.add_cmd(...)`
2) Replies for service bot commands are sent using `myservice.reply(irc, text)` instead of `irc.reply(...)`
### Featured commands
Commands for service bots can also be marked as *featured*, which shows it with its command arguments in the default `LIST` command. To mark a command as featured, enable the `featured` option when binding it: e.g. `myservice.add_cmd(cmdfunc, featured=True)`.
### Command aliases
Since PyLink 2.0-alpha1, `ServiceBot.add_cmd(...)` and `utils.add_cmd(...)` support assigning aliases to a command by defining the `aliases` argument. Command aliases do not show in `LIST`, allowing command listings to be much cleaner. Instead, they are only mentioned when `HELP` is called on an alias command name or its parent.
Example:
```python
myservice.add_cmd(functwo, aliases=('abc',))
myservice.add_cmd(somefunc, aliases=('command1', 'command2'))
```
Note: use `(variable,)` when defining one length tuples to [prevent them from being parsed as a single string](https://wiki.python.org/moin/TupleSyntax).

View File

@ -1,20 +0,0 @@
#!/bin/bash
# Deletes and updates the contents of the autogen/ folder
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
pwd
shopt -s nullglob
rm -v autogen/*.html
# cd to the main folder
cd ../..
pwd
# Iterate over all the .py files and run pydoc3 on them.
for module in *.py protocols/*.py; do
echo "Running pydoc3 -w ./$module"
pydoc3 -w "./$module"
# Then, move the generated HTML files to the right place.
name="$(basename $module .py).html"
mv "$name" "$SCRIPT_DIR/autogen/$name"
done

View File

@ -0,0 +1,123 @@
# Using utils.IRCParser()
**As of 22/02/2017 (1.2-dev), PyLink allows plugin creators to either parse command arguments themselves
or use a sub-classed instance of [argparse.ArgumentParser()](https://docs.python.org/3/library/argparse.html)
to parse their arguments.**
First off, you will already have access to IRCParser due to importing `utils`.
Otherwise, this is how to include it...
```python
from pylinkirc import utils
```
When you add a command that you want to use `utils.IRCParser()` with, the following is a guide on how to add arguments.
**Note**: Most if not all the examples are from Python's argparse documentation, linked above.
#### Positional (Named) Arguments
```python
SomeParser.add_argument('argname')
```
#### Flag Arguments / Switch Arguments
```python
SomeParser = utils.IRCParser()
SomeParser.addargument('-a', '--argumentname')
```
##### Action
Actions define what to do when given an argument (i.e. whether it is used by itself or as some other sort of value).
Here are some of the actions that `argparse` defines:
* `store` - just stores the value given. This is the default when an action isn't provided.
```python
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo')
>>> parser.parse_args('--foo 1'.split())
Namespace(foo='1')
```
* `store_true`/`store_false` - used when you just want to check if an argument was used.
```python
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', action='store_true')
>>> parser.add_argument('--bar', action='store_false')
>>> parser.add_argument('--baz', action='store_false')
>>> parser.parse_args('--foo --bar'.split())
Namespace(foo=True, bar=False, baz=True)
```
* `append` - additively stores arguments if a switch is given multiple times.
```python
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--foo', action='append')
>>> parser.parse_args('--foo 1 --foo 2'.split())
Namespace(foo=['1', '2'])
```
* `count` - counts how many times an argument was used (for flag/switch arguments only)
```python
>>> parser = argparse.ArgumentParser()
>>> parser.add_argument('--verbose', '-v', action='count')
>>> parser.parse_args(['-vvv'])
Namespace(verbose=3)
```
You can also specify an arbitrary `Action` by sub-classing Action. If you want
to do this, you must `import argparse` in your plugin.
More info on that is available [here](https://docs.python.org/3/library/argparse.html#action).
##### Type Constraints
If you want an argument to be of a certain type, you can include a `type=TYPE` keyword, done like so.
```python
SomeParser.add_argument('argname', type=int)
```
As such this will return an error if the input can not be converted to an `int`.
Types usable are `str` and `int`,
there may be more that are allowed in this keyword argument,
but `str` and `int` are the only ones we have throughly used.
**Note**: TYPE can be technically any callable. More about that [here](https://docs.python.org/3/library/argparse.html#type)!
##### Choices
If you want to limit what the user can enter for an argument,
like if they have to choose something from a pre-existing list.
This can be used by adding `choices=['A', 'AAAA', 'CNAME']` into the
`SomeParser.add_argument()` call along with the option entries (-a/--argname).
```python
SomeParser.add_argument('argname', choices=['A', 'AAAA', 'CNAME'])
```
##### Needed Args (aka. nargs)
The keyword argument `nargs` or Needed Args associates a different number of arguments to an action.
* `N` - this is an integer; N arguments will be gathered into a list. nargs=1 produces a list of one item, while the default (not using nargs) produces just the argument itself.
* `'?'` - One argument will be used. If `default` is defined in the call, then default will be used if there is no given argument.
* `'*'` - All arguments are gathered into a list. It only makes sense to use this once in a command handler.
* `'+'` - Like '*' but raises an error if there wasn't at least one argument given.
* `utils.IRCParser.REMAINDER` - remaining arguments are gathered into a list; this is usually used when you need to get a phrase stored, such as the 'quote' text of a quote, a service bot part reason, etc. This is an alias to `argparse.REMAINDER`.

View File

@ -1 +0,0 @@
See [autogen/utils.html](autogen/utils.html) for auto-generated documentation for the utils module.

View File

@ -1,44 +1,73 @@
# Writing plugins for PyLink # Writing plugins for PyLink
PyLink plugins are modules that extend its functionality by giving it something to do. Without any plugins loaded, PyLink can only sit on a server and do absolutely nothing. ***Last updated for 2.1-alpha2 (2019-06-27).***
This guide, along with the sample plugins [`plugins/example.py`](../../plugins/example.py), and [`plugins/service.py`](../../plugins/demo_service.py) aim to show the basics of writing plugins for PyLink. Most features in PyLink (Relay, Automode, etc.) are implemented as plugins, which can be mix-and-matched on any particular instance. Without any plugins loaded, PyLink can connect to servers but won't accomplish anything useful.
This guide, along with the sample plugin [`example.py`](../../plugins/example.py) aim to show the basics of writing plugins for PyLink.
## Receiving data from IRC ## Receiving data from IRC
Plugins have two ways of communicating with IRC: hooks, and commands sent in PM to the main PyLink client. A simple plugin can use one, or any mixture of these. Plugins have two ways of communicating with IRC: hooks, and commands directed towards service clients. Any plugin can use one or a combination of these.
### Hooks ### Hook events
Hooks are probably the most versatile form of communication. The data in each hook payload is formatted as a Python `dict`, with different data keys depending on the command. PyLink's hooks system is designed as a protocol-independent method for protocol modules to communicate with plugins (and to a lesser extend, for plugins to communicate with each other). Hook events are the most versatile form of communication available, with each individual event generally corresponding to a specific chat or server event (e.g. `PRIVMSG`, `JOIN`, `KICK`). Each hook payload includes 4 parts:
For example, a `PRIVMSG` payload would give you the fields `target` and `text`, while a `PART` payload would only give you `channels` and `reason` fields.
There are many hook types available (one for each supported IRC command), and you can read more about them in the [PyLink hooks reference](hooks-reference.md). 1) The corresponding network object (IRC object) where the event took place (**type**: a subclass of `pylinkirc.classes.PyLinkNetworkCore`)
2) The numeric ID† of the sender (**type**: `str`)
3) An identifier for the command name, which may or may not be the same as the name of the hook depending on context (**type**: `str`)
4) A freeform `dict` of arguments, where data keys vary by command - see the [PyLink hooks reference](hooks-reference.md) for what's available where.
Plugins can bind to hooks using the `utils.add_hook()` function like so: `utils.add_hook(function_name, 'PRIVMSG')`, where `function_name` is your function definition, and `PRIVMSG` is whatever hook name you want to bind to. Once set up, `function_name` will be called whenever the protocol module receives a `PRIVMSG` command. Functions intended to be hook handlers therefore take in 4 arguments corresponding to the ones listed above: `irc`, `source`, `command`, and `args`.
Each hook-bound function takes 4 arguments: `irc, source, command, args`. #### Return codes for hook handlers
- **irc**: The IRC object where the hook was called. Plugins are globally loaded, so there will be one of these per network.
- **source**: The numeric of the sender. This will usually be a UID (for users) or a SID (for server).
- **command**: The true command name where the hook originates. This may or may not be the same as the name of the hook, depending on context.
- **args**: The hook data (a `dict`) associated with the command. Again, the available data keys differ by hook name
(see the [hooks reference](hooks-reference.md) for a list of which can be used).
Hook functions do not return anything, and can raise exceptions to be caught by the core. As of PyLink 2.0, the return value of hook handlers are used to determine how the original event will be passed on to further handlers (that is, those created by plugins loaded later, or hook handlers registered with a lower priority).
The following return values are supported so far:
- `None` or `True`: passthrough the event unchanged to further handlers (the default behavior)
- `False`: block the event from reaching other handlers
Hook handlers may raise exceptions without blocking the event from reaching further handlers; these are caught by PyLink and logged appropriately.
#### Modifying a hook payload
As of PyLink 2.1, it is acceptable to modify a hook event payload in any plugin handler. This can be used for filtering purposes, e.g. Antispam's part/quit message filtering.
You should take extra caution not to corrupt hook payloads, especially ones that relate to state keeping. Otherwise, other plugins may fail to function correctly.
### Hook priorities
The `priority` option in `utils.add_hook()` allows setting a hook handler's priority when binding it. When multiple modules bind to one hook command, handlers are called in order of decreasing priority (i.e. highest first).
There is no standard for hook priorities as of 2.0; instead they are declared as necessary. Some priority values used in 2.0 are shown here for reference (the default priority for handlers is **100**):
| Module | Commands | Priority | Description |
|-------------------|-----------------|----------|-------------|
| `service_support` | ENDBURST | 500 | This sets up services bots before plugins run so that they can assume their presence when initializing. |
| `antispam` | PRIVMSG, NOTICE | 990-1000 | This allows `antispam` to filter away spam before it can reach other handlers. |
| `relay` | PRIVMSG, NOTICE | 200 | Fixes https://github.com/jlu5/PyLink/issues/123. Essentially, this lets Relay forward messages calling commands before letting the command handler work (and then relaying its responses). |
| `ctcp` | PRIVMSG | 200 | The `ctcp` plugin processes CTCPs and blocks them from reaching the services command handler, preventing extraneous "unknown command" errors. |
### Bot commands ### Bot commands
For plugins that interact with regular users, you can also write commands for the PyLink bot, or [create service bots with their own command set](services-api.md). This section only details the former: Plugins can also define service bot commands, either for the main PyLink service bot or for one created by the plugin itself. This section only details the former - see the [Services API Guide](services-api.md) for details on the latter.
Plugins can add commands by including something like `utils.add_cmd(testcommand, "hello")`. Here, `testcommand` is the name of your function, and `hello` is the (optional) name of the command. If no command name is specified, it will use the same name as the function. Commands are registered by calling `utils.add_cmd()` with one or two arguments. Ex)
Now, your command function will be called whenever someone PMs the PyLink client with the command (e.g. `/msg PyLink hello`, case-insensitive). - `utils.add_cmd(testcommand, "hello")` registers a function named `testcommand` as the command handler for `hello` (i.e. `/msg PyLink hello`)
- `utils.add_cmd(testcommand)` registers a function named `testcommand` as the command handler for `testcommand`.
Each command function takes 3 arguments: `irc, source, args`. `utils.add_cmd(...)` also takes some keyword arguments, described in the [services API guide](services-api.md#service-bots-and-commands) (replace `myservice.add_cmd` with `utils.add_cmd`). Decorator syntax (`@utils.add_cmd`) can also be used for the second example above.
- **irc**: The IRC object where the command was called.
- **source**: The numeric of the sender. This will usually be a UID (for users) or a SID (for server).
- **args**: A `list` of space-separated command arguments (excluding the command name) that the command was called with. For example, `/msg PyLink hello world 1234` would give an `args` list of `['world', '1234']`
(Unfortunately, this means that for now, any fancy argument parsing has to be done manually.)
Each command handler function takes 3 arguments: `irc, source, args`.
- **irc**: The network object where the command was called.
- **source**: The numeric ID (or pseudo-ID) of the sender.
- **args**: A `list` of command arguments (not including the command name) that the command was called with. For example, `/msg PyLink hello world 1234` would give an `args` list of `['world', '1234']`
As of PyLink 1.2, there are two ways for a plugin to parse arguments: as a raw list of strings, or with `utils.IRCParser` (an [argparse](https://docs.python.org/3/library/argparse.html) wrapper). `IRCParser()` is documented in the ["using IRCParser"](using-ircparser.md) page.
Command handlers do not return anything and can raise exceptions, which are caught by the core and automatically return an error message. Command handlers do not return anything and can raise exceptions, which are caught by the core and automatically return an error message.
@ -46,15 +75,45 @@ Command handlers do not return anything and can raise exceptions, which are caug
Plugins receive data from the underlying protocol module, and communicate back using outgoing [command functions](pmodule-spec.md) implemented by the protocol module. They should *never* send raw data directly back to IRC, because that wouldn't be portable across different IRCds. Plugins receive data from the underlying protocol module, and communicate back using outgoing [command functions](pmodule-spec.md) implemented by the protocol module. They should *never* send raw data directly back to IRC, because that wouldn't be portable across different IRCds.
These functions are usually called in this fashion: `irc.proto.command(arg1, arg2, ...)`. For example, the command `irc.proto.join('10XAAAAAB', '#bots')` would join a PyLink client with UID `10XAAAAAB` to channel `#bots`. These functions are called in the form: `irc.command(arg1, arg2, ...)`. For example, the command `irc.join('10XAAAAAB', '#bots')` would join a PyLink client with UID `10XAAAAAB` to the channel `#bots`.
For sending messages (e.g. replies to commands), simpler forms of: For sending messages (e.g. replies to commands), simpler forms of:
- `irc.reply(text, notice=False, source=None)` - `irc.reply(text, notice=False, source=None)`
- `irc.error(text, notice=False, source=None)`
- and `irc.msg(targetUID, text, notice=False, source=None)` - and `irc.msg(targetUID, text, notice=False, source=None)`
are preferred. are preferred.
`irc.reply()` is a special form of `irc.msg` in that it automatically finds the target to reply to. If the command was called in a channel using fantasy, it will send the reply in that channel. Otherwise, the reply will be sent in a PM to the caller. `irc.reply()` is a frontend to `irc.msg()` which automatically finds the right target to reply to: that is, the channel for fantasy commands and the caller for PMs. `irc.error()` is in turn a wrapper around `irc.reply()` which prefixes the given text with `Error: `.
The sender UID for both can be set using the `source` argument, and defaults to the main PyLink client. The sender UID for all of these can be set using the `source` argument, and defaults to the main PyLink client.
## Access checking for commands
See the [Permissions API documentation](permissions-api.md) on how to restrict commands to certain users.
## Special triggers for plugin (un)loading
The following functions can also be defined in the body of a plugin to hook onto plugin loading / unloading.
- `main(irc=None)`: Called on plugin load. `irc` is only defined when the plugin is being reloaded from a network: otherwise, it means that PyLink has just been started.
- `die(irc=None)`: Called on plugin unload or daemon shutdown. `irc` is only defined when the shutdown or unload was called from an IRC network.
## Other tips
### Logging
Use PyLink's [global logger](https://docs.python.org/3/library/logging.html) (`from pylinkirc.log import log`) instead of print statements.
### Some useful attributes
- **`world.networkobjects`** provides a dict mapping network names (case sensitive) to their corresponding network objects/protocol module instances.
- **`irc.connected`** is a [`threading.Event()`](https://docs.python.org/3/library/threading.html#event-objects) object that is set when a network finishes bursting.
- `world.started` is a [`threading.Event()`](https://docs.python.org/3/library/threading.html#event-objects) object that is set when all networks have been initialized.
- `world.plugins` provides a dict mapping loaded plugins' names (case sensitive) to their module objects. This is the preferred way to call another plugins's methods if need be (while of course, forcing you to check whether the other plugin is already loaded).
- `world.services` provides a dict mapping service bot names to their `utils.ServiceBot` instances.
### Useful modules
`classes.py`, `utils.py` and `structures.py` all provide a ton of public methods which aren't documented here for conciseness. In `classes.py`, `PyLinkNetworkCore` and `PyLinkNetworkCoreUtils` (which all protocol modules inherit from) are where many utility and state-checking functions sit.

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
#!/usr/bin/env bash
# Script to kill PyLink quickly when running under CPUlimit, since
# it will daemonize after threads are spawned and Ctrl-C won't work.
kill $(cat pylink.pid)
echo 'Killed. Press Ctrl-C in the PyLink window to exit.'

205
launcher.py Normal file
View File

@ -0,0 +1,205 @@
#!/usr/bin/env python3
"""
PyLink IRC Services launcher.
"""
import os
import signal
import sys
import time
from pylinkirc import __version__, conf, real_version, world
try:
import psutil
except ImportError:
psutil = None
args = {}
def _main():
conf.load_conf(args.config)
from pylinkirc.log import log
from pylinkirc import classes, utils, coremods, selectdriver
# Write and check for an existing PID file unless specifically told not to.
if not args.no_pid:
pid_dir = conf.conf['pylink'].get('pid_dir', '')
pidfile = os.path.join(pid_dir, '%s.pid' % conf.confname)
pid_exists = False
pid = None
if os.path.exists(pidfile):
try:
with open(pidfile) as f:
pid = int(f.read())
except OSError:
log.exception("Could not read PID file %s:", pidfile)
else:
pid_exists = True
if psutil is not None and os.name == 'posix':
# FIXME: Haven't tested this on other platforms, so not turning it on by default.
try:
proc = psutil.Process(pid)
except psutil.NoSuchProcess: # Process doesn't exist!
pid_exists = False
log.info("Ignoring stale PID %s from PID file %r: no such process exists.", pid, pidfile)
else:
# This PID got reused for something that isn't us?
if not any('pylink' in arg.lower() for arg in proc.cmdline()):
log.info("Ignoring stale PID %s from PID file %r: process command line %r is not us", pid, pidfile, proc.cmdline())
pid_exists = False
if pid and pid_exists:
if args.rehash:
os.kill(pid, signal.SIGUSR1)
log.info('OK, rehashed PyLink instance %s (config %r)', pid, args.config)
sys.exit()
elif args.stop or args.restart: # Handle --stop and --restart options
os.kill(pid, signal.SIGTERM)
log.info("Waiting for PyLink instance %s (config %r) to stop...", pid, args.config)
while os.path.exists(pidfile):
# XXX: this is ugly, but os.waitpid() only works on non-child processes on Windows
time.sleep(0.2)
log.info("Successfully killed PID %s for config %r.", pid, args.config)
if args.stop:
sys.exit()
else:
log.error("PID file %r exists; aborting!", pidfile)
if psutil is None:
log.error("If PyLink didn't shut down cleanly last time it ran, or you're upgrading "
"from PyLink < 1.1-dev, delete %r and start the server again.", pidfile)
if os.name == 'posix':
log.error("Alternatively, you can install psutil for Python 3 (pip3 install psutil), "
"which will allow this launcher to detect stale PID files and ignore them.")
sys.exit(1)
elif args.stop or args.restart or args.rehash: # XXX: also repetitive
# --stop and --restart should take care of stale PIDs.
if pid:
world._should_remove_pid = True
log.error('Cannot stop/rehash PyLink: no process with PID %s exists.', pid)
else:
log.error('Cannot stop/rehash PyLink: PID file %r does not exist or cannot be read.', pidfile)
sys.exit(1)
world._should_remove_pid = True
log.info('PyLink %s starting...', __version__)
world.daemon = args.daemonize
if args.daemonize:
if args.no_pid:
print('ERROR: Combining --no-pid and --daemonize is not supported.')
sys.exit(1)
elif os.name != 'posix':
print('ERROR: Daemonization is not supported outside POSIX systems.')
sys.exit(1)
else:
log.info('Forking into the background.')
log.removeHandler(world.console_handler)
# Adapted from https://stackoverflow.com/questions/5975124/
if os.fork():
# Fork and exit the parent process.
os._exit(0)
os.setsid() # Decouple from our lovely terminal
if os.fork():
# Fork again to prevent starting zombie apocalypses.
os._exit(0)
else:
# For foreground sessions, set the terminal window title.
# See https://bbs.archlinux.org/viewtopic.php?id=85567 &
# https://stackoverflow.com/questions/7387276/
if os.name == 'nt':
import ctypes
ctypes.windll.kernel32.SetConsoleTitleW("PyLink %s" % __version__)
elif os.name == 'posix':
sys.stdout.write("\x1b]2;PyLink %s\x07" % __version__)
if not args.no_pid:
# Write the PID file only after forking.
with open(pidfile, 'w') as f:
f.write(str(os.getpid()))
# Load configured plugins
to_load = conf.conf['plugins']
utils._reset_module_dirs()
for plugin in to_load:
try:
world.plugins[plugin] = pl = utils._load_plugin(plugin)
except Exception as e:
log.exception('Failed to load plugin %r: %s: %s', plugin, type(e).__name__, str(e))
else:
if hasattr(pl, 'main'):
log.debug('Calling main() function of plugin %r', pl)
pl.main()
# Initialize all the networks one by one
for network, sdata in conf.conf['servers'].items():
try:
protoname = sdata['protocol']
except (KeyError, TypeError):
log.error("(%s) Configuration error: No protocol module specified, aborting.", network)
else:
# Fetch the correct protocol module.
try:
proto = utils._get_protocol_module(protoname)
# Create and connect the network.
world.networkobjects[network] = irc = proto.Class(network)
log.debug('Connecting to network %r', network)
irc.connect()
except:
log.exception('(%s) Failed to connect to network %r, skipping it...',
network, network)
continue
world.started.set()
log.info("Loaded plugins: %s", ', '.join(sorted(world.plugins.keys())))
selectdriver.start()
def main():
import argparse
global args
parser = argparse.ArgumentParser(description='Starts an instance of PyLink IRC Services.')
parser.add_argument('config', help='specifies the path to the config file (defaults to pylink.yml)', nargs='?', default='pylink.yml')
parser.add_argument("-v", "--version", help="displays the program version and exits", action='store_true')
parser.add_argument("-c", "--check-pid", help="no-op; kept for compatibility with PyLink <= 1.2.x", action='store_true')
parser.add_argument("-n", "--no-pid", help="skips generating and checking PID files", action='store_true')
parser.add_argument("-r", "--restart", help="restarts the PyLink instance with the given config file", action='store_true')
parser.add_argument("-s", "--stop", help="stops the PyLink instance with the given config file", action='store_true')
parser.add_argument("-R", "--rehash", help="rehashes the PyLink instance with the given config file", action='store_true')
parser.add_argument("-d", "--daemonize", help="daemonizes the PyLink instance on POSIX systems", action='store_true')
parser.add_argument("-t", "--trace", help="traces through running Python code; useful for debugging", action='store_true')
parser.add_argument('--trace-ignore-mods', help='comma-separated list of extra modules to ignore when tracing', action='store', default='')
parser.add_argument('--trace-ignore-dirs', help='comma-separated list of extra directories to ignore when tracing', action='store', default='')
args = parser.parse_args()
if args.version: # Display version and exit
print('PyLink %s (in VCS: %s)' % (__version__, real_version))
sys.exit()
# XXX: repetitive
elif args.no_pid and (args.restart or args.stop or args.rehash):
print('ERROR: --no-pid cannot be combined with --restart or --stop')
sys.exit(1)
elif args.rehash and os.name != 'posix':
print('ERROR: Rehashing via the command line is not supported outside Unix.')
sys.exit(1)
if args.trace:
import trace
tracer = trace.Trace(ignoremods=args.trace_ignore_mods.split(','),
ignoredirs=args.trace_ignore_dirs.split(','))
tracer.runctx('_main()', globals=globals(), locals=locals())
else:
_main()

93
log.py
View File

@ -7,61 +7,105 @@ access the global logger object by importing "log" from this module
""" """
import logging import logging
import sys import logging.handlers
import os import os
import world
from conf import conf, confname from . import conf, world
stdout_level = conf['logging'].get('stdout') or 'INFO' __all__ = ['log']
# Set the logging directory to $CURDIR/log, creating it if it doesn't # Stores a list of active file loggers.
# already exist fileloggers = []
curdir = os.path.dirname(os.path.realpath(__file__))
logdir = os.path.join(curdir, 'log')
os.makedirs(logdir, exist_ok=True)
# TODO: perhaps make this format configurable?
_format = '%(asctime)s [%(levelname)s] %(message)s' _format = '%(asctime)s [%(levelname)s] %(message)s'
logformatter = logging.Formatter(_format) logformatter = logging.Formatter(_format)
def _get_console_log_level():
"""
Returns the configured console log level.
"""
logconf = conf.conf['logging']
return logconf.get('console', logconf.get('stdout')) or 'INFO'
# Set up logging to STDERR # Set up logging to STDERR
world.stdout_handler = logging.StreamHandler() world.console_handler = logging.StreamHandler()
world.stdout_handler.setFormatter(logformatter) world.console_handler.setFormatter(logformatter)
world.stdout_handler.setLevel(stdout_level) world.console_handler.setLevel(_get_console_log_level())
# Get the main logger object; plugins can import this variable for convenience. # Get the main logger object; plugins can import this variable for convenience.
log = logging.getLogger() log = logging.getLogger('pylinkirc')
log.addHandler(world.stdout_handler) log.addHandler(world.console_handler)
# This is confusing, but we have to set the root logger to accept all events. Only this way # This is confusing, but we have to set the root logger to accept all events. Only this way
# can other loggers filter out events on their own, instead of having everything dropped by # can other loggers filter out events on their own, instead of having everything dropped by
# the root logger. https://stackoverflow.com/questions/16624695 # the root logger. https://stackoverflow.com/questions/16624695
log.setLevel(1) log.setLevel(1)
def makeFileLogger(filename, level=None): def _make_file_logger(filename, level=None):
""" """
Initializes a file logging target with the given filename and level. Initializes a file logging target with the given filename and level.
""" """
logconf = conf.conf.get('logging', {})
logdir = logconf.get('log_dir')
if logdir is None:
logdir = os.path.join(os.getcwd(), 'log')
os.makedirs(logdir, exist_ok=True)
# Use log names specific to the current instance, to prevent multiple # Use log names specific to the current instance, to prevent multiple
# PyLink instances from overwriting each others' log files. # PyLink instances from overwriting each others' log files.
target = os.path.join(logdir, '%s-%s.log' % (confname, filename)) target = os.path.join(logdir, '%s-%s.log' % (conf.confname, filename))
filelogger = logging.FileHandler(target, mode='w') logrotconf = logconf.get('filerotation', {})
# Max amount of bytes per file, before rotation is done. Defaults to 20 MiB.
maxbytes = logrotconf.get('max_bytes', 20971520)
# Amount of backups to make (e.g. pylink-debug.log, pylink-debug.log.1, pylink-debug.log.2, ...)
# Defaults to 5.
backups = logrotconf.get('backup_count', 5)
filelogger = logging.handlers.RotatingFileHandler(target, maxBytes=maxbytes, backupCount=backups, encoding='utf-8')
filelogger.setFormatter(logformatter) filelogger.setFormatter(logformatter)
# If no log level is specified, use the same one as STDOUT. # If no log level is specified, use the same one as the console logger.
level = level or stdout_level level = level or _get_console_log_level()
filelogger.setLevel(level) filelogger.setLevel(level)
log.addHandler(filelogger) log.addHandler(filelogger)
global fileloggers
fileloggers.append(filelogger)
return filelogger return filelogger
def _stop_file_loggers():
"""
De-initializes all file loggers.
"""
global fileloggers
for handler in fileloggers.copy():
handler.close()
log.removeHandler(handler)
fileloggers.remove(handler)
# Set up file logging now, creating a file logger for each block. # Set up file logging now, creating a file logger for each block.
files = conf['logging'].get('files') files = conf.conf['logging'].get('files')
if files: if files:
for filename, config in files.items(): for filename, config in files.items():
makeFileLogger(filename, config.get('loglevel')) if isinstance(config, dict):
_make_file_logger(filename, config.get('loglevel'))
else:
log.warning('Got invalid file logging pair %r: %r; are your indentation and block '
'commenting consistent?', filename, config)
log.debug("log: Emptying _log_queue")
# Process and empty the log queue
while world._log_queue:
level, text = world._log_queue.popleft()
log.log(level, text)
log.debug("log: Emptied _log_queue")
class PyLinkChannelLogger(logging.Handler): class PyLinkChannelLogger(logging.Handler):
""" """
@ -100,11 +144,9 @@ class PyLinkChannelLogger(logging.Handler):
# 1) irc.pseudoclient must be initialized already # 1) irc.pseudoclient must be initialized already
# 2) IRC object must be finished bursting # 2) IRC object must be finished bursting
# 3) Target channel must exist # 3) Target channel must exist
# 4) Main PyLink client must be in this target channel # 4) This function hasn't been called already (prevents recursive loops).
# 5) This function hasn't been called already (prevents recursive loops).
if self.irc.pseudoclient and self.irc.connected.is_set() \ if self.irc.pseudoclient and self.irc.connected.is_set() \
and self.channel in self.irc.channels and self.irc.pseudoclient.uid in \ and self.channel in self.irc.channels and not self.called:
self.irc.channels[self.channel].users and not self.called:
self.called = True self.called = True
msg = self.format(record) msg = self.format(record)
@ -118,4 +160,3 @@ class PyLinkChannelLogger(logging.Handler):
return return
else: else:
self.called = False self.called = False

2
log/.gitignore vendored
View File

@ -1,2 +0,0 @@
*
!.gitignore

1
plugins/__init__.py Normal file
View File

@ -0,0 +1 @@
# Stub so that pylinkirc.plugins is a module

409
plugins/antispam.py Normal file
View File

@ -0,0 +1,409 @@
# antispam.py: Basic services-side spamfilters for IRC
from pylinkirc import conf, utils
from pylinkirc.log import log
mydesc = ("Provides anti-spam functionality.")
sbot = utils.register_service("antispam", default_nick="AntiSpam", desc=mydesc)
def die(irc=None):
utils.unregister_service("antispam")
_UNICODE_CHARMAP = {
'A': 'AΑА𝐀𝐴𝑨𝒜𝓐𝔄𝔸𝕬𝖠𝗔𝘈𝘼𝙰𝚨𝛢𝜜𝝖𝞐',
'B': 'ΒВв𐌁𝐁𝐵𝑩𝓑𝔅𝔹𝕭𝖡𝗕𝘉𝘽𝙱𝚩𝛣𝜝𝝗𝞑',
'C': 'CϹС𐌂𝐂𝐶𝑪𝒞𝓒𝕮𝖢𝗖𝘊𝘾𝙲',
'D': 'D𝐃𝐷𝑫𝒟𝓓𝔇𝔻𝕯𝖣𝗗𝘋𝘿𝙳',
'E': 'EΕЕ𝐄𝐸𝑬𝓔𝔈𝔼𝕰𝖤𝗘𝘌𝙀𝙴𝚬𝛦𝜠𝝚𝞔',
'F': 'FϜ𝐅𝐹𝑭𝓕𝔉𝔽𝕱𝖥𝗙𝘍𝙁𝙵𝟊',
'G': 'Ԍԍ𝐆𝐺𝑮𝒢𝓖𝔊𝔾𝕲𝖦𝗚𝘎𝙂𝙶',
'H': 'ΗНн𝐇𝐻𝑯𝓗𝕳𝖧𝗛𝘏𝙃𝙷𝚮𝛨𝜢𝝜𝞖',
'J': 'JЈ𝐉𝐽𝑱𝒥𝓙𝔍𝕁𝕵𝖩𝗝𝘑𝙅𝙹',
'K': 'KΚК𝐊𝐾𝑲𝒦𝓚𝔎𝕂𝕶𝖪𝗞𝘒𝙆𝙺𝚱𝛫𝜥𝝟𝞙',
'L': '𝐋𝐿𝑳𝓛𝔏𝕃𝕷𝖫𝗟𝘓𝙇𝙻',
'M': 'MΜϺМ𐌑𝐌𝑀𝑴𝓜𝔐𝕄𝕸𝖬𝗠𝘔𝙈𝙼𝚳𝛭𝜧𝝡𝞛',
'N': 'Ν𝐍𝑁𝑵𝒩𝓝𝔑𝕹𝖭𝗡𝘕𝙉𝙽𝚴𝛮𝜨𝝢𝞜',
'P': 'PΡРᴘᴩ𝐏𝑃𝑷𝒫𝓟𝔓𝕻𝖯𝗣𝘗𝙋𝙿𝚸𝛲𝜬𝝦𝞠',
'Q': 'Q𝐐𝑄𝑸𝒬𝓠𝔔𝕼𝖰𝗤𝘘𝙌𝚀',
'R': 'RƦʀ𝐑𝑅𝑹𝓡𝕽𝖱𝗥𝘙𝙍𝚁',
'S': 'SЅՏ𝐒𝑆𝑺𝒮𝓢𝔖𝕊𝕾𝖲𝗦𝘚𝙎𝚂',
'T': 'TΤτТт𐌕𝐓𝑇𝑻𝒯𝓣𝔗𝕋𝕿𝖳𝗧𝘛𝙏𝚃𝚻𝛕𝛵𝜏𝜯𝝉𝝩𝞃𝞣𝞽',
'U': 'UՍ𝐔𝑈𝑼𝒰𝓤𝔘𝕌𝖀𝖴𝗨𝘜𝙐𝚄',
'V': 'VѴ٧۷𝐕𝑉𝑽𝒱𝓥𝔙𝕍𝖁𝖵𝗩𝘝𝙑𝚅',
'W': 'WԜ𝐖𝑊𝑾𝒲𝓦𝔚𝕎𝖂𝖶𝗪𝘞𝙒𝚆',
'X': 'XΧХ𐌗𐌢𝐗𝑋𝑿𝒳𝓧𝔛𝕏𝖃𝖷𝗫𝘟𝙓𝚇𝚾𝛸𝜲𝝬𝞦',
'Y': 'YΥϒУҮ𝐘𝑌𝒀𝒴𝓨𝔜𝕐𝖄𝖸𝗬𝘠𝙔𝚈𝚼𝛶𝜰𝝪𝞤',
'Z': 'ZΖ𝐙𝑍𝒁𝒵𝓩𝖅𝖹𝗭𝘡𝙕𝚉𝚭𝛧𝜡𝝛𝞕',
'a': 'aɑαа𝐚𝑎𝒂𝒶𝓪𝔞𝕒𝖆𝖺𝗮𝘢𝙖𝚊𝛂𝛼𝜶𝝰𝞪',
'b': 'bƄЬ𝐛𝑏𝒃𝒷𝓫𝔟𝕓𝖇𝖻𝗯𝘣𝙗𝚋',
'c': 'cϲс𝐜𝑐𝒄𝒸𝓬𝔠𝕔𝖈𝖼𝗰𝘤𝙘𝚌',
'd': 'dԁ𝐝𝑑𝒅𝒹𝓭𝔡𝕕𝖉𝖽𝗱𝘥𝙙𝚍',
'e': 'eеҽ𝐞𝑒𝒆𝓮𝔢𝕖𝖊𝖾𝗲𝘦𝙚𝚎',
'f': 'fſϝք𝐟𝑓𝒇𝒻𝓯𝔣𝕗𝖋𝖿𝗳𝘧𝙛𝚏𝟋',
'g': 'gƍɡց𝐠𝑔𝒈𝓰𝔤𝕘𝖌𝗀𝗴𝘨𝙜𝚐',
'h': 'hһհ𝐡𝒉𝒽𝓱𝔥𝕙𝖍𝗁𝗵𝘩𝙝𝚑',
'i': 'iıɩɪιіӏ𝐢𝑖𝒊𝒾𝓲𝔦𝕚𝖎𝗂𝗶𝘪𝙞𝚒𝚤𝛊𝜄𝜾𝝸𝞲',
'j': 'jϳј𝐣𝑗𝒋𝒿𝓳𝔧𝕛𝖏𝗃𝗷𝘫𝙟𝚓',
'k': 'k𝐤𝑘𝒌𝓀𝓴𝔨𝕜𝖐𝗄𝗸𝘬𝙠𝚔',
'l': '',
'm': 'ⅿm',
'n': 'nոռ𝐧𝑛𝒏𝓃𝓷𝔫𝕟𝖓𝗇𝗻𝘯𝙣𝚗',
'o': 'οо',
'p': 'pρϱр𝐩𝑝𝒑𝓅𝓹𝔭𝕡𝖕𝗉𝗽𝘱𝙥𝚙𝛒𝛠𝜌𝜚𝝆𝝔𝞀𝞎𝞺𝟈',
'q': 'qԛգզ𝐪𝑞𝒒𝓆𝓺𝔮𝕢𝖖𝗊𝗾𝘲𝙦𝚚',
'r': 'rг𝐫𝑟𝒓𝓇𝓻𝔯𝕣𝖗𝗋𝗿𝘳𝙧𝚛',
's': 'sƽѕ𝐬𝑠𝒔𝓈𝓼𝔰𝕤𝖘𝗌𝘀𝘴𝙨𝚜',
't': 't𝐭𝑡𝒕𝓉𝓽𝔱𝕥𝖙𝗍𝘁𝘵𝙩𝚝',
'u': 'uʋυս𝐮𝑢𝒖𝓊𝓾𝔲𝕦𝖚𝗎𝘂𝘶𝙪𝚞𝛖𝜐𝝊𝞄𝞾',
'v': 'vνѵט𝐯𝑣𝒗𝓋𝓿𝔳𝕧𝖛𝗏𝘃𝘷𝙫𝚟𝛎𝜈𝝂𝝼𝞶',
'w': 'wɯѡԝա𝐰𝑤𝒘𝓌𝔀𝔴𝕨𝖜𝗐𝘄𝘸𝙬𝚠',
'x': 'x×х𝐱𝑥𝒙𝓍𝔁𝔵𝕩𝖝𝗑𝘅𝘹𝙭𝚡',
'y': 'yɣʏγуүỿ𝐲𝑦𝒚𝓎𝔂𝔶𝕪𝖞𝗒𝘆𝘺𝙮𝚢𝛄𝛾𝜸𝝲𝞬',
'z': 'z𝐳𝑧𝒛𝓏𝔃𝔷𝕫𝖟𝗓𝘇𝘻𝙯𝚣',
'/': '',
'\\': '',
' ': '\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\xa0\u202f\u205f',
'.': '',
'-': '˗╴﹣-−⎼',
'!': '﹗!ǃⵑ︕',
':': ':˸։፡᛬⁚∶⠆︓﹕',
'#': '#﹟'
}
def _prep_maketrans(data):
from_s = ''
to_s = ''
for target, chars in data.items():
from_s += chars
to_s += target * len(chars)
return str.maketrans(from_s, to_s)
UNICODE_CHARMAP = _prep_maketrans(_UNICODE_CHARMAP)
PUNISH_OPTIONS = ['kill', 'ban', 'quiet', 'kick', 'block']
EXEMPT_OPTIONS = ['voice', 'halfop', 'op']
DEFAULT_EXEMPT_OPTION = 'halfop'
def _punish(irc, target, channel, punishment, reason):
"""Punishes the target user. This function returns True if the user was successfully punished."""
if target not in irc.users:
log.warning("(%s) antispam: got target %r that isn't a user?", irc.name, target)
return False
elif irc.is_oper(target):
log.debug("(%s) antispam: refusing to punish oper %s/%s", irc.name, target, irc.get_friendly_name(target))
return False
target_nick = irc.get_friendly_name(target)
if channel:
c = irc.channels[channel]
exempt_level = irc.get_service_option('antispam', 'exempt_level', DEFAULT_EXEMPT_OPTION).lower()
if exempt_level not in EXEMPT_OPTIONS:
log.error('(%s) Antispam exempt %r is not a valid setting, '
'falling back to defaults; accepted settings include: %s',
irc.name, exempt_level, ', '.join(EXEMPT_OPTIONS))
exempt_level = DEFAULT_EXEMPT_OPTION
if exempt_level == 'voice' and c.is_voice_plus(target):
log.debug("(%s) antispam: refusing to punish voiced and above %s/%s", irc.name, target, target_nick)
return False
elif exempt_level == 'halfop' and c.is_halfop_plus(target):
log.debug("(%s) antispam: refusing to punish halfop and above %s/%s", irc.name, target, target_nick)
return False
elif exempt_level == 'op' and c.is_op_plus(target):
log.debug("(%s) antispam: refusing to punish op and above %s/%s", irc.name, target, target_nick)
return False
my_uid = sbot.uids.get(irc.name)
# XXX workaround for single-bot protocols like Clientbot
if irc.pseudoclient and not irc.has_cap('can-spawn-clients'):
my_uid = irc.pseudoclient.uid
bans = set()
log.debug('(%s) antispam: got %r as punishment for %s/%s', irc.name, punishment,
target, irc.get_friendly_name(target))
def _ban():
bans.add(irc.make_channel_ban(target))
def _quiet():
bans.add(irc.make_channel_ban(target, ban_type='quiet'))
def _kick():
irc.kick(my_uid, channel, target, reason)
irc.call_hooks([my_uid, 'ANTISPAM_KICK', {'channel': channel, 'text': reason, 'target': target,
'parse_as': 'KICK'}])
def _kill():
if target not in irc.users:
log.debug('(%s) antispam: not killing %s/%s; they already left', irc.name, target,
irc.get_friendly_name(target))
return
userdata = irc.users[target]
irc.kill(my_uid, target, reason)
irc.call_hooks([my_uid, 'ANTISPAM_KILL', {'target': target, 'text': reason,
'userdata': userdata, 'parse_as': 'KILL'}])
kill = False
successful_punishments = 0
for action in set(punishment.split('+')):
if action not in PUNISH_OPTIONS:
log.error('(%s) Antispam punishment %r is not a valid setting; '
'accepted settings include: %s OR any combination of '
'these joined together with a "+".',
irc.name, punishment, ', '.join(PUNISH_OPTIONS))
return
elif action == 'block':
# We only need to increment this for this function to return True
successful_punishments += 1
elif action == 'kill':
kill = True # Delay kills so that the user data doesn't disappear.
# XXX factorize these blocks
elif action == 'kick' and channel:
try:
_kick()
except NotImplementedError:
log.warning("(%s) antispam: Kicks are not supported on this network, skipping; "
"target was %s/%s", irc.name, target_nick, channel)
else:
successful_punishments += 1
elif action == 'ban' and channel:
try:
_ban()
except (ValueError, NotImplementedError):
log.warning("(%s) antispam: Bans are not supported on this network, skipping; "
"target was %s/%s", irc.name, target_nick, channel)
else:
successful_punishments += 1
elif action == 'quiet' and channel:
try:
_quiet()
except (ValueError, NotImplementedError):
log.warning("(%s) antispam: Quiet is not supported on this network, skipping; "
"target was %s/%s", irc.name, target_nick, channel)
else:
successful_punishments += 1
if bans: # Set all bans at once to prevent spam
irc.mode(my_uid, channel, bans)
irc.call_hooks([my_uid, 'ANTISPAM_BAN',
{'target': channel, 'modes': bans, 'parse_as': 'MODE'}])
if kill:
try:
_kill()
except NotImplementedError:
log.warning("(%s) antispam: Kills are not supported on this network, skipping; "
"target was %s/%s", irc.name, target_nick, channel)
else:
successful_punishments += 1
if not successful_punishments:
log.warning('(%s) antispam: Failed to punish %s with %r, target was %s', irc.name,
target_nick, punishment, channel or 'a PM')
return bool(successful_punishments)
MASSHIGHLIGHT_DEFAULTS = {
'min_length': 50,
'min_nicks': 5,
'reason': "Mass highlight spam is prohibited",
'punishment': 'kick+ban',
'enabled': False
}
def handle_masshighlight(irc, source, command, args):
"""Handles mass highlight attacks."""
channel = args['target']
text = args['text']
mhl_settings = irc.get_service_option('antispam', 'masshighlight',
MASSHIGHLIGHT_DEFAULTS)
if not mhl_settings.get('enabled', False):
return
my_uid = sbot.uids.get(irc.name)
# XXX workaround for single-bot protocols like Clientbot
if irc.pseudoclient and not irc.has_cap('can-spawn-clients'):
my_uid = irc.pseudoclient.uid
if (not irc.connected.is_set()) or (not my_uid):
# Break if the network isn't ready.
log.debug("(%s) antispam.masshighlight: skipping processing; network isn't ready", irc.name)
return
elif not irc.is_channel(channel):
# Not a channel - mass highlight blocking only makes sense within channels
log.debug("(%s) antispam.masshighlight: skipping processing; %r is not a channel", irc.name, channel)
return
elif irc.is_internal_client(source):
# Ignore messages from our own clients.
log.debug("(%s) antispam.masshighlight: skipping processing message from internal client %s", irc.name, source)
return
elif source not in irc.users:
log.debug("(%s) antispam.masshighlight: ignoring message from non-user %s", irc.name, source)
return
elif channel not in irc.channels or my_uid not in irc.channels[channel].users:
# We're not monitoring this channel.
log.debug("(%s) antispam.masshighlight: skipping processing message from channel %r we're not in", irc.name, channel)
return
elif len(text) < mhl_settings.get('min_length', MASSHIGHLIGHT_DEFAULTS['min_length']):
log.debug("(%s) antispam.masshighlight: skipping processing message %r; it's too short", irc.name, text)
return
if irc.get_service_option('antispam', 'strip_formatting', True):
text = utils.strip_irc_formatting(text)
# Strip :, from potential nicks
words = [word.rstrip(':,') for word in text.split()]
userlist = [irc.users[uid].nick for uid in irc.channels[channel].users.copy()]
min_nicks = mhl_settings.get('min_nicks', MASSHIGHLIGHT_DEFAULTS['min_nicks'])
# Don't allow repeating the same nick to trigger punishment
nicks_caught = set()
punished = False
for word in words:
if word in userlist:
nicks_caught.add(word)
if len(nicks_caught) >= min_nicks:
# Get the punishment and reason.
punishment = mhl_settings.get('punishment', MASSHIGHLIGHT_DEFAULTS['punishment']).lower()
reason = mhl_settings.get('reason', MASSHIGHLIGHT_DEFAULTS['reason'])
log.info("(%s) antispam: punishing %s => %s for mass highlight spam",
irc.name,
irc.get_friendly_name(source),
channel)
punished = _punish(irc, source, channel, punishment, reason)
break
log.debug('(%s) antispam.masshighlight: got %s/%s nicks on message to %r', irc.name,
len(nicks_caught), min_nicks, channel)
return not punished # Filter this message from relay, etc. if it triggered protection
utils.add_hook(handle_masshighlight, 'PRIVMSG', priority=1000)
utils.add_hook(handle_masshighlight, 'NOTICE', priority=1000)
TEXTFILTER_DEFAULTS = {
'reason': "Spam is prohibited",
'punishment': 'kick+ban+block',
'watch_pms': False,
'enabled': False,
'munge_unicode': True,
}
def handle_textfilter(irc, source, command, args):
"""Antispam text filter handler."""
target = args['target']
text = args['text']
txf_settings = irc.get_service_option('antispam', 'textfilter',
TEXTFILTER_DEFAULTS)
if not txf_settings.get('enabled', False):
return
my_uid = sbot.uids.get(irc.name)
# XXX workaround for single-bot protocols like Clientbot
if irc.pseudoclient and not irc.has_cap('can-spawn-clients'):
my_uid = irc.pseudoclient.uid
if (not irc.connected.is_set()) or (not my_uid):
# Break if the network isn't ready.
log.debug("(%s) antispam.textfilters: skipping processing; network isn't ready", irc.name)
return
elif irc.is_internal_client(source):
# Ignore messages from our own clients.
log.debug("(%s) antispam.textfilters: skipping processing message from internal client %s", irc.name, source)
return
elif source not in irc.users:
log.debug("(%s) antispam.textfilters: ignoring message from non-user %s", irc.name, source)
return
if irc.is_channel(target):
channel_or_none = target
if target not in irc.channels or my_uid not in irc.channels[target].users:
# We're not monitoring this channel.
log.debug("(%s) antispam.textfilters: skipping processing message from channel %r we're not in", irc.name, target)
return
else:
channel_or_none = None
watch_pms = txf_settings.get('watch_pms', TEXTFILTER_DEFAULTS['watch_pms'])
if watch_pms == 'services':
if not irc.get_service_bot(target):
log.debug("(%s) antispam.textfilters: skipping processing; %r is not a service bot (watch_pms='services')", irc.name, target)
return
elif watch_pms == 'all':
log.debug("(%s) antispam.textfilters: checking all PMs (watch_pms='all')", irc.name)
pass
else:
# Not a channel.
log.debug("(%s) antispam.textfilters: skipping processing; %r is not a channel and watch_pms is disabled", irc.name, target)
return
# Merge together global and local textfilter lists.
txf_globs = set(conf.conf.get('antispam', {}).get('textfilter_globs', [])) | \
set(irc.serverdata.get('antispam_textfilter_globs', []))
punishment = txf_settings.get('punishment', TEXTFILTER_DEFAULTS['punishment']).lower()
reason = txf_settings.get('reason', TEXTFILTER_DEFAULTS['reason'])
if irc.get_service_option('antispam', 'strip_formatting', True):
text = utils.strip_irc_formatting(text)
if txf_settings.get('munge_unicode', TEXTFILTER_DEFAULTS['munge_unicode']):
text = str.translate(text, UNICODE_CHARMAP)
punished = False
for filterglob in txf_globs:
if utils.match_text(filterglob, text):
log.info("(%s) antispam: punishing %s => %s for text filter %r",
irc.name,
irc.get_friendly_name(source),
irc.get_friendly_name(target),
filterglob)
punished = _punish(irc, source, channel_or_none, punishment, reason)
break
return not punished # Filter this message from relay, etc. if it triggered protection
utils.add_hook(handle_textfilter, 'PRIVMSG', priority=999)
utils.add_hook(handle_textfilter, 'NOTICE', priority=999)
PARTQUIT_DEFAULTS = {
'watch_quits': True,
'watch_parts': True,
'part_filter_message': "Reason filtered",
'quit_filter_message': "Reason filtered",
}
def handle_partquit(irc, source, command, args):
"""Antispam part/quit message filter."""
text = args.get('text')
pq_settings = irc.get_service_option('antispam', 'partquit',
PARTQUIT_DEFAULTS)
if not text:
return # No text to match against
elif command == 'QUIT' and not pq_settings.get('watch_quits', True):
return # Not enabled
elif command == 'PART' and not pq_settings.get('watch_parts', True):
return
# Merge together global and local partquit filter lists.
pq_globs = set(conf.conf.get('antispam', {}).get('partquit_globs', [])) | \
set(irc.serverdata.get('antispam_partquit_globs', []))
if not pq_globs:
return
for filterglob in pq_globs:
if utils.match_text(filterglob, text):
# For parts, also log the affected channels
if command == 'PART':
filtered_message = pq_settings.get('part_filter_message', PARTQUIT_DEFAULTS['part_filter_message'])
log.info('(%s) antispam: filtered part message from %s on %s due to part/quit filter glob %s',
irc.name, irc.get_hostmask(source), ','.join(args['channels']), filterglob)
else:
filtered_message = pq_settings.get('quit_filter_message', PARTQUIT_DEFAULTS['quit_filter_message'])
log.info('(%s) antispam: filtered quit message from %s due to part/quit filter glob %s',
irc.name, args['userdata'].nick, filterglob)
args['text'] = filtered_message
break
utils.add_hook(handle_partquit, 'PART', priority=999)
utils.add_hook(handle_partquit, 'QUIT', priority=999)

382
plugins/automode.py Normal file
View File

@ -0,0 +1,382 @@
"""
automode.py - Provide simple channel ACL management by giving prefix modes to users matching
hostmasks or exttargets.
"""
import collections
import string
from pylinkirc import conf, structures, utils, world
from pylinkirc.coremods import permissions
from pylinkirc.log import log
mydesc = ("The \x02Automode\x02 plugin provides simple channel ACL management by giving prefix modes "
"to users matching hostmasks or exttargets.")
# Register ourselves as a service.
modebot = utils.register_service("automode", default_nick="Automode", desc=mydesc)
reply = modebot.reply
error = modebot.error
# Databasing variables.
dbname = conf.get_database_name('automode')
datastore = structures.JSONDataStore('automode', dbname, default_db=collections.defaultdict(dict))
db = datastore.store
# The default set of Automode permissions.
default_permissions = {"$ircop": ['automode.manage.relay_owned', 'automode.sync.relay_owned',
'automode.list']}
def _join_db_channels(irc):
"""
Joins the Automode service client to channels on the current network in its DB.
"""
if not irc.connected.is_set():
log.debug('(%s) _join_db_channels: aborting, network not ready yet', irc.name)
return
for entry in db:
netname, channel = entry.split('#', 1)
channel = '#' + channel
if netname == irc.name:
modebot.add_persistent_channel(irc, 'automode', channel)
def main(irc=None):
"""Main function, called during plugin loading."""
# Load the automode database.
datastore.load()
# Register our permissions.
permissions.add_default_permissions(default_permissions)
if irc: # This was a reload.
for ircobj in world.networkobjects.values():
_join_db_channels(ircobj)
def die(irc=None):
"""Saves the Automode database and quit."""
datastore.die()
permissions.remove_default_permissions(default_permissions)
utils.unregister_service('automode')
def _check_automode_access(irc, uid, channel, command):
"""Checks the caller's access to Automode."""
# Automode defines the following permissions, where <command> is either "manage", "list",
# "sync", "clear", "remotemanage", "remotelist", "remotesync", "remoteclear":
# - automode.<command> OR automode.<command>.*: ability to <command> automode on all channels.
# - automode.<command>.relay_owned: ability to <command> automode on channels owned via Relay.
# If Relay isn't loaded, this permission check FAILS.
# - automode.<command>.#channel: ability to <command> automode on the given channel.
# - automode.savedb: ability to save the automode DB.
log.debug('(%s) Automode: checking access for %s/%s for %s capability on %s', irc.name, uid,
irc.get_hostmask(uid), command, channel)
baseperm = 'automode.%s' % command
try:
# First, check the catch all and channel permissions.
perms = [baseperm, baseperm+'.*', '%s.%s' % (baseperm, channel)]
return permissions.check_permissions(irc, uid, perms)
except utils.NotAuthorizedError:
if not command.startswith('remote'):
# Relay-based ACL checking only works with local calls.
log.debug('(%s) Automode: falling back to automode.%s.relay_owned', irc.name, command)
permissions.check_permissions(irc, uid, [baseperm+'.relay_owned'], also_show=perms)
relay = world.plugins.get('relay')
if relay is None:
raise utils.NotAuthorizedError("You are not authorized to use Automode when Relay is "
"disabled. You are missing one of the following "
"permissions: %s or %s.%s" % (baseperm, baseperm, channel))
elif (irc.name, channel) not in relay.db:
raise utils.NotAuthorizedError("The network you are on does not own the relay channel %s." % channel)
return True
raise
def match(irc, channel, uids=None):
"""
Set modes on matching users. If uids is not given, check all users in the channel and give
them modes as needed.
"""
if isinstance(channel, int) or str(channel).startswith(tuple(string.digits)):
channel = '#' + str(channel) # Mangle channels on networks where they're stored as an ID
dbentry = db.get(irc.name+channel)
if not irc.has_cap('has-irc-modes'):
log.debug('(%s) automode: skipping match() because IRC modes are not supported on this protocol', irc.name)
return
elif dbentry is None:
return
modebot_uid = modebot.uids.get(irc.name)
# Check every mask defined in the channel ACL.
outgoing_modes = []
# If UIDs are given, match those. Otherwise, match all users in the given channel.
uids = uids or irc.channels[channel].users
for mask, modes in dbentry.items():
for uid in uids:
if irc.match_host(mask, uid):
# User matched a mask. Filter the mode list given to only those that are valid
# prefix mode characters.
outgoing_modes += [('+'+mode, uid) for mode in modes if mode in irc.prefixmodes]
log.debug("(%s) automode: Filtered mode list of %s to %s (protocol:%s)",
irc.name, modes, outgoing_modes, irc.protoname)
if outgoing_modes:
# If the Automode bot is missing, send the mode through the PyLink server.
if modebot_uid not in irc.users:
modebot_uid = irc.sid
log.debug("(%s) automode: sending modes from modebot_uid %s",
irc.name, modebot_uid)
irc.mode(modebot_uid, channel, outgoing_modes)
# Create a hook payload to support plugins like relay.
irc.call_hooks([modebot_uid, 'AUTOMODE_MODE',
{'target': channel, 'modes': outgoing_modes, 'parse_as': 'MODE'}])
def handle_endburst(irc, source, command, args):
"""ENDBURST hook handler - used to join the Automode service to channels where it has entries."""
if source == irc.uplink:
_join_db_channels(irc)
utils.add_hook(handle_endburst, 'ENDBURST')
def handle_join(irc, source, command, args):
"""
Automode JOIN listener. This sets modes accordingly if the person joining matches a mask in the
ACL.
"""
channel = irc.to_lower(args['channel'])
match(irc, channel, args['users'])
utils.add_hook(handle_join, 'JOIN')
utils.add_hook(handle_join, 'PYLINK_RELAY_JOIN') # Handle the relay version of join
utils.add_hook(handle_join, 'PYLINK_SERVICE_JOIN') # And the version for service bots
def handle_services_login(irc, source, command, args):
"""
Handles services login change, to trigger Automode matching.
"""
for channel in irc.users[source].channels:
# Look at all the users' channels for any possible changes.
match(irc, channel, [source])
utils.add_hook(handle_services_login, 'CLIENT_SERVICES_LOGIN')
utils.add_hook(handle_services_login, 'PYLINK_RELAY_SERVICES_LOGIN')
def _get_channel_pair(irc, source, chanpair, perm=None):
"""
Fetches the network and channel given a channel pair, also optionally checking the caller's permissions.
"""
log.debug('(%s) Looking up chanpair %s', irc.name, chanpair)
if '#' not in chanpair and chanpair.startswith(tuple(string.digits)):
chanpair = '#' + chanpair # Mangle channels on networks where they're stored by ID
try:
network, channel = chanpair.split('#', 1)
except ValueError:
raise ValueError("Invalid channel pair %r" % chanpair)
channel = '#' + channel
channel = irc.to_lower(channel)
if network:
ircobj = world.networkobjects.get(network)
else:
ircobj = irc
if not ircobj:
raise ValueError("Unknown network %s" % network)
if perm is not None:
# Only check for permissions if we're told to and the irc object exists.
if ircobj.name != irc.name:
perm = 'remote' + perm
_check_automode_access(irc, source, channel, perm)
return (ircobj, channel)
def setacc(irc, source, args):
"""<channel/chanpair> <mask> <mode list>
Assigns the given prefix mode characters to the given mask for the channel given. Extended targets are supported for masks - use this to your advantage!
Channel pairs are also supported (for operations on remote channels), using the form "network#channel".
Examples:
\x02SETACC #channel *!*@localhost ohv
\x02SETACC #channel $account v
\x02SETACC othernet#channel $ircop:Network?Administrator qo
\x02SETACC #staffchan $channel:#mainchan:op o
"""
if not irc.has_cap('has-irc-modes'):
error(irc, "IRC style modes are not supported on this protocol.")
return
try:
chanpair, mask, modes = args
except ValueError:
error(irc, "Invalid arguments given. Needs 3: channel, mask, mode list.")
return
else:
ircobj, channel = _get_channel_pair(irc, source, chanpair, perm='manage')
# Database entries for any network+channel pair are automatically created using
# defaultdict. Note: string keys are used here instead of tuples so they can be
# exported easily as JSON.
dbentry = db[ircobj.name+channel]
modes = modes.lstrip('+') # remove extraneous leading +'s
dbentry[mask] = modes
log.info('(%s) %s set modes +%s for %s on %s', ircobj.name, irc.get_hostmask(source), modes, mask, channel)
reply(irc, "Done. \x02%s\x02 now has modes \x02+%s\x02 in \x02%s\x02." % (mask, modes, channel))
# Join the Automode bot to the channel persistently.
modebot.add_persistent_channel(ircobj, 'automode', channel)
modebot.add_cmd(setacc, aliases=('setaccess', 'set'), featured=True)
def delacc(irc, source, args):
"""<channel/chanpair> <mask or range string>
Removes the Automode entry for the given mask or range string, if they exist.
Range strings are indices (entry numbers) or ranges of them joined together with commas: e.g.
"1", "2-10", "1,3,5-8". Entry numbers are shown by LISTACC.
"""
try:
chanpair, mask = args
except ValueError:
error(irc, "Invalid arguments given. Needs 2: channel, mask")
return
else:
ircobj, channel = _get_channel_pair(irc, source, chanpair, perm='manage')
dbentry = db.get(ircobj.name+channel)
if dbentry is None:
error(irc, "No Automode access entries exist for \x02%s\x02." % channel)
return
if mask in dbentry:
del dbentry[mask]
log.info('(%s) %s removed modes for %s on %s', ircobj.name, irc.get_hostmask(source), mask, channel)
reply(irc, "Done. Removed the Automode access entry for \x02%s\x02 in \x02%s\x02." % (mask, channel))
else:
# Treat the mask as a range string.
try:
new_keys = utils.remove_range(mask, sorted(dbentry.keys()))
except ValueError:
error(irc, "No Automode access entry for \x02%s\x02 exists in \x02%s\x02." % (mask, channel))
return
# XXX: Automode entries are actually unordered: what we're actually doing is sorting the keys
# by name into a list, running remove_range on that, and removing the difference.
removed = []
source_host = irc.get_hostmask(source)
for mask_entry in dbentry.copy():
if mask_entry not in new_keys:
del dbentry[mask_entry]
log.info('(%s) %s removed modes for %s on %s', ircobj.name, source_host, mask_entry, channel)
removed.append(mask_entry)
reply(irc, 'Done. Removed \x02%d\x02 entries on \x02%s\x02: %s' % (len(removed), channel, ', '.join(removed)))
# Remove channels if no more entries are left.
if not dbentry:
log.debug("Automode: purging empty channel pair %s/%s", ircobj.name, channel)
del db[ircobj.name+channel]
modebot.remove_persistent_channel(ircobj, 'automode', channel)
modebot.add_cmd(delacc, aliases=('delaccess', 'del'), featured=True)
def listacc(irc, source, args):
"""<channel/chanpair>
Lists all Automode entries for the given channel."""
try:
chanpair = args[0]
except IndexError:
error(irc, "Invalid arguments given. Needs 1: channel.")
return
else:
ircobj, channel = _get_channel_pair(irc, source, chanpair, perm='list')
dbentry = db.get(ircobj.name+channel)
if not dbentry:
error(irc, "No Automode access entries exist for \x02%s\x02." % channel)
return
else:
# Iterate over all entries and print them. Do this in private to prevent channel
# floods.
reply(irc, "Showing Automode entries for \x02%s\x02:" % channel, private=True)
for entrynum, entry in enumerate(sorted(dbentry.items()), start=1):
mask, modes = entry
reply(irc, "[%s] \x02%s\x02 has modes +\x02%s\x02" % (entrynum, mask, modes), private=True)
reply(irc, "End of Automode entries list.", private=True)
modebot.add_cmd(listacc, featured=True, aliases=('listaccess',))
def save(irc, source, args):
"""takes no arguments.
Saves the Automode database to disk."""
permissions.check_permissions(irc, source, ['automode.savedb'])
datastore.save()
reply(irc, 'Done.')
modebot.add_cmd(save)
def syncacc(irc, source, args):
"""<channel/chanpair>
Syncs Automode access lists to the channel.
"""
try:
chanpair = args[0]
except IndexError:
error(irc, "Invalid arguments given. Needs 1: channel.")
return
else:
ircobj, channel = _get_channel_pair(irc, source, chanpair, perm='sync')
log.info('(%s) %s synced modes on %s', ircobj.name, irc.get_hostmask(source), channel)
match(ircobj, channel)
reply(irc, 'Done.')
modebot.add_cmd(syncacc, featured=True, aliases=('sync', 'syncaccess'))
def clearacc(irc, source, args):
"""<channel>
Removes all Automode entries for the given channel.
"""
try:
chanpair = args[0]
except IndexError:
error(irc, "Invalid arguments given. Needs 1: channel.")
return
else:
ircobj, channel = _get_channel_pair(irc, source, chanpair, perm='clear')
if db.get(ircobj.name+channel):
del db[ircobj.name+channel]
log.info('(%s) %s cleared modes on %s', ircobj.name, irc.get_hostmask(source), channel)
reply(irc, "Done. Removed all Automode access entries for \x02%s\x02." % channel)
modebot.remove_persistent_channel(ircobj, 'automode', channel)
else:
error(irc, "No Automode access entries exist for \x02%s\x02." % channel)
modebot.add_cmd(clearacc, aliases=('clearaccess', 'clear'), featured=True)

View File

@ -2,105 +2,134 @@
bots.py: Spawn virtual users/bots on a PyLink server and make them interact bots.py: Spawn virtual users/bots on a PyLink server and make them interact
with things. with things.
""" """
from pylinkirc import utils
from pylinkirc.coremods import permissions
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import utils
from log import log
@utils.add_cmd @utils.add_cmd
def spawnclient(irc, source, args): def spawnclient(irc, source, args):
"""<nick> <ident> <host> """<nick> <ident> <host>
Admin-only. Spawns the specified PseudoClient on the PyLink server. Spawns the specified client on the PyLink server.
Note: this doesn't check the validity of any fields you give it!""" Note: this doesn't check the validity of any fields you give it!"""
irc.checkAuthenticated(source, allowOper=False)
if not irc.has_cap('can-spawn-clients'):
irc.error("This network does not support client spawning.")
return
permissions.check_permissions(irc, source, ['bots.spawnclient'])
try: try:
nick, ident, host = args[:3] nick, ident, host = args[:3]
except ValueError: except ValueError:
irc.reply("Error: Not enough arguments. Needs 3: nick, user, host.") irc.error("Not enough arguments. Needs 3: nick, user, host.")
return return
irc.proto.spawnClient(nick, ident, host, manipulatable=True) irc.spawn_client(nick, ident, host, manipulatable=True)
irc.reply("Done.")
@utils.add_cmd @utils.add_cmd
def quit(irc, source, args): def quit(irc, source, args):
"""<target> [<reason>] """<target> [<reason>]
Admin-only. Quits the PyLink client with nick <target>, if one exists.""" Quits the PyLink client with nick <target>, if one exists."""
irc.checkAuthenticated(source, allowOper=False) permissions.check_permissions(irc, source, ['bots.quit'])
try: try:
nick = args[0] nick = args[0]
except IndexError: except IndexError:
irc.reply("Error: Not enough arguments. Needs 1-2: nick, reason (optional).") irc.error("Not enough arguments. Needs 1-2: nick, reason (optional).")
return
if irc.pseudoclient.uid == irc.nickToUid(nick):
irc.reply("Error: Cannot quit the main PyLink PseudoClient!")
return return
u = irc.nickToUid(nick) u = irc.nick_to_uid(nick, filterfunc=irc.is_internal_client)
if u is None:
quitmsg = ' '.join(args[1:]) or 'Client Quit' irc.error("Unknown user %r" % nick)
if not irc.isManipulatableClient(u):
irc.reply("Error: Cannot force quit a protected PyLink services client.")
return return
irc.proto.quit(u, quitmsg) if irc.pseudoclient.uid == u:
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_QUIT', {'text': quitmsg, 'parse_as': 'QUIT'}]) irc.error("Cannot quit the main PyLink client!")
return
quitmsg = ' '.join(args[1:]) or 'Client Quit'
if not irc.is_manipulatable_client(u):
irc.error("Cannot force quit a protected PyLink services client.")
return
irc.quit(u, quitmsg)
irc.reply("Done.")
irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_QUIT', {'text': quitmsg, 'parse_as': 'QUIT'}])
def joinclient(irc, source, args): def joinclient(irc, source, args):
"""[<target>] <channel1>,[<channel2>], etc. """[<target>] <channel1>[,<channel2>,<channel3>,...]
Admin-only. Joins <target>, the nick of a PyLink client, to a comma-separated list of channels. If <target> is not given, it defaults to the main PyLink client.""" Joins <target>, the nick of a PyLink client, to a comma-separated list of channels.
irc.checkAuthenticated(source, allowOper=False) If <target> is not given, it defaults to the main PyLink client.
For the channel arguments, prefixes can also be specified to join the given client with
(e.g. @#channel will join the client with op, while ~@#channel will join it with +qo.
"""
permissions.check_permissions(irc, source, ['bots.join', 'bots.joinclient'])
try: try:
# Check if the first argument is an existing PyLink client. If it is not, # Check if the first argument is an existing PyLink client. If it is not,
# then assume that the first argument was actually the channels being joined. # then assume that the first argument was actually the channels being joined.
u = irc.nickToUid(args[0]) u = irc.nick_to_uid(args[0], filterfunc=irc.is_internal_client)
if not irc.isInternalClient(u): # First argument isn't one of our clients if u is None: # First argument isn't one of our clients
raise IndexError raise IndexError
clist = args[1] clist = args[1]
except IndexError: # No nick was given; shift arguments one to the left. except IndexError: # No valid nick was given; shift arguments one to the left.
u = irc.pseudoclient.uid u = irc.pseudoclient.uid
try: try:
clist = args[0] clist = args[0]
except IndexError: except IndexError:
irc.reply("Error: Not enough arguments. Needs 1-2: nick (optional), comma separated list of channels.") irc.error("Not enough arguments. Needs 1-2: nick (optional), comma separated list of channels.")
return return
clist = clist.split(',') clist = clist.split(',')
if not clist: if not clist:
irc.reply("Error: No valid channels given.") irc.error("No valid channels given.")
return return
if not irc.isManipulatableClient(u): if not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)):
irc.reply("Error: Cannot force join a protected PyLink services client.") irc.error("Cannot force join a protected PyLink services client.")
return return
prefix_to_mode = {v: k for k, v in irc.prefixmodes.items()}
for channel in clist: for channel in clist:
if not utils.isChannel(channel): real_channel = channel.lstrip(''.join(prefix_to_mode))
irc.reply("Error: Invalid channel name %r." % channel) # XXX we need a better way to do this, but only the other option I can think of is regex...
return prefixes = channel[:len(channel)-len(real_channel)]
irc.proto.join(u, channel) joinmodes = ''.join(prefix_to_mode[prefix] for prefix in prefixes)
# Call a join hook manually so other plugins like relay can understand it. if not irc.is_channel(real_channel):
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_JOIN', {'channel': channel, 'users': [u], irc.error("Invalid channel name %r." % real_channel)
'modes': irc.channels[channel].modes, return
'parse_as': 'JOIN'}])
# join() doesn't support prefixes.
if prefixes:
irc.sjoin(irc.sid, real_channel, [(joinmodes, u)])
else:
irc.join(u, real_channel)
try:
modes = irc.channels[real_channel].modes
except KeyError:
modes = []
# Signal the join to other plugins
irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_JOIN', {'channel': real_channel, 'users': [u],
'modes': modes, 'parse_as': 'JOIN'}])
irc.reply("Done.")
utils.add_cmd(joinclient, name='join') utils.add_cmd(joinclient, name='join')
@utils.add_cmd @utils.add_cmd
def nick(irc, source, args): def nick(irc, source, args):
"""[<target>] <newnick> """[<target>] <newnick>
Admin-only. Changes the nick of <target>, a PyLink client, to <newnick>. If <target> is not given, it defaults to the main PyLink client.""" Changes the nick of <target>, a PyLink client, to <newnick>. If <target> is not given, it defaults to the main PyLink client."""
irc.checkAuthenticated(source, allowOper=False)
permissions.check_permissions(irc, source, ['bots.nick'])
try: try:
nick = args[0] nick = args[0]
@ -110,31 +139,32 @@ def nick(irc, source, args):
nick = irc.pseudoclient.nick nick = irc.pseudoclient.nick
newnick = args[0] newnick = args[0]
except IndexError: except IndexError:
irc.reply("Error: Not enough arguments. Needs 1-2: nick (optional), newnick.") irc.error("Not enough arguments. Needs 1-2: nick (optional), newnick.")
return return
u = irc.nickToUid(nick) u = irc.nick_to_uid(nick, filterfunc=irc.is_internal_client)
if newnick in ('0', u): # Allow /nick 0 to work if newnick in ('0', u): # Allow /nick 0 to work
newnick = u newnick = u
elif not utils.isNick(newnick): elif not irc.is_nick(newnick):
irc.reply('Error: Invalid nickname %r.' % newnick) irc.error('Invalid nickname %r.' % newnick)
return return
elif not irc.isManipulatableClient(u): elif not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)):
irc.reply("Error: Cannot force nick changes for a protected PyLink services client.") irc.error("Cannot force nick changes for a protected PyLink services client.")
return return
irc.proto.nick(u, newnick) irc.nick(u, newnick)
# Ditto above: manually send a NICK change hook payload to other plugins. irc.reply("Done.")
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_NICK', {'newnick': newnick, 'oldnick': nick, 'parse_as': 'NICK'}]) # Signal the nick change to other plugins
irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_NICK', {'newnick': newnick, 'oldnick': nick, 'parse_as': 'NICK'}])
@utils.add_cmd @utils.add_cmd
def part(irc, source, args): def part(irc, source, args):
"""[<target>] <channel1>,[<channel2>],... [<reason>] """[<target>] <channel1>,[<channel2>],... [<reason>]
Admin-only. Parts <target>, the nick of a PyLink client, from a comma-separated list of channels. If <target> is not given, it defaults to the main PyLink client.""" Parts <target>, the nick of a PyLink client, from a comma-separated list of channels. If <target> is not given, it defaults to the main PyLink client."""
irc.checkAuthenticated(source, allowOper=False) permissions.check_permissions(irc, source, ['bots.part'])
try: try:
nick = args[0] nick = args[0]
@ -144,8 +174,8 @@ def part(irc, source, args):
# First, check if the first argument is an existing PyLink client. If it is not, # First, check if the first argument is an existing PyLink client. If it is not,
# then assume that the first argument was actually the channels being parted. # then assume that the first argument was actually the channels being parted.
u = irc.nickToUid(nick) u = irc.nick_to_uid(nick, filterfunc=irc.is_internal_client)
if not irc.isInternalClient(u): # First argument isn't one of our clients if u is None: # First argument isn't one of our clients
raise IndexError raise IndexError
except IndexError: # No nick was given; shift arguments one to the left. except IndexError: # No nick was given; shift arguments one to the left.
@ -154,33 +184,33 @@ def part(irc, source, args):
try: try:
clist = args[0] clist = args[0]
except IndexError: except IndexError:
irc.reply("Error: Not enough arguments. Needs 1-2: nick (optional), comma separated list of channels.") irc.error("Not enough arguments. Needs 1-2: nick (optional), comma separated list of channels.")
return return
reason = ' '.join(args[1:]) reason = ' '.join(args[1:])
clist = clist.split(',') clist = clist.split(',')
if not clist: if not clist:
irc.reply("Error: No valid channels given.") irc.error("No valid channels given.")
return return
if not irc.isManipulatableClient(u): if not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)):
irc.reply("Error: Cannot force part a protected PyLink services client.") irc.error("Cannot force part a protected PyLink services client.")
return return
for channel in clist: for channel in clist:
if not utils.isChannel(channel): if not irc.is_channel(channel):
irc.reply("Error: Invalid channel name %r." % channel) irc.error("Invalid channel name %r." % channel)
return return
irc.proto.part(u, channel, reason) irc.part(u, channel, reason)
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_PART', {'channels': clist, 'text': reason, 'parse_as': 'PART'}]) irc.reply("Done.")
irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_PART', {'channels': clist, 'text': reason, 'parse_as': 'PART'}])
@utils.add_cmd
def msg(irc, source, args): def msg(irc, source, args):
"""[<source>] <target> <text> """[<source>] <target> <text>
Admin-only. Sends message <text> from <source>, where <source> is the nick of a PyLink client. If <source> is not given, it defaults to the main PyLink client.""" Sends message <text> from <source>, where <source> is the nick of a PyLink client. If <source> is not given, it defaults to the main PyLink client."""
irc.checkAuthenticated(source, allowOper=False) permissions.check_permissions(irc, source, ['bots.msg'])
# Because we want the source nick to be optional, this argument parsing gets a bit tricky. # Because we want the source nick to be optional, this argument parsing gets a bit tricky.
try: try:
@ -190,34 +220,48 @@ def msg(irc, source, args):
# First, check if the first argument is an existing PyLink client. If it is not, # First, check if the first argument is an existing PyLink client. If it is not,
# then assume that the first argument was actually the message TARGET. # then assume that the first argument was actually the message TARGET.
sourceuid = irc.nickToUid(msgsource) sourceuid = irc.nick_to_uid(msgsource, filterfunc=irc.is_internal_client)
if not irc.isInternalClient(sourceuid): # First argument isn't one of our clients
if sourceuid is None or not text: # First argument isn't one of our clients
raise IndexError raise IndexError
if not text:
raise IndexError
except IndexError: except IndexError:
try: try:
sourceuid = irc.pseudoclient.uid sourceuid = irc.pseudoclient.uid
target = args[0] target = args[0]
text = ' '.join(args[1:]) text = ' '.join(args[1:])
except IndexError: except IndexError:
irc.reply('Error: Not enough arguments. Needs 2-3: source nick (optional), target, text.') irc.error('Not enough arguments. Needs 2-3: source nick (optional), target, text.')
return return
if not text: if not text:
irc.reply('Error: No text given.') irc.error('No text given.')
return return
if not utils.isChannel(target): try:
# Convert nick of the message target to a UID, if the target isn't a channel int_u = int(target)
real_target = irc.nickToUid(target) except:
if real_target is None: # Unknown target user, if target isn't a valid channel name int_u = None
irc.reply('Error: Unknown user %r.' % target)
if int_u and int_u in irc.users:
real_target = int_u # Some protocols use numeric UIDs
elif target in irc.users:
real_target = target
elif not irc.is_channel(target):
# Convert nick of the message target to a UID, if the target isn't a channel or UID
potential_targets = irc.nick_to_uid(target, multi=True)
if not potential_targets: # Unknown target user, if target isn't a valid channel name
irc.error('Unknown user %r.' % target)
return return
elif len(potential_targets) > 1:
irc.error('Multiple users with the nick %r found: please select the right UID: %s' % (target, str(potential_targets)))
return
else:
real_target = potential_targets[0]
else: else:
real_target = target real_target = target
irc.proto.message(sourceuid, real_target, text) irc.message(sourceuid, real_target, text)
irc.callHooks([sourceuid, 'PYLINK_BOTSPLUGIN_MSG', {'target': real_target, 'text': text, 'parse_as': 'PRIVMSG'}]) irc.reply("Done.")
utils.add_cmd(msg, 'say') irc.call_hooks([sourceuid, 'PYLINK_BOTSPLUGIN_MSG', {'target': real_target, 'text': text, 'parse_as': 'PRIVMSG'}])
utils.add_cmd(msg, aliases=('say',))

View File

@ -1,63 +1,73 @@
""" """
Changehost plugin - automatically changes the hostname of matching users. Changehost plugin - automatically changes the hostname of matching users.
""" """
# Import hacks to access utils and log.
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import string import string
# ircmatch library from https://github.com/mammon-ircd/ircmatch from pylinkirc import conf, utils, world
# (pip install ircmatch) from pylinkirc.coremods import permissions
import ircmatch from pylinkirc.log import log
import utils
import world
from log import log
# Characters allowed in a hostname. # Characters allowed in a hostname.
allowed_chars = string.ascii_letters + '-./:' + string.digits allowed_chars = string.ascii_letters + '-./:' + string.digits
def _changehost(irc, target, args): def _changehost(irc, target):
changehost_conf = irc.conf.get("changehost") changehost_conf = conf.conf.get("changehost")
if not changehost_conf: if target not in irc.users:
log.warning("(%s) Missing 'changehost:' configuration block; "
"Changehost will not function correctly!", irc.name)
return return
elif irc.name not in changehost_conf.get('enabled_nets'): elif irc.is_internal_client(target):
log.debug('(%s) Skipping changehost on internal client %s', irc.name, target)
return
if irc.name not in changehost_conf.get('enabled_nets') and not irc.serverdata.get('changehost_enable'):
# We're not enabled on the network, break. # We're not enabled on the network, break.
return return
changehost_hosts = changehost_conf.get('hosts') match_ip = irc.get_service_option('changehost', 'match_ip', default=False)
match_realhosts = irc.get_service_option('changehost', 'match_realhosts', default=False)
changehost_hosts = irc.get_service_options('changehost', 'hosts', dict)
if not changehost_hosts: if not changehost_hosts:
log.warning("(%s) No hosts were defined in changehost::hosts; "
"Changehost will not function correctly!", irc.name)
return return
# Match against both the user's IP and real host. args = irc.users[target].get_fields()
target_host = irc.getHostmask(target, realhost=True)
target_ip = irc.getHostmask(target, ip=True) # $host is explicitly forbidden by default because it can cause recursive
# loops when IP or real host masks are used to match a target. vHost
# updates do not affect these fields, so any further host application will
# cause the vHost to grow rapidly in size.
# That said, it is possible to get away with this expansion if you're
# careful enough, and that's why this hidden option exists.
if not changehost_conf.get('force_host_expansion'):
del args['host']
log.debug('(%s) Changehost args: %s', irc.name, args)
for host_glob, host_template in changehost_hosts.items(): for host_glob, host_template in changehost_hosts.items():
if ircmatch.match(0, host_glob, target_host) or ircmatch.match(0, host_glob, target_ip): log.debug('(%s) Changehost: checking mask %s', irc.name, host_glob)
if irc.match_host(host_glob, target, ip=match_ip, realhost=match_realhosts):
log.debug('(%s) Changehost matched mask %s', irc.name, host_glob)
# This uses template strings for simple substitution: # This uses template strings for simple substitution:
# https://docs.python.org/3/library/string.html#template-strings # https://docs.python.org/3/library/string.html#template-strings
template = string.Template(host_template) template = string.Template(host_template)
# Substitute using the fields provided the hook data. This means # Substitute using the fields provided the hook data. This means
# that the following variables are available for substitution: # that the following variables are available for substitution:
# $uid, $ts, $nick, $realhost, $host, $ident, $ip # $uid, $ts, $nick, $realhost, $ident, and $ip.
new_host = template.substitute(args) try:
new_host = template.substitute(args)
except KeyError as e:
log.warning('(%s) Bad expansion %s in template %s' % (irc.name, e, host_template))
continue
# Replace characters that are not allowed in hosts with "-". # Replace characters that are not allowed in hosts with "-".
for char in new_host: for char in new_host:
if char not in allowed_chars: if char not in allowed_chars:
new_host = new_host.replace(char, '-') new_host = new_host.replace(char, '-')
irc.proto.updateClient(target, 'HOST', new_host) # Only send a host change if something has changed
if new_host != irc.users[target].host:
irc.update_client(target, 'HOST', new_host)
# Only operate on the first match. # Only operate on the first match.
break break
@ -68,25 +78,57 @@ def handle_uid(irc, sender, command, args):
""" """
target = args['uid'] target = args['uid']
_changehost(irc, target, args) _changehost(irc, target)
utils.add_hook(handle_uid, 'UID') utils.add_hook(handle_uid, 'UID')
def handle_chghost(irc, sender, command, args):
"""
Handles incoming CHGHOST requests for optional host-change enforcement.
"""
changehost_conf = conf.conf.get("changehost", {})
target = args['target']
if (not irc.is_internal_client(sender)) and (not irc.is_internal_server(sender)):
if irc.name in changehost_conf.get('enforced_nets', []) or irc.serverdata.get('changehost_enforce'):
log.debug('(%s) Enforce for network is on, re-checking host for target %s/%s',
irc.name, target, irc.get_friendly_name(target))
for ex in irc.get_service_options("changehost", "enforce_exceptions", list):
if irc.match_host(ex, target):
log.debug('(%s) Skipping host change for target %s; they are exempted by mask %s',
irc.name, target, ex)
return
userobj = irc.users.get(target)
if userobj:
_changehost(irc, target)
utils.add_hook(handle_chghost, 'CHGHOST')
def handle_svslogin(irc, sender, command, args):
"""
Handles services account changes for changehost.
"""
_changehost(irc, sender)
utils.add_hook(handle_svslogin, 'CLIENT_SERVICES_LOGIN')
@utils.add_cmd @utils.add_cmd
def applyhosts(irc, sender, args): def applyhosts(irc, sender, args):
"""[<network>] """[<network>]
Applies all configured hosts for users on the given network, or the current network if none is specified.""" Applies all configured hosts for users on the given network, or the current network if none is specified."""
permissions.check_permissions(irc, sender, ['changehost.applyhosts'])
try: # Try to get network from the command line. try: # Try to get network from the command line.
network = world.networkobjects[args[0]] network = world.networkobjects[args[0]]
except IndexError: # No network was given except IndexError: # No network was given
network = irc network = irc
except KeyError: # Unknown network except KeyError: # Unknown network
irc.reply("Error: Unknown network '%s'." % network) irc.error("Unknown network '%s'." % network)
return return
for user, userdata in network.users.copy().items(): for user in network.users.copy():
_changehost(network, user, userdata.__dict__) _changehost(network, user)
irc.reply("Done.") irc.reply("Done.")

View File

@ -1,120 +1,267 @@
# commands.py: base PyLink commands # commands.py: base PyLink commands
import sys import sys
import os import time
from time import ctime
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from pylinkirc import __version__, conf, real_version, utils, world
import utils from pylinkirc.coremods import permissions
from log import log from pylinkirc.coremods.login import pwd_context
import world
default_permissions = {"*!*@*": ['commands.status', 'commands.showuser', 'commands.showchan', 'commands.shownet']}
def main(irc=None):
"""Commands plugin main function, called on plugin load."""
# Register our permissions.
permissions.add_default_permissions(default_permissions)
def die(irc=None):
"""Commands plugin die function, called on plugin unload."""
permissions.remove_default_permissions(default_permissions)
@utils.add_cmd @utils.add_cmd
def status(irc, source, args): def status(irc, source, args):
"""takes no arguments. """takes no arguments.
Returns your current PyLink login status.""" Returns your current PyLink login status."""
identified = irc.users[source].identified permissions.check_permissions(irc, source, ['commands.status'])
identified = irc.users[source].account
if identified: if identified:
irc.reply('You are identified as \x02%s\x02.' % identified) irc.reply('You are identified as \x02%s\x02.' % identified)
else: else:
irc.reply('You are not identified as anyone.') irc.reply('You are not identified as anyone.')
irc.reply('Operator access: \x02%s\x02' % bool(irc.isOper(source))) irc.reply('Operator access: \x02%s\x02' % bool(irc.is_oper(source)))
_none = '\x1D(none)\x1D' _none = '\x1D(none)\x1D'
_notavail = '\x1DN/A\x1D'
def _do_showuser(irc, source, u):
"""Helper function for showuser."""
# Some protocol modules store UIDs as ints; make sure we check for that.
try:
int_u = int(u)
except ValueError:
pass
else:
if int_u in irc.users:
u = int_u
# Only show private info if the person is calling 'showuser' on themselves,
# or is an oper.
verbose = irc.is_oper(source) or u == source
if u not in irc.users:
irc.error('Unknown user %r.' % u)
return
f = lambda s: irc.reply(' ' + s, private=True)
userobj = irc.users[u]
irc.reply('Showing information on user \x02%s\x02 (%s@%s): %s' % (userobj.nick, userobj.ident,
userobj.host, userobj.realname), private=True)
sid = irc.get_server(u)
serverobj = irc.servers[sid]
ts = userobj.ts
# Show connected server & nick TS if available
serverinfo = '%s[%s]' % (serverobj.name, sid) \
if irc.has_cap('can-track-servers') else None
tsinfo = '%s [UTC] (%s)' % (time.asctime(time.gmtime(int(ts))), ts) \
if irc.has_cap('has-ts') else None
if tsinfo or serverinfo:
f('\x02Home server\x02: %s; \x02Nick TS:\x02 %s' % (serverinfo or _notavail, tsinfo or _notavail))
if verbose: # Oper/self only data: user modes, channels in, account info, etc.
f('\x02Protocol UID\x02: %s; \x02Real host\x02: %s; \x02IP\x02: %s' % \
(u, userobj.realhost or _notavail, userobj.ip))
channels = sorted(userobj.channels)
f('\x02Channels\x02: %s' % (' '.join(map(str, channels)) or _none))
f('\x02PyLink identification\x02: %s; \x02Services account\x02: %s; \x02Away status\x02: %s' % \
((userobj.account or _none), (userobj.services_account or _none), userobj.away or _none))
f('\x02User modes\x02: %s' % irc.join_modes(userobj.modes, sort=True))
# Show relay user data if available
relay = world.plugins.get('relay')
if relay:
try:
userpair = relay.get_orig_user(irc, u) or (irc.name, u)
remoteusers = relay.relayusers[userpair].items()
except KeyError:
pass
else:
nicks = []
if remoteusers:
# Display all of the user's relay subclients, if there are any
nicks.append('%s:\x02%s\x02' % (userpair[0],
world.networkobjects[userpair[0]].users[userpair[1]].nick))
for r in remoteusers:
remotenet, remoteuser = r
remoteirc = world.networkobjects[remotenet]
nicks.append('%s:\x02%s\x02' % (remotenet, remoteirc.users[remoteuser].nick))
f("\x02Relay nicks\x02: %s" % ', '.join(nicks))
if verbose:
# Show the relay channels the user is in, if applicable
relaychannels = []
for ch in irc.users[u].channels:
relayentry = relay.get_relay(irc, ch)
if relayentry:
relaychannels.append(''.join(relayentry))
if relaychannels and verbose:
f("\x02Relay channels\x02: %s" % ' '.join(relaychannels))
@utils.add_cmd @utils.add_cmd
def showuser(irc, source, args): def showuser(irc, source, args):
"""<user> """<user>
Shows information about <user>.""" Shows information about <user>."""
permissions.check_permissions(irc, source, ['commands.showuser'])
target = ' '.join(args)
if not target:
irc.error("Not enough arguments. Needs 1: nick.")
return
users = irc.nick_to_uid(target, multi=True) or [target]
for user in users:
_do_showuser(irc, source, user)
@utils.add_cmd
def shownet(irc, source, args):
"""[<network name>]
Shows information about <network name>, or the current network if no argument is given."""
permissions.check_permissions(irc, source, ['commands.shownet'])
try:
extended = permissions.check_permissions(irc, source, ['commands.shownet.extended'])
except utils.NotAuthorizedError:
extended = False
try: try:
target = args[0] target = args[0]
except IndexError: except IndexError:
irc.reply("Error: Not enough arguments. Needs 1: nick.") target = irc.name
return
u = irc.nickToUid(target) or target
# Only show private info if the person is calling 'showuser' on themselves,
# or is an oper.
verbose = irc.isOper(source) or u == source
if u not in irc.users:
irc.reply('Error: Unknown user %r.' % target)
return
f = lambda s: irc.msg(source, s) try:
userobj = irc.users[u] netobj = world.networkobjects[target]
f('Showing information on user \x02%s\x02 (%s@%s): %s' % (userobj.nick, userobj.ident, serverdata = netobj.serverdata
userobj.host, userobj.realname)) except KeyError:
netobj = None
sid = irc.getServer(u) # If we have extended access, also look for disconnected networks
serverobj = irc.servers[sid] if extended and target in conf.conf['servers']:
ts = userobj.ts serverdata = conf.conf['servers'][target]
else:
irc.error('Unknown network %r' % target)
return
# Show connected server & signon time # Get extended protocol details: IRCd type, virtual server info
f('\x02Home server\x02: %s (%s); \x02Signon time:\x02 %s (%s)' % \ protocol_name = serverdata.get('protocol')
(serverobj.name, sid, ctime(float(ts)), ts)) ircd_type = None
if verbose: # Oper only data: user modes, channels on, account info, etc. # A bit of hardcoding here :(
if protocol_name == 'ts6':
ircd_type = serverdata.get('ircd', 'charybdis[default]')
elif protocol_name == 'inspircd':
ircd_type = serverdata.get('target_version', 'insp20[default]')
elif protocol_name == 'p10':
ircd_type = serverdata.get('ircd') or serverdata.get('p10_ircd') or 'nefarious[default]'
f('\x02User modes\x02: %s' % irc.joinModes(userobj.modes)) if protocol_name and ircd_type:
f('\x02Protocol UID\x02: %s; \x02Real host\x02: %s; \x02IP\x02: %s' % \ protocol_name = '%s/%s' % (protocol_name, ircd_type)
(u, userobj.realhost, userobj.ip)) elif netobj and not protocol_name: # Show virtual server detail if applicable
channels = sorted(userobj.channels) try:
f('\x02Channels\x02: %s' % (' '.join(channels) or _none)) parent_name = netobj.virtual_parent.name
f('\x02PyLink identification\x02: %s; \x02Services account\x02: %s; \x02Away status\x02: %s' % \ except AttributeError:
((userobj.identified or _none), (userobj.services_account or _none), userobj.away or _none)) parent_name = None
protocol_name = 'none; virtual server defined by \x02%s\x02' % parent_name
irc.reply('Information on network \x02%s\x02: \x02%s\x02' %
(target, netobj.get_full_network_name() if netobj else '\x1dCurrently not connected\x1d'))
irc.reply('\x02PyLink protocol module\x02: %s; \x02Encoding\x02: %s' %
(protocol_name, netobj.encoding if netobj else serverdata.get('encoding', 'utf-8[default]')))
# Extended info: target host, defined hostname / SID
if extended:
connected = netobj and netobj.connected.is_set()
irc.reply('\x02Connected?\x02 %s' % ('\x0303true' if connected else '\x0304false'))
if serverdata.get('ip'):
irc.reply('\x02Server target\x02: \x1f%s:%s' % (serverdata['ip'], serverdata.get('port')))
if serverdata.get('hostname'):
irc.reply('\x02PyLink hostname\x02: %s; \x02SID:\x02 %s; \x02SID range:\x02 %s' %
(serverdata.get('hostname') or _none,
serverdata.get('sid') or _none,
serverdata.get('sidrange') or _none))
@utils.add_cmd @utils.add_cmd
def showchan(irc, source, args): def showchan(irc, source, args):
"""<channel> """<channel>
Shows information about <channel>.""" Shows information about <channel>."""
permissions.check_permissions(irc, source, ['commands.showchan'])
try: try:
channel = irc.toLower(args[0]) channel = args[0]
except IndexError: except IndexError:
irc.reply("Error: Not enough arguments. Needs 1: channel.") irc.error("Not enough arguments. Needs 1: channel.")
return return
if channel not in irc.channels: if channel not in irc.channels:
irc.reply('Error: Unknown channel %r.' % channel) irc.error('Unknown channel %r.' % channel)
return return
f = lambda s: irc.msg(source, s) f = lambda s: irc.reply(s, private=True)
c = irc.channels[channel] c = irc.channels[channel]
# Only show verbose info if caller is oper or is in the target channel. # Only show verbose info if caller is oper or is in the target channel.
verbose = source in c.users or irc.isOper(source) verbose = source in c.users or irc.is_oper(source)
secret = ('s', None) in c.modes secret = ('s', None) in c.modes
if secret and not verbose: if secret and not verbose:
# Hide secret channels from normal users. # Hide secret channels from normal users.
irc.msg(source, 'Error: Unknown channel %r.' % channel) irc.error('Unknown channel %r.' % channel)
return return
nicks = [irc.users[u].nick for u in c.users] nicks = [irc.users[u].nick for u in c.users]
pmodes = ('owner', 'admin', 'op', 'halfop', 'voice')
f('Information on channel \x02%s\x02:' % channel) f('Information on channel \x02%s\x02:' % channel)
f('\x02Channel topic\x02: %s' % c.topic) if c.topic:
f('\x02Channel creation time\x02: %s (%s)' % (ctime(c.ts), c.ts)) f('\x02Channel topic\x02: %s' % c.topic)
# Mark TS values as untrusted on Clientbot and others (where TS is read-only or not trackable)
f('\x02Channel creation time\x02: %s (%s) [UTC]%s' %
(time.asctime(time.gmtime(int(c.ts))), c.ts,
' [UNTRUSTED]' if not irc.has_cap('has-ts') else ''))
# Show only modes that aren't list-style modes. # Show only modes that aren't list-style modes.
modes = irc.joinModes([m for m in c.modes if m[0] not in irc.cmodes['*A']]) modes = irc.join_modes([m for m in c.modes if m[0] not in irc.cmodes['*A']], sort=True)
f('\x02Channel modes\x02: %s' % modes) f('\x02Channel modes\x02: %s' % modes)
if verbose: if verbose:
nicklist = [] nicklist = []
# Iterate over the user list, sorted by nick. # Iterate over the user list, sorted by nick.
for user, nick in sorted(zip(c.users, nicks), for user, nick in sorted(zip(c.users, nicks),
key=lambda userpair: userpair[1].lower()): key=lambda userpair: userpair[1].lower()):
prefixmodes = [irc.prefixmodes.get(irc.cmodes.get(pmode, ''), '') # Note: reversed() is used here because we're adding prefixes onto the nick in reverse
for pmode in pmodes if user in c.prefixmodes[pmode]] for pmode in reversed(c.get_prefix_modes(user)):
nicklist.append(''.join(prefixmodes) + nick) # Show prefix modes in order from highest to lowest.
nick = irc.prefixmodes.get(irc.cmodes.get(pmode, ''), '') + nick
nicklist.append(nick)
while nicklist[:20]: # 20 nicks per line to prevent message cutoff. f('\x02User list\x02: %s' % ' '.join(nicklist))
f('\x02User list\x02: %s' % ' '.join(nicklist[:20]))
nicklist = nicklist[20:] # Show relay info, if applicable
relay = world.plugins.get('relay')
if relay:
relayentry = relay.get_relay(irc, channel)
if relayentry:
relays = ['\x02%s\x02' % ''.join(relayentry)]
relays += [''.join(link) for link in relay.db[relayentry]['links']]
f('\x02Relayed channels:\x02 %s' % (' '.join(relays)))
@utils.add_cmd @utils.add_cmd
def version(irc, source, args): def version(irc, source, args):
"""takes no arguments. """takes no arguments.
Returns the version of the currently running PyLink instance.""" Returns the version of the currently running PyLink instance."""
irc.reply("PyLink version \x02%s\x02, released under the Mozilla Public License version 2.0." % world.version) py_version = utils.NORMALIZEWHITESPACE_RE.sub(' ', sys.version)
irc.reply("PyLink version \x02%s\x02 (in VCS: %s), running on Python %s." % (__version__, real_version, py_version))
irc.reply("The source of this program is available at \x02%s\x02." % world.source) irc.reply("The source of this program is available at \x02%s\x02." % world.source)
@utils.add_cmd @utils.add_cmd
@ -122,8 +269,60 @@ def echo(irc, source, args):
"""<text> """<text>
Echoes the text given.""" Echoes the text given."""
permissions.check_permissions(irc, source, ['commands.echo'])
if not args:
irc.error('No text to send!')
return
irc.reply(' '.join(args)) irc.reply(' '.join(args))
def _check_logout_access(irc, source, target, perms):
"""
Checks whether the source UID has access to log out the target UID.
This returns True if the source user has a permission specified,
or if the source and target are both logged in and have the same account.
"""
assert source in irc.users, "Unknown source user"
assert target in irc.users, "Unknown target user"
try:
permissions.check_permissions(irc, source, perms)
except utils.NotAuthorizedError:
if irc.users[source].account and (irc.users[source].account == irc.users[target].account):
return True
else:
raise
else:
return True
@utils.add_cmd
def logout(irc, source, args):
"""[<other nick/UID>]
Logs your account out of PyLink. If you have the 'commands.logout.force' permission, or are
attempting to log out yourself, you can also specify a nick to force a logout for."""
try:
othernick = args[0]
except IndexError: # No user specified
if irc.users[source].account:
irc.users[source].account = ''
else:
irc.error("You are not logged in!")
return
else:
otheruid = irc.nick_to_uid(othernick)
if not otheruid:
irc.error("Unknown user %s." % othernick)
return
else:
_check_logout_access(irc, source, otheruid, ['commands.logout.force'])
if irc.users[otheruid].account:
irc.users[otheruid].account = ''
else:
irc.error("%s is not logged in." % othernick)
return
irc.reply("Done.")
loglevels = {'DEBUG': 10, 'INFO': 20, 'WARNING': 30, 'ERROR': 40, 'CRITICAL': 50} loglevels = {'DEBUG': 10, 'INFO': 20, 'WARNING': 30, 'ERROR': 40, 'CRITICAL': 50}
@utils.add_cmd @utils.add_cmd
def loglevel(irc, source, args): def loglevel(irc, source, args):
@ -131,16 +330,37 @@ def loglevel(irc, source, args):
Sets the log level to the given <level>. <level> must be either DEBUG, INFO, WARNING, ERROR, or CRITICAL. Sets the log level to the given <level>. <level> must be either DEBUG, INFO, WARNING, ERROR, or CRITICAL.
If no log level is given, shows the current one.""" If no log level is given, shows the current one."""
irc.checkAuthenticated(source, allowOper=False) permissions.check_permissions(irc, source, ['commands.loglevel'])
try: try:
level = args[0].upper() level = args[0].upper()
try: try:
loglevel = loglevels[level] loglevel = loglevels[level]
except KeyError: except KeyError:
irc.reply('Error: Unknown log level "%s".' % level) irc.error('Unknown log level "%s".' % level)
return return
else: else:
world.stdout_handler.setLevel(loglevel) world.console_handler.setLevel(loglevel)
irc.reply("Done.") irc.reply("Done.")
except IndexError: except IndexError:
irc.reply(world.stdout_handler.level) irc.reply(world.console_handler.level)
@utils.add_cmd
def mkpasswd(irc, source, args):
"""<password>
Hashes a password for use in the configuration file."""
# TODO: restrict to only certain users?
try:
password = args[0]
except IndexError:
irc.error("Not enough arguments. (Needs 1, password)")
return
if not password:
irc.error("Password cannot be empty.")
return
if not pwd_context:
irc.error("Password encryption is not available (missing passlib).")
return
hashed_pass = pwd_context.encrypt(password)
irc.reply(hashed_pass, private=True)

88
plugins/ctcp.py Normal file
View File

@ -0,0 +1,88 @@
# ctcp.py: Handles basic CTCP requests.
import datetime
import random
from pylinkirc import utils
from pylinkirc.log import log
def handle_ctcp(irc, source, command, args):
"""
CTCP event handler.
"""
text = args['text']
if not (text.startswith('\x01') and text.endswith('\x01')):
return None # Pass through to other plugins
target = args['target']
if not irc.get_service_bot(target):
# Ignore this message if the target isn't a service bot
return None
text = text.strip('\x01')
try:
ctcp_command, data = text.split(" ", 1)
except ValueError:
ctcp_command = text
data = ''
ctcp_command = ctcp_command.upper()
log.debug('(%s) ctcp: got CTCP command %r, data %r',
irc.name, ctcp_command, data)
if ctcp_command in SUPPORTED_COMMANDS:
log.info('(%s) Received CTCP %s from %s to %s',
irc.name, ctcp_command, irc.get_hostmask(source),
irc.get_friendly_name(target))
# Call the helper function and display its result.
result = SUPPORTED_COMMANDS[ctcp_command](irc, source, ctcp_command, data)
if result and source in irc.users:
# Note, do NOT use irc.reply() in hook handlers because nothing except the
# command handler system actually updates the last caller.
irc.msg(source, '\x01%s %s\x01' % (ctcp_command, result),
notice=True, source=target)
return False # Block this message from reaching the general command handler
else:
log.info('(%s) Received unknown CTCP %s from %s to %s',
irc.name, ctcp_command, irc.get_hostmask(source),
irc.get_friendly_name(target))
return False
utils.add_hook(handle_ctcp, 'PRIVMSG', priority=200)
def handle_ctcpversion(irc, source, ctcp, data):
"""
Handles CTCP version requests.
"""
return irc.version()
def handle_ctcpeaster(irc, source, ctcp, data):
"""
Secret easter egg.
"""
responses = ["Legends say the cord monster was born only %s years ago..." % \
(datetime.datetime.now().year - 2014),
"Hiss%s" % ('...' * random.randint(1, 5)),
"His%s%s" % ('s' * random.randint(1, 4), '...' * random.randint(1, 5)),
"It's Easter already? Where are the eggs?",
"Maybe later.",
"Janus? Never heard of it.",
irc.version(),
"Let me out of here, I'll give you cookies!",
"About as likely as pigs flying.",
"Request timed out.",
"No actual pie here, sorry.",
"Hey, no loitering!",
"Hey, can you keep a secret? \x031,1 %s" % " " * random.randint(1,20),
]
return random.choice(responses)
# Map CTCP commands to functions generating an appropriate text response.
SUPPORTED_COMMANDS = {'VERSION': handle_ctcpversion,
'PING': lambda irc, source, ctcp, data: data,
'ABOUT': handle_ctcpeaster,
'EASTER': handle_ctcpeaster}

View File

@ -1,15 +1,9 @@
# example.py: An example PyLink plugin. # example.py: An example PyLink plugin.
# These two lines add PyLink's root directory to the PATH, so that importing things like
# 'utils' and 'log' work.
import sys, os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import utils
from log import log
import random import random
from pylinkirc import utils
from pylinkirc.log import log
# Example PRIVMSG hook that returns "hi there!" when PyLink's nick is mentioned # Example PRIVMSG hook that returns "hi there!" when PyLink's nick is mentioned
# in a channel. # in a channel.
@ -24,9 +18,9 @@ def hook_privmsg(irc, source, command, args):
channel = args['target'] channel = args['target']
text = args['text'] text = args['text']
# irc.pseudoclient stores the IrcUser object of the main PyLink client. # irc.pseudoclient stores the User object of the main PyLink client.
# (i.e. the user defined in the bot: section of the config) # (i.e. the user defined in the bot: section of the config)
if utils.isChannel(channel) and irc.pseudoclient.nick in text: if irc.is_channel(channel) and irc.pseudoclient.nick in text:
irc.msg(channel, 'hi there!') irc.msg(channel, 'hi there!')
# log.debug, log.info, log.warning, log.error, log.exception (within except: clauses) # log.debug, log.info, log.warning, log.error, log.exception (within except: clauses)
# and log.critical are supported here. # and log.critical are supported here.
@ -37,21 +31,26 @@ utils.add_hook(hook_privmsg, 'PRIVMSG')
# Example command function. @utils.add_cmd binds it to an IRC command of the same name, # Example command function. @utils.add_cmd binds it to an IRC command of the same name,
# but you can also use a different name by specifying a second 'name' argument (see below). # but you can also use a different name by specifying a second 'name' argument (see below).
@utils.add_cmd #@utils.add_cmd
# irc: The IRC object where the command was called. # irc: The IRC object where the command was called.
# source: The UID/numeric of the calling user. # source: The UID/numeric of the calling user.
# args: A list of command args (excluding the command name) that the command was called with. # args: A list of command args (excluding the command name) that the command was called with.
def randint(irc, source, args): def randint(irc, source, args):
# The docstring here is used as command help by the 'help' command, and formatted using the # The 'help' command uses command functions' docstrings as help text, and formats them
# same line breaks as the raw string. You shouldn't make this text or any one line too long, # in the following manner:
# to prevent flooding users or getting long lines cut off. # - Any newlines immediately adjacent to text on both sides are replaced with a space. This
# means that the first descriptive paragraph ("Returns a random...given.") shows up as one
# The same applies to message replies in general: plugins sending long strings of text should # line, even though it is physically written on two.
# be wary that long messages can get cut off. Automatic word-wrap may be added in the future: # - Double line breaks are treated as breaks between two paragraphs, and will be shown
# https://github.com/GLolol/PyLink/issues/153 # as distinct lines in IRC.
# As of PyLink 2.0, long paragraphs are automatically word-wrapped by irc.reply().
"""[<min> <max>] """[<min> <max>]
Returns a random number between <min> and <max>. <min> and <max> default
to 1 and 10 respectively, if both aren't given.""" Returns a random number between <min> and <max>. <min> and <max> default to 1 and 10
respectively, if both aren't given.
Example second paragraph here."""
try: try:
rmin = args[0] rmin = args[0]
rmax = args[1] rmax = args[1]
@ -64,6 +63,5 @@ def randint(irc, source, args):
# it will send replies into the channel instead of in your PM. # it will send replies into the channel instead of in your PM.
irc.reply(str(n)) irc.reply(str(n))
# You can also bind a command function multiple times, and/or to different command names via a # You can bind a command function to multiple names using the 'aliases' option.
# second argument. utils.add_cmd(randint, "random", aliases=("randint", "getrandint"))
utils.add_cmd(randint, "random")

View File

@ -1,27 +1,31 @@
""" """
exec.py: Provides commands for executing raw code and debugging PyLink. exec.py: Provides commands for executing raw code and debugging PyLink.
""" """
import pprint
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import utils
from log import log
# These imports are not strictly necessary, but make the following modules # These imports are not strictly necessary, but make the following modules
# easier to access through eval and exec. # easier to access through eval and exec.
import world
import threading import threading
import re
import time
def _exec(irc, source, args): from pylinkirc import utils, world, conf
from pylinkirc.coremods import permissions
from pylinkirc.log import log
exec_locals_dict = {}
PPRINT_MAX_LINES = 20
PPRINT_WIDTH = 200
if not conf.conf['pylink'].get("debug_enabled", False):
raise RuntimeError("pylink::debug_enabled must be enabled to load this plugin. "
"This should ONLY be used in test environments for debugging and development, "
"as anyone with access to this plugin's commands can run arbitrary code as the PyLink user!")
def _exec(irc, source, args, locals_dict=None):
"""<code> """<code>
Admin-only. Executes <code> in the current PyLink instance. This command performs backslash escaping of characters, so things like \\n and \\ will work. Admin-only. Executes <code> in the current PyLink instance. This command performs backslash escaping of characters, so things like \\n and \\ will work.
\x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02""" \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02"""
irc.checkAuthenticated(source, allowOper=False) permissions.check_permissions(irc, source, ['exec.exec'])
# Allow using \n in the code, while escaping backslashes correctly otherwise. # Allow using \n in the code, while escaping backslashes correctly otherwise.
args = bytes(' '.join(args), 'utf-8').decode("unicode_escape") args = bytes(' '.join(args), 'utf-8').decode("unicode_escape")
@ -30,54 +34,117 @@ def _exec(irc, source, args):
return return
log.info('(%s) Executing %r for %s', irc.name, args, log.info('(%s) Executing %r for %s', irc.name, args,
irc.getHostmask(source)) irc.get_hostmask(source))
exec(args, globals(), locals()) if locals_dict is None:
locals_dict = locals()
else:
# Add irc, source, and args to the given locals_dict, to allow basic things like irc.reply()
# to still work.
locals_dict['irc'] = irc
locals_dict['source'] = source
locals_dict['args'] = args
exec(args, globals(), locals_dict)
irc.reply("Done.")
utils.add_cmd(_exec, 'exec') utils.add_cmd(_exec, 'exec')
def _eval(irc, source, args): @utils.add_cmd
def iexec(irc, source, args):
"""<code>
Admin-only. Executes <code> in the current PyLink instance with a persistent, isolated
locals scope (world.plugins['exec'].exec_local_dict).
Note: irc, source, and args are added into this locals dict to allow things like irc.reply()
to still work.
\x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02
"""
_exec(irc, source, args, locals_dict=exec_locals_dict)
def _eval(irc, source, args, locals_dict=None, pretty_print=False):
"""<Python expression> """<Python expression>
Admin-only. Evaluates the given Python expression and returns the result. Admin-only. Evaluates the given Python expression and returns the result.
\x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02""" \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02"""
irc.checkAuthenticated(source, allowOper=False) permissions.check_permissions(irc, source, ['exec.eval'])
args = ' '.join(args) args = ' '.join(args)
if not args.strip(): if not args.strip():
irc.reply('No code entered!') irc.reply('No code entered!')
return return
if locals_dict is None:
locals_dict = locals()
else:
# Add irc, source, and args to the given locals_dict, to allow basic things like irc.reply()
# to still work.
locals_dict['irc'] = irc
locals_dict['source'] = source
locals_dict['args'] = args
log.info('(%s) Evaluating %r for %s', irc.name, args, log.info('(%s) Evaluating %r for %s', irc.name, args,
irc.getHostmask(source)) irc.get_hostmask(source))
irc.reply(eval(args))
result = eval(args, globals(), locals_dict)
if pretty_print:
lines = pprint.pformat(result, width=PPRINT_WIDTH, compact=True).splitlines()
for line in lines[:PPRINT_MAX_LINES]:
irc.reply(line)
if len(lines) > PPRINT_MAX_LINES:
irc.reply('Suppressing %s more line(s) of output.' % (len(lines) - PPRINT_MAX_LINES))
else:
# Purposely disable text wrapping so results are cut instead of potentially flooding;
# 'peval' is specifically designed to work around that.
irc.reply(repr(result), wrap=False)
utils.add_cmd(_eval, 'eval') utils.add_cmd(_eval, 'eval')
@utils.add_cmd @utils.add_cmd
def raw(irc, source, args): def peval(irc, source, args):
"""<text> """<Python expression>
Admin-only. Sends raw text to the uplink IRC server. Admin-only. This command is the same as 'eval', except that results are pretty formatted.
\x02**WARNING: THIS CAN BREAK YOUR NETWORK IF USED IMPROPERLY!**\x02"""
irc.checkAuthenticated(source, allowOper=False)
args = ' '.join(args) \x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02
if not args.strip(): """
irc.reply('No text entered!') _eval(irc, source, args, pretty_print=True)
return
log.info('(%s) Sending raw text %r to IRC for %s', irc.name, args, @utils.add_cmd
irc.getHostmask(source)) def ieval(irc, source, args):
irc.send(args) """<Python expression>
irc.reply("Done.") Admin-only. Evaluates the given Python expression using a persistent, isolated
locals scope (world.plugins['exec'].exec_local_dict).
Note: irc, source, and args are added into this locals dict to allow things like irc.reply()
to still work.
\x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02
"""
_eval(irc, source, args, locals_dict=exec_locals_dict)
@utils.add_cmd
def pieval(irc, source, args):
"""<Python expression>
Admin-only. This command is the same as 'ieval', except that results are pretty formatted.
\x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02
"""
_eval(irc, source, args, locals_dict=exec_locals_dict, pretty_print=True)
@utils.add_cmd @utils.add_cmd
def inject(irc, source, args): def inject(irc, source, args):
"""<text> """<text>
Admin-only. Injects raw text into the running PyLink protocol module, replying with the hook data returned. Admin-only. Injects raw text into the running PyLink protocol module, replying with the hook data returned.
\x02**WARNING: THIS CAN BREAK YOUR NETWORK IF USED IMPROPERLY!**\x02""" \x02**WARNING: THIS CAN BREAK YOUR NETWORK IF USED IMPROPERLY!**\x02"""
irc.checkAuthenticated(source, allowOper=False) permissions.check_permissions(irc, source, ['exec.inject'])
args = ' '.join(args) args = ' '.join(args)
if not args.strip(): if not args.strip():
@ -85,5 +152,25 @@ def inject(irc, source, args):
return return
log.info('(%s) Injecting raw text %r into protocol module for %s', irc.name, log.info('(%s) Injecting raw text %r into protocol module for %s', irc.name,
args, irc.getHostmask(source)) args, irc.get_hostmask(source))
irc.reply(irc.runline(args)) irc.reply(repr(irc.parse_irc_command(args)))
@utils.add_cmd
def threadinfo(irc, source, args):
"""takes no arguments.
Lists all threads currently present in this PyLink instance."""
permissions.check_permissions(irc, source, ['exec.threadinfo'])
for t in sorted(threading.enumerate(), key=lambda t: t.name.lower()):
name = t.name
# Unnamed threads are something we want to avoid throughout PyLink.
if name.startswith('Thread-'):
name = '\x0305%s\x03' % t.name
# Also VERY bad: remaining threads for networks not in the networks index anymore!
elif name.startswith(('Listener for', 'Ping timer loop for', 'Queue thread for')) and name.rsplit(" ", 1)[-1] not in world.networkobjects:
name = '\x0304%s\x03' % t.name
irc.reply('\x02%s\x02[%s]: daemon=%s; alive=%s' % (name, t.ident, t.daemon, t.is_alive()), private=True)
irc.reply("Total of %s threads." % threading.active_count())

View File

@ -1,11 +1,7 @@
# fantasy.py: Adds FANTASY command support, to allow calling commands in channels # fantasy.py: Adds FANTASY command support, to allow calling commands in channels
import sys from pylinkirc import conf, utils, world
import os from pylinkirc.log import log
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import utils
import world
from log import log
def handle_fantasy(irc, source, command, args): def handle_fantasy(irc, source, command, args):
"""Fantasy command handler.""" """Fantasy command handler."""
@ -14,12 +10,10 @@ def handle_fantasy(irc, source, command, args):
# Break if the IRC network isn't ready. # Break if the IRC network isn't ready.
return return
respondtonick = irc.botdata.get("respondtonick")
channel = args['target'] channel = args['target']
orig_text = args['text'] orig_text = args['text']
if utils.isChannel(channel) and not irc.isInternalClient(source): if irc.is_channel(channel) and not irc.is_internal_client(source):
# The following conditions must be met for an incoming message for # The following conditions must be met for an incoming message for
# fantasy to trigger: # fantasy to trigger:
# 1) The message target is a channel. # 1) The message target is a channel.
@ -27,38 +21,53 @@ def handle_fantasy(irc, source, command, args):
# 3) The message starts with one of our fantasy prefixes. # 3) The message starts with one of our fantasy prefixes.
# 4) The sender is NOT a PyLink client (this prevents infinite # 4) The sender is NOT a PyLink client (this prevents infinite
# message loops). # message loops).
for botname, sbot in world.services.items(): for botname, sbot in world.services.copy().items():
if botname not in world.services: # Bot was removed during iteration
continue
# Check respond to nick options in this order:
# 1) The service specific "respond_to_nick" option
# 2) The global "pylink::respond_to_nick" option
# 3) The (deprecated) global "bot::respondtonick" option.
respondtonick = conf.conf.get(botname, {}).get('respond_to_nick',
conf.conf['pylink'].get("respond_to_nick", conf.conf['pylink'].get("respondtonick")))
log.debug('(%s) fantasy: checking bot %s', irc.name, botname) log.debug('(%s) fantasy: checking bot %s', irc.name, botname)
servuid = sbot.uids.get(irc.name) servuid = sbot.uids.get(irc.name)
if servuid in irc.channels[channel].users: if servuid in irc.channels[channel].users:
# Try to look up a prefix specific for this bot in # Look up a string prefix for this bot in either its own configuration block, or
# bot: prefixes: <botname>, falling back to the default prefix if not # in bot::prefixes::<botname>.
# specified. prefixes = [conf.conf.get(botname, {}).get('prefix',
prefixes = [irc.botdata.get('prefixes', {}).get(botname) or conf.conf['pylink'].get('prefixes', {}).get(botname))]
irc.botdata.get('prefix')]
# If responding to nick is enabled, add variations of the current nick # If responding to nick is enabled, add variations of the current nick
# to the prefix list: "<nick>," and "<nick>:" # to the prefix list: "<nick>,", "<nick>:", and "@<nick>" (for Discord and other protocols)
nick = irc.users[servuid].nick nick = irc.to_lower(irc.users[servuid].nick)
nick_prefixes = [nick+',', nick+':', '@'+nick]
if respondtonick: if respondtonick:
prefixes += [nick+',', nick+':'] prefixes += nick_prefixes
if not any(prefixes): if not any(prefixes):
# We finished with an empty prefixes list, meaning fantasy is misconfigured! # No prefixes were set, so skip.
log.warning("(%s) Fantasy prefix for bot %s was not set in configuration - "
"fantasy commands will not work!", irc.name, botname)
continue continue
for prefix in prefixes: # Cycle through the prefixes list we finished with. lowered_text = irc.to_lower(orig_text)
if prefix and orig_text.startswith(prefix): for prefix in filter(None, prefixes): # Cycle through the prefixes list we finished with.
if lowered_text.startswith(prefix):
# Cut off the length of the prefix from the text. # Cut off the length of the prefix from the text.
text = orig_text[len(prefix):] text = orig_text[len(prefix):]
# HACK: don't trigger on commands like "& help" to prevent false positives.
# Weird spacing like "PyLink: help" and "/msg PyLink help" should still
# work though.
if text.startswith(' ') and prefix not in nick_prefixes:
log.debug('(%s) fantasy: skipping trigger with text prefix followed by space', irc.name)
continue
# Finally, call the bot command and loop to the next bot. # Finally, call the bot command and loop to the next bot.
sbot.call_cmd(irc, source, text, called_by=channel, notice=False) sbot.call_cmd(irc, source, text, called_in=channel)
continue continue
utils.add_hook(handle_fantasy, 'PRIVMSG') utils.add_hook(handle_fantasy, 'PRIVMSG')

View File

@ -1,22 +1,15 @@
""" """
games.py: Create a bot that provides game functionality (dice, 8ball, etc). games.py: Creates a bot providing a few simple games.
""" """
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import random import random
import urllib.request
import urllib.error
from xml.etree import ElementTree
import utils from pylinkirc import utils
from log import log
import world
gameclient = utils.registerService("Games", manipulatable=True) mydesc = "The \x02Games\x02 plugin provides simple games for IRC."
gameclient = utils.register_service("Games", default_nick="Games", manipulatable=True, desc=mydesc)
reply = gameclient.reply # TODO find a better syntax for ServiceBot.reply() reply = gameclient.reply # TODO find a better syntax for ServiceBot.reply()
error = gameclient.error # TODO find a better syntax for ServiceBot.error()
# commands # commands
def dice(irc, source, args): def dice(irc, source, args):
"""<num>d<sides> """<num>d<sides>
@ -46,8 +39,7 @@ def dice(irc, source, args):
s = 'You rolled %s: %s (total: %s)' % (args[0], ' '.join([str(x) for x in results]), sum(results)) s = 'You rolled %s: %s (total: %s)' % (args[0], ' '.join([str(x) for x in results]), sum(results))
reply(irc, s) reply(irc, s)
gameclient.add_cmd(dice, 'd') gameclient.add_cmd(dice, aliases=('d'), featured=True)
gameclient.add_cmd(dice)
eightball_responses = ["It is certain.", eightball_responses = ["It is certain.",
"It is decidedly so.", "It is decidedly so.",
@ -75,56 +67,7 @@ def eightball(irc, source, args):
Asks the Magic 8-ball a question. Asks the Magic 8-ball a question.
""" """
reply(irc, random.choice(eightball_responses)) reply(irc, random.choice(eightball_responses))
gameclient.add_cmd(eightball) gameclient.add_cmd(eightball, featured=True, aliases=('8ball', '8b'))
gameclient.add_cmd(eightball, '8ball')
gameclient.add_cmd(eightball, '8b')
def fml(irc, source, args): def die(irc=None):
"""[<id>] utils.unregister_service('games')
Displays an entry from fmylife.com. If <id>
is not given, fetch a random entry from the API."""
try:
query = args[0]
except IndexError:
# Get a random FML from the API.
query = 'random'
# TODO: configurable language?
url = ('http://api.betacie.com/view/%s/nocomment'
'?key=4be9c43fc03fe&language=en' % query)
try:
data = urllib.request.urlopen(url).read()
except urllib.error as e:
reply(irc, 'Error: %s' % e)
return
tree = ElementTree.fromstring(data.decode('utf-8'))
tree = tree.find('items/item')
try:
category = tree.find('category').text
text = tree.find('text').text
fmlid = tree.attrib['id']
url = tree.find('short_url').text
except AttributeError as e:
log.debug("games.FML: Error fetching FML %s from URL %s: %s",
query, url, e)
reply(irc, "Error: That FML does not exist or there was an error "
"fetching data from the API.")
return
if not fmlid:
reply(irc, "Error: That FML does not exist.")
return
# TODO: customizable formatting
votes = "\x02[Agreed: %s / Deserved: %s]\x02" % \
(tree.find('agree').text, tree.find('deserved').text)
s = '\x02#%s [%s]\x02: %s - %s \x02<\x0311%s\x03>\x02' % \
(fmlid, category, text, votes, url)
reply(irc, s)
gameclient.add_cmd(fml)
def die(irc):
utils.unregisterService('games')

63
plugins/global.py Normal file
View File

@ -0,0 +1,63 @@
# global.py: Global Noticing Plugin
import string
from pylinkirc import conf, utils, world
from pylinkirc.coremods import permissions
from pylinkirc.log import log
DEFAULT_FORMAT = "[$sender@$fullnetwork] $text"
def g(irc, source, args):
"""<message text>
Sends out a Instance-wide notice.
"""
permissions.check_permissions(irc, source, ["global.global"])
message = " ".join(args).strip()
if not message:
irc.error("Refusing to send an empty message.")
return
global_conf = conf.conf.get('global') or {}
template = string.Template(global_conf.get('format', DEFAULT_FORMAT))
exempt_channels = set(global_conf.get('exempt_channels', set()))
netcount = 0
chancount = 0
for netname, ircd in world.networkobjects.items():
# Skip networks that aren't ready and dummy networks which don't have .pseudoclient set
if ircd.connected.is_set() and ircd.pseudoclient:
netcount += 1
for channel in ircd.pseudoclient.channels:
local_exempt_channels = exempt_channels | set(ircd.serverdata.get('global_exempt_channels', set()))
skip = False
for exempt in local_exempt_channels:
if ircd.match_text(exempt, str(channel)):
log.debug('global: Skipping channel %s%s for exempt %r', netname, channel, exempt)
skip = True
break
if skip:
continue
subst = {'sender': irc.get_friendly_name(source),
'network': irc.name,
'fullnetwork': irc.get_full_network_name(),
'current_channel': channel,
'current_network': netname,
'current_fullnetwork': ircd.get_full_network_name(),
'text': message}
# Disable relaying or other plugins handling the global message.
ircd.msg(channel, template.safe_substitute(subst), loopback=False)
chancount += 1
irc.reply('Done. Sent to %d channels across %d networks.' % (chancount, netcount))
utils.add_cmd(g, "global", featured=True)

View File

@ -1,67 +1,40 @@
"""Networks plugin - allows you to manipulate connections to various configured networks.""" """Networks plugin - allows you to manipulate connections to various configured networks."""
import importlib
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import threading import threading
import types
import utils import pylinkirc
import world from pylinkirc import utils, world
from log import log from pylinkirc.coremods import control, permissions
from pylinkirc.log import log
REMOTE_IN_USE = threading.Event()
@utils.add_cmd @utils.add_cmd
def disconnect(irc, source, args): def disconnect(irc, source, args):
"""<network> """<network>
Disconnects the network <network>. When all networks are disconnected, PyLink will automatically exit. Disconnects the network <network>. When all networks are disconnected, PyLink will automatically exit.
Note: This does not affect the autoreconnect settings of any network, so the network will likely just reconnect unless autoconnect is disabled (see the 'autoconnect' command)."""
irc.checkAuthenticated(source, allowOper=False) To reconnect a network disconnected using this command, use REHASH to reload the networks list."""
permissions.check_permissions(irc, source, ['networks.disconnect'])
try: try:
netname = args[0] netname = args[0]
network = world.networkobjects[netname] network = world.networkobjects[netname]
except IndexError: # No argument given. except IndexError: # No argument given.
irc.reply('Error: Not enough arguments (needs 1: network name (case sensitive)).') irc.error('Not enough arguments (needs 1: network name (case sensitive)).')
return return
except KeyError: # Unknown network. except KeyError: # Unknown network.
irc.reply('Error: No such network "%s" (case sensitive).' % netname) irc.error('No such network "%s" (case sensitive).' % netname)
return return
irc.reply("Done.")
# Abort the connection! Simple as that. if network.has_cap('virtual-server'):
network.disconnect() irc.error('"%s" is a virtual server and cannot be directly disconnected.' % netname)
if network.serverdata["autoconnect"] < 1: # Remove networks if autoconnect is disabled.
del world.networkobjects[netname]
@utils.add_cmd
def connect(irc, source, args):
"""<network>
Initiates a connection to the network <network>."""
irc.checkAuthenticated(source, allowOper=False)
try:
netname = args[0]
network = world.networkobjects[netname]
except IndexError: # No argument given.
irc.reply('Error: Not enough arguments (needs 1: network name (case sensitive)).')
return return
except KeyError: # Unknown network.
irc.reply('Error: No such network "%s" (case sensitive).' % netname)
return
if network.connection_thread.is_alive():
irc.reply('Error: Network "%s" seems to be already connected.' % netname)
else: # Reconnect the network!
network.connection_thread = threading.Thread(target=network.connect)
network.connection_thread.start()
# And the plugins we have too. log.info('Disconnecting network %r per %s', netname, irc.get_hostmask(source))
for plugin in world.plugins.values(): control.remove_network(network)
if hasattr(plugin, 'main'): irc.reply("Done. If you want to reconnect this network, use the 'rehash' command.")
log.debug('(%s) Calling main() function of plugin %r', irc.name, plugin)
plugin.main(irc)
irc.reply("Done.")
@utils.add_cmd @utils.add_cmd
def autoconnect(irc, source, args): def autoconnect(irc, source, args):
@ -69,55 +42,157 @@ def autoconnect(irc, source, args):
Sets the autoconnect time for <network> to <seconds>. Sets the autoconnect time for <network> to <seconds>.
You can disable autoconnect for a network by setting <seconds> to a negative value.""" You can disable autoconnect for a network by setting <seconds> to a negative value."""
irc.checkAuthenticated(source) permissions.check_permissions(irc, source, ['networks.autoconnect'])
try: try:
netname = args[0] netname = args[0]
seconds = float(args[1]) seconds = float(args[1])
network = world.networkobjects[netname] network = world.networkobjects[netname]
except IndexError: # Arguments not given. except IndexError: # Arguments not given.
irc.reply('Error: Not enough arguments (needs 2: network name (case sensitive), autoconnect time (in seconds)).') irc.error('Not enough arguments (needs 2: network name (case sensitive), autoconnect time (in seconds)).')
return return
except KeyError: # Unknown network. except KeyError: # Unknown network.
irc.reply('Error: No such network "%s" (case sensitive).' % netname) irc.error('No such network "%s" (case sensitive).' % netname)
return return
except ValueError: except ValueError:
irc.reply('Error: Invalid argument "%s" for <seconds>.' % seconds) irc.error('Invalid argument "%s" for <seconds>.' % seconds)
return return
network.serverdata['autoconnect'] = seconds network.serverdata['autoconnect'] = seconds
irc.reply("Done.") irc.reply("Done.")
remote_parser = utils.IRCParser()
remote_parser.add_argument('--service', type=str, default='pylink')
remote_parser.add_argument('network')
remote_parser.add_argument('command', nargs=utils.IRCParser.REMAINDER)
@utils.add_cmd @utils.add_cmd
def remote(irc, source, args): def remote(irc, source, args):
"""<network> <command> """[--service <service name>] <network> <command>
Runs <command> on the remote network <network>. No replies are sent back due to protocol limitations.""" Runs <command> on the remote network <network>. Plugin responses sent using irc.reply() are
irc.checkAuthenticated(source, allowOper=False) supported and returned here, but others are dropped due to protocol limitations."""
args = remote_parser.parse_args(args)
if not args.command:
irc.error('No command given!')
return
netname = args.network
permissions.check_permissions(irc, source, [
# Quite a few permissions are allowed. 'networks.remote' is the global permission,
'networks.remote',
# networks.remote.<network> allows running any command on a specific network,
'networks.remote.%s' % netname,
# networks.remote.<network>.<service> allows running any command on the given service on a
# specific network,
'networks.remote.%s.%s' % (netname, args.service),
# and networks.remote.<network>.<service>.<command> narrows this further into which command
# can be used.
'networks.remote.%s.%s.%s' % (netname, args.service, args.command[0])
])
# XXX: things like 'remote network1 remote network2 echo hi' will crash PyLink if the source network is network1...
global REMOTE_IN_USE
if REMOTE_IN_USE.is_set():
irc.error("The 'remote' command can not be nested.")
return
REMOTE_IN_USE.set()
if netname == irc.name:
# This would actually throw _remote_reply() into a loop, so check for it here...
# XXX: properly fix this.
irc.error("Cannot remote-send a command to the local network; use a normal command!")
REMOTE_IN_USE.clear()
return
try: try:
netname = args[0]
cmd_args = ' '.join(args[1:]).strip()
remoteirc = world.networkobjects[netname] remoteirc = world.networkobjects[netname]
except IndexError: # Arguments not given.
irc.reply('Error: Not enough arguments (needs 2 or more: network name (case sensitive), command name & arguments).')
return
except KeyError: # Unknown network. except KeyError: # Unknown network.
irc.reply('Error: No such network "%s" (case sensitive).' % netname) irc.error('No such network %r (case sensitive).' % netname)
REMOTE_IN_USE.clear()
return return
if not cmd_args: if args.service not in world.services:
irc.reply('No text entered!') irc.error('Unknown service %r.' % args.service)
REMOTE_IN_USE.clear()
return
elif not remoteirc.connected.is_set():
irc.error('Network %r is not connected.' % netname)
REMOTE_IN_USE.clear()
return
elif not world.services[args.service].uids.get(netname):
irc.error('The requested service %r is not available on %r.' % (args.service, netname))
REMOTE_IN_USE.clear()
return return
# Force remoteirc.called_by to something private in order to prevent # Force remoteirc.called_in to something private in order to prevent
# accidental information leakage from replies. # accidental information leakage from replies.
remoteirc.called_by = remoteirc.pseudoclient.uid try:
remoteirc.called_in = remoteirc.called_by = remoteirc.pseudoclient.uid
# Set PyLink's identification to admin. # Set the identification override to the caller's account.
remoteirc.pseudoclient.identified = "<PyLink networks.remote override>" remoteirc.pseudoclient.account = irc.users[source].account
except:
REMOTE_IN_USE.clear()
raise
try: # Remotely call the command (use the PyLink client as a dummy user). def _remote_reply(placeholder_self, text, **kwargs):
remoteirc.callCommand(remoteirc.pseudoclient.uid, cmd_args) """
finally: # Remove the identification override after we finish. reply() rerouter for the 'remote' command.
remoteirc.pseudoclient.identified = '' """
assert irc.name != placeholder_self.name, \
"Refusing to route reply back to the same " \
"network, as this would cause a recursive loop"
log.debug('(%s) networks.remote: re-routing reply %r from network %s', irc.name,
text, placeholder_self.name)
irc.reply("Done.") # Override the source option to make sure the source is valid on the local network.
if 'source' in kwargs:
del kwargs['source']
irc.reply(text, source=irc.pseudoclient.uid, **kwargs)
old_reply = remoteirc._reply
with remoteirc._reply_lock:
try: # Remotely call the command (use the PyLink client as a dummy user).
# Override the remote irc.reply() to send replies HERE.
log.debug('(%s) networks.remote: overriding reply() of IRC object %s', irc.name, netname)
remoteirc._reply = types.MethodType(_remote_reply, remoteirc)
world.services[args.service].call_cmd(remoteirc, remoteirc.pseudoclient.uid,
' '.join(args.command))
finally:
# Restore the original remoteirc.reply()
log.debug('(%s) networks.remote: restoring reply() of IRC object %s', irc.name, netname)
remoteirc._reply = old_reply
# Remove the identification override after we finish.
try:
remoteirc.pseudoclient.account = ''
except:
log.warning('(%s) networks.remote: failed to restore pseudoclient account for %s; '
'did the remote network disconnect while running this command?', irc.name, netname)
REMOTE_IN_USE.clear()
@utils.add_cmd
def reloadproto(irc, source, args):
"""<protocol module name>
Reloads the given protocol module without restart. You will have to manually disconnect and reconnect any network using the module for changes to apply."""
permissions.check_permissions(irc, source, ['networks.reloadproto'])
try:
name = args[0]
except IndexError:
irc.error('Not enough arguments (needs 1: protocol module name)')
return
# Reload the dependency libraries first
importlib.reload(pylinkirc.classes)
log.debug('networks.reloadproto: reloading %s', pylinkirc.classes)
for common_name in pylinkirc.protocols.common_modules:
module = utils._get_protocol_module(common_name)
log.debug('networks.reloadproto: reloading %s', module)
importlib.reload(module)
proto = utils._get_protocol_module(name)
log.debug('networks.reloadproto: reloading %s', proto)
importlib.reload(proto)
irc.reply("Done. You will have to manually disconnect and reconnect any network using the %r module for changes to apply." % name)

View File

@ -1,228 +1,454 @@
""" """
opercmds.py: Provides a subset of network management commands. opercmds.py: Provides a subset of network management commands.
""" """
import argparse
import sys from pylinkirc import utils, world
import os from pylinkirc.coremods import permissions
# Add the base PyLink folder to path, so we can import utils and log. from pylinkirc.log import log
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
# ircmatch library from https://github.com/mammon-ircd/ircmatch # Having a hard limit here is sensible because otherwise it can flood the client or server off.
# (pip install ircmatch) CHECKBAN_MAX_RESULTS = 200
try:
import ircmatch
except ImportError:
ircmatch = None
import utils def _checkban_positiveint(value):
from log import log value = int(value)
if value <= 0 or value > CHECKBAN_MAX_RESULTS:
raise argparse.ArgumentTypeError("%s is not a positive integer between 1 and %s." % (value, CHECKBAN_MAX_RESULTS))
return value
@utils.add_cmd checkban_parser = utils.IRCParser()
def checkban(irc, source, args): checkban_parser.add_argument('banmask')
"""<banmask (nick!user@host or user@host)> [<nick or hostmask to check>] checkban_parser.add_argument('target', nargs='?', default='')
checkban_parser.add_argument('--channel', default='')
checkban_parser.add_argument('--maxresults', type=_checkban_positiveint, default=50)
Oper only. If a nick or hostmask is given, return whether the given banmask will match it. Otherwise, returns a list of connected users that would be affected by such a ban, up to 50 results.""" def checkban(irc, source, args, use_regex=False):
irc.checkAuthenticated(source, allowOper=False) """<banmask> [<target nick or hostmask>] [--channel #channel] [--maxresults <num>]
if ircmatch is None: CHECKBAN provides a ban checker command based on nick!user@host masks, user@host masks, and
irc.reply("Error: missing ircmatch module (install it via 'pip install ircmatch').") PyLink extended targets.
return
try: If a target nick or hostmask is given, this command returns whether the given banmask will match it.
banmask = args[0] Otherwise, it will display a list of connected users matching the banmask.
except IndexError:
irc.reply("Error: Not enough arguments. Needs 1-2: banmask, nick or hostmask to check (optional).")
return
# Casemapping value (0 is rfc1459, 1 is ascii) used by ircmatch. If the --channel argument is given without a target mask, the returned results will only
if irc.proto.casemapping == 'rfc1459': include users in the given channel.
casemapping = 0
else:
casemapping = 1
The --maxresults option configures how many responses will be shown."""
permissions.check_permissions(irc, source, ['opercmds.checkban'])
try: args = checkban_parser.parse_args(args)
targetmask = args[1] if not args.target:
except IndexError: # No hostmask was given, return a list of matched users.
# No hostmask was given, return a list of affected users.
irc.msg(source, "Checking matches for \x02%s\x02:" % banmask, notice=True)
results = 0 results = 0
for uid, userobj in irc.users.copy().items():
targetmask = irc.getHostmask(uid)
if ircmatch.match(casemapping, banmask, targetmask):
if results < 50: # XXX rather arbitrary limit
serverobj = irc.servers[irc.getServer(uid)]
s = "\x02%s\x02 (%s@%s) [%s] {\x02%s\x02}" % (userobj.nick, userobj.ident,
userobj.host, userobj.realname, serverobj.name)
# Always reply in private to prevent information leaks. userlist_func = irc.match_all_re if use_regex else irc.match_all
irc.msg(source, s, notice=True) irc.reply("Checking for hosts that match \x02%s\x02:" % args.banmask, private=True)
results += 1 for uid in userlist_func(args.banmask, channel=args.channel):
if results < args.maxresults:
userobj = irc.users[uid]
s = "\x02%s\x02 (%s@%s) [%s] {\x02%s\x02}" % (userobj.nick, userobj.ident,
userobj.host, userobj.realname, irc.get_friendly_name(irc.get_server(uid)))
# Always reply in private to prevent information leaks.
irc.reply(s, private=True)
results += 1
else: else:
if results: if results:
irc.msg(source, "\x02%s\x02 out of \x02%s\x02 results shown." % irc.reply("\x02%s\x02 out of \x02%s\x02 results shown." %
(min([results, 50]), results), notice=True) (min([results, args.maxresults]), results), private=True)
else: else:
irc.msg(source, "No results found.", notice=True) irc.reply("No results found.", private=True)
else: else:
# Target can be both a nick (of an online user) or a hostmask. # Target can be both a nick (of an online user) or a hostmask. irc.match_host() handles this
uid = irc.nickToUid(targetmask) # automatically.
if uid: if irc.match_host(args.banmask, args.target):
targetmask = irc.getHostmask(uid) irc.reply('Yes, \x02%s\x02 matches \x02%s\x02.' % (args.target, args.banmask))
elif not utils.isHostmask(targetmask):
irc.reply("Error: Invalid nick or hostmask '%s'." % targetmask)
return
if ircmatch.match(casemapping, banmask, targetmask):
irc.reply('Yes, \x02%s\x02 matches \x02%s\x02.' % (targetmask, banmask))
else: else:
irc.reply('No, \x02%s\x02 does not match \x02%s\x02.' % (targetmask, banmask)) irc.reply('No, \x02%s\x02 does not match \x02%s\x02.' % (args.target, args.banmask))
utils.add_cmd(checkban, aliases=('cban', 'trace'))
def checkbanre(irc, source, args):
"""<regular expression> [<target nick or hostmask>] [--channel #channel] [--maxresults <num>]
CHECKBANRE provides a ban checker command based on regular expressions matched against
users' "nick!user@host [gecos]" mask.
If a target nick or hostmask is given, this command returns whether the given banmask will match it.
Otherwise, it will display a list of connected users matching the banmask.
If the --channel argument is given without a target mask, the returned results will only
include users in the given channel.
The --maxresults option configures how many responses will be shown."""
permissions.check_permissions(irc, source, ['opercmds.checkban.re'])
return checkban(irc, source, args, use_regex=True)
utils.add_cmd(checkbanre, aliases=('crban',))
massban_parser = utils.IRCParser()
massban_parser.add_argument('channel')
massban_parser.add_argument('banmask')
# Regarding default ban reason: it's a good idea not to leave in the caller to prevent retaliation...
massban_parser.add_argument('reason', nargs='*', default=["User banned"])
massban_parser.add_argument('--quiet', '-q', action='store_true')
massban_parser.add_argument('--force', '-f', action='store_true')
massban_parser.add_argument('--include-opers', '-o', action='store_true')
def massban(irc, source, args, use_regex=False):
"""<channel> <banmask / exttarget> [<kick reason>] [--quiet/-q] [--force/-f] [--include-opers/-o]
Applies (i.e. kicks affected users) the given PyLink banmask on the specified channel.
The --quiet option can also be given to mass-mute the given user on networks where this is supported
(currently ts6, unreal, and inspircd). No kicks will be sent in this case.
By default, this command will ignore opers. This behaviour can be suppressed using the --include-opers option.
Relay CLAIM checking is used on Relay channels if it is enabled; use the --force option
to override this if needed."""
permissions.check_permissions(irc, source, ['opercmds.massban'])
args = massban_parser.parse_args(args)
reason = ' '.join(args.reason)
if args.force:
permissions.check_permissions(irc, source, ['opercmds.massban.force'])
if args.channel not in irc.channels:
irc.error("Unknown channel %r." % args.channel)
return
elif 'relay' in world.plugins and (not world.plugins['relay'].check_claim(irc, args.channel, source)) and (not args.force):
irc.error("You do not have access to set bans in %s. Ask someone to op you or use the --force option." % args.channel)
return
results = 0
userlist_func = irc.match_all_re if use_regex else irc.match_all
for uid in userlist_func(args.banmask, channel=args.channel):
if irc.is_oper(uid) and not args.include_opers:
irc.reply('Skipping banning \x02%s\x02 because they are opered.' % irc.users[uid].nick)
continue
elif irc.get_service_bot(uid):
irc.reply('Skipping banning \x02%s\x02 because it is a service client.' % irc.users[uid].nick)
continue
# Remove the target's access before banning them.
bans = [('-%s' % irc.cmodes[prefix], uid) for prefix in irc.channels[args.channel].get_prefix_modes(uid) if prefix in irc.cmodes]
# Then, add the actual ban.
bans += [irc.make_channel_ban(uid, ban_type='quiet' if args.quiet else 'ban')]
irc.mode(irc.pseudoclient.uid, args.channel, bans)
try:
irc.call_hooks([irc.pseudoclient.uid, 'OPERCMDS_MASSBAN',
{'target': args.channel, 'modes': bans, 'parse_as': 'MODE'}])
except:
log.exception('(%s) Failed to send process massban hook; some bans may have not '
'been sent to plugins / relay networks!', irc.name)
if not args.quiet:
irc.kick(irc.pseudoclient.uid, args.channel, uid, reason)
# XXX: this better not be blocking...
try:
irc.call_hooks([irc.pseudoclient.uid, 'OPERCMDS_MASSKICK',
{'channel': args.channel, 'target': uid, 'text': reason, 'parse_as': 'KICK'}])
except:
log.exception('(%s) Failed to send process massban hook; some kicks may have not '
'been sent to plugins / relay networks!', irc.name)
results += 1
else:
irc.reply('Banned %s users on %r.' % (results, args.channel))
log.info('(%s) Ran massban%s for %s on %s (%s user(s) removed)', irc.name, 're' if use_regex else '',
irc.get_hostmask(source), args.channel, results)
utils.add_cmd(massban, aliases=('mban',))
def massbanre(irc, source, args):
"""<channel> <regular expression> [<kick reason>] [--quiet/-q] [--include-opers/-o]
Bans users on the specified channel whose "nick!user@host [gecos]" mask matches the given Python-style regular expression.
(https://docs.python.org/3/library/re.html#regular-expression-syntax describes supported syntax)
The --quiet option can also be given to mass-mute the given user on networks where this is supported
(currently ts6, unreal, and inspircd). No kicks will be sent in this case.
By default, this command will ignore opers. This behaviour can be suppressed using the --include-opers option.
\x02Be careful when using this command, as it is easy to make mistakes with regex. Use 'checkbanre'
to check your bans first!\x02
"""
permissions.check_permissions(irc, source, ['opercmds.massban.re'])
return massban(irc, source, args, use_regex=True)
utils.add_cmd(massbanre, aliases=('rban',))
masskill_parser = utils.IRCParser()
masskill_parser.add_argument('banmask')
# Regarding default ban reason: it's a good idea not to leave in the caller to prevent retaliation...
masskill_parser.add_argument('reason', nargs='*', default=["User banned"], type=str)
masskill_parser.add_argument('--akill', '-ak', action='store_true')
masskill_parser.add_argument('--force-kb', '-f', action='store_true')
masskill_parser.add_argument('--include-opers', '-o', action='store_true')
def masskill(irc, source, args, use_regex=False):
"""<banmask / exttarget> [<kill/ban reason>] [--akill/ak] [--force-kb/-f] [--include-opers/-o]
Kills all users matching the given PyLink banmask.
The --akill option can also be given to convert kills to akills, which expire after 7 days.
For relay users, attempts to kill are forwarded as a kickban to every channel where the calling user
meets claim requirements to set a ban (i.e. this is true if you are opped, if your network is in claim list, etc.;
see "help CLAIM" for more specific rules). This can also be extended to all shared channels
the user is in using the --force-kb option (we hope this feature is only used for good).
By default, this command will ignore opers. This behaviour can be suppressed using the --include-opers option.
To properly kill abusers on another network, combine this command with the 'remote' command in the
'networks' plugin and adjust your banmasks accordingly."""
permissions.check_permissions(irc, source, ['opercmds.masskill'])
args = masskill_parser.parse_args(args)
if args.force_kb:
permissions.check_permissions(irc, source, ['opercmds.masskill.force'])
reason = ' '.join(args.reason)
results = killed = 0
userlist_func = irc.match_all_re if use_regex else irc.match_all
seen_users = set()
for uid in userlist_func(args.banmask):
userobj = irc.users[uid]
if irc.is_oper(uid) and not args.include_opers:
irc.reply('Skipping killing \x02%s\x02 because they are opered.' % userobj.nick)
continue
elif irc.get_service_bot(uid):
irc.reply('Skipping killing \x02%s\x02 because it is a service client.' % userobj.nick)
continue
relay = world.plugins.get('relay')
if relay and hasattr(userobj, 'remote'):
# For relay users, forward kill attempts as kickban because we don't want networks k-lining each others' users.
bans = [irc.make_channel_ban(uid)]
for channel in userobj.channels.copy(): # Look in which channels the user appears to be in locally
if (args.force_kb or relay.check_claim(irc, channel, source)):
irc.mode(irc.pseudoclient.uid, channel, bans)
irc.kick(irc.pseudoclient.uid, channel, uid, reason)
# XXX: code duplication with massban.
try:
irc.call_hooks([irc.pseudoclient.uid, 'OPERCMDS_MASSKILL_BAN',
{'target': channel, 'modes': bans, 'parse_as': 'MODE'}])
irc.call_hooks([irc.pseudoclient.uid, 'OPERCMDS_MASSKILL_KICK',
{'channel': channel, 'target': uid, 'text': reason, 'parse_as': 'KICK'}])
except:
log.exception('(%s) Failed to send process massban hook; some kickbans may have not '
'been sent to plugins / relay networks!', irc.name)
if uid not in seen_users: # Don't count users multiple times on different channels
killed += 1
else:
irc.reply("Not kicking \x02%s\x02 from \x02%s\x02 because you don't have CLAIM access. If this is "
"another network's channel, ask someone to op you or use the --force-kb option." % (userobj.nick, channel))
else:
if args.akill: # TODO: configurable length via strings such as "2w3d5h6m3s" - though month and minute clash this way?
if not (userobj.realhost or userobj.ip):
irc.reply("Skipping akill on %s because PyLink doesn't know the real host." % irc.get_hostmask(uid))
continue
irc.set_server_ban(irc.pseudoclient.uid, 604800, host=userobj.realhost or userobj.ip or userobj.host, reason=reason)
else:
irc.kill(irc.pseudoclient.uid, uid, reason)
try:
irc.call_hooks([irc.pseudoclient.uid, 'OPERCMDS_MASSKILL',
{'target': uid, 'parse_as': 'KILL', 'userdata': userobj, 'text': reason}])
except:
log.exception('(%s) Failed to send process massban hook; some kickbans may have not '
'been sent to plugins / relay networks!', irc.name)
killed += 1
results += 1
seen_users.add(uid)
else:
log.info('(%s) Ran masskill%s for %s (%s/%s user(s) removed)', irc.name, 're' if use_regex else '',
irc.get_hostmask(source), killed, results)
irc.reply('Masskilled %s/%s users.' % (killed, results))
utils.add_cmd(masskill, aliases=('mkill',))
def masskillre(irc, source, args):
"""<regular expression> [<kill/ban reason>] [--akill/ak] [--force-kb/-f] [--include-opers/-o]
Kills all users whose "nick!user@host [gecos]" mask matches the given Python-style regular expression.
(https://docs.python.org/3/library/re.html#regular-expression-syntax describes supported syntax)
The --akill option can also be given to convert kills to akills that expire after 7 days.
For relay users, attempts to kill are forwarded as a kickban to every channel where the calling user
meets claim requirements to set a ban (i.e. this is true if you are opped, if your network is in claim list, etc.;
see "help CLAIM" for more specific rules). This can also be extended to all shared channels
the user is in using the --force-kb option (we hope this feature is only used for good).
By default, this command will ignore opers. This behaviour can be suppressed using the --include-opers option.
\x02Be careful when using this command, as it is easy to make mistakes with regex. Use 'checkbanre'
to check your bans first!\x02
"""
permissions.check_permissions(irc, source, ['opercmds.masskill.re'])
return masskill(irc, source, args, use_regex=True)
utils.add_cmd(masskillre, aliases=('rkill',))
@utils.add_cmd @utils.add_cmd
def jupe(irc, source, args): def jupe(irc, source, args):
"""<server> [<reason>] """<server> [<reason>]
Oper-only, jupes the given server.""" Jupes the given server."""
# Check that the caller is either opered or logged in as admin. permissions.check_permissions(irc, source, ['opercmds.jupe'])
irc.checkAuthenticated(source)
try: try:
servername = args[0] servername = args[0]
reason = ' '.join(args[1:]) or "No reason given" reason = ' '.join(args[1:]) or "No reason given"
desc = "Juped by %s: [%s]" % (irc.getHostmask(source), reason) desc = "Juped by %s: [%s]" % (irc.get_hostmask(source), reason)
except IndexError: except IndexError:
irc.reply('Error: Not enough arguments. Needs 1-2: servername, reason (optional).') irc.error('Not enough arguments. Needs 1-2: servername, reason (optional).')
return return
if not utils.isServerName(servername): if not irc.is_server_name(servername):
irc.reply("Error: Invalid server name '%s'." % servername) irc.error("Invalid server name %r." % servername)
return return
sid = irc.proto.spawnServer(servername, desc=desc) sid = irc.spawn_server(servername, desc=desc)
irc.callHooks([irc.pseudoclient.uid, 'OPERCMDS_SPAWNSERVER', irc.call_hooks([irc.pseudoclient.uid, 'OPERCMDS_SPAWNSERVER',
{'name': servername, 'sid': sid, 'text': desc}]) {'name': servername, 'sid': sid, 'text': desc}])
irc.reply("Done.") irc.reply("Done.")
def _try_find_target(irc, nick):
"""
Tries to find the target UID for the given nick, raising LookupError if it doesn't exist or is ambiguous.
"""
try:
int_u = int(nick)
except:
int_u = None
if int_u and int_u in irc.users:
return int_u # Some protocols use numeric UIDs
elif nick in irc.users:
return nick
potential_targets = irc.nick_to_uid(nick, multi=True)
if not potential_targets:
# Whatever we were told to kick doesn't exist!
raise LookupError("No such target %r." % nick)
elif len(potential_targets) > 1:
raise LookupError("Multiple users with the nick %r found: please select the right UID: %s" % (nick, str(potential_targets)))
else:
return potential_targets[0]
@utils.add_cmd @utils.add_cmd
def kick(irc, source, args): def kick(irc, source, args):
"""<source> <channel> <user> [<reason>] """<channel> <user> [<reason>]
Admin only. Kicks <user> from <channel> via <source>, where <source> is either the nick of a PyLink client or the SID of a PyLink server.""" Kicks <user> from the specified channel."""
irc.checkAuthenticated(source, allowOper=False) permissions.check_permissions(irc, source, ['opercmds.kick'])
try: try:
sourcenick = args[0] channel = args[0]
channel = args[1]
target = args[2]
reason = ' '.join(args[3:])
except IndexError:
irc.reply("Error: Not enough arguments. Needs 3-4: source nick, channel, target, reason (optional).")
return
# Convert the source and target nicks to UIDs.
sender = irc.nickToUid(sourcenick) or sourcenick
targetu = irc.nickToUid(target)
if channel not in irc.channels: # KICK only works on channels that exist.
irc.reply("Error: Unknown channel %r." % channel)
return
if (not irc.isInternalClient(sender)) and \
(not irc.isInternalServer(sender)):
# Whatever we were told to send the kick from wasn't valid; try to be
# somewhat user friendly in the error message
irc.reply("Error: No such PyLink client '%s'. The first argument to "
"KICK should be the name of a PyLink client (e.g. '%s'; see "
"'help kick' for details." % (sourcenick,
irc.pseudoclient.nick))
return
elif not targetu:
# Whatever we were told to kick doesn't exist!
irc.reply("Error: No such target nick '%s'." % target)
return
irc.proto.kick(sender, channel, targetu, reason)
irc.callHooks([sender, 'CHANCMDS_KICK', {'channel': channel, 'target': targetu,
'text': reason, 'parse_as': 'KICK'}])
@utils.add_cmd
def kill(irc, source, args):
"""<source> <target> [<reason>]
Admin only. Kills <target> via <source>, where <source> is either the nick of a PyLink client or the SID of a PyLink server."""
irc.checkAuthenticated(source, allowOper=False)
try:
sourcenick = args[0]
target = args[1] target = args[1]
reason = ' '.join(args[2:]) reason = ' '.join(args[2:])
except IndexError: except IndexError:
irc.reply("Error: Not enough arguments. Needs 3-4: source nick, target, reason (optional).") irc.error("Not enough arguments. Needs 2-3: channel, target, reason (optional).")
return
if channel not in irc.channels: # KICK only works on channels that exist.
irc.error("Unknown channel %r." % channel)
return
targetu = _try_find_target(irc, target)
sender = irc.pseudoclient.uid
irc.kick(sender, channel, targetu, reason)
irc.reply("Done.")
irc.call_hooks([sender, 'OPERCMDS_KICK', {'channel': channel, 'target': targetu,
'text': reason, 'parse_as': 'KICK'}])
@utils.add_cmd
def kill(irc, source, args):
"""<target> [<reason>]
Kills the given target."""
permissions.check_permissions(irc, source, ['opercmds.kill'])
try:
target = args[0]
reason = ' '.join(args[1:])
except IndexError:
irc.error("Not enough arguments. Needs 1-2: target, reason (optional).")
return return
# Convert the source and target nicks to UIDs. # Convert the source and target nicks to UIDs.
sender = irc.nickToUid(sourcenick) or sourcenick sender = irc.pseudoclient.uid
targetu = irc.nickToUid(target)
targetu = _try_find_target(irc, target)
if irc.pseudoclient.uid == targetu:
irc.error("Cannot kill the main PyLink client!")
return
userdata = irc.users.get(targetu) userdata = irc.users.get(targetu)
if (not irc.isInternalClient(sender)) and \ reason = "Requested by %s: %s" % (irc.get_friendly_name(source), reason)
(not irc.isInternalServer(sender)):
# Whatever we were told to send the kick from wasn't valid; try to be
# somewhat user friendly in the error message
irc.reply("Error: No such PyLink client '%s'. The first argument to "
"KILL should be the name of a PyLink client (e.g. '%s'; see "
"'help kick' for details." % (sourcenick,
irc.pseudoclient.nick))
return
elif targetu not in irc.users:
# Whatever we were told to kick doesn't exist!
irc.reply("Error: No such nick '%s'." % target)
return
irc.proto.kill(sender, targetu, reason) irc.kill(sender, targetu, reason)
irc.callHooks([sender, 'CHANCMDS_KILL', {'target': targetu, 'text': reason,
'userdata': userdata, 'parse_as': 'KILL'}]) irc.reply("Done.")
irc.call_hooks([source, 'OPERCMDS_KILL', {'target': targetu, 'text': reason,
'userdata': userdata, 'parse_as': 'KILL'}])
@utils.add_cmd @utils.add_cmd
def mode(irc, source, args): def mode(irc, source, args):
"""<channel> <modes> """<channel> <modes>
Oper-only, sets modes <modes> on the target channel.""" Sets the given modes on the target channel."""
# Check that the caller is either opered or logged in as admin. permissions.check_permissions(irc, source, ['opercmds.mode'])
irc.checkAuthenticated(source)
try: try:
target, modes = args[0], args[1:] target, modes = args[0], args[1:]
except IndexError: except IndexError:
irc.reply('Error: Not enough arguments. Needs 2: target, modes to set.') irc.error('Not enough arguments. Needs 2: target, modes to set.')
return return
if target not in irc.channels: if target not in irc.channels:
irc.reply("Error: Unknown channel '%s'." % target) irc.error("Unknown channel %r." % target)
return return
elif not modes: elif not modes:
# No modes were given before parsing (i.e. mode list was blank). # No modes were given before parsing (i.e. mode list was blank).
irc.reply("Error: No valid modes were given.") irc.error("No valid modes were given.")
return return
parsedmodes = utils.parseModes(irc, target, modes) parsedmodes = irc.parse_modes(target, modes)
if not parsedmodes: if not parsedmodes:
# Modes were given but they failed to parse into anything meaningful. # Modes were given but they failed to parse into anything meaningful.
# For example, "mode #somechan +o" would be erroneous because +o # For example, "mode #somechan +o" would be erroneous because +o
# requires an argument! # requires an argument!
irc.reply("Error: No valid modes were given.") irc.error("No valid modes were given.")
return return
irc.proto.mode(irc.pseudoclient.uid, target, parsedmodes) irc.mode(irc.pseudoclient.uid, target, parsedmodes)
# Call the appropriate hooks for plugins like relay. # Call the appropriate hooks for plugins like relay.
irc.callHooks([irc.pseudoclient.uid, 'OPERCMDS_MODEOVERRIDE', irc.call_hooks([irc.pseudoclient.uid, 'OPERCMDS_MODE',
{'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}]) {'target': target, 'modes': parsedmodes, 'parse_as': 'MODE'}])
irc.reply("Done.") irc.reply("Done.")
@ -231,21 +457,60 @@ def mode(irc, source, args):
def topic(irc, source, args): def topic(irc, source, args):
"""<channel> <topic> """<channel> <topic>
Admin only. Updates the topic in a channel.""" Changes the topic in a channel."""
irc.checkAuthenticated(source, allowOper=False) permissions.check_permissions(irc, source, ['opercmds.topic'])
try: try:
channel = args[0] channel = args[0]
topic = ' '.join(args[1:]) topic = ' '.join(args[1:])
except IndexError: except IndexError:
irc.reply("Error: Not enough arguments. Needs 2: channel, topic.") irc.error("Not enough arguments. Needs 2: channel, topic.")
return return
if channel not in irc.channels: if channel not in irc.channels:
irc.reply("Error: Unknown channel %r." % channel) irc.error("Unknown channel %r." % channel)
return return
irc.proto.topic(irc.pseudoclient.uid, channel, topic) irc.topic(irc.pseudoclient.uid, channel, topic)
irc.callHooks([irc.pseudoclient.uid, 'CHANCMDS_TOPIC', irc.reply("Done.")
irc.call_hooks([irc.pseudoclient.uid, 'OPERCMDS_TOPIC',
{'channel': channel, 'text': topic, 'setter': source, {'channel': channel, 'text': topic, 'setter': source,
'parse_as': 'TOPIC'}]) 'parse_as': 'TOPIC'}])
@utils.add_cmd
def chghost(irc, source, args):
"""<user> <new host>
Changes the visible host of the target user."""
_chgfield(irc, source, args, 'host')
@utils.add_cmd
def chgident(irc, source, args):
"""<user> <new ident>
Changes the ident of the target user."""
_chgfield(irc, source, args, 'ident')
@utils.add_cmd
def chgname(irc, source, args):
"""<user> <new name>
Changes the GECOS (realname) of the target user."""
_chgfield(irc, source, args, 'name', 'GECOS')
def _chgfield(irc, source, args, human_field, internal_field=None):
"""Helper function for chghost/chgident/chgname."""
permissions.check_permissions(irc, source, ['opercmds.chg' + human_field])
try:
target = args[0]
new = args[1]
except IndexError:
irc.error("Not enough arguments. Needs 2: target, new %s." % human_field)
return
# Find the user
targetu = _try_find_target(irc, target)
internal_field = internal_field or human_field.upper()
irc.update_client(targetu, internal_field, new)
irc.reply("Done.")

33
plugins/raw.py Normal file
View File

@ -0,0 +1,33 @@
"""
raw.py: Provides a 'raw' command for sending raw text to IRC.
"""
from pylinkirc import utils
from pylinkirc.coremods import permissions
from pylinkirc.log import log
from pylinkirc import conf
@utils.add_cmd
def raw(irc, source, args):
"""<text>
Sends raw text to the IRC server.
Use with caution - This command is only officially supported on Clientbot networks."""
if not conf.conf['pylink'].get("raw_enabled", False):
raise RuntimeError("Raw commands are not supported on this protocol")
# exec.raw is included for backwards compatibility with PyLink 1.x
permissions.check_permissions(irc, source, ['raw.raw', 'exec.raw'])
args = ' '.join(args)
if not args.strip():
irc.reply('No text entered!')
return
# Note: This is loglevel debug so that we don't risk leaking things like
# NickServ passwords on Clientbot networks.
log.debug('(%s) Sending raw text %r to IRC for %s', irc.name, args,
irc.get_hostmask(source))
irc.send(args)
irc.reply("Done.")

File diff suppressed because it is too large Load Diff

274
plugins/relay_clientbot.py Normal file
View File

@ -0,0 +1,274 @@
# relay_clientbot.py: Clientbot extensions for Relay
import shlex
import string
import time
from pylinkirc import conf, utils, world
from pylinkirc.log import log
# Clientbot default styles:
# These use template strings as documented @ https://docs.python.org/3/library/string.html#template-strings
default_styles = {'MESSAGE': '\x02[$netname]\x02 <$mode_prefix$colored_sender> $text',
'KICK': '\x02[$netname]\x02 - $colored_sender$sender_identhost has kicked $target_nick from $channel ($text)',
'PART': '\x02[$netname]\x02 - $colored_sender$sender_identhost has left $channel ($text)',
'JOIN': '\x02[$netname]\x02 - $colored_sender$sender_identhost has joined $channel',
'NICK': '\x02[$netname]\x02 - $colored_sender$sender_identhost is now known as $newnick',
'QUIT': '\x02[$netname]\x02 - $colored_sender$sender_identhost has quit ($text)',
'ACTION': '\x02[$netname]\x02 * $mode_prefix$colored_sender $text',
'NOTICE': '\x02[$netname]\x02 - Notice from $mode_prefix$colored_sender: $text',
'SQUIT': '\x02[$netname]\x02 - Netsplit lost users: $colored_nicks',
'SJOIN': '\x02[$netname]\x02 - Netjoin gained users: $colored_nicks',
'MODE': '\x02[$netname]\x02 - $colored_sender$sender_identhost sets mode $modes on $channel',
'PM': 'PM from $sender on $netname: $text',
'PNOTICE': '<$sender> $text',
}
def color_text(s):
"""
Returns a colorized version of the given text based on a simple hash algorithm.
"""
if not s:
return s
colors = ('03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '13', '15')
hash_output = hash(s.encode())
num = hash_output % len(colors)
return "\x03%s%s\x03" % (colors[num], s)
def cb_relay_core(irc, source, command, args):
"""
This function takes Clientbot events and formats them as text to the target channel / user.
"""
real_command = command.split('_')[-1]
relay = world.plugins.get('relay')
private = False
if irc.pseudoclient and relay:
try:
sourcename = irc.get_friendly_name(source)
except KeyError: # User has left due to /quit
sourcename = args['userdata'].nick
relay_conf = conf.conf.get('relay') or {}
# Be less floody on startup: don't relay non-PRIVMSGs for the first X seconds after connect.
startup_delay = relay_conf.get('clientbot_startup_delay', 20)
target = args.get('target')
if isinstance(target, str):
# Remove STATUSMSG prefixes (e.g. @#channel) before checking whether target is a channel
target = target.lstrip(''.join(irc.prefixmodes.values()))
# Special case for CTCPs.
if real_command == 'MESSAGE':
# CTCP action, format accordingly
if (not args.get('is_notice')) and args['text'].startswith('\x01ACTION ') and args['text'].endswith('\x01'):
args['text'] = args['text'][8:-1]
real_command = 'ACTION'
elif not irc.is_channel(target):
# Target is a user; handle this accordingly.
if relay_conf.get('allow_clientbot_pms'):
real_command = 'PNOTICE' if args.get('is_notice') else 'PM'
private = True
# Other CTCPs are ignored
elif args['text'].startswith('\x01'):
return
elif args.get('is_notice'): # Different syntax for notices
real_command = 'NOTICE'
elif (time.time() - irc.start_ts) < startup_delay:
log.debug('(%s) relay_cb_core: Not relaying %s because of startup delay of %s.', irc.name,
real_command, startup_delay)
return
# Try to fetch the format for the given command from the relay:clientbot_styles:$command
# key, falling back to one defined in default_styles above, and then nothing if not found
# there.
text_template = irc.get_service_options('relay', 'clientbot_styles', dict).get(
real_command, default_styles.get(real_command, ''))
text_template = string.Template(text_template)
if text_template:
if irc.get_service_bot(source):
# HACK: service bots are global and lack the relay state we look for.
# just pretend the message comes from the current network.
log.debug('(%s) relay_cb_core: Overriding network origin to local (source=%s)', irc.name, source)
sourcenet = irc.name
realsource = source
else:
# Get the original client that the relay client source was meant for.
log.debug('(%s) relay_cb_core: Trying to find original sender (user) for %s', irc.name, source)
try:
origuser = relay.get_orig_user(irc, source) or args['userdata'].remote
except (AttributeError, KeyError):
log.debug('(%s) relay_cb_core: Trying to find original sender (server) for %s. serverdata=%s', irc.name, source, args.get('serverdata'))
try:
localsid = args.get('serverdata') or irc.servers[source]
origuser = (localsid.remote, world.networkobjects[localsid.remote].uplink)
except (AttributeError, KeyError):
return
log.debug('(%s) relay_cb_core: Original sender found as %s', irc.name, origuser)
sourcenet, realsource = origuser
try: # Try to get the full network name
netname = conf.conf['servers'][sourcenet]['netname']
except KeyError:
netname = sourcenet
# Figure out where the message is destined to.
stripped_target = target = args.get('channel') or args.get('target')
if isinstance(target, str):
# HACK: cheap fix to prevent @#channel messages from interpreted as non-channel specific
stripped_target = target.lstrip(''.join(irc.prefixmodes.values()))
if target is None or not (irc.is_channel(stripped_target) or private):
# Non-channel specific message (e.g. QUIT or NICK). If this isn't a PM, figure out
# all channels that the sender shares over the relay, and relay them to those
# channels.
userdata = args.get('userdata') or irc.users.get(source)
if not userdata:
# No user data given. This was probably some other global event such as SQUIT.
userdata = irc.pseudoclient
targets = [channel for channel in userdata.channels if relay.get_relay(irc, channel)]
else:
# Pluralize the channel so that we can iterate over it.
targets = [target]
args['channel'] = stripped_target
log.debug('(%s) relay_cb_core: Relaying event %s to channels: %s', irc.name, real_command, targets)
identhost = ''
if source in irc.users:
try:
identhost = irc.get_hostmask(source).split('!')[-1]
except KeyError: # User got removed due to quit
identhost = '%s@%s' % (args['userdata'].ident, args['userdata'].host)
# This is specifically spaced so that ident@host is only shown for users that have
# one, and not servers.
identhost = ' (%s)' % identhost
# $target_nick: Convert the target for kicks, etc. from a UID to a nick
if args.get("target") in irc.users:
args["target_nick"] = irc.get_friendly_name(args['target'])
# Join up modes from their list form
if args.get('modes'):
args['modes'] = irc.join_modes(args['modes'])
mode_prefix = ''
if 'channel' in args:
# Display the real (remote) channel name instead of the local one, if applicable.
args['local_channel'] = args['channel']
log.debug('(%s) relay_clientbot: coersing $channel from %s to %s', irc.name, args['local_channel'], args['channel'])
sourceirc = world.networkobjects.get(sourcenet)
log.debug('(%s) relay_clientbot: Checking prefix modes for %s on %s (relaying to %s)',
irc.name, realsource, sourcenet, args['channel'])
if sourceirc:
args['channel'] = remotechan = relay.get_remote_channel(irc, sourceirc, args['channel'])
if source in irc.users and remotechan in sourceirc.channels and \
realsource in sourceirc.channels[remotechan].users:
# Fetch the prefixmode prefixes (e.g. ~@%) for the sender, if available.
prefixmodes = sourceirc.channels[remotechan].get_prefix_modes(realsource)
log.debug('(%s) relay_clientbot: got prefix modes %s for %s on %s@%s',
irc.name, prefixmodes, realsource, remotechan, sourcenet)
if prefixmodes:
# Only pick the highest prefix.
mode_prefix = sourceirc.prefixmodes.get(
sourceirc.cmodes.get(prefixmodes[0]))
args.update({
'netname': netname, 'sender': sourcename, 'sender_identhost': identhost,
'colored_sender': color_text(sourcename), 'colored_netname': color_text(netname),
'mode_prefix': mode_prefix
})
for target in targets:
cargs = args.copy() # Copy args list to manipulate them in a channel specific way
# $nicks / $colored_nicks: used when the event affects multiple users, such as SJOIN or SQUIT.
# For SJOIN, this is simply a list of nicks. For SQUIT, this is sent as a dict
# mapping channels to lists of nicks, as netsplits aren't channel specific but
# still have to be relayed as such.
nicklist = args.get('nicks')
if nicklist:
# Get channel-specific nick list if relevent.
if isinstance(nicklist, dict):
nicklist = nicklist.get(target, [])
# Ignore if no nicks are affected on the channel.
if not nicklist:
continue
colored_nicks = [color_text(nick) for nick in nicklist]
# Join both the nicks and colored_nicks fields into a comma separated string.
cargs['nicks'] = ', '.join(nicklist)
cargs['colored_nicks'] = ', '.join(colored_nicks)
text = text_template.safe_substitute(cargs)
# PMs are always sent as notice - this prevents unknown command loops with bots.
irc.msg(target, text, loopback=False, notice=private)
utils.add_hook(cb_relay_core, 'CLIENTBOT_MESSAGE')
utils.add_hook(cb_relay_core, 'CLIENTBOT_KICK')
utils.add_hook(cb_relay_core, 'CLIENTBOT_PART')
utils.add_hook(cb_relay_core, 'CLIENTBOT_JOIN')
utils.add_hook(cb_relay_core, 'CLIENTBOT_QUIT')
utils.add_hook(cb_relay_core, 'CLIENTBOT_NICK')
utils.add_hook(cb_relay_core, 'CLIENTBOT_SJOIN')
utils.add_hook(cb_relay_core, 'CLIENTBOT_SQUIT')
utils.add_hook(cb_relay_core, 'RELAY_RAW_MODE')
@utils.add_cmd
def rpm(irc, source, args):
"""<target nick/UID> <text>
Sends PMs to users over Relay, if Clientbot PMs are enabled.
If the target nick has spaces in it, you may quote the nick as "nick".
"""
args = shlex.split(' '.join(args)) # HACK: use shlex.split so that quotes are preserved
try:
target = args[0]
text = ' '.join(args[1:])
except IndexError:
irc.error('Not enough arguments. Needs 2: target nick and text.')
return
relay = world.plugins.get('relay')
if irc.has_cap('can-spawn-clients'):
irc.error('This command is only supported on Clientbot networks. Try /msg %s <text>' % target)
return
elif relay is None:
irc.error('PyLink Relay is not loaded.')
return
elif not text:
irc.error('No text given.')
return
elif not conf.conf.get('relay').get('allow_clientbot_pms'):
irc.error('Private messages with users connected via Clientbot have been '
'administratively disabled.')
return
if target in irc.users:
uids = [target]
else:
uids = irc.nick_to_uid(target, multi=True, filterfunc=lambda u: relay.is_relay_client(irc, u))
if not uids:
irc.error('Unknown user %s.' % target)
return
elif len(uids) > 1:
targets = ['\x02%s\x02: %s @ %s' % (uid, irc.get_hostmask(uid), irc.users[uid].remote[0]) for uid in uids]
irc.error('Please select the target you want to PM: %s' % (', '.join(targets)))
return
else:
assert not irc.is_internal_client(source), "rpm is not allowed from PyLink bots"
# Send the message through relay by faking a hook for its handler.
relay.handle_messages(irc, source, 'RELAY_CLIENTBOT_PRIVMSG', {'target': uids[0], 'text': text})
irc.reply('Message sent.')

125
plugins/servermaps.py Normal file
View File

@ -0,0 +1,125 @@
# servermaps.py: Maps out connected IRC servers.
import collections
from pylinkirc import utils, world
from pylinkirc.coremods import permissions
from pylinkirc.log import log
DEFAULT_PERMISSIONS = {"$ircop": ['servermaps.localmap']}
def main(irc=None):
"""Servermaps plugin main function, called on plugin load."""
# Register our permissions.
permissions.add_default_permissions(DEFAULT_PERMISSIONS)
def die(irc=None):
"""Servermaps plugin die function, called on plugin unload."""
permissions.remove_default_permissions(DEFAULT_PERMISSIONS)
def _percent(num, total):
return '%.1f' % (num/total*100)
def _map(irc, source, args, show_relay=True):
"""[<network>]
Shows the network map for the given network, or the current network if not specified."""
if show_relay:
perm = 'servermaps.map'
else:
perm = 'servermaps.localmap'
permissions.check_permissions(irc, source, [perm])
try:
netname = args[0]
except IndexError:
netname = irc.name
try:
ircobj = world.networkobjects[netname]
except KeyError:
irc.error('no such network %s' % netname)
return
servers = collections.defaultdict(set)
hostsid = ircobj.sid
usercount = len(ircobj.users)
# Iterate over every connected server on every network.
for remotenet, remoteirc in world.networkobjects.items():
for sid, serverobj in remoteirc.servers.copy().items():
if sid == remoteirc.sid: # Don't re-add our own SID to the index
continue
# Save the server as UNDER its uplink.
servers[(remotenet, serverobj.uplink or remoteirc.sid)].add(sid)
log.debug('(%s) servermaps.map servers fetched for %s: %s', irc.name, netname, servers)
reply = lambda text: irc.reply(text, private=True)
def showall(ircobj, sid, hops=0, is_relay_server=False):
log.debug('servermaps: got showall() for SID %s on network %s', sid, ircobj.name)
serverlist = ircobj.servers.copy()
netname = ircobj.name
if hops == 0:
# Show our root server once.
rootusers = len(serverlist[sid].users)
reply('\x02%s\x02[%s]: %s user(s) (%s%%) {hopcount: %d}' % (serverlist[sid].name, sid,
rootusers, _percent(rootusers, usercount), serverlist[sid].hopcount))
log.debug('(%s) servermaps: servers under sid %s: %s', irc.name, sid, servers)
# Every time we descend a server to process its map, raise the hopcount used in formatting.
hops += 1
leaves = servers[(netname, sid)]
for leafcount, leaf in enumerate(leaves):
if is_relay_server and hasattr(serverlist[leaf], 'remote'):
# Don't show relay subservers more than once.
continue
serverusers = len(serverlist[leaf].users)
if is_relay_server:
# Skip showing user data for relay servers.
reply("%s\x02%s\x02[%s] (via PyLink Relay)" %
(' '*hops, serverlist[leaf].name, leaf))
else:
reply("%s\x02%s\x02[%s]: %s user(s) (%s%%) {hopcount: %d}" %
(' '*hops, serverlist[leaf].name, leaf,
serverusers, _percent(serverusers, usercount), serverlist[leaf].hopcount))
showall(ircobj, leaf, hops, is_relay_server=is_relay_server)
if (not is_relay_server) and hasattr(serverlist[leaf], 'remote') and show_relay:
# This is a relay server - display the remote map of the network it represents
relay_server = serverlist[leaf].remote
remoteirc = world.networkobjects[relay_server]
if remoteirc.has_cap('can-track-servers'):
# Only ever show relay subservers once - this prevents infinite loops.
showall(remoteirc, remoteirc.sid, hops=hops, is_relay_server=True)
else:
# For Clientbot links, show the server we're actually connected to.
reply("%s\x02%s\x02 (actual server name)" %
(' '*(hops+1), remoteirc.uplink))
else:
# Afterwards, decrement the hopcount.
hops -= 1
# Start the map at our PyLink server
firstserver = hostsid
showall(ircobj, firstserver)
serverlist = irc.servers
reply('Total %s users on %s local servers - average of %1.f per server' % (usercount, len(serverlist),
usercount/len(serverlist)))
utils.add_cmd(_map, 'map')
@utils.add_cmd
def localmap(irc, source, args):
"""[<network>]
Shows the network map for the given network, or the current network if not specified.
This command does not expand Relay subservers."""
_map(irc, source, args, show_relay=False)

View File

@ -1,28 +1,42 @@
# servprotect.py: Protects against KILL and nick collision floods # servprotect.py: Protects against KILL and nick collision floods
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from expiringdict import ExpiringDict import threading
import utils from pylinkirc import conf, utils
from log import log from pylinkirc.log import log
# TODO: make length and time configurable try:
savecache = ExpiringDict(max_len=5, max_age_seconds=10) from cachetools import TTLCache
killcache = ExpiringDict(max_len=5, max_age_seconds=10) except ImportError:
log.warning('servprotect: expiringdict support is deprecated as of PyLink 3.0; consider installing cachetools instead')
from expiringdict import ExpiringDict as TTLCache
# check for definitions
servprotect_conf = conf.conf.get('servprotect', {})
length = servprotect_conf.get('length', 10)
age = servprotect_conf.get('age', 10)
def _new_cache_dict():
return TTLCache(length, age)
savecache = _new_cache_dict()
killcache = _new_cache_dict()
lock = threading.Lock()
def handle_kill(irc, numeric, command, args): def handle_kill(irc, numeric, command, args):
""" """
Tracks kills against PyLink clients. If too many are received, Tracks kills against PyLink clients. If too many are received,
automatically disconnects from the network. automatically disconnects from the network.
""" """
if killcache.setdefault(irc.name, 1) >= 5:
log.error('(%s) servprotect: Too many kills received, aborting!', irc.name)
irc.disconnect()
log.debug('(%s) servprotect: Incrementing killcache by 1', irc.name) if (args['userdata'] and irc.is_internal_server(args['userdata'].server)) or irc.is_internal_client(args['target']):
killcache[irc.name] += 1 with lock:
if killcache.setdefault(irc.name, 1) >= length:
log.error('(%s) servprotect: Too many kills received, aborting!', irc.name)
irc.disconnect()
log.debug('(%s) servprotect: Incrementing killcache by 1', irc.name)
killcache[irc.name] += 1
utils.add_hook(handle_kill, 'KILL') utils.add_hook(handle_kill, 'KILL')
@ -31,11 +45,13 @@ def handle_save(irc, numeric, command, args):
Tracks SAVEs (nick collision) against PyLink clients. If too many are received, Tracks SAVEs (nick collision) against PyLink clients. If too many are received,
automatically disconnects from the network. automatically disconnects from the network.
""" """
if savecache.setdefault(irc.name, 0) >= 5: if irc.is_internal_client(args['target']):
log.error('(%s) servprotect: Too many nick collisions, aborting!', irc.name) with lock:
irc.disconnect() if savecache.setdefault(irc.name, 0) >= length:
log.error('(%s) servprotect: Too many nick collisions, aborting!', irc.name)
irc.disconnect()
log.debug('(%s) servprotect: Incrementing savecache by 1', irc.name) log.debug('(%s) servprotect: Incrementing savecache by 1', irc.name)
savecache[irc.name] += 1 savecache[irc.name] += 1
utils.add_hook(handle_save, 'SAVE') utils.add_hook(handle_save, 'SAVE')

129
plugins/stats.py Normal file
View File

@ -0,0 +1,129 @@
"""
stats.py: Simple statistics for PyLink IRC Services.
"""
import datetime
import time
from pylinkirc import conf, utils, world
from pylinkirc.coremods import permissions
from pylinkirc.log import log
def timediff(before, now):
"""
Returns the time difference between "before" and "now" as a formatted string.
"""
td = datetime.timedelta(seconds=now-before)
days = td.days
hours, leftover = divmod(td.seconds, 3600)
minutes, seconds = divmod(leftover, 60)
# XXX: I would make this more configurable but it's a lot of work for little gain,
# since there's no strftime for time differences.
return '%d day%s, %02d:%02d:%02d' % (td.days, 's' if td.days != 1 else '',
hours, minutes, seconds)
# From RFC 2822: https://tools.ietf.org/html/rfc2822.html#section-3.3
DEFAULT_TIME_FORMAT = "%a, %d %b %Y %H:%M:%S +0000"
@utils.add_cmd
def uptime(irc, source, args):
"""[<network> / --all]
Returns the uptime for PyLink and the given network's connection (or the current network if not specified).
The --all argument can also be given to show the uptime for all networks."""
permissions.check_permissions(irc, source, ['stats.uptime'])
try:
network = args[0]
except IndexError:
network = irc.name
if network == '--all': # XXX: we really need smart argument parsing some time
# Filter by all connected networks.
ircobjs = {k:v for k,v in world.networkobjects.items() if v.connected.is_set()}
else:
try:
ircobjs = {network: world.networkobjects[network]}
except KeyError:
irc.error("No such network %r." % network)
return
if not world.networkobjects[network].connected.is_set():
irc.error("Network %s is not connected." % network)
return
current_time = int(time.time())
time_format = conf.conf.get('stats', {}).get('time_format', DEFAULT_TIME_FORMAT)
irc.reply("PyLink uptime: \x02%s\x02 (started on %s)" %
(timediff(world.start_ts, current_time),
time.strftime(time_format, time.gmtime(world.start_ts))
)
)
for network, ircobj in sorted(ircobjs.items()):
irc.reply("Connected to %s: \x02%s\x02 (connected on %s)" %
(network,
timediff(ircobj.start_ts, current_time),
time.strftime(time_format, time.gmtime(ircobj.start_ts))
)
)
def handle_stats(irc, source, command, args):
"""/STATS handler. Currently supports the following:
c - link blocks
o - oper blocks (accounts)
u - shows uptime
"""
stats_type = args['stats_type'][0].lower() # stats_type shouldn't be more than 1 char anyways
perms = ['stats.%s' % stats_type]
if stats_type == 'u':
perms.append('stats.uptime') # Consistency
try:
permissions.check_permissions(irc, source, perms)
except utils.NotAuthorizedError as e:
# Note, no irc.error() because this is not a command, but a handler
irc.msg(source, 'Error: %s' % e, notice=True)
return
log.info('(%s) /STATS %s requested by %s', irc.name, stats_type, irc.get_hostmask(source))
def _num(num, text):
irc.numeric(args['target'], num, source, text)
if stats_type == 'c':
# 213/RPL_STATSCLINE: "C <host> * <name> <port> <class>"
for netname, serverdata in sorted(conf.conf['servers'].items()):
# We're cramming as much as we can into the class field...
_num(213, "C %s * %s %s [%s:%s:%s]" %
(serverdata.get('ip', '0.0.0.0'),
netname,
serverdata.get('port', 0),
serverdata['protocol'],
'ssl' if serverdata.get('ssl') else 'no-ssl',
serverdata.get('encoding', 'utf-8'))
)
elif stats_type == 'o':
# 243/RPL_STATSOLINE: "O <hostmask> * <nick> [:<info>]"
# New style accounts only!
for accountname, accountdata in conf.conf['login'].get('accounts', {}).items():
networks = accountdata.get('networks', [])
if irc.name in networks or not networks:
hosts = ' '.join(accountdata.get('hosts', ['*@*']))
needoper = 'needoper' if accountdata.get('require_oper') else ''
_num(243, "O %s * %s :%s" % (hosts, accountname, needoper))
elif stats_type == 'u':
# 242/RPL_STATSUPTIME: ":Server Up <days> days <hours>:<minutes>:<seconds>"
_num(242, ':Server Up %s' % timediff(world.start_ts, int(time.time())))
else:
log.info('(%s) Unknown /STATS type %r requested by %s', irc.name, stats_type, irc.get_hostmask(source))
_num(219, "%s :End of /STATS report" % stats_type)
utils.add_hook(handle_stats, 'STATS')

2
protocols/__init__.py Normal file
View File

@ -0,0 +1,2 @@
# Abstract modules containing shared protocol code; modules higher in the hierarchy go first
common_modules = ['ircs2s_common', 'ts6_common']

Some files were not shown because too many files have changed in this diff Show More