# ------------------------------------------------------------------------------------------------
# _
# / \ _ __ _ __ ___ ___ _ _ _ __
# / _ \ | '__| '_ ` _ \ / _ \| | | | '__|
# / ___ \| | | | | | | | (_) | |_| | |
# /_/ \_\_| |_| |_| |_|\___/ \__,_|_|
#
#
# Abuse and channel management script for eggdrop bots on IRC networks.
#
# https://armour.bot
#
# Armour automatic onjoin scanning be be placed in one of three modes:
# ON : Automatic scans upon client channel /join
# SECURE : Restricted channel modes (e.g., +Dm) with actions post-scan
# OFF : Manual scans only
#
# ------------------------------------------------------------------------------------------------
# SUPPORTING
# ------------------------------------------------------------------------------------------------
#
# whitelists
# blacklists
# dnsbl scans : dnswl exemptions and remote 'bounce' bot supported
# port scans : remote 'bounce' bot supported
# onchan scans : remote 'bounce' bot supported
#
# ------------------------------------------------------------------------------------------------
# WHITELIST & BLACKLIST TYPES:
# ------------------------------------------------------------------------------------------------
#
# user : authenticated username
# host : hostmask, hostname, IP and CIDR notation accepted
# regex : nick!user@host/rname
# text : channel text matches
# country : geo location ip lookup
# asn : autonomous system number lookup
# chan : onchan #channel
# rname : realname
#
# ------------------------------------------------------------------------------------------------
# WHITELIST ACTIONS:
# ------------------------------------------------------------------------------------------------
#
# A: Accept User (exempt scans) -- default
# O: Op User
# V: Voice User
#
# ------------------------------------------------------------------------------------------------
# BLACKLIST ACTIONS
# ------------------------------------------------------------------------------------------------
#
# B: KickBan User
#
# ------------------------------------------------------------------------------------------------
#
# Code by : Empus @ Undernet - empus@undernet.org
# Last modified : 2024-08-31
#
# Please contact me should you need any assistence, but I hope most config options are explanatory.
#
# Documentation can be found at: https://armour.bot
#
# Contact me at #armour on Undernet
#
# ------------------------------------------------------------------------------------------------
namespace eval arm {
# ------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------
# GENERAL SETTINGS
# ------------------------------------------------------------------------------------------------
# -- bot name variable
# -- used to automate db filename and assist script updates
# -- ensure this *.conf file is named as BOTNAME.conf
# -- if multiple Armour bots run from the same eggdrop directory, each MUST have a different value
set cfg(botname) "EDITME"
# -- alert users if they don't have a password set? (0|1) - [0]
set cfg(alert:nopass) 1
# -- debug type? (putlog|putloglev) - [putloglev]
# putlog will log everything to partyline (very verbose!)
# putloglev allows debug levels to be used by enabling each level (1-5) with .console +N
# .console +123
# ... etc
set cfg(debug:type) "putloglev"
# -- file logging level (0-4) - [0]
# -- data logged to ./armour/logs/<botname>.log
# -- levels:
# 0: most important information
# 1: gentle verbosity
# 2: extra information
# 3: useful for debugging
# 4: extremely verbose
set cfg(log:level) 1
# ------------------------------------------------------------------------------------------------
# UPDATE SETTINGS
# ------------------------------------------------------------------------------------------------
# -- run hourly check for new script updates? (0|1) - [1]
# -- notes are sent to global level 500 users when updates are found
set cfg(update) 1
# -- send notes to global 500 users when new script versions are found? (0|1) - [1]
set cfg(update:notes) 1
# -- automatically update script when new updates are found? (0|1) - [0]
# -- notes are sent to global level 500 users when updates are installed
# -- requires cfg(update) to be enabled
set cfg(update:auto) 0
# -- github branch to check and apply updates from - [master]
set cfg(update:branch) "master"
# -- automatically remove old script backups after N days - [7]
set cfg(update:flush) 7
# -- enable debug mode to download and stage new updates but do not execute (0|1) - 0
set cfg(update:debug) 0
# ------------------------------------------------------------------------------------------------
# USER REGISTRATION SETTINGS
# ------------------------------------------------------------------------------------------------
# -- allow users to self-register bot usernames? (0|1) - [1]
set cfg(register) 1
# -- if allowed, users must be in which channels to self-register usernames?
# space delimited; can be empty to allow in all chans
set cfg(register:inchan) "#chan1 #chan2"
# -- what global access level to give to newusers? - [1]
# -- applies to 'newuser' command when no global level given, and to 'register' command
set cfg(register:level) 1
# -- send notes to global level 500 users when newuser is created with 'register' cmd? (0|1) - [1]
set cfg(note:register) 1
# -- random password string length
set cfg(randpass) 10
# ------------------------------------------------------------------------------------------------
# NETWORK SETTINGS
# ------------------------------------------------------------------------------------------------
# -- ircd type [1-2] - [1]
# 1: ircu (e.g., Undernet, Quakenet)
# 2: IRCnet/EFnet
# 3: InspIRCd/Solanum/ircd-seven
# 4: Unreal
set cfg(ircd) 1
# -- connected via ZNC bouncer? (0|1) - [0]
# this changes 'jump' command behaviour
set cfg(znc) 0
# -- add automatic blacklist host entry upon g-line? (0|1) - [1]
set cfg(gline:auto) 1
# -- required G-Line reason mask for above to apply (wildcards) - [G-Lined *]
set cfg(gline:mask) "G-Lined *"
# -- mask for which G-Lines do NOT add blacklist entry (wildcard) - [AUTO *]
set cfg(gline:nomask) "G-lined (AUTO *"
# -- bot realname on IRC
set cfg(realname) "Armour - https://armour.bot"
# -- hostname of services - [undernet.org]
set cfg(servicehost) "undernet.org"
# ------------------------------------------------------------------------------------------------
# BOT SETTINGS
# ------------------------------------------------------------------------------------------------
# -- command prefix [c]
# If prefix is a letter, command follows (i.e. c op <chan> <nick>)
# If prefix is a control char, command is embedded (i.e. !op <chan> <nick>)
# prefix cannot be *
set cfg(prefix) "c"
# -- allow use of nickname (with or without nick completion char :) as control char? (0|1) - [1]
set cfg(char:nick) 1
# -- require tab completion char ':' on nickname prefix? (0|1) - [1]
set cfg(char:tab) 1
# -- allow global prefix char '*' (0|1) - [0]
set cfg(char:glob) 1
# -- allow command shortcuts? (0|1) - [1]
# k = kick kb = kickban b = black
# a = add v = view r = rem
# e = exempt s = stats t = topic
# o = op d = deop u = userlist
set cfg(cmd:short) 1
# -- default mode on load? (on|off|secure) - [on]
set cfg(mode) "on"
# -- automatically change mode based on changing of mode +D and -D? (0|1) - [1]
set cfg(mode:auto) 1
# -- allow jump command to specify server? (0|1) - [1]
set cfg(jump) 1
# -- send help and syntax responses from public commands via /notice? (0|1) - [0]
set cfg(help:notc) 0
# -- also sent web documentation links in help command responses? (0|1)? - [1]
set cfg(help:web) 1
# -- only output usage and web links in help responses? (0|1)? - [1]
set cfg(help:webonly) 1
# -- stack voice modes when in secure mode? - [0|1]
# -- this will slow down voicing of new users, by the time set in cfg(stack:secs)
set cfg(stackvoice) 1
# -- stackvoice timer (secs) - [5]
set cfg(stack:secs) 5
# -- default whitelist reason - [regular]
set cfg(def:wreason) "regular"
# -- default blacklist reason - [abuse]
set cfg(def:breason) "abuse"
# -- auto add blacklist entries on floodnet?
set cfg(auto:black) 0
# -- netsplit memory timeout (mins) - [60]
set cfg(split:mem) 60
# -- lockdown eggdrop dcc commands? (0|1) - [1]
set cfg(lockegg) 1
# -- if locked egg, what flag is required for all commands? - [n]
set cfg(lockegg:flag) "n"
# -- default temporary exemptions last for how long? (mins) - [5]
# - via 'exempt' command, time can be overriden
set cfg(exempt:time) 5
# -- allow users to set their own channel greetings? (0|1) - [1]
set cfg(greet:self) 1
# -- add cronjob for bots created with 'deploy' command? (0|1) - [1]
set cfg(deploy:cron) 1
# ------------------------------------------------------------------------------------------------
# NOTE SETTINGS
# ------------------------------------------------------------------------------------------------
# -- allow user notes? (0|1) - [1]
set cfg(note) 1
# -- global command level to send notes to all users? - [450]
set cfg(note:glob) 450
# -- send users note read receipts? (0|1) - [1]
set cfg(note:receipt) 1
# -- send users notes when added to channels? (0|1) - [1]
set cfg(note:adduser) 1
# -- send users notes when removed from channels? (0|1) - [1]
set cfg(note:remuser) 1
# -- send users notes when channel level is modified? (0|1) - [1]
set cfg(note:level) 1
# ------------------------------------------------------------------------------------------------
# CHANNEL SETTINGS
# ------------------------------------------------------------------------------------------------
# --- channels to NOT operate on. comma separated
# -- command chans (comma delimited). leaeve empy to allow on all channels
set cfg(chan:nocmd) "#foo1 #foo2"
# -- autologin /who cycle (secs) - [60]
# -- how frequently to check the channel list for people who authenticated to X after joining
# - only if enabled above
set cfg(autologin:cycle) 60
# -- default command chan (for applicable privmsg & dcc commands)
set cfg(chan:def) "EDITCHAN"
# -- reporting channel (backchan)
# -- useful for command logging and other diagnostics
set cfg(chan:report) ""
# -- how long to disable floodnet detection after chanmode '+d' (secs) - [60]
# - will be automatically re-enabled on chanmode '-d' anyway
set cfg(time:moded) 60
# -- how many recently joined hostnames to track? (num) - [600]
# - for fast blacklists using type 'last'
set cfg(lasthosts) 600
# -- append the username that set a topic, when set via bot? (0|1) - [1]
set cfg(topic:who) 0
# -- the frequency in mins to check if topic should be reset when 'autotopic' is enabled - [60]
set cfg(atopic:period) 60
# ------------------------------------------------------------------------------------------------
# FLOAT LIMIT SETTINGS
# ------------------------------------------------------------------------------------------------
# -- automatically enable FLOATLIM with default values for new channels? (0|1) - [1]
set cfg(float:auto) 1
# -- the default frequency in seconds to check if limit should be adjusted - [60]
# -- can be overridden with: modchan <chan> floatperiod <value>
set cfg(float:period) 60
# -- the default margin to set limit above currentUser count - [10]
# -- can be overridden with: modchan <chan> floatmargin <value>
set cfg(float:margin) 10
# -- the grace limit to trigger limit adjustment - [3]
# -- can be overridden with: modchan <chan> floatgrace <value>
set cfg(float:grace) 3
# ------------------------------------------------------------------------------------------------
# SERVICE AUTHENTICATION SETTINGS
# ------------------------------------------------------------------------------------------------
# -- method to use for channel actions? (1-3) - [1]
# 1: use server for all actions
# 2: use X for BAN, UNBAN, and KICK, but server for everything else
# 3: use X for everything: BAN, UNBAN, KICK, MODE, TOPIC, OP, DEOP, VOICE, DEVOICE, INVITE
set cfg(chan:method) "1"
# -- authentication username
# leave blank if login not used or handled in another script
# leave blank if using NickServ auth mechanism below
set cfg(auth:user) ""
# -- authentication password
# leave blank if login not used or handled in another script
set cfg(auth:pass) ""
# -- TOTP (2FA) secret base32 key
# leave blank to disable 2FA authentication
# note that this currently requires 'oathtool' to be installed on the server
set cfg(auth:totp) ""
# -- mechanism for authentication (gnuworld|nickserv) - [gnuworld]
# set to "gnuworld" for Undernet
# set to "nickserv" for networks with NickServ
set cfg(auth:mech) "gnuworld"
# -- auth service nickname - [X]
# set to "X" for Undernet
# set to "NickServ" for networks with NickServ
set cfg(auth:serv:nick) "X"
# -- auth service host - [channels.undernet.org]
# -- ie. /msg x@channels.undernet.org login <user> <pass>
# if using NickServ, change to black of the NickServ services host as needed
set cfg(auth:serv:host) "channels.undernet.org"
# -- extension for umode +x registered users - [users.undernet.org]
# -- change with care to suit your own network
set cfg(xhost:ext) "users.undernet.org"
# -- use umode +x? (host hiding) (0|1) - [1]
set cfg(auth:hide) 1
# -- frequency for authentication retries (mins) - [5]
set cfg(auth:retry) 5
# -- use a random nickname until authenticated? (0|1) - 0
set cfg(auth:rand) 0
# -- wait to join channels until authenticated? (0|1) - 0
set cfg(auth:wait) 0
# ------------------------------------------------------------------------------------------------
# KICKBAN SETTINGS
# ------------------------------------------------------------------------------------------------
# -- X ban level (if using X) - [100]
set cfg(ban:level) "100"
# -- how long to set temp bans? - [3d]
# units supported:
# s = seconds
# m = minutes
# h = hours
# d = days
set cfg(ban:time) "3d"
# -- autoban on blacklist entry addition? (0|1) - [1]
set cfg(ban:auto) 1
# -- include 'userid' in banmask where ~ not present in ident and user not authed to services? (0|1) - [1]
# -- note that idents with ~ will still always ban as *!~*@host
# 0: *!*@host
# 1: *!user@host
set cfg(ban:idents) 1
# -- how long to set temp adaptive regex bans? - [7d]
set cfg(ban:time:adapt) "7d"
# -- channel modes to set when channel banlist is full - [+r]
set cfg(ban:full:modes) "+r"
# -- how long (secs) to remember 'newcomers' for (for stringent public chatter rules) - [60]
# -- stringent rules apply to newcomers after first joining channel:
# - excessive word repeat
# - profanity (badwords)
# - spam (channels / website)
# - coloured text
set cfg(time:newjoin) "60"
# -- badword masks for public chatter (space delimited with wildcards)
# -- note: more powerful string now matching supported by 'text' type blacklist entries
set cfg(badwords) "*fuck* *shit*"
# -- time to allow automatic removal of recent bans by specifying blacklist id (secs) - [3600]
# - also provides memory used for optional blacklist removals based on unbans
set cfg(id:unban:time) 3600
# -- automatically remove blacklist entries when channel ban is removed? (0|1) - [1]
# - user must have 'rem' access; unban must occur within cfg(id:unban:time) of ban
set cfg(black:unban:rem) 1
# ------------------------------------------------------------------------------------------------
# SCANNER SETTINGS
# ------------------------------------------------------------------------------------------------
# -- include blacklist entry value in kick messages? (0|1) - [0]
set cfg(black:kick:value) 0
# -- include blacklist reason in kick messages? (0|1) - [1]
set cfg(black:kick:reason) 1
# -- auto convert hostnames to IP addresses when using 'add' command for type 'host'? (0|1) - [0]
set cfg(list:host:convert) 0
# -- run blacklist scans on type 'country' when user has resolved ident? - [0]
# -- whitelist scans will always run
set cfg(country:ident) 0
# -- enable dnsbl scans? (0|1) - [1]
set cfg(dnsbl) 1
# -- enable botnet (remote) dnsbl scans? (0|1) - [1]
set cfg(dnsbl:remote) 0
# -- dnsbl, description and score
# last parameter (0|1) determines if RBL is only used for manual scans (scanrbl cmd) or not
# <rbl> {<desc> <score> <onlymanual>}
# - score must be + or - with a score of >0 constituting a hit
# - if 'onlymanual' is set, RBL is only checked when 'scanrbl' command is used but not in auto scans
# format to add:
# set addrbl(<rbl>) "{<description>} <score> <onlymanual>"
set addrbl(dnsbl.dronebl.org) "DroneBL +1.0 0"; # -- auto scanner (and manual)
set addrbl(sbl.cymru.com) "{Cymru SBL} +1.0 0"; # -- auto scanner (and manual)
set addrbl(pbl.cymru.com) "{Cymru PBL} +1.0 0"; # -- auto scanner (and manual)
set addrbl(mbl.cymru.com) "{Cymru MBL} +1.0 0"; # -- auto scanner (and manual)
set addrbl(bbl.cymru.com) "{Cymru BBL} +1.0 0"; # -- auto scanner (and manual)
set addrbl(rbl.ircbl.org) "IRCBL +1.0 1"; # -- only manual scans as Undernet uses network-wide
#set addrbl(rbl.undernet.org) "{Undernet} +1.0 1"; # -- only manual scans as Undernet uses network-wide
#set addrbl(zen.spamhaus.org) "{Spamhaus Zen} +1.0 0"; # -- auto scanner (and manual); eggdrop box must not use public nameservers
# -- enable port scanner? (0|1) [1]
set cfg(portscan) 1
# -- port scan kick reason
set cfg(portscan:reason) "Armour: possible insecure host unintended for IRC -- please install identd"
# -- enable botnet (remote) port scans? (0|1) - [1]
set cfg(portscan:remote) 0
# -- scanports and description
# -- add an 'addport' line for each port to scan, in the format of:
# set addport(<port>) "<description>"
set addport(21) "ftp"
set addport(22) "ssh"
set addport(23) "telnet"
set addport(25) "smtp"
set addport(80) "www"
set addport(7070) "realserver"
set addport(31337) "31337/tcp"
set addport(10000) "webmin"
# -- minimum port match before action - [1]
set cfg(portscan:min) 2
# -- enable /whois lookups? (0|1) - [1]
# - note: required for 'chan' list entries
set cfg(whois) 1
# -- enable botnet (remote) /whois lookups? (0|1) - [0]
# - not required, but lowers server queue to improve response time on busy channels
# - if enabled, remote bot mut be connected via botnet and have remotescan.tcl (only) loaded
set cfg(whois:remote) 0
# -- if any scanner enabled, scan all? (1) or only unresolved idents? (0) - [0]
set cfg(scans:all) 0
# -- botnet nick of remote bots, if enabled above
# -- ensure this bot is connected to botnet
set cfg(bot:remote:dnsbl) "remote-bot"
set cfg(bot:remote:port) "remote-bot"
set cfg(bot:remote:whois) "remote-bot"
# ------------------------------------------------------------------------------------------------
# CAPTCHA SETTINGS
# -- requires channel mode to be 'secure'
# -- web CAPTCHA requires 'armour-verify' web application @ https://github.com/empus/armour-verify
# ------------------------------------------------------------------------------------------------
# CAPTCHA used to help validate suspicious joining clients
# -- enable CAPTCHA for suspicious clients? (0|1) - [1]
set cfg(captcha) 0
# -- CAPTCHA type (web|text) - [text]
# -- note that "web" requires the armour-verify web application
set cfg(captcha:type) "text"
# -- only if cfg(captcha:type) is "web", what is the URL to the web CAPTCHA application?
# -- note that "web" requires the armour-verify web application
set cfg(captcha:web:url) "https://verify.armour.bot"
# -- www.textcaptcha.com ID
# -- only required if cfg(captcha:type) is "text"
set cfg(captcha:id) "your@emailaddress.com"
# -- send CAPTCHA challenge to user via notice or privmsg? (notice|privmsg) - [notice]
set cfg(captcha:method) "notice"
# -- send an opnotice when a question is sent to a user? (0|1) - [1]
set cfg(captcha:opnotc) 1
# -- how long to wait (in secs) for CAPTCHA responses? - [180]
set cfg(captcha:time) 180
# -- action to take for expired responses (manual|kick|kickban) - [kickban]
set cfg(captcha:expired) "kickban"
# -- kick message for expired responses
set cfg(captcha:expired:kick) "Armour: your lack of CAPTCHA response is disturbing. Please try again or login to X."
# -- kick message from web CAPTCHA when kick history is identified
set cfg(captcha:kick:kick) "Armour: you have a history of abuse."
# -- kick message from web CAPTCHA when IP is rate-limited
set cfg(captcha:ratelimit:kick) "Armour: you have been CAPTCHA rate-limited. Please come back later or login to X."
# -- send notice to channel operators when a captcha expectation expires? (0|1) - 1
set cfg(captcha:expired:ops) 1
# -- how many incorrect attempts are allowed before action? - 1
set cfg(captcha:wrong:attempts) 1
# -- action to take for incorrect responses (manual|kick|kickban) - [kickban]
# -- manual: send opnotice to ask ops for manual review
set cfg(captcha:wrong) "kickban"
# -- bantime duration for incorrect responses? - [10m]
set cfg(captcha:wrong:bantime) "10m"
# -- bantime duration for expired responses - [1h]
set cfg(captcha:expired:bantime) "10m"
# -- kick message for incorrect responses
set cfg(captcha:wrong:kick) "Incorrect response! Please give it some more thought next time."
# -- send acknowledgement for correct answers? (0|1) - [1]
set cfg(captcha:ack) 1
# -- what acknwledgement message to send?
set cfg(captcha:ack:msg) "Correct! You can now speak in the channel."
# -- send acknowledgement to channel ops from correct answers? (0|1) - [1]
set cfg(captcha:ack:ops) 1
# ------------------------------------------------------------------------------------------------
# IP QUALITY SCORE SETTINGS
# ------------------------------------------------------------------------------------------------
# This section relates to the use of ipqualityscore.com lookups for potentially malicious clients
# -- IPQS enable? (0|1) - [0]
set cfg(ipqs) 0
# -- IPQS API key
set cfg(ipqs:key) "YOUR-KEY-HERE"
# -- IPQS only match clients with ~ in ident? (0|1) - [1]
set cfg(ipqs:onlynoident) 1
# -- IPQS minimum fraud score required before action is taken (0-100) - [85]
set cfg(ipqs:minscore) 90
# -- IPQS action (in mode=secure) -- kick|kickban|warn - [kick]
set cfg(ipqs:action:secure) "kick"
# -- IPQS action (in mode=on) -- kick|kickban|warn - [kickban]
set cfg(ipqs:action:on) "kickban"
# -- IPQS ban duration (secs) - [3600]
set cfg(ipqs:duration) 3600
# ------------------------------------------------------------------------------------------------
# IRCBL INTEGRATION SETTINGS
# ------------------------------------------------------------------------------------------------
# This section relates to the use of IRCBL integration for for insertion & deletion
# -- ircbl.org enable
set cfg(ircbl) 0;
# -- ircbl.org key
set cfg(ircbl:key) "YOUR-KEY-HERE";
# -- ircbl.org default addition type - [11]
set cfg(ircbl:type) 11;
# -- ircbl.org generic comment
set cfg(ircbl:add:comment) "IRC drone detected";
# -- ircbl.org manual addition global level required
set cfg(ircbl:lvl) 500;
# ------------------------------------------------------------------------------------------------
# TEXT MATCH BLACKLIST ENTRY SETTINGS
# ------------------------------------------------------------------------------------------------
# This section relates to blacklist entries matching channel text (wildcard or regex)
# -- exempt from matching if they are opped? (0|1) - [1]
set cfg(text:exempt:op) 1
# -- exempt from matching if they are voiced? (0|1) - [0]
set cfg(text:exempt:voice) 0
# -- only match clients who have recently joined the channel? (0|1) - [0]
# this relates to above variable cfg(time:newjoin)
set cfg(text:newcomer) 0
# -- add automatic blacklist entries for users who trigger text matches? (0|1) - [0]
set cfg(text:autoblack) 0
# -- if automatic blacklist entries are enabled, what reason to use?
set cfg(text:autoblack:reason) "(auto) blacklisted from text match violation"
# -- if threshold is >1, display warning to user on first occurrence? (0|1) - [0]
set cfg(text:warn) 1
# -- if warning is enabled, send via channel (chan) or /notice (notc)? (chan|notc) - [notc]
set cfg(text:warn:type) "notc"
# -- if warning is enabled, what should the message say?
set cfg(text:warn:msg) "Please avoid using such language in #channel. Consider this a warning."
# -- if sending report to ops/debugchan, what should it say?
# substitution:
# %N% nickname
# %I% ID of entry
set cfg(text:warn:report) "Armour: %N% has been warned about use of language. \[id: %I%\]"
# ------------------------------------------------------------------------------------------------
# LINE FLOOD SETTINGS
# ------------------------------------------------------------------------------------------------
# This section relates to line flood handling (from individual clients, or the entire channel)
# -- how many lines to allow from a single client in how many seconds before acting? (lines:secs) - [5:2]
# -- leave config value blank to disable nickname based line flood tracking
set cfg(flood:line:nicks) "5:2"
# -- how many lines to allow from the whole channel in how many seconds before acting? (lines:secs) - [10:2]
# -- NOTE: this should be set based on the expected activity of the channel (to avoid false positives)
# -- leave config value blank to disable channel based line flood tracking
set cfg(flood:line:chan) "10:2"
# -- modes to temporarily lockdown channel with, during entire channel lineflood - [+mr]
set cfg(flood:line:chan:mode) "+mr"
# -- temporary lockdown time when whole channel encounters lineflood (secs) - [60]
set cfg(flood:line:chan:lock) 60
# -- exempt from matching if they are opped? (0|1) - [1]
set cfg(flood:line:exempt:op) 1
# -- exempt from matching if they are voiced? (0|1) - [1]
set cfg(flood:line:exempt:voice) 1
# -- only match clients who have recently joined the channel? (0|1) - [1]
# this relates to above variable cfg(time:newjoin)
set cfg(flood:line:newcomer) 1
# -- kickban reason when user reaches line limit
set cfg(flood:line:reason) "Armour: flood detected"
# -- add automatic blacklist entries for users who trigger text matches? (0|1) - [0]
set cfg(flood:line:autoblack) 0
# -- if automatic blacklist entries are enabled, what reason to use?
set cfg(flood:line:autoblack:reason) "(auto) blacklisted from line flood"
# ------------------------------------------------------------------------------------------------
# ADAPTIVE REGEX FLOODNET DETECTION
# ------------------------------------------------------------------------------------------------
# -- enable adaptive regex floodnet detection? - [1]
set cfg(adapt) 1
# -- adaptive regex types on join? (space delimited) - [n i]
# n : nickname
# ni : nick!ident
# i : ident
set cfg(adapt:types:join) "n i"
# -- adaptive regex types after /who (space delimited) - [r]
# -- used in secure mode or once rname known
# n : nickname
# ni : nick!ident
# nir : nick!ident/rname
# nr : nick/rname
# i : ident
# ir : ident/rname
# r : rname
#
# use types involving rname and do not duplicate those done above in cfg(adapt:types:join)
# this will prevent double handling for a given pattern (ie. false positives)
set cfg(adapt:types:who) "r"
# -- adaptive regex max join rate (clients:seconds:retain) - [2:1:5]
# clients: client joins
# seconds: .. in N seconds
# retain: & retain common pattern for N seconds (for further joins)
set cfg(adapt:rate) "2:1:5"
# ------------------------------------------------------------------------------------------------
# REPORT SETTINGS
# ------------------------------------------------------------------------------------------------
# -- include blacklist entry value in kick messages? (0|1) - [0]
set cfg(report:value) 0
# -- include blacklist entry reason in kick messages? (0|1) - [1]
set cfg(report:reason) 1
# -- send user notices for whitelist entries? (0|1) - [0]
set cfg(notc:white) 0
# -- send user notices for blacklist entries? (0|1) - [0]
set cfg(notc:black) 0
# -- send op notices for whitelist entries? (0|1) - [1]
set cfg(opnotc:white) 1
# -- send op notices for blacklist entries? (0|1) - [1]
set cfg(opnotc:black) 1
# -- send op notices for IRC Operator autoops using channel operop setting? (0|1) - [1]
set cfg(opnotc:operop) 1
# -- send op notices for blacklist text warnings? (0|1) - [1]
set cfg(opnotc:text) 1
# -- send debug chan notices for whitelist entries? (0|1) - [1]
set cfg(dnotc:white) 1
# -- send debug chan notices for blacklist entries? (0|1) - [1]
set cfg(dnotc:black) 1
# -- send debug chan notices for blacklist text warnings? (0|1) - [1]
set cfg(dnotc:text) 1
# -- send debug chan notices for IRC Operator autoops using channel operop setting? (0|1) - [1]
set cfg(dnotc:operop) 1
# ------------------------------------------------------------------------------------------------
# CUSTOM QUEUE SETTINGS
# ------------------------------------------------------------------------------------------------
# -- frequency (in secs) to find hidden users when in secure mode (chanmode +Dm) - [5]
set cfg(queue:secure) 5
# ------------------------------------------------------------------------------------------------
# FLOODNET SETTINGS
# ------------------------------------------------------------------------------------------------
# -- channel lock modes during floodnet - [+mr]
set cfg(chanlock:mode) "+mr"
# -- channel lock time expiry (secs) - [5]
# -- note: lock will still only be removed after server queue is cleared (ie. kicks)
set cfg(chanlock:time) "60"
# ------------------------------------------------------------------------------------------------
# TRAKKA SETTINGS
# ------------------------------------------------------------------------------------------------
# -- channel to send trakka alerts to? prefix with @ for opnotice
# -- defaults to channel the client joined, if empty
set cfg(trakka:alertchan) ""
# -- routinely add points every how many mins? - [360]
# - every 6 hours (4 points per day)
set cfg(trakka:routine) 360
# -- routinely save trakka scores to db every N minutes? - [60]
set cfg(trakka:autosave) 60
# -- remove how many points daily from those not in the channel? - [1]
set cfg(trakka:subtract) 1
# -- initial newcomer score gain after how many seconds in chan? - [3600]
set cfg(trakka:init) 3600
# -- kickban user if user still has no score, is not opped, or voiced after a wait period (0|1) - [1]
# -- voice will not be factored if channel is in secure mode
set cfg(trakka:kb:enable) 1
# -- if kickban is enabled, how long should the bot wait before removing? (secs) - [300]
# -- NOTE: this needs to be less than trakka:init value above
set cfg(trakka:kb:wait) 1800
# -- if kickban is enabled, what kick reason to use?
set cfg(trakka:kb:reason) "Armour: you appear to be unknown and have therefore been removed (trakka)"
# -- reason for kick message when nudge command is used
set cfg(trakka:nudge:reason) "Armour: you appear to be unknown and have therefore been removed (trakka)"
# -- duration for kickban when nudge command is used - [1h]
set cfg(trakka:nudge:time) "1h"
# ------------------------------------------------------------------------------------------------
# EXTERNAL SCRIPT INTEGRATION
# ------------------------------------------------------------------------------------------------
# -- pass user data after negative scans to external scripts with args: nick uhost hand chan
# -- use this so that other scripts can process joining users but only after Armour clears them
# - space delimited
set cfg(integrate:procs) ""
# ------------------------------------------------------------------------------------------------
# BOT PROTECTION
# ------------------------------------------------------------------------------------------------
# -- list of /silence masks
# - provides server level flood protection
# - leave empty for no /SILENCE
# -- WARNING: if CAPTCHA is being used, SILENCE should be disabled (to allow captcha responses)
#set cfg(silence) "+*!*@*,+~*!*@*.users.undernet.org"; # -- only allow messages from authed users
set cfg(silence) ""
# ------------------------------------------------------------------------------------------------
# SECURE MODE (PARANOIA) SETTINGS
# ------------------------------------------------------------------------------------------------
# -- when in mode 'secure'
# - number of clones to mark for manual review (no autovoice) [2]
set cfg(paranoid:clone) 2
# -- when in mode 'secure'
# - server connection time must be older than N seconds for autovoice [10]
set cfg(paranoid:signon) 10
# -- send CTCP VERSION to clients to help validate when in 'secure' mode? (0|1) - 1
set cfg(paranoid:ctcp) 1
# -- if enabled, how long to wait for CTCP version response? (secs) - 3
set cfg(paranoid:ctcp:wait) 3
# -- if CTCP reply not received, what action to take? (manual|captcha|kick|kickban) - manual
set cfg(paranoid:ctcp:action) "manual"
# -- if action is set to kick or kickban, what kick message should be used?
set cfg(paranoid:ctcp:kickmsg) "Armour: your presence in this channel is suspicious."
# -- how many times can a client be kicked in what period (secs), before being kickbanned? - [1:330]
# this will prevent a client from continuously auto-rejoining when actions only kick
# the next time a kick is attempted, it will instead revert to kickban
# ideally this should be longer than double both cfg(queue:secure) and cfg(captcha:time) combined (if captcha is used)
set cfg(paranoid:klimit) "1:670"
# ------------------------------------------------------------------------------------------------
# DRONEBL
# ------------------------------------------------------------------------------------------------
# -- enable DroneBL to support RBL additions and removals?
# -- note that this is not required only to do RBL lookups, which can be done via `set scan:rrbls`
set cfg(dronebl) 0
# -- DroneBL RPC Key (www.dronebl.org)
# - caution: you are responsible for DroneBL submissions with this key
# - allocate access with care
set cfg(dronebl:key) ""
# -- level required for DroneBL submission [500]
set cfg(dronebl:lvl) 500
# -- default submission type [17]
set cfg(dronebl:type) 17
# ------------------------------------------------------------------------------------------------
# QUOTE PLUGIN -- settings only relevant if optional plugin is loaded: quote
# ------------------------------------------------------------------------------------------------
# -- who can use the seen command? (1-5) - [2]
# 1: all channel users
# 2: all voiced, opped, and authed users
# 3: only voiced when not secure mode, opped, and authed users
# 4: only opped and authed channel users
# 5: only authed users with command access
set cfg(quote:allow) 2
# -- level required to delete quotes that are not your own - [200]
set cfg(quote:cmd:del) 200
# -- level required to recall quote stats - [200]
set cfg(quote:cmd:stats) 200
# -- how long to remember last spoken lines in a channel? (mins) - 180
set cfg(quote:lastspeak:mins) 180
# -- with 'quote last', recently spoken lines in the last N seconds will be ignored (secs) - 2
set cfg(quote:lastspeak:ts) 2
# ------------------------------------------------------------------------------------------------
# SEEN PLUGIN -- settings only relevant if optional plugin is loaded: seen
# ------------------------------------------------------------------------------------------------
# -- who can use the seen command? (1-5) - [2]
# 1: all channel users
# 2: all voiced, opped, and authed users
# 3: only voiced when not secure mode, opped, and authed users
# 4: only opped and authed channel users
# 5: only authed users with command access
set cfg(seen:allow) 3
# ------------------------------------------------------------------------------------------------
# WEATHER PLUGIN -- settings only relevant if optional plugin is loaded: weather
# https://www.openweathermap.org
# ------------------------------------------------------------------------------------------------
# -- who can use the weather command? (1-5) - [2]
# 1: all channel users
# 2: all voiced, opped, and authed users
# 3: only voiced when not secure mode, opped, and authed users
# 4: only opped and authed channel users
# 5: only authed users with command access
set cfg(weather:allow) 2
# -- API key for weather data (www.openweathermap.org)
set cfg(weather:key) "YOUR-KEY-HERE"
# -- units for weather output (metric, imperial, or both) - [both]
set cfg(weather:units) "both"
# -- specify celsius temperature and windspeed with decimal precision? (0|1) - [0]
set cfg(weather:precise) 0
# ------------------------------------------------------------------------------------------------
# NINJAS PLUGIN -- settings only relevant if optional plugin is loaded: ninjas
# commands: joke, dad, history, fact, chuck, cocktail
# https://www.api-ninjas.com
# ------------------------------------------------------------------------------------------------
# -- who can use the commands: joke, dad, history, fact, chuck, cocktail (1-5) - [3]
# 1: all channel users
# 2: all voiced, opped, and authed users
# 3: only voiced when not secure mode, opped, and authed users
# 4: only opped and authed channel users
# 5: only authed users with command access
set cfg(ninjas:allow) 3
# -- API Key (www.api-ninjas.com)
set cfg(ninjas:key) "YOUR-KEY-HERE"
# ------------------------------------------------------------------------------------------------
# HUMOUR PLUGIN -- settings only relevant if optional plugin is loaded: humour
# commands: meme, gif, praise, insult
# https://www.humorapi.com
# ------------------------------------------------------------------------------------------------
# -- who can use the commands: gif, meme, praise, insult (1-5) - [2]
# 1: all channel users
# 2: all voiced, opped, and authed users
# 3: only voiced when not secure mode, opped, and authed users
# 4: only opped and authed channel users
# 5: only authed users with command access
set cfg(humour:allow) 3
# -- API Key (www.humor-api.com)
set cfg(humour:key) "YOUR-KEY-HERE"
# ------------------------------------------------------------------------------------------------
# OPENAI
# -- requires 'openai' plugin to be loaded
# -- requires 'openai' per channel setting to be enabled
# ------------------------------------------------------------------------------------------------
# -- AI service
# -- perplexity: perplexity.ai
# -- openai: openai.com
set cfg(ask:service) "openai"
# -- AI model:
# openai: https://platform.openai.com/docs/models
# https://openai.com/api/pricing
# perplexity: https://docs.perplexity.ai/docs/model-cards
# https://docs.perplexity.ai/docs/pricing
set cfg(ask:model) "gpt-4o-mini"
# -- AI API token
# OpenAI: https://platform.openai.com/account/api-keys
# Perplexity: https://www.perplexity.ai/settings/api
set cfg(ask:token) "YOUR-TOKEN-HERE"
# -- OpenAI ChatGPT API organization
# NOTE: This is only required if cfg(ask:service) is set to 'openai'
# https://platform.openai.com/account/org-settings
set cfg(ask:org) "YOUR-ORG-HERE"
# -- API query timeout (secs) - [60]
set cfg(ask:timeout) 60
# -- API Max Answer Lines
set cfg(ask:lines) "3"
# -- API temperature
# The sampling temperature, between 0 and 1. Higher values like 0.8 will make the output more
# random, while lower values like 0.2 will make it more focused and deterministic. If set to 0,
# the model will use log probability to automatically increase the temperature until certain
# thresholds are hit.
set cfg(ask:temp) "0.7"
# -- Chatbot description
# Optional instruction to the chatbot to influence its responses
set cfg(ask:system) "You are $cfg(botname). Be fun and helpful, pretending to be a sentient robot created by Empus."
# -- API system behaviour for all user requests
# notes:
# * setting is always applied to all requests
# * per user per chan behaviour can be set with 'askmode' command
# * restricting output line count can help avoid floods or throttlings
# examples:
# "Answer humorously."
# "Answer sarcastically."
# "Pretend to be a sarcastic politician."
# "Act like an idio."
set cfg(ask:prefix) "Answer in 3 lines or less."
# -- channels to apply blacklisted request strings in (space delimited)
# note: set to "" to disable blacklist on all channels
set cfg(ask:blacklist:chans) ""
# -- string masks blacklisted for use in queries
# note: this list is only applied to channels listed in cfg(ask:blacklist:chans)
set cfg(ask:blacklist) "*badword1* *another* *example2*"
# -- AI conversation memory after initial question (mins) - [60]
set cfg(ask:mem) 60
# -- send all queries to 'ask' when botnick is used? (0|1) - [1]
# NOTE: this will prevent all other commands from using the botnick as a command prefix
set cfg(ask:cmdnick) 1
# -- who can send queries? (1-5) - [3]
# 1: all channel users
# 2: all voiced, opped, and authed users
# 3: only voiced when not secure mode, opped, and authed users
# 4: only opped and authed channel users
# 5: only authed users
set cfg(ask:allow) 3
# -- DALL-E image creation enable? (0|1) - [1]
set cfg(ask:image) 1
# -- DALL-E image size (pixels) - "1024x1024"
# -- smaller images are faster to generate (512x512, 1024x1024)
# -- note that dall-e-3 does not support 512x512
set cfg(ask:image:size) "1024x1024"
# -- DALL-E image model - [dall-e-3]
set cfg(ask:image:model) "dall-e-3"
# -- DALL-E image overlay with requesting nick and prompt? (0|1) - [1]
# note: ImageMagick7 must be installed on machine (using binary 'convert')
set cfg(ask:image:overlay) 0
# -- DALL-E image creation rate limiting (req:mins:hold) - [5:5:30]
# -- triggered at 5 requests per 5 mins, then retaining rate limit for 30 mins
# -- set to "" to disable rate limiting
set cfg(ask:image:rate) "5:5:30"
# -- DALL-E image creation rate limiting action
# -- set to "" to temporarily prevent all image requests
# -- set value to alternate DALL-E model to reduce API costs during rate limited restriction period
set cfg(ask:rate:act) "dall-e-2"
# -- absolute path to save DALL-E image PNG files to webserver
set cfg(ask:path) "/home/armour/www/armour"
# -- retain PNG and mp3 files for how many days? - [90]
# notes:
# - to keep forever, use empty value
# - those with votes or in quotes will remain preserved
set cfg(ask:expire) "90"
# -- URL root serving PNG files from above path
set cfg(ask:site) "https://files.armour.bot"
# ------------------------------------------------------------------------------------------------
# AI SPEAK (text-to-speech)
# ------------------------------------------------------------------------------------------------
# -- enable 'speak' functionality? (0|1) - [0]
set cfg(speak:enable) 0
# -- max number of lines to speak in responses
set cfg(speak:lines) "4"
# -- text-to-speech service (elevenlabs, openai) - [openai]
# -- openai: www.openai.com
# -- elevenlabs: www.elevenlabs.io
set cfg(speak:service) "openai"
# -- API query timeout (seconds)
set cfg(speak:timeout) 30
# ------------------------------------------------------------------------------------------------
# OPENAI -- relevant settings only if cfg(speak:service) set to: openai
# ------------------------------------------------------------------------------------------------
# -- OpenAI text-to-speech voice (alloy, echo, fable, onyx, nova, shimmer) - [onyx]
set cfg(speak:openai:voice) "onyx"
# -- OpenAI text-to-speech model - [tts-1]
set cfg(speak:openai:model) "tts-1"
# -- OpenAI text-to-speech file format mp3, opus, aac, flac
set cfg(speak:openai:format) "mp3"
# ------------------------------------------------------------------------------------------------
# ELEVENLABS -- relevant settings only if cfg(speak:service) set to: elevenlabs
# www.elevenlabs.io
# ------------------------------------------------------------------------------------------------
# -- ElevenLabs text-to-speech API key
set cfg(speak:key) "API-KEY-HERE"
# -- ElevenLabs text-to-speech voice model
set cfg(speak:model) "eleven_turbo_v2_5"
# -- ElevenLabs text-to-speech voice
# -- https://elevenlabs.io/app/voice-lab
set cfg(speak:voice) "ZQe5CZNOzWyzPSCn5a3c"
# ------------------------------------------------------------------------------------------------
# COMMAND LEVELS
# ------------------------------------------------------------------------------------------------
# -- level requirements for commands
# -- standard command structure
# - to disable a command, comment out line
# - to change the required level, update the level per command
# - to modify whether a command is accepted via public chan, privmsg, or dcc, update the binds
#
# - binds:
# pub: public channel command
# msg: privmsg command
# dcc: dcc command
# ------------------------------------------------------------------------------------------------
# command plugin level req. binds
# ------------------------------------------------------------------------------------------------
set addcmd(register) { userdb 0 pub msg dcc }; # -- only if configured
set addcmd(ask) { arm 1 pub msg dcc }; # -- requires openai plugin
set addcmd(askmode) { arm 1 pub msg dcc }; # -- requires openai plugin
set addcmd(and) { arm 1 pub msg dcc }; # -- requires openai plugin
set addcmd(image) { arm 1 pub msg dcc }; # -- requires openai plugin
set addcmd(speak) { arm 1 pub msg dcc }; # -- requires speak plugin
set addcmd(joke) { arm 1 pub msg dcc }; # -- requires ninjas plugin
set addcmd(dad) { arm 1 pub msg dcc }; # -- requires ninjas plugin
set addcmd(history) { arm 1 pub msg dcc }; # -- requires ninjas plugin
set addcmd(fact) { arm 1 pub msg dcc }; # -- requires ninjas plugin
set addcmd(chuck) { arm 1 pub msg dcc }; # -- requires ninjas plugin
set addcmd(cocktail) { arm 1 pub msg dcc }; # -- requires ninjas plugin
set addcmd(weather) { arm 1 pub msg dcc }; # -- requires weather plugin
#set addcmd(meme) { arm 1 pub msg dcc }; # -- requires humour plugin
set addcmd(gif) { arm 1 pub msg dcc }; # -- requires humour plugin
set addcmd(praise) { arm 1 pub msg dcc }; # -- requires humour plugin
set addcmd(insult) { arm 1 pub msg dcc }; # -- requires humour plugin
set addcmd(seen) { seen 1 pub msg dcc }; # -- requires seen plugin
set addcmd(access) { userdb 1 pub msg dcc }
set addcmd(cmds) { arm 1 pub msg dcc }
set addcmd(help) { arm 1 pub msg dcc }
set addcmd(info) { userdb 1 pub msg dcc }
set addcmd(verify) { userdb 1 pub msg dcc }
set addcmd(lang) { userdb 1 pub msg dcc }
set addcmd(set) { userdb 1 pub msg dcc }
set addcmd(time) { userdb 1 pub msg dcc }
set addcmd(version) { arm 1 pub msg dcc }
set addcmd(asn) { arm 1 pub msg dcc }
set addcmd(country) { arm 1 pub msg dcc }
set addcmd(note) { arm 1 pub msg dcc }
set addcmd(exempt) { arm 100 pub msg dcc }
set addcmd(queue) { arm 100 pub msg dcc }
set addcmd(ack) { trakka 100 pub msg dcc }; # -- requires trakka plugin
set addcmd(score) { trakka 100 pub msg dcc }; # -- requires trakka plugin
set addcmd(nudge) { trakka 100 pub msg dcc }; # -- requires trakka plugin
set addcmd(idle) { arm 100 pub msg dcc }
set addcmd(ipqs) { ipqs 100 pub msg dcc }; # -- only if configured
set addcmd(view) { arm 100 pub msg dcc }
set addcmd(scan) { arm 100 pub msg dcc }
set addcmd(search) { arm 100 pub msg dcc }
set addcmd(scanrbl) { arm 100 pub msg dcc }; # -- only if configured
set addcmd(scanport) { arm 100 pub msg dcc }; # -- only if configured
set addcmd(mode) { arm 100 pub msg dcc }
set addcmd(add) { arm 100 pub msg dcc }
set addcmd(op) { arm 100 pub msg dcc }
set addcmd(deop) { arm 100 pub msg dcc }
set addcmd(voice) { arm 100 pub msg dcc }
set addcmd(devoice) { arm 100 pub msg dcc }
set addcmd(kick) { arm 100 pub msg dcc }
set addcmd(ban) { arm 100 pub msg dcc }
set addcmd(unban) { arm 100 pub msg dcc }
set addcmd(topic) { arm 100 pub msg dcc }
set addcmd(invite) { arm 100 pub msg dcc }
set addcmd(stats) { arm 200 pub msg dcc }
set addcmd(status) { arm 200 pub msg dcc }
set addcmd(mod) { arm 200 pub msg dcc }
set addcmd(black) { arm 200 pub msg dcc }
set addcmd(captcha) { arm 300 pub msg dcc }; # -- only if configured
set addcmd(chanscan) { arm 300 pub msg dcc }
set addcmd(rem) { arm 300 pub msg dcc }
set addcmd(newuser) { userdb 400 pub msg dcc }
set addcmd(adduser) { userdb 400 pub msg dcc }
set addcmd(remuser) { userdb 400 pub msg dcc }
set addcmd(userlist) { userdb 400 pub msg dcc }
set addcmd(usearch) { userdb 400 pub msg dcc }
set addcmd(chanlist) { userdb 400 pub msg dcc }
set addcmd(moduser) { userdb 400 pub msg dcc }
set addcmd(ignore) { arm 450 pub msg dcc }
set addcmd(jump) { arm 450 pub msg dcc }
set addcmd(load) { arm 450 pub msg dcc }
set addcmd(reload) { arm 450 pub msg dcc }
set addcmd(restart) { arm 450 pub msg dcc }
set addcmd(rehash) { arm 450 pub msg dcc }
set addcmd(addchan) { userdb 450 pub msg dcc }
set addcmd(remchan) { userdb 450 pub msg dcc }
set addcmd(modchan) { userdb 450 pub msg dcc }
set addcmd(deluser) { userdb 450 pub mdg dcc }
set addcmd(say) { arm 500 pub msg dcc }
set addcmd(deploy) { arm 500 pub msg dcc }
set addcmd(do) { userdb 500 pub msg dcc }
set addcmd(die) { arm 500 pub msg dcc }
set addcmd(conf) { arm 500 pub msg dcc }
set addcmd(showlog) { arm 500 pub msg dcc }
set addcmd(update) { arm 500 pub msg dcc }
# ------------------------------------------------------------------------------------------------
# PLUGINS
# ------------------------------------------------------------------------------------------------
# -- optional plugins to load
# -- to load plugins, uncomment their line after editing any config parameters in each file
#set addplugin(quote) "./armour/plugins/quote.tcl"; # -- quote management
#set addplugin(openai) "./armour/plugins/openai.tcl"; # -- openai (ChatGPT)
#set addplugin(speak) "./armour/plugins/speak.tcl"; # -- speak (text-to-speech)
#set addplugin(summarise) "./armour/plugins/summarise.tcl"; # -- sumarise chans/people (requires openai)
#set addplugin(ninjas) "./armour/plugins/ninjas.tcl"; # -- ninjas (jokes, facts, etc)
#set addplugin(humour) "./armour/plugins/humour.tcl"; # -- humour (gifs, memes, etc)
#set addplugin(weather) "./armour/plugins/weather.tcl"; # -- current weather by city
#set addplugin(seen) "./armour/plugins/seen.tcl"; # -- showing user lastseen
#set addplugin(tell) "./armour/plugins/tell.tcl"; # -- tell reminders
#set addplugin(trakka) "./armour/plugins/trakka/trakka.tcl"; # -- trakka scoring for private chans
#set addplugin(smsbot) "./armour/plugins/smsbot.tcl"; # -- SMS (via smsglobal.com)
#set addplugin(push) "./armour/plugins/push.tcl"; # -- push (via pushover.net)
#set addplugin(email) "./armour/plugins/email.tcl"; # -- send email notes
# ------------------------------------------------------------------------------------------------
# END OF CONFIGURATION
# ------------------------------------------------------------------------------------------------
# ------------------------------------------------------------------------------------------------
}; # -- end of namespace
# ------------------------------------------------------------------------------------------------
source ./armour/armour.tcl; # -- do not edit
# ------------------------------------------------------------------------------------------------