tmb_mod package

Submodules

tmb_mod.antiflood module

Anti-flood module

If enabled, we keep track of messages sent by all users in all moderated channels. If a user sends more than the configured number of messages within the configured time period, they are considered to be flooding and we take some action(s) against them.

The Action class lists all possible actions to take. For example, quiet_host will mute the user’s host in the channel they flooded.

See the ANTIFLOOD_* options in config for our configuration options.

class tmb_mod.antiflood.Action(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: Enum

Actions we can take when a user floods.

quiet_host = 'quiet_host'

tell chanserv to +q *!*@example.com

class tmb_mod.antiflood.AntiFloodModule

Bases: Module

See the module-level documentation

NAME = 'antiflood'

Required: The name of this module, used for finding config options

max_msgs()

How many messages a user may send “recently” before they are considered to be flooding

privmsg_cb(user, receiver, message, is_opmod)

Main tormodbot code calls into this when we’re enabled and the given tmb_util.userstr.UserStr has sent message (str) to recevier (str). The receiver can be a channel (“#foo”) or a nick (“foo”).

recent

Storage for recent message timestamps. Items are a three-tuple:

(ts, nick, chan)

If the above changes, then look for usage of this and update it.

recent_actions

A FIFO list of Actions we’ve recently made. This is used to not repeat ourselves in case the flooder is able to send multiple messages after crossing the “is flooding” threshold before we’ve stopped them.

Append Actions that you are taking to the right and cleanup old actions from the left.

The items in this queue are a tuple:

(timestamp, Action, UserStr, '#channel')

If this fact changes, then AntiFloodModule._action_done_recently() needs to be updated

recent_secs()

The duration in which a user may send max_msgs, and if they send more, they are flooding.

tmb_mod.antiflood.RECENT_ACTION_SECS = 300

The RECENT_ACTION_SECS cleanup function will forgot Actions #: older than this number of seconds.

tmb_mod.antiflood.TEMP_QUIET_DAYS = 0.010416666666666666

The duration of a quiet. 0.25/24 is 15 minutes

tmb_mod.autovoice module

Auto-voice module

If enabled, the autovoice module can:

  • auto +v users with a matching nick!user@host string

  • auto +v users who have registered at least X seconds ago with a matching nick!user@host string

See the AUTOVOICE_* options in config for our configuration options.

See Limitations, especially regarding cloaks.

class tmb_mod.autovoice.AutoVoiceModule

Bases: Module

See the module-level documentation

NAME = 'autovoice'

Required: The name of this module, used for finding config options

join_cb(user, chan)

Main tormodbot code calls into this when we’re enabled and the given tmb_util.userstr.UserStr has joined the given channel

nickserv_time_reg_q

Queue of (UserStr, chan) we want to ask nickserv about to see if they registered, and if so, when they did so

notice_cb(sender, receiver, message)

Main tormodbot code calls into this when we’re enabled and have received a notice message

tmb_mod.badwords module

Bad words module

If enabled, look for people sending a message that matches a “bad word,” and if found, take action against them.

The Action class lists all possible actions to take. For example, quiet_nick will mute the user’s nick in the channel they flooded.

See the BADWORDS_* options in config for our configuration options.

class tmb_mod.badwords.Action(value, names=None, *, module=None, qualname=None, type=None, start=1, boundary=None)

Bases: Enum

Actions we can take when a user says a bad word.

plusr_chan = 'plusr_chan'

set the channel to +R

quiet_nick = 'quiet_nick'

tell chanserv to +q pastly!*@*

class tmb_mod.badwords.BadWordsModule

Bases: Module

See the module-level documentation

NAME = 'badwords'

Required: The name of this module, used for finding config options

privmsg_cb(user, receiver, message, is_opmod)

Main tormodbot code calls into this when we’re enabled and the given tmb_util.userstr.UserStr has sent message (str) to recevier (str). The receiver can be a channel (“#foo”) or a nick (“foo”).

tmb_mod.botabuse module

Anti-bot abuse module

If enabled, look for bots spamming a specific channel and temporarily mute them.

This is a lot like the anti-flood module, except we undo our mutes and are specifically meant for mitigating bots’ nicks from spamming.

class tmb_mod.botabuse.BotAbuseModule

Bases: Module

See the module-level documentation

NAME = 'botabuse'

Required: The name of this module, used for finding config options

actions

Storage for recording actions we’ve taken so that we don’t remake them and end up spamming a channel ourselves. Items are a the same as in self.recent, and the same places need to be updated if this changes.

actions_secs

How long we remember an action that we took.

handle_message(nick, chan)
mute_secs

How long to mute, in seconds

notice_cb(sender, receiver, message)

Called whenever we are enabled and see a NOTICE.

Note

Bots aren’t supposed to take action based on notices. It’s in the standards! Most likely you don’t want to overwrite this.

Note

Globally-ignored users do not trigger this.

Parameters:
  • sender (str) – Who sent the NOTICE. Not necessarily a string that can be turned into a :class:`UserStr`. Servers send NOTICEs. Expect something like “dacia.oftc.net” or “nick!user@host”. Only the latter can be turned into a UserStr.

  • receiver (str) – The channel name in which the NOTICE was seen, or our nick.

  • messge (str) – The message sent, without leading or trailing whitespace.

privmsg_cb(user, receiver, message, is_opmod)

Called whenever we are enabled and see a PRIVMSG.

You want to overwrite this function if you care about messages in moderated channels and/or PMs directly to us.

Note

Globally-ignored users do not trigger this.

Parameters:
  • user (UserStr) – Who PRIVMSGed.

  • dest (str) – The channel name (e.g. “#foo”) in which the message was seen, or our nick (if a literal PM to us).

  • message (str) – The message sent, without leading or trailing whitespace.

  • is_opmod (bool) – Whether or not this message is a statusmsg targeted to @#channel as opposed to #channel as usual. OFTC’s +z channel mode (hybrid, not solanum) sends messages that would otherwise be blocked to chanops using this method, and when it does, it calls it opmod. If this is set on this message, you must be a chanop in the channel (congratulations!) and you and your fellow chanops are the only ones that saw the message.

recent

Storage for recent bot messages. Items are three-tuples:

(ts, nick, chan)

If the above changes, then _clear_old and handle_message needs to be updated.

recent_max

How many messages a bot chan send in a channel per recent_secs. Hitting this causes us to mute the bot

recent_secs

How long ago a message is considered recent

tmb_mod.faq module

FAQ module

If enabled, we can read a library of FAQ responses from plain-text files on disk, both bundled with tormodbot and ones the operator wrote themself.

Usage

In public (in a moderated channel)

A FAQ query can take one of two forms:

# public usage
!faq
!faq <keyword>
  • A simple !faq in public will case us to list, in public, all keywords we know about in that channel.

  • !faq foo, where foo is a keyword known in that channel, will case us to respond with the FAQ response for foo. If we do not have a response, we respond saying so and include a link to the configured source code repository.

If more than one argument is provided, we behave as if we received just !faq.

In private (via a private message)

A FAQ query can take one of four forms:

# private usage
!faq
!faq <#chan>
!faq <keyword>
!faq <#chan> <keyword>
  • A simple !faq will case us to behave as if we received !faq all about, i.e. we respond with text regarding this bot.

  • !faq #bar will case us to list all keywords we know about in #bar.

  • !faq foo will case us to behave as if we received !faq all foo.

  • !faq #bar foo will case us to respond with the FAQ response for foo in #bar.

In all the above cases where a channel can be specified, instead you can provide all to search the globally known FAQ responses only, as opposed to responses known to a specific channel and those known globally.

If more than two arguments are provided, we behave as if we received just !faq.

If we cannot find a FAQ response, we respond saying so and include a link to the configured source code repository.

Note

If just one argument is provided and it is not all or a moderated channel, it is treated as a keyword. Assume #baz is not a moderated channel. That means while !faq #baz looks like we should respond with the list of known keywords in #baz, since we don’t have any (it’s not moderated), we treat it as a keyword and act as if we received !faq all #baz.

FAQ response file format

FAQ response files can be multiple lines; however, blank lines are ignored and leading/trailing whitespace is stripped. Comment lines – those whose first non-whitespace character is ‘#’ – are also ignored. There is no such thing as an end-of-line comment.

This is the first line of a FAQ response.

This line is printed right after the first, with no blank line in between.
# This is a comment, thus isn't printed.
This entire line is printed # because this isn't a comment.

Regardless of the length of the lines in the input file, paragraphs are wrapped to 400-character-length lines. Paragraphs are separated by a blank line.

This is the first paragraph.
This is the second sentence
of the first paragraph, and these
five lines appear as a single
IRC message.

This is the second paragraph and
would start a second IRC message.

FAQ response search order

FAQ responses can be specific to a moderated channel, or they can be general for all channels. Assuming the default WeeChat data directory, when searching for the response to FAQ keyword bar in channel #foo, we search in the following places in order and select the first FAQ response found:

~/.weechat/tmb_data/faq/#foo/bar.txt
~/.weechat/tmb_data/faq/all/bar.txt
~/.weechat/python/faq/#foo/bar.txt
~/.weechat/python/faq/all/bar.txt

Put your FAQ responses in ``~/.weechat/tmb_data/faq``. If you believe your FAQ response should be bundled with the code, make a pull request, get it merged, and then you can pull the latest code and have tne FAQ response in ~/.weechat/python/faq.

The motivation for this order is to allow the operator to have a general keyword with the same response in all channels, but override that response in specific channels. Additionally, any FAQ responses that come bundled with this code will be overridden by any operator-created FAQ response that uses the same keyword.

Keywords

The keyword is not case-sensitive, has no spaces, and otherwise lacks any characters that would constitute an invalid filename.

Anti-spam

To limit the extent to which we can be used for a spam tool, we rate limits ourself in each moderated channel. See the FAQ_* options in config for the options how many responses we can burst in a channel, as well as the steady-state rate at which we will send responses in a channel. Additionally, we won’t give the same FAQ response in a channel if we have done so recently.

class tmb_mod.faq.FAQModule

Bases: Module

See the module-level documentation

NAME = 'faq'

Required: The name of this module, used for finding config options

initialize()

Called whenever we are (re)starting

privmsg_cb(user, receiver, message, is_opmod)

Main tormodbot code calls into this when we’re enabled and the given tmb_util.userstr.UserStr has sent message (str) to recevier (str). The receiver can be a channel (“#foo”) or a nick (“foo”).

recent_faqs

A FIFO list of responses we’ve recently made. This is used to not repeat ourselves in case we are asked to paste the same FAQ rapidly.

Append responses that you are taking to the right and cleanup old ones from the left.

The items in this queue are a tuple:

(timestamp, destination, channel, keyword)

If this fact changes, then _faq_done_recently() needs to be updated

tb_func

Function to call every time we send a message to take away a token from that channel’s bucket. The function takes the channels’s state as an argument and returns (wait_time, new_state). wait_time is the amount of time we need to wait until we would have a non-zero number of tokens left (so if we currently have tokens for this channel, wait_time is 0), and new_state is the channels’s updated state that should be passed in next time.

token_buckets

Store per-chan token buckets so we prevent ourselves from flooding when used as a toy

tmb_mod.faq.UNKNOWN_FAQ_RESP = 'I do not know about "{1}" in {0}. If I should, please open a ticket at {2}. Also try sending me a private message with "!faq {0}".'

Reponse template to give when we do not know the answer. The arguments, in order: - The channel - The unknown keyword - The URL at which to report bugs

tmb_mod.hello module

Module that notices new nicks saying something simple like “hello?” upon joining and responds with a message.

The module keeps track of how many times they’ve seen a nick join each channel and how many times a nick has ever sent a message in each channel. If their number of joins and messages are sufficiently low, then we send them an automated reply such as “this is a support channel, please ask your question.”

If enabled, this module keeps track of joins and messages in all moderated channels; however, it will not send an automated response in channels that do not have a response configured.

The responses for channels are stored in weechat’s configuration with the prefix hello_reponse_* where * is the channel name. To configure a response for channel #foo, use weechat’s /set command:

/set plugins.var.python.tormodbot.hello_response_#foo This is a support channel. Please ask your question.

See the HELLO_* options in config for our configuration options.

class tmb_mod.hello.HelloModule

Bases: Module

See the module-level documentation

NAME = 'hello'

Required: The name of this module, used for finding config options

hello_words()
initialize()

Called whenever we are (re)starting

interval()
join_cb(user, chan)

Main tormodbot code calls into this when we’re enabled and the given tmb_util.userstr.UserStr has joined the given channel

last_response

Keep track of last time we sent a manual autoreponse in each channel so we don’t spam.

msg_max_len()
new_joins()

Return maximum number of joins in a channel we can see from a nick and still consider the nick a new user

new_msgs()

Return maximum number of messages in a channel we can see from a nick and still consider the nick a new user

privmsg_cb(user, receiver, message, is_opmod)

Main tormodbot code calls into this when we’re enabled and the given tmb_util.userstr.UserStr has sent message (str) to recevier (str). The receiver can be a channel (“#foo”) or a nick (“foo”).

response_for_chan(chan)

Returns the response we have for chan, or None if no configured response

tmb_mod.joinspam module

Join spam module

If enabled, look for people (possibly unintentionally) join/part or join/quit spamming. If found, take action against them.

See the JOINSPAM_* options in config for our configuration options.

class tmb_mod.joinspam.JoinSpamModule

Bases: Module

See the module-level documentation

NAME = 'joinspam'

Required: The name of this module, used for finding config options

join_cb(user, receiver)

Called whenever we are enabled and see a JOIN.

You want to overwrite this function if you care about JOINs.

Parameters:
  • user (UserStr) – Who JOINed.

  • chan (str) – The channel that was JOINed.

max_joins()
recent

Storage for recent joins. Items are three-tuples:

(ts, nick, chan)

If the above changes, then look for usage of this and update it.

recent_mins()
tmb_mod.joinspam.TEMP_BAN_DAYS = 0.16666666666666666

The duration of a temporary ban

tmb_mod.joinspam.TEMP_BAN_REASON = 'Your client rejoined >={} times in {} minutes. Tell pastly when fixed or wait 4 hours for the ban to expire.'

The reason to log for a temporary ban

Module contents

class tmb_mod.Module

Bases: object

TorModBot modules – essentially major “self contained” features – have this class as a parent. We provide a uniform interface for tormodbot.py to use and default implementations for all of the functions exposed to it.

There are required static class values values that must be set.

NAME = ''

Required: The name of this module, used for finding config options

enabled()

Returns True if this module is configured to be enabled, otherwise False.

It is very unlikely that you should overwrite this member function with your own.

initialize()

Called whenever the plugin is restarting or reloading and this module is enabled.

It’s possible you need to overwrite this member function with your own if your module keeps state.

join_cb(user, chan)

Called whenever we are enabled and see a JOIN.

You want to overwrite this function if you care about JOINs.

Parameters:
  • user (UserStr) – Who JOINed.

  • chan (str) – The channel that was JOINed.

notice_cb(sender, receiver, message)

Called whenever we are enabled and see a NOTICE.

Note

Bots aren’t supposed to take action based on notices. It’s in the standards! Most likely you don’t want to overwrite this.

Note

Globally-ignored users do not trigger this.

Parameters:
  • sender (str) – Who sent the NOTICE. Not necessarily a string that can be turned into a :class:`UserStr`. Servers send NOTICEs. Expect something like “dacia.oftc.net” or “nick!user@host”. Only the latter can be turned into a UserStr.

  • receiver (str) – The channel name in which the NOTICE was seen, or our nick.

  • messge (str) – The message sent, without leading or trailing whitespace.

privmsg_cb(user, dest, message, is_opmod)

Called whenever we are enabled and see a PRIVMSG.

You want to overwrite this function if you care about messages in moderated channels and/or PMs directly to us.

Note

Globally-ignored users do not trigger this.

Parameters:
  • user (UserStr) – Who PRIVMSGed.

  • dest (str) – The channel name (e.g. “#foo”) in which the message was seen, or our nick (if a literal PM to us).

  • message (str) – The message sent, without leading or trailing whitespace.

  • is_opmod (bool) – Whether or not this message is a statusmsg targeted to @#channel as opposed to #channel as usual. OFTC’s +z channel mode (hybrid, not solanum) sends messages that would otherwise be blocked to chanops using this method, and when it does, it calls it opmod. If this is set on this message, you must be a chanop in the channel (congratulations!) and you and your fellow chanops are the only ones that saw the message.

timer_cb()

tormodbot.py needs to be edited to call this if you use WeeChat timers.

tmb_mod.w

To make calling weechat functions easier