Compare commits

...

162 Commits
1.3 ... master

Author SHA1 Message Date
Krytarik Raido
4d1e5e6b53 Improve handling of replies. 2024-01-28 21:34:04 +01:00
Krytarik Raido
acde82892a Add channel-specific 'enabled' setting.
This allows easier and more thorough disabling
of the plugin features per channel.
2023-10-22 21:03:10 +02:00
Krytarik Raido
d2d25f94d2 Improve upon previous commit. 2023-04-14 16:56:04 +02:00
Krytarik Raido
10a1b41c47 Further improve exempting commands from prompt input. 2023-04-14 04:56:04 +02:00
Krytarik Raido
e5ce04c99b Only set default expiry if none is specified. 2023-03-07 04:40:04 +01:00
Nicolas Coevoet
41a9bd5eaa feat: #36 adds optional ignoreOnAbuse configuration property at channel level 2023-02-03 10:27:09 +01:00
Krytarik Raido
f0dce70427 Fix oversight from previous commit.
This affected automatical kicking on mode change.
2022-12-08 03:13:04 +01:00
Nicolas Coevoet
e50ca6fe0d fix: missleading scope and name on property vs method and also a big call overflow 2022-11-09 17:26:35 +00:00
Krytarik Raido
992406118b Improve handling on full ban list. 2022-10-30 17:56:04 +01:00
Krytarik Raido
bbd7ace6ca Try harder at attaching hostmasks to nicks. 2022-10-30 00:45:04 +02:00
Krytarik Raido
546cbab98d Drop special handling of log channel also being target.
This makes it possible to decide individually whether to use
statusmsg for log messages, regardless of what channels.
2022-10-28 23:56:04 +02:00
Krytarik Raido
724f4126ef Update and improve command list in README.md 2022-10-13 22:56:04 +02:00
Krytarik Raido
0ff2476e87 Make 'isvip' output differentiate between types.
Also drop capability checks off functions where
they are already done at command level.
2022-10-13 19:34:04 +02:00
Krytarik Raido
7fe15a15d8 Update and improve README.md 2022-10-13 05:50:04 +02:00
Nicolas Coevoet
fc00cf9a3b
Merge pull request #35 from progval/pipify
Add setup.py, to be installable via pip
2022-10-12 20:03:57 +02:00
Valentin Lorentz
fe6ce22908 Add setup.py, to be installable via pip
This is a simpler way to install Limnoria plugin:
https://docs.limnoria.net/develop/plugin_distribution.html#via-pip-pypi

This will allow installing ChanTracker with this command:

    pip3 install git+https://github.com/ncoevoet/ChanTracker.git
2022-10-12 19:41:41 +02:00
Krytarik Raido
2646838ad7 Add option to exempt voiced users from channel protections. 2022-10-12 18:34:04 +02:00
Krytarik Raido
4c53d28f11 Various improvements.
* Handle 'edit' and 'editandmark' strict wrt expected arguments.
* Allow specifying duration using multiple units as single string.
* Improve handling of items prompted for commenting.
* Improve exempting commands from prompt input.
* Improve consistency in coloring output.
* Improve smartlog handling.
2022-04-02 04:27:04 +02:00
Krytarik Raido
0d257df7d6 Simplify code on getting active channel modes. 2022-08-03 22:34:04 +02:00
Nicolas Coevoet
a2aa1fa9ea
Merge pull request #33 from SamStrongTalks/master
Add display of matching pattern
2022-06-23 08:50:57 +02:00
gildarts
278ea99e51 Add display of matching pattern
This adds display of the matching pattern to the log channel
rather than just displaying the pattern match.
2022-06-16 13:58:10 -04:00
Krytarik Raido
3931b790d5 Various minor fixes and improvements. 2022-04-30 01:34:04 +02:00
Nicolas Coevoet
67b334a431
Create FUNDING.yml 2022-04-06 10:19:38 +02:00
Krytarik Raido
bf3f2ef52d Various minor fixes and improvements. 2022-04-03 21:56:04 +02:00
Krytarik Raido
307908fc99 Fix logic in handling of 'pool' setting. 2022-04-02 21:56:04 +02:00
Krytarik Raido
e552313951 Fix logic in doMode wrt conditional kicking of users. 2022-04-02 04:27:04 +02:00
Krytarik Raido
2c94abb49a Fix mistake in user notification. 2022-03-12 05:23:04 +01:00
Krytarik Raido
f02c24868a Improve output of gathered user details. 2022-03-12 03:50:04 +01:00
Krytarik Raido
a62ac36f62 Fix textual regression from previous commit. 2022-03-12 03:34:04 +01:00
Krytarik Raido
065d698b31 Fix regression from previous commit. 2022-03-06 03:45:04 +01:00
Krytarik Raido
4f320388dd Account for over-expiration, set server address as needed. 2022-03-02 05:34:04 +01:00
Krytarik Raido
789e01a28a Add 'weblink' function to provide link to web UI. 2022-03-02 02:17:04 +01:00
Krytarik Raido
2990b35f13 Migrate off old Freenode'isms. 2022-03-01 03:56:04 +01:00
Krytarik Raido
11abee0247 Various improvements and updates.
* Realign logic in editandmark() with state prior to smartlog
  implementation as well as anticipated user expectation.
