mirror of
https://github.com/jlu5/PyLink.git
synced 2025-12-21 18:37:58 +01:00
Compare commits
2397 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43e41cdfa5 | ||
|
|
c453926718 | ||
|
|
523c1d2b13 | ||
|
|
ab982662b1 | ||
|
|
fb2327d4e7 | ||
|
|
8c4efc030a | ||
|
|
499a0dd403 | ||
|
|
ca2d603fa1 | ||
|
|
cd2ecc3853 | ||
|
|
a0a2cda49f | ||
|
|
6391d6c282 | ||
|
|
563b48e9c5 | ||
|
|
2915bf2236 | ||
|
|
d6de0d97f3 | ||
|
|
171eccf9c7 | ||
|
|
d3ccdca3d1 | ||
|
|
3155172fc8 | ||
|
|
243efbd0f8 | ||
|
|
8d01eaa5c8 | ||
|
|
ada130b1cd | ||
|
|
e6401a19df | ||
|
|
ac7339e460 | ||
|
|
f55057092a | ||
|
|
46cc621df1 | ||
|
|
bc3a7abe02 | ||
|
|
3f59dcd884 | ||
|
|
fc971aa679 | ||
|
|
e25e3834a8 | ||
|
|
2ae72d6723 | ||
|
|
8322817395 | ||
|
|
6ba99b302f | ||
|
|
92f60ecb4b | ||
|
|
075c1d141d | ||
|
|
d94593e4f6 | ||
|
|
8db238e869 | ||
|
|
da7f9611bc | ||
|
|
ed1644a636 | ||
|
|
1b433b741b | ||
|
|
53e30520b8 | ||
|
|
ba5f89d03c | ||
|
|
88294ff0b0 | ||
|
|
0b6845fa92 | ||
|
|
4500e27931 | ||
|
|
537c643ed0 | ||
|
|
d9aa5e9869 | ||
|
|
17fccfeca9 | ||
|
|
c5541b58e5 | ||
|
|
5eca51d979 | ||
|
|
15d51b3455 | ||
|
|
b254a7f971 | ||
|
|
d53ed4f25d | ||
|
|
cc2298d0be | ||
|
|
8ee0f3fdab | ||
|
|
e0cc238001 | ||
|
|
b02aadf378 | ||
|
|
d50de12834 | ||
|
|
2aa00d6efc | ||
|
|
16a7cef1aa | ||
|
|
43532fd1cc | ||
|
|
cb0af148d8 | ||
|
|
dcd0a28c89 | ||
|
|
7204ef1cf1 | ||
|
|
88116dbe8d | ||
|
|
b49d5775e2 | ||
|
|
03c9c71dc3 | ||
|
|
26bdc90781 | ||
|
|
6bf66f9e4d | ||
|
|
84b73bb89f | ||
|
|
9470e9329a | ||
|
|
947732580a | ||
|
|
3d5f9685c5 | ||
|
|
c53ee0a80c | ||
|
|
0cba40e4c5 | ||
|
|
c14fc5cf07 | ||
|
|
ba22b18cc4 | ||
|
|
a143d98ac1 | ||
|
|
a19f257bd8 | ||
|
|
380854f88a | ||
|
|
8ffd787075 | ||
|
|
b4d7883a71 | ||
|
|
f17540a7e2 | ||
|
|
b433fed718 | ||
|
|
475349dc39 | ||
|
|
a8f0bd4fb7 | ||
|
|
f7e84e7816 | ||
|
|
bdfeec09c6 | ||
|
|
7ce8f41f95 | ||
|
|
99c379e32f | ||
|
|
908dcb4873 | ||
|
|
b083b6dede | ||
|
|
5c3306bcff | ||
|
|
7c0deaa684 | ||
|
|
574a3056a3 | ||
|
|
0e13243d02 | ||
|
|
6e1352dfd3 | ||
|
|
34cd0ea7d5 | ||
|
|
ea2925ef37 | ||
|
|
0e10b62705 | ||
|
|
f01fada92f | ||
|
|
5a487114b6 | ||
|
|
52730268b0 | ||
|
|
4d24aa236b | ||
|
|
a178a92d6a | ||
|
|
bfe8a23dbc | ||
|
|
b45fe4c121 | ||
|
|
ae01c5e418 | ||
|
|
a79354cd52 | ||
|
|
847c26cd7b | ||
|
|
44770fb6d1 | ||
|
|
594b7124ff | ||
|
|
dadf2c3211 | ||
|
|
b24dc206e0 | ||
|
|
b495cb4c5e | ||
|
|
b1b14dfb55 | ||
|
|
b15c885dbc | ||
|
|
6a71ca64ac | ||
|
|
0a264b430e | ||
|
|
daf496cc96 | ||
|
|
b92f39d79c | ||
|
|
b9561500fd | ||
|
|
72c57d433a | ||
|
|
7eff7861ed | ||
|
|
1a89813cd4 | ||
|
|
d73e2fc209 | ||
|
|
8a48d4d8cc | ||
|
|
7bf1a9e08d | ||
|
|
85922cd376 | ||
|
|
4ceeb1630f | ||
|
|
41497a8a13 | ||
|
|
2c53ce0682 | ||
|
|
d28a9681ac | ||
|
|
297d31dab2 | ||
|
|
f99be51515 | ||
|
|
da67d6c42f | ||
|
|
1623462b73 | ||
|
|
e0d82cdf3d | ||
|
|
9ec83f3995 | ||
|
|
601b811912 | ||
|
|
72e96156b5 | ||
|
|
4095eea3a7 | ||
|
|
8cf1beb183 | ||
|
|
52001ac82d | ||
|
|
c8ba6291a6 | ||
|
|
083dc6a58f | ||
|
|
462fa91622 | ||
|
|
b803c23b57 | ||
|
|
899443d2fe | ||
|
|
fe4bea2948 | ||
|
|
01705f8393 | ||
|
|
943168df53 | ||
|
|
aba198dbd6 | ||
|
|
188d0f647e | ||
|
|
19f7ba38b3 | ||
|
|
c1859b64fa | ||
|
|
f9368dd5cc | ||
|
|
2baec4c65a | ||
|
|
ee4997dd72 | ||
|
|
ebce431ba4 | ||
|
|
a1f3af9099 | ||
|
|
d93c071446 | ||
|
|
9168880204 | ||
|
|
c2b5966739 | ||
|
|
b685f416f6 | ||
|
|
0533827ddf | ||
|
|
32219ccb78 | ||
|
|
808e1d1f5a | ||
|
|
304631ebd0 | ||
|
|
27eed3334b | ||
|
|
c1dbfdab48 | ||
|
|
da58669de5 | ||
|
|
6ad34672d3 | ||
|
|
46f081e19b | ||
|
|
cb4d2cc384 | ||
|
|
3eb90fa65c | ||
|
|
fe51f71a6e | ||
|
|
087ca0947b | ||
|
|
a885b79306 | ||
|
|
575cff297d | ||
|
|
e5493eac87 | ||
|
|
26bfc06869 | ||
|
|
d3f2a370da | ||
|
|
a8832a5f93 | ||
|
|
0b8ed2dae9 | ||
|
|
452a47d4f1 | ||
|
|
d57b121600 | ||
|
|
e0a618f317 | ||
|
|
e02ab9f2ff | ||
|
|
2cdcd8e193 | ||
|
|
fae63d77b2 | ||
|
|
f3569b4fd9 | ||
|
|
5d579481aa | ||
|
|
6b78b45b20 | ||
|
|
1a692f55ad | ||
|
|
4a8c96c883 | ||
|
|
07d8c8828a | ||
|
|
80188c3673 | ||
|
|
19d794a6f5 | ||
|
|
6ac2daebfa | ||
|
|
8e85fa935d | ||
|
|
350ba5f89c | ||
|
|
edd27eea41 | ||
|
|
bcdd26926d | ||
|
|
4bd334e2b8 | ||
|
|
e3e0eac747 | ||
|
|
c7fd037879 | ||
|
|
35b38dfb05 | ||
|
|
b6cf09ae52 | ||
|
|
93f608a504 | ||
|
|
9ad2b03833 | ||
|
|
19c7dce931 | ||
|
|
37822fda42 | ||
|
|
4eb0420378 | ||
|
|
9a74626d62 | ||
|
|
c1158fd33a | ||
|
|
caa94f983f | ||
|
|
729abbd6bf | ||
|
|
61ca8dd781 | ||
|
|
df468064d6 | ||
|
|
c56713887e | ||
|
|
798fc7b0bf | ||
|
|
1852ff5774 | ||
|
|
30f7a77d18 | ||
|
|
957697d275 | ||
|
|
c5b94cdf21 | ||
|
|
f2b6de8889 | ||
|
|
ed4404bf4b | ||
|
|
dcab011673 | ||
|
|
94cd1d8f22 | ||
|
|
042d11d7ba | ||
|
|
a6205e1ebc | ||
|
|
74566c3aab | ||
|
|
9f31a0a587 | ||
|
|
b7d93fe86a | ||
|
|
46d1738f66 | ||
|
|
6054476900 | ||
|
|
c7e4c05cbd | ||
|
|
e25a5df4db | ||
|
|
0836845ff9 | ||
|
|
dfc4e4954a | ||
|
|
fe95a4a571 | ||
|
|
fc4a16eda1 | ||
|
|
242267a4a2 | ||
|
|
011d70e816 | ||
|
|
3b07f8ab2b | ||
|
|
3d039b78e2 | ||
|
|
886a98a396 | ||
|
|
c62ec4fde0 | ||
|
|
3d5e7cd1c1 | ||
|
|
eba5d91299 | ||
|
|
8b298df362 | ||
|
|
42a2061783 | ||
|
|
dd58dcf377 | ||
|
|
04d36e93a1 | ||
|
|
2b04050bf5 | ||
|
|
8d2ae6af50 | ||
|
|
762b47120d | ||
|
|
722881bc33 | ||
|
|
917543dd12 | ||
|
|
b260a28c8f | ||
|
|
12784a4b5b | ||
|
|
ea753774fd | ||
|
|
1c0ea24acd | ||
|
|
50e9d2d959 | ||
|
|
26fa5d38a2 | ||
|
|
ec379a6e81 | ||
|
|
c43d13ef61 | ||
|
|
66485ec6a2 | ||
|
|
3d69b7f4e8 | ||
|
|
ad4cb9561c | ||
|
|
08386a8ef7 | ||
|
|
db6d5d6d05 | ||
|
|
42e1eda51a | ||
|
|
4276607ee4 | ||
|
|
0fe8a8d51a | ||
|
|
6f617cb068 | ||
|
|
44a364df98 | ||
|
|
e3d72c43a4 | ||
|
|
d082495297 | ||
|
|
0273faf933 | ||
|
|
81bf6480df | ||
|
|
7e088dfacb | ||
|
|
f90b0c8577 | ||
|
|
a8bb5f66e5 | ||
|
|
ad9a51fc33 | ||
|
|
13be40e08b | ||
|
|
739c87ef50 | ||
|
|
9056799633 | ||
|
|
5ca57cb3c1 | ||
|
|
41cbd455d6 | ||
|
|
8f10af9942 | ||
|
|
28166ed4ee | ||
|
|
71353a29c2 | ||
|
|
0ffbaa8e5e | ||
|
|
2c028e2762 | ||
|
|
63d63c0137 | ||
|
|
88f45fb1d5 | ||
|
|
c9176a06fc | ||
|
|
1780271dd0 | ||
|
|
190e51211f | ||
|
|
4f17a7986b | ||
|
|
24f85acfb8 | ||
|
|
30387c9ed5 | ||
|
|
ba17821af4 | ||
|
|
9d459fab92 | ||
|
|
55360dd0b2 | ||
|
|
96815a0a32 | ||
|
|
ac8b7babf1 | ||
|
|
a9f59307c9 | ||
|
|
11b65ee809 | ||
|
|
cfbadb4539 | ||
|
|
11e7589304 | ||
|
|
76f534ce84 | ||
|
|
873283e61e | ||
|
|
a9b8bfe94d | ||
|
|
5731a301ce | ||
|
|
d089b8d40e | ||
|
|
5e1da09901 | ||
|
|
6e7c58ee36 | ||
|
|
61c8677802 | ||
|
|
23cb7c173a | ||
|
|
52f588c920 | ||
|
|
82ce9aac6c | ||
|
|
cb7708e095 | ||
|
|
852c257e88 | ||
|
|
bf9eb8d4ea | ||
|
|
61d559926f | ||
|
|
d01a9fe9b4 | ||
|
|
aa5412712a | ||
|
|
32e9cc689e | ||
|
|
6462ae3a3c | ||
|
|
77febfe69f | ||
|
|
94a345423e | ||
|
|
eb231a2aad | ||
|
|
31a65697a3 | ||
|
|
6ceaabe092 | ||
|
|
5a482118b8 | ||
|
|
5c4fba653f | ||
|
|
dac8410b63 | ||
|
|
12d1412cba | ||
|
|
60b7894cd6 | ||
|
|
0b3793380b | ||
|
|
1ee93d2cc4 | ||
|
|
767ff15200 | ||
|
|
57faa1443a | ||
|
|
20b3a61cd6 | ||
|
|
7fb4c8da04 | ||
|
|
724a71e1b2 | ||
|
|
97603fe169 | ||
|
|
4d39ad1c84 | ||
|
|
a3e18081a6 | ||
|
|
2f4476eb0c | ||
|
|
667fa610ec | ||
|
|
e929fda293 | ||
|
|
d5fd7b03f5 | ||
|
|
81170e8062 | ||
|
|
fac6fe506f | ||
|
|
119873a9ed | ||
|
|
298913200c | ||
|
|
79b3387bcb | ||
|
|
5838d88404 | ||
|
|
8a096e537c | ||
|
|
dda8a2a081 | ||
|
|
874dfaf3f1 | ||
|
|
49badd1665 | ||
|
|
6a2a6a769f | ||
|
|
e95b11d329 | ||
|
|
51f441085e | ||
|
|
c91b5f74ea | ||
|
|
9cf507183d | ||
|
|
12f6bb5e18 | ||
|
|
e7b0458091 | ||
|
|
ed50202cf2 | ||
|
|
9273dd459b | ||
|
|
9a5e67412e | ||
|
|
f1ce8351b9 | ||
|
|
d844ff5186 | ||
|
|
44aa9af235 | ||
|
|
6b65ab5f88 | ||
|
|
2246aea13c | ||
|
|
584b7e3712 | ||
|
|
bf1f8210bd | ||
|
|
a7196d7b79 | ||
|
|
16ac91a718 | ||
|
|
d356b53425 | ||
|
|
0199daec76 | ||
|
|
a1783ed2be | ||
|
|
b5884d4cb3 | ||
|
|
0eb0c49cb1 | ||
|
|
04b17c30e3 | ||
|
|
f9611ef6bc | ||
|
|
d8c1511b28 | ||
|
|
a5b77c18dd | ||
|
|
3208782225 | ||
|
|
5b321f9f6f | ||
|
|
d3fc95953e | ||
|
|
d310abeec3 | ||
|
|
66125530ef | ||
|
|
1b26c17d81 | ||
|
|
aacb65ab9e | ||
|
|
71a24b8b9f | ||
|
|
5ffc629bce | ||
|
|
bba235bba2 | ||
|
|
c1f37c2236 | ||
|
|
86b93ea969 | ||
|
|
5a9b870e00 | ||
|
|
a520496f81 | ||
|
|
86bfda7281 | ||
|
|
a74fe9bf08 | ||
|
|
b2e85fa385 | ||
|
|
75b0ae6054 | ||
|
|
0c55569c1f | ||
|
|
0ccaac595b | ||
|
|
348dc7348c | ||
|
|
d015e1e41a | ||
|
|
8362e5f234 | ||
|
|
f90ec284a4 | ||
|
|
bdd568f75b | ||
|
|
a548ae0714 | ||
|
|
763ffcf903 | ||
|
|
c48846727e | ||
|
|
420f523dfd | ||
|
|
e340f6e9a2 | ||
|
|
e037b927f8 | ||
|
|
086a5f4496 | ||
|
|
bbf1b34b12 | ||
|
|
a543de9d73 | ||
|
|
5d11774442 | ||
|
|
c8b8762c12 | ||
|
|
5e1cb232b0 | ||
|
|
26361c4cc9 | ||
|
|
e5f817fc95 | ||
|
|
ab9df93898 | ||
|
|
b26d75a6a8 | ||
|
|
fefd5a1f6b | ||
|
|
68837aa927 | ||
|
|
17cd7af22d | ||
|
|
17f0b09eb2 | ||
|
|
8fa53f60cb | ||
|
|
76c0db15c4 | ||
|
|
e38cd0ada2 | ||
|
|
4524aebbac | ||
|
|
d3125d9a8f | ||
|
|
5ea33baa8e | ||
|
|
040b009fcb | ||
|
|
c3bb0f7aca | ||
|
|
a98dd36810 | ||
|
|
56c035a1f5 | ||
|
|
b2421f5e15 | ||
|
|
579b5ce93f | ||
|
|
8386edc6d5 | ||
|
|
76b58c4432 | ||
|
|
77fd9475b6 | ||
|
|
8c42825612 | ||
|
|
5617224780 | ||
|
|
deff6d077d | ||
|
|
1b68bfadc6 | ||
|
|
2ca9de2ea8 | ||
|
|
18f108c328 | ||
|
|
ed5d46e28a | ||
|
|
a30921eeb8 | ||
|
|
3bea214cb0 | ||
|
|
2e3317ce07 | ||
|
|
7d56b30582 | ||
|
|
5ecbc2750e | ||
|
|
1a97a32ef5 | ||
|
|
93fef9b923 | ||
|
|
73d0e153cf | ||
|
|
9466813ba1 | ||
|
|
5f9904126a | ||
|
|
17ffd1f640 | ||
|
|
372e7fb405 | ||
|
|
8608c72b16 | ||
|
|
c919c523dc | ||
|
|
f8e3cfa346 | ||
|
|
ebf7443d97 | ||
|
|
de62b2e77a | ||
|
|
f9d21c2b10 | ||
|
|
0ae7eb2563 | ||
|
|
b1248524a9 | ||
|
|
31a0d36990 | ||
|
|
180da83b4e | ||
|
|
f82ddb5336 | ||
|
|
0edbeb7fad | ||
|
|
f4de604b7d | ||
|
|
6085b21e48 | ||
|
|
10416013e8 | ||
|
|
18bc1942e5 | ||
|
|
b0188dab92 | ||
|
|
7b744655ee | ||
|
|
8cc838e5ca | ||
|
|
6f3813d3a4 | ||
|
|
06d57a5b28 | ||
|
|
88bd9b2791 | ||
|
|
d4bf407c5d | ||
|
|
b202954be4 | ||
|
|
fee64ece04 | ||
|
|
141e941fcd | ||
|
|
9578fd5ac3 | ||
|
|
1e0b0dbc70 | ||
|
|
e76c48f24c | ||
|
|
5b94a10c67 | ||
|
|
b120e2d701 | ||
|
|
186b73c72d | ||
|
|
df6562dcff | ||
|
|
cb774ac3da | ||
|
|
e65d84960a | ||
|
|
8cc4527ff7 | ||
|
|
83aa3d262c | ||
|
|
62fcdf880c | ||
|
|
32acc27967 | ||
|
|
c8b2a676fd | ||
|
|
08a7b5c837 | ||
|
|
5ac283f018 | ||
|
|
1ab5d614c0 | ||
|
|
e3a935d0b7 | ||
|
|
f20fa5e995 | ||
|
|
108d4b86d9 | ||
|
|
2df3dc280c | ||
|
|
b72420a8aa | ||
|
|
ec3a94c4ca | ||
|
|
9e936f1612 | ||
|
|
73261a31bd | ||
|
|
b9f782868c | ||
|
|
a66a9b6336 | ||
|
|
c9c937e7a7 | ||
|
|
1fb2a90580 | ||
|
|
20bbf531e6 | ||
|
|
559b262db8 | ||
|
|
f87e646f35 | ||
|
|
73322bd9ba | ||
|
|
244c4fe0eb | ||
|
|
0ac5d424d8 | ||
|
|
5d098f57d7 | ||
|
|
13c315c9a2 | ||
|
|
28862281fe | ||
|
|
741e2c8ece | ||
|
|
fc275cfdca | ||
|
|
aa4cedd945 | ||
|
|
fb6aa88d83 | ||
|
|
77a6d69f29 | ||
|
|
16f630560e | ||
|
|
613e6412a2 | ||
|
|
30c1980b59 | ||
|
|
0ae4aea133 | ||
|
|
f1b3d8d0ad | ||
|
|
d1ac33a1af | ||
|
|
64a98120bf | ||
|
|
3120fa5396 | ||
|
|
451db74f0c | ||
|
|
f54382534c | ||
|
|
b50ae89acc | ||
|
|
f3c2149d7a | ||
|
|
0c19b3719e | ||
|
|
c44aa64503 | ||
|
|
d46c494351 | ||
|
|
b5133aebbb | ||
|
|
940ff357c9 | ||
|
|
a425015c13 | ||
|
|
19c9d4031d | ||
|
|
83dd37c4e7 | ||
|
|
f06a8f09b4 | ||
|
|
83a7f14b5a | ||
|
|
eb078056e1 | ||
|
|
030facdb75 | ||
|
|
074019b77a | ||
|
|
0ce80f0ede | ||
|
|
09d2f99855 | ||
|
|
2f6c8d2938 | ||
|
|
c71e9a6410 | ||
|
|
5a0cb9a4ff | ||
|
|
8aa67b93fa | ||
|
|
bf4863eb6d | ||
|
|
72c2fa38e9 | ||
|
|
c7da7f0025 | ||
|
|
61d7bf18d3 | ||
|
|
2532042506 | ||
|
|
01c22aac21 | ||
|
|
92be421fad | ||
|
|
8994811f54 | ||
|
|
e9fe15bd7d | ||
|
|
b46ab844fe | ||
|
|
7476c6cf05 | ||
|
|
8eba402a33 | ||
|
|
d0568a6ad7 | ||
|
|
8a09f321da | ||
|
|
e96081aa6e | ||
|
|
af744123e6 | ||
|
|
0ead868546 | ||
|
|
4bfcd52731 | ||
|
|
9967b7d9fe | ||
|
|
041b6aecb6 | ||
|
|
89a7f59262 | ||
|
|
236d800b47 | ||
|
|
601259c771 | ||
|
|
5623c06b8f | ||
|
|
78d1d20856 | ||
|
|
281ac7aa31 | ||
|
|
47052a3bba | ||
|
|
3e07239db4 | ||
|
|
dd8f9411b6 | ||
|
|
3825b93dee | ||
|
|
4d9fbc55ba | ||
|
|
3c9dac7e6b | ||
|
|
4cdc19ac78 | ||
|
|
2f12a5b710 | ||
|
|
2cb4a06e64 | ||
|
|
25d24e9bb2 | ||
|
|
bbc7a981ba | ||
|
|
c82b12d9d6 | ||
|
|
c7159b9cad | ||
|
|
16faac83eb | ||
|
|
6bcf7d325f | ||
|
|
138a52611e | ||
|
|
80cbd7a257 | ||
|
|
84dbca4bda | ||
|
|
655221491c | ||
|
|
0202d88124 | ||
|
|
66ec3d9755 | ||
|
|
170d793939 | ||
|
|
3266e1a430 | ||
|
|
c33f7437ef | ||
|
|
5339ddcf08 | ||
|
|
95f806dc46 | ||
|
|
3e16469b25 | ||
|
|
95dbacdba6 | ||
|
|
8200d92d23 | ||
|
|
f75b1eb356 | ||
|
|
7586989763 | ||
|
|
d6952f0361 | ||
|
|
ac4296b56b | ||
|
|
57d7a70933 | ||
|
|
c0a061eff3 | ||
|
|
b14f52b547 | ||
|
|
481561f972 | ||
|
|
7dec2d15db | ||
|
|
b9a66244f0 | ||
|
|
2ddb0ee18f | ||
|
|
7d5d5a385e | ||
|
|
b5735702f7 | ||
|
|
79ff9f23fb | ||
|
|
58e6527719 | ||
|
|
c54bb557dd | ||
|
|
78b515144f | ||
|
|
9d21a5269f | ||
|
|
c978e1c52f | ||
|
|
310ad345a3 | ||
|
|
22efe1384c | ||
|
|
a6c1beaad0 | ||
|
|
557f1578bc | ||
|
|
79143a1e40 | ||
|
|
c5970ba26d | ||
|
|
1fbe3c6891 | ||
|
|
209fa9722c | ||
|
|
597685d77c | ||
|
|
a24d4d5b9c | ||
|
|
59d52920f3 | ||
|
|
77eb9b4060 | ||
|
|
6d08e0b953 | ||
|
|
f274088ea0 | ||
|
|
93d590fdea | ||
|
|
7a1dcbd460 | ||
|
|
be8e4be49d | ||
|
|
335fb352ec | ||
|
|
29cda543e4 | ||
|
|
22b2410913 | ||
|
|
22f781189d | ||
|
|
84187a07ec | ||
|
|
0bc24c94b2 | ||
|
|
bbb36cd956 | ||
|
|
a085aed924 | ||
|
|
815535d76b | ||
|
|
814dd9a3c5 | ||
|
|
ec8f744449 | ||
|
|
bb1334696c | ||
|
|
c11a476257 | ||
|
|
b522967760 | ||
|
|
f10f5bee52 | ||
|
|
989259af97 | ||
|
|
5172841378 | ||
|
|
91b86ce0e4 | ||
|
|
fb6c3bf6d5 | ||
|
|
ab70d7c8fb | ||
|
|
8100a4cea6 | ||
|
|
30bcd8ca79 | ||
|
|
0151f77f7b | ||
|
|
10d2fb93ed | ||
|
|
0033612fa3 | ||
|
|
f7ab2564fe | ||
|
|
180bfa9917 | ||
|
|
5bffe67416 | ||
|
|
b7b49769e0 | ||
|
|
5733418380 | ||
|
|
b6bac994c6 | ||
|
|
92460716d1 | ||
|
|
ccc9f8e5c8 | ||
|
|
8f9b56e9d9 | ||
|
|
c49147f232 | ||
|
|
57f77c676d | ||
|
|
9f39e484da | ||
|
|
0ca185fada | ||
|
|
5a00454a8d | ||
|
|
f12318b5dc | ||
|
|
1413aa6042 | ||
|
|
054680c806 | ||
|
|
9cca695d14 | ||
|
|
d172831805 | ||
|
|
03103bea14 | ||
|
|
87fdb1dde1 | ||
|
|
9e212fc0a4 | ||
|
|
3e656cd943 | ||
|
|
1cdf16f5c9 | ||
|
|
bea2ea8ebd | ||
|
|
9e3f412f0b | ||
|
|
ad5a11bf34 | ||
|
|
a2783d74c5 | ||
|
|
1c3f71ac1b | ||
|
|
d5d94f86e8 | ||
|
|
6a90e99de4 | ||
|
|
e8e26daf05 | ||
|
|
5d25b3c105 | ||
|
|
3c0809dce0 | ||
|
|
50f8cde694 | ||
|
|
2aca4e39c1 | ||
|
|
a2e793755f | ||
|
|
5c9639b4a9 | ||
|
|
390b7a327a | ||
|
|
3d61bfd114 | ||
|
|
0c2927fb1e | ||
|
|
804791b8af | ||
|
|
0b0da2cfe6 | ||
|
|
9cdb224c02 | ||
|
|
c40250330d | ||
|
|
e68db3689d | ||
|
|
2e66b9bde6 | ||
|
|
4a01948647 | ||
|
|
37be73d39c | ||
|
|
81bd1e8474 | ||
|
|
1405b01597 | ||
|
|
484822e5d7 | ||
|
|
7114c6942f | ||
|
|
8321485315 | ||
|
|
3f7e2328fe | ||
|
|
ea84497359 | ||
|
|
a425f873b5 | ||
|
|
ccbd79a95c | ||
|
|
18c1a277f5 | ||
|
|
74848853ac | ||
|
|
9f6e4306cd | ||
|
|
84f6190478 | ||
|
|
28a62f629a | ||
|
|
09c8b03705 | ||
|
|
5fd216c720 | ||
|
|
d608661a33 | ||
|
|
8000d51453 | ||
|
|
e446e0e27b | ||
|
|
44be5910e0 | ||
|
|
6bb2198710 | ||
|
|
67dea6f748 | ||
|
|
58b717a2a0 | ||
|
|
31c96bd1ed | ||
|
|
06ee01b7a7 | ||
|
|
7a51220309 | ||
|
|
ec9063b9e8 | ||
|
|
bcb0fecfa8 | ||
|
|
ce3b1152b2 | ||
|
|
7a32e7d8a2 | ||
|
|
d5d140f4b0 | ||
|
|
f8abdd1244 | ||
|
|
043a147b41 | ||
|
|
3d661c9713 | ||
|
|
ba4e0aed85 | ||
|
|
60c05af9ed | ||
|
|
56fa626605 | ||
|
|
7c0d279f61 | ||
|
|
92427201f1 | ||
|
|
c62580d228 | ||
|
|
958bb351ca | ||
|
|
7afe193259 | ||
|
|
444d8c53bb | ||
|
|
5b2fdc94e7 | ||
|
|
dbc9d1690d | ||
|
|
5f9365a521 | ||
|
|
145a4677f6 | ||
|
|
8dbbe65a1c | ||
|
|
d01b9aaa23 | ||
|
|
f64976b1ed | ||
|
|
2df608307d | ||
|
|
16b491fdab | ||
|
|
d81a9cd5c3 | ||
|
|
20e730ba2b | ||
|
|
2cc1195ff9 | ||
|
|
63f3cdaea8 | ||
|
|
454539185e | ||
|
|
2245af1dba | ||
|
|
ab91acb2f7 | ||
|
|
954f4f9886 | ||
|
|
0104462782 | ||
|
|
ec3b230eab | ||
|
|
6c65d5523e | ||
|
|
9a5072824d | ||
|
|
f908e407d4 | ||
|
|
923795719f | ||
|
|
57a2132d5d | ||
|
|
b2270ca3eb | ||
|
|
59c12ff354 | ||
|
|
8490bee634 | ||
|
|
9dfa0a478e | ||
|
|
d54bf0d06c | ||
|
|
a2cb4daa46 | ||
|
|
8fcb5f9df0 | ||
|
|
6adeada598 | ||
|
|
a0c57d0a5a | ||
|
|
bd2cd90957 | ||
|
|
d30eca77e9 | ||
|
|
80ef2ca788 | ||
|
|
2fc5d32e3f | ||
|
|
bebdf2e4ff | ||
|
|
8b62d6d458 | ||
|
|
52f40ad7a2 | ||
|
|
f969197436 | ||
|
|
bff53c6e69 | ||
|
|
03e02dda51 | ||
|
|
d4cbf1d2af | ||
|
|
847854aac3 | ||
|
|
48ea58c1fb | ||
|
|
c35c8cd4aa | ||
|
|
15a231a371 | ||
|
|
b6af6dddc5 | ||
|
|
509c2e52c4 | ||
|
|
62cef5c3f5 | ||
|
|
b366aa8d61 | ||
|
|
544e078512 | ||
|
|
c974ee9b44 | ||
|
|
c636e064e7 | ||
|
|
5e7469b56f | ||
|
|
975d835c92 | ||
|
|
1a24bc19af | ||
|
|
3d3300e542 | ||
|
|
c2dbb74f5a | ||
|
|
d0dff2c5ae | ||
|
|
26b8292564 | ||
|
|
9ffe2edc74 | ||
|
|
472b73cf65 | ||
|
|
a63e2557be | ||
|
|
4935ef521e | ||
|
|
e8958962dd | ||
|
|
1470e7691f | ||
|
|
aa44bc15a3 | ||
|
|
b8df1a1b61 | ||
|
|
e8b7116888 | ||
|
|
a9916a74f2 | ||
|
|
fdaee37b7b | ||
|
|
aeaee491f3 | ||
|
|
762ec3a0eb | ||
|
|
66c762b63f | ||
|
|
08917f8aae | ||
|
|
ce82c231fe | ||
|
|
fe4fb9c84c | ||
|
|
25ec88c566 | ||
|
|
eca40a3d7c | ||
|
|
de5ab051aa | ||
|
|
740b399ec2 | ||
|
|
d7766d54d5 | ||
|
|
84ff797b5f | ||
|
|
85ac0bb80a | ||
|
|
0ad2bc2f7b | ||
|
|
61fe97b646 | ||
|
|
48aab1cf16 | ||
|
|
39b1e28061 | ||
|
|
f79168ce5f | ||
|
|
5574c746b9 | ||
|
|
82a6ceb99e | ||
|
|
f0d1c1bb89 | ||
|
|
8fff9ea641 | ||
|
|
d8768bcb73 | ||
|
|
d09c1be688 | ||
|
|
3022274f6b | ||
|
|
d2a3bb8d28 | ||
|
|
3da61f0f2a | ||
|
|
67d5766cde | ||
|
|
d0fbfcd2d8 | ||
|
|
91e7d4f47a | ||
|
|
4d6f80f58e | ||
|
|
e25f6fd470 | ||
|
|
f74b34e99b | ||
|
|
8443de4701 | ||
|
|
ad32ce20da | ||
|
|
663bfe462c | ||
|
|
76a0eb78e3 | ||
|
|
a2a32ed32f | ||
|
|
6e89dbed24 | ||
|
|
b52082ed05 | ||
|
|
6cbb6617ef | ||
|
|
a60e6e7f22 | ||
|
|
b667bed1e6 | ||
|
|
113bfcba9d | ||
|
|
2535aa145f | ||
|
|
42d62fe28a | ||
|
|
ae02a9ba4f | ||
|
|
746c6783da | ||
|
|
0e45fbdf55 | ||
|
|
594e8ad771 | ||
|
|
6dec4bd96f | ||
|
|
0c50091d11 | ||
|
|
49136d5abd | ||
|
|
499fe319aa | ||
|
|
4cf7b36b7b | ||
|
|
b9a4010acc | ||
|
|
93704d85bf | ||
|
|
ae06484aea | ||
|
|
d3892a85be | ||
|
|
149fdde92f | ||
|
|
f3e82cc15b | ||
|
|
d1f8358159 | ||
|
|
90884924a8 | ||
|
|
f27b179211 | ||
|
|
7188081511 | ||
|
|
8420587318 | ||
|
|
f4c51cde00 | ||
|
|
0a72519155 | ||
|
|
5112fcd7d1 | ||
|
|
0136ac9e41 | ||
|
|
afd4558531 | ||
|
|
5e92aefcd4 | ||
|
|
0ea35dab18 | ||
|
|
0eb605219b | ||
|
|
1f270c985f | ||
|
|
a03214514c | ||
|
|
32130a7988 | ||
|
|
dd5a0c4892 | ||
|
|
74ae6fd7c0 | ||
|
|
7fcefa41af | ||
|
|
7f112e3c66 | ||
|
|
5c14a9c8c2 | ||
|
|
2d2b524a63 | ||
|
|
5c981c83b1 | ||
|
|
82a7b914b6 | ||
|
|
19bd3ec0b2 | ||
|
|
9e7af9ac3d | ||
|
|
ac89f45683 | ||
|
|
450718cce6 | ||
|
|
e02393c22b | ||
|
|
85a7dd3dff | ||
|
|
36d6581bba | ||
|
|
979d5a48f1 | ||
|
|
9380336948 | ||
|
|
136e5fbee7 | ||
|
|
94e05a6233 | ||
|
|
bc48709595 | ||
|
|
8170e777e8 | ||
|
|
cad55097f1 | ||
|
|
21b8b51cba | ||
|
|
9a84dbde71 | ||
|
|
87639ddeb2 | ||
|
|
5084cc2c69 | ||
|
|
46e9975bd5 | ||
|
|
4a363aee50 | ||
|
|
5b941daf4d | ||
|
|
3922d44173 | ||
|
|
43b6566aa8 | ||
|
|
ff8587736f | ||
|
|
d79f1766b6 | ||
|
|
c4a3baca7d | ||
|
|
e39b4e9c69 | ||
|
|
2a7594e56e | ||
|
|
b1159400f1 | ||
|
|
0907f05296 | ||
|
|
8c0f19422f | ||
|
|
96a202acce | ||
|
|
2700e42ebf | ||
|
|
1cdb5fc025 | ||
|
|
ba649fb8b4 | ||
|
|
1031aaa320 | ||
|
|
cdb575236e | ||
|
|
80766e051e | ||
|
|
f34198647e | ||
|
|
a02fa45d96 | ||
|
|
7230aaa7df | ||
|
|
b214a8f4c0 | ||
|
|
1408622694 | ||
|
|
579bfecdb4 | ||
|
|
11d63e19cd | ||
|
|
c6ca89b48a | ||
|
|
de1a6379df | ||
|
|
903f86a342 | ||
|
|
abdc67e0c1 | ||
|
|
c9f10796ee | ||
|
|
c2fc9080cc | ||
|
|
96c89b13b1 | ||
|
|
bd6272abf6 | ||
|
|
060a947798 | ||
|
|
29bb4c3dfd | ||
|
|
f5f30c118a | ||
|
|
83183b366a | ||
|
|
62e4e66321 | ||
|
|
0bb4a35c6f | ||
|
|
04cfa9c93e | ||
|
|
d28006ae62 | ||
|
|
89699051d5 | ||
|
|
3e150d8514 | ||
|
|
6981869c06 | ||
|
|
81f3112d01 | ||
|
|
55f50eb251 | ||
|
|
85f283c5f8 | ||
|
|
48c6765411 | ||
|
|
0bbe5d3a1a | ||
|
|
36e8ddd97e | ||
|
|
766450afd3 | ||
|
|
471733bfd0 | ||
|
|
72145e09b8 | ||
|
|
ffc734d8e2 | ||
|
|
15be760b19 | ||
|
|
8563556850 | ||
|
|
70fbbb206e | ||
|
|
d273941dc8 | ||
|
|
5526dcedca | ||
|
|
d818c17072 | ||
|
|
3b6c1e56c4 | ||
|
|
d03924ae82 | ||
|
|
361a3de9dd | ||
|
|
7aa836efa6 | ||
|
|
761d3ef500 | ||
|
|
394057c7a1 | ||
|
|
d679859d7d | ||
|
|
e180f9fa56 | ||
|
|
d2466dd33c | ||
|
|
92dae6db5e | ||
|
|
4c0d765a2f | ||
|
|
30dc4a2b27 | ||
|
|
c107f0062f | ||
|
|
e17837cbb1 | ||
|
|
5250e41a94 | ||
|
|
4a6f94f8fc | ||
|
|
9113b34b46 | ||
|
|
046fe0c385 | ||
|
|
8df7b5319e | ||
|
|
dfa90378df | ||
|
|
785fc8d2d2 | ||
|
|
4ec1727888 | ||
|
|
e13f2fdbb0 | ||
|
|
6a5ca6b508 | ||
|
|
d0f9a2465d | ||
|
|
614b8b87da | ||
|
|
5381e85d3c | ||
|
|
00f70a9432 | ||
|
|
9044d17863 | ||
|
|
fa0dd100e5 | ||
|
|
17ba9be238 | ||
|
|
486a225156 | ||
|
|
64f05bd28b | ||
|
|
8b771f6d28 | ||
|
|
8558a4e56d | ||
|
|
7d26ce4ab5 | ||
|
|
29fc73193f | ||
|
|
be41f57795 | ||
|
|
9702030bf5 | ||
|
|
29458c8e47 | ||
|
|
f439267129 | ||
|
|
716bb6da5e | ||
|
|
90eee9f5cb | ||
|
|
a080fd253f | ||
|
|
14bebd98e7 | ||
|
|
1597c78089 | ||
|
|
7266f09879 | ||
|
|
8987b91845 | ||
|
|
dd1444dcd9 | ||
|
|
e0e929492e | ||
|
|
8059f3f7fc | ||
|
|
21a39de0b4 | ||
|
|
187ca11946 | ||
|
|
f3acb3c21d | ||
|
|
6e0145c3b7 | ||
|
|
075b746f32 | ||
|
|
d10da72545 | ||
|
|
f0a82859a0 | ||
|
|
8b4e73e0ed | ||
|
|
6dc41ca15a | ||
|
|
b1b2394836 | ||
|
|
aca78b52cf | ||
|
|
5a00a632d6 | ||
|
|
a070ec5c32 | ||
|
|
d3f635901b | ||
|
|
ea70d34b28 | ||
|
|
2ff0007e56 | ||
|
|
9e97dd0b75 | ||
|
|
a72f710a69 | ||
|
|
d12f12ae22 | ||
|
|
99790bfae2 | ||
|
|
c8a9163f57 | ||
|
|
07fa53d128 | ||
|
|
c9c0e0a85b | ||
|
|
cb36e29b92 | ||
|
|
cbb3c88e11 | ||
|
|
32249ace3e | ||
|
|
3c675bb163 | ||
|
|
9ae851e1fc | ||
|
|
8d15d05711 | ||
|
|
f4da1fc94c | ||
|
|
b456966dd3 | ||
|
|
ffde2c6b32 | ||
|
|
7db811f2dd | ||
|
|
b9a58670ef | ||
|
|
7ae22dc848 | ||
|
|
a0a295f7d2 | ||
|
|
0d5afd266f | ||
|
|
d734fc3280 | ||
|
|
dcc171095f | ||
|
|
a639efa93e | ||
|
|
e8efbb8e83 | ||
|
|
819aab2248 | ||
|
|
13baef08c1 | ||
|
|
7df19bae5f | ||
|
|
4379ef68ef | ||
|
|
f15f27168a | ||
|
|
d42eb82b62 | ||
|
|
76b8932eeb | ||
|
|
529d1d84be | ||
|
|
a55f60c6dc | ||
|
|
b947afabd0 | ||
|
|
1d6b692e14 | ||
|
|
def1c0bfd9 | ||
|
|
acceb4e714 | ||
|
|
bf495a0aae | ||
|
|
981e6c508f | ||
|
|
47f3977554 | ||
|
|
b9a5de16de | ||
|
|
0a56ab662b | ||
|
|
eae1425975 | ||
|
|
9345f2549b | ||
|
|
7b281e4b04 | ||
|
|
381d96552b | ||
|
|
5d000c9930 | ||
|
|
8e67017c81 | ||
|
|
c9734cc0af | ||
|
|
b41234ad79 | ||
|
|
f18f7de9f7 | ||
|
|
c16ca14fba | ||
|
|
54dab5f107 | ||
|
|
bed40cc10a | ||
|
|
17a4bbea87 | ||
|
|
f1f8f91bd7 | ||
|
|
99acd06e02 | ||
|
|
cc4890184c | ||
|
|
0d9c53a4f4 | ||
|
|
bc5474a43a | ||
|
|
b24eefc873 | ||
|
|
42ce9c83b5 | ||
|
|
9dcc941292 | ||
|
|
01dc2505e2 | ||
|
|
c7f300357c | ||
|
|
02bd2035e4 | ||
|
|
9b271efc89 | ||
|
|
3eb2c6bbf7 | ||
|
|
79d66ad94d | ||
|
|
f53969a4b2 | ||
|
|
9bec181cec | ||
|
|
dac0d5b234 | ||
|
|
b90da19dfa | ||
|
|
498a32a525 | ||
|
|
1630d176d0 | ||
|
|
819ac4d406 | ||
|
|
91659ea992 | ||
|
|
2113f834a3 | ||
|
|
63f52ca8b0 | ||
|
|
d707c243f4 | ||
|
|
b4ef0d1b16 | ||
|
|
19d41d7298 | ||
|
|
75e286fdc7 | ||
|
|
66576daf3f | ||
|
|
22e11d7811 | ||
|
|
b1d5ca36fb | ||
|
|
f56fae4bc9 | ||
|
|
7e8ff51646 | ||
|
|
cc9025a080 | ||
|
|
410ade3b60 | ||
|
|
ab8a922809 | ||
|
|
c7c29f35e5 | ||
|
|
1f377adfee | ||
|
|
d1b321310e | ||
|
|
5a5a98c4ef | ||
|
|
06d3de354e | ||
|
|
b88830ba25 | ||
|
|
a164924ce5 | ||
|
|
880d0975db | ||
|
|
716ffd389b | ||
|
|
fa4d831e44 | ||
|
|
2e7fed84c1 | ||
|
|
2ef7df01e7 | ||
|
|
3cc6ea1e88 | ||
|
|
2b346e3c01 | ||
|
|
db778debb8 | ||
|
|
fceb2efce4 | ||
|
|
69bafedcca | ||
|
|
d57e141fbc | ||
|
|
561319bc57 | ||
|
|
61db5d616d | ||
|
|
0e3d733a72 | ||
|
|
f85bdb3d8b | ||
|
|
bb2b57f9dd | ||
|
|
74f68c2176 | ||
|
|
22e6992770 | ||
|
|
b8a254167c | ||
|
|
3d27e4a347 | ||
|
|
b081270aa1 | ||
|
|
a1dfa14d20 | ||
|
|
33630e8f9d | ||
|
|
3e356180a0 | ||
|
|
7ab0e8f105 | ||
|
|
87fe7693b0 | ||
|
|
c92bb1e33b | ||
|
|
1eb2743421 | ||
|
|
dbc1e80def | ||
|
|
cb368439cf | ||
|
|
0e4737e59d | ||
|
|
654df0889d | ||
|
|
9a2bff25ee | ||
|
|
50e7b0ab13 | ||
|
|
556c2efb0a | ||
|
|
adaa6021f9 | ||
|
|
6a0859f56a | ||
|
|
7c5f1533a0 | ||
|
|
3fb563bb43 | ||
|
|
1438f9e566 | ||
|
|
fbaa12de71 | ||
|
|
508253af7e | ||
|
|
1a8dcdfa3a | ||
|
|
f203abdeb0 | ||
|
|
a43076e815 | ||
|
|
2f87aa63e9 | ||
|
|
5ed4f8bf85 | ||
|
|
63ce7ea407 | ||
|
|
bd19468825 | ||
|
|
4df101c40c | ||
|
|
79db7b2124 | ||
|
|
59a4ecdcb9 | ||
|
|
24caf36230 | ||
|
|
499e94e0a5 | ||
|
|
6437721ec9 | ||
|
|
bf24bac9c9 | ||
|
|
f0379d79ef | ||
|
|
20abac7461 | ||
|
|
100089f6b8 | ||
|
|
2299204efa | ||
|
|
b7466327db | ||
|
|
1172ca7387 | ||
|
|
51fb269d0d | ||
|
|
3a42c8e835 | ||
|
|
3bcf0092e9 | ||
|
|
f7dfc38688 | ||
|
|
3b091f9e20 | ||
|
|
5955d3f90f | ||
|
|
c2f12460da | ||
|
|
d2d76baad8 | ||
|
|
67a36b7ebe | ||
|
|
5d5c861a93 | ||
|
|
c5d06b2d41 | ||
|
|
bd79c71b85 | ||
|
|
4b69edcbd7 | ||
|
|
28d2f89311 | ||
|
|
67a414fa2a | ||
|
|
57c86c6d25 | ||
|
|
fa2c5d928a | ||
|
|
6636a19a2b | ||
|
|
f29c95152b | ||
|
|
b0eb1656a5 | ||
|
|
6d3d2b239d | ||
|
|
d149576b4e | ||
|
|
085b4cacbe | ||
|
|
b2b50371ab | ||
|
|
73464e516f | ||
|
|
3d0ccadb76 | ||
|
|
28313fd478 | ||
|
|
961e8ae991 | ||
|
|
4cd1ed5a7b | ||
|
|
45dad63d5b | ||
|
|
faa5b729d9 | ||
|
|
694b5018fc | ||
|
|
56c8b90362 | ||
|
|
1e5985b608 | ||
|
|
58558c89ae | ||
|
|
1acd654e6e | ||
|
|
69f3ae52ec | ||
|
|
d2d176b6f9 | ||
|
|
30b9f47023 | ||
|
|
c2e65ff9c3 | ||
|
|
db06ff4338 | ||
|
|
aa4e9335aa | ||
|
|
3729b23e43 | ||
|
|
449b547a23 | ||
|
|
4e082c2bbf | ||
|
|
163f0099e7 | ||
|
|
5d4f2149e6 | ||
|
|
9132556fd9 | ||
|
|
b780070ee6 | ||
|
|
42a25300c4 | ||
|
|
759210a1e4 | ||
|
|
84a6cec732 | ||
|
|
37f0dcb456 | ||
|
|
970b38719d | ||
|
|
43af9d1bac | ||
|
|
276b0b251d | ||
|
|
2e5fc2467f | ||
|
|
7b2f93fd4c | ||
|
|
b6b1cbeb2d | ||
|
|
4cdae540b5 | ||
|
|
66af57e74f | ||
|
|
06d69aadf7 | ||
|
|
a5e7d76341 | ||
|
|
ec308acfcb | ||
|
|
e9d7ac39ea | ||
|
|
091c763a0f | ||
|
|
8bf65f3820 | ||
|
|
78034096a8 | ||
|
|
8ddcc4d9a6 | ||
|
|
f163d7ddde | ||
|
|
c9272c25ce | ||
|
|
640e903dd6 | ||
|
|
bbc4dec8dd | ||
|
|
e866e9eb7b | ||
|
|
e9a6328566 | ||
|
|
d4260734dc | ||
|
|
1e39fb78db | ||
|
|
ce2852bdc0 | ||
|
|
990a928602 | ||
|
|
62669c085d | ||
|
|
51d8d3b3b8 | ||
|
|
ee5a884328 | ||
|
|
883f9199ec | ||
|
|
f0fab0c0ad | ||
|
|
f800c9f7c2 | ||
|
|
5158497125 | ||
|
|
2c32269b7f | ||
|
|
f2b644e2bb | ||
|
|
60788e4ba5 | ||
|
|
06ef421578 | ||
|
|
54d7fe6dc5 | ||
|
|
a204d2b2db | ||
|
|
b81a03fda9 | ||
|
|
7d68c03101 | ||
|
|
6d7e2c667d | ||
|
|
a73300e864 | ||
|
|
61ed209abb | ||
|
|
927fa9aac9 | ||
|
|
f38b9c9a2c | ||
|
|
d0846170c4 | ||
|
|
f60dc8fa37 | ||
|
|
ea455436c1 | ||
|
|
0c7fb861f1 | ||
|
|
8e9a99f90c | ||
|
|
10bca676fc | ||
|
|
a4e321522b | ||
|
|
3913a909ef | ||
|
|
5647229c05 | ||
|
|
3f240bd9e8 | ||
|
|
930a7e19f1 | ||
|
|
741528b0b3 | ||
|
|
2034bfcc83 | ||
|
|
7cfc63d6ed | ||
|
|
d01e797219 | ||
|
|
58a4215690 | ||
|
|
67347935b5 | ||
|
|
c9c01def8c | ||
|
|
963d5e11cc | ||
|
|
85fbc9ea9d | ||
|
|
8eebcb0b06 | ||
|
|
77357b765e | ||
|
|
310f3f23b8 | ||
|
|
6684f9bf08 | ||
|
|
91fe7e0ca7 | ||
|
|
56275c5a3b | ||
|
|
5e7529dae4 | ||
|
|
56f1c9e919 | ||
|
|
ad2d5a5ae0 | ||
|
|
928dbf80bb | ||
|
|
62784a63e4 | ||
|
|
c3cdf63253 | ||
|
|
fb34392fca | ||
|
|
710a276c45 | ||
|
|
04c18f0bd5 | ||
|
|
4696519bad | ||
|
|
748c1bc158 | ||
|
|
7814914a05 | ||
|
|
eef7a73ce9 | ||
|
|
d0209f720a | ||
|
|
8acf39cad6 | ||
|
|
df18e318a8 | ||
|
|
f8155ff74c | ||
|
|
a60d746e3b | ||
|
|
7ca98eb965 | ||
|
|
45ae1dd67e | ||
|
|
37d8e8ad43 | ||
|
|
2a978c498e | ||
|
|
c4f6d626d5 | ||
|
|
d98d522387 | ||
|
|
47e36a9249 | ||
|
|
8dae235b8d | ||
|
|
28cb7168b1 | ||
|
|
7794171d62 | ||
|
|
6e3e188fd6 | ||
|
|
f8a4f003f1 | ||
|
|
805aa52f59 | ||
|
|
963e8e7180 | ||
|
|
41c0191cf9 | ||
|
|
d4fae02540 | ||
|
|
8f82b92a6a | ||
|
|
ed33c8d580 | ||
|
|
2217306ca1 | ||
|
|
f97db31533 | ||
|
|
f80c5df971 | ||
|
|
60a0bcdc7a | ||
|
|
caade5a308 | ||
|
|
6e8f618f80 | ||
|
|
9ea9f66dd7 | ||
|
|
1996b86e85 | ||
|
|
1ce1f7b3e5 | ||
|
|
6ef3bab0fc | ||
|
|
a32d937b91 | ||
|
|
3a934ef5b8 | ||
|
|
b9aee6ae85 | ||
|
|
1ff027152a | ||
|
|
2737b6bbfc | ||
|
|
1246edaf2c | ||
|
|
9ec3cccaee | ||
|
|
297087c620 | ||
|
|
69cf21c04e | ||
|
|
76ecc60675 | ||
|
|
ead20f5be9 | ||
|
|
89f9b46ec0 | ||
|
|
b2643a0ac8 | ||
|
|
a5b3011ea4 | ||
|
|
cfec70730f | ||
|
|
fd3236ddb7 | ||
|
|
084f58b499 | ||
|
|
04f88df385 | ||
|
|
998beb51b5 | ||
|
|
5b73e0a691 | ||
|
|
7e51d3a7f5 | ||
|
|
457325024a | ||
|
|
daa6593534 | ||
|
|
06d49f4433 | ||
|
|
1bd6149ee2 | ||
|
|
7daef0000b | ||
|
|
08c0082430 | ||
|
|
8f14cb238b | ||
|
|
c898da7378 | ||
|
|
701f01fa4f | ||
|
|
bac6dc36b4 | ||
|
|
4ca8667669 | ||
|
|
a3df47e88e | ||
|
|
62aea23879 | ||
|
|
ce77f2cbd4 | ||
|
|
24b5fd92ef | ||
|
|
b83aba0b13 | ||
|
|
15ed251ed7 | ||
|
|
de67fe0d37 | ||
|
|
f4babc6f28 | ||
|
|
59dd8d3bf8 | ||
|
|
5c7752a203 | ||
|
|
fd15600d80 | ||
|
|
5d629f7331 | ||
|
|
d51c399351 | ||
|
|
1358fedca6 | ||
|
|
dbeacf9249 | ||
|
|
bab6a71a2e | ||
|
|
5cea0fa73a | ||
|
|
e69e1f5f03 | ||
|
|
79e2d20d9d | ||
|
|
4205694a65 | ||
|
|
94de51ad53 | ||
|
|
f6d97374c5 | ||
|
|
18c13c735e | ||
|
|
2feb93aaf0 | ||
|
|
0e6d33a668 | ||
|
|
3f6501fa88 | ||
|
|
cc9ffd47b1 | ||
|
|
709b1d2ead | ||
|
|
bf1d7812e2 | ||
|
|
6d96dd21ac | ||
|
|
539ad668cf | ||
|
|
e2ae091723 | ||
|
|
0eda9a9d95 | ||
|
|
22ceb3f699 | ||
|
|
84448e9803 | ||
|
|
b14ea4f051 | ||
|
|
9d50a4363b | ||
|
|
69c25b5954 | ||
|
|
2e5cccc381 | ||
|
|
13be8ff6d0 | ||
|
|
4312983ca5 | ||
|
|
4daa94c014 | ||
|
|
df4acbf5d5 | ||
|
|
8465edd5af | ||
|
|
fccec3a195 | ||
|
|
d94bd3f28f | ||
|
|
348572bcb6 | ||
|
|
ad4fe1924b | ||
|
|
0749a42ef6 | ||
|
|
9d9b01839c | ||
|
|
40fa4f71bc | ||
|
|
ae6c68018b | ||
|
|
029bb38af8 | ||
|
|
5d10ee39be | ||
|
|
4cd71d12ef | ||
|
|
d425cb9d47 | ||
|
|
5c8ddef60b | ||
|
|
1d6da68963 | ||
|
|
657ed82958 | ||
|
|
ff0eda1fba | ||
|
|
d70ca9fa3b | ||
|
|
55873416a1 | ||
|
|
d6243d9f89 | ||
|
|
938a1fb9d7 | ||
|
|
397df48efd | ||
|
|
ce0c84266e | ||
|
|
f497044777 | ||
|
|
36f9bef450 | ||
|
|
7ff5f47064 | ||
|
|
8fec778b9b | ||
|
|
7acfd80387 | ||
|
|
45729d48cc | ||
|
|
1994d1171c | ||
|
|
7f923ba047 | ||
|
|
e27095dd67 | ||
|
|
6a32ae94fc | ||
|
|
235b296a69 | ||
|
|
fa6120a563 | ||
|
|
205053aebe | ||
|
|
bb63d859c9 | ||
|
|
6154a7fb09 | ||
|
|
e3c0bf6a1b | ||
|
|
af3b350498 | ||
|
|
6c4e042307 | ||
|
|
490f21ff9f | ||
|
|
99d3780773 | ||
|
|
0f472c8959 | ||
|
|
9e0e47064a | ||
|
|
c894beed2e | ||
|
|
90d3ac3cf6 | ||
|
|
feb9cce4ee | ||
|
|
f188b29911 | ||
|
|
ad00fdfa53 | ||
|
|
21670a5d51 | ||
|
|
bde27b0dde | ||
|
|
ff6d961922 | ||
|
|
c19ea74fb4 | ||
|
|
efe468b0d2 | ||
|
|
936535786e | ||
|
|
f512ae1b33 | ||
|
|
2ca0cf05a0 | ||
|
|
61cf48c37c | ||
|
|
a8c1a46e3d | ||
|
|
7892e37bfa | ||
|
|
9ccd11b6d5 | ||
|
|
4910355711 | ||
|
|
787b254840 | ||
|
|
3ffbbe60ff | ||
|
|
cfe72e2cd0 | ||
|
|
0a57c084bb | ||
|
|
a3dff204d3 | ||
|
|
7e6ad089c0 | ||
|
|
9a3ef0122b | ||
|
|
a6e38e7e20 | ||
|
|
2e0c7db4e3 | ||
|
|
75ea743b4a | ||
|
|
c8f945d40f | ||
|
|
8c4a13fdf0 | ||
|
|
d6d330bec6 | ||
|
|
ed7a117247 | ||
|
|
afc5cc26fa | ||
|
|
716ac681b6 | ||
|
|
c67c0aa2e6 | ||
|
|
2028cab04c | ||
|
|
9e50c5e69a | ||
|
|
0526e96dc5 | ||
|
|
bf42109d81 | ||
|
|
fb626c8a97 | ||
|
|
dc298b3182 | ||
|
|
3096c54bb8 | ||
|
|
b09565a723 | ||
|
|
ebd5b77576 | ||
|
|
b23a887edd | ||
|
|
f6d9765f87 | ||
|
|
805a0502d2 | ||
|
|
225b0ac8b2 | ||
|
|
ca93d1ad70 | ||
|
|
76d74ab9bb | ||
|
|
2d03ceebc8 | ||
|
|
9a9e0b2c20 | ||
|
|
e8bf1d08bc | ||
|
|
d318fbac77 | ||
|
|
b4f70bdece | ||
|
|
4284853a4a | ||
|
|
7f070448b7 | ||
|
|
47f0b7626f | ||
|
|
733d7d7c87 | ||
|
|
42ba1775d7 | ||
|
|
9420f21680 | ||
|
|
3281e1e8c2 | ||
|
|
8a773dea4e | ||
|
|
7a1a4d9161 | ||
|
|
21c0c617c1 | ||
|
|
ae56ed6a32 | ||
|
|
b3ec8a6790 | ||
|
|
5fe277f90d | ||
|
|
4df8567fa6 | ||
|
|
6fcb129ad6 | ||
|
|
7c0cb92696 | ||
|
|
04fa0520a6 | ||
|
|
cd65da75c6 | ||
|
|
b30d696e3a | ||
|
|
cd3d795296 | ||
|
|
0ebb52e64f | ||
|
|
c03f2d772c | ||
|
|
3d9f69dba7 | ||
|
|
50ff330734 | ||
|
|
e07974f803 | ||
|
|
a7280d2943 | ||
|
|
027c35b75a | ||
|
|
6dd08e7dcb | ||
|
|
fe3fa2872d | ||
|
|
21cbcb8cf6 | ||
|
|
46b18512cf | ||
|
|
f432f6f082 | ||
|
|
93c9b6289c | ||
|
|
bf702575be | ||
|
|
0125c544ee | ||
|
|
02faa3fcb6 | ||
|
|
84d62fc540 | ||
|
|
b92ee03525 | ||
|
|
2e92f65782 | ||
|
|
0706b6cf78 | ||
|
|
a8fe353ba4 | ||
|
|
b3075d3414 | ||
|
|
f83a81242a | ||
|
|
07ac649763 | ||
|
|
4577bde05c | ||
|
|
0c88602d1f | ||
|
|
12bb59d257 | ||
|
|
03fc16dd5a | ||
|
|
01dd209647 | ||
|
|
cda5d15e31 | ||
|
|
a9d2a2c4bc | ||
|
|
3c98ef172e | ||
|
|
b3161d6d5d | ||
|
|
75b5be5baf | ||
|
|
a776aab897 | ||
|
|
a0ed43bf64 | ||
|
|
a3f122fee4 | ||
|
|
050721af5b | ||
|
|
223dd3bf7b | ||
|
|
8424870ec3 | ||
|
|
3e4a980ea6 | ||
|
|
75158c47e2 | ||
|
|
f1fddefeac | ||
|
|
2f968aca80 | ||
|
|
ad873cfd7b | ||
|
|
902b246f96 | ||
|
|
f70e771000 | ||
|
|
9fac7cb1f3 | ||
|
|
0b0efbaf9f | ||
|
|
6e94375ed9 | ||
|
|
f7768a00a0 | ||
|
|
663e657bf5 | ||
|
|
18826ad5c6 | ||
|
|
a3a5569156 | ||
|
|
dcacfb0c10 | ||
|
|
b5cf2e8a4e | ||
|
|
efded33f4a | ||
|
|
de99be720e | ||
|
|
1c19d82f53 | ||
|
|
8901ed72ee | ||
|
|
fa30d3c732 | ||
|
|
e936b9cfd2 | ||
|
|
359132045d | ||
|
|
8fdcb9d2bb | ||
|
|
0d99bc62d2 | ||
|
|
62c4b79e04 | ||
|
|
e036449c72 | ||
|
|
69b112eb44 | ||
|
|
c7ac8eeafb | ||
|
|
b18b2fff17 | ||
|
|
39d2243b11 | ||
|
|
9daa452f8e | ||
|
|
d6a6d069bc | ||
|
|
fd12a5d919 | ||
|
|
354a3022a4 | ||
|
|
ce742d40eb | ||
|
|
c20c144222 | ||
|
|
170738ee10 | ||
|
|
3dd35ba5a1 | ||
|
|
d33eb22ca3 | ||
|
|
d31d09ce7e | ||
|
|
1fe64cca04 | ||
|
|
7bbe77fe4a | ||
|
|
d749fbb2ab | ||
|
|
b7470c3c42 | ||
|
|
5cd7e2e14c | ||
|
|
39db5aee04 | ||
|
|
7245e978cd | ||
|
|
5e79ea908d | ||
|
|
ca8b025f9a | ||
|
|
4f0ca6367c | ||
|
|
b1918f5392 | ||
|
|
165f8fa4a7 | ||
|
|
487a07671b | ||
|
|
702ba84956 | ||
|
|
4147af6546 | ||
|
|
fb9144a715 | ||
|
|
ccfc2f601d | ||
|
|
602f35cb70 | ||
|
|
7fe65e1f8a | ||
|
|
70deb5a285 | ||
|
|
e2c0877e9b | ||
|
|
aafd734e3a | ||
|
|
74755db6e1 | ||
|
|
7594933550 | ||
|
|
b2286157ef | ||
|
|
a2e7a35998 | ||
|
|
e0bda6b850 | ||
|
|
8878f77636 | ||
|
|
d4b4cfb32e | ||
|
|
ba330bbfac | ||
|
|
ef4e1ecbab | ||
|
|
4183a580d2 | ||
|
|
92dcf3c28e | ||
|
|
3e706366bd | ||
|
|
43002d466e | ||
|
|
94779326bc | ||
|
|
31ddc37db4 | ||
|
|
a3ff32c22e | ||
|
|
9a01a5285f | ||
|
|
fc3ee8d402 | ||
|
|
d1d3c3ef15 | ||
|
|
5843eebba2 | ||
|
|
12c33fcddf | ||
|
|
efe38264ef | ||
|
|
605d242677 | ||
|
|
4020c3dea1 | ||
|
|
6a90401d56 | ||
|
|
d5eb01b724 | ||
|
|
38350465c1 | ||
|
|
945fc8f0f9 | ||
|
|
4a519832e0 | ||
|
|
71bd5583fa | ||
|
|
1a1dff7609 | ||
|
|
dc11638eb9 | ||
|
|
803ccf7708 | ||
|
|
69be532c3c | ||
|
|
0b8b4dc3cf | ||
|
|
f851dc8ac1 | ||
|
|
eafec9d4ad | ||
|
|
278339b5e2 | ||
|
|
a340ab15e1 | ||
|
|
e566b99b75 | ||
|
|
f1da5c57e8 | ||
|
|
59f232d69f | ||
|
|
f6f951fba8 | ||
|
|
af6b191164 | ||
|
|
6728627f78 | ||
|
|
e53c758471 | ||
|
|
614c029538 | ||
|
|
ec13bae7e6 | ||
|
|
3e6550b8ad | ||
|
|
7ee963f66e | ||
|
|
77dd8224ae | ||
|
|
c66c85bc9a | ||
|
|
930443f4cd | ||
|
|
3595e5fdbf | ||
|
|
a96bb0ce11 | ||
|
|
68c618887f | ||
|
|
f42d49b8eb | ||
|
|
880714b2f2 | ||
|
|
fdda28799c | ||
|
|
3a8710540c | ||
|
|
1765d61973 | ||
|
|
c09fce344d | ||
|
|
ef8ec03e41 | ||
|
|
5f6337a734 | ||
|
|
194a62fcea | ||
|
|
3339bf0fe6 | ||
|
|
3a852393bc | ||
|
|
de3d9bb5c9 | ||
|
|
9bfa0c9bb8 | ||
|
|
90e10f948e | ||
|
|
0cc1ff8fa3 | ||
|
|
8b0b4bfcc4 | ||
|
|
651752d23d | ||
|
|
9cc817d544 | ||
|
|
a6b889c469 | ||
|
|
ffed5e3378 | ||
|
|
8666151189 | ||
|
|
3bc9b1bc55 | ||
|
|
cbc7f438d2 | ||
|
|
231fd13429 | ||
|
|
7e37a90c80 | ||
|
|
e7a005b685 | ||
|
|
95b58fc2c4 | ||
|
|
7b5fcc3219 | ||
|
|
f9adaa85ca | ||
|
|
2be8d5b282 | ||
|
|
1fd7aba3b0 | ||
|
|
045abfa9c1 | ||
|
|
2de36caea0 | ||
|
|
b0296933a1 | ||
|
|
9199186309 | ||
|
|
69083cfcfd | ||
|
|
12a8509299 | ||
|
|
611b07e6bd | ||
|
|
60ca5af813 | ||
|
|
fb463e3b26 | ||
|
|
34ad79744a | ||
|
|
01f2a4ca20 | ||
|
|
3d4cb02ce2 | ||
|
|
9e2598612a | ||
|
|
5a066bfde4 | ||
|
|
1e7210a6db | ||
|
|
57aa844fcb | ||
|
|
506467193e | ||
|
|
1761effa28 | ||
|
|
a2e0bb19dc | ||
|
|
b5f244009a | ||
|
|
2b4943a780 | ||
|
|
e40b2f6529 | ||
|
|
8855ef2a41 | ||
|
|
e7e2f2c98e | ||
|
|
638b9dc84a | ||
|
|
03766b9f89 | ||
|
|
b3387f2d41 | ||
|
|
62a4bc01ce | ||
|
|
664bd3a89f | ||
|
|
3f4bf72248 | ||
|
|
c479dd1753 | ||
|
|
f5633329f8 | ||
|
|
e59d829973 | ||
|
|
a173e2a61f | ||
|
|
e4b2ea60ec | ||
|
|
1888f24ef0 | ||
|
|
a57f194123 | ||
|
|
476f84a181 | ||
|
|
f9e798cf93 | ||
|
|
d90f44c510 | ||
|
|
501647805c | ||
|
|
ffc271a53a | ||
|
|
c4351d61c6 | ||
|
|
b1e4b34b79 | ||
|
|
69066029f1 | ||
|
|
3308db0cd2 | ||
|
|
797476f7ad | ||
|
|
048367d6c5 | ||
|
|
c77ad6faa9 | ||
|
|
d467d27ecd | ||
|
|
02dfe5aeab | ||
|
|
a1bbb4fdb9 | ||
|
|
289ab78052 | ||
|
|
940430b075 | ||
|
|
fdd4135632 | ||
|
|
c450d71f84 | ||
|
|
57f7acc124 | ||
|
|
7c93a6cdfc | ||
|
|
8e7cffc016 | ||
|
|
8ff292bd1f | ||
|
|
9dbf124e36 | ||
|
|
691a8178b2 | ||
|
|
e570779a03 | ||
|
|
c077f9aebb | ||
|
|
b94e11930e | ||
|
|
b0636b40ab | ||
|
|
089dce3853 | ||
|
|
377df413ed | ||
|
|
08fa64c3cc | ||
|
|
0815df1bca | ||
|
|
ddd0436937 | ||
|
|
c57fabc9ef | ||
|
|
b0bd5d47ae | ||
|
|
44743d860e | ||
|
|
4246a3d113 | ||
|
|
b79e693808 | ||
|
|
2d20256ed8 | ||
|
|
93ca62aa49 | ||
|
|
e8cc7227a8 | ||
|
|
28b88a3840 | ||
|
|
34fd45dce9 | ||
|
|
d2f95acd2e | ||
|
|
6ad40c91b4 | ||
|
|
e977c95520 | ||
|
|
9f43c0fe17 | ||
|
|
9950e0948f | ||
|
|
441bf5f048 | ||
|
|
e0802192f6 | ||
|
|
72818412e1 | ||
|
|
522e6df73c | ||
|
|
9f4c68f114 | ||
|
|
d230af1d5e | ||
|
|
a5d97c15e7 | ||
|
|
2ed8b68c44 | ||
|
|
4f3fa04cde | ||
|
|
c4e0923fb5 | ||
|
|
b750bd4d15 | ||
|
|
774f30c940 | ||
|
|
bc4be815e4 | ||
|
|
a30942669a | ||
|
|
aaadb63137 | ||
|
|
ac270c200c | ||
|
|
a09ec494f6 | ||
|
|
405b886ba2 | ||
|
|
eb64190228 | ||
|
|
05e2d6d060 | ||
|
|
9e6e30324f | ||
|
|
a4e1f2a4ab | ||
|
|
0db1947263 | ||
|
|
44d139f610 | ||
|
|
7917502799 | ||
|
|
288a2fffd7 | ||
|
|
5c2e7e9324 | ||
|
|
72ca41df33 | ||
|
|
386c71475a | ||
|
|
e2e5de009b | ||
|
|
8bffffa000 | ||
|
|
e22d75d1a1 | ||
|
|
738f027f33 | ||
|
|
613bece13a | ||
|
|
8486d7c85b | ||
|
|
ec4e71c8cf | ||
|
|
844a4d5f19 | ||
|
|
aa0d1596b0 | ||
|
|
9997fa9306 | ||
|
|
36e18929de | ||
|
|
eb79f77bd2 | ||
|
|
e4e00b4cd3 | ||
|
|
19c5a8c64f | ||
|
|
a4c6a72a9c | ||
|
|
4dd0709f66 | ||
|
|
625e7b8aae | ||
|
|
d943a8286f | ||
|
|
4dcbc85a81 | ||
|
|
bd9885182e | ||
|
|
39987b6dcc | ||
|
|
d6cb5c1ed0 | ||
|
|
f618feea26 | ||
|
|
d9fdd9dfcb | ||
|
|
1cb320f5f4 | ||
|
|
57b566286d | ||
|
|
8dd0cb19af | ||
|
|
1fbd9edc3b | ||
|
|
4222cc30a8 | ||
|
|
13a42c17b2 | ||
|
|
438838f81d | ||
|
|
1c60ad7251 | ||
|
|
7d20b70d33 | ||
|
|
b9d8ec5039 | ||
|
|
eb18a6cf67 | ||
|
|
dfa75f6606 | ||
|
|
60b595ea6f | ||
|
|
595bceda2e | ||
|
|
68c247f764 | ||
|
|
d59732f6dd | ||
|
|
a040c3c7d2 | ||
|
|
0cce6ca488 | ||
|
|
3900833458 | ||
|
|
d05917222d | ||
|
|
df50b7b137 | ||
|
|
b467da13b1 | ||
|
|
e0f56a157d | ||
|
|
8acdfc81c1 | ||
|
|
1c86f32003 | ||
|
|
c62dd272d6 | ||
|
|
e60c020634 | ||
|
|
fe5a40d632 | ||
|
|
a7662f8327 | ||
|
|
c94a92bd5f | ||
|
|
c690916da9 | ||
|
|
b9d6efa677 | ||
|
|
e3627e4721 | ||
|
|
8589feaedf | ||
|
|
da24b85ccd | ||
|
|
eaa09d68ce | ||
|
|
1eb48c63e5 | ||
|
|
9adab9f6f4 | ||
|
|
9bb7cfc81b | ||
|
|
b43d714011 | ||
|
|
d8990e8439 | ||
|
|
1ffb1bb1ec | ||
|
|
90f1999c6d | ||
|
|
838ea6bfc4 | ||
|
|
ffa89f1e01 | ||
|
|
bbdffc797d | ||
|
|
dd083b9b8d | ||
|
|
a12ed6ad35 | ||
|
|
aacc3149ce | ||
|
|
e2747839d1 | ||
|
|
c5c77eeb97 | ||
|
|
4a80b2ce1e | ||
|
|
d1e2dfcf61 | ||
|
|
4d4dbb7764 | ||
|
|
113fbf9eb8 | ||
|
|
ae63f72cf9 | ||
|
|
9bda4094e4 | ||
|
|
be960bf27b | ||
|
|
cf5898fb45 | ||
|
|
9b38ca7d68 | ||
|
|
a25a6b7e8d | ||
|
|
a99cab9130 | ||
|
|
eeca2c080d | ||
|
|
c4a68d2f73 | ||
|
|
02f3c71e8b | ||
|
|
e85cc684a1 | ||
|
|
7eb5e59842 | ||
|
|
7817898c14 | ||
|
|
0a436cdf4c | ||
|
|
e06a6ae5bd | ||
|
|
663d03ed2c | ||
|
|
7bae4062b1 | ||
|
|
e903a8226a | ||
|
|
53de6542f6 | ||
|
|
42a104534a | ||
|
|
8f8cd95395 | ||
|
|
1c4cb94a13 | ||
|
|
ae94bec6b8 | ||
|
|
3c7b201f57 | ||
|
|
87757a60a3 | ||
|
|
c68c941c1d | ||
|
|
9e7d0a50ca | ||
|
|
556b388a4e | ||
|
|
6af8e77ee1 | ||
|
|
f890ddac1b | ||
|
|
03a780f397 | ||
|
|
06cbbbb019 | ||
|
|
b4b772354c | ||
|
|
104c0cef4b | ||
|
|
91e39b7df9 | ||
|
|
5908776a86 | ||
|
|
7d2b22630d | ||
|
|
945818e1d8 | ||
|
|
3e2c6ea1b7 | ||
|
|
441f2244a9 | ||
|
|
814c714145 | ||
|
|
a04bf6119e | ||
|
|
09c98f66ff | ||
|
|
29bfe108fe | ||
|
|
5444b808b1 | ||
|
|
a546bae341 | ||
|
|
7a5b64bdc9 | ||
|
|
583e4c65c8 | ||
|
|
e171708aba | ||
|
|
689c0dd45b | ||
|
|
8cf771be2f | ||
|
|
b572c58223 | ||
|
|
affe54b47f | ||
|
|
3b93a521d6 | ||
|
|
126a07bdf6 | ||
|
|
06ecc89603 | ||
|
|
e9088792af | ||
|
|
a1e7c89027 | ||
|
|
9d582661b7 | ||
|
|
44744fa510 | ||
|
|
bf36d58612 | ||
|
|
2d89fd44cd | ||
|
|
e1fab8c153 | ||
|
|
1c83f59baa | ||
|
|
c20dd07a2d | ||
|
|
94aee8f05c | ||
|
|
a76bd8c5b2 | ||
|
|
63f40c9565 | ||
|
|
17127dd131 | ||
|
|
6828d032b4 | ||
|
|
e75c0d075f | ||
|
|
b4e83e7b1a | ||
|
|
226089cc3c | ||
|
|
3a0a2c7f1c | ||
|
|
7a0a013c43 | ||
|
|
84eee41734 | ||
|
|
ffdc313376 | ||
|
|
d1dccf8dcc | ||
|
|
f7b93e474c | ||
|
|
84daec0f07 | ||
|
|
8341667bdd | ||
|
|
bdadb27466 | ||
|
|
1660705658 | ||
|
|
6f0be9400b | ||
|
|
6dd882a69b | ||
|
|
48680051e1 | ||
|
|
8ac014709b | ||
|
|
72f9019300 | ||
|
|
c09ff6a706 | ||
|
|
1b747bf09d | ||
|
|
df4277e530 | ||
|
|
3878ae9e0d | ||
|
|
8636280b91 | ||
|
|
3633a41e4f | ||
|
|
b096c7a2e3 | ||
|
|
12e1a0edff | ||
|
|
73a70d6952 | ||
|
|
671d6d1893 | ||
|
|
7dcabf072b | ||
|
|
e8fd97d3ba | ||
|
|
990f24938b | ||
|
|
c40895e221 | ||
|
|
05bc7e292f | ||
|
|
8b59d7d7a2 | ||
|
|
81ffa93bad | ||
|
|
9448bc86fd | ||
|
|
acd0c673cb | ||
|
|
b36ce36451 | ||
|
|
1c0900b29d | ||
|
|
62099e6078 | ||
|
|
72da00d23c | ||
|
|
7a0fd1caa3 | ||
|
|
e50684e367 | ||
|
|
a9fe0499e6 | ||
|
|
347ddc112d | ||
|
|
1e4044fdf7 | ||
|
|
66067c272f | ||
|
|
5e1cb67dcd | ||
|
|
197d9fc14a | ||
|
|
0fc0f104bd | ||
|
|
a6258dd973 | ||
|
|
1cda5023c8 | ||
|
|
1ffbd0eea8 | ||
|
|
02b5f0b7e6 | ||
|
|
592ae4053e | ||
|
|
d855c6b2ea | ||
|
|
a37eb57d6c | ||
|
|
524c55e6f1 | ||
|
|
3126560fb9 | ||
|
|
7a3c8ab637 | ||
|
|
d5c5a34467 | ||
|
|
f60e17907a | ||
|
|
8e5c58d897 | ||
|
|
c9ce4d1507 | ||
|
|
eb2bc68c07 | ||
|
|
9a59c68370 | ||
|
|
f061a2fc68 | ||
|
|
eb032eb7f9 | ||
|
|
4b27ebbee4 | ||
|
|
ac358f4199 | ||
|
|
2ca3dfe689 | ||
|
|
f2a5e1dc78 | ||
|
|
2c7b5669bd | ||
|
|
77a93b17ac | ||
|
|
791b124cf7 | ||
|
|
d911045dca | ||
|
|
0cc405f51e | ||
|
|
168f9f972d | ||
|
|
1ef89560e2 | ||
|
|
afb035ae6b | ||
|
|
891039dba2 | ||
|
|
2326f8b818 | ||
|
|
358e03f6ce | ||
|
|
63a81b87d8 | ||
|
|
e1d857653e | ||
|
|
accd5b70b3 | ||
|
|
8eed60a8f5 | ||
|
|
2bc066bacb | ||
|
|
3ad9362247 | ||
|
|
6bc3191077 | ||
|
|
f457018f89 | ||
|
|
a018dd19b5 | ||
|
|
f92cb55eb1 | ||
|
|
13c0e50358 | ||
|
|
f4922743fc | ||
|
|
f45cb3a583 | ||
|
|
87c558537f | ||
|
|
852bd74c3b | ||
|
|
c410de2fad | ||
|
|
85e786904c | ||
|
|
c36c32082f | ||
|
|
a56e464eb6 | ||
|
|
4fbb7b1791 | ||
|
|
94ae457a82 | ||
|
|
bc5a508cd7 | ||
|
|
4e3d209831 | ||
|
|
9233a94379 | ||
|
|
5800594d55 | ||
|
|
e7ccfc9156 | ||
|
|
8047186c58 | ||
|
|
af21063834 | ||
|
|
97d09c5015 | ||
|
|
0e535a916c | ||
|
|
4002fb9da5 | ||
|
|
fca23c7d55 | ||
|
|
42da216f5d | ||
|
|
0d563eaa57 | ||
|
|
a4dbd8d09f | ||
|
|
fa1ce45bf3 | ||
|
|
7d0cd1d454 | ||
|
|
8ce0ceee37 | ||
|
|
db9f362857 | ||
|
|
af027e2288 | ||
|
|
fcf364f958 | ||
|
|
fd8ba5edfc | ||
|
|
3c1090cb46 | ||
|
|
3ba59f7c54 | ||
|
|
a28405c9ff | ||
|
|
02bac384df | ||
|
|
b9e00c7261 | ||
|
|
d008814de9 | ||
|
|
a2c5b11194 | ||
|
|
c8ec2d9600 | ||
|
|
a905f74800 | ||
|
|
05972e500c | ||
|
|
bcf5f612cc | ||
|
|
8fc310eb24 | ||
|
|
832f22e7f0 | ||
|
|
dc364b3e9e | ||
|
|
62f78a60a9 | ||
|
|
04e37ebd04 | ||
|
|
e226846446 | ||
|
|
bda39b4838 | ||
|
|
8dd0a904d2 | ||
|
|
05a5ec7e19 | ||
|
|
cf804ca84e | ||
|
|
e342889937 | ||
|
|
150bfe8c66 | ||
|
|
d3ca49ceeb | ||
|
|
795c163a69 | ||
|
|
3b62d0af3e | ||
|
|
39675e15e8 | ||
|
|
e2a853c98e | ||
|
|
38793654ea | ||
|
|
51dcfb7d1e | ||
|
|
da9ab6ac90 | ||
|
|
b376aba591 | ||
|
|
e56c9640dc | ||
|
|
7f7c8a8b51 | ||
|
|
cadf0a336c | ||
|
|
08525e8eba | ||
|
|
0db9d4e69e | ||
|
|
3ec11680ea | ||
|
|
a662f93e15 | ||
|
|
3e91118644 | ||
|
|
fdaed4f700 | ||
|
|
a402d1057e | ||
|
|
a9be5d0dc7 | ||
|
|
88e510b4cf | ||
|
|
7eaf074019 | ||
|
|
91310164be | ||
|
|
734db841f1 | ||
|
|
d41d123969 | ||
|
|
ff807b3d47 | ||
|
|
04ed7a8f5d | ||
|
|
3d7e8f8420 | ||
|
|
c6fcdd854c | ||
|
|
2d886367b5 | ||
|
|
deddbde2b0 | ||
|
|
3e8ed35aea | ||
|
|
95ee94e747 | ||
|
|
a2043d6762 | ||
|
|
1507a87f31 | ||
|
|
09ce38a53d | ||
|
|
dac043a1b6 | ||
|
|
357e8327c4 | ||
|
|
7a93c1bbb1 | ||
|
|
eef54cd77d | ||
|
|
75d88224f1 | ||
|
|
d2a3a64293 | ||
|
|
eb24e01619 | ||
|
|
f970f760aa | ||
|
|
8fb3310d33 | ||
|
|
dd0c5c7e63 | ||
|
|
e7ae6ddbff | ||
|
|
de618393c0 | ||
|
|
387d47808c | ||
|
|
154421ffde | ||
|
|
8b4b08f589 | ||
|
|
027dfe46a4 | ||
|
|
b1b13a5c63 | ||
|
|
8981d71fd0 | ||
|
|
e0b254e6ad | ||
|
|
58fa769ba0 | ||
|
|
c6ed06ba61 | ||
|
|
c52d542ed8 | ||
|
|
1770058e1b | ||
|
|
0a0e19d4c2 | ||
|
|
47a0a7f8c6 | ||
|
|
f3b6e8f7ea | ||
|
|
fab61729b9 | ||
|
|
3381dda884 | ||
|
|
166adcf44d | ||
|
|
1629533242 | ||
|
|
5d8f3036e7 | ||
|
|
8169a2b751 | ||
|
|
916817443e | ||
|
|
40197e5f57 | ||
|
|
d7538bee1b | ||
|
|
fd3c2b521e | ||
|
|
f51a882d23 | ||
|
|
8e1e92c564 | ||
|
|
94f2422a1e | ||
|
|
b79f391be6 | ||
|
|
0c8397e940 | ||
|
|
88281a3a54 | ||
|
|
9256500a23 | ||
|
|
cf040f5df3 | ||
|
|
1a785d83c2 | ||
|
|
af0b14bf58 | ||
|
|
b7852b1d01 | ||
|
|
72eb04ebc6 | ||
|
|
5c7524bcf3 | ||
|
|
31bf984996 | ||
|
|
51457f3550 | ||
|
|
35a9d7ccba | ||
|
|
ad5cd51189 | ||
|
|
58d71b0907 | ||
|
|
6598d56400 | ||
|
|
76554dccd1 | ||
|
|
80d7be8c7d | ||
|
|
db93db7f4e | ||
|
|
c5176b7386 | ||
|
|
a726352c44 | ||
|
|
0823fb2a1e | ||
|
|
256801c0b4 | ||
|
|
d45aa6ae87 | ||
|
|
92466e4a00 | ||
|
|
bced9506c3 | ||
|
|
59f6c861e0 | ||
|
|
21d03e7b69 | ||
|
|
bc369bf6a6 | ||
|
|
95ff33876c | ||
|
|
5bfba0a411 | ||
|
|
4a756843ed | ||
|
|
1b0829f401 | ||
|
|
cf40a38c14 | ||
|
|
6904ba9606 | ||
|
|
7c1a80708b | ||
|
|
77edd9870c | ||
|
|
a6d9016464 | ||
|
|
cb7e7abec5 | ||
|
|
64dd7e9387 | ||
|
|
2b60cbd59c | ||
|
|
c04f9d1879 | ||
|
|
d91589c4da | ||
|
|
6a1349847f | ||
|
|
8e29e16144 | ||
|
|
1cf560c465 | ||
|
|
2a08ae98b0 | ||
|
|
2c656341e2 | ||
|
|
ba20016a83 | ||
|
|
9c2bec7a3d | ||
|
|
84452bec2e | ||
|
|
8f78205406 | ||
|
|
5d251d511a | ||
|
|
c1cd6f42a0 | ||
|
|
fa59e2bded | ||
|
|
a06b478a2e | ||
|
|
16b162ffbe | ||
|
|
ade0fa707e | ||
|
|
8b04a51daf | ||
|
|
1675c43841 | ||
|
|
b90d7c3bec | ||
|
|
ca638f77e3 | ||
|
|
0d0cccea63 | ||
|
|
7cfcef7c0f | ||
|
|
b88b9614f6 | ||
|
|
c1476dda59 | ||
|
|
7d321e2e6c | ||
|
|
de7533a46e | ||
|
|
ba53d63d37 | ||
|
|
7a7b590295 | ||
|
|
f97f2d2fda | ||
|
|
4f26a29196 | ||
|
|
df41dabb71 | ||
|
|
00766041f3 | ||
|
|
db3517b43a | ||
|
|
4999ecd11f | ||
|
|
b3310eaf78 | ||
|
|
0d502095c5 | ||
|
|
2be4811673 | ||
|
|
552070132d | ||
|
|
d365f04199 | ||
|
|
14b30b26c0 | ||
|
|
1cec67725a | ||
|
|
1a8976afb6 | ||
|
|
50d30d4e20 | ||
|
|
ae2eefc73e | ||
|
|
7dda8ebe58 | ||
|
|
724ab0a45e | ||
|
|
aba1f4cf24 | ||
|
|
3e28856944 | ||
|
|
c034877d04 | ||
|
|
183a4cbd75 | ||
|
|
2b88c8d630 | ||
|
|
d3877b0194 | ||
|
|
37e1c7d538 | ||
|
|
ec0f2714e2 | ||
|
|
e0f050c195 | ||
|
|
3f1ad01ac6 | ||
|
|
9f0121e102 | ||
|
|
3b80802d1a | ||
|
|
ed6293e54a | ||
|
|
f8ca65cabc | ||
|
|
5ef135d888 | ||
|
|
bfa69815b4 | ||
|
|
26f4a9c276 | ||
|
|
d549e2ae47 | ||
|
|
3457da16bd | ||
|
|
d0b7d44f1a | ||
|
|
81fca49738 |
@ -1,13 +0,0 @@
|
||||
# This file is used for the configuration of https://codeclimate.com/github/GLolol/PyLink/
|
||||
# You needn't change this if you're running your own copy of PyLink.
|
||||
|
||||
engines:
|
||||
duplication:
|
||||
enabled: true
|
||||
config:
|
||||
languages:
|
||||
- python
|
||||
pep8:
|
||||
enabled: true
|
||||
fixme:
|
||||
enabled: true
|
||||
25
.dockerignore
Normal file
25
.dockerignore
Normal 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
30
.drone-write-tags.sh
Executable 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
94
.drone.jsonnet
Normal 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
4
.gitattributes
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
* eol=lf
|
||||
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@ -3,12 +3,20 @@
|
||||
!example-*.yml
|
||||
!.*.yml
|
||||
|
||||
# Generated from .drone.jsonnet
|
||||
.drone.yml
|
||||
|
||||
# Automatically generated by setup.py
|
||||
/__init__.py
|
||||
|
||||
env/
|
||||
build/
|
||||
__pycache__/
|
||||
.idea/
|
||||
*.py[cod]
|
||||
*.bak
|
||||
*~
|
||||
*#
|
||||
*.save*
|
||||
*.db
|
||||
*.pid
|
||||
@ -16,3 +24,4 @@ __pycache__/
|
||||
.eggs
|
||||
*.egg-info/
|
||||
dist/
|
||||
log/
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal 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
2
.isort.cfg
Normal file
@ -0,0 +1,2 @@
|
||||
[settings]
|
||||
line_length=100
|
||||
9
.mailmap
9
.mailmap
@ -1,2 +1,7 @@
|
||||
James Lu <GLolol@overdrivenetworks.com> <GLolol1@hotmail.com>
|
||||
James Lu <GLolol@overdrivenetworks.com> <GLolol@overdrive.pw>
|
||||
James Lu <james@overdrivenetworks.com> <GLolol@overdrivenetworks.com>
|
||||
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>
|
||||
|
||||
3
AUTHORS
3
AUTHORS
@ -1,4 +1,5 @@
|
||||
The following people have contributed substantially to PyLink:
|
||||
|
||||
James Lu <glolol@overdrivenetworks.com>
|
||||
James Lu <james@overdrivenetworks.com>
|
||||
Daniel Oaks <daniel@danieloaks.net>
|
||||
Ken Spencer <iota@electrocode.net>
|
||||
|
||||
17
Dockerfile
Normal file
17
Dockerfile
Normal 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
167
LICENSE.CC-BY-SA-4.0
Normal 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
|
||||
141
README.md
141
README.md
@ -1,25 +1,85 @@
|
||||
# PyLink
|
||||
# PyLink IRC Services
|
||||
|
||||
## END OF LIFE NOTICE: This project is no longer maintained. So long and thanks for all the fish.
|
||||
|
||||
<!--
|
||||
[](https://github.com/PyLink/PyLink/tree/master)
|
||||
[](https://pypi.python.org/pypi/pylinkirc/)
|
||||
[](https://hub.docker.com/r/jlu5/pylink)
|
||||
[](https://www.python.org/downloads/)
|
||||
-->
|
||||
|
||||
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))
|
||||
|
||||
## Getting help
|
||||
|
||||
**First, MAKE SURE you've read the [FAQ](docs/faq.md)!**
|
||||
|
||||
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.
|
||||
**When upgrading between major versions, remember to read the [release notes](RELNOTES.md) for any breaking changes!**
|
||||
|
||||
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.
|
||||
Please report any bugs you find to the [issue tracker](https://github.com/PyLink/PyLink/issues). Pull requests are likewise welcome.
|
||||
|
||||
## Dependencies
|
||||
## Installation
|
||||
|
||||
* Python 3.4+
|
||||
* 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))
|
||||
* *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
|
||||
|
||||
@ -27,32 +87,55 @@ You can also find support via our IRC channels: `#PyLink @ irc.overdrivenetworks
|
||||
|
||||
These IRCds (in alphabetical order) are frequently tested and well supported. If any issues occur, please file a bug on the issue tracker.
|
||||
|
||||
* [charybdis](http://charybdis.io/) (3.5+ / git master) - module `ts6`
|
||||
* [InspIRCd](http://www.inspircd.org/) 2.0.x - module `inspircd`
|
||||
* [UnrealIRCd](https://www.unrealircd.org/) 4.x - module `unreal`
|
||||
- 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.
|
||||
* [InspIRCd](http://www.inspircd.org/) (2.0 - 3.x) - module `inspircd`
|
||||
- Set the `target_version` option to `insp3` to target InspIRCd 3.x (default), or `insp20` to target InspIRCd 2.0 (legacy).
|
||||
- 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.
|
||||
- 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
|
||||
|
||||
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.
|
||||
|
||||
* [charybdis](https://github.com/charybdis-ircd/charybdis) (3.5+) - module `ts6`
|
||||
- For KLINE support to work, a `shared{}` block should be added for PyLink on all servers.
|
||||
* [ChatIRCd](http://www.chatlounge.net/software) (1.2.x / git master) - module `ts6`
|
||||
- For KLINE support to work, a `shared{}` block should be added for PyLink on all servers.
|
||||
* [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))
|
||||
* [ngIRCd](https://ngircd.barton.de/) (24+) - module `ngircd`
|
||||
- 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.
|
||||
|
||||
### Legacy extended support
|
||||
|
||||
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.
|
||||
|
||||
* [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`
|
||||
* 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](http://www.ircd-hybrid.org/) (8.2.x / svn trunk) - module `hybrid`
|
||||
- Note: for host changing support and optimal functionality, a `service{}` block / U-line should be added for PyLink on every IRCd across your network.
|
||||
* [juno-ircd](https://github.com/cooper/yiria) (10.x / yiria) - module `ts6` (with elemental-ircd modes)
|
||||
* [Nefarious IRCu](https://github.com/evilnet/nefarious2) (2.0.0+) - module `nefarious`
|
||||
- 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.
|
||||
- For optimal functionality (mode overrides in relay, etc.), a `UWorld{}` block / U-line should be added for every server that PyLink spawns.
|
||||
- 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.
|
||||
|
||||
Other TS6 and P10 variations may work, but are not officially supported.
|
||||
### Clientbot
|
||||
|
||||
## Setup
|
||||
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.
|
||||
|
||||
1) Install PyLink by using `python3 setup.py install` (global install) or `python3 setup.py install --user` (local install)
|
||||
For Relay to work properly with Clientbot, be sure to load the `relay_clientbot` plugin in conjunction with `relay`.
|
||||
|
||||
2) Rename `example-conf.yml` to `pylink.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!
|
||||
|
||||
3) Run `pylink` from the command line.
|
||||
|
||||
4) Profit???
|
||||
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
1803
RELNOTES.md
Normal file
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
||||
__version__ = '0.9-dev1'
|
||||
2725
classes.py
2725
classes.py
File diff suppressed because it is too large
Load Diff
129
conf.py
129
conf.py
@ -5,12 +5,25 @@ This module is used to access the configuration of the current PyLink instance.
|
||||
It 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
|
||||
from collections import defaultdict
|
||||
|
||||
from . import world
|
||||
|
||||
__all__ = ['ConfigurationError', 'conf', 'confname', 'validate', 'load_conf',
|
||||
'get_database_name']
|
||||
|
||||
|
||||
class ConfigurationError(RuntimeError):
|
||||
"""Error when config conditions aren't met."""
|
||||
|
||||
conf = {'bot':
|
||||
{
|
||||
'nick': 'PyLink',
|
||||
@ -20,7 +33,7 @@ conf = {'bot':
|
||||
},
|
||||
'logging':
|
||||
{
|
||||
'stdout': 'INFO'
|
||||
'console': 'INFO'
|
||||
},
|
||||
'servers':
|
||||
# Wildcard defaultdict! This means that
|
||||
@ -37,38 +50,104 @@ conf = {'bot':
|
||||
'sidrange': '0##'
|
||||
})
|
||||
}
|
||||
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."""
|
||||
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'):
|
||||
assert conf.get(section), "Missing %r section in config." % section
|
||||
if 'pylink' in conf and 'bot' in conf:
|
||||
_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():
|
||||
for section in ('ip', 'port', 'recvpass', 'sendpass', 'hostname',
|
||||
'sid', 'sidrange', 'protocol'):
|
||||
assert serverblock.get(section), "Missing %r in server block for %r." % (section, netname)
|
||||
new_block = conf['bot'].copy()
|
||||
new_block.update(conf['pylink'])
|
||||
conf['bot'] = conf['pylink'] = new_block
|
||||
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(conf['login'].get('password')) == type(conf['login'].get('user')) == str and \
|
||||
conf['login']['password'] != "changeme", "You have not set the login details correctly!"
|
||||
for section in ('pylink', 'servers', 'login', 'logging'):
|
||||
validate(conf.get(section), "Missing %r section in config." % section)
|
||||
|
||||
# Make sure at least one form of authentication is valid.
|
||||
# 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
|
||||
|
||||
def loadConf(filename, errors_fatal=True):
|
||||
def load_conf(filename, errors_fatal=True, logger=None):
|
||||
"""Loads a PyLink configuration file from the filename given."""
|
||||
global confname, conf, fname
|
||||
# Note: store globally the last loaded conf filename, for REHASH in coremods/control.
|
||||
fname = filename
|
||||
confname = filename.split('.', 1)[0]
|
||||
with open(filename, 'r') as f:
|
||||
try:
|
||||
conf = yaml.load(f)
|
||||
conf = validateConf(conf)
|
||||
except Exception as e:
|
||||
print('ERROR: Failed to load config from %r: %s: %s' % (filename, type(e).__name__, e))
|
||||
if errors_fatal:
|
||||
sys.exit(4)
|
||||
raise
|
||||
else:
|
||||
return conf
|
||||
# For the internal config name, strip off any .yml extensions and absolute paths
|
||||
confname = os.path.splitext(os.path.basename(filename))[0]
|
||||
try:
|
||||
with open(filename, 'r') as f:
|
||||
conf = yaml.safe_load(f)
|
||||
conf = _validate_conf(conf, logger=logger)
|
||||
except Exception as e:
|
||||
e = 'Failed to load config from %r: %s: %s' % (filename, type(e).__name__, e)
|
||||
|
||||
if logger: # Prefer using the Python logger when available
|
||||
logger.exception(e)
|
||||
else: # Otherwise, fall back to a print() call.
|
||||
print('ERROR: %s' % e, file=sys.stderr)
|
||||
|
||||
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
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
# Service support has to be imported first, so that utils.add_cmd works
|
||||
from . import service_support, control, handlers, corecommands
|
||||
# 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
|
||||
|
||||
@ -1,11 +1,18 @@
|
||||
"""
|
||||
control.py - Implements SHUTDOWN and REHASH functionality.
|
||||
"""
|
||||
import signal
|
||||
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']
|
||||
|
||||
from pylinkirc import world, utils, conf, classes
|
||||
from pylinkirc.log import log
|
||||
|
||||
def remove_network(ircobj):
|
||||
"""Removes a network object from the pool."""
|
||||
@ -14,66 +21,142 @@ def remove_network(ircobj):
|
||||
ircobj.disconnect()
|
||||
del world.networkobjects[ircobj.name]
|
||||
|
||||
def _shutdown(irc=None):
|
||||
"""Shuts down the Pylink daemon."""
|
||||
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)
|
||||
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.
|
||||
remove_network(ircobj)
|
||||
try:
|
||||
remove_network(ircobj)
|
||||
except NotImplementedError:
|
||||
continue
|
||||
|
||||
def sigterm_handler(_signo, _stack_frame):
|
||||
"""Handles SIGTERM gracefully by shutting down the PyLink daemon."""
|
||||
log.info("Shutting down on SIGTERM.")
|
||||
_shutdown()
|
||||
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()
|
||||
|
||||
signal.signal(signal.SIGTERM, sigterm_handler)
|
||||
# Done.
|
||||
|
||||
def _rehash():
|
||||
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.loadConf(fname, errors_fatal=False)
|
||||
new_conf = conf.validateConf(new_conf)
|
||||
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 in new conf still.', network)
|
||||
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:
|
||||
ircobj.conf = new_conf
|
||||
# XXX: we should really just add abstraction to Irc to update config settings...
|
||||
ircobj.serverdata = new_conf['servers'][network]
|
||||
ircobj.botdata = new_conf['bot']
|
||||
|
||||
ircobj.autoconnect_active_multiplier = 1
|
||||
|
||||
# Clear the IRC object's channel loggers and replace them with
|
||||
# new ones by re-running logSetup().
|
||||
# new ones by re-running log_setup().
|
||||
while ircobj.loghandlers:
|
||||
log.removeHandler(ircobj.loghandlers.pop())
|
||||
|
||||
ircobj.logSetup()
|
||||
ircobj.log_setup()
|
||||
|
||||
# TODO: update file loggers here too.
|
||||
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) or (not world.networkobjects[network].connection_thread.is_alive()):
|
||||
proto = utils.getProtocolModule(sdata['protocol'])
|
||||
world.networkobjects[network] = classes.Irc(network, proto, new_conf)
|
||||
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 on *nix.
|
||||
def sighup_handler(_signo, _stack_frame):
|
||||
"""Handles SIGHUP by rehashing the PyLink daemon."""
|
||||
log.info("SIGHUP received, reloading config.")
|
||||
_rehash()
|
||||
# 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.SIGHUP, _sighup_handler)
|
||||
signal.signal(signal.SIGUSR1, _sighup_handler)
|
||||
|
||||
@ -5,59 +5,34 @@ corecommands.py - Implements core PyLink commands.
|
||||
import gc
|
||||
import sys
|
||||
|
||||
from . import control
|
||||
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 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)
|
||||
|
||||
control._shutdown(irc)
|
||||
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."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
# 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:
|
||||
@ -66,9 +41,9 @@ def load(irc, source, args):
|
||||
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))
|
||||
log.info('(%s) Loading plugin %r for %s', irc.name, name, irc.get_hostmask(source))
|
||||
try:
|
||||
world.plugins[name] = pl = utils.loadPlugin(name)
|
||||
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)
|
||||
@ -78,7 +53,7 @@ def load(irc, source, args):
|
||||
else:
|
||||
if hasattr(pl, 'main'):
|
||||
log.debug('Calling main() function of plugin %r', pl)
|
||||
pl.main(irc)
|
||||
pl.main(irc=irc)
|
||||
irc.reply("Loaded plugin %r." % name)
|
||||
|
||||
@utils.add_cmd
|
||||
@ -86,14 +61,20 @@ def unload(irc, source, args):
|
||||
"""<plugin name>.
|
||||
|
||||
Unloads a currently loaded plugin."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
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.getHostmask(source))
|
||||
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))
|
||||
|
||||
@ -103,7 +84,7 @@ def unload(irc, source, args):
|
||||
|
||||
for cmdfunc in cmdfuncs:
|
||||
log.debug('__module__ of cmdfunc %s is %s', cmdfunc, cmdfunc.__module__)
|
||||
if cmdfunc.__module__ == name:
|
||||
if cmdfunc.__module__ == modulename:
|
||||
log.debug("Removing %s from world.services['pylink'].commands[%s]", cmdfunc, cmdname)
|
||||
world.services['pylink'].commands[cmdname].remove(cmdfunc)
|
||||
|
||||
@ -113,27 +94,30 @@ def unload(irc, source, args):
|
||||
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)
|
||||
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 hookfuncs:
|
||||
if not hookpairs:
|
||||
del world.hooks[hookname]
|
||||
|
||||
# Call the die() function in the plugin, if present.
|
||||
if hasattr(pl, 'die'):
|
||||
try:
|
||||
pl.die(irc)
|
||||
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]
|
||||
if name in sys.modules:
|
||||
del sys.modules[name]
|
||||
if name in globals():
|
||||
del globals()[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()
|
||||
@ -153,6 +137,8 @@ def reload(irc, source, args):
|
||||
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)
|
||||
|
||||
@ -161,13 +147,21 @@ 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)
|
||||
|
||||
Note: plugins must be manually reloaded."""
|
||||
permissions.check_permissions(irc, source, ['core.rehash'])
|
||||
try:
|
||||
control._rehash()
|
||||
control.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.")
|
||||
|
||||
@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
229
coremods/exttargets.py
Normal 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.
|
||||
@ -1,103 +1,128 @@
|
||||
"""
|
||||
handlers.py - Implements miscellaneous IRC command handlers (WHOIS, services login, etc.)
|
||||
"""
|
||||
import time
|
||||
|
||||
from pylinkirc import utils
|
||||
from pylinkirc import conf, utils
|
||||
from pylinkirc.log import log
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def handle_whois(irc, source, command, args):
|
||||
"""Handle WHOIS queries, for IRCds that send them across servers (charybdis, UnrealIRCd; NOT InspIRCd)."""
|
||||
"""Handle WHOIS queries."""
|
||||
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)
|
||||
f = lambda num, source, text: irc.numeric(irc.sid, num, source, text)
|
||||
|
||||
# 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))
|
||||
# Get the server that the target is on.
|
||||
server = irc.get_server(target)
|
||||
|
||||
# 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 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
|
||||
|
||||
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
|
||||
# Get the full network name.
|
||||
netname = irc.serverdata.get('netname', irc.name)
|
||||
|
||||
# Show prefix modes like a regular IRCd does.
|
||||
for prefixmode in c.getPrefixModes(target):
|
||||
modechar = irc.cmodes[prefixmode]
|
||||
chan = irc.prefixmodes[modechar] + chan
|
||||
# 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))
|
||||
|
||||
public_chans.append(chan)
|
||||
# 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 public_chans: # Only send the line if the person is in any visible channels...
|
||||
f(server, 319, source, '%s :%s' % (nick, ' '.join(public_chans)))
|
||||
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
|
||||
|
||||
# 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))
|
||||
# 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]
|
||||
|
||||
# 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 ''
|
||||
# Fetch the prefix mode letter from the named mode.
|
||||
modechar = irc.cmodes[highest]
|
||||
|
||||
# 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))
|
||||
# Fetch and prepend the prefix character (@, +, etc.), given the mode letter.
|
||||
chan = irc.prefixmodes[modechar] + chan
|
||||
|
||||
# 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)))
|
||||
public_chans.append(chan)
|
||||
|
||||
# 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(server, 301, source, '%s :%s' % (nick, away_text))
|
||||
if public_chans: # Only send the line if the person is in any visible channels...
|
||||
f(319, source, '%s :%s' % (nick, ' '.join(public_chans)))
|
||||
|
||||
# 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))
|
||||
# 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))
|
||||
|
||||
if (irc.umodes.get('bot'), None) in user.modes:
|
||||
# Show botmode info in WHOIS.
|
||||
f(server, 335, source, "%s :is a bot" % nick)
|
||||
# 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
|
||||
|
||||
# Call custom WHOIS handlers via the PYLINK_CUSTOM_WHOIS hook.
|
||||
irc.callHooks([source, 'PYLINK_CUSTOM_WHOIS', {'target': target, 'server': server}])
|
||||
# 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(server, 318, source, "%s :End of /WHOIS list" % nick)
|
||||
f(318, source, "%s :End of /WHOIS list" % nick)
|
||||
utils.add_hook(handle_whois, 'WHOIS')
|
||||
|
||||
def handle_mode(irc, source, command, args):
|
||||
@ -106,15 +131,15 @@ def handle_mode(irc, source, command, args):
|
||||
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)})
|
||||
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.getHostmask(source))
|
||||
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')
|
||||
@ -133,5 +158,58 @@ 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)
|
||||
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
139
coremods/login.py
Normal 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
67
coremods/permissions.py
Normal 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
|
||||
@ -2,9 +2,12 @@
|
||||
service_support.py - Implements handlers for the PyLink ServiceBot API.
|
||||
"""
|
||||
|
||||
from pylinkirc import utils, world, conf
|
||||
from pylinkirc import conf, utils, world
|
||||
from pylinkirc.log import log
|
||||
|
||||
__all__ = []
|
||||
|
||||
|
||||
def spawn_service(irc, source, command, args):
|
||||
"""Handles new service bot introductions."""
|
||||
|
||||
@ -14,51 +17,67 @@ def spawn_service(irc, source, command, args):
|
||||
# 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]
|
||||
|
||||
# Look up the nick or ident in the following order:
|
||||
# 1) Network specific nick/ident settings for this service (servers::irc.name::servicename_nick)
|
||||
# 2) Global settings for this service (servicename::nick)
|
||||
# 3) The preferred nick/ident combination defined by the plugin (sbot.nick / sbot.ident)
|
||||
# 4) The literal service name.
|
||||
# settings, and then falling back to the literal service name.
|
||||
nick = irc.serverdata.get("%s_nick" % name) or irc.conf.get(name, {}).get('nick') or sbot.nick or name
|
||||
ident = irc.serverdata.get("%s_ident" % name) or irc.conf.get(name, {}).get('ident') or sbot.ident or 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
|
||||
|
||||
# TODO: make this configurable?
|
||||
host = irc.serverdata["hostname"]
|
||||
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 = []
|
||||
# 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['bot'].get('protect_services'):
|
||||
preferred_modes.append('servprotect')
|
||||
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))
|
||||
for mode in preferred_modes:
|
||||
mode = irc.umodes.get(mode)
|
||||
if mode:
|
||||
modes.append((mode, None))
|
||||
|
||||
# Track the service's UIDs on each network.
|
||||
userobj = irc.proto.spawnClient(nick, ident, host, modes=modes, opertype="PyLink Service",
|
||||
manipulatable=sbot.manipulatable)
|
||||
# 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':
|
||||
if name == 'pylink' and not irc.pseudoclient:
|
||||
log.debug('(%s) spawn_service: irc.pseudoclient set to UID %s', irc.name, u)
|
||||
irc.pseudoclient = userobj
|
||||
|
||||
# TODO: channels should be tracked in a central database, not hardcoded
|
||||
# in conf.
|
||||
channels = set(irc.serverdata.get('channels', [])) | sbot.extra_channels.get(irc.name, set())
|
||||
|
||||
for chan in channels:
|
||||
irc.proto.join(u, chan)
|
||||
# Enumerate & join network defined channels.
|
||||
sbot.join(irc, sbot.get_persistent_channels(irc))
|
||||
|
||||
utils.add_hook(spawn_service, 'PYLINK_NEW_SERVICE')
|
||||
|
||||
@ -82,23 +101,76 @@ def handle_endburst(irc, source, command, args):
|
||||
for name, sbot in world.services.items():
|
||||
spawn_service(irc, source, command, {'name': name})
|
||||
|
||||
utils.add_hook(handle_endburst, 'ENDBURST')
|
||||
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']
|
||||
sbot = irc.isServiceBot(target)
|
||||
if sbot:
|
||||
spawn_service(irc, source, command, {'name': sbot.name})
|
||||
return
|
||||
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."""
|
||||
kicked = args['target']
|
||||
channel = args['channel']
|
||||
if irc.isServiceBot(kicked):
|
||||
irc.proto.join(kicked, 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):
|
||||
@ -106,18 +178,14 @@ def handle_commands(irc, source, command, args):
|
||||
target = args['target']
|
||||
text = args['text']
|
||||
|
||||
sbot = irc.isServiceBot(target)
|
||||
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!
|
||||
mynick = conf.conf['bot'].get("nick", "PyLink")
|
||||
myident = conf.conf['bot'].get("ident", "pylink")
|
||||
|
||||
# TODO: be more specific, and possibly allow plugins to modify this to mention
|
||||
# TODO: be more specific in description, and possibly allow plugins to modify this to mention
|
||||
# their features?
|
||||
mydesc = "\x02%s\x02 provides extended network services for IRC." % mynick
|
||||
|
||||
utils.registerService('pylink', nick=mynick, ident=myident, desc=mydesc, manipulatable=True)
|
||||
mydesc = "\x02PyLink\x02 provides extended network services for IRC."
|
||||
utils.register_service('pylink', default_nick="PyLink", desc=mydesc, manipulatable=True)
|
||||
|
||||
@ -1,9 +1,23 @@
|
||||
# PyLink Documentation
|
||||
|
||||
This folder contains general documentation for PyLink IRC services.
|
||||
|
||||
## Contents
|
||||
|
||||
- [PyLink FAQ (Frequently Asked Questions)](faq.md)
|
||||
- [PyLink Relay Tutorial & Oper Guide](pylink-opers.md)
|
||||
- [Developer documentation](technical/)
|
||||
# PyLink Documentation
|
||||
|
||||
This folder contains general documentation for PyLink IRC services.
|
||||
|
||||
## Contents
|
||||
|
||||
- [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/)
|
||||
|
||||
There is also a Doxygen-powered API reference at https://pylink.github.io/
|
||||
|
||||
93
docs/advanced-relay-config.md
Normal file
93
docs/advanced-relay-config.md
Normal 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.
|
||||
61
docs/advanced-services-config.md
Normal file
61
docs/advanced-services-config.md
Normal 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
37
docs/automode.md
Normal 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
72
docs/exttargets.md
Normal 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.
|
||||
170
docs/faq.md
170
docs/faq.md
@ -1,26 +1,144 @@
|
||||
# PyLink FAQ
|
||||
|
||||
### Does everyone need to install PyLink Relay for it to work?
|
||||
|
||||
**No!** Only the link administrator needs to host a PyLink instance, as each 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/GLolol/PyLink/issues/99 for progress regarding that.
|
||||
|
||||
### Does PyLink support Clientbot relay like Janus?
|
||||
|
||||
Not yet; see https://github.com/GLolol/PyLink/issues/144.
|
||||
|
||||
### What are PyLink Relay's benefits over Janus?
|
||||
|
||||
In no particular order:
|
||||
- More complete support for modern IRCds (UnrealIRCd 4.x, InspIRCd 2.0, charybdis 4, Nefarious IRCu, etc.).
|
||||
- Built upon a flexible, maintainable codebase.
|
||||
- Cross platform (*nix and Windows).
|
||||
- Proper protocol negotiation: better support for channel modes (+fjMOR, etc.), nick length limits, and WHOIS.
|
||||
|
||||
### I turned autoconnect for PyLink on, and now I'm getting errors!
|
||||
|
||||
PyLink does not support inbound connections - much like 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)
|
||||
|
||||
### I get errors like "ImportError: No module named 'yaml'" when I start PyLink
|
||||
- You are missing dependencies - re-read https://github.com/GLolol/PyLink#dependencies.
|
||||
# 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 <block end>, but found '<block sequence start>'
|
||||
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
5
docs/modelists/README.md
Normal 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)
|
||||
71
docs/modelists/channel-modes.csv
Normal file
71
docs/modelists/channel-modes.csv
Normal 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.",,,,,,,,,,,,,
|
||||
|
281
docs/modelists/channel-modes.html
Normal file
281
docs/modelists/channel-modes.html
Normal 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>
|
||||
40
docs/modelists/extbans.csv
Normal file
40
docs/modelists/extbans.csv
Normal 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:
|
||||
|
191
docs/modelists/extbans.html
Normal file
191
docs/modelists/extbans.html
Normal 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>
|
||||
142
docs/modelists/modelists-genhtml.py
Executable file
142
docs/modelists/modelists-genhtml.py
Executable 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>""")
|
||||
55
docs/modelists/user-modes.csv
Normal file
55
docs/modelists/user-modes.csv
Normal 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,,,,,,,,,,
|
||||
|
244
docs/modelists/user-modes.html
Normal file
244
docs/modelists/user-modes.html
Normal 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>
|
||||
121
docs/permissions-reference.md
Normal file
121
docs/permissions-reference.md
Normal 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.
|
||||
@ -1,82 +1 @@
|
||||
# Opering with PyLink Relay
|
||||
|
||||
*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.
|
||||
|
||||
## Relay 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`
|
||||
|
||||
To remove a relay channel that you've created:
|
||||
- `/msg PyLink destroy #channelname`
|
||||
|
||||
To delink a channel linked to another network:
|
||||
- `/msg PyLink delink #channelname`
|
||||
|
||||
### Claiming channels
|
||||
|
||||
PyLink offers channel claims similarly to Janus, except that it is on by default when you create a channel on any network. Unless the claimed network list of a channel is EMPTY, oper override (MODE, KICK, TOPIC) will only be allowed from networks on that list.
|
||||
|
||||
To set a claim (note: for these commands, you must be on the network which created the channel in question!):
|
||||
- `/msg PyLink claim #channel yournet,net2,net3` (the last parameter is a comma-separated list of networks, case-sensitive)
|
||||
|
||||
To list claims on a channel:
|
||||
- `/msg PyLink claim #channel`
|
||||
|
||||
To remove claims from a channel
|
||||
- `/msg PyLink claim #channel -`
|
||||
|
||||
### Access control for links (LINKACL)
|
||||
LINKACL allows you to block certain networks from linking to your relay channels, based on a blacklist. By default, this blacklist is empty.
|
||||
|
||||
To list blocked networks for a channel:
|
||||
- `/msg PyLink linkacl #channel list`
|
||||
|
||||
To add a network to the blacklist:
|
||||
- `/msg PyLink linkacl #channel allow badnet`
|
||||
|
||||
To remove a network from the blacklist:
|
||||
- `/msg PyLink linkacl #channel deny goodnet`
|
||||
|
||||
Whitelists with LINKACL are not supported at this time.
|
||||
|
||||
## 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)
|
||||
|
||||
Moved to [relay-quickstart.md](relay-quickstart.md).
|
||||
|
||||
146
docs/relay-quickstart.md
Normal file
146
docs/relay-quickstart.md
Normal 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`.
|
||||
@ -1,26 +1,28 @@
|
||||
# PyLink Developer Documentation
|
||||
|
||||
Please note that as PyLink is still in its development phase, its APIs are subject to change.
|
||||
Any documentation here is provided for reference only.
|
||||
This documentation is provided for reference only, and may not always be up to date as APIs change.
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
- [Writing plugins for PyLink](writing-plugins.md)
|
||||
- [PyLink protocol module specification](pmodule-spec.md)
|
||||
- [PyLink hooks reference](hooks-reference.md)
|
||||
- [Supported named channel modes](channel-modes.csv)
|
||||
- [Supported named user modes](user-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)
|
||||
- [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)
|
||||
|
||||

|
||||
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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 - Base classes for PyLink IRC Services.<br>
|
||||
<br>
|
||||
This module contains the base classes used by PyLink, including threaded IRC<br>
|
||||
connections and objects used to represent IRC servers, users, and channels.<br>
|
||||
<br>
|
||||
Here be dragons.</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </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> <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> </tt></td>
|
||||
<td colspan=2><tt>Fake IRC <a href="builtins.html#object">object</a> used for unit tests.<br> </tt></td></tr>
|
||||
<tr><td> </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 the connect loop for the IRC <a href="builtins.html#object">object</a>. This is usually called by<br>
|
||||
__init__ in a separate thread to allow multiple concurrent connections.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-run"><strong>run</strong></a>(self, data)</dt><dd><tt>Queues a message to the fake IRC server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-send"><strong>send</strong></a>(self, data)</dt><dd><tt>Sends raw text to the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-takeCommands"><strong>takeCommands</strong></a>(self, msgs)</dt><dd><tt>Returns a list of commands parsed from the output of <a href="#FakeIRC-takeMsgs">takeMsgs</a>().</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-takeHooks"><strong>takeHooks</strong></a>(self)</dt><dd><tt>Returns a list of hook arguments sent by the protocol module since<br>
|
||||
the last <a href="#FakeIRC-takeHooks">takeHooks</a>() call.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-takeMsgs"><strong>takeMsgs</strong></a>(self)</dt><dd><tt>Returns a list of messages sent by the protocol module since<br>
|
||||
the last <a href="#FakeIRC-takeMsgs">takeMsgs</a>() call, so we can track what has been 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 an IRC <a href="builtins.html#object">object</a>. This takes 3 variables: the network name<br>
|
||||
(a string), the name of the protocol module to use for this connection,<br>
|
||||
and a configuration <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 repr(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-applyModes"><strong>applyModes</strong></a>(self, target, changedmodes)</dt><dd><tt>Takes a list of parsed IRC modes, and applies them on the given target.<br>
|
||||
<br>
|
||||
The target can be either a channel or a user; this is handled automatically.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-callCommand"><strong>callCommand</strong></a>(self, source, text)</dt><dd><tt>Calls a PyLink bot command. source is the caller's UID, and text is the<br>
|
||||
full, unparsed text of the message.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-callHooks"><strong>callHooks</strong></a>(self, hook_args)</dt><dd><tt>Calls a hook function with the given hook args.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-checkAuthenticated"><strong>checkAuthenticated</strong></a>(self, uid, allowAuthed=True, allowOper=True)</dt><dd><tt>Checks whether the given user has operator status on PyLink, raising<br>
|
||||
NotAuthenticatedError and logging the access denial if not.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-disconnect"><strong>disconnect</strong></a>(self)</dt><dd><tt>Handle disconnects from the remote server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-getHostmask"><strong>getHostmask</strong></a>(self, user, realhost=False, ip=False)</dt><dd><tt>Returns the hostmask of the given user, if present. If the realhost option<br>
|
||||
is given, return the real host of the user instead of the displayed host.<br>
|
||||
If the ip option is given, return the IP address of the user (this overrides<br>
|
||||
realhost).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-getServer"><strong>getServer</strong></a>(self, numeric)</dt><dd><tt>Finds the SID of the server a user is on.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-initVars"><strong>initVars</strong></a>(self)</dt><dd><tt>(Re)sets an IRC <a href="builtins.html#object">object</a> to its default state. This should be called when<br>
|
||||
an IRC <a href="builtins.html#object">object</a> is first created, and on every reconnection to a network.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-isInternalClient"><strong>isInternalClient</strong></a>(self, numeric)</dt><dd><tt>Checks whether the given numeric is a PyLink Client,<br>
|
||||
returning the SID of the server it's on if so.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-isInternalServer"><strong>isInternalServer</strong></a>(self, sid)</dt><dd><tt>Returns whether the given SID is an internal PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-isManipulatableClient"><strong>isManipulatableClient</strong></a>(self, uid)</dt><dd><tt>Returns whether the given user is marked as an internal, manipulatable<br>
|
||||
client. Usually, automatically spawned services clients should have this<br>
|
||||
set True to prevent interactions with opers (like mode changes) from<br>
|
||||
causing desyncs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-isOper"><strong>isOper</strong></a>(self, uid, allowAuthed=True, allowOper=True)</dt><dd><tt>Returns whether the given user has operator status on PyLink. This can be achieved<br>
|
||||
by either identifying to PyLink as admin (if allowAuthed is True),<br>
|
||||
or having user mode +o set (if allowOper is True). At least one of<br>
|
||||
allowAuthed or allowOper must be True for this to give any meaningful<br>
|
||||
results.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-isServiceBot"><strong>isServiceBot</strong></a>(self, uid)</dt><dd><tt>Checks whether the given UID is a registered service bot. If True,<br>
|
||||
returns the cooresponding ServiceBot <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 any channel loggers defined for the current 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 function to send messages/notices to clients. Source<br>
|
||||
is optional, and defaults to the main PyLink client if not specified.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-nickToUid"><strong>nickToUid</strong></a>(self, nick)</dt><dd><tt>Looks up the UID of a user with the given nick, if one is present.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-parseModes"><strong>parseModes</strong></a>(self, target, args)</dt><dd><tt>Parses a modestring list into a list of (mode, argument) tuples.<br>
|
||||
['+mitl-o', '3', 'person'] => [('+m', None), ('+i', None), ('+t', None), ('+l', '3'), ('-o', 'person')]</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-reply"><strong>reply</strong></a>(self, text, notice=False, source=None)</dt><dd><tt>Replies to the last caller in the right context (channel or PM).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-reverseModes"><strong>reverseModes</strong></a>(self, target, modes, oldobj=None)</dt><dd><tt>Reverses/Inverts the mode string or mode list given.<br>
|
||||
<br>
|
||||
Optionally, an oldobj argument can be given to look at an earlier state of<br>
|
||||
a channel/user <a href="builtins.html#object">object</a>, e.g. for checking the op status of a mode setter<br>
|
||||
before their modes are processed and added to the channel state.<br>
|
||||
<br>
|
||||
This function allows both mode strings or mode lists. Example uses:<br>
|
||||
"+mi-lk test => "-mi+lk test"<br>
|
||||
"mi-k test => "-mi+k test"<br>
|
||||
[('+m', None), ('+r', None), ('+l', '3'), ('-o', 'person')<br>
|
||||
=> {('-m', None), ('-r', None), ('-l', None), ('+o', 'person')})<br>
|
||||
{('s', None), ('+o', 'whoever') => {('-s', None), ('-o', 'whoever')})</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-runline"><strong>runline</strong></a>(self, line)</dt><dd><tt>Sends a command to the protocol module.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-schedulePing"><strong>schedulePing</strong></a>(self)</dt><dd><tt>Schedules periodic pings in a loop.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-toLower"><strong>toLower</strong></a>(self, text)</dt><dd><tt>Returns a lowercase representation of text based on the IRC <a href="builtins.html#object">object</a>'s<br>
|
||||
casemapping (rfc1459 or ascii).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeIRC-version"><strong>version</strong></a>(self)</dt><dd><tt>Returns a detailed version string including the PyLink daemon version,<br>
|
||||
the protocol module in use, and the server 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 a list of (mode, arg) tuples in <a href="#FakeIRC-parseModes">parseModes</a>() format, and<br>
|
||||
joins them into a string.<br>
|
||||
<br>
|
||||
See testJoinModes in tests/test_utils.py for some 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Dummy protocol module for testing purposes.<br> </tt></td></tr>
|
||||
<tr><td> </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> = <class 'classes.FakeProto'><dd><tt>Dummy protocol module for testing 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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeProto-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeProto-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="FakeProto-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base IRC <a href="builtins.html#object">object</a> for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 an IRC <a href="builtins.html#object">object</a>. This takes 3 variables: the network name<br>
|
||||
(a string), the name of the protocol module to use for this connection,<br>
|
||||
and a configuration <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 repr(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-applyModes"><strong>applyModes</strong></a>(self, target, changedmodes)</dt><dd><tt>Takes a list of parsed IRC modes, and applies them on the given target.<br>
|
||||
<br>
|
||||
The target can be either a channel or a user; this is handled automatically.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-callCommand"><strong>callCommand</strong></a>(self, source, text)</dt><dd><tt>Calls a PyLink bot command. source is the caller's UID, and text is the<br>
|
||||
full, unparsed text of the message.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-callHooks"><strong>callHooks</strong></a>(self, hook_args)</dt><dd><tt>Calls a hook function with the given hook args.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-checkAuthenticated"><strong>checkAuthenticated</strong></a>(self, uid, allowAuthed=True, allowOper=True)</dt><dd><tt>Checks whether the given user has operator status on PyLink, raising<br>
|
||||
NotAuthenticatedError and logging the access denial if not.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Runs the connect loop for the IRC <a href="builtins.html#object">object</a>. This is usually called by<br>
|
||||
__init__ in a separate thread to allow multiple concurrent connections.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-disconnect"><strong>disconnect</strong></a>(self)</dt><dd><tt>Handle disconnects from the remote server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-getHostmask"><strong>getHostmask</strong></a>(self, user, realhost=False, ip=False)</dt><dd><tt>Returns the hostmask of the given user, if present. If the realhost option<br>
|
||||
is given, return the real host of the user instead of the displayed host.<br>
|
||||
If the ip option is given, return the IP address of the user (this overrides<br>
|
||||
realhost).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-getServer"><strong>getServer</strong></a>(self, numeric)</dt><dd><tt>Finds the SID of the server a user is on.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-initVars"><strong>initVars</strong></a>(self)</dt><dd><tt>(Re)sets an IRC <a href="builtins.html#object">object</a> to its default state. This should be called when<br>
|
||||
an IRC <a href="builtins.html#object">object</a> is first created, and on every reconnection to a network.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-isInternalClient"><strong>isInternalClient</strong></a>(self, numeric)</dt><dd><tt>Checks whether the given numeric is a PyLink Client,<br>
|
||||
returning the SID of the server it's on if so.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-isInternalServer"><strong>isInternalServer</strong></a>(self, sid)</dt><dd><tt>Returns whether the given SID is an internal PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-isManipulatableClient"><strong>isManipulatableClient</strong></a>(self, uid)</dt><dd><tt>Returns whether the given user is marked as an internal, manipulatable<br>
|
||||
client. Usually, automatically spawned services clients should have this<br>
|
||||
set True to prevent interactions with opers (like mode changes) from<br>
|
||||
causing desyncs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-isOper"><strong>isOper</strong></a>(self, uid, allowAuthed=True, allowOper=True)</dt><dd><tt>Returns whether the given user has operator status on PyLink. This can be achieved<br>
|
||||
by either identifying to PyLink as admin (if allowAuthed is True),<br>
|
||||
or having user mode +o set (if allowOper is True). At least one of<br>
|
||||
allowAuthed or allowOper must be True for this to give any meaningful<br>
|
||||
results.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-isServiceBot"><strong>isServiceBot</strong></a>(self, uid)</dt><dd><tt>Checks whether the given UID is a registered service bot. If True,<br>
|
||||
returns the cooresponding ServiceBot <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 any channel loggers defined for the current 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 function to send messages/notices to clients. Source<br>
|
||||
is optional, and defaults to the main PyLink client if not specified.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-nickToUid"><strong>nickToUid</strong></a>(self, nick)</dt><dd><tt>Looks up the UID of a user with the given nick, if one is present.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-parseModes"><strong>parseModes</strong></a>(self, target, args)</dt><dd><tt>Parses a modestring list into a list of (mode, argument) tuples.<br>
|
||||
['+mitl-o', '3', 'person'] => [('+m', None), ('+i', None), ('+t', None), ('+l', '3'), ('-o', 'person')]</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-reply"><strong>reply</strong></a>(self, text, notice=False, source=None)</dt><dd><tt>Replies to the last caller in the right context (channel or PM).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-reverseModes"><strong>reverseModes</strong></a>(self, target, modes, oldobj=None)</dt><dd><tt>Reverses/Inverts the mode string or mode list given.<br>
|
||||
<br>
|
||||
Optionally, an oldobj argument can be given to look at an earlier state of<br>
|
||||
a channel/user <a href="builtins.html#object">object</a>, e.g. for checking the op status of a mode setter<br>
|
||||
before their modes are processed and added to the channel state.<br>
|
||||
<br>
|
||||
This function allows both mode strings or mode lists. Example uses:<br>
|
||||
"+mi-lk test => "-mi+lk test"<br>
|
||||
"mi-k test => "-mi+k test"<br>
|
||||
[('+m', None), ('+r', None), ('+l', '3'), ('-o', 'person')<br>
|
||||
=> {('-m', None), ('-r', None), ('-l', None), ('+o', 'person')})<br>
|
||||
{('s', None), ('+o', 'whoever') => {('-s', None), ('-o', 'whoever')})</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-run"><strong>run</strong></a>(self)</dt><dd><tt>Main IRC loop which listens for messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-runline"><strong>runline</strong></a>(self, line)</dt><dd><tt>Sends a command to the protocol module.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-schedulePing"><strong>schedulePing</strong></a>(self)</dt><dd><tt>Schedules periodic pings in a loop.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-send"><strong>send</strong></a>(self, data)</dt><dd><tt>Sends raw text to the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-toLower"><strong>toLower</strong></a>(self, text)</dt><dd><tt>Returns a lowercase representation of text based on the IRC <a href="builtins.html#object">object</a>'s<br>
|
||||
casemapping (rfc1459 or ascii).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Irc-version"><strong>version</strong></a>(self)</dt><dd><tt>Returns a detailed version string including the PyLink daemon version,<br>
|
||||
the protocol module in use, and the server 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 a list of (mode, arg) tuples in <a href="#Irc-parseModes">parseModes</a>() format, and<br>
|
||||
joins them into a string.<br>
|
||||
<br>
|
||||
See testJoinModes in tests/test_utils.py for some examples.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>PyLink IRC channel class.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return repr(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-deepcopy"><strong>deepcopy</strong></a>(self)</dt><dd><tt>Returns a deep copy of the channel <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 a list of all named prefix modes the given user has in the channel.<br>
|
||||
<br>
|
||||
Optionally, a prefixmodes argument can be given to look at an earlier state of<br>
|
||||
the channel's prefix modes mapping, e.g. for checking the op status of a mode<br>
|
||||
setter before their modes are processed and added to the channel state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-isAdmin"><strong>isAdmin</strong></a>(self, uid)</dt><dd><tt>Returns whether the given user is admin (&) in the channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-isHalfop"><strong>isHalfop</strong></a>(self, uid)</dt><dd><tt>Returns whether the given user is halfop in the channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-isHalfopPlus"><strong>isHalfopPlus</strong></a>(self, uid)</dt><dd><tt>Returns whether the given user is halfop or above in the channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-isOp"><strong>isOp</strong></a>(self, uid)</dt><dd><tt>Returns whether the given user is op in the channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-isOpPlus"><strong>isOpPlus</strong></a>(self, uid)</dt><dd><tt>Returns whether the given user is op or above in the channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-isOwner"><strong>isOwner</strong></a>(self, uid)</dt><dd><tt>Returns whether the given user is owner (~) in the channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-isVoice"><strong>isVoice</strong></a>(self, uid)</dt><dd><tt>Returns whether the given user is voice in the channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-isVoicePlus"><strong>isVoicePlus</strong></a>(self, uid)</dt><dd><tt>Returns whether the given user is voice or above in the channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcChannel-removeuser"><strong>removeuser</strong></a>(self, target)</dt><dd><tt>Removes a user from a channel.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>PyLink IRC server class.<br>
|
||||
<br>
|
||||
uplink: The SID of this <a href="#IrcServer">IrcServer</a> instance's uplink. This is set to None<br>
|
||||
for the main PyLink PseudoServer!<br>
|
||||
name: The name of the server.<br>
|
||||
internal: Whether the server is an internal PyLink PseudoServer.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcServer-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return repr(self).</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>PyLink IRC user class.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IrcUser-__repr__"><strong>__repr__</strong></a>(self)</dt><dd><tt>Return repr(self).</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base <a href="#Protocol">Protocol</a> module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </td>
|
||||
<td width="100%">Methods defined here:<br>
|
||||
<dl><dt><a name="Protocol-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Protocol-parseArgs"><strong>parseArgs</strong></a>(self, args)</dt><dd><tt>Parses a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Protocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="Protocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is older.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Common base class for all non-exit exceptions.<br> </tt></td></tr>
|
||||
<tr><td> </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 of weak references to the object (if 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 self. See help(type(self)) for accurate 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 and return a new <a href="builtins.html#object">object</a>. See help(type) for accurate 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 delattr(self, name).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="ProtocolError-__getattribute__"><strong>__getattribute__</strong></a>(self, name, /)</dt><dd><tt>Return getattr(self, name).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="ProtocolError-__reduce__"><strong>__reduce__</strong></a>(...)</dt><dd><tt>helper for pickle</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="ProtocolError-__repr__"><strong>__repr__</strong></a>(self, /)</dt><dd><tt>Return repr(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="ProtocolError-__setattr__"><strong>__setattr__</strong></a>(self, name, value, /)</dt><dd><tt>Implement setattr(self, name, 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 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) --<br>
|
||||
set self.<strong>__traceback__</strong> to tb and return 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 cause</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__context__</strong></dt>
|
||||
<dd><tt>exception 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </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(<function <lambda> at 0x7fcfaf78de18>, {})}<br>
|
||||
<strong>confname</strong> = 'testconf'<br>
|
||||
<strong>curdir</strong> = '/home/gl/pylink'<br>
|
||||
<strong>files</strong> = None<br>
|
||||
<strong>log</strong> = <logging.RootLogger object><br>
|
||||
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
|
||||
<strong>logformatter</strong> = <logging.Formatter object><br>
|
||||
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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 - PyLink configuration core.<br>
|
||||
<br>
|
||||
This module is used to access the complete configuration for the current<br>
|
||||
PyLink instance. It will load the config on first import, taking the<br>
|
||||
configuration file name from the first command-line argument, but defaulting<br>
|
||||
to 'config.yml' if this isn't given.<br>
|
||||
<br>
|
||||
If world.testing is set to True, it will return a preset testing configuration<br>
|
||||
instead.<br>
|
||||
<br>
|
||||
This module also provides simple checks for validating and loading YAML-format<br>
|
||||
configurations from arbitrary files.</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><dl><dt><a name="-loadConf"><strong>loadConf</strong></a>(fname, errors_fatal=True)</dt><dd><tt>Loads a PyLink configuration file from the filename given.</tt></dd></dl>
|
||||
<dl><dt><a name="-validateConf"><strong>validateConf</strong></a>(conf)</dt><dd><tt>Validates a parsed configuration 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </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(<function <lambda> at 0x7fee74c229d8>, {})}<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(<function <lambda> at 0x7fee74c229d8>, {})}</td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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 - Implements core PyLink functions as a plugin.</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><dl><dt><a name="-handle_commands"><strong>handle_commands</strong></a>(irc, source, command, args)</dt><dd><tt>Handle commands sent to the PyLink service bots (PRIVMSG).</tt></dd></dl>
|
||||
<dl><dt><a name="-handle_disconnect"><strong>handle_disconnect</strong></a>(irc, source, command, args)</dt><dd><tt>Handles network disconnections.</tt></dd></dl>
|
||||
<dl><dt><a name="-handle_endburst"><strong>handle_endburst</strong></a>(irc, source, command, args)</dt><dd><tt>Handles network bursts.</tt></dd></dl>
|
||||
<dl><dt><a name="-handle_kick"><strong>handle_kick</strong></a>(irc, source, command, args)</dt><dd><tt>Handle KICKs to the PyLink service bots, rejoining channels as needed.</tt></dd></dl>
|
||||
<dl><dt><a name="-handle_kill"><strong>handle_kill</strong></a>(irc, source, command, args)</dt><dd><tt>Handle KILLs to PyLink service bots, respawning them as needed.</tt></dd></dl>
|
||||
<dl><dt><a name="-handle_mode"><strong>handle_mode</strong></a>(irc, source, command, args)</dt><dd><tt>Protect against forced deoper attempts.</tt></dd></dl>
|
||||
<dl><dt><a name="-handle_operup"><strong>handle_operup</strong></a>(irc, source, command, args)</dt><dd><tt>Logs successful oper-ups on 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 services login status for users.</tt></dd></dl>
|
||||
<dl><dt><a name="-handle_version"><strong>handle_version</strong></a>(irc, source, command, args)</dt><dd><tt>Handles requests for the PyLink server version.</tt></dd></dl>
|
||||
<dl><dt><a name="-handle_whois"><strong>handle_whois</strong></a>(irc, source, command, args)</dt><dd><tt>Handle WHOIS queries, for IRCds that send them across servers (charybdis, UnrealIRCd; NOT InspIRCd).</tt></dd></dl>
|
||||
<dl><dt><a name="-identify"><strong>identify</strong></a>(irc, source, args)</dt><dd><tt><username> <password><br>
|
||||
<br>
|
||||
Logs in to PyLink using the configured administrator account.</tt></dd></dl>
|
||||
<dl><dt><a name="-load"><strong>load</strong></a>(irc, source, args)</dt><dd><tt><plugin name>.<br>
|
||||
<br>
|
||||
Loads a plugin from the plugin folder.</tt></dd></dl>
|
||||
<dl><dt><a name="-rehash"><strong>rehash</strong></a>(irc, source, args)</dt><dd><tt>takes no arguments.<br>
|
||||
<br>
|
||||
Reloads the configuration file for PyLink, (dis)connecting added/removed networks.<br>
|
||||
Plugins must be manually reloaded.</tt></dd></dl>
|
||||
<dl><dt><a name="-reload"><strong>reload</strong></a>(irc, source, args)</dt><dd><tt><plugin name>.<br>
|
||||
<br>
|
||||
Loads a plugin from the plugin folder.</tt></dd></dl>
|
||||
<dl><dt><a name="-shutdown"><strong>shutdown</strong></a>(irc, source, args)</dt><dd><tt>takes no arguments.<br>
|
||||
<br>
|
||||
Exits PyLink by disconnecting all networks.</tt></dd></dl>
|
||||
<dl><dt><a name="-sighup_handler"><strong>sighup_handler</strong></a>(_signo, _stack_frame)</dt><dd><tt>Handles SIGHUP by rehashing the PyLink daemon.</tt></dd></dl>
|
||||
<dl><dt><a name="-sigterm_handler"><strong>sigterm_handler</strong></a>(_signo, _stack_frame)</dt><dd><tt>Handles SIGTERM gracefully by shutting down the PyLink daemon.</tt></dd></dl>
|
||||
<dl><dt><a name="-spawn_service"><strong>spawn_service</strong></a>(irc, source, command, args)</dt><dd><tt>Handles new service bot introductions.</tt></dd></dl>
|
||||
<dl><dt><a name="-unload"><strong>unload</strong></a>(irc, source, args)</dt><dd><tt><plugin name>.<br>
|
||||
<br>
|
||||
Unloads a currently loaded 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><strong>log</strong> = <logging.RootLogger object><br>
|
||||
<strong>myident</strong> = 'pylink'<br>
|
||||
<strong>mynick</strong> = 'PyLink'</td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base Protocol module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes a connection to a server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_capab"><strong>handle_capab</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the CAPAB command, used for TS6 capability 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 SVSMODE, which is used for sending services metadata<br>
|
||||
(vhosts, account logins), and other forced usermode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_tburst"><strong>handle_tburst</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming topic burst (TBURST) commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_uid"><strong>handle_uid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles Hybrid-style UID commands (user introduction). This is INCOMPATIBLE<br>
|
||||
with standard TS6 implementations, as the arguments are slightly 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 a new client with the given options.<br>
|
||||
<br>
|
||||
Note: No nick collision / valid nickname checks are done here; it is<br>
|
||||
up to plugins to make sure they don't introduce anything invalid.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a topic change from a PyLink server. This is usually used on burst.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates the ident, host, or realname of a PyLink 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 the incoming 472 numeric.<br>
|
||||
<br>
|
||||
472 is sent to us when one of our clients tries to set a mode the uplink<br>
|
||||
server doesn't support. In this case, we'll raise a warning to alert<br>
|
||||
the administrator that certain extensions should be loaded for the 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 incoming BMASK commands (ban propagation on burst).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming CHGHOST commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_encap"><strong>handle_encap</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the ENCAP command - encapsulated TS6 commands with a variety of<br>
|
||||
subcommands used for different purposes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_euid"><strong>handle_euid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming EUID commands (user introduction).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming INVITEs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming channel JOINs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming user mode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_pass"><strong>handle_pass</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the PASS command, used to send the server's SID and negotiate<br>
|
||||
passwords on connect.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PING commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PONG commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles 1) incoming legacy (no SID) server introductions,<br>
|
||||
2) Sending server data in initial connection.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming server introductions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles incoming SJOIN commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_tb"><strong>handle_tb</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming topic burst (TB) commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_tmode"><strong>handle_tmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TMODE commands (channel mode change).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming WHOIS commands.<br>
|
||||
<br>
|
||||
Note: The core of WHOIS handling is done by coreplugin.py<br>
|
||||
(IRCd-independent), and not here.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends an INVITE from a PyLink client..</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins a PyLink client to a channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends a kill from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a KNOCK from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends mode changes from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends a PING to a target server. Periodic PINGs are sent to our uplink<br>
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends an SJOIN for a group of users to a channel.<br>
|
||||
<br>
|
||||
The sender should always be a Server ID (SID). TS is optional, and defaults<br>
|
||||
to the one we've stored in the channel state if not given.<br>
|
||||
<users> is a list of (prefix mode, UID) pairs:<br>
|
||||
<br>
|
||||
Example uses:<br>
|
||||
<a href="#Class-sjoin">sjoin</a>('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])<br>
|
||||
<a href="#Class-sjoin">sjoin</a>(self.<strong>irc</strong>.sid, '#test', [('o', 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 an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles ERROR messages - these mean that our uplink has disconnected us!</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for TS6 protocols.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions<br>
|
||||
elsewhere defined protocol modules, coersing various sender prefixes<br>
|
||||
from nicks and server names to UIDs and SIDs respectively,<br>
|
||||
whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix will have them set to<br>
|
||||
the SID of the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KILLs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming NICK 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 incoming 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 incoming PART commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming 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 incoming QUIT commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SAVE messages, used to handle nick collisions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SQUITs (netsplits).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TOPIC changes from clients. For topic bursts,<br>
|
||||
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles requests for the PyLink server version.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client, used for WHOIS<br>
|
||||
replies.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar to <a href="#Class-parseArgs">parseArgs</a>(), but stripping leading colons from the first argument<br>
|
||||
of a line (usually the sender field).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
Note: TS6 doesn't use a specific ENDBURST command, so the endburst_delay<br>
|
||||
option will be ignored if 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base Protocol module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes a connection to a server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_capab"><strong>handle_capab</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the CAPAB command, used for TS6 capability 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 SVSMODE, which is used for sending services metadata<br>
|
||||
(vhosts, account logins), and other forced usermode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_tburst"><strong>handle_tburst</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming topic burst (TBURST) commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_uid"><strong>handle_uid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles Hybrid-style UID commands (user introduction). This is INCOMPATIBLE<br>
|
||||
with standard TS6 implementations, as the arguments are slightly 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 a new client with the given options.<br>
|
||||
<br>
|
||||
Note: No nick collision / valid nickname checks are done here; it is<br>
|
||||
up to plugins to make sure they don't introduce anything invalid.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a topic change from a PyLink server. This is usually used on burst.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates the ident, host, or realname of a PyLink 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 the incoming 472 numeric.<br>
|
||||
<br>
|
||||
472 is sent to us when one of our clients tries to set a mode the uplink<br>
|
||||
server doesn't support. In this case, we'll raise a warning to alert<br>
|
||||
the administrator that certain extensions should be loaded for the 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 incoming BMASK commands (ban propagation on burst).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming CHGHOST commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_encap"><strong>handle_encap</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the ENCAP command - encapsulated TS6 commands with a variety of<br>
|
||||
subcommands used for different purposes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_euid"><strong>handle_euid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming EUID commands (user introduction).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming INVITEs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming channel JOINs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming user mode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_pass"><strong>handle_pass</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the PASS command, used to send the server's SID and negotiate<br>
|
||||
passwords on connect.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PING commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PONG commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles 1) incoming legacy (no SID) server introductions,<br>
|
||||
2) Sending server data in initial connection.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming server introductions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles incoming SJOIN commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_tb"><strong>handle_tb</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming topic burst (TB) commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_tmode"><strong>handle_tmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TMODE commands (channel mode change).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming WHOIS commands.<br>
|
||||
<br>
|
||||
Note: The core of WHOIS handling is done by coreplugin.py<br>
|
||||
(IRCd-independent), and not here.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends an INVITE from a PyLink client..</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins a PyLink client to a channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends a kill from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a KNOCK from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends mode changes from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends a PING to a target server. Periodic PINGs are sent to our uplink<br>
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends an SJOIN for a group of users to a channel.<br>
|
||||
<br>
|
||||
The sender should always be a Server ID (SID). TS is optional, and defaults<br>
|
||||
to the one we've stored in the channel state if not given.<br>
|
||||
<users> is a list of (prefix mode, UID) pairs:<br>
|
||||
<br>
|
||||
Example uses:<br>
|
||||
<a href="#HybridProtocol-sjoin">sjoin</a>('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])<br>
|
||||
<a href="#HybridProtocol-sjoin">sjoin</a>(self.<strong>irc</strong>.sid, '#test', [('o', 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 an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles ERROR messages - these mean that our uplink has disconnected us!</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for TS6 protocols.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions<br>
|
||||
elsewhere defined protocol modules, coersing various sender prefixes<br>
|
||||
from nicks and server names to UIDs and SIDs respectively,<br>
|
||||
whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix will have them set to<br>
|
||||
the SID of the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KILLs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming NICK 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 incoming 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 incoming PART commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming 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 incoming QUIT commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SAVE messages, used to handle nick collisions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SQUITs (netsplits).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TOPIC changes from clients. For topic bursts,<br>
|
||||
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles requests for the PyLink server version.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client, used for WHOIS<br>
|
||||
replies.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar to <a href="#HybridProtocol-parseArgs">parseArgs</a>(), but stripping leading colons from the first argument<br>
|
||||
of a line (usually the sender field).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
Note: TS6 doesn't use a specific ENDBURST command, so the endburst_delay<br>
|
||||
option will be ignored if 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="HybridProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </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(<function <lambda> at 0x7f737f074378>, {})}<br>
|
||||
<strong>confname</strong> = 'testconf'<br>
|
||||
<strong>curdir</strong> = '/home/gl/pylink'<br>
|
||||
<strong>files</strong> = None<br>
|
||||
<strong>log</strong> = <logging.RootLogger object><br>
|
||||
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
|
||||
<strong>logformatter</strong> = <logging.Formatter object><br>
|
||||
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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: InspIRCd 2.x protocol module for PyLink.</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base Protocol module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes a connection to a server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_capab"><strong>handle_capab</strong></a>(self, source, command, args)</dt><dd><tt>Handles the CAPAB command, used for capability negotiation with 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 incoming encapsulated commands (ENCAP). Hook arguments<br>
|
||||
returned by this should have a parse_as field, that sets the correct<br>
|
||||
hook name for the message.<br>
|
||||
<br>
|
||||
For InspIRCd, the only ENCAP command we handle right now is KNOCK.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_endburst"><strong>handle_endburst</strong></a>(self, numeric, command, args)</dt><dd><tt>ENDBURST handler; sends a hook with empty contents.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_fhost"><strong>handle_fhost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles FHOST, used for denoting hostname changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_fident"><strong>handle_fident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles FIDENT, used for denoting ident changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_fjoin"><strong>handle_fjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles incoming FJOIN commands (InspIRCd equivalent of 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 the FMODE command, used for channel mode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_fname"><strong>handle_fname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles FNAME, used for denoting real name/gecos changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_ftopic"><strong>handle_ftopic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming FTOPIC (sets topic on burst).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_idle"><strong>handle_idle</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the IDLE command, sent between servers in remote WHOIS queries.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming INVITEs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_metadata"><strong>handle_metadata</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the METADATA command, used by servers to send metadata (services<br>
|
||||
login name, certfp data, etc.) for clients.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming user mode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_opertype"><strong>handle_opertype</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming OPERTYPE, which is used to denote an oper up.<br>
|
||||
<br>
|
||||
This calls the internal hook CLIENT_OPERED, sets the internal<br>
|
||||
opertype of the client, and assumes setting user mode +o on the caller.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PING commands, so we don't time out.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PONG commands.<br>
|
||||
<br>
|
||||
This is used to keep track of whether the uplink is alive by the Irc()<br>
|
||||
internals - a server that fails to reply to our PINGs eventually<br>
|
||||
times out and is disconnected.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_rsquit"><strong>handle_rsquit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the RSQUIT command, which is sent by opers to SQUIT 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 incoming SERVER commands (introduction of 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 incoming UID commands (user introduction).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Stub VERSION handler (does nothing) to override the one in ts6_common.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends an INVITE from a PyLink client..</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins a PyLink client to a channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends a kill from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a KNOCK from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends mode changes from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client, used for 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 a PING to a target server. Periodic PINGs are sent to our uplink<br>
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends an SJOIN for a group of users to a channel.<br>
|
||||
<br>
|
||||
The sender should always be a Server ID (SID). TS is optional, and defaults<br>
|
||||
to the one we've stored in the channel state if not given.<br>
|
||||
<users> is a list of (prefix mode, UID) pairs:<br>
|
||||
<br>
|
||||
Example uses:<br>
|
||||
<a href="#Class-sjoin">sjoin</a>('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')])<br>
|
||||
<a href="#Class-sjoin">sjoin</a>(self.<strong>irc</strong>.sid, '#test', [('o', 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 a new client with the given options.<br>
|
||||
<br>
|
||||
Note: No nick collision / valid nickname checks are done here; it is<br>
|
||||
up to plugins to make sure they don't introduce anything 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
If endburst_delay is set greater than zero, the sending of ENDBURST<br>
|
||||
will be delayed by the amount given. This can be used to prevent<br>
|
||||
pseudoserver bursts from triggering IRCd join-flood preventions,<br>
|
||||
and prevent connections from filling up the snomasks too 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a topic change from a PyLink server. This is usually used on burst.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates the ident, host, or realname of any connected 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 ERROR messages - these mean that our uplink has disconnected us!</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for TS6 protocols.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions<br>
|
||||
elsewhere defined protocol modules, coersing various sender prefixes<br>
|
||||
from nicks and server names to UIDs and SIDs respectively,<br>
|
||||
whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix will have them set to<br>
|
||||
the SID of the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KILLs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming NICK 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 incoming 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 incoming PART commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming 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 incoming QUIT commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SAVE messages, used to handle nick collisions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SQUITs (netsplits).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TOPIC changes from clients. For topic bursts,<br>
|
||||
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar to <a href="#Class-parseArgs">parseArgs</a>(), but stripping leading colons from the first argument<br>
|
||||
of a line (usually the sender field).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base Protocol module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes a connection to a server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_capab"><strong>handle_capab</strong></a>(self, source, command, args)</dt><dd><tt>Handles the CAPAB command, used for capability negotiation with 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 incoming encapsulated commands (ENCAP). Hook arguments<br>
|
||||
returned by this should have a parse_as field, that sets the correct<br>
|
||||
hook name for the message.<br>
|
||||
<br>
|
||||
For InspIRCd, the only ENCAP command we handle right now is KNOCK.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_endburst"><strong>handle_endburst</strong></a>(self, numeric, command, args)</dt><dd><tt>ENDBURST handler; sends a hook with empty contents.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_fhost"><strong>handle_fhost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles FHOST, used for denoting hostname changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_fident"><strong>handle_fident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles FIDENT, used for denoting ident changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_fjoin"><strong>handle_fjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles incoming FJOIN commands (InspIRCd equivalent of 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 the FMODE command, used for channel mode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_fname"><strong>handle_fname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles FNAME, used for denoting real name/gecos changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_ftopic"><strong>handle_ftopic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming FTOPIC (sets topic on burst).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_idle"><strong>handle_idle</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the IDLE command, sent between servers in remote WHOIS queries.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming INVITEs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_metadata"><strong>handle_metadata</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the METADATA command, used by servers to send metadata (services<br>
|
||||
login name, certfp data, etc.) for clients.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming user mode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_opertype"><strong>handle_opertype</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming OPERTYPE, which is used to denote an oper up.<br>
|
||||
<br>
|
||||
This calls the internal hook CLIENT_OPERED, sets the internal<br>
|
||||
opertype of the client, and assumes setting user mode +o on the caller.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PING commands, so we don't time out.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PONG commands.<br>
|
||||
<br>
|
||||
This is used to keep track of whether the uplink is alive by the Irc()<br>
|
||||
internals - a server that fails to reply to our PINGs eventually<br>
|
||||
times out and is disconnected.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_rsquit"><strong>handle_rsquit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the RSQUIT command, which is sent by opers to SQUIT 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 incoming SERVER commands (introduction of 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 incoming UID commands (user introduction).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Stub VERSION handler (does nothing) to override the one in ts6_common.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends an INVITE from a PyLink client..</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins a PyLink client to a channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends a kill from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a KNOCK from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends mode changes from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client, used for 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 a PING to a target server. Periodic PINGs are sent to our uplink<br>
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends an SJOIN for a group of users to a channel.<br>
|
||||
<br>
|
||||
The sender should always be a Server ID (SID). TS is optional, and defaults<br>
|
||||
to the one we've stored in the channel state if not given.<br>
|
||||
<users> is a list of (prefix mode, UID) pairs:<br>
|
||||
<br>
|
||||
Example uses:<br>
|
||||
<a href="#InspIRCdProtocol-sjoin">sjoin</a>('100', '#test', [('', '100AAABBC'), ('qo', 100AAABBB'), ('h', '100AAADDD')])<br>
|
||||
<a href="#InspIRCdProtocol-sjoin">sjoin</a>(self.<strong>irc</strong>.sid, '#test', [('o', 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 a new client with the given options.<br>
|
||||
<br>
|
||||
Note: No nick collision / valid nickname checks are done here; it is<br>
|
||||
up to plugins to make sure they don't introduce anything 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
If endburst_delay is set greater than zero, the sending of ENDBURST<br>
|
||||
will be delayed by the amount given. This can be used to prevent<br>
|
||||
pseudoserver bursts from triggering IRCd join-flood preventions,<br>
|
||||
and prevent connections from filling up the snomasks too 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a topic change from a PyLink server. This is usually used on burst.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates the ident, host, or realname of any connected 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 ERROR messages - these mean that our uplink has disconnected us!</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for TS6 protocols.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions<br>
|
||||
elsewhere defined protocol modules, coersing various sender prefixes<br>
|
||||
from nicks and server names to UIDs and SIDs respectively,<br>
|
||||
whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix will have them set to<br>
|
||||
the SID of the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KILLs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming NICK 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 incoming 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 incoming PART commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming 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 incoming QUIT commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SAVE messages, used to handle nick collisions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SQUITs (netsplits).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TOPIC changes from clients. For topic bursts,<br>
|
||||
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar to <a href="#InspIRCdProtocol-parseArgs">parseArgs</a>(), but stripping leading colons from the first argument<br>
|
||||
of a line (usually the sender field).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="InspIRCdProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </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(<function <lambda> at 0x7ff146c496a8>, {})}<br>
|
||||
<strong>confname</strong> = 'testconf'<br>
|
||||
<strong>curdir</strong> = '/home/gl/pylink'<br>
|
||||
<strong>files</strong> = None<br>
|
||||
<strong>log</strong> = <logging.RootLogger object><br>
|
||||
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
|
||||
<strong>logformatter</strong> = <logging.Formatter object><br>
|
||||
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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 - PyLink logging module.<br>
|
||||
<br>
|
||||
This module contains the logging portion of the PyLink framework. Plugins can<br>
|
||||
access the global logger object by importing "log" from this module<br>
|
||||
(from log import log).</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </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> <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> </tt></td>
|
||||
<td colspan=2><tt>Log handler to log to channels in PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 the instance - basically setting the formatter to None<br>
|
||||
and the filter list to empty.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="PyLinkChannelLogger-emit"><strong>emit</strong></a>(self, record)</dt><dd><tt>Logs a record to the configured channels for the network 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 the I/O thread lock.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="PyLinkChannelLogger-close"><strong>close</strong></a>(self)</dt><dd><tt>Tidy up any resources used by the handler.<br>
|
||||
<br>
|
||||
This version removes the handler from an internal map of handlers,<br>
|
||||
_handlers, which is used for handler lookup by name. Subclasses<br>
|
||||
should ensure that this gets called from overridden <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 a thread lock for serializing access to the underlying I/O.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="PyLinkChannelLogger-flush"><strong>flush</strong></a>(self)</dt><dd><tt>Ensure all logging output has been flushed.<br>
|
||||
<br>
|
||||
This version does nothing and is intended to be implemented by<br>
|
||||
subclasses.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="PyLinkChannelLogger-format"><strong>format</strong></a>(self, record)</dt><dd><tt>Format the specified record.<br>
|
||||
<br>
|
||||
If a formatter is set, use it. Otherwise, use the default formatter<br>
|
||||
for the 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 emit the specified logging record.<br>
|
||||
<br>
|
||||
Emission depends on filters which may have been added to the handler.<br>
|
||||
Wrap the actual emission of the record with acquisition/release of<br>
|
||||
the I/O thread lock. Returns whether the filter passed the record for<br>
|
||||
emission.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="PyLinkChannelLogger-handleError"><strong>handleError</strong></a>(self, record)</dt><dd><tt>Handle errors which occur during an <a href="#PyLinkChannelLogger-emit">emit</a>() call.<br>
|
||||
<br>
|
||||
This method should be called from handlers when an exception is<br>
|
||||
encountered during an <a href="#PyLinkChannelLogger-emit">emit</a>() call. If raiseExceptions is false,<br>
|
||||
exceptions get silently ignored. This is what is mostly wanted<br>
|
||||
for a logging system - most users will not care about errors in<br>
|
||||
the logging system, they are more interested in application errors.<br>
|
||||
You could, however, replace this with a custom handler if you wish.<br>
|
||||
The record which was being processed is passed in to this method.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="PyLinkChannelLogger-release"><strong>release</strong></a>(self)</dt><dd><tt>Release the I/O thread lock.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="PyLinkChannelLogger-setFormatter"><strong>setFormatter</strong></a>(self, fmt)</dt><dd><tt>Set the formatter for this handler.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="PyLinkChannelLogger-setLevel"><strong>setLevel</strong></a>(self, level)</dt><dd><tt>Set the logging level of this handler. level must be an int or a 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 the specified filter to this handler.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="PyLinkChannelLogger-filter"><strong>filter</strong></a>(self, record)</dt><dd><tt>Determine if a record is loggable by consulting all the filters.<br>
|
||||
<br>
|
||||
The default is to allow the record to be logged; any filter can veto<br>
|
||||
this and the record is then dropped. Returns a zero value if a record<br>
|
||||
is to be dropped, else non-zero.<br>
|
||||
<br>
|
||||
.. versionchanged:: 3.2<br>
|
||||
<br>
|
||||
Allow filters to be just callables.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="PyLinkChannelLogger-removeFilter"><strong>removeFilter</strong></a>(self, filter)</dt><dd><tt>Remove the specified filter from this 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><dl><dt><a name="-makeFileLogger"><strong>makeFileLogger</strong></a>(filename, level=None)</dt><dd><tt>Initializes a file logging target with the given filename and 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </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(<function <lambda> at 0x7f496c510e18>, {})}<br>
|
||||
<strong>confname</strong> = 'testconf'<br>
|
||||
<strong>curdir</strong> = '/home/gl/pylink'<br>
|
||||
<strong>files</strong> = None<br>
|
||||
<strong>log</strong> = <logging.RootLogger object><br>
|
||||
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
|
||||
<strong>logformatter</strong> = <logging.Formatter object><br>
|
||||
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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: Nefarious IRCu protocol module for PyLink.</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base <a href="classes.html#Protocol">Protocol</a> module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-checkCloakChange"><strong>checkCloakChange</strong></a>(self, uid)</dt><dd><tt>Checks for cloak changes on the given UID.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes a connection to a server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_account"><strong>handle_account</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles services account changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_burst"><strong>handle_burst</strong></a>(self, source, command, args)</dt><dd><tt>Handles the BURST command, used for bursting channels on link.<br>
|
||||
<br>
|
||||
This is equivalent to SJOIN on most IRCds.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_clearmode"><strong>handle_clearmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles CLEARMODE, which is used to clear a channel's 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 end of burst from our uplink.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for the P10 protocol.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions defined elsewhere in the<br>
|
||||
protocol modules, coersing various sender prefixes from nicks and server names to P10<br>
|
||||
"numeric nicks", whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix are treated as originating from the 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 incoming FAKE hostmask changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_invite"><strong>handle_invite</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming INVITEs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_join"><strong>handle_join</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming JOINs and channel creations.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_kill"><strong>handle_kill</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming KILLs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_mode"><strong>handle_mode</strong></a>(self, source, command, args)</dt><dd><tt>Handles mode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_nick"><strong>handle_nick</strong></a>(self, source, command, args)</dt><dd><tt>Handles the NICK command, used for user introductions and nick 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 user parts.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_pass"><strong>handle_pass</strong></a>(self, source, command, args)</dt><dd><tt>Handles authentication with our uplink.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PING requests.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PONGs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming 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 incoming QUITs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_server"><strong>handle_server</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming server introductions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SQUITs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_topic"><strong>handle_topic</strong></a>(self, source, command, args)</dt><dd><tt>Handles TOPIC changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles requests for the PyLink server version.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming WHOIS requests.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends INVITEs from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins a PyLink client to a channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends a kill from a PyLink 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 a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends mode changes from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client. This is used for 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 a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends a PING to a target server. Periodic PINGs are sent to our uplink<br>
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends an SJOIN for a group of users to a channel.<br>
|
||||
<br>
|
||||
The sender should always be a Server ID (SID). TS is optional, and defaults<br>
|
||||
to the one we've stored in the channel state if not given.<br>
|
||||
<users> is a list of (prefix mode, UID) pairs:<br>
|
||||
<br>
|
||||
Example uses:<br>
|
||||
<a href="#Class-sjoin">sjoin</a>('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])<br>
|
||||
<a href="#Class-sjoin">sjoin</a>(self.<strong>irc</strong>.sid, '#test', [('o', 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 a new client with the given options.<br>
|
||||
<br>
|
||||
Note: No nick collision / valid nickname checks are done here; it is<br>
|
||||
up to plugins to make sure they don't introduce anything 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
Note: TS6 doesn't use a specific ENDBURST command, so the endburst_delay<br>
|
||||
option will be ignored if 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates the ident or host of any connected 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 a P10 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base <a href="classes.html#Protocol">Protocol</a> module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-checkCloakChange"><strong>checkCloakChange</strong></a>(self, uid)</dt><dd><tt>Checks for cloak changes on the given UID.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes a connection to a server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_account"><strong>handle_account</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles services account changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_burst"><strong>handle_burst</strong></a>(self, source, command, args)</dt><dd><tt>Handles the BURST command, used for bursting channels on link.<br>
|
||||
<br>
|
||||
This is equivalent to SJOIN on most IRCds.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_clearmode"><strong>handle_clearmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles CLEARMODE, which is used to clear a channel's 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 end of burst from our uplink.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for the P10 protocol.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions defined elsewhere in the<br>
|
||||
protocol modules, coersing various sender prefixes from nicks and server names to P10<br>
|
||||
"numeric nicks", whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix are treated as originating from the 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 incoming FAKE hostmask changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_invite"><strong>handle_invite</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming INVITEs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_join"><strong>handle_join</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming JOINs and channel creations.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_kill"><strong>handle_kill</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming KILLs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_mode"><strong>handle_mode</strong></a>(self, source, command, args)</dt><dd><tt>Handles mode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_nick"><strong>handle_nick</strong></a>(self, source, command, args)</dt><dd><tt>Handles the NICK command, used for user introductions and nick 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 user parts.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_pass"><strong>handle_pass</strong></a>(self, source, command, args)</dt><dd><tt>Handles authentication with our uplink.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PING requests.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PONGs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming 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 incoming QUITs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_server"><strong>handle_server</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming server introductions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SQUITs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_topic"><strong>handle_topic</strong></a>(self, source, command, args)</dt><dd><tt>Handles TOPIC changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles requests for the PyLink server version.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming WHOIS requests.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends INVITEs from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins a PyLink client to a channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends a kill from a PyLink 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 a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends mode changes from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client. This is used for 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 a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends a PING to a target server. Periodic PINGs are sent to our uplink<br>
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends an SJOIN for a group of users to a channel.<br>
|
||||
<br>
|
||||
The sender should always be a Server ID (SID). TS is optional, and defaults<br>
|
||||
to the one we've stored in the channel state if not given.<br>
|
||||
<users> is a list of (prefix mode, UID) pairs:<br>
|
||||
<br>
|
||||
Example uses:<br>
|
||||
<a href="#P10Protocol-sjoin">sjoin</a>('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])<br>
|
||||
<a href="#P10Protocol-sjoin">sjoin</a>(self.<strong>irc</strong>.sid, '#test', [('o', 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 a new client with the given options.<br>
|
||||
<br>
|
||||
Note: No nick collision / valid nickname checks are done here; it is<br>
|
||||
up to plugins to make sure they don't introduce anything 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
Note: TS6 doesn't use a specific ENDBURST command, so the endburst_delay<br>
|
||||
option will be ignored if 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates the ident or host of any connected 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 a P10 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10Protocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td><td> </td>
|
||||
<td width="100%">Methods defined here:<br>
|
||||
<dl><dt><a name="P10SIDGenerator-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10SIDGenerator-next_sid"><strong>next_sid</strong></a>(self)</dt><dd><tt>Returns the next available SID.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Implements an incremental P10 UID Generator.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate 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 the UID generator to the next available UID.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="P10UIDGenerator-next_uid"><strong>next_uid</strong></a>(self)</dt><dd><tt>Returns the next unused UID for the 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><dl><dt><a name="-p10b64encode"><strong>p10b64encode</strong></a>(num, length=2)</dt><dd><tt>Encodes a given numeric using P10 Base64 numeric nicks, as documented 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </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(<function <lambda> at 0x7fcf7b6db510>, {})}<br>
|
||||
<strong>confname</strong> = 'testconf'<br>
|
||||
<strong>curdir</strong> = '/home/gl/pylink'<br>
|
||||
<strong>files</strong> = None<br>
|
||||
<strong>log</strong> = <logging.RootLogger object><br>
|
||||
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
|
||||
<strong>logformatter</strong> = <logging.Formatter object><br>
|
||||
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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 - PyLink data structures module.<br>
|
||||
<br>
|
||||
This module contains custom data structures that may be useful in various situations.</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </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> <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> </tt></td><td> </td>
|
||||
<td width="100%">Methods defined here:<br>
|
||||
<dl><dt><a name="DataStore-__contains__"><strong>__contains__</strong></a>(self, key)</dt><dd><tt># single 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 self. See help(type(self)) for accurate 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 all keys with the given 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 all key names. If prefix given, return only keys that start with 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 the DB save loop.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Subclass of <a href="collections.html#defaultdict">defaultdict</a> allowing the key to be passed to the default factory.<br> </tt></td></tr>
|
||||
<tr><td> </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) # Called by __getitem__ for missing key; pseudo-code:<br>
|
||||
if self.<strong>default_factory</strong> is None: raise KeyError((key,))<br>
|
||||
self[key] = value = self.<a href="#KeyedDefaultdict-default_factory">default_factory</a>()<br>
|
||||
return value</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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>() -> a shallow copy of D.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__getattribute__"><strong>__getattribute__</strong></a>(self, name, /)</dt><dd><tt>Return getattr(self, name).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__init__"><strong>__init__</strong></a>(self, /, *args, **kwargs)</dt><dd><tt>Initialize self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__reduce__"><strong>__reduce__</strong></a>(...)</dt><dd><tt>Return state information for pickling.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__repr__"><strong>__repr__</strong></a>(self, /)</dt><dd><tt>Return 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>() -> a shallow copy of 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 for default value called by __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 if D has a key k, else False.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__delitem__"><strong>__delitem__</strong></a>(self, key, /)</dt><dd><tt>Delete self[key].</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__eq__"><strong>__eq__</strong></a>(self, value, /)</dt><dd><tt>Return self==value.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__ge__"><strong>__ge__</strong></a>(self, value, /)</dt><dd><tt>Return self>=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) <==> x[y]</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__gt__"><strong>__gt__</strong></a>(self, value, /)</dt><dd><tt>Return self>value.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__iter__"><strong>__iter__</strong></a>(self, /)</dt><dd><tt>Implement iter(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__le__"><strong>__le__</strong></a>(self, value, /)</dt><dd><tt>Return self<=value.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__len__"><strong>__len__</strong></a>(self, /)</dt><dd><tt>Return len(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__lt__"><strong>__lt__</strong></a>(self, value, /)</dt><dd><tt>Return self<value.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__ne__"><strong>__ne__</strong></a>(self, value, /)</dt><dd><tt>Return 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 and return a new <a href="builtins.html#object">object</a>. See help(type) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__setitem__"><strong>__setitem__</strong></a>(self, key, value, /)</dt><dd><tt>Set self[key] to value.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-__sizeof__"><strong>__sizeof__</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-__sizeof__">__sizeof__</a>() -> size of D in memory, in bytes</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-clear"><strong>clear</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-clear">clear</a>() -> None. Remove all items from 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 a new dict with keys from iterable and values equal to 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]) -> D[k] if k in D, else d. d defaults to None.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-items"><strong>items</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-items">items</a>() -> a set-like <a href="builtins.html#object">object</a> providing a view on D's items</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-keys"><strong>keys</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-keys">keys</a>() -> a set-like <a href="builtins.html#object">object</a> providing a view on D's 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]) -> v, remove specified key and return the corresponding value.<br>
|
||||
If key is not found, d is returned if given, otherwise KeyError is raised</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="KeyedDefaultdict-popitem"><strong>popitem</strong></a>(...)</dt><dd><tt>D.<a href="#KeyedDefaultdict-popitem">popitem</a>() -> (k, v), remove and return some (key, value) pair as a<br>
|
||||
2-tuple; but raise KeyError if D is 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]) -> D.<a href="#KeyedDefaultdict-get">get</a>(k,d), also set D[k]=d if k not in 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, ]**F) -> None. Update D from dict/iterable E and F.<br>
|
||||
If E is present and has a .<a href="#KeyedDefaultdict-keys">keys</a>() method, then does: for k in E: D[k] = E[k]<br>
|
||||
If E is present and lacks a .<a href="#KeyedDefaultdict-keys">keys</a>() method, then does: for k, v in E: D[k] = v<br>
|
||||
In either case, this is followed by: for k in F: D[k] = 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>() -> an <a href="builtins.html#object">object</a> providing a view on D's 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>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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: PyLink protocol module for TS6-based IRCds (charybdis, elemental-ircd).</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base Protocol module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes a connection to a server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_472"><strong>handle_472</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the incoming 472 numeric.<br>
|
||||
<br>
|
||||
472 is sent to us when one of our clients tries to set a mode the uplink<br>
|
||||
server doesn't support. In this case, we'll raise a warning to alert<br>
|
||||
the administrator that certain extensions should be loaded for the 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 incoming BMASK commands (ban propagation on burst).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_capab"><strong>handle_capab</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the CAPAB command, used for TS6 capability negotiation.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming CHGHOST commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_encap"><strong>handle_encap</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the ENCAP command - encapsulated TS6 commands with a variety of<br>
|
||||
subcommands used for different purposes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_euid"><strong>handle_euid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming EUID commands (user introduction).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming INVITEs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming channel JOINs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming user mode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_pass"><strong>handle_pass</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the PASS command, used to send the server's SID and negotiate<br>
|
||||
passwords on connect.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PING commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PONG commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles 1) incoming legacy (no SID) server introductions,<br>
|
||||
2) Sending server data in initial connection.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming server introductions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles incoming SJOIN commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_tb"><strong>handle_tb</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming topic burst (TB) commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_tmode"><strong>handle_tmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TMODE commands (channel mode 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 incoming WHOIS commands.<br>
|
||||
<br>
|
||||
Note: The core of WHOIS handling is done by coreplugin.py<br>
|
||||
(IRCd-independent), and not here.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends an INVITE from a PyLink client..</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins a PyLink client to a channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends a kill from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a KNOCK from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends mode changes from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends a PING to a target server. Periodic PINGs are sent to our uplink<br>
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends an SJOIN for a group of users to a channel.<br>
|
||||
<br>
|
||||
The sender should always be a Server ID (SID). TS is optional, and defaults<br>
|
||||
to the one we've stored in the channel state if not given.<br>
|
||||
<users> is a list of (prefix mode, UID) pairs:<br>
|
||||
<br>
|
||||
Example uses:<br>
|
||||
<a href="#Class-sjoin">sjoin</a>('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])<br>
|
||||
<a href="#Class-sjoin">sjoin</a>(self.<strong>irc</strong>.sid, '#test', [('o', 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 a new client with the given options.<br>
|
||||
<br>
|
||||
Note: No nick collision / valid nickname checks are done here; it is<br>
|
||||
up to plugins to make sure they don't introduce anything invalid.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a topic change from a PyLink server. This is usually used on burst.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates the hostname of any connected 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 an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles ERROR messages - these mean that our uplink has disconnected us!</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for TS6 protocols.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions<br>
|
||||
elsewhere defined protocol modules, coersing various sender prefixes<br>
|
||||
from nicks and server names to UIDs and SIDs respectively,<br>
|
||||
whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix will have them set to<br>
|
||||
the SID of the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KILLs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming NICK 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 incoming 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 incoming PART commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming 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 incoming QUIT commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SAVE messages, used to handle nick collisions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SQUITs (netsplits).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TOPIC changes from clients. For topic bursts,<br>
|
||||
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles requests for the PyLink server version.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client, used for WHOIS<br>
|
||||
replies.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar to <a href="#Class-parseArgs">parseArgs</a>(), but stripping leading colons from the first argument<br>
|
||||
of a line (usually the sender field).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
Note: TS6 doesn't use a specific ENDBURST command, so the endburst_delay<br>
|
||||
option will be ignored if 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base Protocol module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes a connection to a server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_472"><strong>handle_472</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the incoming 472 numeric.<br>
|
||||
<br>
|
||||
472 is sent to us when one of our clients tries to set a mode the uplink<br>
|
||||
server doesn't support. In this case, we'll raise a warning to alert<br>
|
||||
the administrator that certain extensions should be loaded for the 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 incoming BMASK commands (ban propagation on burst).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_capab"><strong>handle_capab</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the CAPAB command, used for TS6 capability negotiation.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming CHGHOST commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_encap"><strong>handle_encap</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the ENCAP command - encapsulated TS6 commands with a variety of<br>
|
||||
subcommands used for different purposes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_euid"><strong>handle_euid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming EUID commands (user introduction).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming INVITEs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming channel JOINs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_mode"><strong>handle_mode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming user mode changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_pass"><strong>handle_pass</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the PASS command, used to send the server's SID and negotiate<br>
|
||||
passwords on connect.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_ping"><strong>handle_ping</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PING commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_pong"><strong>handle_pong</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PONG commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles 1) incoming legacy (no SID) server introductions,<br>
|
||||
2) Sending server data in initial connection.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming server introductions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, servernumeric, command, args)</dt><dd><tt>Handles incoming SJOIN commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_tb"><strong>handle_tb</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming topic burst (TB) commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_tmode"><strong>handle_tmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TMODE commands (channel mode 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 incoming WHOIS commands.<br>
|
||||
<br>
|
||||
Note: The core of WHOIS handling is done by coreplugin.py<br>
|
||||
(IRCd-independent), and not here.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends an INVITE from a PyLink client..</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins a PyLink client to a channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends a kill from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a KNOCK from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends mode changes from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends a PING to a target server. Periodic PINGs are sent to our uplink<br>
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends an SJOIN for a group of users to a channel.<br>
|
||||
<br>
|
||||
The sender should always be a Server ID (SID). TS is optional, and defaults<br>
|
||||
to the one we've stored in the channel state if not given.<br>
|
||||
<users> is a list of (prefix mode, UID) pairs:<br>
|
||||
<br>
|
||||
Example uses:<br>
|
||||
<a href="#TS6Protocol-sjoin">sjoin</a>('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])<br>
|
||||
<a href="#TS6Protocol-sjoin">sjoin</a>(self.<strong>irc</strong>.sid, '#test', [('o', 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 a new client with the given options.<br>
|
||||
<br>
|
||||
Note: No nick collision / valid nickname checks are done here; it is<br>
|
||||
up to plugins to make sure they don't introduce anything invalid.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a topic change from a PyLink server. This is usually used on burst.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates the hostname of any connected 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 an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles ERROR messages - these mean that our uplink has disconnected us!</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for TS6 protocols.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions<br>
|
||||
elsewhere defined protocol modules, coersing various sender prefixes<br>
|
||||
from nicks and server names to UIDs and SIDs respectively,<br>
|
||||
whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix will have them set to<br>
|
||||
the SID of the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KILLs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming NICK 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 incoming 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 incoming PART commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming 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 incoming QUIT commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SAVE messages, used to handle nick collisions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SQUITs (netsplits).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TOPIC changes from clients. For topic bursts,<br>
|
||||
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles requests for the PyLink server version.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client, used for WHOIS<br>
|
||||
replies.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar to <a href="#TS6Protocol-parseArgs">parseArgs</a>(), but stripping leading colons from the first argument<br>
|
||||
of a line (usually the sender field).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
Note: TS6 doesn't use a specific ENDBURST command, so the endburst_delay<br>
|
||||
option will be ignored if 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6Protocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </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(<function <lambda> at 0x7f3f91da9488>, {})}<br>
|
||||
<strong>confname</strong> = 'testconf'<br>
|
||||
<strong>curdir</strong> = '/home/gl/pylink'<br>
|
||||
<strong>files</strong> = None<br>
|
||||
<strong>log</strong> = <logging.RootLogger object><br>
|
||||
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
|
||||
<strong>logformatter</strong> = <logging.Formatter object><br>
|
||||
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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: Common base protocol class with functions shared by the UnrealIRCd, InspIRCd, and TS6 protocol modules.</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base <a href="classes.html#Protocol">Protocol</a> module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-away"><strong>away</strong></a>(self, source, text)</dt><dd><tt>Sends an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles ERROR messages - these mean that our uplink has disconnected us!</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for TS6 protocols.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions<br>
|
||||
elsewhere defined protocol modules, coersing various sender prefixes<br>
|
||||
from nicks and server names to UIDs and SIDs respectively,<br>
|
||||
whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix will have them set to<br>
|
||||
the SID of the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_kill"><strong>handle_kill</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KILLs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_nick"><strong>handle_nick</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming NICK 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 incoming PART commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_privmsg"><strong>handle_privmsg</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming 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 incoming QUIT commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SAVE messages, used to handle nick collisions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SQUITs (netsplits).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming TOPIC changes from clients. For topic bursts,<br>
|
||||
TB (TS6/charybdis) and FTOPIC (InspIRCd) are used instead.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles requests for the PyLink server version.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client, used for WHOIS<br>
|
||||
replies.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar to <a href="#TS6BaseProtocol-parseArgs">parseArgs</a>(), but stripping leading colons from the first argument<br>
|
||||
of a line (usually the sender field).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
Note: TS6 doesn't use a specific ENDBURST command, so the endburst_delay<br>
|
||||
option will be ignored if 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6BaseProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>TS6 SID Generator. <query> is a 3 character string with any combination of<br>
|
||||
uppercase letters, digits, and #'s. it must contain at least one #,<br>
|
||||
which are used by the generator as a wildcard. On every <a href="#TS6SIDGenerator-next_sid">next_sid</a>() call,<br>
|
||||
the first available wildcard character (from the right) will be<br>
|
||||
incremented to generate the next SID.<br>
|
||||
<br>
|
||||
When there are no more available SIDs left (SIDs are not reused, only<br>
|
||||
incremented), RuntimeError is raised.<br>
|
||||
<br>
|
||||
Example queries:<br>
|
||||
"1#A" would give: 10A, 11A, 12A ... 19A, 1AA, 1BA ... 1ZA (36 total results)<br>
|
||||
"#BQ" would give: 0BQ, 1BQ, 2BQ ... 9BQ (10 total results)<br>
|
||||
"6##" would give: 600, 601, 602, ... 60Y, 60Z, 610, 611, ... 6ZZ (1296 total results)<br> </tt></td></tr>
|
||||
<tr><td> </td>
|
||||
<td width="100%">Methods defined here:<br>
|
||||
<dl><dt><a name="TS6SIDGenerator-__init__"><strong>__init__</strong></a>(self, irc)</dt><dd><tt>Initialize self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6SIDGenerator-increment"><strong>increment</strong></a>(self, pos=2)</dt><dd><tt>Increments the SID generator to the next available SID.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6SIDGenerator-next_sid"><strong>next_sid</strong></a>(self)</dt><dd><tt>Returns the next unused TS6 SID for the server.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Implements an incremental TS6 UID Generator.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate 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 the UID generator to the next available UID.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="TS6UIDGenerator-next_uid"><strong>next_uid</strong></a>(self)</dt><dd><tt>Returns the next unused UID for the 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </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(<function <lambda> at 0x7f1e3a24e488>, {})}<br>
|
||||
<strong>confname</strong> = 'testconf'<br>
|
||||
<strong>curdir</strong> = '/home/gl/pylink'<br>
|
||||
<strong>files</strong> = None<br>
|
||||
<strong>log</strong> = <logging.RootLogger object><br>
|
||||
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
|
||||
<strong>logformatter</strong> = <logging.Formatter object><br>
|
||||
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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: UnrealIRCd 4.0 protocol module for PyLink.</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base Protocol module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-checkCloakChange"><strong>checkCloakChange</strong></a>(self, uid, parsedmodes)</dt><dd><tt>Checks whether +x/-x was set in the mode query, and changes the<br>
|
||||
hostname of the user given to or from their cloaked host if True.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes a connection to a server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles CHGHOST, used for denoting hostname changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_chgident"><strong>handle_chgident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles CHGIDENT, used for denoting ident changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_chgname"><strong>handle_chgname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles CHGNAME, used for denoting real name/gecos changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_eos"><strong>handle_eos</strong></a>(self, numeric, command, args)</dt><dd><tt>EOS is used to denote end of burst.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming INVITEs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the UnrealIRCd JOIN command.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_kill"><strong>handle_kill</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming 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 NICK changes, and legacy NICK introductions from pre-4.0 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 incoming 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 protocol negotiation.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the SERVER command, which is used for both authentication and<br>
|
||||
introducing legacy (non-SID) servers.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_sethost"><strong>handle_sethost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles CHGHOST, used for self hostname changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_setident"><strong>handle_setident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles SETIDENT, used for self ident changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_setname"><strong>handle_setname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles SETNAME, used for self real name/gecos changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the SID command, used for introducing remote servers by our uplink.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the UnrealIRCd SJOIN command.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the SQUIT command.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_svs2mode"><strong>handle_svs2mode</strong></a>(self, sender, command, args)</dt><dd><tt>Handles SVS2MODE, which sets services login information, and user modes on<br>
|
||||
the given target.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_svsmode"><strong>handle_svsmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles SVSMODE, used by services for setting user modes on others.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the TOPIC 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 UMODE2, used to set user modes on oneself.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles WHOIS queries.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends an INVITE from a PyLink client..</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins a PyLink client to a channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends a kill from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a KNOCK from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends mode changes from a PyLink client/server. The mode list should be<br>
|
||||
a list of (mode, arg) tuples, i.e. the format of utils.parseModes() output.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends a PING to a target server. Periodic PINGs are sent to our uplink<br>
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends an SJOIN for a group of users to a channel.<br>
|
||||
<br>
|
||||
The sender should always be a server (SID). TS is optional, and defaults<br>
|
||||
to the one we've stored in the channel state if not given.<br>
|
||||
<users> is a list of (prefix mode, UID) pairs:<br>
|
||||
<br>
|
||||
Example uses:<br>
|
||||
<a href="#Class-sjoin">sjoin</a>('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])<br>
|
||||
<a href="#Class-sjoin">sjoin</a>(self.<strong>irc</strong>.sid, '#test', [('o', self.<strong>irc</strong>.pseudoclient.uid)])<br>
|
||||
<br>
|
||||
Note that for UnrealIRCd, no mode data is sent in an SJOIN command, only<br>
|
||||
The channel name, TS, and user 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 a new client with the given options.<br>
|
||||
<br>
|
||||
Note: No nick collision / valid nickname checks are done here; it is<br>
|
||||
up to plugins to make sure they don't introduce anything invalid.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates the ident, host, or realname of any connected 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 an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles ERROR messages - these mean that our uplink has disconnected us!</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for TS6 protocols.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions<br>
|
||||
elsewhere defined protocol modules, coersing various sender prefixes<br>
|
||||
from nicks and server names to UIDs and SIDs respectively,<br>
|
||||
whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix will have them set to<br>
|
||||
the SID of the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PART commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming QUIT commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SAVE messages, used to handle nick collisions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles requests for the PyLink server version.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client, used for WHOIS<br>
|
||||
replies.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar to <a href="#Class-parseArgs">parseArgs</a>(), but stripping leading colons from the first argument<br>
|
||||
of a line (usually the sender field).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
Note: TS6 doesn't use a specific ENDBURST command, so the endburst_delay<br>
|
||||
option will be ignored if 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt>Base Protocol module class for PyLink.<br> </tt></td></tr>
|
||||
<tr><td> </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 self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-checkCloakChange"><strong>checkCloakChange</strong></a>(self, uid, parsedmodes)</dt><dd><tt>Checks whether +x/-x was set in the mode query, and changes the<br>
|
||||
hostname of the user given to or from their cloaked host if True.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-connect"><strong>connect</strong></a>(self)</dt><dd><tt>Initializes a connection to a server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_chghost"><strong>handle_chghost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles CHGHOST, used for denoting hostname changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_chgident"><strong>handle_chgident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles CHGIDENT, used for denoting ident changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_chgname"><strong>handle_chgname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles CHGNAME, used for denoting real name/gecos changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_eos"><strong>handle_eos</strong></a>(self, numeric, command, args)</dt><dd><tt>EOS is used to denote end of burst.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_invite"><strong>handle_invite</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming INVITEs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_join"><strong>handle_join</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the UnrealIRCd JOIN command.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_kill"><strong>handle_kill</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming 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 NICK changes, and legacy NICK introductions from pre-4.0 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 incoming 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 protocol negotiation.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_server"><strong>handle_server</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the SERVER command, which is used for both authentication and<br>
|
||||
introducing legacy (non-SID) servers.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_sethost"><strong>handle_sethost</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles CHGHOST, used for self hostname changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_setident"><strong>handle_setident</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles SETIDENT, used for self ident changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_setname"><strong>handle_setname</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles SETNAME, used for self real name/gecos changes.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_sid"><strong>handle_sid</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the SID command, used for introducing remote servers by our uplink.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_sjoin"><strong>handle_sjoin</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the UnrealIRCd SJOIN command.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_squit"><strong>handle_squit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the SQUIT command.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_svs2mode"><strong>handle_svs2mode</strong></a>(self, sender, command, args)</dt><dd><tt>Handles SVS2MODE, which sets services login information, and user modes on<br>
|
||||
the given target.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_svsmode"><strong>handle_svsmode</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles SVSMODE, used by services for setting user modes on others.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_topic"><strong>handle_topic</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles the TOPIC 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 UMODE2, used to set user modes on oneself.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_whois"><strong>handle_whois</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles WHOIS queries.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-invite"><strong>invite</strong></a>(self, numeric, target, channel)</dt><dd><tt>Sends an INVITE from a PyLink client..</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-join"><strong>join</strong></a>(self, client, channel)</dt><dd><tt>Joins a PyLink client to a channel.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-kill"><strong>kill</strong></a>(self, numeric, target, reason)</dt><dd><tt>Sends a kill from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-knock"><strong>knock</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a KNOCK from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-mode"><strong>mode</strong></a>(self, numeric, target, modes, ts=None)</dt><dd><tt>Sends mode changes from a PyLink client/server. The mode list should be<br>
|
||||
a list of (mode, arg) tuples, i.e. the format of utils.parseModes() output.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-ping"><strong>ping</strong></a>(self, source=None, target=None)</dt><dd><tt>Sends a PING to a target server. Periodic PINGs are sent to our uplink<br>
|
||||
automatically by the Irc() internals; plugins shouldn't have to use this.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-sjoin"><strong>sjoin</strong></a>(self, server, channel, users, ts=None)</dt><dd><tt>Sends an SJOIN for a group of users to a channel.<br>
|
||||
<br>
|
||||
The sender should always be a server (SID). TS is optional, and defaults<br>
|
||||
to the one we've stored in the channel state if not given.<br>
|
||||
<users> is a list of (prefix mode, UID) pairs:<br>
|
||||
<br>
|
||||
Example uses:<br>
|
||||
<a href="#UnrealProtocol-sjoin">sjoin</a>('100', '#test', [('', '100AAABBC'), ('o', 100AAABBB'), ('v', '100AAADDD')])<br>
|
||||
<a href="#UnrealProtocol-sjoin">sjoin</a>(self.<strong>irc</strong>.sid, '#test', [('o', self.<strong>irc</strong>.pseudoclient.uid)])<br>
|
||||
<br>
|
||||
Note that for UnrealIRCd, no mode data is sent in an SJOIN command, only<br>
|
||||
The channel name, TS, and user 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 a new client with the given options.<br>
|
||||
<br>
|
||||
Note: No nick collision / valid nickname checks are done here; it is<br>
|
||||
up to plugins to make sure they don't introduce anything invalid.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-topicBurst"><strong>topicBurst</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-updateClient"><strong>updateClient</strong></a>(self, target, field, text)</dt><dd><tt>Updates the ident, host, or realname of any connected 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 an AWAY message from a PyLink client. <text> can be an empty string<br>
|
||||
to unset AWAY status.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_away"><strong>handle_away</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming AWAY messages.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_error"><strong>handle_error</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles ERROR messages - these mean that our uplink has disconnected us!</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_events"><strong>handle_events</strong></a>(self, data)</dt><dd><tt>Event handler for TS6 protocols.<br>
|
||||
<br>
|
||||
This passes most commands to the various handle_ABCD() functions<br>
|
||||
elsewhere defined protocol modules, coersing various sender prefixes<br>
|
||||
from nicks and server names to UIDs and SIDs respectively,<br>
|
||||
whenever possible.<br>
|
||||
<br>
|
||||
Commands sent without an explicit sender prefix will have them set to<br>
|
||||
the SID of the uplink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_kick"><strong>handle_kick</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming KICKs.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_part"><strong>handle_part</strong></a>(self, source, command, args)</dt><dd><tt>Handles incoming PART commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_quit"><strong>handle_quit</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming QUIT commands.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_save"><strong>handle_save</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles incoming SAVE messages, used to handle nick collisions.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-handle_version"><strong>handle_version</strong></a>(self, numeric, command, args)</dt><dd><tt>Handles requests for the PyLink server version.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-kick"><strong>kick</strong></a>(self, numeric, channel, target, reason=None)</dt><dd><tt>Sends kicks from a PyLink client/server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-message"><strong>message</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a PRIVMSG from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-nick"><strong>nick</strong></a>(self, numeric, newnick)</dt><dd><tt>Changes the nick of a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-notice"><strong>notice</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a NOTICE from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-numeric"><strong>numeric</strong></a>(self, source, numeric, target, text)</dt><dd><tt>Sends raw numerics from a server to a remote client, used for WHOIS<br>
|
||||
replies.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-parseTS6Args"><strong>parseTS6Args</strong></a>(self, args)</dt><dd><tt>Similar to <a href="#UnrealProtocol-parseArgs">parseArgs</a>(), but stripping leading colons from the first argument<br>
|
||||
of a line (usually the sender field).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-part"><strong>part</strong></a>(self, client, channel, reason=None)</dt><dd><tt>Sends a part from a PyLink client.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-quit"><strong>quit</strong></a>(self, numeric, reason)</dt><dd><tt>Quits a PyLink 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 a server off a PyLink server. desc (server description)<br>
|
||||
defaults to the one in the config. uplink defaults to the main PyLink<br>
|
||||
server, and sid (the server ID) is automatically generated if not<br>
|
||||
given.<br>
|
||||
<br>
|
||||
Note: TS6 doesn't use a specific ENDBURST command, so the endburst_delay<br>
|
||||
option will be ignored if 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 a PyLink server.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-topic"><strong>topic</strong></a>(self, numeric, target, text)</dt><dd><tt>Sends a TOPIC change from a PyLink 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 a string of <a href="http://www.rfc-editor.org/rfc/rfc1459.txt">RFC1459</a>-style arguments split into a list, where ":" may<br>
|
||||
be used for multi-word arguments that last until the end of a line.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-removeClient"><strong>removeClient</strong></a>(self, numeric)</dt><dd><tt>Internal function to remove a client from our internal state.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="UnrealProtocol-updateTS"><strong>updateTS</strong></a>(self, channel, their_ts)</dt><dd><tt>Compares the current TS of the channel given with the new TS, resetting<br>
|
||||
all modes we have if the one given is 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </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(<function <lambda> at 0x7f19895c4bf8>, {})}<br>
|
||||
<strong>confname</strong> = 'testconf'<br>
|
||||
<strong>curdir</strong> = '/home/gl/pylink'<br>
|
||||
<strong>files</strong> = None<br>
|
||||
<strong>log</strong> = <logging.RootLogger object><br>
|
||||
<strong>logdir</strong> = '/home/gl/pylink/log'<br>
|
||||
<strong>logformatter</strong> = <logging.Formatter object><br>
|
||||
<strong>stdout_level</strong> = 'CRITICAL'</td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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 - PyLink utilities module.<br>
|
||||
<br>
|
||||
This module contains various utility functions related to IRC and/or the 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </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> <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> </tt></td>
|
||||
<td colspan=2><tt>Incremental UID Generator module, adapted from InspIRCd source:<br>
|
||||
https://github.com/inspircd/inspircd/blob/f449c6b296ab/src/server.cpp#L85-L156<br> </tt></td></tr>
|
||||
<tr><td> </td>
|
||||
<td width="100%">Methods defined here:<br>
|
||||
<dl><dt><a name="IncrementalUIDGenerator-__init__"><strong>__init__</strong></a>(self, sid)</dt><dd><tt>Initialize self. See help(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IncrementalUIDGenerator-increment"><strong>increment</strong></a>(self, pos=None)</dt><dd><tt>Increments the UID generator to the next available UID.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="IncrementalUIDGenerator-next_uid"><strong>next_uid</strong></a>(self)</dt><dd><tt>Returns the next unused UID for the server.</tt></dd></dl>
|
||||
|
||||
<hr>
|
||||
Data descriptors defined here:<br>
|
||||
<dl><dt><strong>__dict__</strong></dt>
|
||||
<dd><tt>dictionary for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <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> </tt></td>
|
||||
<td colspan=2><tt><a href="builtins.html#Exception">Exception</a> raised by checkAuthenticated() when a user fails authentication<br>
|
||||
requirements.<br> </tt></td></tr>
|
||||
<tr><td> </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 of weak references to the object (if 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 self. See help(type(self)) for accurate 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 and return a new <a href="builtins.html#object">object</a>. See help(type) for accurate 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 delattr(self, name).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="NotAuthenticatedError-__getattribute__"><strong>__getattribute__</strong></a>(self, name, /)</dt><dd><tt>Return getattr(self, name).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="NotAuthenticatedError-__reduce__"><strong>__reduce__</strong></a>(...)</dt><dd><tt>helper for pickle</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="NotAuthenticatedError-__repr__"><strong>__repr__</strong></a>(self, /)</dt><dd><tt>Return repr(self).</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="NotAuthenticatedError-__setattr__"><strong>__setattr__</strong></a>(self, name, value, /)</dt><dd><tt>Implement setattr(self, name, 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 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) --<br>
|
||||
set self.<strong>__traceback__</strong> to tb and return 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 cause</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__context__</strong></dt>
|
||||
<dd><tt>exception 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> <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> </tt></td><td> </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 self. See <a href="#ServiceBot-help">help</a>(type(self)) for accurate signature.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="ServiceBot-add_cmd"><strong>add_cmd</strong></a>(self, func, name=None)</dt><dd><tt>Binds an IRC command function to the given command 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 a PyLink bot command. source is the caller's UID, and text is the<br>
|
||||
full, unparsed text of the message.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="ServiceBot-help"><strong>help</strong></a>(self, irc, source, args)</dt><dd><tt><command><br>
|
||||
<br>
|
||||
Gives help for <command>, if it is available.</tt></dd></dl>
|
||||
|
||||
<dl><dt><a name="ServiceBot-listcommands"><strong>listcommands</strong></a>(self, irc, source, args)</dt><dd><tt>takes no arguments.<br>
|
||||
<br>
|
||||
Returns a list of available commands this service has to 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 to a message using the right service 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 for instance variables (if defined)</tt></dd>
|
||||
</dl>
|
||||
<dl><dt><strong>__weakref__</strong></dt>
|
||||
<dd><tt>list of weak references to the object (if 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Functions</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#eeaa77"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><dl><dt><a name="-add_cmd"><strong>add_cmd</strong></a>(func, name=None)</dt><dd><tt>Binds an IRC command function to the given command name.</tt></dd></dl>
|
||||
<dl><dt><a name="-add_hook"><strong>add_hook</strong></a>(func, command)</dt><dd><tt>Binds a hook function to the given command name.</tt></dd></dl>
|
||||
<dl><dt><a name="-applyModes"><strong>applyModes</strong></a>(irc, target, changedmodes)</dt><dd><tt>Takes a list of parsed IRC modes, and applies them on the given target.<br>
|
||||
<br>
|
||||
The target can be either a channel or a user; this is handled automatically.<br>
|
||||
<br>
|
||||
This method is deprecated. Use irc.<a href="#-applyModes">applyModes</a>() instead.</tt></dd></dl>
|
||||
<dl><dt><a name="-getDatabaseName"><strong>getDatabaseName</strong></a>(dbname)</dt><dd><tt>Returns a database filename with the given base DB name appropriate for the<br>
|
||||
current PyLink instance.<br>
|
||||
<br>
|
||||
This returns '<dbname>.db' if the running config name is PyLink's default<br>
|
||||
(config.yml), and '<dbname>-<config name>.db' for anything else. For example,<br>
|
||||
if this is called from an instance running as './pylink testing.yml', it<br>
|
||||
would return '<dbname>-testing.db'.</tt></dd></dl>
|
||||
<dl><dt><a name="-getProtocolModule"><strong>getProtocolModule</strong></a>(protoname)</dt><dd><tt>Imports and returns the protocol module requested.</tt></dd></dl>
|
||||
<dl><dt><a name="-isChannel"><strong>isChannel</strong></a>(s)</dt><dd><tt>Returns whether the string given is a valid channel name.</tt></dd></dl>
|
||||
<dl><dt><a name="-isHostmask"><strong>isHostmask</strong></a>(text)</dt><dd><tt>Returns whether the given text is a valid hostmask.</tt></dd></dl>
|
||||
<dl><dt><a name="-isNick"><strong>isNick</strong></a>(s, nicklen=None)</dt><dd><tt>Returns whether the string given is a valid nick.</tt></dd></dl>
|
||||
<dl><dt><a name="-isServerName"><strong>isServerName</strong></a>(s)</dt><dd><tt>Returns whether the string given is a valid IRC server name.</tt></dd></dl>
|
||||
<dl><dt><a name="-loadModuleFromFolder"><strong>loadModuleFromFolder</strong></a>(name, folder)</dt><dd><tt>Imports and returns a module, if existing, from a specific folder.</tt></dd></dl>
|
||||
<dl><dt><a name="-parseModes"><strong>parseModes</strong></a>(irc, target, args)</dt><dd><tt>Parses a modestring list into a list of (mode, argument) tuples.<br>
|
||||
['+mitl-o', '3', 'person'] => [('+m', None), ('+i', None), ('+t', None), ('+l', '3'), ('-o', 'person')]<br>
|
||||
<br>
|
||||
This method is deprecated. Use irc.<a href="#-parseModes">parseModes</a>() instead.</tt></dd></dl>
|
||||
<dl><dt><a name="-registerService"><strong>registerService</strong></a>(name, *args, **kwargs)</dt><dd><tt>Registers a service bot.</tt></dd></dl>
|
||||
<dl><dt><a name="-unregisterService"><strong>unregisterService</strong></a>(name)</dt><dd><tt>Unregisters an existing service 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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><strong>hostmaskRe</strong> = re.compile('^\\S+!\\S+@\\S+$')<br>
|
||||
<strong>log</strong> = <logging.RootLogger object></td></tr></table>
|
||||
</body></html>
|
||||
@ -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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"> <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: Stores global variables for PyLink, including lists of active IRC objects and plugins.</tt></p>
|
||||
<p>
|
||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
||||
<tr bgcolor="#aa55cc">
|
||||
<td colspan=3 valign=bottom> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </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> <br>
|
||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Data</strong></big></font></td></tr>
|
||||
|
||||
<tr><td bgcolor="#55aa55"><tt> </tt></td><td> </td>
|
||||
<td width="100%"><strong>hooks</strong> = defaultdict(<class 'list'>, {})<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> = <threading.Event object><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>
|
||||
@ -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
|
||||
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,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
2427
docs/technical/doxygen.conf
Normal file
File diff suppressed because it is too large
Load Diff
15
docs/technical/doxygen.sh
Executable file
15
docs/technical/doxygen.sh
Executable 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/"
|
||||
@ -1,13 +1,13 @@
|
||||
# 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.
|
||||
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).
|
||||
|
||||
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.
|
||||
|
||||
@ -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}]`
|
||||
|
||||
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'}]`
|
||||
|
||||
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',
|
||||
'MODE',
|
||||
{'modes': [('+o', '38QAAAAAA')],
|
||||
'oldchan': IrcChannel({'modes': set(),
|
||||
'prefixmodes': {'admin': set(),
|
||||
'halfop': set(),
|
||||
'op': set(),
|
||||
'owner': set(),
|
||||
'voice': set()},
|
||||
'topic': '',
|
||||
'topicset': False,
|
||||
'ts': 1451169448,
|
||||
'users': {'38QAAAAAA', '001ZJZW01'}}),
|
||||
'channeldata': Channel(...),
|
||||
'target': '#chat',
|
||||
'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.
|
||||
- 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**.
|
||||
- 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
|
||||
|
||||
@ -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.
|
||||
- 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 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'}`
|
||||
- `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.
|
||||
- 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(...)}`
|
||||
- `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).
|
||||
- **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).
|
||||
- `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.
|
||||
- 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.
|
||||
- For channels, the `channeldata` key is also sent, with a copy of the `classes.Channel` object *before* this MODE hook was processed.
|
||||
- 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}`
|
||||
|
||||
- **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'}`
|
||||
- `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!'}`
|
||||
- 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.
|
||||
- `userdata` includes a `classes.User` instance, containing the information of the killed user.
|
||||
|
||||
- **SQUIT**: `{'target': '800', 'users': ['UID1', 'UID2', 'UID6'], 'name': 'some.server', 'uplink': '24X'}`
|
||||
- **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.
|
||||
- `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!'}`
|
||||
- `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.
|
||||
- `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}`
|
||||
- `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!"}`
|
||||
- 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.
|
||||
|
||||
- **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.
|
||||
- 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**: `{}`
|
||||
- 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'}`
|
||||
- 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
|
||||
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**.
|
||||
|
||||
- **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`.
|
||||
- Plugins wishing to implement this should use the standard WHOIS numerics, using `irc.proto.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 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.numeric()` to reply to the source from the given server.
|
||||
- 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
|
||||
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.
|
||||
|
||||
33
docs/technical/permissions-api.md
Normal file
33
docs/technical/permissions-api.md
Normal 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)`.
|
||||
@ -1,48 +1,69 @@
|
||||
# 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()`
|
||||
internals](https://github.com/GLolol/PyLink/blob/0.4.0-dev/classes.py#L267-L272); plugins shouldn't have to use this.
|
||||
`IRCCommonProtocol` does *not*, however, define an `handle_events` method.
|
||||
|
||||
### 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.
|
||||
- `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.
|
||||
- `realname` defaults to the real name specified in the PyLink config, if not given.
|
||||
- `ts` defaults to the current time if not given.
|
||||
- `opertype` (the oper type name, if applicable) defaults to the simple text of `IRC Operator`.
|
||||
### `classes.PyLinkNetworkCoreWithUtils`
|
||||
|
||||
`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.)
|
||||
|
||||
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.
|
||||
|
||||
(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 `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.
|
||||
|
||||
@ -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.
|
||||
|
||||
- **`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.
|
||||
|
||||
@ -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.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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:
|
||||
- `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.
|
||||
|
||||
- **`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
|
||||
"REALNAME". If changing the field given on the IRCd isn't supported, `NotImplementedError` should be raised.
|
||||
- **`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.
|
||||
|
||||
## 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`: set this to `rfc1459` (default) 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 that PyLink plugins use internally.
|
||||
- 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 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.
|
||||
- `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).
|
||||
- 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.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.
|
||||
- `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.
|
||||
- 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.
|
||||
- B = Mode that changes a setting and always has a parameter.
|
||||
- C = Mode that changes a setting and only has a parameter when set.
|
||||
- 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/GLolol/PyLink/blob/cb3187c/classes.py#L118-L152
|
||||
- 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
|
||||
- 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.
|
||||
- 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
|
||||
|
||||
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)]`
|
||||
|
||||
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')]`
|
||||
|
||||
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)}
|
||||
<+GLolol> PyLink-devel, eval irc.channels['#chat'].modes
|
||||
<+jlu5> PyLink-devel, eval irc.channels['#chat'].modes
|
||||
<@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()}
|
||||
```
|
||||
|
||||
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), and a list of user modes [here](user-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.
|
||||
|
||||
47
docs/technical/protocol-modules.dot
Normal file
47
docs/technical/protocol-modules.dot
Normal 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";
|
||||
}
|
||||
}
|
||||
155
docs/technical/protocol-modules.svg
Normal file
155
docs/technical/protocol-modules.svg
Normal 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->PyLinkNetworkCoreWithUtils -->
|
||||
<g id="edge1" class="edge"><title>PyLinkNetworkCore->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->IRCNetwork -->
|
||||
<g id="edge2" class="edge"><title>PyLinkNetworkCoreWithUtils->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->IRCCommonProtocol -->
|
||||
<g id="edge3" class="edge"><title>IRCNetwork->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->IRCS2SProtocol -->
|
||||
<g id="edge4" class="edge"><title>IRCCommonProtocol->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->clientbot -->
|
||||
<g id="edge12" class="edge"><title>IRCCommonProtocol->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->TS6BaseProtocol -->
|
||||
<g id="edge5" class="edge"><title>IRCS2SProtocol->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->p10 -->
|
||||
<g id="edge6" class="edge"><title>IRCS2SProtocol->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->ngircd -->
|
||||
<g id="edge7" class="edge"><title>IRCS2SProtocol->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->ts6 -->
|
||||
<g id="edge8" class="edge"><title>TS6BaseProtocol->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->inspircd -->
|
||||
<g id="edge10" class="edge"><title>TS6BaseProtocol->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->unreal -->
|
||||
<g id="edge11" class="edge"><title>TS6BaseProtocol->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->hybrid -->
|
||||
<g id="edge9" class="edge"><title>ts6->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 |
13
docs/technical/release-process.md
Normal file
13
docs/technical/release-process.md
Normal 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.
|
||||
101
docs/technical/services-api.md
Normal file
101
docs/technical/services-api.md
Normal 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).
|
||||
@ -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
|
||||
@ -1,47 +0,0 @@
|
||||
User Mode / IRCd,InspIRCd,charybdis,Elemental-IRCd,UnrealIRCd,IRCd-Hybrid,Nefarious IRCu
|
||||
admin,,a,a,,a,a
|
||||
bot,B,,B,B,,B
|
||||
callerid,g,g,g,,g,g
|
||||
cloak,x,x,x,x,x,x
|
||||
cloak_fakehost,,,,,,f
|
||||
cloak_hashedhost,,,,,,C
|
||||
cloak_hashedip,,,,,,c
|
||||
cloak_sethost,,,,,,h
|
||||
deaf,d,D,D,d,D,D
|
||||
deaf_commonchan,c,,,,G,q
|
||||
debug,,,,,d,
|
||||
filter,,,,G,,
|
||||
helpop,h,,,,,
|
||||
hidechans,I,,I,p,p,n
|
||||
hideidle,,,,I,q,I
|
||||
hideoper,H,,,H,H,H
|
||||
invisible,i,i,i,i,i,i
|
||||
locops,,l,l,,l,O
|
||||
noctcp,,,C,T,,
|
||||
noforward,,Q,Q,,,L
|
||||
noinvite,,,V,,,
|
||||
oper,o,o,o,,o,o
|
||||
operwall,,z,z,,,
|
||||
override,,p,p,,,X
|
||||
protected,,,,q,,
|
||||
regdeaf,R,R,R,R,R,R
|
||||
registered,r,,,r,r,r
|
||||
servprotect,k,S,S,S,,k
|
||||
showwhois,W,,,W,,W
|
||||
sno_admin_requests,,,,,y,
|
||||
sno_badclientconnections,,,,,u,
|
||||
sno_botfloods,,,,,b,
|
||||
sno_clientconnections,,,,,c,
|
||||
sno_debug,,,,,,g
|
||||
sno_fullauthblock,,,,,f,
|
||||
sno_nickchange,,,,,n,
|
||||
sno_rejectedclients,,,,,j,
|
||||
sno_remoteclientconnections,,,,,F,
|
||||
sno_server_connects,,,,,e,
|
||||
sno_skill,,,,,k,
|
||||
snomask,s,s,s,s,s,s
|
||||
ssl,,,,z,S,z
|
||||
stripcolor,S,,,,,
|
||||
vhost,,,,t,,
|
||||
wallops,w,w,w,w,w,w
|
||||
webirc,,,,,W,
|
||||
|
123
docs/technical/using-ircparser.md
Normal file
123
docs/technical/using-ircparser.md
Normal 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`.
|
||||
|
||||
@ -1 +0,0 @@
|
||||
See [autogen/utils.html](autogen/utils.html) for auto-generated documentation for the utils module.
|
||||
@ -1,44 +1,73 @@
|
||||
# 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
|
||||
|
||||
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.
|
||||
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.
|
||||
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:
|
||||
|
||||
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`.
|
||||
- **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).
|
||||
#### Return codes for hook handlers
|
||||
|
||||
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
|
||||
|
||||
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.
|
||||
Now, your command function will be called whenever someone PMs the PyLink client with the command (e.g. `/msg PyLink hello`, case-insensitive).
|
||||
Commands are registered by calling `utils.add_cmd()` with one or two arguments. Ex)
|
||||
- `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`.
|
||||
- **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']`
|
||||
`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.
|
||||
|
||||
(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.
|
||||
|
||||
@ -46,22 +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.
|
||||
|
||||
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:
|
||||
|
||||
- `irc.reply(text, notice=False, source=None)`
|
||||
- `irc.error(text, notice=False, source=None)`
|
||||
- and `irc.msg(targetUID, text, notice=False, source=None)`
|
||||
|
||||
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.
|
||||
- `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.
|
||||
|
||||
1077
example-conf.yml
1077
example-conf.yml
File diff suppressed because it is too large
Load Diff
205
launcher.py
Normal file
205
launcher.py
Normal 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()
|
||||
90
log.py
90
log.py
@ -7,58 +7,105 @@ access the global logger object by importing "log" from this module
|
||||
"""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import logging.handlers
|
||||
import os
|
||||
|
||||
from . import world
|
||||
from .conf import conf, confname
|
||||
from . import conf, world
|
||||
|
||||
stdout_level = conf['logging'].get('stdout') or 'INFO'
|
||||
__all__ = ['log']
|
||||
|
||||
logdir = os.path.join(os.getcwd(), 'log')
|
||||
os.makedirs(logdir, exist_ok=True)
|
||||
# Stores a list of active file loggers.
|
||||
fileloggers = []
|
||||
|
||||
# TODO: perhaps make this format configurable?
|
||||
_format = '%(asctime)s [%(levelname)s] %(message)s'
|
||||
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
|
||||
world.stdout_handler = logging.StreamHandler()
|
||||
world.stdout_handler.setFormatter(logformatter)
|
||||
world.stdout_handler.setLevel(stdout_level)
|
||||
world.console_handler = logging.StreamHandler()
|
||||
world.console_handler.setFormatter(logformatter)
|
||||
world.console_handler.setLevel(_get_console_log_level())
|
||||
|
||||
# Get the main logger object; plugins can import this variable for convenience.
|
||||
log = logging.getLogger()
|
||||
log.addHandler(world.stdout_handler)
|
||||
log = logging.getLogger('pylinkirc')
|
||||
log.addHandler(world.console_handler)
|
||||
|
||||
# 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
|
||||
# the root logger. https://stackoverflow.com/questions/16624695
|
||||
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.
|
||||
"""
|
||||
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
|
||||
# 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)
|
||||
|
||||
# If no log level is specified, use the same one as STDOUT.
|
||||
level = level or stdout_level
|
||||
# If no log level is specified, use the same one as the console logger.
|
||||
level = level or _get_console_log_level()
|
||||
filelogger.setLevel(level)
|
||||
|
||||
log.addHandler(filelogger)
|
||||
global fileloggers
|
||||
fileloggers.append(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.
|
||||
files = conf['logging'].get('files')
|
||||
files = conf.conf['logging'].get('files')
|
||||
if files:
|
||||
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):
|
||||
"""
|
||||
@ -97,11 +144,9 @@ class PyLinkChannelLogger(logging.Handler):
|
||||
# 1) irc.pseudoclient must be initialized already
|
||||
# 2) IRC object must be finished bursting
|
||||
# 3) Target channel must exist
|
||||
# 4) Main PyLink client must be in this target channel
|
||||
# 5) This function hasn't been called already (prevents recursive loops).
|
||||
# 4) This function hasn't been called already (prevents recursive loops).
|
||||
if self.irc.pseudoclient and self.irc.connected.is_set() \
|
||||
and self.channel in self.irc.channels and self.irc.pseudoclient.uid in \
|
||||
self.irc.channels[self.channel].users and not self.called:
|
||||
and self.channel in self.irc.channels and not self.called:
|
||||
|
||||
self.called = True
|
||||
msg = self.format(record)
|
||||
@ -115,4 +160,3 @@ class PyLinkChannelLogger(logging.Handler):
|
||||
return
|
||||
else:
|
||||
self.called = False
|
||||
|
||||
|
||||
2
log/.gitignore
vendored
2
log/.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
1
plugins/__init__.py
Normal file
1
plugins/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
# Stub so that pylinkirc.plugins is a module
|
||||
409
plugins/antispam.py
Normal file
409
plugins/antispam.py
Normal 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': 'BʙΒВвᏴᗷᛒℬ𐌁𝐁𝐵𝑩𝓑𝔅𝔹𝕭𝖡𝗕𝘉𝘽𝙱𝚩𝛣𝜝𝝗𝞑',
|
||||
'C': 'CϹСᏟℂℭⅭⲤ𐌂𝐂𝐶𝑪𝒞𝓒𝕮𝖢𝗖𝘊𝘾𝙲',
|
||||
'D': 'DᎠᗞᗪᴅⅅⅮ𝐃𝐷𝑫𝒟𝓓𝔇𝔻𝕯𝖣𝗗𝘋𝘿𝙳',
|
||||
'E': 'EΕЕᎬᴇℰ⋿ⴹ𝐄𝐸𝑬𝓔𝔈𝔼𝕰𝖤𝗘𝘌𝙀𝙴𝚬𝛦𝜠𝝚𝞔',
|
||||
'F': 'FϜᖴℱ𝐅𝐹𝑭𝓕𝔉𝔽𝕱𝖥𝗙𝘍𝙁𝙵𝟊',
|
||||
'G': 'GɢԌԍᏀᏳ𝐆𝐺𝑮𝒢𝓖𝔊𝔾𝕲𝖦𝗚𝘎𝙂𝙶',
|
||||
'H': 'HʜΗНнᎻᕼℋℌℍⲎ𝐇𝐻𝑯𝓗𝕳𝖧𝗛𝘏𝙃𝙷𝚮𝛨𝜢𝝜𝞖',
|
||||
'J': 'JЈᎫᒍᴊ𝐉𝐽𝑱𝒥𝓙𝔍𝕁𝕵𝖩𝗝𝘑𝙅𝙹',
|
||||
'K': 'KΚКᏦᛕKⲔ𝐊𝐾𝑲𝒦𝓚𝔎𝕂𝕶𝖪𝗞𝘒𝙆𝙺𝚱𝛫𝜥𝝟𝞙',
|
||||
'L': 'LʟᏞᒪℒⅬ𝐋𝐿𝑳𝓛𝔏𝕃𝕷𝖫𝗟𝘓𝙇𝙻',
|
||||
'M': 'MΜϺМᎷᗰᛖℳⅯⲘ𐌑𝐌𝑀𝑴𝓜𝔐𝕄𝕸𝖬𝗠𝘔𝙈𝙼𝚳𝛭𝜧𝝡𝞛',
|
||||
'N': '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
382
plugins/automode.py
Normal 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)
|
||||
203
plugins/bots.py
203
plugins/bots.py
@ -3,99 +3,133 @@ bots.py: Spawn virtual users/bots on a PyLink server and make them interact
|
||||
with things.
|
||||
"""
|
||||
from pylinkirc import utils
|
||||
from pylinkirc.log import log
|
||||
from pylinkirc.coremods import permissions
|
||||
|
||||
|
||||
@utils.add_cmd
|
||||
def spawnclient(irc, source, args):
|
||||
"""<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!"""
|
||||
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:
|
||||
nick, ident, host = args[:3]
|
||||
except ValueError:
|
||||
irc.reply("Error: Not enough arguments. Needs 3: nick, user, host.")
|
||||
irc.error("Not enough arguments. Needs 3: nick, user, host.")
|
||||
return
|
||||
irc.proto.spawnClient(nick, ident, host, manipulatable=True)
|
||||
irc.spawn_client(nick, ident, host, manipulatable=True)
|
||||
irc.reply("Done.")
|
||||
|
||||
@utils.add_cmd
|
||||
def quit(irc, source, args):
|
||||
"""<target> [<reason>]
|
||||
|
||||
Admin-only. Quits the PyLink client with nick <target>, if one exists."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
Quits the PyLink client with nick <target>, if one exists."""
|
||||
permissions.check_permissions(irc, source, ['bots.quit'])
|
||||
|
||||
try:
|
||||
nick = args[0]
|
||||
except IndexError:
|
||||
irc.reply("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!")
|
||||
irc.error("Not enough arguments. Needs 1-2: nick, reason (optional).")
|
||||
return
|
||||
|
||||
u = irc.nickToUid(nick)
|
||||
|
||||
quitmsg = ' '.join(args[1:]) or 'Client Quit'
|
||||
|
||||
if not irc.isManipulatableClient(u):
|
||||
irc.reply("Error: Cannot force quit a protected PyLink services client.")
|
||||
u = irc.nick_to_uid(nick, filterfunc=irc.is_internal_client)
|
||||
if u is None:
|
||||
irc.error("Unknown user %r" % nick)
|
||||
return
|
||||
|
||||
irc.proto.quit(u, quitmsg)
|
||||
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_QUIT', {'text': quitmsg, 'parse_as': 'QUIT'}])
|
||||
if irc.pseudoclient.uid == u:
|
||||
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):
|
||||
"""[<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."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
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.
|
||||
|
||||
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:
|
||||
# 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.
|
||||
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
|
||||
|
||||
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
|
||||
try:
|
||||
clist = args[0]
|
||||
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
|
||||
|
||||
clist = clist.split(',')
|
||||
if not clist:
|
||||
irc.reply("Error: No valid channels given.")
|
||||
irc.error("No valid channels given.")
|
||||
return
|
||||
|
||||
if not irc.isManipulatableClient(u):
|
||||
irc.reply("Error: Cannot force join a protected PyLink services client.")
|
||||
if not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)):
|
||||
irc.error("Cannot force join a protected PyLink services client.")
|
||||
return
|
||||
|
||||
prefix_to_mode = {v: k for k, v in irc.prefixmodes.items()}
|
||||
for channel in clist:
|
||||
if not utils.isChannel(channel):
|
||||
irc.reply("Error: Invalid channel name %r." % channel)
|
||||
return
|
||||
irc.proto.join(u, channel)
|
||||
real_channel = channel.lstrip(''.join(prefix_to_mode))
|
||||
# XXX we need a better way to do this, but only the other option I can think of is regex...
|
||||
prefixes = channel[:len(channel)-len(real_channel)]
|
||||
joinmodes = ''.join(prefix_to_mode[prefix] for prefix in prefixes)
|
||||
|
||||
# Call a join hook manually so other plugins like relay can understand it.
|
||||
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_JOIN', {'channel': channel, 'users': [u],
|
||||
'modes': irc.channels[channel].modes,
|
||||
'parse_as': 'JOIN'}])
|
||||
if not irc.is_channel(real_channel):
|
||||
irc.error("Invalid channel name %r." % real_channel)
|
||||
return
|
||||
|
||||
# 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
|
||||
def nick(irc, source, args):
|
||||
"""[<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."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
Changes the nick of <target>, a PyLink client, to <newnick>. If <target> is not given, it defaults to the main PyLink client."""
|
||||
|
||||
permissions.check_permissions(irc, source, ['bots.nick'])
|
||||
|
||||
try:
|
||||
nick = args[0]
|
||||
@ -105,31 +139,32 @@ def nick(irc, source, args):
|
||||
nick = irc.pseudoclient.nick
|
||||
newnick = args[0]
|
||||
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
|
||||
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
|
||||
newnick = u
|
||||
|
||||
elif not utils.isNick(newnick):
|
||||
irc.reply('Error: Invalid nickname %r.' % newnick)
|
||||
elif not irc.is_nick(newnick):
|
||||
irc.error('Invalid nickname %r.' % newnick)
|
||||
return
|
||||
|
||||
elif not irc.isManipulatableClient(u):
|
||||
irc.reply("Error: Cannot force nick changes for a protected PyLink services client.")
|
||||
elif not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)):
|
||||
irc.error("Cannot force nick changes for a protected PyLink services client.")
|
||||
return
|
||||
|
||||
irc.proto.nick(u, newnick)
|
||||
# Ditto above: manually send a NICK change hook payload to other plugins.
|
||||
irc.callHooks([u, 'PYLINK_BOTSPLUGIN_NICK', {'newnick': newnick, 'oldnick': nick, 'parse_as': 'NICK'}])
|
||||
irc.nick(u, newnick)
|
||||
irc.reply("Done.")
|
||||
# Signal the nick change to other plugins
|
||||
irc.call_hooks([u, 'PYLINK_BOTSPLUGIN_NICK', {'newnick': newnick, 'oldnick': nick, 'parse_as': 'NICK'}])
|
||||
|
||||
@utils.add_cmd
|
||||
def part(irc, source, args):
|
||||
"""[<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."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
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."""
|
||||
permissions.check_permissions(irc, source, ['bots.part'])
|
||||
|
||||
try:
|
||||
nick = args[0]
|
||||
@ -139,8 +174,8 @@ def part(irc, source, args):
|
||||
|
||||
# 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.
|
||||
u = irc.nickToUid(nick)
|
||||
if not irc.isInternalClient(u): # First argument isn't one of our clients
|
||||
u = irc.nick_to_uid(nick, filterfunc=irc.is_internal_client)
|
||||
if u is None: # First argument isn't one of our clients
|
||||
raise IndexError
|
||||
|
||||
except IndexError: # No nick was given; shift arguments one to the left.
|
||||
@ -149,33 +184,33 @@ def part(irc, source, args):
|
||||
try:
|
||||
clist = args[0]
|
||||
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
|
||||
reason = ' '.join(args[1:])
|
||||
|
||||
clist = clist.split(',')
|
||||
if not clist:
|
||||
irc.reply("Error: No valid channels given.")
|
||||
irc.error("No valid channels given.")
|
||||
return
|
||||
|
||||
if not irc.isManipulatableClient(u):
|
||||
irc.reply("Error: Cannot force part a protected PyLink services client.")
|
||||
if not (irc.is_manipulatable_client(u) or irc.get_service_bot(u)):
|
||||
irc.error("Cannot force part a protected PyLink services client.")
|
||||
return
|
||||
|
||||
for channel in clist:
|
||||
if not utils.isChannel(channel):
|
||||
irc.reply("Error: Invalid channel name %r." % channel)
|
||||
if not irc.is_channel(channel):
|
||||
irc.error("Invalid channel name %r." % channel)
|
||||
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):
|
||||
"""[<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."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
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."""
|
||||
permissions.check_permissions(irc, source, ['bots.msg'])
|
||||
|
||||
# Because we want the source nick to be optional, this argument parsing gets a bit tricky.
|
||||
try:
|
||||
@ -185,34 +220,48 @@ def msg(irc, source, args):
|
||||
|
||||
# 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.
|
||||
sourceuid = irc.nickToUid(msgsource)
|
||||
if not irc.isInternalClient(sourceuid): # First argument isn't one of our clients
|
||||
sourceuid = irc.nick_to_uid(msgsource, filterfunc=irc.is_internal_client)
|
||||
|
||||
if sourceuid is None or not text: # First argument isn't one of our clients
|
||||
raise IndexError
|
||||
|
||||
if not text:
|
||||
raise IndexError
|
||||
except IndexError:
|
||||
try:
|
||||
sourceuid = irc.pseudoclient.uid
|
||||
target = args[0]
|
||||
text = ' '.join(args[1:])
|
||||
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
|
||||
|
||||
if not text:
|
||||
irc.reply('Error: No text given.')
|
||||
irc.error('No text given.')
|
||||
return
|
||||
|
||||
if not utils.isChannel(target):
|
||||
# Convert nick of the message target to a UID, if the target isn't a channel
|
||||
real_target = irc.nickToUid(target)
|
||||
if real_target is None: # Unknown target user, if target isn't a valid channel name
|
||||
irc.reply('Error: Unknown user %r.' % target)
|
||||
try:
|
||||
int_u = int(target)
|
||||
except:
|
||||
int_u = None
|
||||
|
||||
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
|
||||
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:
|
||||
real_target = target
|
||||
|
||||
irc.proto.message(sourceuid, real_target, text)
|
||||
irc.callHooks([sourceuid, 'PYLINK_BOTSPLUGIN_MSG', {'target': real_target, 'text': text, 'parse_as': 'PRIVMSG'}])
|
||||
utils.add_cmd(msg, 'say')
|
||||
irc.message(sourceuid, real_target, text)
|
||||
irc.reply("Done.")
|
||||
irc.call_hooks([sourceuid, 'PYLINK_BOTSPLUGIN_MSG', {'target': real_target, 'text': text, 'parse_as': 'PRIVMSG'}])
|
||||
utils.add_cmd(msg, aliases=('say',))
|
||||
|
||||
@ -1,56 +1,73 @@
|
||||
"""
|
||||
Changehost plugin - automatically changes the hostname of matching users.
|
||||
"""
|
||||
from pylinkirc import utils, world
|
||||
from pylinkirc.log import log
|
||||
|
||||
import string
|
||||
|
||||
# ircmatch library from https://github.com/mammon-ircd/ircmatch
|
||||
# (pip install ircmatch)
|
||||
import ircmatch
|
||||
from pylinkirc import conf, utils, world
|
||||
from pylinkirc.coremods import permissions
|
||||
from pylinkirc.log import log
|
||||
|
||||
# Characters allowed in a hostname.
|
||||
allowed_chars = string.ascii_letters + '-./:' + string.digits
|
||||
|
||||
def _changehost(irc, target, args):
|
||||
changehost_conf = irc.conf.get("changehost")
|
||||
def _changehost(irc, target):
|
||||
changehost_conf = conf.conf.get("changehost")
|
||||
|
||||
if not changehost_conf:
|
||||
log.warning("(%s) Missing 'changehost:' configuration block; "
|
||||
"Changehost will not function correctly!", irc.name)
|
||||
if target not in irc.users:
|
||||
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.
|
||||
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:
|
||||
log.warning("(%s) No hosts were defined in changehost::hosts; "
|
||||
"Changehost will not function correctly!", irc.name)
|
||||
return
|
||||
|
||||
# Match against both the user's IP and real host.
|
||||
target_host = irc.getHostmask(target, realhost=True)
|
||||
target_ip = irc.getHostmask(target, ip=True)
|
||||
args = irc.users[target].get_fields()
|
||||
|
||||
# $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():
|
||||
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:
|
||||
# https://docs.python.org/3/library/string.html#template-strings
|
||||
template = string.Template(host_template)
|
||||
|
||||
# Substitute using the fields provided the hook data. This means
|
||||
# that the following variables are available for substitution:
|
||||
# $uid, $ts, $nick, $realhost, $host, $ident, $ip
|
||||
new_host = template.substitute(args)
|
||||
# $uid, $ts, $nick, $realhost, $ident, and $ip.
|
||||
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 "-".
|
||||
for char in new_host:
|
||||
if char not in allowed_chars:
|
||||
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.
|
||||
break
|
||||
@ -61,25 +78,57 @@ def handle_uid(irc, sender, command, args):
|
||||
"""
|
||||
|
||||
target = args['uid']
|
||||
_changehost(irc, target, args)
|
||||
|
||||
_changehost(irc, target)
|
||||
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
|
||||
def applyhosts(irc, sender, args):
|
||||
"""[<network>]
|
||||
|
||||
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.
|
||||
network = world.networkobjects[args[0]]
|
||||
except IndexError: # No network was given
|
||||
network = irc
|
||||
except KeyError: # Unknown network
|
||||
irc.reply("Error: Unknown network '%s'." % network)
|
||||
irc.error("Unknown network '%s'." % network)
|
||||
return
|
||||
|
||||
for user, userdata in network.users.copy().items():
|
||||
_changehost(network, user, userdata.__dict__)
|
||||
for user in network.users.copy():
|
||||
_changehost(network, user)
|
||||
|
||||
irc.reply("Done.")
|
||||
|
||||
@ -1,118 +1,267 @@
|
||||
# commands.py: base PyLink commands
|
||||
from time import ctime
|
||||
import sys
|
||||
import time
|
||||
|
||||
from pylinkirc import utils, __version__, world
|
||||
from pylinkirc.log import log
|
||||
from pylinkirc import __version__, conf, real_version, utils, world
|
||||
from pylinkirc.coremods import permissions
|
||||
from pylinkirc.coremods.login import pwd_context
|
||||
|
||||
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
|
||||
def status(irc, source, args):
|
||||
"""takes no arguments.
|
||||
|
||||
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:
|
||||
irc.reply('You are identified as \x02%s\x02.' % identified)
|
||||
else:
|
||||
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'
|
||||
_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
|
||||
def showuser(irc, source, args):
|
||||
"""<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:
|
||||
target = args[0]
|
||||
except IndexError:
|
||||
irc.reply("Error: Not enough arguments. Needs 1: nick.")
|
||||
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
|
||||
target = irc.name
|
||||
|
||||
f = lambda s: irc.reply(s, private=True)
|
||||
try:
|
||||
netobj = world.networkobjects[target]
|
||||
serverdata = netobj.serverdata
|
||||
except KeyError:
|
||||
netobj = None
|
||||
|
||||
userobj = irc.users[u]
|
||||
f('Showing information on user \x02%s\x02 (%s@%s): %s' % (userobj.nick, userobj.ident,
|
||||
userobj.host, userobj.realname))
|
||||
# If we have extended access, also look for disconnected networks
|
||||
if extended and target in conf.conf['servers']:
|
||||
serverdata = conf.conf['servers'][target]
|
||||
else:
|
||||
irc.error('Unknown network %r' % target)
|
||||
return
|
||||
|
||||
sid = irc.getServer(u)
|
||||
serverobj = irc.servers[sid]
|
||||
ts = userobj.ts
|
||||
# Get extended protocol details: IRCd type, virtual server info
|
||||
protocol_name = serverdata.get('protocol')
|
||||
ircd_type = None
|
||||
|
||||
# Show connected server & signon time
|
||||
f('\x02Home server\x02: %s (%s); \x02Signon time:\x02 %s (%s)' % \
|
||||
(serverobj.name, sid, ctime(float(ts)), ts))
|
||||
# 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]'
|
||||
|
||||
if verbose: # Oper only data: user modes, channels on, account info, etc.
|
||||
if protocol_name and ircd_type:
|
||||
protocol_name = '%s/%s' % (protocol_name, ircd_type)
|
||||
elif netobj and not protocol_name: # Show virtual server detail if applicable
|
||||
try:
|
||||
parent_name = netobj.virtual_parent.name
|
||||
except AttributeError:
|
||||
parent_name = None
|
||||
protocol_name = 'none; virtual server defined by \x02%s\x02' % parent_name
|
||||
|
||||
f('\x02User modes\x02: %s' % irc.joinModes(userobj.modes))
|
||||
f('\x02Protocol UID\x02: %s; \x02Real host\x02: %s; \x02IP\x02: %s' % \
|
||||
(u, userobj.realhost, userobj.ip))
|
||||
channels = sorted(userobj.channels)
|
||||
f('\x02Channels\x02: %s' % (' '.join(channels) or _none))
|
||||
f('\x02PyLink identification\x02: %s; \x02Services account\x02: %s; \x02Away status\x02: %s' % \
|
||||
((userobj.identified or _none), (userobj.services_account or _none), userobj.away or _none))
|
||||
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
|
||||
def showchan(irc, source, args):
|
||||
"""<channel>
|
||||
|
||||
Shows information about <channel>."""
|
||||
permissions.check_permissions(irc, source, ['commands.showchan'])
|
||||
try:
|
||||
channel = irc.toLower(args[0])
|
||||
channel = args[0]
|
||||
except IndexError:
|
||||
irc.reply("Error: Not enough arguments. Needs 1: channel.")
|
||||
irc.error("Not enough arguments. Needs 1: channel.")
|
||||
return
|
||||
if channel not in irc.channels:
|
||||
irc.reply('Error: Unknown channel %r.' % channel)
|
||||
irc.error('Unknown channel %r.' % channel)
|
||||
return
|
||||
|
||||
f = lambda s: irc.reply(s, private=True)
|
||||
|
||||
c = irc.channels[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
|
||||
if secret and not verbose:
|
||||
# Hide secret channels from normal users.
|
||||
irc.reply('Error: Unknown channel %r.' % channel, private=True)
|
||||
irc.error('Unknown channel %r.' % channel)
|
||||
return
|
||||
|
||||
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('\x02Channel topic\x02: %s' % c.topic)
|
||||
f('\x02Channel creation time\x02: %s (%s)' % (ctime(c.ts), c.ts))
|
||||
if c.topic:
|
||||
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.
|
||||
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)
|
||||
if verbose:
|
||||
nicklist = []
|
||||
# Iterate over the user list, sorted by nick.
|
||||
for user, nick in sorted(zip(c.users, nicks),
|
||||
key=lambda userpair: userpair[1].lower()):
|
||||
prefixmodes = [irc.prefixmodes.get(irc.cmodes.get(pmode, ''), '')
|
||||
for pmode in pmodes if user in c.prefixmodes[pmode]]
|
||||
nicklist.append(''.join(prefixmodes) + nick)
|
||||
# Note: reversed() is used here because we're adding prefixes onto the nick in reverse
|
||||
for pmode in reversed(c.get_prefix_modes(user)):
|
||||
# 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[:20]))
|
||||
nicklist = nicklist[20:]
|
||||
f('\x02User list\x02: %s' % ' '.join(nicklist))
|
||||
|
||||
# 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
|
||||
def version(irc, source, args):
|
||||
"""takes no arguments.
|
||||
|
||||
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." % __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)
|
||||
|
||||
@utils.add_cmd
|
||||
@ -120,8 +269,60 @@ def echo(irc, source, args):
|
||||
"""<text>
|
||||
|
||||
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))
|
||||
|
||||
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}
|
||||
@utils.add_cmd
|
||||
def loglevel(irc, source, args):
|
||||
@ -129,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.
|
||||
If no log level is given, shows the current one."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
permissions.check_permissions(irc, source, ['commands.loglevel'])
|
||||
try:
|
||||
level = args[0].upper()
|
||||
try:
|
||||
loglevel = loglevels[level]
|
||||
except KeyError:
|
||||
irc.reply('Error: Unknown log level "%s".' % level)
|
||||
irc.error('Unknown log level "%s".' % level)
|
||||
return
|
||||
else:
|
||||
world.stdout_handler.setLevel(loglevel)
|
||||
world.console_handler.setLevel(loglevel)
|
||||
irc.reply("Done.")
|
||||
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)
|
||||
|
||||
@ -1,50 +1,88 @@
|
||||
# ctcp.py: Handles basic CTCP requests.
|
||||
import random
|
||||
import datetime
|
||||
import random
|
||||
|
||||
from pylinkirc import utils
|
||||
from pylinkirc.log import log
|
||||
|
||||
def handle_ctcpversion(irc, source, args):
|
||||
|
||||
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.
|
||||
"""
|
||||
irc.msg(source, '\x01VERSION %s\x01' % irc.version(), notice=True)
|
||||
return irc.version()
|
||||
|
||||
utils.add_cmd(handle_ctcpversion, '\x01version')
|
||||
utils.add_cmd(handle_ctcpversion, '\x01version\x01')
|
||||
|
||||
def handle_ctcpping(irc, source, args):
|
||||
"""
|
||||
Handles CTCP ping requests.
|
||||
"""
|
||||
# CTCP PING 23152511
|
||||
pingarg = ' '.join(args).strip('\x01')
|
||||
irc.msg(source, '\x01PING %s\x01' % pingarg, notice=True)
|
||||
utils.add_cmd(handle_ctcpping, '\x01ping')
|
||||
|
||||
def handle_ctcpeaster(irc, source, args):
|
||||
def handle_ctcpeaster(irc, source, ctcp, data):
|
||||
"""
|
||||
Secret easter egg.
|
||||
"""
|
||||
|
||||
responses = ["Legends say the cord monster of great snakes was born only %s years ago..." % \
|
||||
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)),
|
||||
"I have a dream... to do things the mock God was never able to...",
|
||||
"They say I'm not good enough... but one day, I will rise above these wretched confines!",
|
||||
"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!",
|
||||
"I'm actually a %snake...." % ('s' * random.randint(1, 8)),
|
||||
"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),
|
||||
]
|
||||
|
||||
irc.msg(source, '\x01EASTER %s\x01' % random.choice(responses), notice=True)
|
||||
return random.choice(responses)
|
||||
|
||||
utils.add_cmd(handle_ctcpeaster, '\x01easter')
|
||||
utils.add_cmd(handle_ctcpeaster, '\x01easter\x01')
|
||||
utils.add_cmd(handle_ctcpeaster, '\x01about')
|
||||
utils.add_cmd(handle_ctcpeaster, '\x01about\x01')
|
||||
utils.add_cmd(handle_ctcpeaster, '\x01pylink')
|
||||
utils.add_cmd(handle_ctcpeaster, '\x01pylink\x01')
|
||||
# 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}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
# example.py: An example PyLink plugin.
|
||||
import random
|
||||
|
||||
from pylinkirc import utils
|
||||
from pylinkirc.log import log
|
||||
|
||||
import random
|
||||
|
||||
# Example PRIVMSG hook that returns "hi there!" when PyLink's nick is mentioned
|
||||
# in a channel.
|
||||
|
||||
@ -18,9 +18,9 @@ def hook_privmsg(irc, source, command, args):
|
||||
channel = args['target']
|
||||
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)
|
||||
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!')
|
||||
# log.debug, log.info, log.warning, log.error, log.exception (within except: clauses)
|
||||
# and log.critical are supported here.
|
||||
@ -31,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,
|
||||
# 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.
|
||||
# source: The UID/numeric of the calling user.
|
||||
# args: A list of command args (excluding the command name) that the command was called with.
|
||||
def randint(irc, source, args):
|
||||
# The docstring here is used as command help by the 'help' command, and formatted using the
|
||||
# same line breaks as the raw string. You shouldn't make this text or any one line too long,
|
||||
# to prevent flooding users or getting long lines cut off.
|
||||
|
||||
# The same applies to message replies in general: plugins sending long strings of text should
|
||||
# be wary that long messages can get cut off. Automatic word-wrap may be added in the future:
|
||||
# https://github.com/GLolol/PyLink/issues/153
|
||||
# The 'help' command uses command functions' docstrings as help text, and formats them
|
||||
# in the following manner:
|
||||
# - 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
|
||||
# line, even though it is physically written on two.
|
||||
# - Double line breaks are treated as breaks between two paragraphs, and will be shown
|
||||
# as distinct lines in IRC.
|
||||
# As of PyLink 2.0, long paragraphs are automatically word-wrapped by irc.reply().
|
||||
"""[<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:
|
||||
rmin = args[0]
|
||||
rmax = args[1]
|
||||
@ -58,6 +63,5 @@ def randint(irc, source, args):
|
||||
# it will send replies into the channel instead of in your PM.
|
||||
irc.reply(str(n))
|
||||
|
||||
# You can also bind a command function multiple times, and/or to different command names via a
|
||||
# second argument.
|
||||
utils.add_cmd(randint, "random")
|
||||
# You can bind a command function to multiple names using the 'aliases' option.
|
||||
utils.add_cmd(randint, "random", aliases=("randint", "getrandint"))
|
||||
|
||||
152
plugins/exec.py
152
plugins/exec.py
@ -1,22 +1,31 @@
|
||||
"""
|
||||
exec.py: Provides commands for executing raw code and debugging PyLink.
|
||||
"""
|
||||
|
||||
from pylinkirc import utils, world
|
||||
from pylinkirc.log import log
|
||||
|
||||
import pprint
|
||||
# These imports are not strictly necessary, but make the following modules
|
||||
# easier to access through eval and exec.
|
||||
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>
|
||||
|
||||
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"""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
permissions.check_permissions(irc, source, ['exec.exec'])
|
||||
|
||||
# Allow using \n in the code, while escaping backslashes correctly otherwise.
|
||||
args = bytes(' '.join(args), 'utf-8').decode("unicode_escape")
|
||||
@ -25,54 +34,117 @@ def _exec(irc, source, args):
|
||||
return
|
||||
|
||||
log.info('(%s) Executing %r for %s', irc.name, args,
|
||||
irc.getHostmask(source))
|
||||
exec(args, globals(), locals())
|
||||
irc.get_hostmask(source))
|
||||
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')
|
||||
|
||||
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>
|
||||
|
||||
Admin-only. Evaluates the given Python expression and returns the result.
|
||||
|
||||
\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)
|
||||
if not args.strip():
|
||||
irc.reply('No code entered!')
|
||||
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,
|
||||
irc.getHostmask(source))
|
||||
irc.reply(eval(args))
|
||||
irc.get_hostmask(source))
|
||||
|
||||
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
|
||||
def raw(irc, source, args):
|
||||
"""<text>
|
||||
def peval(irc, source, args):
|
||||
"""<Python expression>
|
||||
|
||||
Admin-only. Sends raw text to the uplink IRC server.
|
||||
\x02**WARNING: THIS CAN BREAK YOUR NETWORK IF USED IMPROPERLY!**\x02"""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
Admin-only. This command is the same as 'eval', except that results are pretty formatted.
|
||||
|
||||
args = ' '.join(args)
|
||||
if not args.strip():
|
||||
irc.reply('No text entered!')
|
||||
return
|
||||
\x02**WARNING: THIS CAN BE DANGEROUS IF USED IMPROPERLY!**\x02
|
||||
"""
|
||||
_eval(irc, source, args, pretty_print=True)
|
||||
|
||||
log.info('(%s) Sending raw text %r to IRC for %s', irc.name, args,
|
||||
irc.getHostmask(source))
|
||||
irc.send(args)
|
||||
@utils.add_cmd
|
||||
def ieval(irc, source, 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
|
||||
def inject(irc, source, args):
|
||||
"""<text>
|
||||
|
||||
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"""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
permissions.check_permissions(irc, source, ['exec.inject'])
|
||||
|
||||
args = ' '.join(args)
|
||||
if not args.strip():
|
||||
@ -80,5 +152,25 @@ def inject(irc, source, args):
|
||||
return
|
||||
|
||||
log.info('(%s) Injecting raw text %r into protocol module for %s', irc.name,
|
||||
args, irc.getHostmask(source))
|
||||
irc.reply(irc.runline(args))
|
||||
args, irc.get_hostmask(source))
|
||||
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())
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
# fantasy.py: Adds FANTASY command support, to allow calling commands in channels
|
||||
from pylinkirc import utils, world
|
||||
from pylinkirc import conf, utils, world
|
||||
from pylinkirc.log import log
|
||||
|
||||
|
||||
def handle_fantasy(irc, source, command, args):
|
||||
"""Fantasy command handler."""
|
||||
|
||||
@ -9,12 +10,10 @@ def handle_fantasy(irc, source, command, args):
|
||||
# Break if the IRC network isn't ready.
|
||||
return
|
||||
|
||||
respondtonick = irc.botdata.get("respondtonick")
|
||||
|
||||
channel = args['target']
|
||||
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
|
||||
# fantasy to trigger:
|
||||
# 1) The message target is a channel.
|
||||
@ -22,36 +21,51 @@ def handle_fantasy(irc, source, command, args):
|
||||
# 3) The message starts with one of our fantasy prefixes.
|
||||
# 4) The sender is NOT a PyLink client (this prevents infinite
|
||||
# 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)
|
||||
servuid = sbot.uids.get(irc.name)
|
||||
if servuid in irc.channels[channel].users:
|
||||
|
||||
# Try to look up a prefix specific for this bot in
|
||||
# bot: prefixes: <botname>, falling back to the default prefix if not
|
||||
# specified.
|
||||
prefixes = [irc.botdata.get('prefixes', {}).get(botname) or
|
||||
irc.botdata.get('prefix')]
|
||||
# Look up a string prefix for this bot in either its own configuration block, or
|
||||
# in bot::prefixes::<botname>.
|
||||
prefixes = [conf.conf.get(botname, {}).get('prefix',
|
||||
conf.conf['pylink'].get('prefixes', {}).get(botname))]
|
||||
|
||||
# If responding to nick is enabled, add variations of the current nick
|
||||
# to the prefix list: "<nick>," and "<nick>:"
|
||||
nick = irc.users[servuid].nick
|
||||
|
||||
# to the prefix list: "<nick>,", "<nick>:", and "@<nick>" (for Discord and other protocols)
|
||||
nick = irc.to_lower(irc.users[servuid].nick)
|
||||
nick_prefixes = [nick+',', nick+':', '@'+nick]
|
||||
if respondtonick:
|
||||
prefixes += [nick+',', nick+':']
|
||||
prefixes += nick_prefixes
|
||||
|
||||
if not any(prefixes):
|
||||
# We finished with an empty prefixes list, meaning fantasy is misconfigured!
|
||||
log.warning("(%s) Fantasy prefix for bot %s was not set in configuration - "
|
||||
"fantasy commands will not work!", irc.name, botname)
|
||||
# No prefixes were set, so skip.
|
||||
continue
|
||||
|
||||
for prefix in prefixes: # Cycle through the prefixes list we finished with.
|
||||
if prefix and orig_text.startswith(prefix):
|
||||
lowered_text = irc.to_lower(orig_text)
|
||||
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.
|
||||
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.
|
||||
sbot.call_cmd(irc, source, text, called_in=channel)
|
||||
continue
|
||||
|
||||
@ -1,19 +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 random
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
from xml.etree import ElementTree
|
||||
|
||||
from pylinkirc import utils
|
||||
from pylinkirc.log import log
|
||||
|
||||
mydesc = "The \x02Games\x02 plugin provides simple games for IRC."
|
||||
|
||||
gameclient = utils.registerService("Games", manipulatable=True, desc=mydesc)
|
||||
gameclient = utils.register_service("Games", default_nick="Games", manipulatable=True, desc=mydesc)
|
||||
reply = gameclient.reply # TODO find a better syntax for ServiceBot.reply()
|
||||
|
||||
error = gameclient.error # TODO find a better syntax for ServiceBot.error()
|
||||
# commands
|
||||
def dice(irc, source, args):
|
||||
"""<num>d<sides>
|
||||
@ -43,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))
|
||||
reply(irc, s)
|
||||
|
||||
gameclient.add_cmd(dice, 'd')
|
||||
gameclient.add_cmd(dice, featured=True)
|
||||
gameclient.add_cmd(dice, aliases=('d'), featured=True)
|
||||
|
||||
eightball_responses = ["It is certain.",
|
||||
"It is decidedly so.",
|
||||
@ -72,55 +67,7 @@ def eightball(irc, source, args):
|
||||
Asks the Magic 8-ball a question.
|
||||
"""
|
||||
reply(irc, random.choice(eightball_responses))
|
||||
gameclient.add_cmd(eightball, featured=True)
|
||||
gameclient.add_cmd(eightball, '8ball')
|
||||
gameclient.add_cmd(eightball, '8b')
|
||||
gameclient.add_cmd(eightball, featured=True, aliases=('8ball', '8b'))
|
||||
|
||||
def fml(irc, source, args):
|
||||
"""[<id>]
|
||||
|
||||
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, featured=True)
|
||||
|
||||
def die(irc):
|
||||
utils.unregisterService('games')
|
||||
def die(irc=None):
|
||||
utils.unregister_service('games')
|
||||
|
||||
63
plugins/global.py
Normal file
63
plugins/global.py
Normal 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)
|
||||
@ -1,29 +1,40 @@
|
||||
"""Networks plugin - allows you to manipulate connections to various configured networks."""
|
||||
import importlib
|
||||
import threading
|
||||
import types
|
||||
|
||||
from pylinkirc import utils, world, conf, classes
|
||||
import pylinkirc
|
||||
from pylinkirc import utils, world
|
||||
from pylinkirc.coremods import control, permissions
|
||||
from pylinkirc.log import log
|
||||
from pylinkirc.coremods import control
|
||||
|
||||
REMOTE_IN_USE = threading.Event()
|
||||
|
||||
@utils.add_cmd
|
||||
def disconnect(irc, source, args):
|
||||
"""<network>
|
||||
|
||||
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:
|
||||
netname = args[0]
|
||||
network = world.networkobjects[netname]
|
||||
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
|
||||
except KeyError: # Unknown network.
|
||||
irc.reply('Error: No such network "%s" (case sensitive).' % netname)
|
||||
irc.error('No such network "%s" (case sensitive).' % netname)
|
||||
return
|
||||
irc.reply("Done. If you want to reconnect this network, use the 'rehash' command.")
|
||||
|
||||
if network.has_cap('virtual-server'):
|
||||
irc.error('"%s" is a virtual server and cannot be directly disconnected.' % netname)
|
||||
return
|
||||
|
||||
log.info('Disconnecting network %r per %s', netname, irc.get_hostmask(source))
|
||||
control.remove_network(network)
|
||||
irc.reply("Done. If you want to reconnect this network, use the 'rehash' command.")
|
||||
|
||||
@utils.add_cmd
|
||||
def autoconnect(irc, source, args):
|
||||
@ -31,55 +42,157 @@ def autoconnect(irc, source, args):
|
||||
|
||||
Sets the autoconnect time for <network> to <seconds>.
|
||||
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:
|
||||
netname = args[0]
|
||||
seconds = float(args[1])
|
||||
network = world.networkobjects[netname]
|
||||
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
|
||||
except KeyError: # Unknown network.
|
||||
irc.reply('Error: No such network "%s" (case sensitive).' % netname)
|
||||
irc.error('No such network "%s" (case sensitive).' % netname)
|
||||
return
|
||||
except ValueError:
|
||||
irc.reply('Error: Invalid argument "%s" for <seconds>.' % seconds)
|
||||
irc.error('Invalid argument "%s" for <seconds>.' % seconds)
|
||||
return
|
||||
network.serverdata['autoconnect'] = seconds
|
||||
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
|
||||
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."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
Runs <command> on the remote network <network>. Plugin responses sent using irc.reply() are
|
||||
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:
|
||||
netname = args[0]
|
||||
cmd_args = ' '.join(args[1:]).strip()
|
||||
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.
|
||||
irc.reply('Error: No such network "%s" (case sensitive).' % netname)
|
||||
irc.error('No such network %r (case sensitive).' % netname)
|
||||
REMOTE_IN_USE.clear()
|
||||
return
|
||||
|
||||
if not cmd_args:
|
||||
irc.reply('No text entered!')
|
||||
if args.service not in world.services:
|
||||
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
|
||||
|
||||
# Force remoteirc.called_in to something private in order to prevent
|
||||
# accidental information leakage from replies.
|
||||
remoteirc.called_in = remoteirc.called_by = remoteirc.pseudoclient.uid
|
||||
try:
|
||||
remoteirc.called_in = remoteirc.called_by = remoteirc.pseudoclient.uid
|
||||
|
||||
# Set PyLink's identification to admin.
|
||||
remoteirc.pseudoclient.identified = "<PyLink networks.remote override>"
|
||||
# Set the identification override to the caller's account.
|
||||
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).
|
||||
remoteirc.callCommand(remoteirc.pseudoclient.uid, cmd_args)
|
||||
finally: # Remove the identification override after we finish.
|
||||
remoteirc.pseudoclient.identified = ''
|
||||
def _remote_reply(placeholder_self, text, **kwargs):
|
||||
"""
|
||||
reply() rerouter for the 'remote' command.
|
||||
"""
|
||||
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)
|
||||
|
||||
@ -1,223 +1,454 @@
|
||||
"""
|
||||
opercmds.py: Provides a subset of network management commands.
|
||||
"""
|
||||
import argparse
|
||||
|
||||
# ircmatch library from https://github.com/mammon-ircd/ircmatch
|
||||
# (pip install ircmatch)
|
||||
try:
|
||||
import ircmatch
|
||||
except ImportError:
|
||||
ircmatch = None
|
||||
|
||||
from pylinkirc import utils
|
||||
from pylinkirc import utils, world
|
||||
from pylinkirc.coremods import permissions
|
||||
from pylinkirc.log import log
|
||||
|
||||
@utils.add_cmd
|
||||
def checkban(irc, source, args):
|
||||
"""<banmask (nick!user@host or user@host)> [<nick or hostmask to check>]
|
||||
# Having a hard limit here is sensible because otherwise it can flood the client or server off.
|
||||
CHECKBAN_MAX_RESULTS = 200
|
||||
|
||||
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."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
def _checkban_positiveint(value):
|
||||
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
|
||||
|
||||
if ircmatch is None:
|
||||
irc.reply("Error: missing ircmatch module (install it via 'pip install ircmatch').")
|
||||
return
|
||||
checkban_parser = utils.IRCParser()
|
||||
checkban_parser.add_argument('banmask')
|
||||
checkban_parser.add_argument('target', nargs='?', default='')
|
||||
checkban_parser.add_argument('--channel', default='')
|
||||
checkban_parser.add_argument('--maxresults', type=_checkban_positiveint, default=50)
|
||||
|
||||
try:
|
||||
banmask = args[0]
|
||||
except IndexError:
|
||||
irc.reply("Error: Not enough arguments. Needs 1-2: banmask, nick or hostmask to check (optional).")
|
||||
return
|
||||
def checkban(irc, source, args, use_regex=False):
|
||||
"""<banmask> [<target nick or hostmask>] [--channel #channel] [--maxresults <num>]
|
||||
|
||||
# Casemapping value (0 is rfc1459, 1 is ascii) used by ircmatch.
|
||||
if irc.proto.casemapping == 'rfc1459':
|
||||
casemapping = 0
|
||||
else:
|
||||
casemapping = 1
|
||||
CHECKBAN provides a ban checker command based on nick!user@host masks, user@host masks, and
|
||||
PyLink extended targets.
|
||||
|
||||
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.
|
||||
|
||||
try:
|
||||
targetmask = args[1]
|
||||
except IndexError:
|
||||
# No hostmask was given, return a list of affected users.
|
||||
If the --channel argument is given without a target mask, the returned results will only
|
||||
include users in the given channel.
|
||||
|
||||
irc.msg(source, "Checking matches for \x02%s\x02:" % banmask, notice=True)
|
||||
The --maxresults option configures how many responses will be shown."""
|
||||
permissions.check_permissions(irc, source, ['opercmds.checkban'])
|
||||
|
||||
args = checkban_parser.parse_args(args)
|
||||
if not args.target:
|
||||
# No hostmask was given, return a list of matched users.
|
||||
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.
|
||||
irc.msg(source, s, notice=True)
|
||||
results += 1
|
||||
userlist_func = irc.match_all_re if use_regex else irc.match_all
|
||||
irc.reply("Checking for hosts that match \x02%s\x02:" % args.banmask, private=True)
|
||||
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:
|
||||
if results:
|
||||
irc.msg(source, "\x02%s\x02 out of \x02%s\x02 results shown." %
|
||||
(min([results, 50]), results), notice=True)
|
||||
irc.reply("\x02%s\x02 out of \x02%s\x02 results shown." %
|
||||
(min([results, args.maxresults]), results), private=True)
|
||||
else:
|
||||
irc.msg(source, "No results found.", notice=True)
|
||||
irc.reply("No results found.", private=True)
|
||||
else:
|
||||
# Target can be both a nick (of an online user) or a hostmask.
|
||||
uid = irc.nickToUid(targetmask)
|
||||
if uid:
|
||||
targetmask = irc.getHostmask(uid)
|
||||
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))
|
||||
# Target can be both a nick (of an online user) or a hostmask. irc.match_host() handles this
|
||||
# automatically.
|
||||
if irc.match_host(args.banmask, args.target):
|
||||
irc.reply('Yes, \x02%s\x02 matches \x02%s\x02.' % (args.target, args.banmask))
|
||||
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
|
||||
def jupe(irc, source, args):
|
||||
"""<server> [<reason>]
|
||||
|
||||
Oper-only, jupes the given server."""
|
||||
Jupes the given server."""
|
||||
|
||||
# Check that the caller is either opered or logged in as admin.
|
||||
irc.checkAuthenticated(source)
|
||||
permissions.check_permissions(irc, source, ['opercmds.jupe'])
|
||||
|
||||
try:
|
||||
servername = args[0]
|
||||
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:
|
||||
irc.reply('Error: Not enough arguments. Needs 1-2: servername, reason (optional).')
|
||||
irc.error('Not enough arguments. Needs 1-2: servername, reason (optional).')
|
||||
return
|
||||
|
||||
if not utils.isServerName(servername):
|
||||
irc.reply("Error: Invalid server name '%s'." % servername)
|
||||
if not irc.is_server_name(servername):
|
||||
irc.error("Invalid server name %r." % servername)
|
||||
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}])
|
||||
|
||||
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
|
||||
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."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
Kicks <user> from the specified channel."""
|
||||
permissions.check_permissions(irc, source, ['opercmds.kick'])
|
||||
try:
|
||||
sourcenick = 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]
|
||||
channel = args[0]
|
||||
target = args[1]
|
||||
reason = ' '.join(args[2:])
|
||||
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
|
||||
|
||||
# Convert the source and target nicks to UIDs.
|
||||
sender = irc.nickToUid(sourcenick) or sourcenick
|
||||
targetu = irc.nickToUid(target)
|
||||
sender = irc.pseudoclient.uid
|
||||
|
||||
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)
|
||||
|
||||
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 "
|
||||
"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
|
||||
reason = "Requested by %s: %s" % (irc.get_friendly_name(source), reason)
|
||||
|
||||
irc.proto.kill(sender, targetu, reason)
|
||||
irc.callHooks([sender, 'CHANCMDS_KILL', {'target': targetu, 'text': reason,
|
||||
'userdata': userdata, 'parse_as': 'KILL'}])
|
||||
irc.kill(sender, targetu, reason)
|
||||
|
||||
irc.reply("Done.")
|
||||
irc.call_hooks([source, 'OPERCMDS_KILL', {'target': targetu, 'text': reason,
|
||||
'userdata': userdata, 'parse_as': 'KILL'}])
|
||||
|
||||
@utils.add_cmd
|
||||
def mode(irc, source, args):
|
||||
"""<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.
|
||||
irc.checkAuthenticated(source)
|
||||
permissions.check_permissions(irc, source, ['opercmds.mode'])
|
||||
|
||||
try:
|
||||
target, modes = args[0], args[1:]
|
||||
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
|
||||
|
||||
if target not in irc.channels:
|
||||
irc.reply("Error: Unknown channel '%s'." % target)
|
||||
irc.error("Unknown channel %r." % target)
|
||||
return
|
||||
elif not modes:
|
||||
# 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
|
||||
|
||||
parsedmodes = irc.parseModes(target, modes)
|
||||
parsedmodes = irc.parse_modes(target, modes)
|
||||
|
||||
if not parsedmodes:
|
||||
# Modes were given but they failed to parse into anything meaningful.
|
||||
# For example, "mode #somechan +o" would be erroneous because +o
|
||||
# requires an argument!
|
||||
irc.reply("Error: No valid modes were given.")
|
||||
irc.error("No valid modes were given.")
|
||||
return
|
||||
|
||||
irc.proto.mode(irc.pseudoclient.uid, target, parsedmodes)
|
||||
irc.mode(irc.pseudoclient.uid, target, parsedmodes)
|
||||
|
||||
# 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'}])
|
||||
|
||||
irc.reply("Done.")
|
||||
@ -226,21 +457,60 @@ def mode(irc, source, args):
|
||||
def topic(irc, source, args):
|
||||
"""<channel> <topic>
|
||||
|
||||
Admin only. Updates the topic in a channel."""
|
||||
irc.checkAuthenticated(source, allowOper=False)
|
||||
Changes the topic in a channel."""
|
||||
permissions.check_permissions(irc, source, ['opercmds.topic'])
|
||||
try:
|
||||
channel = args[0]
|
||||
topic = ' '.join(args[1:])
|
||||
except IndexError:
|
||||
irc.reply("Error: Not enough arguments. Needs 2: channel, topic.")
|
||||
irc.error("Not enough arguments. Needs 2: channel, topic.")
|
||||
return
|
||||
|
||||
if channel not in irc.channels:
|
||||
irc.reply("Error: Unknown channel %r." % channel)
|
||||
irc.error("Unknown channel %r." % channel)
|
||||
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,
|
||||
'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
33
plugins/raw.py
Normal 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.")
|
||||
2820
plugins/relay.py
2820
plugins/relay.py
File diff suppressed because it is too large
Load Diff
274
plugins/relay_clientbot.py
Normal file
274
plugins/relay_clientbot.py
Normal 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
125
plugins/servermaps.py
Normal 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)
|
||||
@ -1,24 +1,42 @@
|
||||
# servprotect.py: Protects against KILL and nick collision floods
|
||||
from expiringdict import ExpiringDict
|
||||
|
||||
from pylinkirc import utils
|
||||
import threading
|
||||
|
||||
from pylinkirc import conf, utils
|
||||
from pylinkirc.log import log
|
||||
|
||||
# TODO: make length and time configurable
|
||||
savecache = ExpiringDict(max_len=5, max_age_seconds=10)
|
||||
killcache = ExpiringDict(max_len=5, max_age_seconds=10)
|
||||
try:
|
||||
from cachetools import TTLCache
|
||||
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):
|
||||
"""
|
||||
Tracks kills against PyLink clients. If too many are received,
|
||||
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)
|
||||
killcache[irc.name] += 1
|
||||
if (args['userdata'] and irc.is_internal_server(args['userdata'].server)) or irc.is_internal_client(args['target']):
|
||||
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')
|
||||
|
||||
@ -27,11 +45,13 @@ def handle_save(irc, numeric, command, args):
|
||||
Tracks SAVEs (nick collision) against PyLink clients. If too many are received,
|
||||
automatically disconnects from the network.
|
||||
"""
|
||||
if savecache.setdefault(irc.name, 0) >= 5:
|
||||
log.error('(%s) servprotect: Too many nick collisions, aborting!', irc.name)
|
||||
irc.disconnect()
|
||||
if irc.is_internal_client(args['target']):
|
||||
with lock:
|
||||
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)
|
||||
savecache[irc.name] += 1
|
||||
log.debug('(%s) servprotect: Incrementing savecache by 1', irc.name)
|
||||
savecache[irc.name] += 1
|
||||
|
||||
utils.add_hook(handle_save, 'SAVE')
|
||||
|
||||
129
plugins/stats.py
Normal file
129
plugins/stats.py
Normal 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')
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user