aboutsummaryrefslogtreecommitdiffstats
path: root/modules
diff options
context:
space:
mode:
authorMárcio Silva <coadde at hyperbola dot info>2017-06-02 15:44:54 -0300
committerMárcio Silva <coadde at hyperbola dot info>2017-06-02 15:44:54 -0300
commitb4830e97ae51396ccaa9ca2acb469aef80094ae8 (patch)
treec069b1ef6f9189848726121afa8d815e4e56d138 /modules
downloadhyperbot-b4830e97ae51396ccaa9ca2acb469aef80094ae8.tar.lz
hyperbot-b4830e97ae51396ccaa9ca2acb469aef80094ae8.tar.xz
hyperbot-b4830e97ae51396ccaa9ca2acb469aef80094ae8.zip
Add initial files from envbot v0.1-beta1
Diffstat (limited to 'modules')
-rw-r--r--modules/m_assign_mode.sh228
-rw-r--r--modules/m_autojoin.sh63
-rw-r--r--modules/m_check_numerics.sh45
-rw-r--r--modules/m_commands.sh103
-rw-r--r--modules/m_ctcp.sh91
-rw-r--r--modules/m_dice.sh84
-rw-r--r--modules/m_die.sh75
-rw-r--r--modules/m_dumpvars.sh54
-rw-r--r--modules/m_factoids.sh461
-rw-r--r--modules/m_faq.sh138
-rw-r--r--modules/m_help.sh151
-rw-r--r--modules/m_join.sh92
-rw-r--r--modules/m_karma.sh252
-rw-r--r--modules/m_kick_ban.sh145
-rw-r--r--modules/m_modules.sh178
-rw-r--r--modules/m_nicktracking.sh316
-rw-r--r--modules/m_ping.sh87
-rw-r--r--modules/m_quote.sh85
-rw-r--r--modules/m_rehash.sh74
-rw-r--r--modules/m_say.sh98
-rw-r--r--modules/m_seen.sh209
-rw-r--r--modules/m_sendraw.sh53
-rw-r--r--modules/m_services.sh87
-rw-r--r--modules/m_sqlite3.sh89
-rw-r--r--modules/m_umodes.sh63
-rw-r--r--modules/m_uptime.sh56
26 files changed, 3377 insertions, 0 deletions
diff --git a/modules/m_assign_mode.sh b/modules/m_assign_mode.sh
new file mode 100644
index 0000000..7fb4ea1
--- /dev/null
+++ b/modules/m_assign_mode.sh
@@ -0,0 +1,228 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# Copyright (C) 2007-2008 EmErgE <halt.system@gmail.com> #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Channel modes
+#---------------------------------------------------------------------
+
+module_assign_mode_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'op' || return 1
+ commands_register "$1" 'deop' || return 1
+ commands_register "$1" 'halfop' || return 1
+ commands_register "$1" 'dehalfop' || return 1
+ commands_register "$1" 'voice' || return 1
+ commands_register "$1" 'devoice' || return 1
+ commands_register "$1" 'protect' || return 1
+ commands_register "$1" 'deprotect' || return 1
+ commands_register "$1" 'topic' || return 1
+ helpentry_module_assign_mode_description="Provides op, deop and related commands."
+
+ local help_cmd
+ for help_cmd in op deop halfop halfdeop voice devoice protect deprotect; do
+ printf -v "helpentry_assign_mode_${help_cmd}_syntax" '<#channel> <nick>'
+ printf -v "helpentry_assign_mode_${help_cmd}_description" "$(tr 'a-z' 'A-Z' <<< "${help_cmd:0:1}")${help_cmd:1} <nick> in <#channel>."
+ done
+
+ helpentry_assign_mode_topic_syntax='<#channel> <new topic>'
+ helpentry_assign_mode_topic_description='Change topic to <new topic> in <#channel>'
+}
+
+module_assign_mode_UNLOAD() {
+ return 0
+}
+
+module_assign_mode_REHASH() {
+ return 0
+}
+
+module_assign_mode_handler_op() {
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)\ ([^ ]+) ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local nick="${BASH_REMATCH[2]}"
+ if access_check_capab "op" "$sender" "$channel"; then
+ send_modes "$channel" "+o $nick"
+ else
+ access_fail "$sender" "make the bot op somebody" "op"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "op" "<#channel> <nick>"
+ fi
+}
+
+module_assign_mode_handler_deop() {
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)\ ([^ ]+) ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local nick="${BASH_REMATCH[2]}"
+ if access_check_capab "op" "$sender" "$channel"; then
+ send_modes "$channel" "-o $nick"
+ else
+ access_fail "$sender" "make the bot deop somebody" "op"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "deop" "<#channel> <nick>"
+ fi
+}
+
+module_assign_mode_handler_halfop() {
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)\ ([^ ]+) ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local nick="${BASH_REMATCH[2]}"
+ if access_check_capab "halfop" "$sender" "$channel"; then
+ send_modes "$channel" "+h $nick"
+ else
+ access_fail "$sender" "make the bot halfop somebody" "halfop"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "halfop" "<#channel> <nick>"
+ fi
+}
+
+module_assign_mode_handler_dehalfop() {
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)\ ([^ ]+) ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local nick="${BASH_REMATCH[2]}"
+ if access_check_capab "halfop" "$sender" "$channel"; then
+ send_modes "$channel" "-h $nick"
+ else
+ access_fail "$sender" "make the bot dehalfop somebody" "halfop"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "dehalfop" "<#channel> <nick>"
+ fi
+}
+
+module_assign_mode_handler_voice() {
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)\ ([^ ]+) ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local nick="${BASH_REMATCH[2]}"
+ if access_check_capab "voice" "$sender" "$channel"; then
+ send_modes "$channel" "+v $nick"
+ else
+ access_fail "$sender" "make the bot give voice to somebody" "voice"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "voice" "<#channel> <nick>"
+ fi
+}
+
+module_assign_mode_handler_devoice() {
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)\ ([^ ]+) ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local nick="${BASH_REMATCH[2]}"
+ if access_check_capab "voice" "$sender" "$channel"; then
+ send_modes "$channel" "-v $nick"
+ else
+ access_fail "$sender" "make the bot take voice from somebody" "voice"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "devoice" "<#channel> <nick>"
+ fi
+}
+
+module_assign_mode_handler_protect() {
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)\ ([^ ]+) ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local nick="${BASH_REMATCH[2]}"
+ if access_check_capab "protect" "$sender" "$channel"; then
+ send_modes "$channel" "+a $nick"
+ else
+ access_fail "$sender" "make the bot protect somebody" "protect"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "protect" "<#channel> <nick>"
+ fi
+}
+
+module_assign_mode_handler_deprotect() {
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)\ ([^ ]+) ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local nick="${BASH_REMATCH[2]}"
+ if access_check_capab "protect" "$sender" "$channel"; then
+ send_modes "$channel" "-a $nick"
+ else
+ access_fail "$sender" "make the bot deprotect somebody" "protect"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "deprotect" "<#channel> <nick>"
+ fi
+}
+
+module_assign_mode_handler_topic() {
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)\ (.+) ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local message="${BASH_REMATCH[2]}"
+ if access_check_capab "topic" "$sender" "$channel"; then
+ send_topic "$channel" "$message"
+ else
+ access_fail "$sender" "change the topic" "topic"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "topic" "<#channel> <new topic>"
+ fi
+}
diff --git a/modules/m_autojoin.sh b/modules/m_autojoin.sh
new file mode 100644
index 0000000..468bffc
--- /dev/null
+++ b/modules/m_autojoin.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## This module does autojoin after connect.
+#---------------------------------------------------------------------
+
+module_autojoin_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_connect'
+ helpentry_module_autojoin_description="Provides support for autojoining channels."
+}
+
+module_autojoin_UNLOAD() {
+ unset module_autojoin_join_from_config
+}
+
+module_autojoin_REHASH() {
+ module_autojoin_join_from_config
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Autojoin channels from config.
+## @Type Private
+#---------------------------------------------------------------------
+module_autojoin_join_from_config() {
+ local channel
+ for channel in "${config_module_autojoin_channels[@]}"; do
+ # No quotes around channel because second word of it may be a key
+ # and list_contains just uses the first 2 arguments so a
+ # third one will be ignored.
+ if ! list_contains "channels_current" $channel; then
+ log_info "Joining $channel"
+ # No quotes here because then second argument can be a key
+ channels_join $channel
+ sleep 2
+ fi
+ done
+}
+
+# Called after bot has connected
+module_autojoin_after_connect() {
+ module_autojoin_join_from_config
+}
diff --git a/modules/m_check_numerics.sh b/modules/m_check_numerics.sh
new file mode 100644
index 0000000..44c97f5
--- /dev/null
+++ b/modules/m_check_numerics.sh
@@ -0,0 +1,45 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## For debugging, report any unknown numerics.
+#---------------------------------------------------------------------
+
+module_check_numerics_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='on_numeric'
+ helpentry_module_check_numerics_description="Debugging module to check if any numeric we get is unknown."
+}
+
+module_check_numerics_UNLOAD() {
+ return 0
+}
+
+module_check_numerics_REHASH() {
+ return 0
+}
+
+module_check_numerics_on_numeric() {
+ # Make sure it is in base 10 here.
+ if [[ -z "${numerics[10#${1}]}" ]]; then
+ log_warning_file unknown_data.log "Unknown numeric $1 Data: $2"
+ fi
+}
diff --git a/modules/m_commands.sh b/modules/m_commands.sh
new file mode 100644
index 0000000..ca1f759
--- /dev/null
+++ b/modules/m_commands.sh
@@ -0,0 +1,103 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# Copyright (C) 2007-2008 Vsevolod Kozlov #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Command-related utility commands
+#---------------------------------------------------------------------
+
+module_commands_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'provides' || return 1
+ commands_register "$1" 'commands' || return 1
+ helpentry_module_commands_description="Provides a set of command-related commands."
+
+ helpentry_commands_provides_syntax='<command>'
+ helpentry_commands_provides_description='Shows which module provides command <command>'
+
+ helpentry_commands_commands_syntax='[<module>]'
+ helpentry_commands_commands_description='Lists commands available in <module>. If module name is not given, lists all commands'
+}
+
+module_commands_UNLOAD() {
+ return 0
+}
+
+module_commands_REHASH() {
+ return 0
+}
+
+module_commands_handler_provides() {
+ local sender="$1"
+ local parameters="$3"
+ if [[ $parameters =~ ^([a-zA-Z0-9][^ ]*)( [^ ]+)? ]]; then # regex suggested by AnMaster
+ local command_name="${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
+ local target
+ if [[ $2 =~ ^# ]]; then
+ target="$2"
+ else
+ parse_hostmask_nick "$sender" 'target'
+ fi
+ local module_name
+ commands_provides "$command_name" module_name
+ if [[ -z $module_name ]]; then # No such command
+ send_msg "$target" "Command \"$command_name\" does not exist."
+ else
+ send_msg "$target" "Command \"$command_name\" is provided by module \"$module_name\""
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "provides" "<modulename>"
+ fi
+}
+
+module_commands_handler_commands() {
+ local parameters="$3"
+ local target
+ if [[ $2 =~ ^# ]]; then
+ target="$2"
+ else
+ parse_hostmask_nick "$1" 'target'
+ fi
+ if [[ -z $parameters ]]; then
+ send_msg "$target" "${format_bold}Available commands${format_bold}: ${commands_commands//,/, }"
+ else
+ # So we got a parameter
+ local module_name
+ if [[ $parameters =~ ^([^ ]+)\ *$ ]]; then
+ module_name="${BASH_REMATCH[1]}"
+ else
+ send_notice "$target" "\"$parameters\" is not a valid module name"
+ return 1
+ fi
+ local commands_in_module
+ commands_in_module "$module_name" 'commands_in_module'
+ if [[ $commands_in_module ]]; then
+ send_msg "$target" "${format_bold}Available commands (in module \"$module_name\")${format_bold}: ${commands_in_module//,/, }"
+ elif list_contains "modules_loaded" "$module_name"; then
+ send_notice "$target" "Module \"$module_name\" provides no commands"
+ else
+ send_notice "$target" "Module \"$module_name\" is not loaded"
+ fi
+ fi
+}
diff --git a/modules/m_ctcp.sh b/modules/m_ctcp.sh
new file mode 100644
index 0000000..dfbe874
--- /dev/null
+++ b/modules/m_ctcp.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Handle CTCP
+#---------------------------------------------------------------------
+
+module_ctcp_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_load on_PRIVMSG'
+ helpentry_module_ctcp_description="Answers CTCP requests."
+}
+
+module_ctcp_UNLOAD() {
+ return 0
+}
+
+module_ctcp_REHASH() {
+ return 0
+}
+
+module_ctcp_after_load() {
+ if [[ -z $config_module_ctcp_version_reply ]]; then
+ log_error "VERSION reply (config_module_ctcp_version_reply) must be set in config to use CTCP module."
+ return 1
+ fi
+}
+
+# Called on a PRIVMSG
+#
+# $1 = from who (n!u@h)
+# $2 = to who (channel or botnick)
+# $3 = the message
+module_ctcp_on_PRIVMSG() {
+ local sender="$1"
+ local query="$3"
+ # We can't use regex here. For some unknown reason bash drops \001 from
+ # regex.
+ if [[ $query = $'\001'* ]]; then
+ # Get rid of \001 in the string.
+ local data="${query//$'\001'}"
+ local ctcp_command ctcp_parameters
+ # Split it up into command and any parameters.
+ read -r ctcp_command ctcp_parameters <<< "$data"
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ case "$ctcp_command" in
+ "CLIENTINFO")
+ send_nctcp "$sendernick" "CLIENTINFO CLIENTINFO PING SOURCE TIME VERSION"
+ ;;
+ "PING")
+ send_nctcp "$sendernick" "PING $ctcp_parameters"
+ ;;
+ "SOURCE")
+ send_nctcp "$sendernick" "SOURCE http://envbot.org"
+ ;;
+ "TIME")
+ send_nctcp "$sendernick" "TIME $(date +'%Y-%m-%d %k:%M:%S')"
+ ;;
+ "VERSION")
+ send_nctcp "$sendernick" "VERSION $config_module_ctcp_version_reply"
+ ;;
+ *)
+ # So we didn't handle this CTCP? Return 0 then, someone else may want it.
+ return 0
+ ;;
+ esac
+ # See above. We didn't fall back to not handle it and did not return
+ # so therefore we must have handled it.
+ return 1
+ fi
+ return 0
+}
diff --git a/modules/m_dice.sh b/modules/m_dice.sh
new file mode 100644
index 0000000..7c4c6ab
--- /dev/null
+++ b/modules/m_dice.sh
@@ -0,0 +1,84 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# Copyright (C) 2007-2008 Vsevolod Kozlov #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Rolls dies.
+#---------------------------------------------------------------------
+
+module_dice_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'roll' || return 1
+ helpentry_module_dice_description="Rolls dies for you."
+
+ helpentry_dice_roll_syntax="<dies>d<sides>"
+ helpentry_dice_roll_description="Rolls <dies> dies, each <sides> sides."
+}
+
+module_dice_UNLOAD() {
+ return 0
+}
+
+module_dice_REHASH() {
+ return 0
+}
+
+module_dice_handler_roll() {
+ local sender="$1"
+ local parameters="$3"
+ if [[ $parameters =~ ^([0-9]+)d([0-9]+)$ ]]; then
+ local how_much_times="${BASH_REMATCH[1]}"
+ local how_many_sides="${BASH_REMATCH[2]}"
+ local target=
+ if [[ $2 =~ ^# ]]; then
+ target="$2"
+ else
+ parse_hostmask_nick "$sender" 'target'
+ fi
+ local insane=0
+ # Chech if number of dies and sides are sane.
+ if (( ($how_many_sides < 2 || $how_many_sides > 100)
+ || ($how_much_times < 1 || $how_much_times > 100) )); then
+ log_warning "Tried to roll $how_much_times dies $how_many_sides sides each!"
+ log_warning "This is above the allowed maximum or below the allowed minimum, and was aborted."
+ send_msg "$target" "You can't roll that."
+ return 0
+ fi
+ # Roll $how_much_times dies, each with $how_many_sides sides.
+ local result=""
+ local total=0
+ for (( i=0; $i < $how_much_times; i+=1 )); do
+ local rolled=$(( ($RANDOM % $how_many_sides) + 1 ))
+ result+="$rolled, "
+ ((total += $rolled))
+ done
+ result=${result%, }
+ if [[ $how_much_times != 1 ]]; then
+ result+=" with the grand total of $total"
+ fi
+ send_msg "$target" "You rolled ${result}."
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "roll" "<dies>d<sides>"
+ fi
+}
diff --git a/modules/m_die.sh b/modules/m_die.sh
new file mode 100644
index 0000000..199089a
--- /dev/null
+++ b/modules/m_die.sh
@@ -0,0 +1,75 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# Copyright (C) 2007-2008 EmErgE <halt.system@gmail.com> #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Quit the bot.
+#---------------------------------------------------------------------
+
+module_die_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'die' || return 1
+ commands_register "$1" 'restart' || return 1
+ helpentry_module_die_description="Commands to shut down and restart bot."
+ helpentry_die_die_syntax='[reason]'
+ helpentry_die_die_description='Quit with an optional quit reason.'
+ helpentry_die_restart_syntax='[reason]'
+ helpentry_die_restart_description='Disconnect the bot with an optional quit reason, then rerun itself.'
+}
+
+module_die_UNLOAD() {
+ return 0
+}
+
+module_die_REHASH() {
+ return 0
+}
+
+module_die_handler_die() {
+ local sender="$1"
+ if access_check_owner "$sender"; then
+ local parameters="$3"
+ access_log_action "$sender" "made the bot die with reason: $parameters"
+ local reason=
+ if [[ $parameters ]]; then
+ reason=": $parameters"
+ fi
+ bot_quit "Dying ($sender)$reason"
+ else
+ access_fail "$sender" "make the bot die" "owner"
+ fi
+}
+
+module_die_handler_restart() {
+ local sender="$1"
+ if access_check_owner "$sender"; then
+ local parameters="$3"
+ access_log_action "$sender" "made the bot restart with reason: $parameters"
+ local reason=
+ if [[ $parameters ]]; then
+ reason=": $parameters"
+ fi
+ bot_restart "Restarting ($sender)$reason"
+ else
+ access_fail "$sender" "make the bot restart" "owner"
+ fi
+}
diff --git a/modules/m_dumpvars.sh b/modules/m_dumpvars.sh
new file mode 100644
index 0000000..77a9c14
--- /dev/null
+++ b/modules/m_dumpvars.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Debug module, dump all variables to console.
+#---------------------------------------------------------------------
+
+module_dumpvars_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'dumpvars' || return 1
+ helpentry_module_dumpvars_description="Debugging module to dump all variables in the bot."
+ helpentry_dumpvars_dumpvars_syntax=''
+ helpentry_dumpvars_dumpvars_description='Dump all variables to STDOUT.'
+}
+
+module_dumpvars_UNLOAD() {
+ return 0
+}
+
+module_dumpvars_REHASH() {
+ return 0
+}
+
+module_dumpvars_handler_dumpvars() {
+ local sender="$1"
+ if access_check_owner "$sender"; then
+ # This is hackish, we only display
+ # lines unique to "file" 1.
+ # Also remove one variable that may fill our scrollback.
+ access_log_action "$sender" "a dump of variables"
+ comm -2 -3 <(declare) <(declare -f) | grep -Ev '^module_quote_quotes'
+ else
+ access_fail "$sender" "dump variables to STDOUT" "owner"
+ fi
+}
diff --git a/modules/m_factoids.sh b/modules/m_factoids.sh
new file mode 100644
index 0000000..ba164ed
--- /dev/null
+++ b/modules/m_factoids.sh
@@ -0,0 +1,461 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Simple factoids module using SQLite3
+#---------------------------------------------------------------------
+
+module_factoids_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_load on_PRIVMSG'
+ commands_register "$1" 'learn' || return 1
+ commands_register "$1" 'forget' || return 1
+ commands_register "$1" 'lock_factoid' 'lock factoid' || return 1
+ commands_register "$1" 'unlock_factoid' 'unlock factoid' || return 1
+ commands_register "$1" 'whatis' || return 1
+ commands_register "$1" 'factoid_stats' 'factoid stats' || return 1
+ helpentry_module_factoids_description="Provides a factoid database."
+
+ helpentry_factoids_learn_syntax='<key> (as|is|are|=) <value>'
+ helpentry_factoids_learn_description='Teach the bot a new factoid.'
+
+ helpentry_factoids_forget_syntax='<key>'
+ helpentry_factoids_forget_description='Make the bot forget the factoid <key>.'
+
+ helpentry_factoids_lock_factoid_syntax='<key>'
+ helpentry_factoids_lock_factoid_description='Prevent normal users from changing the factoid <key>.'
+
+ helpentry_factoids_unlock_factoid_syntax='<key>'
+ helpentry_factoids_unlock_factoid_description='Allow changes to a previously locked factoid <key>.'
+
+ helpentry_factoids_whatis_syntax='<key>'
+ helpentry_factoids_whatis_description='Look up the factoid <key>.'
+
+ helpentry_factoids_factoid_stats_syntax=''
+ helpentry_factoids_factoid_stats_description='Report some statistics on the factoid database.'
+}
+
+
+module_factoids_UNLOAD() {
+ # Ok this is a LOT. I hope I got all...
+ unset module_factoids_set module_factoids_remove module_factoids_parse_assignment
+ unset module_factoids_parse_key module_factoids_parse_value
+ unset module_factoids_set_INSERT_or_UPDATE module_factoids_send_factoid
+ unset module_factoids_get_count module_factoids_get_locked_count
+ unset module_factoids_is_locked module_factoids_lock module_factoids_unlock
+ unset module_factoids_SELECT module_factoids_INSERT module_factoids_UPDATE module_factoids_DELETE
+}
+
+
+module_factoids_REHASH() {
+ return 0
+}
+
+
+# Called after module has loaded.
+module_factoids_after_load() {
+ modules_depends_register "factoids" "sqlite3" || {
+ # This error reporting is hackish, will fix later.
+ if ! list_contains "modules_loaded" "sqlite3"; then
+ log_error "The factoids module depends upon the SQLite3 module being loaded."
+ fi
+ return 1
+ }
+ if [[ -z $config_module_factoids_table ]]; then
+ log_error "Factiods table (config_module_factoids_table) must be set in config if you want to use factoids module."
+ return 1
+ fi
+ if ! module_sqlite3_table_exists "$config_module_factoids_table"; then
+ log_error "factoids module: $config_module_factoids_table does not exist in the database file."
+ log_error "factoids module: See comment in doc/factoids.sql for how to create the table."
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Get an item from DB
+## @Type Private
+## @param Key
+## @Stdout The result of the database query.
+#---------------------------------------------------------------------
+module_factoids_SELECT() {
+ #$ sqlite3 -list data/factoids.sqlite "SELECT value from factoids WHERE name='factoids';"
+ #A system that stores useful bits of information
+ module_sqlite3_exec_sql "SELECT value FROM $config_module_factoids_table WHERE name='$(module_sqlite3_clean_string "$1")';"
+}
+
+
+#---------------------------------------------------------------------
+## Insert a new item into DB
+## @Type Private
+## @param key
+## @param value
+## @param hostmask of person who added it
+#---------------------------------------------------------------------
+module_factoids_INSERT() {
+ module_sqlite3_exec_sql \
+ "INSERT INTO $config_module_factoids_table (name, value, who) VALUES('$(module_sqlite3_clean_string "$1")', '$(module_sqlite3_clean_string "$2")', '$(module_sqlite3_clean_string "$3")');"
+}
+
+
+#---------------------------------------------------------------------
+## Change the item in DB
+## @Type Private
+## @param key
+## @param new value
+## @param hostmask of person who changed it
+#---------------------------------------------------------------------
+module_factoids_UPDATE() {
+ module_sqlite3_exec_sql \
+ "UPDATE $config_module_factoids_table SET value='$(module_sqlite3_clean_string "$2")', who='$(module_sqlite3_clean_string "$3")' WHERE name='$(module_sqlite3_clean_string "$1")';"
+}
+
+
+#---------------------------------------------------------------------
+## Remove an item
+## @Type Private
+## @param key
+#---------------------------------------------------------------------
+module_factoids_DELETE() {
+ module_sqlite3_exec_sql "DELETE FROM $config_module_factoids_table WHERE name='$(module_sqlite3_clean_string "$1")';"
+}
+
+
+#---------------------------------------------------------------------
+## How many factoids are there
+## @Type Private
+## @Stdout Count of factoids.
+#---------------------------------------------------------------------
+module_factoids_get_count() {
+ module_sqlite3_exec_sql "SELECT COUNT(name) FROM $config_module_factoids_table;"
+}
+
+
+#---------------------------------------------------------------------
+## How many locked factoids are there
+## @Type Private
+## @Stdout Count of locked factoids.
+#---------------------------------------------------------------------
+module_factoids_get_locked_count() {
+ module_sqlite3_exec_sql "SELECT COUNT(name) FROM $config_module_factoids_table WHERE is_locked='1';"
+}
+
+
+#---------------------------------------------------------------------
+## Check if factoid is locked or not.
+## @Type Private
+## @param key
+## @return 0 locked
+## @return 1 not locked
+#---------------------------------------------------------------------
+module_factoids_is_locked() {
+ local lock="$(module_sqlite3_exec_sql "SELECT is_locked FROM $config_module_factoids_table WHERE name='$(module_sqlite3_clean_string "$1")';")"
+ if [[ $lock == "1" ]]; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Lock a factoid against changes from non-owners
+## @Type Private
+## @param key
+#---------------------------------------------------------------------
+module_factoids_lock() {
+ module_sqlite3_exec_sql "UPDATE $config_module_factoids_table SET is_locked='1' WHERE name='$(module_sqlite3_clean_string "$1")';"
+}
+
+
+#---------------------------------------------------------------------
+## Unlock a factoid from protection against non-owners
+## @Type Private
+## @param key
+#---------------------------------------------------------------------
+module_factoids_unlock() {
+ module_sqlite3_exec_sql "UPDATE $config_module_factoids_table SET is_locked='0' WHERE name='$(module_sqlite3_clean_string "$1")';"
+}
+
+
+#---------------------------------------------------------------------
+## Wrapper, call either INSERT or UPDATE
+## @Type Private
+## @param key
+## @param value
+## @param hostmask of person set it
+#---------------------------------------------------------------------
+module_factoids_set_INSERT_or_UPDATE() {
+ if [[ $(module_factoids_SELECT "$1") ]]; then
+ module_factoids_UPDATE "$1" "$2" "$3"
+ else
+ module_factoids_INSERT "$1" "$2" "$3"
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Wrapper, call either INSERT or UPDATE
+## @Type Private
+## @param key
+## @param value
+## @param sender
+## @param channel
+#---------------------------------------------------------------------
+module_factoids_set() {
+ local key="$1"
+ local value="$2"
+ local sender="$3"
+ local channel="$4"
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ if module_factoids_is_locked "$key"; then
+ if access_check_capab "factoid_admin" "$sender" "GLOBAL"; then
+ module_factoids_set_INSERT_or_UPDATE "$key" "$value" "$sender"
+ send_msg "$channel" "Ok ${sendernick}, I will remember, $key is $value"
+ else
+ access_fail "$sender" "change a locked factoid" "factoid_admin"
+ fi
+ else
+ module_factoids_set_INSERT_or_UPDATE "$key" "$value" "$sender"
+ send_msg "$channel" "Ok ${sendernick}, I will remember, $key is $value"
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Wrapper, check access
+## @Type Private
+## @param key
+## @param sender
+## @param channel
+#---------------------------------------------------------------------
+module_factoids_remove() {
+ local key="$1"
+ local sender="$2"
+ local channel="$3"
+ local value="$(module_factoids_SELECT "$(tr '[:upper:]' '[:lower:]' <<< "$key")")"
+ if [[ "$value" ]]; then
+ if module_factoids_is_locked "$key"; then
+ if access_check_capab "factoid_admin" "$sender" "GLOBAL"; then
+ module_factoids_DELETE "$key"
+ send_msg "$channel" "I forgot $key"
+ else
+ access_fail "$sender" "remove a locked factoid" "factoid_admin"
+ fi
+ else
+ module_factoids_DELETE "$key"
+ send_msg "$channel" "I forgot $key"
+ fi
+ else
+ send_msg "$channel" "I didn't have a factoid matching \"$key\""
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Send the factoid:
+## @Type Private
+## @param To where (channel or nick)
+## @param What factoid.
+#---------------------------------------------------------------------
+module_factoids_send_factoid() {
+ local channel="$1"
+ local key="$2"
+ local value="$(module_factoids_SELECT "$(tr '[:upper:]' '[:lower:]' <<< "$key")")"
+ if [[ "$value" ]]; then
+ if [[ $value =~ ^\<REPLY\>\ *(.*) ]]; then
+ send_msg "$channel" "${BASH_REMATCH[1]}"
+ elif [[ $value =~ ^\<ACTION\>\ *(.*) ]]; then
+ send_ctcp "$channel" "ACTION ${BASH_REMATCH[1]}"
+ else
+ send_msg "$channel" "$key is $value"
+ fi
+ else
+ send_msg "$channel" "I don't know what \"$key\" is."
+ fi
+}
+
+
+#---------------------------------------------------------------------
+## Parse assignment:
+## @Type Private
+## @param String to parse
+## @Note Will return using Global variables
+## @Globals $module_factoids_parse_key $module_factoids_parse_value
+#---------------------------------------------------------------------
+module_factoids_parse_assignment() {
+ local word key value
+ # Have we hit a separator yet?
+ local state=0
+ while read -rd ' ' word; do
+ case "$state" in
+ 0)
+ # If state is 1 the rest is value
+ if [[ "$word" =~ ^(as|is|are|=)$ ]]; then
+ state=1
+ else
+ key+=" $word"
+ fi
+ ;;
+ 1)
+ value+=" $word"
+ ;;
+ esac
+ # Extra space at end is intended, to make read work correctly.
+ done <<< "$1 "
+ # And clean spaces, fastest way
+ read -ra module_factoids_parse_key <<< "$key"
+ read -ra module_factoids_parse_value <<< "$value"
+}
+
+
+module_factoids_handler_learn() {
+ local sender="$1"
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ local channel="$2"
+ if ! [[ $2 =~ ^# ]]; then
+ channel="$sendernick"
+ fi
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(.+)\ (as|is|are|=)\ (.+) ]]; then
+ # Do the actual parsing elsewhere:
+ module_factoids_parse_assignment "$parameters"
+ local key="${module_factoids_parse_key[*]}"
+ local value="${module_factoids_parse_value[*]}"
+ unset module_factoids_parse_key module_factoids_parse_value
+ module_factoids_set "$(tr '[:upper:]' '[:lower:]' <<< "$key")" "$value" "$sender" "$channel"
+ else
+ feedback_bad_syntax "$sendernick" "learn" "<key> (as|is|are|=) <value>"
+ fi
+ return 1
+}
+
+module_factoids_handler_forget() {
+ local sender="$1"
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ local channel="$2"
+ if ! [[ $2 =~ ^# ]]; then
+ channel="$sendernick"
+ fi
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(.+) ]]; then
+ local key="${BASH_REMATCH[1]}"
+ module_factoids_remove "$(tr '[:upper:]' '[:lower:]' <<< "$key")" "$sender" "$channel"
+ else
+ feedback_bad_syntax "$sendernick" "forget" "<key>"
+ fi
+}
+
+module_factoids_handler_lock_factoid() {
+ local sender="$1"
+ if access_check_capab "factoid_admin" "$sender" "GLOBAL"; then
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ local channel="$2"
+ if ! [[ $2 =~ ^# ]]; then
+ channel="$sendernick"
+ fi
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(.+) ]]; then
+ local key="${BASH_REMATCH[1]}"
+ module_factoids_lock "$(tr '[:upper:]' '[:lower:]' <<< "$key")"
+ send_msg "$channel" "Ok ${sendernick}, the factoid \"$key\" is now protected from changes"
+ else
+ feedback_bad_syntax "$sendernick" "lock" "<key>"
+ fi
+ else
+ access_fail "$sender" "lock a factoid" "factoid_admin"
+ fi
+}
+
+module_factoids_handler_unlock_factoid() {
+ local sender="$1"
+ if access_check_capab "factoid_admin" "$sender" "GLOBAL"; then
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ local channel="$2"
+ if ! [[ $2 =~ ^# ]]; then
+ channel="$sendernick"
+ fi
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(.+) ]]; then
+ local key="${BASH_REMATCH[1]}"
+ module_factoids_unlock "$(tr '[:upper:]' '[:lower:]' <<< "$key")"
+ send_msg "$channel" "Ok ${sendernick}, the factoid \"$key\" is no longer protected from changes"
+ else
+ feedback_bad_syntax "$sendernick" "lock" "<key>"
+ fi
+ else
+ access_fail "$sender" "lock a factoid" "factoid_admin"
+ fi
+}
+
+module_factoids_handler_whatis() {
+ local sender="$1"
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ local channel="$2"
+ if ! [[ $2 =~ ^# ]]; then
+ channel="$sendernick"
+ fi
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(.+) ]]; then
+ local key="${BASH_REMATCH[1]}"
+ module_factoids_send_factoid "$channel" "$key"
+ else
+ feedback_bad_syntax "$sendernick" "whatis" "<key>"
+ fi
+}
+
+module_factoids_handler_factoid_stats() {
+ local sender="$1"
+ local channel="$2"
+ if ! [[ $2 =~ ^# ]]; then
+ parse_hostmask_nick "$sender" 'channel'
+ fi
+ local count="$(module_factoids_get_count)"
+ local lockedcount="$(module_factoids_get_locked_count)"
+ if [[ "$count" ]]; then
+ send_msg "$channel" "There are $count items in my factoid database. $lockedcount of the factoids are locked."
+ fi
+}
+
+module_factoids_on_PRIVMSG() {
+ local sender="$1"
+ local channel="$2"
+ if ! [[ $2 =~ ^# ]]; then
+ parse_hostmask_nick "$sender" 'channel'
+ fi
+ local query="$3"
+ # Answer question in channel if we got a factoid.
+ if [[ "$query" =~ ^((what|where|who|why|how)\ )?((is|are|were|was|to|can I find)\ )?([^\?]+)\?? ]]; then
+ local key="${BASH_REMATCH[@]: -1}"
+ local value="$(module_factoids_SELECT "$(tr '[:upper:]' '[:lower:]' <<< "$key")")"
+ if [[ "$value" ]]; then
+ module_factoids_send_factoid "$channel" "$key"
+ return 1
+ fi
+ fi
+ return 0
+}
diff --git a/modules/m_faq.sh b/modules/m_faq.sh
new file mode 100644
index 0000000..f1557dc
--- /dev/null
+++ b/modules/m_faq.sh
@@ -0,0 +1,138 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Simple FAQ module
+#---------------------------------------------------------------------
+
+module_faq_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_load'
+ commands_register "$1" 'faq' || return 1
+ helpentry_module_faq_description="FAQ from a file."
+
+ helpentry_faq_faq_syntax='[number|string]'
+ helpentry_faq_faq_description='Show the <number>th faq item or search for a <string> in all the faq items.'
+}
+
+module_faq_UNLOAD() {
+ unset module_faq_array module_faq_last_query_item
+ unset module_faq_load module_faq_last_query_time
+}
+
+module_faq_REHASH() {
+ module_faq_load
+}
+
+#---------------------------------------------------------------------
+## Load or reload FAQ items
+## @Type Private
+#---------------------------------------------------------------------
+module_faq_load() {
+ local i=0
+ unset module_faq_array
+ if [[ -z "$config_module_faq_file" ]]; then
+ log_error "faq module: You need to set config_module_faq_file in your config!"
+ return 1
+ elif [[ -r "$config_module_faq_file" ]]; then
+ while read -d $'\n' line ;do
+ # Skip empty lines
+ if [[ "$line" ]]; then
+ (( i++ ))
+ module_faq_array[$i]="$line"
+ fi
+ done < "${config_module_faq_file}"
+ log_info 'Loaded FAQ items'
+ return 0
+ else
+ log_error "faq module: Cannot load '${config_module_faq_file}'. File doesn't exist or can't be read."
+ return 1
+ fi
+}
+
+# Called after module has loaded.
+module_faq_after_load() {
+ module_faq_last_query_item='null'
+ module_faq_last_query_time='null'
+ module_faq_load
+}
+
+# Called on a PRIVMSG
+#
+# $1 = from who (n!u@h)
+# $2 = to who (channel or botnick)
+# $3 = the message
+module_faq_handler_faq() {
+ local sender="$1"
+ local channel="$2"
+ # If it isn't in a channel send message back to person who send it,
+ # otherwise send in channel
+ if ! [[ $2 =~ ^# ]]; then
+ parse_hostmask_nick "$sender" 'channel'
+ fi
+ local query="$3"
+ if [[ "$query" ]]; then
+ if [[ "$query" == "reload" ]]; then
+ if access_check_capab "faq_admin" "$sender" "GLOBAL"; then
+ send_msg "$channel" "Reloading FAQ items..."
+ module_faq_load
+ send_msg "$channel" "Done."
+ else
+ access_fail "$sender" "reload faq items" "faq_admin"
+ fi
+ return 0
+ fi
+ # Is it a flood? Then 1.
+ local ok=0
+ if [[ "$module_faq_last_query_item" == "$line" ]]; then
+ time_check_interval "$module_faq_last_query_time" 60 || ok=1
+ fi
+ if [[ $ok -eq 0 ]] ; then # Must be at least 1 min old or different query...
+ time_get_current 'module_faq_last_query_time'
+ # Update anti-flood variables
+ module_faq_last_query_item="$line"
+ module_faq_last_query="$query_time"
+
+ if [[ "$query" =~ ^\ *([0-9]+)\ *$ ]]; then
+ local index="${BASH_REMATCH[1]}"
+ if [[ "${module_faq_array[$index]}" ]]; then
+ send_msg "$channel" "${module_faq_array[$index]}"
+ else
+ send_msg "$channel" "That FAQ item doesn't exist"
+ fi
+ # Check length of search to be at least 3 chars
+ elif [[ "${#query}" -ge 3 ]] ; then
+ local i=0
+ while [[ $i -lt "${#module_faq_array[*]}" ]] ; do
+ (( i++ ))
+ # FIXME: This code is hard to read.
+ # This module needs rewriting...
+ if grep -qiFm 1 "$query" <<< "${module_faq_array[$i]}" ; then
+ send_msg "$channel" "${module_faq_array[$i]}"
+ break 1
+ fi
+ done
+ fi
+ else
+ log_error "FLOOD DETECTED in FAQ module"
+ fi
+ fi
+}
diff --git a/modules/m_help.sh b/modules/m_help.sh
new file mode 100644
index 0000000..3fdd9b9
--- /dev/null
+++ b/modules/m_help.sh
@@ -0,0 +1,151 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# Copyright (C) 2007-2008 Vsevolod Kozlov #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Provides help command.
+#---------------------------------------------------------------------
+
+module_help_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'help' || return 1
+ commands_register "$1" 'modinfo' || return 1
+ helpentry_module_help_description="Provides help and information for commands and modules."
+
+ helpentry_help_help_syntax='<command>'
+ helpentry_help_help_description='Displays help for command <command>'
+
+ helpentry_help_modinfo_syntax='<module>'
+ helpentry_help_modinfo_description='Displays a description for module <module>'
+}
+
+module_help_UNLOAD() {
+ unset module_help_fetch_module_function_data
+ unset module_help_fetch_module_data
+}
+
+module_help_REHASH() {
+ return 0
+}
+
+module_help_fetch_module_function_data() {
+ local module_name="$1"
+ local function_name="$2"
+ local target_syntax="$3"
+ local target_description="$4"
+
+ local varname_syntax="helpentry_${module_name}_${function_name}_syntax"
+ local varname_description="helpentry_${module_name}_${function_name}_description"
+ if [[ -z ${!varname_description} ]]; then
+ return 1
+ fi
+
+ printf -v "$target_description" '%s' "${!varname_description}"
+
+ if [[ ${!varname_syntax} ]]; then
+ printf -v "$target_syntax" '%s' " ${!varname_syntax}"
+ fi
+}
+
+module_help_fetch_module_data() {
+ local module_name="$1"
+ local target_description="$2"
+
+ local varname_description="helpentry_module_${module_name}_description"
+ if [[ -z ${!varname_description} ]]; then
+ return 1
+ fi
+
+ printf -v "$target_description" '%s' "${!varname_description}"
+}
+
+module_help_handler_help() {
+ local sender="$1"
+ local parameters="$3"
+ if [[ $parameters =~ ^([a-zA-Z0-9][^ ]*)( [^ ]+)? ]]; then
+ local command_name="${BASH_REMATCH[1]}${BASH_REMATCH[2]}"
+ # Look where we will reply to. We will not reply in the channel, even if the request was made in a channel, unless appropriate option is set
+ local target
+ if [[ $2 =~ ^# && $config_module_help_reply_in_channel == 1 ]]; then
+ target="$2"
+ else
+ parse_hostmask_nick "$sender" 'target'
+ fi
+ # Get the module name the command belongs to.
+ local module_name=
+ commands_provides "$command_name" 'module_name'
+ # Extract the function name.
+ local function_name=
+ hash_get 'commands_list' "$command_name" 'function_name'
+ if [[ $function_name =~ ^module_${module_name}_handler_(.+)$ ]]; then
+ function_name="${BASH_REMATCH[1]}"
+ fi
+ # Finally get the data for a specific function in specific module.
+ local syntax=
+ local description=
+ module_help_fetch_module_function_data "$module_name" "$function_name" syntax description || {
+ send_notice "$target" "Sorry, no help for ${format_bold}${command_name}${format_bold}"
+ return
+ }
+ # And send it back to the user.
+ if [[ $config_module_help_reply_in_one_line == 1 ]]; then
+ send_notice "$target" "${format_bold}${command_name}${format_bold}$syntax -- $description"
+ else
+ send_notice "$target" "${format_bold}${command_name}${format_bold}$syntax"
+ send_notice "$target" "$description"
+ fi
+ else
+ local sendernick=
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "help" "<command>"
+ fi
+}
+
+module_help_handler_modinfo() {
+ local sender="$1"
+ local parameters="$3"
+ if [[ $parameters =~ ^([^ ]+) ]]; then
+ local module_name="${BASH_REMATCH[1]}"
+ # See module_help_handler_help
+ local target
+ if [[ $2 =~ ^# && $config_module_help_reply_in_channel == 1 ]]; then
+ target="$2"
+ else
+ parse_hostmask_nick "$sender" 'target'
+ fi
+ local description=
+ module_help_fetch_module_data "$module_name" description || {
+ send_notice "$target" "Sorry, no information for module ${format_bold}${module_name}${format_bold}"
+ return
+ }
+ if [[ $config_module_help_reply_in_one_line == 1 ]]; then
+ send_notice "$target" "${format_bold}${module_name}${format_bold} -- $description"
+ else
+ send_notice "$target" "${format_bold}${module_name}${format_bold}"
+ send_notice "$target" "$description"
+ fi
+ else
+ local sendernick=
+ parse_hostmask_nick "$sender" sendernick
+ feedback_bad_syntax "$sendernick" "modinfo" "<module>"
+ fi
+}
diff --git a/modules/m_join.sh b/modules/m_join.sh
new file mode 100644
index 0000000..3151eef
--- /dev/null
+++ b/modules/m_join.sh
@@ -0,0 +1,92 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# Copyright (C) 2007-2008 EmErgE <halt.system@gmail.com> #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Join/part
+#---------------------------------------------------------------------
+
+module_join_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'join' || return 1
+ commands_register "$1" 'part' || return 1
+ helpentry_module_join_description="Join/part commands."
+
+ helpentry_join_join_syntax='<#channel> [<key>]'
+ helpentry_join_join_description='Join a <#channel>, with an optional channel <key>.'
+
+ helpentry_join_part_syntax='<#channel> [<reason>]"'
+ helpentry_join_part_description='Part a <#channel> with an optional <reason>.'
+}
+
+module_join_UNLOAD() {
+ return 0
+}
+
+module_join_REHASH() {
+ return 0
+}
+
+module_join_handler_part() {
+ local sender="$1"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)(\ (.+))? ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local reason="${BASH_REMATCH[3]}"
+ if access_check_capab "join" "$sender" "$channel"; then
+ if [[ -z "$reason" ]]; then
+ channels_part "$channel"
+ else
+ channels_part "$channel" "$reason"
+ fi
+ else
+ access_fail "$sender" "make the bot part channel" "join"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "part" "<#channel> [<reason>]"
+ fi
+}
+
+module_join_handler_join() {
+ local sender="$1"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)(\ [^ ]+)? ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local key="${BASH_REMATCH[2]}"
+ if access_check_capab "join" "$sender" "$channel"; then
+ key="${key## }"
+ if [[ -z "$key" ]]; then
+ channels_join "${channel}"
+ else
+ channels_join "${channel}" "$key"
+ fi
+ else
+ access_fail "$sender" "make the join channel" "join"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "join" "<#channel> [<key>]"
+ fi
+}
diff --git a/modules/m_karma.sh b/modules/m_karma.sh
new file mode 100644
index 0000000..8d2aed8
--- /dev/null
+++ b/modules/m_karma.sh
@@ -0,0 +1,252 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Karma module
+#---------------------------------------------------------------------
+
+module_karma_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_load on_PRIVMSG'
+ commands_register "$1" 'karma' || return 1
+ helpentry_module_karma_description="Provides karma support. Use ++ and -- after a string in a channel to change karma."
+ helpentry_karma_karma_syntax='<string>'
+ helpentry_karma_karma_description='Get current karma for <string>.'
+}
+
+module_karma_UNLOAD() {
+ unset module_karma_SELECT
+ unset module_karma_INSERT module_karma_UPDATE module_karma_set_INSERT_or_UPDATE
+ unset module_karma_substract module_karma_add module_karma_check
+ unset module_karma_is_nick
+ unset module_karma_check_return
+ return 0
+}
+
+module_karma_REHASH() {
+ return 0
+}
+
+module_karma_after_load() {
+ modules_depends_register "karma" "sqlite3" || {
+ # This error reporting is hackish, will fix later.
+ if ! list_contains "modules_loaded" "sqlite3"; then
+ log_error "The karma module depends upon the SQLite3 module being loaded."
+ fi
+ return 1
+ }
+ if [[ -z $config_module_karma_table ]]; then
+ log_error "Karma table (config_module_karma_table) must be set in config to use the karma module."
+ return 1
+ fi
+ if ! module_sqlite3_table_exists "$config_module_karma_table"; then
+ log_error "karma module: $config_module_karma_table does not exist in the database file."
+ log_error "karma module: See comment in doc/karma.sql for how to create the table."
+ fi
+}
+
+#---------------------------------------------------------------------
+## Get an item from DB
+## @Type Private
+## @param key
+## @Stdout The result of the database query.
+#---------------------------------------------------------------------
+module_karma_SELECT() {
+ module_sqlite3_exec_sql "SELECT rating FROM $config_module_karma_table WHERE target='$(module_sqlite3_clean_string "$1")';"
+}
+
+
+#---------------------------------------------------------------------
+## Insert a new item into DB
+## @Type Private
+## @param key
+## @param karma
+#---------------------------------------------------------------------
+module_karma_INSERT() {
+ module_sqlite3_exec_sql \
+ "INSERT INTO $config_module_karma_table (target, rating) VALUES('$(module_sqlite3_clean_string "$1")', '$(module_sqlite3_clean_string "$2")');"
+}
+
+
+#---------------------------------------------------------------------
+## Change the item in DB
+## @Type Private
+## @param key
+## @param karma
+#---------------------------------------------------------------------
+module_karma_UPDATE() {
+ module_sqlite3_exec_sql \
+ "UPDATE $config_module_karma_table SET rating='$(module_sqlite3_clean_string "$2")' WHERE target='$(module_sqlite3_clean_string "$1")';"
+}
+
+#---------------------------------------------------------------------
+## Wrapper, call either INSERT or UPDATE
+## @Type Private
+## @param key
+## @param karma
+#---------------------------------------------------------------------
+module_karma_set_INSERT_or_UPDATE() {
+ if [[ $(module_karma_SELECT "$1") ]]; then
+ module_karma_UPDATE "$1" "$2"
+ else
+ module_karma_INSERT "$1" "$2"
+ fi
+}
+
+#---------------------------------------------------------------------
+## Remove 1 from key
+## @Type Private
+## @param key to remove from.
+#---------------------------------------------------------------------
+module_karma_substract() {
+ # Clean spaces and convert to lower case
+ local keyarray
+ read -ra keyarray <<< "$1"
+ local key="$(tr '[:upper:]' '[:lower:]' <<< "${keyarray[*]}")"
+ local old="$(module_karma_SELECT "$key")"
+ # -1 + any old value (yes looks backwards but works)
+ local new=-1
+ if [[ "$old" ]]; then
+ (( new += old ))
+ fi
+ module_karma_set_INSERT_or_UPDATE "$key" "$new"
+}
+
+#---------------------------------------------------------------------
+## Add 1 from key
+## @Type Private
+## @param key to add to.
+#---------------------------------------------------------------------
+module_karma_add() {
+ # Clean spaces and convert to lower case
+ local keyarray
+ read -ra keyarray <<< "$1"
+ local key="$(tr '[:upper:]' '[:lower:]' <<< "${keyarray[*]}")"
+ local old="$(module_karma_SELECT "$key")"
+ # 1 + any old value
+ local new=1
+ if [[ "$old" ]]; then
+ (( new += old ))
+ fi
+ module_karma_set_INSERT_or_UPDATE "$key" "$new"
+}
+
+#---------------------------------------------------------------------
+## Return karma value for key
+## The result is returned in $module_karma_check_return
+## @Type Private
+## @param key to return karma for
+## @Globals $module_karma_check_return
+#---------------------------------------------------------------------
+module_karma_check() {
+ # Clean spaces and convert to lower case
+ local keyarray
+ read -ra keyarray <<< "$1"
+ local key="$(tr '[:upper:]' '[:lower:]' <<< "${keyarray[*]}")"
+ module_karma_check_return="$(module_karma_SELECT "$key")"
+ if [[ -z "$module_karma_check_return" ]]; then
+ module_karma_check_return=0
+ fi
+}
+
+#---------------------------------------------------------------------
+## Check if the key is the nick of sender.
+## @Type Private
+## @param key
+## @param sender
+## @return 0 If nick and key are same
+## @return 1 Otherwise
+#---------------------------------------------------------------------
+module_karma_is_nick() {
+ local keyarray
+ read -ra keyarray <<< "$1"
+ local key="$(tr '[:upper:]' '[:lower:]' <<< "${keyarray[*]}")"
+ local sendernick
+ parse_hostmask_nick "$2" 'sendernick'
+ local nickarray
+ read -ra nickarray <<< "$(tr '[:upper:]' '[:lower:]' <<< "$sendernick")"
+ local nick="${nickarray[*]}"
+ if [[ "$key" = "$nick" ]]; then
+ return 0
+ fi
+ return 1
+}
+
+
+# Called on a PRIVMSG
+#
+# $1 = from who (n!u@h)
+# $2 = to who (channel or botnick)
+# $3 = the message
+module_karma_on_PRIVMSG() {
+ local sender="$1"
+ local query="$3"
+ local sendon_channel
+ # If it isn't in a channel send message back to person who sent it,
+ # otherwise send in channel
+ if [[ $2 =~ ^# ]]; then
+ sendon_channel="$2"
+ # An item must begin with an alphanumeric char.
+ if [[ "$query" =~ ^([a-zA-Z0-9].*)\+\+$ ]]; then
+ local key="${BASH_REMATCH[1]}"
+ if module_karma_is_nick "$key" "$sender"; then
+ send_msg "$sendon_channel" "You can't change karma of yourself."
+ else
+ module_karma_add "$key"
+ fi
+ elif [[ "$query" =~ ^([a-zA-Z0-9].*)--$ ]]; then
+ local key="${BASH_REMATCH[1]}"
+ if module_karma_is_nick "$key" "$sender"; then
+ send_msg "$sendon_channel" "You can't change karma of yourself."
+ else
+ module_karma_substract "$key"
+ fi
+ fi
+ else
+ parse_hostmask_nick "$sender" 'sendon_channel'
+ # Karma is only possible in channels
+ if [[ "$query" =~ ^[a-zA-Z0-9].*(--|\+\+)$ ]]; then
+ send_notice "$sendon_channel" "You can only change karma in channels."
+ return 1
+ fi
+ fi
+ return 0
+}
+
+module_karma_handler_karma() {
+ local sender="$1"
+ local sendon_channel
+ if [[ $2 =~ ^# ]]; then
+ sendon_channel="$2"
+ else
+ parse_hostmask_nick "$sender" 'sendon_channel'
+ fi
+ local parameters="$3"
+ if [[ $parameters =~ ^(.+)$ ]]; then
+ local key="${BASH_REMATCH[1]}"
+ module_karma_check "$key"
+ send_msg "$sendon_channel" "Karma for $key is $module_karma_check_return"
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "karma" "<string>"
+ fi
+}
diff --git a/modules/m_kick_ban.sh b/modules/m_kick_ban.sh
new file mode 100644
index 0000000..d234c62
--- /dev/null
+++ b/modules/m_kick_ban.sh
@@ -0,0 +1,145 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# Copyright (C) 2007-2008 EmErgE <halt.system@gmail.com> #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Kicking and banning.
+#---------------------------------------------------------------------
+
+module_kick_ban_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_load after_connect on_numeric'
+ unset module_kick_ban_next_unset module_kick_ban_timed_bans
+ commands_register "$1" 'kick' || return 1
+ commands_register "$1" 'ban' || return 1
+ helpentry_module_kick_ban_description="Provides kick and ban commands."
+
+ helpentry_kick_ban_kick_syntax='[<#channel>] <nick> <reason>'
+ helpentry_kick_ban_kick_description='Kick someone from a channel. Channel parameter only needed if not sent in a channel.'
+
+ helpentry_kick_ban_ban_syntax='<#channel> <nick> [<duration>]'
+ helpentry_kick_ban_ban_description='Ban someone from a channel. Duration is optional and defaults to infinite.'
+}
+
+module_kick_ban_UNLOAD() {
+ unset module_kick_ban_TBAN_supported
+}
+
+module_kick_ban_REHASH() {
+ return 0
+}
+
+# Lets check if TBAN is supported
+# :photon.kuonet-ng.org 461 envbot TBAN :Not enough parameters.
+# :photon.kuonet-ng.org 304 envbot :SYNTAX TBAN <channel> <duration> <banmask>
+module_kick_ban_after_connect() {
+ module_kick_ban_TBAN_supported=0
+ send_raw "TBAN"
+}
+
+# HACK: If module is loaded after connect, module_kick_ban_after_connect won't
+# get called, therefore lets check if we are connected here and check for
+# TBAN here if that is the case.
+module_kick_ban_after_load() {
+ if [[ $server_connected -eq 1 ]]; then
+ module_kick_ban_TBAN_supported=0
+ send_raw "TBAN"
+ fi
+}
+
+module_kick_ban_on_numeric() {
+ if [[ $1 == $numeric_ERR_NEEDMOREPARAMS ]]; then
+ if [[ "$2" =~ ^TBAN\ : ]]; then
+ module_kick_ban_TBAN_supported=1
+ fi
+ fi
+}
+
+module_kick_ban_handler_kick() {
+ # Accept this anywhere, unless someone can give a good reason not to.
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ $parameters =~ ^((#[^ ]+)\ )(.*) ]]; then
+ local channel="${BASH_REMATCH[2]}"
+ parameters="${BASH_REMATCH[3]}"
+ else
+ if ! [[ $channel =~ ^# ]]; then
+ if [[ $sendon_channel =~ ^# ]]; then
+ local channel="$sendon_channel"
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "kick" "[<#channel>] <nick> <reason> # Channel must be send when the message is not sent in a channel"
+ return 0
+ fi
+ fi
+ fi
+
+ if [[ "$parameters" =~ ^([^ ]+)\ (.+) ]]; then
+ local nick="${BASH_REMATCH[1]}"
+ local kickmessage="${BASH_REMATCH[2]}"
+ if access_check_capab "kick" "$sender" "$channel"; then
+ send_raw "KICK $channel $nick :$kickmessage"
+ access_log_action "$sender" "kicked $nick from $channel with kick message: $kickmessage"
+ else
+ access_fail "$sender" "make the bot kick somebody" "kick"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "kick" "[<#channel>] <nick> <reason> # Channel must be send when the message is not sent in a channel"
+ fi
+}
+
+module_kick_ban_handler_ban() {
+ local sender="$1"
+ local sendon_channel="$2"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^(#[^ ]+)\ ([^ ]+)(\ ([0-9]+))? ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local nick="${BASH_REMATCH[2]}"
+ # Optional parameter.
+ local duration="${BASH_REMATCH[4]}"
+ if access_check_capab "ban" "$sender" "$channel"; then
+ if [[ $duration ]]; then
+ # send_modes "$channel" "+b" get_hostmask $nick <-- not implemented yet
+ if [[ $module_kick_ban_TBAN_supported -eq 1 ]]; then
+ send_raw "TBAN $channel $duration $nick"
+ else
+ send_modes "$channel" "+b $nick"
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ send_notice "$sendernick" "Sorry ban will not be timed, this IRCd didn't support TBAN command when I checked before."
+ fi
+ else
+ send_modes "$channel" "+b $nick"
+ fi
+ access_log_action "$sender" "banned $nick from $channel"
+ else
+ access_fail "$sender" "make the bot ban somebody" "ban"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "ban" "<#channel> <nick> [<duration>]"
+ fi
+}
diff --git a/modules/m_modules.sh b/modules/m_modules.sh
new file mode 100644
index 0000000..62b3917
--- /dev/null
+++ b/modules/m_modules.sh
@@ -0,0 +1,178 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Manage (load/unload/list) modules.
+#---------------------------------------------------------------------
+
+module_modules_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'modload' || return 1
+ commands_register "$1" 'modunload' || return 1
+ commands_register "$1" 'modreload' || return 1
+ commands_register "$1" 'modlist' || return 1
+ helpentry_module_modules_description="Exposes the internal module loading and unloading support to owners."
+
+ helpentry_modules_modload_syntax='<module name>'
+ helpentry_modules_modload_description='Try to load a module.'
+
+ helpentry_modules_modunload_syntax='<module name>'
+ helpentry_modules_modunload_description='Try to unload a module.'
+
+ helpentry_modules_modreload_syntax='<module name>'
+ helpentry_modules_modreload_description='Try to unload and reload a module.'
+
+ helpentry_modules_modlist_syntax=''
+ helpentry_modules_modlist_description='List currently loaded moudules.'
+
+}
+
+module_modules_UNLOAD() {
+ unset module_modules_doload module_modules_dounload
+}
+
+module_modules_REHASH() {
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Load a module
+## @param Module to load
+## @param Sender nick
+#---------------------------------------------------------------------
+module_modules_doload() {
+ local target_module="$1"
+ local sendernick="$2"
+ modules_load "$target_module"
+ local status_message status=$?
+ case $status in
+ 0) status_message="Loaded \"$target_module\" successfully" ;;
+ 2) status_message="Module \"$target_module\" is already loaded" ;;
+ 3) status_message="Failed to source \"$target_module\" in safe subshell, see log for details" ;;
+ 4) status_message="Failed to source \"$target_module\"" ;;
+ 5) status_message="Module \"$target_module\" could not be found" ;;
+ 6) status_message="Getting hooks from \"$target_module\" failed" ;;
+ 7) status_message="after_load failed for \"$target_module\", see log for details" ;;
+ *) status_message="Unknown error (code $status) for \"$target_module\"" ;;
+ esac
+ send_notice "$sendernick" "$status_message"
+ return $status
+}
+
+#---------------------------------------------------------------------
+## Unload a module
+## @param Module to unload
+## @param Sender nick
+#---------------------------------------------------------------------
+module_modules_dounload() {
+ local target_module="$1"
+ local sendernick="$2"
+ if [[ $target_module == modules ]]; then
+ send_msg "$sendernick" \
+ "You can't unload/reload the modules module using itself. (The hackish way would be to use the eval module for this.)"
+ return 1
+ fi
+ modules_unload "$target_module"
+ local status_message status=$?
+ case $status in
+ 0) status_message="Unloaded \"$target_module\" successfully" ;;
+ 2) status_message="Module \"$target_module\" is not loaded" ;;
+ 3) status_message="Module \"$target_module\" can't be unloaded, some these module(s) depend(s) on it: $(modules_depends_list_deps "$target_module")" ;;
+ *) status_message="Unknown error (code $status) for \"$target_module\"" ;;
+ esac
+ send_notice "$sendernick" "$status_message"
+ return $status
+}
+
+module_modules_handler_modload() {
+ # Accept this anywhere, unless someone can give a good reason not to.
+ local sender="$1"
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ local parameters="$3"
+ if [[ "$parameters" =~ ^([^ ]+) ]]; then
+ local target_module="${BASH_REMATCH[1]}"
+ if access_check_owner "$sender"; then
+ access_log_action "$sender" "loaded the module $target_module"
+ module_modules_doload "$target_module" "$sendernick"
+ else
+ access_fail "$sender" "load a module" "owner"
+ fi
+ else
+ feedback_bad_syntax "$sendernick" "modload" "<module name>"
+ fi
+}
+
+module_modules_handler_modunload() {
+ local sender="$1"
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ local parameters="$3"
+ if [[ "$parameters" =~ ^([^ ]+) ]]; then
+ local target_module="${BASH_REMATCH[1]}"
+ if access_check_owner "$sender"; then
+ access_log_action "$sender" "unloaded the module $target_module"
+ module_modules_dounload "$target_module" "$sendernick"
+ else
+ access_fail "$sender" "unload a module" "owner"
+ fi
+ else
+ feedback_bad_syntax "$sendernick" "modunload" "<module name>"
+ fi
+}
+
+module_modules_handler_modreload() {
+ local sender="$1"
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ local parameters="$3"
+ if [[ "$parameters" =~ ^([^ ]+) ]]; then
+ local target_module="${BASH_REMATCH[1]}"
+ if access_check_owner "$sender"; then
+ access_log_action "$sender" "reloaded the module $target_module"
+ module_modules_dounload "$target_module" "$sendernick"
+ if [[ $? = 0 ]]; then
+ module_modules_doload "$target_module" "$sendernick"
+ else
+ send_notice "$sendernick" "Reload of $target_module failed because it could not be unloaded."
+ fi
+ else
+ access_fail "$sender" "reload a module" "owner"
+ fi
+ else
+ feedback_bad_syntax "$sendernick" "modreload" "<module name>"
+ fi
+}
+
+module_modules_handler_modlist() {
+ local sender="$1"
+ local parameters="$3"
+ local target
+ if [[ $2 =~ ^# ]]; then
+ target="$2"
+ else
+ parse_hostmask_nick "$sender" 'target'
+ fi
+ local modlist="${modules_loaded## }"
+ modlist="${modlist%% }"
+ send_msg "$target" "${format_bold}Modules currently loaded${format_bold}: ${modlist// / }"
+}
diff --git a/modules/m_nicktracking.sh b/modules/m_nicktracking.sh
new file mode 100644
index 0000000..34b6873
--- /dev/null
+++ b/modules/m_nicktracking.sh
@@ -0,0 +1,316 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Provides nick tracking API for other modules.
+#---------------------------------------------------------------------
+
+module_nicktracking_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_load before_connect on_numeric on_NICK on_QUIT on_KICK on_PART on_JOIN'
+ helpentry_module_nicktracking_description="Provides nicktracking backend for other modules."
+}
+
+module_nicktracking_UNLOAD() {
+ unset module_nicktracking_channels
+ hash_reset module_nicktracking_channels_nicks
+ hash_reset module_nicktracking_nicks
+ # Private functions
+ unset module_nicktracking_clear_nick module_nicktracking_clear_chan
+ unset module_nicktracking_parse_names
+ unset module_nicktracking_add_channel_nick
+ unset module_nicktracking_remove_channel_nick
+ # API functions
+ unset module_nicktracking_get_hostmask_by_nick
+ unset module_nicktracking_get_channel_nicks
+ return 0
+}
+
+module_nicktracking_REHASH() {
+ return 0
+}
+
+
+#################
+# API functions #
+#################
+
+#---------------------------------------------------------------------
+## Return hostmask of a nick
+## @Type API
+## @param Nick to find hostmask for
+## @param Variable to return hostmask in
+## @Note If no nick is found (or data about the nick
+## @Note is missing currently), the return variable will be empty.
+#---------------------------------------------------------------------
+module_nicktracking_get_hostmask_by_nick() {
+ hash_get 'module_nicktracking_nicks' "$(tr '[:upper:]' '[:lower:]' <<< "$1")" "$2"
+}
+
+#---------------------------------------------------------------------
+## Return list of nicks on a channel
+## @Type API
+## @param Channel to check
+## @param Variable to return space separated list in
+## @return 0 Channel data exists.
+## @return 1 We don't track this channel.
+#---------------------------------------------------------------------
+module_nicktracking_get_channel_nicks() {
+ if list_contains 'module_nicktracking_channels' "$1"; then
+ hash_get 'module_nicktracking_channels_nicks' "$1" "$2"
+ return 0
+ else
+ return 1
+ fi
+}
+
+
+#####################
+# Private functions #
+#####################
+
+#---------------------------------------------------------------------
+## Check if a nick should be removed
+## @Type Private
+## @param Nick to check
+#---------------------------------------------------------------------
+module_nicktracking_clear_nick() {
+ # If not on a channel any more, remove knowledge about nick.
+ if ! hash_search 'module_nicktracking_channels_nicks' "$1"; then
+ hash_unset 'module_nicktracking_nicks' "$1"
+ fi
+}
+
+#---------------------------------------------------------------------
+## Clear a channel (if we part it or such)
+## @Type Private
+## @param Channel name
+#---------------------------------------------------------------------
+module_nicktracking_clear_chan() {
+ list_remove 'module_nicktracking_channels' "$1" 'module_nicktracking_channels'
+ # Get list and then unset it.
+ local nicks=
+ hash_get 'module_nicktracking_channels_nicks' "$1" 'nicks'
+ hash_unset 'module_nicktracking_channels_nicks' "$1"
+ # Sigh, this isn't fast I know...
+ local nick
+ for nick in $nicks; do
+ module_nicktracking_clear_nick "$nick"
+ done
+}
+
+#---------------------------------------------------------------------
+## Parse RPL_NAMREPLY data.
+## @Type Private
+## @param NAMES data
+#---------------------------------------------------------------------
+module_nicktracking_parse_names() {
+ if [[ $1 =~ ^[=*@]?\ *(#[^ ]+)\ +:(.+) ]]; then
+ local channel="${BASH_REMATCH[1]}"
+ local nicks="${BASH_REMATCH[2]}"
+ local entry nick realnick
+ # Loop through the entries
+ for entry in $nicks; do
+ # This will work both with and without NAMESX
+ if [[ $entry =~ [$server_PREFIX_prefixes]*([^ ]+) ]]; then
+ nick="${BASH_REMATCH[1]}"
+ # Is UHNAMES enabled?
+ # If yes lets take care of hostmask.
+ if [[ $server_UHNAMES -eq 1 ]]; then
+ parse_hostmask_nick "$nick" 'realnick'
+ realnick="$(tr '[:upper:]' '[:lower:]' <<< "$realnick")"
+ hash_set 'module_nicktracking_nicks' "$realnick" "$nick"
+ # Add to nick list of channel if not in list
+ hash_contains 'module_nicktracking_channels_nicks' "$channel" "$realnick" || \
+ hash_append 'module_nicktracking_channels_nicks' "$channel" "$realnick"
+ else
+ realnick="$(tr '[:upper:]' '[:lower:]' <<< "$nick")"
+ # Add to nick list of channel if not in list
+ hash_contains 'module_nicktracking_channels_nicks' "$channel" "$realnick" || \
+ hash_append 'module_nicktracking_channels_nicks' "$channel" "$realnick"
+ fi
+ else
+ log_error_file unknown_data.log "module_nicktracking_parse_names: Uh uh, regex for inner loop is bad, couldn't parse: $nick"
+ log_error_file unknown_data.log "module_nicktracking_parse_names: Please report a bug with the above message"
+ fi
+ done
+ else
+ log_error_file unknown_data.log "module_nicktracking_parse_names: Uh uh, outer regex is bad, couldn't parse: $1"
+ log_error_file unknown_data.log "module_nicktracking_parse_names: Please report a bug with the above message"
+ fi
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Parse RPL_WHOREPLY data.
+## @Type Private
+## @param WHO data
+#---------------------------------------------------------------------
+module_nicktracking_parse_who() {
+ # Read the who data into an array then extract the data from the array.
+ local whodata
+ read -ra whodata <<< "$1"
+ local channel="${whodata[0]}"
+ local ident="${whodata[1]}"
+ local host="${whodata[2]}"
+ local nick="${whodata[4]}"
+ local lowernick="$(tr '[:upper:]' '[:lower:]' <<< "$nick")"
+ # Set the hash tables
+ hash_set 'module_nicktracking_nicks' "$lowernick" "${nick}!${ident}@${host}"
+ # We don't want to add twice
+ hash_contains 'module_nicktracking_channels_nicks' "$channel" "$lowernick" || \
+ hash_append 'module_nicktracking_channels_nicks' "$channel" "$lowernick"
+}
+
+
+#---------------------------------------------------------------------
+## Add a nick to a channel
+## @Type Private
+## @param Channel
+## @param Hostmask
+## @param Nick
+#---------------------------------------------------------------------
+module_nicktracking_add_channel_nick() {
+ local nick="$(tr '[:upper:]' '[:lower:]' <<< "$3")"
+ hash_append 'module_nicktracking_channels_nicks' "$1" "$nick"
+ hash_set 'module_nicktracking_nicks' "$nick" "$2"
+}
+
+#---------------------------------------------------------------------
+## Remove a nick from a channel
+## @Type Private
+## @param Channel
+## @param Nick
+#---------------------------------------------------------------------
+module_nicktracking_remove_channel_nick() {
+ local nick="$(tr '[:upper:]' '[:lower:]' <<< "$2")"
+ hash_substract 'module_nicktracking_channels_nicks' "$1" "$nick"
+ module_nicktracking_clear_nick "$nick"
+}
+
+
+#########
+# Hooks #
+#########
+
+module_nicktracking_after_load() {
+ # Handle case of loading while bot is running
+ if [[ $server_connected -eq 1 ]]; then
+ module_nicktracking_channels="$channels_current"
+ local channel
+ for channel in $module_nicktracking_channels; do
+ send_raw "NAMES $channel"
+ # We have to send a WHO #channel if servers doesn't support UHNAMES.
+ if [[ $server_UHNAMES -eq 0 ]]; then
+ send_raw "WHO $channel"
+ fi
+ done
+ fi
+}
+
+module_nicktracking_before_connect() {
+ # Reset state.
+ unset module_nicktracking_channels
+ hash_reset module_nicktracking_channels_nicks
+ hash_reset module_nicktracking_nicks
+ return 0
+}
+
+
+##########################
+# Message handling hooks #
+##########################
+
+module_nicktracking_on_numeric() {
+ case $1 in
+ "$numeric_RPL_NAMREPLY")
+ # TODO: Parse NAMES
+ module_nicktracking_parse_names "$2"
+ ;;
+ "$numeric_RPL_WHOREPLY")
+ module_nicktracking_parse_who "$2"
+ ;;
+ esac
+}
+
+module_nicktracking_on_NICK() {
+ local oldnick oldident oldhost oldentry
+ parse_hostmask "$1" 'oldnick' 'oldident' 'oldhost'
+ local oldlowercase="$(tr '[:upper:]' '[:lower:]' <<< "$oldnick")"
+ local newlowercase="$(tr '[:upper:]' '[:lower:]' <<< "$2")"
+ # Remove old and add new.
+ hash_get 'module_nicktracking_nicks' "$oldlowercase" 'oldentry'
+ hash_unset 'module_nicktracking_nicks' "$oldlowercase"
+ hash_set 'module_nicktracking_nicks' "$newlowercase" "${2}!${oldident}@${oldhost}"
+ local channel
+ # Loop through the channels
+ for channel in $module_nicktracking_channels; do
+ hash_replace 'module_nicktracking_channels_nicks' "$channel" "$oldnick" "$newlowercase"
+ done
+ return 0
+}
+
+module_nicktracking_on_QUIT() {
+ local whoquit=
+ parse_hostmask_nick "$1" 'whoquit'
+ local nick="$(tr '[:upper:]' '[:lower:]' <<< "$whoquit")"
+ hash_unset 'module_nicktracking_nicks' "$nick"
+ local channel
+ # Remove from channel
+ for channel in $module_nicktracking_channels; do
+ hash_substract 'module_nicktracking_channels_nicks' "$channel" "$nick"
+ done
+}
+
+module_nicktracking_on_KICK() {
+ local whogotkicked="$3"
+ if [[ $whogotkicked == $server_nick_current ]]; then
+ module_nicktracking_clear_chan "$2"
+ else
+ module_nicktracking_remove_channel_nick "$2" "$whogotkicked"
+ fi
+}
+
+module_nicktracking_on_PART() {
+ # Check if it was us
+ local whoparted=
+ parse_hostmask_nick "$1" 'whoparted'
+ if [[ $whoparted == $server_nick_current ]]; then
+ module_nicktracking_clear_chan "$2"
+ else
+ module_nicktracking_remove_channel_nick "$2" "$whoparted"
+ fi
+}
+
+module_nicktracking_on_JOIN() {
+ local whojoined=
+ parse_hostmask_nick "$1" 'whojoined'
+ if [[ $whojoined == $server_nick_current ]]; then
+ module_nicktracking_channels+=" $2"
+ hash_set 'module_nicktracking_channels_nicks' "$2" "$server_nick_current"
+ # We have to send a WHO #channel if servers doesn't support UHNAMES.
+ if [[ $server_UHNAMES -eq 0 ]]; then
+ send_raw "WHO $2"
+ fi
+ else
+ module_nicktracking_add_channel_nick "$2" "$1" "$whojoined"
+ fi
+}
diff --git a/modules/m_ping.sh b/modules/m_ping.sh
new file mode 100644
index 0000000..6dd1532
--- /dev/null
+++ b/modules/m_ping.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Keeps track of latency
+#---------------------------------------------------------------------
+
+# TODO: Redo with stored "on pong info".
+
+module_ping_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='on_PONG'
+ commands_register "$1" 'ping' || return 1
+ commands_register "$1" 'latency' || return 1
+ helpentry_module_ping_description="Provides latency tracking."
+
+ helpentry_ping_ping_syntax=''
+ helpentry_ping_ping_description='Respond to sender with "PONG!"'
+
+ helpentry_ping_latency_syntax=''
+ helpentry_ping_latency_description='Report current latency to server.'
+}
+
+module_ping_UNLOAD() {
+ return 0
+}
+
+module_ping_REHASH() {
+ return 0
+}
+
+module_ping_on_PONG() {
+ # Is data time_sender?
+ if [[ $3 =~ ([0-9]+)_(#?[A-Za-z0-9][^ ]+) ]]; then
+ local time="${BASH_REMATCH[1]}"
+ local target="${BASH_REMATCH[2]}"
+ local latency
+ (( latency = envbot_time - $time ))
+ local msg=
+ case $latency in
+ 0) msg="less than one second" ;;
+ 1) msg="1 second" ;;
+ *) msg="$latency seconds" ;;
+ esac
+ send_msg "$target" "Latency is $msg"
+ fi
+}
+
+module_ping_handler_ping() {
+ local target
+ local sender_nick
+ parse_hostmask_nick "$1" 'sender_nick'
+ if [[ $2 =~ ^# ]]; then
+ target="$2"
+ else
+ target="$sender_nick"
+ fi
+ send_msg "$target" "$sender_nick: PONG!"
+}
+
+module_ping_handler_latency() {
+ local target
+ if [[ $2 =~ ^# ]]; then
+ target="$2"
+ else
+ parse_hostmask_nick "$1" 'target'
+ fi
+ send_raw "PING :${envbot_time}_${target}"
+}
diff --git a/modules/m_quote.sh b/modules/m_quote.sh
new file mode 100644
index 0000000..51c4752
--- /dev/null
+++ b/modules/m_quote.sh
@@ -0,0 +1,85 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 EmErgE <halt.system@gmail.com> #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Quotes module
+#---------------------------------------------------------------------
+
+module_quote_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_load'
+ commands_register "$1" 'quote' || return 1
+ helpentry_module_quote_description="Provides command for random quotes from a file."
+
+ helpentry_quote_quote_syntax=''
+ helpentry_quote_quote_description='Return a random quote.'
+}
+
+module_quote_UNLOAD() {
+ unset module_quote_load
+ unset module_quote_quotes
+}
+
+module_quote_REHASH() {
+ module_quote_load
+}
+
+#---------------------------------------------------------------------
+## Load quotes from file
+## @Type Private
+#---------------------------------------------------------------------
+module_quote_load() {
+ local i=0 line
+ unset module_quote_quotes
+ if [[ -z "$config_module_quotes_file" ]]; then
+ log_error "quotes module: You need to set config_module_quotes_file in your config!"
+ return 1
+ elif [[ -r "$config_module_quotes_file" ]]; then
+ local IFS=$'\n'
+ module_quote_quotes=( $(<"${config_module_quotes_file}") )
+ unset IFS
+ log_info 'Loaded Quotes.'
+ return 0
+ else
+ log_error "quotes module: Quotes failed to load: Cannot load \"$config_module_quotes_file\". File doesn't exist."
+ return 1
+ fi
+}
+
+module_quote_after_load() {
+ # Return code from last command in a function
+ # will be return code for the function by default.
+ module_quote_load
+}
+
+module_quote_handler_quote() {
+ local sender="$1"
+ local channel="$2"
+ # If it isn't in a channel send message back to person who send it,
+ # otherwise send in channel
+ if ! [[ $2 =~ ^# ]]; then
+ parse_hostmask_nick "$sender" 'channel'
+ fi
+ local number="$RANDOM"
+ (( number %= ${#module_quote_quotes[*]} ))
+ send_msg "$channel" "${module_quote_quotes[$number]}"
+}
diff --git a/modules/m_rehash.sh b/modules/m_rehash.sh
new file mode 100644
index 0000000..4449512
--- /dev/null
+++ b/modules/m_rehash.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Rehashing
+#---------------------------------------------------------------------
+
+module_rehash_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'rehash' || return 1
+ helpentry_module_rehash_description="Exposes the internal rehash support to bot owners."
+
+ helpentry_rehash_rehash_syntax=''
+ helpentry_rehash_rehash_description='Reload configuration file.'
+}
+
+module_rehash_UNLOAD() {
+ unset module_rehash_dorehash
+}
+
+module_rehash_REHASH() {
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Rehash config
+## @Type Private
+## @param Sender
+#---------------------------------------------------------------------
+module_rehash_dorehash() {
+ local sender="$1" status_message
+ config_rehash
+ local status=$?
+ case $status in
+ 0) status_message="Rehash successful. (Also any loaded modules not listed in config have been unloaded.)" ;;
+ 2) status_message="The new config is not the same version as the bot. Rehash won't work." ;;
+ 3) status_message="Failed to source it, but the bot should not be in an undefined state." ;;
+ 4) status_message="Configuration validation on new config failed, but the bot should not be in an undefined state." ;;
+ 5) status_message="Failed to source it and the bot may be in an undefined state." ;;
+ *) status_message="Unknown error (code $status)" ;;
+ esac
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ send_notice "$sendernick" "$status_message"
+}
+
+module_rehash_handler_rehash() {
+ local sender="$1"
+ if access_check_owner "$sender"; then
+ access_log_action "$sender" "did a rehash"
+ module_rehash_dorehash "$sender"
+ else
+ access_fail "$sender" "load a module" "owner"
+ fi
+}
diff --git a/modules/m_say.sh b/modules/m_say.sh
new file mode 100644
index 0000000..104b30c
--- /dev/null
+++ b/modules/m_say.sh
@@ -0,0 +1,98 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Allow owners to make to bot say something
+#---------------------------------------------------------------------
+
+module_say_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'say' || return 1
+ commands_register "$1" 'act' || return 1
+ helpentry_module_say_description="Provides say and act commands."
+
+ helpentry_say_act_syntax='<target> <message>'
+ helpentry_say_act_description='Send a <message> to <target> (nick or channel).'
+
+ helpentry_say_act_syntax='<target> <message>'
+ helpentry_say_act_description='Peform the <message> as a /me to <target> (nick or channel).'
+}
+
+module_say_UNLOAD() {
+ return 0
+}
+
+module_say_REHASH() {
+ return 0
+}
+
+module_say_handler_say() {
+ local sender="$1"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^([^ ]+)\ (.+) ]]; then
+ local target="${BASH_REMATCH[1]}"
+ local message="${BASH_REMATCH[2]}"
+ local scope
+ # Is it a channel?
+ if [[ $target =~ ^# ]]; then
+ scope="$target"
+ else
+ scope="MSG"
+ fi
+ if access_check_capab "say" "$sender" "$scope"; then
+ access_log_action "$sender" "made the bot say \"$message\" in/to \"$target\""
+ send_msg "$target" "$message"
+ else
+ access_fail "$sender" "make the bot talk with say" "say"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "say" "<target> <message> # Where target is a nick or channel"
+ fi
+}
+
+module_say_handler_act() {
+ local sender="$1"
+ local parameters="$3"
+ if [[ "$parameters" =~ ^([^ ]+)\ (.+) ]]; then
+ local target="${BASH_REMATCH[1]}"
+ local message="${BASH_REMATCH[2]}"
+ local scope
+ # Is it a channel?
+ if [[ $target =~ ^# ]]; then
+ scope="$target"
+ else
+ scope="MSG"
+ fi
+ if access_check_capab "say" "$sender" "$scope"; then
+ access_log_action "$sender" "made the bot act \"$message\" in/to \"$target\""
+ send_ctcp "$target" "ACTION ${message}"
+ else
+ access_fail "$sender" "make the bot act" "say"
+ fi
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "act" "<target> <message> # Where target is a nick or channel"
+ fi
+}
diff --git a/modules/m_seen.sh b/modules/m_seen.sh
new file mode 100644
index 0000000..5ecfa8d
--- /dev/null
+++ b/modules/m_seen.sh
@@ -0,0 +1,209 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Simple seen module using SQLite3
+#---------------------------------------------------------------------
+
+module_seen_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_load on_PRIVMSG'
+ commands_register "$1" 'seen' || return 1
+ helpentry_module_seen_description="Provides last seen information."
+
+ helpentry_seen_seen_syntax='<nick>'
+ helpentry_seen_seen_description='Report when the bot last saw <nick>.'
+}
+
+module_seen_UNLOAD() {
+ unset module_seen_exec_sql module_seen_SELECT module_seen_INSERT module_seen_UPDATE
+ unset module_seen_set_INSERT_or_UPDATE
+ unset module_seen_store module_seen_find
+}
+
+module_seen_REHASH() {
+ return 0
+}
+
+
+# Called after module has loaded.
+module_seen_after_load() {
+ modules_depends_register "seen" "sqlite3" || {
+ # This error reporting is hackish, will fix later.
+ if ! list_contains "modules_loaded" "sqlite3"; then
+ log_error "The seen module depends upon the SQLite3 module being loaded."
+ fi
+ return 1
+ }
+ if [[ -z $config_module_seen_table ]]; then
+ log_error "\"Seen table\" (config_module_seen_table) must be set in config."
+ return 1
+ fi
+ if ! module_sqlite3_table_exists "$config_module_seen_table"; then
+ log_error "seen module: $config_module_seen_table does not exist in the database file."
+ log_error "seen module: See comment in doc/seen.sql for how to create the table."
+ fi
+}
+
+#---------------------------------------------------------------------
+## Get the data about nick
+## @Type Private
+## @param The nick
+## @Stdout The result of the database query.
+#---------------------------------------------------------------------
+module_seen_SELECT() {
+ module_sqlite3_exec_sql "SELECT timestamp, channel, message FROM $config_module_seen_table WHERE nick='$(module_sqlite3_clean_string "$1")';"
+}
+
+#---------------------------------------------------------------------
+## Insert a new item into DB
+## @Type Private
+## @param Nick
+## @param Channel
+## @param Timestamp
+## @param Query
+#---------------------------------------------------------------------
+module_seen_INSERT() {
+ module_sqlite3_exec_sql \
+ "INSERT INTO $config_module_seen_table (nick, channel, timestamp, message) VALUES('$(module_sqlite3_clean_string "$1")', '$(module_sqlite3_clean_string "$2")', '$(module_sqlite3_clean_string "$3")', '$(module_sqlite3_clean_string "$4")');"
+}
+
+#---------------------------------------------------------------------
+## Change the item in DB
+## @Type Private
+## @param Nick
+## @param Channel
+## @param Timestamp
+## @param Message
+#---------------------------------------------------------------------
+module_seen_UPDATE() {
+ module_sqlite3_exec_sql \
+ "UPDATE $config_module_seen_table SET channel='$(module_sqlite3_clean_string "$2")', timestamp='$(module_sqlite3_clean_string "$3")', message='$(module_sqlite3_clean_string "$4")' WHERE nick='$(module_sqlite3_clean_string "$1")';"
+}
+
+#---------------------------------------------------------------------
+## Wrapper, call either INSERT or UPDATE
+## @Type Private
+## @param Nick
+## @param Channel
+## @param Timestamp
+## @param Message
+#---------------------------------------------------------------------
+module_seen_set_INSERT_or_UPDATE() {
+ if [[ $(module_seen_SELECT "$1") ]]; then
+ module_seen_UPDATE "$1" "$2" "$3" "$4"
+ else
+ module_seen_INSERT "$1" "$2" "$3" "$4"
+ fi
+}
+
+#---------------------------------------------------------------------
+## Store a line
+## @Type Private
+## @param Sender
+## @param Channel
+## @param Timestamp
+## @param Query
+#---------------------------------------------------------------------
+module_seen_store() {
+ # Clean spaces, fastest way for this
+ local query
+ read -ra query <<< "$4"
+ local sendernick
+ parse_hostmask_nick "$1" 'sendernick'
+ module_seen_set_INSERT_or_UPDATE "$(echo -n "$sendernick" | tr '[:upper:]' '[:lower:]')" "$2" "$3" "${query[*]}"
+}
+
+#---------------------------------------------------------------------
+## Look up a nick and send info to a channel/nick
+## @Type Private
+## @param Sender
+## @param Channel
+## @param Nick to look up
+#---------------------------------------------------------------------
+module_seen_find() {
+ local sender="$1"
+ local channel="$2"
+ local nick="$(tr '[:upper:]' '[:lower:]' <<< "$3")"
+ local sender_nick=
+ parse_hostmask_nick "$sender" 'sender_nick'
+ # Classical ones. We just HAVE to do them.
+ if [[ "$nick" == "$(tr '[:upper:]' '[:lower:]' <<< "$server_nick_current")" ]]; then
+ send_msg "$channel" "$sender_nick, you found me!"
+ return 0
+ elif [[ "$nick" == "$(tr '[:upper:]' '[:lower:]' <<< "$sender_nick")" ]]; then
+ send_ctcp "$channel" "ACTION holds up a mirror for $sender_nick"
+ return 0
+ fi
+ local match="$(module_seen_SELECT "$nick")"
+ if [[ $match ]]; then
+ # So we got a match
+ # Lets use regex
+ if [[ $match =~ ([0-9]+)\|(#[^ |]+)\|(.*) ]]; then
+ local found_timestamp="${BASH_REMATCH[1]}"
+ local found_channel="${BASH_REMATCH[2]}"
+ local found_message="${BASH_REMATCH[3]}"
+ if [[ $found_message =~ ^ACTION\ (.*) ]]; then
+ found_message="* $3 ${BASH_REMATCH[1]}"
+ fi
+ local difference frmtdiff
+ time_get_current 'difference'
+ (( difference -= found_timestamp ))
+ time_format_difference "$difference" 'frmtdiff'
+ send_msg "$channel" "$3 was last seen $frmtdiff ago in $found_channel saying \"$found_message\""
+ fi
+ else
+ send_msg "$channel" "Sorry, I have not seen $3."
+ fi
+}
+
+module_seen_on_PRIVMSG() {
+ local sender="$1"
+ local channel="$2"
+ local query="$3"
+ # If in channel, store
+ if [[ $channel =~ ^# ]]; then
+ local now=
+ time_get_current 'now'
+ module_seen_store "$sender" "$channel" "$now" "$query"
+ # If not in channel respond to any commands in /msg
+ else
+ parse_hostmask_nick "$sender" 'channel'
+ fi
+}
+
+module_seen_handler_seen() {
+ local sender="$1"
+ local channel="$2"
+ if ! [[ $2 =~ ^# ]]; then
+ parse_hostmask_nick "$sender" 'channel'
+ fi
+ # Lets look up messages
+ local parameters="$3"
+ if [[ "$parameters" =~ ^([^ ]+) ]]; then
+ local nick="${BASH_REMATCH[1]}"
+ module_seen_find "$sender" "$channel" "$nick"
+ else
+ local sendernick
+ parse_hostmask_nick "$sender" 'sendernick'
+ feedback_bad_syntax "$sendernick" "seen" "<nick>"
+ fi
+}
diff --git a/modules/m_sendraw.sh b/modules/m_sendraw.sh
new file mode 100644
index 0000000..e94c083
--- /dev/null
+++ b/modules/m_sendraw.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Allow owners to make bot send any line.
+## THIS IS FOR DEBUGGING MAINLY.
+#---------------------------------------------------------------------
+
+module_sendraw_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'raw' || return 1
+ helpentry_module_sendraw_description="Provides raw command to send raw data."
+ helpentry_sendraw_raw_syntax='<line>'
+ helpentry_sendraw_raw_description='Send the <line> to the IRC server.'
+}
+
+module_sendraw_UNLOAD() {
+ return 0
+}
+
+module_sendraw_REHASH() {
+ return 0
+}
+
+module_sendraw_handler_raw() {
+ local sender="$1"
+ if access_check_capab "sendraw" "$sender" "GLOBAL"; then
+ local parameters="$3"
+ access_log_action "$sender" "make the bot send a raw line: $parameters"
+ send_raw "$parameters"
+ else
+ access_fail "$sender" "send a raw line" "sendraw"
+ fi
+}
diff --git a/modules/m_services.sh b/modules/m_services.sh
new file mode 100644
index 0000000..47d688e
--- /dev/null
+++ b/modules/m_services.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Identify to NickServ
+#---------------------------------------------------------------------
+
+module_services_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='on_connect after_load after_disconnect'
+ helpentry_module_services_description="Provides support for identifying with services."
+}
+
+module_services_UNLOAD() {
+ unset module_services_ghost module_services_nickserv_command
+}
+
+module_services_REHASH() {
+ return 0
+}
+
+module_services_after_load() {
+ module_services_ghost=0
+ if [[ $config_module_services_server_alias -eq 0 ]]; then
+ module_services_nickserv_command="PRIVMSG $config_module_services_nickserv_name :"
+ else
+ module_services_nickserv_command="$config_module_services_nickserv_name "
+ fi
+}
+
+# Called for each line on connect
+module_services_on_connect() {
+ local line="$1"
+ if [[ "$line" =~ ^:[^\ ]+\ +([0-9]{3})\ +([^ ]+)\ +(.*) ]]; then
+ local numeric="${BASH_REMATCH[1]}"
+ local numeric="${BASH_REMATCH[1]}"
+ # Check if this is a numeric we will handle.
+ case "$numeric" in
+ "$numeric_ERR_NICKNAMEINUSE"|"$numeric_ERR_ERRONEUSNICKNAME")
+ module_services_ghost=1
+ ;;
+ "$numeric_RPL_ENDOFMOTD"|"$numeric_ERR_NOMOTD")
+ if [[ $config_module_services_style == 'atheme' ]]; then
+ send_raw_flood_nolog "NickServ IDENTIFY (password)" "${module_services_nickserv_command}IDENTIFY $config_firstnick $config_module_services_nickserv_passwd"
+ fi
+ if [[ $module_services_ghost == 1 ]]; then
+ log_info_stdout "Recovering ghost"
+ send_raw_flood_nolog "NickServ GHOST (password)" "${module_services_nickserv_command}GHOST $config_firstnick $config_module_services_nickserv_passwd"
+ # Try to release too, just in case.
+ send_raw_flood_nolog "NickServ RELEASE (password)" "${module_services_nickserv_command}RELEASE $config_firstnick $config_module_services_nickserv_passwd"
+ sleep 2
+ send_nick "$config_firstnick"
+ # HACK: This is a workaround for bug #21
+ server_nick_current="$config_firstnick"
+ fi
+ log_info_stdout "Identifying..."
+ if [[ $config_module_services_style != 'atheme' ]]; then
+ send_raw_flood_nolog "NickServ IDENTIFY (password)" "${module_services_nickserv_command}IDENTIFY $config_module_services_nickserv_passwd"
+ fi
+ sleep 1
+ ;;
+ esac
+ fi
+}
+
+module_services_after_disconnect() {
+ # Reset state.
+ module_services_ghost=0
+}
diff --git a/modules/m_sqlite3.sh b/modules/m_sqlite3.sh
new file mode 100644
index 0000000..4cb8ef2
--- /dev/null
+++ b/modules/m_sqlite3.sh
@@ -0,0 +1,89 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## This module allows other modules to access a SQLite3 database in a
+## "simple" way.
+#---------------------------------------------------------------------
+
+module_sqlite3_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_load'
+ helpentry_module_sqlite3_description="Provides sqlite3 database backend for other modules."
+}
+
+module_sqlite3_UNLOAD() {
+ unset module_sqlite3_clean_string module_sqlite3_exec_sql module_sqlite3_table_exists
+}
+
+module_sqlite3_REHASH() {
+ return 0
+}
+
+# Called after module has loaded.
+module_sqlite3_after_load() {
+ # Check (silently) for sqlite3
+ if ! hash sqlite3 > /dev/null 2>&1; then
+ log_error "Couldn't find sqlite3 command line tool. The sqlite3 module depend on that tool."
+ return 1
+ fi
+ if [[ -z $config_module_sqlite3_database ]]; then
+ log_error "You must set config_module_sqlite3_database in your config to use the sqlite3 module."
+ return 1
+ fi
+ if ! [[ -r $config_module_sqlite3_database ]]; then
+ log_error "sqlite3 module: Database file doesn't exist or can't be read!"
+ log_error "sqlite3 module: To create one follow the comments in one (or several) of the sql files in the doc directory."
+ return 1
+ fi
+}
+
+#---------------------------------------------------------------------
+## Make string safe for SQLite3.
+## @Type API
+## @param String to clean
+## @Note IMPORTANT FOR SECURITY!: Only use the result inside single
+## @Note quotes ('), NEVER inside double quotes (").
+## @Note The output isn't safe for that.
+#---------------------------------------------------------------------
+module_sqlite3_clean_string() {
+ sed "s/'/''/g" <<< "$1"
+}
+
+#---------------------------------------------------------------------
+## Run the query against the data base.
+## @Type API
+## @param Query to run
+#---------------------------------------------------------------------
+module_sqlite3_exec_sql() {
+ sqlite3 -list "$config_module_sqlite3_database" "$1"
+}
+
+#---------------------------------------------------------------------
+## Check if a table exists in the database file.
+## @Type API
+## @param The table name to check for
+## @return 0 If table exists
+## @return 1 If table doesn't exist.
+#---------------------------------------------------------------------
+module_sqlite3_table_exists() {
+ sqlite3 -list "$config_module_sqlite3_database" ".tables" | grep -qw "$1"
+}
diff --git a/modules/m_umodes.sh b/modules/m_umodes.sh
new file mode 100644
index 0000000..0a508f9
--- /dev/null
+++ b/modules/m_umodes.sh
@@ -0,0 +1,63 @@
+#!/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Set umodes when connecting
+#---------------------------------------------------------------------
+
+module_umodes_INIT() {
+ modinit_API='2'
+ modinit_HOOKS='after_connect after_load'
+ helpentry_module_umodes_description="Provides support for setting umodes on connect."
+}
+
+module_umodes_UNLOAD() {
+ unset module_umodes_set_modes
+}
+
+module_umodes_REHASH() {
+ module_umodes_set_modes
+ return 0
+}
+
+#---------------------------------------------------------------------
+## Set the umodes
+## @Type Private
+#---------------------------------------------------------------------
+module_umodes_set_modes() {
+ if [[ $config_module_umodes_default_umodes ]]; then
+ log_info "Setting umodes: $config_module_umodes_default_umodes"
+ send_umodes "$config_module_umodes_default_umodes"
+ fi
+}
+
+# Called after bot has connected
+module_umodes_after_connect() {
+ module_umodes_set_modes
+}
+
+# Called after bot has connected
+module_umodes_after_load() {
+ # Check if connected first
+ if [[ $server_connected -eq 1 ]]; then
+ module_umodes_set_modes
+ fi
+}
diff --git a/modules/m_uptime.sh b/modules/m_uptime.sh
new file mode 100644
index 0000000..834773b
--- /dev/null
+++ b/modules/m_uptime.sh
@@ -0,0 +1,56 @@
+#!/usr/bin/bash
+# -*- coding: utf-8 -*-
+###########################################################################
+# #
+# envbot - an IRC bot in bash #
+# Copyright (C) 2007-2008 Arvid Norlander #
+# Copyright (C) 2007-2008 Vsevolod Kozlov #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+###########################################################################
+#---------------------------------------------------------------------
+## Bot's uptime command.
+#---------------------------------------------------------------------
+
+module_uptime_INIT() {
+ modinit_API='2'
+ modinit_HOOKS=''
+ commands_register "$1" 'uptime' || return 1
+ helpentry_module_uptime_description="Provides a command to show bot's uptime."
+
+ helpentry_uptime_uptime_syntax=''
+ helpentry_uptime_uptime_description='Shows the uptime for the bot.'
+}
+
+module_uptime_UNLOAD() {
+ return 0
+}
+
+module_uptime_REHASH() {
+ return 0
+}
+
+module_uptime_handler_uptime() {
+ local sender="$1"
+ local formatted_time=
+ time_format_difference $SECONDS formatted_time
+ local target=
+ if [[ $2 =~ ^# ]]; then
+ target="$2"
+ else
+ parse_hostmask_nick "$sender" target
+ fi
+ send_msg "$target" "The bot has been up for $formatted_time."
+}