* Remove 'v', 'o', and 'h' from 'announceModes' default value.
* Extend use of smartlog beyond just editandmark()
* Add setting to toggle use of smartlog.
* Sort whole 'info' output chronologically.
* Various coding style improvements.
* Minor textual improvements.
2022-02-11 11:23:04 +01:00
Nicolas Coevoet
d8acb0b785 very small changes for removal of +q $~a 2021-11-03 08:14:06 +00:00
Krytarik Raido
051a76fa2e Fix smartlog function. 2021-10-10 01:04:04 +02:00
Nicolas Coevoet
5231ed21a8 hotfix removals 2021-10-01 07:14:55 +00:00
Nicolas Coevoet
f578c2821f hotfix for verifyremoval 2021-09-30 14:29:51 +00:00
Nicolas Coevoet
dbf84cbf1b allow editandmark to only display one line in log channel with supybot.plugins.ChanTracker.announceInTimeEditAndMark True 2021-09-30 09:03:50 +02:00
Nicolas Coevoet
e43d2c999e fix #31 2021-09-24 18:38:03 +02:00
Nicolas Coevoet
9c53e66bf8 i added an optional configuration autoRemoveUnregisteredQuiets to lift from database +q $~a entries, because they appears in search and are useless 2021-09-24 18:10:15 +02:00
Krytarik Raido
3ef7520394 Fix UnboundLocalError in doTopic. 2021-09-21 22:45:04 +02:00
Krytarik Raido
aca5b627d9 Exempt commands from commenting in private. 2021-09-21 16:23:04 +02:00
Nicolas Coevoet
cbe0f54a16 fixing info call ... 2021-09-16 07:05:10 +00:00
Nicolas Coevoet
1053a2975b Merge branch 'master' of github.com:ncoevoet/ChanTracker 2021-09-15 14:38:00 +00:00
Krytarik Raido
b097d35669 Fix copypaste error. 2021-09-07 22:34:04 +02:00
Nicolas Coevoet
cb6dc92a85 small change related to pool 2021-09-07 08:00:25 +00:00
Krytarik Raido
40098356be Also exempt forced logout from announcing. 2021-08-04 01:23:04 +02:00
Krytarik Raido
6d44e3185a Further improve web page code. 2021-07-27 23:04:04 +02:00
Krytarik Raido
1844e63234 Improve web page code. 2021-07-26 23:04:04 +02:00
Krytarik Raido
8d0d64e843 Fix regression from repeat protection rework. 2021-07-25 03:34:04 +02:00
Krytarik Raido
dc0615a29a Make channel-specific settings also network-sensitive. 2021-07-22 02:50:04 +02:00
Nicolas Coevoet
817353d6dd log chantracker's actions and a little fix when prompting 2021-07-09 14:48:35 +00:00
Nicolas Coevoet
2fcae101ce fix checkNag on plugin reload 2021-07-08 07:23:22 +00:00
Nicolas Coevoet
81e8093cd3 fix cclone being broken due to undefined args 2021-07-07 09:46:22 +00:00
Nicolas Coevoet
7af31bc213
Update README.md
updated explanations for c* methods
2021-07-05 22:13:42 +02:00
Krytarik Raido
a6a22bfe2b Improve upon previous commits. 2021-07-04 23:23:04 +02:00
Nicolas Coevoet
3108d60f3b added discloseOperator to be more specific about revealing it whithout breaking others features 2021-07-04 19:37:38 +00:00
Nicolas Coevoet
a4abc5c506 better helpers for channel protections methods 2021-07-04 10:18:52 +00:00
Nicolas Coevoet
e0c606816d added cautoexpire and few rewording of configuration helper commands 2021-07-04 08:27:14 +00:00
Nicolas Coevoet
b53dbb8dd2
Update README.md
A little better explanation about repeatition abuses.
2021-07-02 14:29:23 +02:00
Nicolas Coevoet
eef26588c9 a little improvement of <summary> output 2021-07-02 05:01:44 +00:00
Krytarik Raido
d7b888b8eb Rework notification system. 2021-07-01 22:34:04 +02:00
Nicolas Coevoet
8250443443
Update README.md 2021-07-01 19:14:31 +02:00
Nicolas Coevoet
e25c845b50 Fix incomplete WHOX on libera.chat due to rate limit 2021-07-01 16:55:59 +00:00
Krytarik Raido
8c1912b1a1 Improve upon previous commits. 2021-07-01 15:45:04 +02:00
Nicolas Coevoet
baa2bfc48f avoid cast error 2021-07-01 15:13:55 +02:00
Nicolas Coevoet
408b449f62 fix a specific case with cs quiet where quietMessage isn't triggered 2021-07-01 14:52:47 +02:00
Nicolas Coevoet
7bced17838 ensure we do not add bot's nick again on reason 2021-07-01 13:02:41 +02:00
Krytarik Raido
0659919ad2 Untangle previous commit and more. 2021-07-01 12:45:04 +02:00
Nicolas Coevoet
96ea85c3b9 make the bot a bit more reactive to actions requested by operators 2021-07-01 12:37:30 +02:00
Nicolas Coevoet
0b24db1311 Added operator nick in /kick /remove, supports for accountability if quietMessage and banMessage are filled 2021-07-01 12:07:28 +02:00
Krytarik Raido
3347a755bd Further coding style improvements. 2021-07-01 11:04:04 +02:00
Nicolas Coevoet
b93c11e7f5 fixing indent and allow 0 values in c* methods 2021-07-01 09:38:56 +02:00
Krytarik Raido
fc6d1fa64b Improve formatting and code of plugin.py 2021-07-01 08:04:04 +02:00
Nicolas Coevoet
63ea755d8f prevent a NPE on mark deletion, and avoid load of notices in +q $~a 2021-06-30 22:30:52 +00:00
Nicolas Coevoet
b98455e798
Update config.py
made modeD a channel registryValue for more versatility
2021-06-30 17:10:12 +02:00
Nicolas Coevoet
2f7b3c548a
Update config.py
little fix for useAccountBanIfPossible
2021-06-30 16:54:15 +02:00
Krytarik Raido
ef3a7973ba Improve formatting and text of config.py 2021-06-30 15:34:04 +02:00
Nicolas Coevoet
be7cb231dd Added in modeD 2021-06-30 09:10:01 +02:00
Nicolas Coevoet
70e98f2c74 fix modeD 2021-06-29 21:01:56 +02:00
Nicolas Coevoet
922e6b83ab added new registry entry modeD, which can be customized when using mode D,
added shareable temporary patterns, and two methods to deal with them, addtmp, rmtmp
2021-06-29 20:46:38 +02:00
Krytarik Raido
af16cbf9de Also HTML-escape comments on web page, fix a few minor things. 2021-06-29 19:56:04 +02:00
Nicolas Coevoet
9c41bf5466 Properly remove announceNag scheduler on Chantracker reload 2021-06-29 19:04:57 +02:00
Nicolas Coevoet
4bcfb7d7c2
Update README.md
Removed explanations about mass repeat because that feature is gone
2021-06-29 08:56:01 +02:00
Krytarik Raido
980d067bf3 HTML-escape logs on web page. 2021-06-29 04:56:04 +02:00
Krytarik Raido
7de2ddf161 Fix server.py on current Python version. 2021-06-29 02:04:04 +02:00
Krytarik Raido
f4cfdf9722 Apply the remainder of the changes. 2021-06-28 23:23:04 +02:00
Nicolas Coevoet
2c69f1bc05 minors improvement by Krytarik Raido 2021-06-28 05:38:39 +00:00
Nicolas Coevoet
17010410e1 hot fix for weired cidr bans 2021-06-25 20:02:20 +00:00
Nicolas Coevoet
0b0b496b49 fix cidr bans ValueError 2021-06-25 17:53:04 +00:00
Nicolas Coevoet
b5d03d9718
Update README.md
Added methods about protection configuration
2021-06-25 10:08:46 +02:00
Nicolas Coevoet
80fa0b9672 Sorry ... autopep8 --in-place <filename> 2021-06-25 09:56:51 +02:00
Nicolas Coevoet
f088cea7f6 removing unneeded CAP REQ 2021-06-24 22:28:16 +00:00
Nicolas Coevoet
f00be5916c Added ability for op (allowOpToConfig) to see and change some channels protection settings 2021-06-24 22:31:27 +02:00
Nicolas Coevoet
15475ec507 preparing new feature allowing op to configure their channels 2021-06-24 22:29:28 +02:00
Nicolas Coevoet
38ab2e449f Complete modification or repetition detection, mostly for pattern creation 2021-06-24 22:29:27 +02:00
Nicolas Coevoet
625761a555 special case with askOpAboutMode when the operator doesn't give a duration, the autoexpire one was overritten to forever 2021-06-24 17:30:15 +00:00
Nicolas Coevoet
f6ff5b8128 better supports of libera: added support for account-tag CAP, CHGHOST 2021-06-23 18:07:17 +00:00
Nicolas Coevoet
c92c1ded5f
Update README.md
advertise "protected" capability a bit more
2021-06-23 16:59:56 +02:00
Nicolas Coevoet
a3d25c44b7 fixed missing --flood option in wrap 2021-06-19 22:23:41 +02:00
Nicolas Coevoet
3c80464cdf added new method cpmode:
usefull to copy elements of <modea> from #a to #b into <modeb>
2021-06-19 22:03:57 +02:00
Nicolas Coevoet
38e21926d1 Added --flood option for pending, for a better view of active elements 2021-06-19 19:18:23 +00:00
Nicolas Coevoet
b1799be27c Modified how uq/ue/ub/ui elements are computed if a hostmask is given
now the hostmask is checked against the active list
that allows to do !uq $a:* which will removes all quiets on $a:<account>
2021-06-19 21:14:14 +02:00
Nicolas Coevoet
22103587fa Added <delay> to 'modes', 5 minutes instead of 3 when prompted 2021-06-07 07:55:32 +00:00
Nicolas Coevoet
4b751acb8e
Update README.md
More visible mention of the protected capability
2021-06-04 16:41:37 +02:00
Nicolas Coevoet
270b2c143f Added new config entries, removeAllBans|Quiets|Invites|Exempts to prevent by default the mass removal with ub * 2021-06-01 20:55:02 +00:00
Nicolas Coevoet
6628af1088 fix some inconsistencies for kickOnMode and kickMode ( and bans ) 2021-06-01 05:41:51 +00:00
Nicolas Coevoet
74c459dc71 Fixed an old issue with computation of affected users on extbans with a channel forwarding, count each ipv6 as unique ( not /64 anymore ), and bold in ops command to logChannel 2021-05-31 11:21:43 +00:00
Nicolas Coevoet
7a60d99121 added , the related per channel config and -clone capability 2021-05-29 23:24:41 +00:00
Nicolas Coevoet
30afb2817c increased during to comment a new ban to 5 minutes 2021-05-29 23:01:25 +00:00
Nicolas Coevoet
05f4297c54 Added --count on , fixed an NPE when logChannel is a nick 2021-05-27 05:37:39 +00:00
Nicolas Coevoet
a456d4e431 Merge branch 'master' of github.com:ncoevoet/ChanTracker 2021-05-24 20:22:43 +00:00
Nicolas Coevoet
33dd4fdcfe better handling on NickServ GHOST with announceKick on libera 2021-05-24 20:21:51 +00:00
Nicolas Coevoet
61abdda95a
Update README.md
Moving to libera
2021-05-23 09:53:26 +02:00
Krytarik Raido
2962c3190b Various improvements to web interface and handling of it. 2021-02-09 15:06:15 +00:00
Nicolas Coevoet
26646c7d7f Revert "patch From: Krytarik Raido <krytarik@gmail.com>"
This reverts commit 2060b4f8c6d611bebc0b64089daa82094a495e35.
2021-02-09 15:06:07 +00:00
Nicolas Coevoet
2060b4f8c6 patch From: Krytarik Raido <krytarik@gmail.com> 2020-11-24 20:22:16 +00:00
Nicolas Coevoet
9ce973e926 Preparing some anti capability for protections 2020-10-28 08:02:32 +00:00
Nicolas Coevoet
762163f44a switch server.py to python3 thanks to Unit193 2020-10-28 08:02:09 +00:00
Nicolas Coevoet
2407d05aa7 #18 2020-10-01 13:09:24 +00:00
Nicolas Coevoet
94990c5234 Merge branch 'master' of github.com:ncoevoet/ChanTracker 2020-10-01 09:52:06 +00:00
Nicolas Coevoet
c0bb2d11e8 #18 2020-10-01 09:51:58 +00:00
Nicolas Coevoet
1068737ed1
Update __init__.py 2020-09-29 19:23:05 +02:00
Nicolas Coevoet
25aee3bc91 added support for halfop checks 2020-09-28 22:15:08 +00:00
Nicolas Coevoet
7c136558f9 added global trusted & protected capabilities for Vip 2020-09-21 07:46:37 +00:00
Nicolas Coevoet
d0ff0d36c4 Fixing error in summary with .* ips 2019-11-15 15:03:22 +00:00
Nicolas Coevoet
7fd423a7d8 Added support for : on the command 'match' 2019-10-30 09:47:23 +00:00
Nicolas Coevoet
c46ab7297e
Merge pull request #28 from teward/patch-2
Add specific Limnoria version reqs.  (Also #26, #27)
2018-10-15 16:34:49 +02:00
Thomas Ward
ea20091803
Add specific Limnoria version reqs.
Add specific Limnoria version per Progrval's request
2018-10-15 10:33:41 -04:00
Nicolas Coevoet
d182868858
Merge pull request #27 from teward/patch-1
README.md: Add note about Limnoria versions. (#26)
2018-10-15 16:32:19 +02:00
Thomas Ward
0a09ad0ac9
README.md: Add note about Limnoria versions. (#26)
Add a blurb about how you may need to rely on non-Distribution packages (such as from PyPI or from source directly) of Limnoria to get this to work properly.  This sort of addresses #26.
2018-10-15 10:02:22 -04:00
Nicolas Coevoet
7b9edfff6b fix for kickMessage 2018-06-11 11:11:19 +02:00
Nicolas Coevoet
84238bfed9 fix for logChannel 2018-06-11 10:48:13 +02:00
Nicolas Coevoet
1aaf30c8fe fix few issue related to py3 2018-06-11 10:34:19 +02:00
Nicolas Coevoet
06013c8aa7 allows nick to be used as target for logChannel 2018-06-06 10:18:34 +02:00
Nicolas Coevoet
9874005392
Update README.md 2018-06-06 09:50:56 +02:00
Nicolas Coevoet
668f03f120 py3 version ?! 2018-05-31 18:20:11 +02:00
Nicolas Coevoet
3d0a3753f4 fix previous commit - ended with *add plugin.py@* banned/quieted 2018-04-16 17:16:46 +02:00
Nicolas Coevoet
d70e44b0b8 resolve #22 2018-04-13 16:11:32 +02:00
Nicolas Coevoet
0c105a03ba resolve #22 this new feature is only available for b,q,e,I and note commands, not automated actions 2018-03-30 11:49:57 +02:00
Nicolas Coevoet
7a4ff48863 fixs and added support for -capability protections 2018-03-09 11:56:13 +01:00
Nicolas Coevoet
02f23df444 opSettable config var 2018-02-09 16:56:45 +01:00
Nicolas Coevoet
903a37b893 force resync on plugin reload 2018-02-02 14:34:07 +01:00
Nicolas Coevoet
88985f3b55 fix extract command and ensure regexp are correct on addregexpattern 2018-02-01 12:43:31 +01:00
Nicolas Coevoet
93d55d9052 fix pattern not correctly updated 2018-01-30 10:08:41 +01:00
Nicolas Coevoet
648c416ca6
Update README.md 2018-01-26 17:04:06 +01:00
Nicolas Coevoet
2e31eb5983
Update README.md
added help about patterns
2018-01-26 17:03:50 +01:00
Nicolas Coevoet
c33569f42a feature added : permanent pattern, you can decide which regexp or word will trigger something ( you don't need chanreg anymore ) 2018-01-26 16:42:03 +01:00
Nicolas Coevoet
9278114aed
Update README.md 2018-01-23 20:53:49 +01:00
Nicolas Coevoet
90b968cfb6 initial changes of v2, which will include integration of sigyn's permanent patterns system, separated plugin for handling hostmasks and features shared between Sigyn and ChanTracker 2018-01-23 20:50:44 +01:00
Nicolas Coevoet
560fc423c7 Merge branch 'master' of https://github.com/ncoevoet/ChanTracker 2016-12-07 21:04:54 +01:00
Nicolas Coevoet
2fe1022861 #19 prevents uneeded errors messages when editing to -1s items already sets forever 2016-12-07 21:03:35 +01:00
Nicolas Coevoet
8a9bfc77fb Update __init__.py 2016-11-11 09:12:25 +01:00
Nicolas Coevoet
a13986ec2c reduce used memory 2016-09-13 18:42:36 +02:00
Nicolas Coevoet
1131f3870d Update README.md 2016-09-03 09:56:28 +02:00
Nicolas Coevoet
a46700b5ef Merge branch 'master' of https://github.com/ncoevoet/ChanTracker 2016-09-03 09:36:03 +02:00
Nicolas Coevoet
8169616808 uses kickMax features when bot is following a ban 2016-09-03 09:35:59 +02:00
Nicolas Coevoet
3c9900a1b0 Merge pull request #17 from damascene/master
update to be able to load plugin with python 3
2016-01-26 10:29:33 +01:00
Usama Akkad
cdb8e51190 removed the space between “print” and the parenthesis for better style
As suggested by <pinkieval> at #limnoria
2016-01-26 10:45:18 +02:00
Usama Akkad
f943106731 update to be able to load plugin with python 3 2016-01-26 10:37:23 +02:00
Nicolas Coevoet
ecaa309c30 better way to handle socket's call inside thread instead of fake internal messages 2016-01-08 18:33:25 +01:00
8 changed files with 5702 additions and 4353 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
github: [ncoevoet]

423
README.md
View File

@ -1,45 +1,124 @@
# ChanTracker : a supybot plugin for ban tracking #
# ChanTracker — a Supybot plugin for ban tracking #
This supybot plugin keeps records of channel mode changes in a sqlite database and permits management of them over time. It stores affected users, enabling deep searching through them, reviewing actives, editing duration, showing logs, marking/annotating them, etc.
This Supybot plugin keeps records of channel mode changes in an SQLite database and permits
management of them over time. It stores affected users, enabling deep searching through them,
reviewing actives, editing duration, showing logs, marking/annotating them, etc.
The plugin is used in various and large channels on freenode and others networks
The plugin is used in various and large channels on Libera.Chat and other networks.
This version works with Python 3.
For cidr supports, you must install ipaddress ( pip for python 2.7 https://pypi.python.org/pypi/py2-ipaddress ) or netaddr ( python 2.7 https://pypi.python.org/pypi/netaddr )
## Installation ##
Note that you may need a newer version of Limnoria than your distribution provides, so you may
need to install it from the source code or via PyPI/`pip` to make the plugin function.
(Currently it requires Limnoria version 2018.04.14 or newer.)
You can install the plugin with:
pip3 install git+https://github.com/ncoevoet/ChanTracker.git
Or with Limnoria versions older than 2020.05.08, in your bot's `plugins` directory:
git clone https://github.com/ncoevoet/ChanTracker.git
Then `@load ChanTracker`.
## Commands ##
!b,e,i,q [<channel>] <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1> or empty means forever] <reason>) -- +mode targets for duration <reason> is mandatory
!ub,ue,ui,uq [<channel>] <nick|hostmask|*> [<nick|hostmask>]) -- sets -mode on them, if * found, remove them all
!check [<channel>] <pattern> returns list of users who will be affected by such pattern
!edit <id> [,<id>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1>] means forever) -- change expiration of some active modes
!info <id> returns information about a mode change
!affect <id> returns affected users by a mode placed
!mark <id> [,<id>] <message> add a comment about a mode change
!editandmark <id> [,<id>] [,<id>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<-1>] [<comment>] edit duration and add comment on a mode change
!pending [<channel>] (pending [--mode=<e|b|q|l>] [--oper=<nick|hostmask>] [--never] [<channel>] ) -- returns active items for --mode if given, filtered by --oper if given, --never never expire only if given
!query [--deep] [--never] [--active] [--channel=<channel>] <pattern|hostmask|comment>) -- search inside ban database, --deep to search on log, --never returns items set forever and active, --active returns only active modes, --channel reduces results to a specific channel
!match [<channel>] <nick|hostmask> returns list of modes that affects the nick,hostmask given
!detail <id> returns log from a mode change
!remove [<channel>] <nick> [<reason>] do a force part on <nick> in <channel> with <reason> if provided
!modes [<channel>] <mode> Sets the mode in <channel> to <mode>, sending the arguments given, bot will ask for op if needed.
!summary [<channel>] returns some stats about <channel>
@b,q,e,i [<channel>] [--perm] <nick|hostmask>[,<nick|hostmask>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] <reason>
+<mode> targets for duration; <reason> is mandatory, <-1> or empty means forever,
add --perm if you want to add it to permanent bans of channel
@ub,uq,ue,ui [<channel>] <nick|hostmask|*> [<nick|hostmask|*>] -- sets -<mode> on them; if * is given, remove them all
@k,r [<channel>] <nick> [<reason>] -- kick or force-part <nick> with <reason> if provided
@edit <id>[,<id>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s]
change expiry of an active mode change; <-1s> means forever, <0s> means remove
@mark <id>[,<id>] <message> -- add comment on a mode change
@editandmark <id>[,<id>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] [<reason>]
change expiry and mark of an active mode change; if you got this message while the bot
prompted you, your changes were not saved; <-1s> means forever, <0s> means remove
@info <id> -- summary of a mode change
@detail <id> -- logs of a mode change
@check [<channel>] <pattern> -- returns a list of users affected by a pattern
@affect <id> -- list users affected by a mode change
@match [<channel>] <nick|hostmask#username>
returns active modes that affect the given target; nick must be in a channel shared with the bot
@query [--deep] [--never] [--active] [--ids] [--channel=<channel>] <pattern|hostmask|comment>
search in tracking database; --deep to search in logs, --never returns items set forever and active,
--active returns only active modes, --ids returns only ids, --channel limits results to the specified channel
@pending [<channel>] [--mode=<e|b|q|l>] [--oper=<nick|hostmask>] [--never] [--ids] [--count] [--flood] [--duration [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s]]
returns active items for --mode, filtered by --oper, --never (never expire), --ids (only ids),
--duration (item longer than), --count returns the total, --flood one message per mode
@modes [<channel>] [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] <mode> [<arg> ...]
sets the mode in <channel> to <mode>, sending the arguments given; <channel> is only
necessary if the message isn't sent in the channel itself, <delay> is optional
@ops [<reason>] -- triggers ops in the operators channel
@summary [<channel>] -- returns various statistics about channel activity
@weblink -- provides link to web interface
@addpattern [<channel>] <limit> <life> <mode>(bqeIkrd) [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] <pattern>
add a <pattern> which triggers <mode> for <duration> if the <pattern> appears
more often than <limit> (0 for immediate action) during <life> in seconds
@addregexpattern [<channel>] <limit> <life> <mode>(bqeIkrd) [<years>y] [<weeks>w] [<days>d] [<hours>h] [<minutes>m] [<seconds>s] /<pattern>/
add a <pattern> which triggers <mode> for <duration> if the <pattern> appears
more often than <limit> (0 for immediate action) during <life> in seconds
@rmpattern [<channel>] <id>[,<id>] -- remove patterns by <id>
@lspattern [<channel>] [<id|pattern>] -- return patterns in <channel> filtered by optional <id> or <pattern>
@addtmp [<channel>] <pattern> -- add temporary pattern, which follows repeat punishments
@rmtmp [<channel>] -- remove temporary patterns if any
@cflood [<channel>] [<permit>] [<life>] [<mode>] [<duration>]
return channel's config or apply <mode> (bqeIkrdD) for <duration> (in seconds)
if a user sends more than <permit> (-1 to disable) messages during <life> (in seconds)
@crepeat [<channel>] [<permit>] [<life>] [<mode>] [<duration>] [<minimum>] [<probability>] [<count>] [<patternLength>] [<patternLife>]
return channel's config or apply <mode> (bqeIkrdD) for <duration> (in seconds)
if <permit> (-1 to disable) repetitions are found during <life> (in seconds);
it will create a temporary lethal pattern with a mininum of <patternLength>
(-1 to disable pattern creation); <probablity> is a float between 0 and 1
@chl [<channel>] [<permit>] [<mode>] [<duration>]
return channel's config or apply <mode> (bqeIkrdD) during <duration> (in seconds)
if <permit> (-1 to disable) channel nicks are found in a message
@cnotice [<channel>] [<permit>] [<life>] [<mode>] [<duration>]
return channel's config or apply <mode> (bqeIkrdD) for <duration> (in seconds)
if <permit> (-1 to disable) messages are channel notices during <life> (in seconds)
@ccycle [<channel>] [<permit>] [<life>] [<mode>] [<duration>]
return channel's config or apply <mode> (bqeIkrdD) for <duration> (in seconds)
if <permit> (-1 to disable) parts/quits are received by a host during <life> (in seconds)
@cclone [<channel>] [<permit>] [<mode>] [<duration>]
return channel's config or apply <mode> (bqeIkrdD) for <duration> (in seconds)
if <permit> (-1 to disable) users with the same host join the channel
@cnick [<channel>] [<permit>] [<life>] [<mode>] [<duration>]
return channel's config or apply <mode> (bqeIkrdD) during <duration> (in seconds)
if a user changes nick <permit> (-1 to disable) times during <life> (in seconds)
@ccap [<channel>] [<permit>] [<life>] [<mode>] [<duration>] [<probability>]
return channel's config or apply <mode> (bqeIkrdD) for <duration> (in seconds)
if <permit> (-1 to disable) messages during <life> (in seconds)
contain more than <probability> (float between 0-1) uppercase chars
@cbad [<channel>] [<permit>] [<life>] [<mode>] [<duration>]
return channel's config or apply <mode> (bqeIkrdD) for <duration> (in seconds)
if a user triggers <permit> (-1 to disable) channel protections during <life> (in seconds)
@cautoexpire [<channel>] [<autoexpire>]
return channel's config or auto remove new elements after <autoexpire> (-1 to disable, in seconds)
## General Usage ##
The bot can be used to place and remove bans (rather than the the op setting channel modes directly). For example, to quiet the argumentative user 'ian' for 10 minutes and ban the spammer 'ham' for a month:
The bot can be used to place and remove bans (rather than the op setting channel modes directly).
For example, to quiet the argumentative user 'ian' for 10 minutes and ban the spammer 'ham' for a month:
!q ian 10m argumentative again
!b ham 30d silly spammer
!b foo 1h30m must stop
@q ian 10m argumentative again
@b ham 30d silly spammer
@b foo 1h30m must stop
These can also be done via a private message to the bot, although you must include the channel in the message:
/msg mybigbadbot q #myChannel ian 10m argumentative again
/msg mybigbadbot b #myChannel ham 30d silly spammer
For each of these bans, the nick is used to generate a \*!\*@host ban. The desired mask can be given directly to the bot instead of the nick. Also note that, by default, the bot will also kick users that have a +b set against them (details below).
For each of these bans, the nick is used to generate a `*!*@host` ban. The desired mask can be given directly
to the bot instead of the nick. Also note that, by default, the bot will also kick users that have a ban set
against them (details below).
Alternatively, the bot can be used just to track the mode changes, with ops using the capabilities of their own IRC clients to set bans. The same sequence as before:
Alternatively, the bot can be used to just track the mode changes, with ops using the capabilities of their
own IRC clients to set bans. The same sequence as before:
/msg chanserv #myChannel op
/mode #myChannel +q *!*@ranty.ian.home
@ -48,7 +127,9 @@ Alternatively, the bot can be used just to track the mode changes, with ops usin
/kick ham
/msg mybigbadbot 30d silly spammer
If you annotate the bans within 3 minutes of setting them, then you can do so without any additional syntax as above; if you miss that window or are otherwise not setting bans via the bot, the `pending`, `edit`, `mark` and `editandmark` commands can be used to provide annotations and expiration information. For example, if you had not immediately annotated the quiet.
If you annotate the bans within 5 minutes of setting them, then you can do so without any additional syntax as above.
Otherwise, the `pending`, `edit`, `mark`, and `editandmark` commands can be used to provide annotations and expiration
information. For example, if you had not immediately annotated the quiet:
/msg mybigbadbot query ian!*@*
/msg mybigbadbot pending #myChannel
@ -57,223 +138,253 @@ If you annotate the bans within 3 minutes of setting them, then you can do so wi
/msg bigbadbot mark 18 even more argumentative and EXTREMELY ANGRY
/msg bigbadbot editandmark 18 20m even more argumentative and EXTREMELY ANGRY
ChanTracker also allows you to work out which users would be affected by a ban before it is placed and which bans affect a given user ( assuming the bot shares a channel with the user ).
ChanTracker also allows you to work out which users would be affected by a ban before it is placed and which bans affect
a given user (assuming the bot shares a channel with the user).
/msg bigbadbot check #myChannel *!*@*.com <-- oops?
/msg bigbadbot match #myChannel ian <-- will return
/msg bigbadbot match #myChannel ian <-- will return
<bigbadbot> [#21 +b ian!*@* by me!~me@example.net expires at 2014-04-13 15:20:03 GMT] "even angrier"
## Settings ##
**If you want the bot to manage its own op status, you must change the config value**:
If you want the bot to manage its own op status, you must change this setting:
!config supybot.plugins.ChanTracker.doNothingAboutOwnOpStatus False
!config channel #myChannel supybot.plugins.ChanTracker.doNothingAboutOwnOpStatus True
@config supybot.plugins.ChanTracker.doNothingAboutOwnOpStatus False
@config channel #myChannel supybot.plugins.ChanTracker.doNothingAboutOwnOpStatus True
After the 'doNothingAboutOwnOpStatus' changed to False, bot will deop in each channel is in (if opped) so take a look at:
After `doNothingAboutOwnOpStatus` is changed to `False`, the bot will deop in each channel it is opped in, so take a look at:
!config supybot.plugins.ChanTracker.keepOp False
!config channel #myChannel supybot.plugins.ChanTracker.keepOp True
@config supybot.plugins.ChanTracker.keepOp False
@config channel #myChannel supybot.plugins.ChanTracker.keepOp True
You should increase the ping interval because when the bot joins a channel it requests lots of data and sometimes the server takes time to answer
You should decrease the ping interval, because when the bot requests a load of data when it joins a channel,
sometimes it could be throttled by the server, and the bot will retry at next ping/pong:
!config supybot.protocols.irc.ping.interval 3600
@config supybot.protocols.irc.ping.interval 60
Here list of data requested by the bot at join:
Here is the list of data requested by the bot at join:
JOIN :#channel
MODE :#channel
MODE :#channel b
MODE :#channel q
WHO #CHANNEL %tnuhiar,42
WHO #CHANNEL %tuhnairf,1
and if opped or at first op:
...and if opped or at first op:
MODE :#channel e
MODE :#channel I
The channel modes that will be tracked are currently defined here (qb, and eI if opped -- only ops can see the e and I lists for a channel):
The channel modes that will be tracked are currently defined here (by default `bq`, and `eI` if opped
-- only ops can see the `e` and `I` lists for a channel):
!config supybot.plugins.ChanTracker.modesToAsk
!config supybot.plugins.ChanTracker.modesToAskWhenOpped
!config channel #myChannel supybot.plugins.ChanTracker.modesToAsk b, q
!config channel #myChannel supybot.plugins.ChanTracker.modesToAskWhenOpped e
@config supybot.plugins.ChanTracker.modesToAsk
@config supybot.plugins.ChanTracker.modesToAskWhenOpped
@config channel #myChannel supybot.plugins.ChanTracker.modesToAsk b, q
@config channel #myChannel supybot.plugins.ChanTracker.modesToAskWhenOpped e
The command used by the bot to op itself is editable here:
The command used by the bot to op itself is editable here, where `$channel` and `$nick` will be replaced
with the target channel and the bot's nick at runtime:
!config supybot.plugins.ChanTracker.opCommand by default it's "CS OP $channel $nick"
@config supybot.plugins.ChanTracker.opCommand "PRIVMSG ChanServ :OP $channel $nick"
Where $channel and $nick will be replaced by targeted channel and bot's nick at runtime, so you could replace it with :
You can also tell the bot to use ChanServ for quiet and unquiet, if it has the `+r` flag on Atheme services:
!config supybot.plugins.ChanTracker.opCommand "PRIVMSG ChanServ :OP $channel $nick"
You can also tell the bot to use ChanServ for quiet and unquiet, if it has +r flag, on freenode:
!config supybot.plugins.ChanTracker.useChanServForQuiets True
!config supybot.plugins.ChanTracker.quietCommand "PRIVMSG ChanServ :QUIET $channel $hostmask"
!config supybot.plugins.ChanTracker.unquietCommand "PRIVMSG ChanServ :UNQUIET $channel $hostmask"
@config supybot.plugins.ChanTracker.useChanServForQuiets True
@config supybot.plugins.ChanTracker.quietCommand "PRIVMSG ChanServ :QUIET $channel $hostmask"
@config supybot.plugins.ChanTracker.unquietCommand "PRIVMSG ChanServ :UNQUIET $channel $hostmask"
For more readable date information in output, you should change this:
!config supybot.reply.format.time.elapsed.short True
@config supybot.reply.format.time.elapsed.short True
The bot can have a "reporting channel" like an -ops channel, where it forwards a lot of important information about channel activity. You can set it globally or per channel:
The bot can have a *reporting channel* like an -ops channel, where it forwards a lot of important
information about channel activity. You can set it globally or per channel:
!config supybot.plugins.ChanTracker.logChannel #myGeneralSecretChannel
!config channel #myChannel supybot.plugins.ChanTracker.logChannel #myChannel-ops
@config supybot.plugins.ChanTracker.logChannel #myGeneralSecretChannel
@config channel #myChannel supybot.plugins.ChanTracker.logChannel #myChannel-ops
You can use colors in it:
!config channel #myChannel supybot.plugins.ChanTracker.useColorForAnnounces True
@config channel #myChannel supybot.plugins.ChanTracker.useColorForAnnounces True
You can tweak which information you would like to be forwarded to the reporting channel. Some reporting is activated by default like topic changes, mode changes, etc, some not, like bot's ban/quiet edit/mark etc. Take a look at:
You can tweak which information you would like to be forwarded to the reporting channel.
Some reporting is activated by default, like topic changes, mode changes, etc.
While some are not, like ban/quiet and edit/mark by the bot, etc. Take a look at:
!search supybot.plugins.ChanTracker.announce
!config help supybot.plugins.ChanTracker.announceModes
@search supybot.plugins.ChanTracker.announce
@config help supybot.plugins.ChanTracker.announceModes
If desired, the bot can send a private message to the op that sets a tracked mode. Note the op must be known as channel's op by the bot; the bot owner automatically has that capability:
If desired, the bot can send a private message to the op that sets a tracked mode. Note the op must
be known as channel op by the bot; the bot owner automatically has that capability:
!config channel #myChannel supybot.plugins.ChanTracker.askOpAboutMode True
@config channel #myChannel supybot.plugins.ChanTracker.askOpAboutMode True
You can add op capability to someone by doing:
!user register opaccount password
!hostmask add opaccount *!*@something
!admin capability add opaccount #myChannel,op
The bot can set a default duration for new tracked mode changes, in order to auto remove them:
@user register opaccount password
@hostmask add opaccount *!*@something
@admin capability add opaccount #myChannel,op
!config channel #myChannel supybot.plugins.ChanTracker.autoExpire 3600 (1h)
The bot can set a default duration for new tracked mode changes, in order to automatically remove them:
The plugin can create persistent bans to help manage large ban lists that exceed the IRCd's limits on the length of ban lists. The plugin can remove bans from the IRCd ban list while checking all joining users against its own lists. If a user matches, then the IRCd ban is reinstated:
@config channel #myChannel supybot.plugins.ChanTracker.autoExpire 3600 (1 hour)
!config channel #myChannel supybot.plugins.ChanTracker.useChannelBansForPermanentBan true
!channel ban add #myChannel *!*@mask
!b #example --perm baduser,baduser2 1w stop trolling ( --perm add computed hostmasks to Channel.ban )
The plugin can create persistent bans to help manage large ban lists that exceed the IRCd's limits on
the length of ban lists. It can remove bans from the IRCd ban list while checking all joining users
against its own lists. If a user matches, then the IRCd ban is reinstated:
With autoExpire enabled, the IRCd list is pruned as appropriate and bans are rotated in a way to not reveal the pattern used for the match. Due to a supybot limitation, extended bans are not supported with this feature.
@config channel #myChannel supybot.plugins.ChanTracker.useChannelBansForPermanentBan true
@channel ban add #myChannel *!*@mask
@b #example --perm baduser,baduser2 1w stop trolling (--perm adds computed hostmasks to Channel.ban)
If supported by the ircd, the bot can track account changes and get GECOS and username information when a user joins the channel. This requires ircd CAP features: http://tools.ietf.org/html/draft-mitchell-irc-capabilities-01
With `autoExpire` enabled, the IRCd list is pruned as appropriate and bans are rotated in a way to not reveal
the pattern used for the match. Due to a Supybot limitation, extended bans are not supported with this feature.
The plugin also supports extended bans/quiets including $r, $x, $a, $j (real name, full match and account name, respectively). If you want the plugin to support your IRCd extended bans, please, report a bug or contact me directly.
If supported by the IRCd, the bot can track account changes and get GECOS and username information when a user
joins the channel. This requires IRCd CAP features: https://ircv3.net/specs/extensions/capability-negotiation.html
By default, if the bot is asked to set a ban (+b), it will also kick affected users (Note: bot will only kick people if the ban was set by the bot -- if an op places the ban, the bot will not kick affected users). See:
The plugin also supports extended bans/quiets including `$a`, `$r`, `$x`, and `$j` (account name, real name, full match,
and ban channel). If you want the plugin to support your IRCd's extended bans, please file a feature request or contact
us via our IRC channel.
!config supybot.plugins.ChanTracker.kickMode
!config supybot.plugins.ChanTracker.kickMessage
!config help supybot.plugins.ChanTracker.kickOnMode
The bot will remove exception modes (that is exempt e, or invite exempt I) for people banned if 'doActionAgainstAffected' for given channel is True.
By default, if the bot is asked to set a ban (`+b`), it will also kick affected users. See:
ChanTracker is trying to resolve ip behind host, but that can affect performance or freeze the bot due to socket's calls, if you use 'supybot.plugins.ChanTracker.resolveIp' to True, you should set 'supybot.debug.threadAllCommands' to True to avoid that.
@config supybot.plugins.ChanTracker.kickMode
@config supybot.plugins.ChanTracker.kickMessage
@config help supybot.plugins.ChanTracker.kickOnMode
**Due to changes on January 5 2016**, if your bot has 'supybot.capabilities.default' to False, Bot must have an account on itself with his cloak/host inside and ChanTracker capability ( because it calls ChanTracker.resolve ).
!user register botaccount randompassword
!hostmask add botaccount *!ident@bot.host
!admin capability add botaccount ChanTracker
The bot will remove exemption modes (that is exempt `e`, or invite exempt `I`) for people banned if
`doActionAgainstAffected` for the given channel is `True`.
## Channel Protection ##
The plugin has a lot of built-in channel protection features that can be enabled either individually and per channel, or globally:
The plugin has a lot of built-in channel protection features that can be enabled either
individually and per-channel, or globally:
- flood detection
- low-rate flood detection: flooding but with client rate-limiting
- repeat detection
- massRepeat detection: when same message comes from different users
- capslock: detect people who are EXTREMELY ANGRY
- ctcp: detect sending CTCP to the channel
- ctcp: detect sending CTCPs to the channel
- notices: detect sending notices to the channel
- hilight: nick spam
- nick: nick change spam
- cycle: join/part flood
- massJoin
- evades of quiet/bans via gateway/ ( if resolveIp is True )
- clones detection
- evades of quiet/bans via gateway (if `resolveIp` is enabled)
- clone detection
You should tweak settings to fits your needs, do not use default values. It really depends channel's population and usage ...
You should tweak settings to fit your needs, do not use default values. It really depends
on the channel's population and usage...
Each of those detections has the same kind of settings: there is *Permit (-1 to disable), *Life (which is the time interval over which the bot will track previous messages/behaviour), *Mode (which allows you to select which action you want to use against the user). The action modes that can be set are:
Each of those detections has the same kind of settings:
- q : quiet the user
- b : ban the user
- k : kick the user
- r : remove (force part) the user, if the IRCd has the feature. 'REMOVE $channel $nick :$reason'
- d : debug > forward action to logChannel if configured
- `*Permit`: (-1 to disable)
- `*Life`: time interval over which the bot will track previous messages/behaviour
- `*Mode`: allows you to select which action you want to use against the user
- `*Duration`: duration of the action taken
- `*Comment`: (empty for no comment)
For bans (b and q mode), you can choose the *Duration of the quiet/ban, and add a *Mark on the related quiet/ban. The 'bad' settings, when enabled (badPermit > -1) keeps track of users who did something wrong during badLife, and can end to badMode if the user exceeds the limit.
The action modes that can be set are:
Example: flood control: to quiet for 1 minute anyone who sends more than 4 messages in 7 seconds to #channel; if the user continues to flood, after 2 times on 5 minutes he will be banned:
- `q`: quiet the user
- `b`: ban the user
- `k`: kick the user
- `r`: remove (force-part) the user, if the IRCd has the feature
- `d`: debug -- forward action to log channel, if configured
!config channel #channel supybot.plugins.ChanTracker.floodPermit 4 <-- max number of messages allowed
!config channel #channel supybot.plugins.ChanTracker.floodLife 7 <-- in 7 seconds
!config channel #channel supybot.plugins.ChanTracker.floodMode q <-- quiet the user
!config channel #channel supybot.plugins.ChanTracker.floodDuration 60 <-- for 60 seconds
!config channel #channel supybot.plugins.ChanTracker.badPermit 2 <-- if user does that 3 times,
!config channel #channel supybot.plugins.ChanTracker.badLife 300 <-- during 5 minutes
!config channel #channel supybot.plugins.ChanTracker.badMode b <-- ban them
For bans (`b` and `q` modes), you can choose the `*Duration` of the quiet/ban, and set a `*Comment` for it.
The `bad` settings, when enabled (`badPermit > -1`) keep track of users who did something wrong during `badLife`,
and can lead to `badMode` if the user exceeds the limit.
Additionally, the bot can track how many bad actions occur over a period of time and if a threshold is passed, this constitutes an attack on the channel. The attack* settings, when enabled keeps track of bad actions, and if the number exceeds attackPermit within attackLife, some specific channel modes are set for an attackDuration.
**Example:** Flood control -- to quiet for 1 minute anyone who sends more than 4 messages in 7 seconds to #channel;
if the user continues to flood, after 3 times within 5 minutes they will be banned:
Example: not flooding: catch a wave of bots which sends the same message from different hosts:
@config channel #channel supybot.plugins.ChanTracker.floodPermit 4 <-- max number of messages allowed
@config channel #channel supybot.plugins.ChanTracker.floodLife 7 <-- in 7 seconds
@config channel #channel supybot.plugins.ChanTracker.floodMode q <-- quiet the user
@config channel #channel supybot.plugins.ChanTracker.floodDuration 60 <-- for 60 seconds
@config channel #channel supybot.plugins.ChanTracker.badPermit 2 <-- if user does that 3 times (more than 2)
@config channel #channel supybot.plugins.ChanTracker.badLife 300 <-- during 5 minutes
@config channel #channel supybot.plugins.ChanTracker.badMode b <-- ban them
!config channel #channel supybot.plugins.ChanTracker.massRepeatChars 200 <-- enable check only if there is at least 200 chars
!config channel #channel supybot.plugins.ChanTracker.massRepeatPermit 1 <-- if found 2 times
!config channel #channel supybot.plugins.ChanTracker.massRepeatLife 60 <-- don't keep messages too long in memory, to avoid false positive
!config channel #channel supybot.plugins.ChanTracker.massRepeatPercent 0.85 <-- set a low value for similarity, in order to catch them if there is some random chars in the messages
!config channel #channel supybot.plugins.ChanTracker.massRepeatMode b
!config channel #channel supybot.plugins.ChanTracker.massRepeatDuration 1800 <- ban for 30 minutes
!config channel #channel supybot.plugins.ChanTracker.attackPermit 2 <- if bot triggers 3 actions during
!config channel #channel supybot.plugins.ChanTracker.attackLife 300 <- 5 minutes
!config channel #channel supybot.plugins.chantracker.attackMode +rq $~a <- then bot will set those modes
!config channel #channel supybot.plugins.chantracker.attackDuration 1800 <- for 30 minutes
!config channel #channel supybot.plugins.chantracker.attackUnMode -rq $~a <- and bot will set those modes after 30 minutes
Example: a user repeating the same thing: (use repeat detection rather than massRepeat for this):
Additionally, the bot can track how many bad actions occur over a period of time and if a threshold is passed,
this constitutes an attack on the channel. The `attack*` settings, when enabled keep track of bad actions,
and if the number exceeds `attackPermit` within `attackLife`, some specified channel modes are set for `attackDuration`.
!config channel #channel supybot.plugins.ChanTracker.repeatPermit 3 <-- triggered after 3 similar message
!config channel #channel supybot.plugins.ChanTracker.repeatLife 40 <-- keep previous messages during 40 seconds
!config channel #channel supybot.plugins.ChanTracker.repeatPercent 0.88 <-- 1.00 for identical message, don't go too lower, you will get false positive
!config channel #channel supybot.plugins.ChanTracker.repeatMode q <-- quiet
!config channel #channel supybot.plugins.ChanTracker.repeatDuration 180 <-- for 3 minutes
**Example:** Bot flooding -- catch a wave of bots which send the same message from different hosts:
Even with all these channel protection features, the bot will do nothing against users with protected capabilities (#channel,protected).
@config channel #channel supybot.plugins.ChanTracker.attackPermit 2 <-- if bot triggers 3 bad actions (more than 2)
@config channel #channel supybot.plugins.ChanTracker.attackLife 300 <-- during 5 minutes
@config channel #channel supybot.plugins.chantracker.attackMode +rq $~a <-- then bot will set these modes
@config channel #channel supybot.plugins.chantracker.attackDuration 1800 <-- for 30 minutes
@config channel #channel supybot.plugins.chantracker.attackUnMode -rq $~a <- and bot will set these modes after they passed
## Other tips ##
**Example:** A user repeating the same thing:
Maintaining separate bots for the banning/bantracking functions and other factoid, snarfing or amusement functions is good practice.
@config channel #channel supybot.plugins.ChanTracker.repeatPermit 3 <-- triggered after 3 similar messages
@config channel #channel supybot.plugins.ChanTracker.repeatLife 40 <-- keep previous messages during 40 seconds
If the main purpose of your bot is to manage bans etc, and never interacts with users you should, as owner remove all plugin with 'owner defaultcapabilities remove <pluginname>', it will prevent the bot to answer to various command, and being used as a flood tool by others (like !echo SPAM). You could otherwise change the value of `config help supybot.capabilities.default` but be prepared to waste a lot of time each time you add a new user account on your bot.
@config channel #channel supybot.plugins.ChanTracker.repeatMinimum 8 <-- minimum size of candidate patterns
@config channel #channel supybot.plugins.ChanTracker.repeatPercent 0.88 <-- 1.00 for identical message, don't go too low or you will get false positives
@config channel #channel supybot.plugins.ChanTracker.repeatCount 6 <-- or the number of times a pattern is repeated in a single message
@config channel #channel supybot.plugins.ChanTracker.repeatPatternMinimum 12 <-- mininum size of temporary lethal pattern
@config channel #channel supybot.plugins.ChanTracker.repeatPatternLife 120 <-- life duration of those patterns in seconds
If 'supybot.capabilities.default' is changed to False, then, when you want to grant access to command for someone, you must do it that way:
@config channel #channel supybot.plugins.ChanTracker.repeatMode q <-- quiet
@config channel #channel supybot.plugins.ChanTracker.repeatDuration 3600 <-- for 1 hour
!admin capability add accountname User
!admin capability add accountname User.whoami
!admin capability add accountname whoami
## `protected` Capability ##
If your bot manage differents channels or community, remove all User.action from defaultcapabilities, create one user per channel/community, and add ops's hostmasks into it, it's easier to manage that way. Until you have someone with rights in 2 community/channels who will need a separate account.
You must remove the `protected` capability given by default to everyone, because the bot will not do anything
against users having this capability:
@defaultcapability remove protected
## Other Tips ##
Maintaining separate bots for the banning/bantracking functions and other factoid, snarfing,
or amusement functions is good practice.
If the main purpose of your bot is to manage bans etc and never interact with users, you should remove all plugins
from the default capabilities, which will prevent the bot from responding to various commands and being used as a
flood tool by others (like with `@echo SPAM`):
@defaultcapability remove <pluginname>
You could otherwise change the value of `supybot.capabilities.default`, but be prepared to waste a lot of time
each time you add a new user account on your bot. If the setting is changed to `False`, then when you want to grant
access to a command for someone, you must do it this way:
@admin capability add <accountname> User
@admin capability add <accountname> User.whoami
@admin capability add <accountname> whoami
If your bot manages different channels or communities, remove all `User.<action>` from the default capabilities,
create one user per channel/community, and add ops' hostmasks to it -- it's easier to manage this way.
Until you have someone with rights in multiple channels/communities, who will need a separate account.
You should keep your bot as quiet as possible. It should not reply to errors, users without capabilities, etc:
!config supybot.reply.error.noCapability True
!config supybot.replies.genericNoCapability ""
!config supybot.abuse.flood.command.invalid.notify False
!config supybot.reply.whenNotCommand False
!config supybot.reply.error.detailed False
!config supybot.replies.error ""
!config defaultcapability remove channel.nicks
!config defaultcapability remove channel.alert
!config defaultcapability remove alias.add
!config defaultcapability remove config
!config defaultcapability remove help
!config defaultcapability remove list
@config supybot.reply.error.noCapability True
@config supybot.replies.genericNoCapability ""
@config supybot.abuse.flood.command.invalid.notify False
@config supybot.reply.whenNotCommand False
@config supybot.reply.error.detailed False
@config supybot.replies.error ""
@config defaultcapability remove channel.nicks
@config defaultcapability remove channel.alert
@config defaultcapability remove alias.add
@config defaultcapability remove config
@config defaultcapability remove help
@config defaultcapability remove list
There are other commands that are prone to abuse as well. It's better to use the following command:
There are other commands that are prone to abuse as well. It's better to use this command:
!config supybot.capabilities.default False
This command works with any version of supybot, vanilla, limnoria, etc.
@config supybot.capabilities.default False
## Bugs and Features ##
Requests can be made via https://github.com/ncoevoet/ChanTracker or in #chantracker on chat.freenode.net.
Requests can be made via https://github.com/ncoevoet/ChanTracker
or in #chantracker on chat.libera.chat

View File

@ -25,7 +25,6 @@
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
"""
@ -37,7 +36,7 @@ import supybot.world as world
# Use this for the version of this plugin. You may wish to put a CVS keyword
# in here if you're keeping the plugin in CVS or some similar system.
__version__ = "1.3"
__version__ = "1.5"
# XXX Replace this with an appropriate author or supybot.Author instance.
__author__ = supybot.authors.unknown
@ -51,8 +50,12 @@ __url__ = 'https://github.com/ncoevoet/ChanTracker'
from . import config
from . import plugin
from imp import reload
reload(plugin) # In case we're being reloaded.
from . import server
from importlib import reload
# In case we're being reloaded.
reload(config)
reload(plugin)
reload(server)
# Add more reloads here if you add third-party modules and want them to be
# reloaded when this plugin is reloaded. Don't forget to import them as well!

417
config.py
View File

@ -25,12 +25,12 @@
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import supybot.conf as conf
import supybot.registry as registry
def configure(advanced):
# This will be called by supybot to configure this module. advanced is
# a bool that specifies whether the user identified himself as an advanced
@ -42,342 +42,427 @@ def configure(advanced):
ChanTracker = conf.registerPlugin('ChanTracker')
conf.registerGlobalValue(ChanTracker, 'pool',
registry.Integer(-1, """delay between two checks about mode removal, in seconds. Note, check is also based on irc activity, so removal may be delayed a bit, -1 to disable delay"""))
## global settings
conf.registerGlobalValue(ChanTracker, 'CAPS',
registry.CommaSeparatedListOfStrings(['account-notify','extended-join'], """CAP asked to the IRCd, that way bot can track username and account changes, without any additional request"""))
conf.registerGlobalValue(ChanTracker, 'pool',
registry.Integer(-1, """delay between two checks about mode removal, in seconds.
Note, check is also based on irc activity, so removal may be delayed a bit, -1 to disable delay"""))
conf.registerGlobalValue(ChanTracker, 'logsSize',
registry.PositiveInteger(60, """number of messages to keep in logs. Note, this is per nick - not per nick per channel"""))
registry.PositiveInteger(1, """number of messages to keep in logs. Note, this is per nick - not per nick per channel"""))
conf.registerGlobalValue(ChanTracker, 'quietCommand',
registry.String("PRIVMSG ChanServ :QUIET $channel $hostmask","""command issued to quiet a user; $channel and $hostmask will be replaced at runtime"""))
registry.String("PRIVMSG ChanServ :QUIET $channel $hostmask",
"""command issued to quiet a user; $channel and $hostmask will be replaced at runtime"""))
conf.registerGlobalValue(ChanTracker, 'unquietCommand',
registry.String("PRIVMSG ChanServ :UNQUIET $channel $hostmask","""command issued to unquiet a user $channel and $hostmask will be replaced at runtime"""))
registry.String("PRIVMSG ChanServ :UNQUIET $channel $hostmask",
"""command issued to unquiet a user; $channel and $hostmask will be replaced at runtime"""))
conf.registerGlobalValue(ChanTracker, 'announceNagInterval',
registry.Integer(-1,"""interval between two check about announceNagMode, this setting is global."""))
registry.Integer(-1, """interval between two checks about announceNagMode, this setting is global"""))
conf.registerGlobalValue(ChanTracker, 'resolveIp',
conf.registerGlobalValue(ChanTracker, 'resolveIp',
registry.Boolean(True, """trying to resolve host's ip with socket, could add latency"""))
#now per channel
conf.registerChannelValue(ChanTracker, 'avoidOverlap',
## per-channel settings
conf.registerChannelValue(ChanTracker, 'enabled',
registry.Boolean(False, """monitor the channel and act following the rest of the settings"""))
conf.registerChannelValue(ChanTracker, 'modeD',
registry.String("", """special mode if you want to use some specific stuff;
$hostmask (*!*@*), $klinemask (*@*), $host, $channel, $reason, $nick and $duration are available"""))
conf.registerChannelValue(ChanTracker, 'useAccountBanIfPossible',
registry.Boolean(False, """bot will use account bans when user is identified, only works for b,q,e,I and m (chantracker.m)"""))
conf.registerChannelValue(ChanTracker, 'avoidOverlap',
registry.Boolean(False, """avoid overlap between items, bot will try to use existing items against users, some limitations with extended bans"""))
conf.registerChannelValue(ChanTracker, 'useIpForGateway',
registry.Boolean(False, """use *!*@*ip bans instead of *!ident@gateway/* when gateways cloak is found and ends with ip.*"""))
conf.registerChannelValue(ChanTracker, 'useIpForGateway',
registry.Boolean(False, """use *!*@*ip bans instead of *!ident@gateway/* when gateway cloak is found and ends with ip.*"""))
conf.registerChannelValue(ChanTracker, 'triggerOps',
registry.Boolean(False, """!ops triggers a message in logChannel"""))
conf.registerChannelValue(ChanTracker, 'allowOpToConfig',
registry.Boolean(False, """grant channel operators the ability to configure some ChanTracker protections for their channel"""))
conf.registerChannelValue(ChanTracker, 'ignoreVoicedUser',
registry.Boolean(False, """exempt voiced users from channel protections"""))
conf.registerChannelValue(ChanTracker, 'opCommand',
registry.String("PRIVMSG ChanServ :OP $channel $nick", """command used to obtain channel operator mode"""))
registry.String("PRIVMSG ChanServ :OP $channel $nick", """command used to obtain channel operator status"""), opSettable=False)
conf.registerChannelValue(ChanTracker, 'modesToAsk',
registry.CommaSeparatedListOfStrings(['b','q'], """list of channel modes to sync into the bot's tracking database when it joins the channel"""))
conf.registerChannelValue(ChanTracker, 'modesToAskWhenOpped',
registry.CommaSeparatedListOfStrings(['e','I'], """list of channel modes to sync into the bot's tracking database when it is opped"""))
registry.CommaSeparatedListOfStrings(['b', 'q'], """list of channel modes to sync into the bot's tracking database when it joins the channel"""))
# per channel settings
# related to ban tracking
conf.registerChannelValue(ChanTracker, 'modesToAskWhenOpped',
registry.CommaSeparatedListOfStrings(['e', 'I'], """list of channel modes to sync into the bot's tracking database when it is opped"""))
conf.registerChannelValue(ChanTracker, 'ignoreOnAbuse',
registry.Boolean(False, """adds abusers on ignore for the punishment period"""))
## related to ban tracking
conf.registerChannelValue(ChanTracker, 'autoExpire',
registry.Integer(-1, """default expiration time for newly placed bans; -1 disables auto-expiration, otherwise it's in seconds"""))
# related to logChannel
conf.registerChannelValue(ChanTracker, 'autoRemoveUnregisteredQuiets',
registry.Boolean(True, """auto remove from database unregistered quiets once expired -q $~a"""))
conf.registerChannelValue(ChanTracker, 'allowPublicInfo',
registry.Boolean(False, """allow !info to be returned in where it was called if True"""))
conf.registerChannelValue(ChanTracker, 'removeAllBans',
registry.Boolean(False, """prevent accidental removal of all bans"""))
conf.registerChannelValue(ChanTracker, 'removeAllQuiets',
registry.Boolean(False, """prevent accidental removal of all quiets"""))
conf.registerChannelValue(ChanTracker, 'removeAllExempts',
registry.Boolean(False, """prevent accidental removal of all exempts"""))
conf.registerChannelValue(ChanTracker, 'removeAllInvites',
registry.Boolean(False, """prevent accidental removal of all invites"""))
## related to logChannel
conf.registerChannelValue(ChanTracker, 'logChannel',
registry.String("", """where bot announces op's actions; it is highly recommended to set an appropriate operator's channel to receive the various useful messages"""))
registry.String("", """where bot announces op actions; it is highly recommended to set an appropriate operators channel
to receive the various useful messages, nick can be used"""), opSettable=False)
conf.registerChannelValue(ChanTracker, 'useSmartLog',
registry.Boolean(True, """compress multiple announce messages at the same time into a single one"""))
conf.registerChannelValue(ChanTracker, 'useColorForAnnounces',
registry.Boolean(False, """use colors for announce messages"""))
conf.registerChannelValue(ChanTracker, 'announceOthers',
registry.Boolean(True,"""forward messages from quieted/banned users to logChannel; used when bot stays opped and channel is +z (reduced moderation).
Messages from users flagged as bad, or when channel is under attack will not be forwarded"""))
registry.Boolean(True, """forward messages from quieted/banned users to logChannel; used when bot stays opped and channel is +z (reduced moderation).
messages from users flagged as bad, or when channel is under attack will not be forwarded"""))
conf.registerChannelValue(ChanTracker, 'announceModeMadeByIgnored',
registry.Boolean(True,"""announce channel modes made by ignored user"""))
registry.Boolean(True, """announce channel modes made by ignored user"""))
conf.registerChannelValue(ChanTracker, 'announceWithNotice',
registry.Boolean(False,"""use NOTICE instead of PRIVMSG to logChannel"""))
registry.Boolean(False, """use NOTICE instead of PRIVMSG to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceModes',
registry.CommaSeparatedListOfStrings(['b','q','e','I','r','l','v','o','h','k','n','t','F','i','t','s','n','c','C'],"""announce modes listed to logChannel"""))
registry.CommaSeparatedListOfStrings(['b', 'q', 'e', 'I', 'r', 'l', 'k', 'n', 't', 'F', 'i', 't', 's', 'n', 'c', 'C'],
"""announce modes listed to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceModeSync',
registry.Boolean(False,"""announce to logChannel that synchronisation of channel modes to tracking database has completed"""))
registry.Boolean(False, """announce to logChannel that synchronisation of channel modes to tracking database has completed"""))
conf.registerChannelValue(ChanTracker, 'announceKick',
registry.Boolean(True,"""announce kick, remove, kill and kline to logChannel"""))
registry.Boolean(True, """announce kick, remove, kill and kline to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceTopic',
registry.Boolean(True,"""announce topic changes to logChannel"""))
registry.Boolean(True, """announce topic changes to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceEdit',
registry.Boolean(True,"""announce tracker item description edits to logChannel"""))
registry.Boolean(True, """announce new expiries on items to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceMark',
registry.Boolean(True,"""announce item expiration settings (marks) to logChannel"""))
registry.Boolean(True, """announce new comments on items to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceInTimeEditAndMark',
registry.Boolean(False,"""announce new comments (edits) and expiries (marks) to logChannel when they are created by the do, q, b, e, i commands"""))
registry.Boolean(False, """announce new expiries and comments to logChannel when they are created by the do, q, b, e and i commands"""))
conf.registerChannelValue(ChanTracker, 'announceMassRemoval',
registry.Boolean(False,"""announce mass ban removals 'undo *', 'uq *', 'ub *' to logChannel"""))
registry.Boolean(False, """announce mass ban removals 'undo *', 'uq *', 'ub *' to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceBotEdit',
registry.Boolean(False,"""when banning based on a channel protection trigger (such as flood prevention), announce the items comment (edit) to logChannel"""))
registry.Boolean(False, """when banning based on a channel protection trigger (such as flood prevention), announce the item expiry to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceBotMark',
registry.Boolean(False,"""when banning based on a channel protection trigger (such as flood prevention), announce the items expiry (mark) to logChannel"""))
registry.Boolean(False, """when banning based on a channel protection trigger (such as flood prevention), announce the item comment to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceNotice',
registry.Boolean(True,"""announce channel notices to logChannel"""))
registry.Boolean(True, """announce channel notices to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceCtcp',
registry.Boolean(True,"""announce channel ctcps to logChannel"""))
registry.Boolean(True, """announce channel ctcps to logChannel"""))
conf.registerChannelValue(ChanTracker, 'announceNagMode',
registry.CommaSeparatedListOfStrings([], """bot will announce that channel has such mode at announceNagInterval"""))
# others settings
conf.registerChannelValue(ChanTracker, 'announceRepeatPattern',
registry.Boolean(True, """announce repeat pattern created to logChannel"""))
## other settings
conf.registerChannelValue(ChanTracker, 'doNothingAboutOwnOpStatus',
registry.Boolean(True, """bot will never try to change his own op status"""))
registry.Boolean(True, """bot will never try to change its own op status"""))
conf.registerChannelValue(ChanTracker, 'keepOp',
registry.Boolean(False, """bot stays opped"""))
conf.registerChannelValue(ChanTracker, 'kickMode',
registry.CommaSeparatedListOfStrings(['b'], """bot will kick affected users when mode is triggered,
use if with caution, if an op bans *!*@*, bot will kick everyone on the channel"""))
registry.CommaSeparatedListOfStrings(['b'], """bot will kick affected users when mode is triggered;
use with caution, if an op bans *!*@*, bot will kick everyone on the channel - see kickMax"""))
conf.registerChannelValue(ChanTracker, 'kickOnMode',
registry.Boolean(False, """bot will kick affected users when kickMode is triggered by someone,
use if with caution"""))
registry.Boolean(False, """if the bot is not the kickMode issuer, it will still kick; use with caution"""))
conf.registerChannelValue(ChanTracker, 'kickMax',
registry.Integer(-1,"""if > 0, disable kick if affected users > kickMax, avoid to cleanup entire channel with ban like *!*@*"""))
registry.Integer(-1, """if > 0, disable kick if affected users > kickMax, avoid to clean up entire channel with ban like *!*@*"""))
conf.registerChannelValue(ChanTracker, 'kickMessage',
registry.CommaSeparatedListOfStrings(["You are banned from this channel"], """bot kick reasons"""))
registry.CommaSeparatedListOfStrings(["You are banned from this channel"], """bot kick reason"""))
conf.registerChannelValue(ChanTracker, 'discloseOperator',
registry.Boolean(True, """reveals operator's nick on sockpuppet action via the bot !b, !q, !r, !k (via the kick reason or banMessage/quietMessage)"""))
conf.registerChannelValue(ChanTracker, 'banMessage',
registry.String("You have been banned on $channel", """set empty if you don't want the bot to tell something to the user
when they have been banned (by/via the bot); in any case, if channel is under attack, bot will not send message;
if filled, the operator nick will be given for accountability if differing"""))
conf.registerChannelValue(ChanTracker, 'banNotice',
registry.Boolean(True, """if False, private message is used, if banMessage is not empty"""))
conf.registerChannelValue(ChanTracker, 'quietMessage',
registry.String("", """leave empty if you don't want the bot to tell something to the user when he has been quieted ( by/via the bot ), in any case, if channel is under attack: bot will not send message"""))
registry.String("You have been quieted on $channel", """set empty if you don't want the bot to tell something to the user
when they have been quieted (by/via the bot); in any case, if channel is under attack, bot will not send message;
if filled, the operator nick will be given for accountability if differing"""))
conf.registerChannelValue(ChanTracker, 'quietNotice',
registry.Boolean(False, """if False, private message is used, if 'quietMessage' is not empty"""))
registry.Boolean(True, """if False, private message is used, if quietMessage is not empty"""))
conf.registerChannelValue(ChanTracker, 'proxyMsgOnly',
registry.Boolean(True, """only send message to user on operator action if it was proxied through the bot"""))
conf.registerChannelValue(ChanTracker, 'trackAffected',
registry.Boolean(True, """bot tracks affected users by mode change, if you encounter too much lags/cpu usage, you could disable this feature, but bot will never kick again affected users or remove voice/op/exempt etc of affected users"""))
registry.Boolean(True, """bot tracks affected users by mode change; if you encounter too much lag or cpu usage, you could disable this feature,
but bot will never kick again affected users or remove voice/op/exempt etc of affected users"""))
conf.registerChannelValue(ChanTracker, 'doActionAgainstAffected',
registry.Boolean(True, """devoice, deop, dehalfop user affected by a mode change"""))
conf.registerChannelValue(ChanTracker, 'useChannelBansForPermanentBan',
registry.Boolean(True, """when users join the channel, check if user matchs a permanent ban set in Channel plugin"""))
registry.Boolean(True, """when users join the channel, check if user matches a permanent ban set in Channel plugin"""))
conf.registerChannelValue(ChanTracker, 'addKickMessageInComment',
registry.Boolean(False, """add kick message to mode comment in tracking database"""))
conf.registerChannelValue(ChanTracker, 'askOpAboutMode',
registry.Boolean(False,"""In a private message, ask the op who added a mode about the duration of the ban and a comment on why it was set"""))
registry.Boolean(False, """in a private message, ask the op who added a mode about the duration of the ban and a comment on why it was set"""))
conf.registerChannelValue(ChanTracker, 'checkEvade',
registry.Boolean(True,"""bot will apply same duration and mode as the evaded ban, currently only works when someone identifies to an account, and the account is banned $a:account, and has ip computed"""))
registry.Boolean(True, """bot will apply same duration and mode as the evaded ban; currently only works when someone identifies to an account
and the account is banned, or has ip computed"""))
conf.registerChannelValue(ChanTracker, 'useChanServForQuiets',
registry.Boolean(False,"""if bot is not opped, use services for quiet / unquiets"""))
registry.Boolean(False, """if bot is not opped, use network services for quiet/unquiets"""))
conf.registerChannelValue(ChanTracker, 'skynet',
registry.Integer(-1,"""when positive, bot could use some experimental features against user's marked as bad, the value represents number of bad users to trigger it"""))
# related to channel's protection
## related to channel protection
# clone detection
#clone
conf.registerChannelValue(ChanTracker, 'clonePermit',
registry.Integer(-1,"""Number of messages allowed , -1 to disable"""))
registry.Integer(-1, """number of clones allowed , -1 to disable"""))
conf.registerChannelValue(ChanTracker, 'cloneMode',
registry.String('d',"""mode used by the bot when clone detection is triggered"""))
registry.String('d', """mode used by the bot when clone detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'cloneDuration',
registry.PositiveInteger(60,"""punishment duration in seconds"""))
registry.PositiveInteger(60, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'cloneComment',
registry.String('clone detected',"""comment added on mode changes database, empty for no comment"""))
registry.String('clone detected', """comment added to tracking database, empty for no comment"""))
# flood detection
# flood detection settings
conf.registerChannelValue(ChanTracker, 'floodPermit',
registry.Integer(-1,"""Number of messages allowed during floodLife, -1 to disable"""))
registry.Integer(-1, """number of messages allowed during floodLife, -1 to disable"""))
conf.registerChannelValue(ChanTracker, 'floodLife',
registry.PositiveInteger(7,"""Duration of messages's life in flood counter, in seconds"""))
registry.PositiveInteger(7, """duration in seconds before messages are removed from counter"""))
conf.registerChannelValue(ChanTracker, 'floodMode',
registry.String('q',"""mode used by the bot when flood detection is triggered"""))
registry.String('q', """mode used by the bot when flood detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'floodDuration',
registry.PositiveInteger(60,"""punishment duration in seconds"""))
registry.PositiveInteger(60, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'floodComment',
registry.String('flood detected',"""comment added on mode changes database, empty for no comment"""))
registry.String('flood detected', """comment added to tracking database, empty for no comment"""))
# another flood queue, for user with throttled irc client, who paste long text
# another flood queue, for user with throttled irc client, who copy / paste long text
conf.registerChannelValue(ChanTracker, 'lowFloodPermit',
registry.Integer(-1,"""Number of messages allowed during lowFloodLife, -1 to disable"""))
registry.Integer(-1, """number of messages allowed during lowFloodLife, -1 to disable"""))
conf.registerChannelValue(ChanTracker, 'lowFloodLife',
registry.Integer(13,"""Duration of messages's life in lowFlood counter, in seconds"""))
registry.PositiveInteger(13, """duration in seconds before messages are removed from counter"""))
conf.registerChannelValue(ChanTracker, 'lowFloodMode',
registry.String('q',"""mode used by the bot when low flood detection is triggered"""))
registry.String('q', """mode used by the bot when low flood detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'lowFloodDuration',
registry.PositiveInteger(180,"""punishment duration in seconds"""))
registry.PositiveInteger(180, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'lowFloodComment',
registry.String('low flood detected',"""comment added on mode changes database, empty for no comment"""))
registry.String('low flood detected', """comment added to tracking database, empty for no comment"""))
# repeat detection
conf.registerChannelValue(ChanTracker, 'repeatPermit',
registry.Integer(-1,"""Number of repeated text allowed, -1 to disable"""))
conf.registerChannelValue(ChanTracker, 'repeatLife',
registry.PositiveInteger(12,"""Duration of messages's life in repeatPermit counter in seconds"""))
conf.registerChannelValue(ChanTracker, 'repeatPercent',
registry.Probability(0.85,"""percent of similarity needed between previous and current message to trigger a repeat count"""))
conf.registerChannelValue(ChanTracker, 'repeatMode',
registry.String('q',"""mode used by the bot when repeat detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'repeatDuration',
registry.PositiveInteger(180,"""punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'repeatComment',
registry.String('repeat detected',"""comment added on mode changes database, empty for no comment"""))
# mass repeat detection
conf.registerChannelValue(ChanTracker, 'massRepeatChars',
registry.PositiveInteger(40,"""number of chars needed to enter massRepeat detection"""))
conf.registerChannelValue(ChanTracker, 'massRepeatPermit',
registry.Integer(-1,"""Number of repeated text allowed, -1 to disable, tracks message repetition from various sources on the given channel"""))
conf.registerChannelValue(ChanTracker, 'massRepeatLife',
registry.PositiveInteger(12,"""Duration of messages's life in massRepeat counter, in seconds"""))
conf.registerChannelValue(ChanTracker, 'massRepeatPercent',
registry.Probability(0.85,"""percentage similarity between previous and current message to trigger a repeat count"""))
conf.registerChannelValue(ChanTracker, 'massRepeatMode',
registry.String('b',"""mode used by the bot when repeat detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'massRepeatDuration',
registry.PositiveInteger(1800,"""punition in seconds"""))
conf.registerChannelValue(ChanTracker, 'massRepeatComment',
registry.String('mass repeat detected',"""comment added on mode changes database, empty for no comment"""))
conf.registerChannelValue(ChanTracker, 'massRepeatPatternLife',
registry.PositiveInteger(300,"""duration of pattern life"""))
conf.registerChannelValue(ChanTracker, 'massRepeatPatternLength',
registry.Integer(-1,"""if -1, it uses the default system to compare strings, otherwise, it try to find the longest common message, and use it as a regexp pattern,
if found string < length setted, it uses the default string compare"""))
conf.registerChannelValue(ChanTracker, 'repeatPermit',
registry.Integer(-1, """number of triggers allowed during repeatLife, -1 to disable"""))
conf.registerChannelValue(ChanTracker, 'repeatLife',
registry.PositiveInteger(12, """duration in seconds before triggers are removed from counter"""))
conf.registerChannelValue(ChanTracker, 'repeatMode',
registry.String('q', """mode used by the bot when repeat detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'repeatDuration',
registry.PositiveInteger(180, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'repeatComment',
registry.String('repeat detected', """comment added to tracking database, empty for no comment"""))
# pattern repeat detection
conf.registerChannelValue(ChanTracker, 'repeatMinimum',
registry.PositiveInteger(8, """minimum length of pattern candidates to detect repetitions"""))
conf.registerChannelValue(ChanTracker, 'repeatPercent',
registry.Probability(0.85, """percent of similarity required between pattern candidates"""))
conf.registerChannelValue(ChanTracker, 'repeatCount',
registry.PositiveInteger(5, """number of occurences of pattern candidates"""))
conf.registerChannelValue(ChanTracker, 'repeatPatternMinimum',
registry.Integer(-1, """minimum length to create automated pattern; if found, triggers same punishment as repeatMode/repeatDuration, -1 to disable"""))
conf.registerChannelValue(ChanTracker, 'repeatPatternLife',
registry.PositiveInteger(300, """duration in seconds before automated patterns are removed"""))
conf.registerChannelValue(ChanTracker, 'shareComputedPatternID',
registry.Integer(-1, """share computed patterns across channels using the same ID, -1 to disable"""))
# YES IT'S ANNOYING
conf.registerChannelValue(ChanTracker, 'capPermit',
registry.Integer(-1,"""Number of UPPERCASE messages allowed, -1 to disable. see capPercent for definition of an UPPERCASE message"""))
registry.Integer(-1, """number of UPPERCASE messages allowed, -1 to disable; see capPercent for definition of an UPPERCASE message"""))
conf.registerChannelValue(ChanTracker, 'capLife',
registry.PositiveInteger(30,"""Duration in seconds before messages are removed from count"""))
registry.PositiveInteger(30, """duration in seconds before messages are removed from counter"""))
conf.registerChannelValue(ChanTracker, 'capPercent',
registry.Probability(0.75,"""percentage of uppercase chars in a message to trigger a cap count"""))
registry.Probability(0.75, """percentage of uppercase chars in a message to trigger a cap count"""))
conf.registerChannelValue(ChanTracker, 'capMode',
registry.String('q',"""mode used by the bot when cap is triggered"""))
registry.String('q', """mode used by the bot when cap detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'capDuration',
registry.PositiveInteger(180,"""punition in seconds"""))
registry.PositiveInteger(180, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'capComment',
registry.String('capslock detected',"""comment added on mode changes database, empty for no comment"""))
registry.String('capslock detected', """comment added to tracking database, empty for no comment"""))
# hilights
# hilight
conf.registerChannelValue(ChanTracker, 'hilightPermit',
registry.Integer(-1,"""Number of nick allowed per message, -1 to disable, note : it doesn't care if it's the same nick"""))
registry.Integer(-1, """number of nicks allowed per message, -1 to disable; note it doesn't care if it's the same nick"""))
conf.registerChannelValue(ChanTracker, 'hilightMode',
registry.String('q',"""mode used by the bot when hilight is triggered"""))
registry.String('q', """mode used by the bot when hilight detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'hilightDuration',
registry.PositiveInteger(180,"""punishment duration in seconds"""))
registry.PositiveInteger(180, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'hilightComment',
registry.String('hilight detected',"""comment added on mode changes database, empty for no comment"""))
registry.String('hilight detected', """comment added to tracking database, empty for no comment"""))
# notices
# channel's notices
conf.registerChannelValue(ChanTracker, 'noticePermit',
registry.Integer(-1,"""Number of messages allowed, -1 to disable, advice 0"""))
registry.Integer(-1, """number of messages allowed, -1 to disable, advice 0"""))
conf.registerChannelValue(ChanTracker, 'noticeLife',
registry.PositiveInteger(3,"""Duration in seconds before messages are removed from count"""))
registry.PositiveInteger(3, """duration in seconds before messages are removed from counter"""))
conf.registerChannelValue(ChanTracker, 'noticeMode',
registry.String('q',"""mode used by the bot when notice is triggered"""))
registry.String('q', """mode used by the bot when notice detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'noticeDuration',
registry.PositiveInteger(300,"""punishment duration in seconds"""))
registry.PositiveInteger(300, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'noticeComment',
registry.String('notice detected',"""comment added on mode changes database, empty for no comment"""))
registry.String('notice detected', """comment added to tracking database, empty for no comment"""))
# ctcps
# channel ctcps
conf.registerChannelValue(ChanTracker, 'ctcpPermit',
registry.Integer(-1,"""Number of messages allowed, -1 to disable"""))
registry.Integer(-1, """number of messages allowed, -1 to disable"""))
conf.registerChannelValue(ChanTracker, 'ctcpLife',
registry.PositiveInteger(3,"""Duration in seconds before messages are removed from count"""))
registry.PositiveInteger(3, """duration in seconds before messages are removed from counter"""))
conf.registerChannelValue(ChanTracker, 'ctcpMode',
registry.String('b',"""mode used by the bot when ctcp detection is triggered"""))
registry.String('b', """mode used by the bot when ctcp detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'ctcpDuration',
registry.PositiveInteger(1800,"""punishment duration in seconds"""))
registry.PositiveInteger(1800, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'ctcpComment',
registry.String('ctcp detected',"""comment added on mode changes database, empty for no comment"""))
registry.String('ctcp detected', """comment added to tracking database, empty for no comment"""))
# join/part flood
# channel join/part flood
conf.registerChannelValue(ChanTracker, 'cyclePermit',
registry.Integer(-1,"""Number of cycles allowed, -1 to disable, count part and quit"""))
registry.Integer(-1, """number of cycles allowed, -1 to disable; counts parts and quits"""))
conf.registerChannelValue(ChanTracker, 'cycleLife',
registry.PositiveInteger(180,"""Duration in seconds before cycles are removed from count"""))
registry.PositiveInteger(180, """duration in seconds before cycles are removed from counter"""))
conf.registerChannelValue(ChanTracker, 'cycleMode',
registry.String('b',"""mode used by the bot when cycle detection is triggered"""))
registry.String('b', """mode used by the bot when cycle detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'cycleDuration',
registry.PositiveInteger(1800,"""punishment duration in seconds"""))
registry.PositiveInteger(1800, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'cycleComment',
registry.String('cycle detected',"""comment added on mode changes database, empty for no comment"""))
registry.String('cycle detected', """comment added to tracking database, empty for no comment"""))
conf.registerChannelValue(ChanTracker, 'cycleForward',
registry.String('',"""if your ircd supports that, you can forward the user to a specific channel"""))
registry.String('', """if your ircd supports that, you can forward the user to a specific channel"""))
# massjoin from a host
# channel massJoin from an host
conf.registerChannelValue(ChanTracker, 'massJoinPermit',
registry.Integer(-1,"""Number of joins allowed, -1 to disable, note, it could mixup a bit with cycle detection"""))
registry.Integer(-1, """number of joins allowed, -1 to disable, note, it could mixup a bit with cycle detection"""))
conf.registerChannelValue(ChanTracker, 'massJoinLife',
registry.PositiveInteger(60,"""Duration in seconds before messages are removed from count"""))
registry.PositiveInteger(60, """duration in seconds before messages are removed from count"""))
conf.registerChannelValue(ChanTracker, 'massJoinMode',
registry.String('+rq-z $~a',"""mode used by the bot when massjoin is triggered"""))
registry.String('+rq-z $~a', """mode used by the bot when massjoin detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'massJoinDuration',
registry.PositiveInteger(300,"""punishment duration in seconds"""))
registry.PositiveInteger(300, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'massJoinUnMode',
registry.String('-rq+z $~a',"""mode used by the bot when massJoinDuration is finished"""))
registry.String('-rq+z $~a', """mode used by the bot when massJoinDuration is finished"""))
# nick changes flood
conf.registerChannelValue(ChanTracker, 'nickPermit',
registry.Integer(-1,"""Number of nick changes allowed, -1 to disable"""))
registry.Integer(-1, """number of nick changes allowed, -1 to disable"""))
conf.registerChannelValue(ChanTracker, 'nickLife',
registry.Integer(300,"""Duration in seconds before nick changes are removed from count"""))
registry.PositiveInteger(300, """duration in seconds before nick changes are removed from count"""))
conf.registerChannelValue(ChanTracker, 'nickMode',
registry.String('q',"""mode used by the bot when nick is triggered"""))
registry.String('q', """mode used by the bot when nick detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'nickDuration',
registry.PositiveInteger(300,"""punishment duration in seconds"""))
registry.PositiveInteger(300, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'nickComment',
registry.String('nick changes flood detected',"""comment added on mode changes database, empty for no comment"""))
registry.String('nick changes flood detected', """comment added to tracking database, empty for no comment"""))
# if you enable this, each time someone triggers other protection, it increases this counter
# if you enable this, each time someone trigger other protection that will increase this queue
conf.registerChannelValue(ChanTracker, 'badPermit',
registry.Integer(-1,"""Number of bad action allowed, -1 to disable, each time bot had to acts on a user, it increase this item"""))
registry.Integer(-1, """number of actions allowed, -1 to disable; each time bot had to act on a user, it increases this counter"""))
conf.registerChannelValue(ChanTracker, 'badLife',
registry.Integer(600,"""Duration in seconds before actions are removed from count"""))
registry.PositiveInteger(600, """duration in seconds before actions are removed from counter"""))
conf.registerChannelValue(ChanTracker, 'badMode',
registry.String('b',"""mode used by the bot when bad is triggered"""))
registry.String('b', """mode used by the bot when bad detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'badDuration',
registry.PositiveInteger(86400,"""punishment duration in seconds"""))
registry.PositiveInteger(86400, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'badComment',
registry.String('bad detected',"""comment added on mode changes database, empty for no comment"""))
registry.String('bad detected', """comment added to tracking database, empty for no comment"""))
# if you enable this, each time someone triggers bad detection, it increases this counter
# if you enable this, each time someone trigger bad in a channel that will increase this queue
conf.registerChannelValue(ChanTracker, 'attackPermit',
registry.Integer(-1,"""Number of bad action allowed, -1 to disable, each time bot flags user as bad, it increase this item"""))
registry.Integer(-1, """number of bad actions allowed, -1 to disable; each time bot flags a user as bad, it increases this counter"""))
conf.registerChannelValue(ChanTracker, 'attackLife',
registry.Integer(600,"""Duration in seconds before actions are removed from count"""))
registry.PositiveInteger(600, """duration in seconds before bad actions are removed from counter"""))
conf.registerChannelValue(ChanTracker, 'attackDuration',
registry.PositiveInteger(1800,"""punishment duration in seconds"""))
registry.PositiveInteger(1800, """punishment duration in seconds"""))
conf.registerChannelValue(ChanTracker, 'attackMode',
registry.String('+rq-z $~a',"""mode used by the bot when attack is triggered"""))
registry.String('+rq-z $~a', """mode used by the bot when attack detection is triggered"""))
conf.registerChannelValue(ChanTracker, 'attackUnMode',
registry.String('-rq+z $~a',"""mode used by the bot when attackDuration is finished"""))
registry.String('-rq+z $~a', """mode used by the bot when attackDuration is finished"""))
# netsplits
conf.registerChannelValue(ChanTracker, 'netsplitModes',
registry.String('',"""leave empty for no modes changes"""))
registry.String('', """leave empty for no modes changes"""))
conf.registerChannelValue(ChanTracker, 'netsplitUnmodes',
registry.String('',"""leave empty for no modes changes"""))
registry.String('', """leave empty for no modes changes"""))
conf.registerChannelValue(ChanTracker, 'netsplitDuration',
registry.PositiveInteger(600,"""duration of netsplit state when detected, it disables massJoin and cycle detection, and could set specific modes"""))
registry.PositiveInteger(600, """duration of netsplit state when detected,
it disables massjoin and cycle detection, and could set specific modes"""))

8573
plugin.py

File diff suppressed because it is too large Load Diff

614
server.py
View File

@ -1,13 +1,6 @@
import BaseHTTPServer
import os
import time
import base64
import re
import os, re, time, base64
import supybot.utils as utils
import sqlite3
import collections
import urllib
from StringIO import StringIO
import http.server, sqlite3
host = 'http://domain.tld'
port = 80
@ -16,70 +9,60 @@ webpath = '/bantracker'
username = 'username'
password = 'password'
filename = '/home/botaccount/data/networkname/ChanTracker.db'
channels = [] # empty to allows view of all channels recorded, otherwise restrict the views to channels
channels = [] # empty to allow view of all channels recorded, otherwise restrict the views to channels
# httpd server address
if not standalone:
servaddr = '127.0.0.1'
else:
servaddr = ''
# usage python server.py
auth = '%s:%s' % (username,password)
base64string = base64.b64encode(auth.encode('utf-8')).decode('utf-8')
base64string = base64.encodestring('%s:%s' % (username,password))[:-1]
def timeElapsed(elapsed, short=False, leadingZeroes=False, years=True,
weeks=True, days=True, hours=True, minutes=True, seconds=True):
"""Given <elapsed> seconds, returns a string with an English description of
how much time as passed. leadingZeroes determines whether 0 days, 0 hours,
etc. will be printed; the others determine what larger time periods should
be used.
"""
ret = []
def Format(s, i):
if i or leadingZeroes or ret:
if short:
ret.append('%s%s' % (i, s[0]))
else:
ret.append(format('%n', (i, s)))
elapsed = int(elapsed)
assert years or weeks or days or \
hours or minutes or seconds, 'One flag must be True'
if years:
(yrs, elapsed) = (elapsed // 31536000, elapsed % 31536000)
Format('year', yrs)
if weeks:
(wks, elapsed) = (elapsed // 604800, elapsed % 604800)
Format('week', wks)
if days:
(ds, elapsed) = (elapsed // 86400, elapsed % 86400)
Format('day', ds)
if hours:
(hrs, elapsed) = (elapsed // 3600, elapsed % 3600)
Format('hour', hrs)
if minutes or seconds:
(mins, secs) = (elapsed // 60, elapsed % 60)
if leadingZeroes or mins:
Format('minute', mins)
if seconds:
leadingZeroes = True
Format('second', secs)
if not ret:
raise ValueError, 'Time difference not great enough to be noted.'
if short:
return ' '.join(ret)
def weblink():
weblink = host
if standalone:
weblink += ':%s' % port
else:
return format('%L', ret)
weblink += webpath
weblink += '/?hash=%s' % base64string
return weblink
class MyHandler( BaseHTTPServer.BaseHTTPRequestHandler ):
server_version= "Ircd-Seven/1.1"
def htmlEscape(text):
return text.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('"','&quot;')
class BanTracker(http.server.BaseHTTPRequestHandler):
if not standalone:
def log_request(self, *args):
pass # disable logging
def do_GET( self ):
self.page( self.path )
def do_GET(self):
self.page(self.path)
def page(self, query):
def write(subtitle, body):
page = [
'<!DOCTYPE html>', '<html>', '<head>',
'<title>BanTracker%s</title>' % (' &raquo; %s' % subtitle if subtitle else ''),
'<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />',
'<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" />',
'</head>', '<body style="margin:0.5em; width:98%;" class="container">'
] + body + ['</body>', '</html>']
self.send_response(200)
self.send_header("Content-Type", "text/html")
full = '\n'.join(page)
print('HTML lines %s' % len(full))
self.send_header("Content-Length", len(full))
self.end_headers()
self.wfile.write(full.encode('utf-8'))
def page (self,query):
if standalone:
h = '%s:%s/' % (host,port)
else:
h = '%s/' % webpath
body = []
if not query:
return
if query.startswith('/?username='):
@ -93,287 +76,260 @@ class MyHandler( BaseHTTPServer.BaseHTTPRequestHandler ):
if aa[0] == 'password':
p = aa[1]
if u and p:
raw = base64.encodestring('%s:%s' % (u,p))[:-1]
if not raw == base64string:
auth = '%s:%s' % (u,p)
raw = base64.b64encode(auth.encode('utf-8')).decode('utf-8')
if raw != base64string:
query = ''
else:
query = '/?hash=%s' % base64string
if not query.startswith('/?hash='):
body.append('<html>\n<head>\n<title>ChanTracker</title>\n')
body.append('<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n')
body.append("</head>\n<body>\n")
body.append('<form action="%s">\n' % h)
body.append('<p>Username:<input name="username" /></p>\n')
body.append('<p>Password:<input name="password" type="password"/></p>\n')
body.append('<input type="submit" value="Login" />\n')
body.append("</form>\n")
body.append("</body>\n<html>\n")
self.send_response(200)
self.send_header("Content-type","text/html")
full = ''.join(body)
self.send_header("Content-length",str(len(full)))
self.end_headers()
self.wfile.write(full)
subtitle = ''
body = [
'<form action="%s">' % h,
'<p>Username: <input name="username" /></p>',
'<p>Password: <input name="password" type="password" /></p>',
'<button type="submit" class="btn btn-default">Login</button>',
'</form>'
]
write(subtitle, body)
return
if query.startswith('/?hash='):
a = query.split('&')[0]
a = a.replace('/?hash=','')
query = query.replace('%3D','=')
query = query.replace('/?hash=%s' % base64string,'/')
q = '?hash=%s' % base64string
query = urllib.unquote( query )
print query
body.append('<html style="text-align:center;font-size:1.2em;">\n<head>\n<title>BanTracker - %s</title>\n' % query)
body.append('<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />\n')
body.append('<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"></link>\n')
# body.append('<script src="http://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>\n')
body.append('</head>\n<body style="margin:0.5em;width:98%;margin-left:auto;margin-right:auto;text-align:left;" class="container">\n')
body.append('<div class="row"><div class="col-xs-6">\n')
body.append('<form action="%s" class="form">\n' % q)
body.append('<div class="input-group">')
body.append('<input type="hidden" name="hash" value="%s">' % base64string)
body.append('<input name="search" class="form-control" />\n')
body.append('<span class="input-group-btn"><button type="submit" class="btn btn-default">Search</button></span>\n')
body.append('</div></form></div></div>\n')
body.append('<div class="clearfix"></div>\n')
query = query.replace('%3D','=')
query = query.replace('/?hash=%s' % base64string,'')
query = query.lstrip('&')
q = '?hash=%s' % base64string
query = utils.web.urlunquote(query)
subtitle = ''
body = [
'<div class="row"><div class="col-xs-6" style="width:100%; max-width:600px;">',
'<form action="%s" class="form">' % q,
'<div class="input-group">',
'<input type="hidden" name="hash" value="%s">' % base64string,
'<input name="search" class="form-control" />',
'<span class="input-group-btn"><button type="submit" class="btn btn-default">Search</button></span>',
'</div></form></div></div>',
'<div class="clearfix"></div>'
]
if not query:
write(subtitle, body)
return
print(query)
subtitle = query
db = self._getbandb()
c = db.cursor()
if query:
ar = []
if query.startswith('/&id='):
search = query.split('/&id=')[1]
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=? ORDER BY id DESC""",(search,))
r = c.fetchall()
if len(r):
ban = r[0]
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
if not len(channels) or channel in channels:
body.append('<h3>#%s</h3>\n' % id)
body.append('<p>#%s by %s in %s : +%s : %s</p>\n' % (id,oper,channel,kind,mask))
body.append('<p>Begin at %s</p>\n' % time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at))))
was = float(begin_at) == float(end_at)
if was:
was = 'forever'
else:
was = timeElapsed(float(end_at) - float(begin_at))
body.append('<p>Original duration : %s</p>\n' % was)
if not removed_at:
if was != 'forever':
body.append('<p>%s</p>\n' % 'It will expire in %s' % timeElapsed(float(end_at) - time.time()))
else:
body.append('<p>%s</p>\n' % 'Removed after %s on %s by %s' % (timeElapsed(float(removed_at)-float(begin_at)),time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at))),removed_by))
c.execute("""SELECT full, log FROM nicks WHERE ban_id=?""",(id,))
r = c.fetchall()
if len(r):
users = r
body.append('<h3>Logs</h3>\n')
for u in users:
(full,log) = u
body.append('<p>for %s</p>\n' % full)
if log != '':
body.append('<ul>\n')
for line in log.split('\n'):
if line != '':
body.append('<li>%s</li>\n' % line)
body.append('</ul>\n')
c.execute("""SELECT oper, at, comment FROM comments WHERE ban_id=?""",(id,))
r = c.fetchall()
if len(r):
body.append('<h3>Comments</h3>\n')
body.append('<ul>\n')
comments = r
for com in comments:
(oper,at,comment) = com
s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(at)))
body.append('<li>%s by %s : %s</li>\n' % (s,oper,comment))
body.append('</ul>\n')
elif query.startswith('/&channel='):
search = '#'+query.split('/&channel=')[1]
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE channel=? ORDER BY id DESC""",(search,))
r = c.fetchall()
if len(r):
bans = r
for ban in bans:
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
ar.append([int(id),channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by])
elif query.startswith('/&removed_by='):
search = query.split('/&removed_by=')[1]
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE removed_by=? ORDER BY id DESC""",(search,))
r = c.fetchall()
if len(r):
bans = r
for ban in bans:
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
ar.append([int(id),channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by])
elif query.startswith('/&oper='):
search = query.split('/&oper=')[1]
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE oper=? ORDER BY id DESC""",(search,))
r = c.fetchall()
if len(r):
bans = r
for ban in bans:
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
ar.append([int(id),channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by])
elif query.startswith('/&mask='):
search = query.split('/&mask=')[1]
glob = '*%s*' % search
like = '%'+search+'%'
c.execute("""SELECT ban_id, full FROM nicks WHERE full GLOB ? OR full LIKE ? OR log GLOB ? OR log LIKE ? ORDER BY ban_id DESC""",(glob,like,glob,like))
L = []
a = {}
r = c.fetchall()
if len(r):
bans = r
d = {}
for ban in bans:
(id,full) = ban
if not id in d:
d[id] = id
for id in d:
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=? ORDER BY id DESC""",(int(id),))
r = c.fetchall()
if len(r):
bans = r
for ban in bans:
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
a[str(id)] = ban
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE mask GLOB ? OR mask LIKE ? ORDER BY id DESC""",(glob,like))
r = c.fetchall()
if len(r):
bans = r
for ban in bans:
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
a[str(id)] = ban
if len(a):
ar = []
for ban in a:
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = a[ban]
ar.append([int(id),channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by])
def sort_function (item):
return item[0]
ar.sort(key=sort_function)
ar.sort(reverse=True)
elif query.startswith('/&search='):
search = query.split('/&search=')[1]
search = search.replace('+','*')
print search
if search:
s = '*%s*' % search
qu = '%'+search+'%'
c.execute("""SELECT ban_id, full FROM nicks WHERE full GLOB ? OR full LIKE ? OR log GLOB ? OR log LIKE ? ORDER BY ban_id DESC""",(s,qu,s,qu))
L = []
a = {}
ar = []
if query.startswith('id='):
search = query.split('=')[1]
si = int(search)
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(si,))
r = c.fetchall()
if len(r):
ban = r[0]
(bid,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
if not channels or channel in channels:
body.extend([
'<h3>#%d</h3>' % bid,
'<p>#%d by <a href="%s%s&amp;%s">%s</a>' % (bid,h,q,utils.web.urlencode({'oper':oper}),oper),
'in <a href="%s%s&amp;channel=%s">%s</a>:' % (h,q,channel.split('#')[1],channel),
'+%s <a href="%s%s&amp;%s">%s</a></p>' % (kind,h,q,utils.web.urlencode({'mask':mask}),mask),
'<p>Begin at %s</p>' % time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at)))
])
was = float(begin_at) == float(end_at)
if was:
was = 'forever'
else:
was = utils.timeElapsed(float(end_at) - float(begin_at))
body.append('<p>Original duration: %s</p>' % was)
if not removed_at:
if was != 'forever':
remaining = float(end_at) - time.time()
if remaining >= 0:
body.append('<p>It will expire in %s</p>' % utils.timeElapsed(remaining))
else:
body.append('<p>It expired %s</p>' % utils.timeElapsed(remaining))
else:
body.extend(['<p>Removed after %s' % utils.timeElapsed(float(removed_at)-float(begin_at)),
'on %s' % time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at))),
'by <a href="%s%s&amp;%s">%s</a></p>' % (h,q,utils.web.urlencode({'removed_by':removed_by}),removed_by)])
c.execute("""SELECT full,log FROM nicks WHERE ban_id=?""",(bid,))
r = c.fetchall()
if len(r):
bans = r
d = {}
for ban in bans:
(id,full) = ban
if not id in d:
d[id] = id
for id in d:
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=? ORDER BY id DESC""",(int(id),))
body.append('<h3>Logs</h3>')
for (full,log) in r:
body.append('<p>for %s</p>' % full)
if log != '':
body.append('<ul>')
for line in log.split('\n'):
if line != '':
body.append('<li>%s</li>' % htmlEscape(line))
body.append('</ul>')
c.execute("""SELECT oper,at,comment FROM comments WHERE ban_id=?""",(bid,))
r = c.fetchall()
if len(r):
body.extend(['<h3>Comments</h3>', '<ul>'])
for (oper,at,com) in r:
s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(at)))
body.append('<li>%s by %s: %s</li>' % (s,oper,htmlEscape(com)))
body.append('</ul>')
c.close()
write(subtitle, body)
return
elif query.startswith('channel='):
search = '#'+query.split('=')[1]
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE channel=? ORDER BY id DESC""",(search,))
r = c.fetchall()
if len(r):
ar.extend(r)
elif query.startswith('removed_by='):
search = query.split('=')[1]
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE removed_by=? ORDER BY id DESC""",(search,))
r = c.fetchall()
if len(r):
ar.extend(r)
elif query.startswith('oper='):
search = query.split('=')[1]
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE oper=? ORDER BY id DESC""",(search,))
r = c.fetchall()
if len(r):
ar.extend(r)
elif query.startswith('mask='):
search = query.split('=')[1]
sg = '*%s*' % search
sl = '%%%s%%' % search
c.execute("""SELECT ban_id,full FROM nicks WHERE full GLOB ? OR full LIKE ? OR log GLOB ? OR log LIKE ? ORDER BY ban_id DESC""",(sg,sl,sg,sl))
r = c.fetchall()
L = []
a = {}
if len(r):
d = []
for (bid,full) in r:
if bid not in d:
d.append(bid)
for bid in d:
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(bid,))
r = c.fetchall()
if len(r):
for ban in r:
a[ban[0]] = ban
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE mask GLOB ? OR mask LIKE ? ORDER BY id DESC""",(sg,sl))
r = c.fetchall()
if len(r):
for ban in r:
a[ban[0]] = ban
if len(a):
ar = []
for ban in list(a.keys()):
ar.append(a[ban])
ar.sort(key=lambda x: x[0], reverse=True)
elif query.startswith('search='):
search = query.split('=')[1]
search = search.replace('+','*')
print(search)
if search:
if not re.match(r'^[0-9]+$', search):
sg = '*%s*' % search
sl = '%%%s%%' % search
si = None
c.execute("""SELECT ban_id,full FROM nicks WHERE full GLOB ? OR full LIKE ? OR log GLOB ? OR log LIKE ? ORDER BY ban_id DESC""",(sg,sl,sg,sl))
r = c.fetchall()
else:
si = int(search)
r = []
L = []
a = {}
if len(r):
d = []
for (bid,full) in r:
if bid not in d:
d.append(bid)
for bid in d:
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(bid,))
r = c.fetchall()
if len(r):
for ban in r:
a[ban[0]] = ban
if not si:
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE mask GLOB ? OR mask LIKE ? OR channel GLOB ? OR channel LIKE ? OR oper GLOB ? OR oper LIKE ? ORDER BY id DESC""",(sg,sl,sg,sl,sg,sl))
else:
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(si,))
r = c.fetchall()
if len(r):
for ban in r:
a[ban[0]] = ban
if not si:
c.execute("""SELECT ban_id, comment FROM comments WHERE comment GLOB ? OR comment LIKE ? ORDER BY ban_id DESC""",(sg,sl))
r = c.fetchall()
else:
r = []
if len(r):
d = []
for (bid,full) in r:
d.append(bid)
for bid in d:
if bid not in a:
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=?""",(bid,))
r = c.fetchall()
if len(r):
bans = r
for ban in bans:
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
a[id] = ban
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE mask GLOB ? OR mask LIKE ? OR channel GLOB ? OR channel LIKE ? OR oper GLOB ? OR oper LIKE ? ORDER BY id DESC""",(s,qu,s,qu,s,qu))
r = c.fetchall()
if len(r):
bans = r
for ban in bans:
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
a[id] = ban
c.execute("""SELECT ban_id, comment FROM comments WHERE comment GLOB ? OR comment LIKE ? ORDER BY ban_id DESC""",(s,qu))
r = c.fetchall()
d = {}
if len(r):
bans = r
for ban in bans:
(id,full) = ban
d[id] = id
for id in d:
if not id in a:
c.execute("""SELECT id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by FROM bans WHERE id=? ORDER BY id DESC LIMIT 1""",(int(id),))
r = c.fetchall()
if len(r):
bans = r
for ban in bans:
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
a[id] = ban
if len(a):
ar = []
for ban in a:
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = a[ban]
ar.append([int(id),channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by])
def sort_function (item):
return item[0]
ar.sort(key=sort_function)
ar.sort(reverse=True)
else:
body.append('<p>nothing found</p>\n')
if len(ar):
print 'found %s results' % len(ar)
i = 0
body.append('<h3>results <small>%s</small></h3>' % search)
body.append('<div class="row"><div class="col-xs-12"><table class="table table-bordered sortable">\n')
body.append('<thead><tr><th>ID</th><th>Channel</th><th>Operator</th><th>Kind</th><th>Target</th><th>Begin date</th><th>End date</th><th>Removed date</th><th>Removed by</th></tr></thead>\n')
body.append('<tbody>\n')
while i < len(ar):
(id,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ar[i]
if not len(channels) or channel in channels:
body.append('<tr>\n')
body.append('<td><a href="%s%s&id=%s">%s</a></td>\n' % (h,q,id,id))
body.append('<td><a href="%s%s&channel=%s">%s</a></td>\n' % (h,q,channel.split('#')[1],channel))
body.append('<td><a href="%s%s&%s">%s</a></td>\n' % (h,q,urllib.urlencode({'oper':oper}),oper))
body.append('<td>+%s</td>\n' % kind)
body.append('<td><a href="%s%s&%s">%s</a></td>\n' % (h,q,urllib.urlencode({'mask':mask}),mask))
s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at)))
body.append('<td>%s</td>\n' % s)
if end_at and end_at != begin_at:
s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(end_at)))
body.append( '<td>%s</td>\n' % s)
else:
body.append( '<td></td>')
if removed_at:
s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at)))
body.append( '<td>%s</td>' % s)
else:
body.append( '<td></td>\n' )
if removed_by:
body.append( '<td><a href="%s%s&%s">%s</a></td>\n' % (h,q,urllib.urlencode({'removed_by':removed_by}),removed_by))
else:
body.append( '<td></td>\n')
for ban in r:
a[ban[0]] = ban
if len(a):
ar = []
for ban in list(a.keys()):
ar.append(a[ban])
ar.sort(key=lambda x: x[0], reverse=True)
if len(ar):
print('Found %s results' % len(ar))
body.extend([
'<h3>Results <small>%s</small></h3>' % search,
'<div class="row"><div class="col-xs-12"><table class="table table-bordered">',
'<thead><tr><th>ID</th><th>Channel</th><th>Operator</th><th>Type</th><th>Mask</th><th>Begin date</th><th>End date</th><th>Removed</th><th>Removed by</th></tr></thead>',
'<tbody>'
])
for ban in ar:
(bid,channel,oper,kind,mask,begin_at,end_at,removed_at,removed_by) = ban
if not channels or channel in channels:
s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(begin_at)))
body.extend([
'<tr>',
'<td><a href="%s%s&amp;id=%d">%d</a></td>' % (h,q,bid,bid),
'<td><a href="%s%s&amp;channel=%s">%s</a></td>' % (h,q,channel.split('#')[1],channel),
'<td><a href="%s%s&amp;%s">%s</a></td>' % (h,q,utils.web.urlencode({'oper':oper}),oper),
'<td>+%s</td>' % kind,
'<td><a href="%s%s&amp;%s">%s</a></td>' % (h,q,utils.web.urlencode({'mask':mask}),mask),
'<td>%s</td>' % s
])
if end_at and end_at != begin_at:
s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(end_at)))
body.append('<td>%s</td>' % s)
else:
body.append('<td></td>')
if removed_at:
s = time.strftime('%Y-%m-%d %H:%M:%S GMT',time.gmtime(float(removed_at)))
body.append('<td>%s</td>' % s)
else:
body.append('<td></td>')
if removed_by:
body.append('<td><a href="%s%s&amp;%s">%s</a></td>' % (h,q,utils.web.urlencode({'removed_by':removed_by}),removed_by))
else:
body.append('<td></td>')
# affected = ''
# try:
# c.execute("""SELECT full, log FROM nicks WHERE ban_id=?""",(bid,))
# affected = len(c.fetchall())
# except:
# affected = ''
# try:
# c.execute("""SELECT full, log FROM nicks WHERE ban_id=?""",(id,))
# affected = len(c.fetchall())
# except:
# affected = ''
# body.append( '<td>%s</td>\n' % affected)
body.append( '</tr>\n')
i = i+1
body.append('</tbody>\n')
body.append('</table></div>\n')
body.append("</body></html>")
self.send_response(200)
self.send_header("Content-type","text/html")
full = ''.join(body)
print 'html lines %s' % len(full)
self.send_header("Content-length",len(full))
self.end_headers()
self.wfile.write(full)
# body.append('<td>%s</td>' % affected)
body.append('</tr>')
body.extend(['</tbody>', '</table></div>'])
else:
body.append('<p>Nothing found</p>')
c.close()
write(subtitle, body)
def _getbandb (self):
def _getbandb(self):
if os.path.exists(filename):
db = sqlite3.connect(filename,timeout=10)
db.text_factory = str
return db
db = sqlite3.connect(filename)
db.text_factory = str
c = db.cursor()
c.execute("""CREATE TABLE bans (
id INTEGER PRIMARY KEY,
@ -387,24 +343,24 @@ class MyHandler( BaseHTTPServer.BaseHTTPRequestHandler ):
removed_by VARCHAR(1000)
)""")
c.execute("""CREATE TABLE nicks (
ban_id INTEGER,
ban_id INTEGER,
ban VARCHAR(1000) NOT NULL,
full VARCHAR(1000) NOT NULL,
log TEXT NOT NULL
log TEXT NOT NULL
)""")
c.execute("""CREATE TABLE comments (
ban_id INTEGER,
oper VARCHAR(1000) NOT NULL,
oper VARCHAR(1000) NOT NULL,
at TIMESTAMP NOT NULL,
comment TEXT NOT NULL
)""")
db.commit()
return db
def httpd(handler_class=MyHandler, server_address = ('', port), ):
srvr = BaseHTTPServer.HTTPServer(server_address, handler_class)
def httpd(handler_class=BanTracker, server_address=(servaddr, port)):
srvr = http.server.HTTPServer(server_address, handler_class)
srvr.serve_forever()
if __name__ == "__main__":
httpd( )
httpd()

13
setup.py Normal file
View File

@ -0,0 +1,13 @@
import os
import sys
from supybot.setup import plugin_setup
# Workaround pip changing the name of the root directory
(parent, dir_) = os.path.split(os.path.dirname(__file__))
sys.path.insert(0, parent)
sys.modules["ChanTracker"] = __import__(dir_)
plugin_setup(
'ChanTracker',
)

View File

@ -25,7 +25,6 @@
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
from supybot.test import *