diff options
author | Márcio Silva <coadde at hyperbola dot info> | 2017-06-02 15:44:54 -0300 |
---|---|---|
committer | Márcio Silva <coadde at hyperbola dot info> | 2017-06-02 15:44:54 -0300 |
commit | b4830e97ae51396ccaa9ca2acb469aef80094ae8 (patch) | |
tree | c069b1ef6f9189848726121afa8d815e4e56d138 /modules | |
download | hyperbot-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.sh | 228 | ||||
-rw-r--r-- | modules/m_autojoin.sh | 63 | ||||
-rw-r--r-- | modules/m_check_numerics.sh | 45 | ||||
-rw-r--r-- | modules/m_commands.sh | 103 | ||||
-rw-r--r-- | modules/m_ctcp.sh | 91 | ||||
-rw-r--r-- | modules/m_dice.sh | 84 | ||||
-rw-r--r-- | modules/m_die.sh | 75 | ||||
-rw-r--r-- | modules/m_dumpvars.sh | 54 | ||||
-rw-r--r-- | modules/m_factoids.sh | 461 | ||||
-rw-r--r-- | modules/m_faq.sh | 138 | ||||
-rw-r--r-- | modules/m_help.sh | 151 | ||||
-rw-r--r-- | modules/m_join.sh | 92 | ||||
-rw-r--r-- | modules/m_karma.sh | 252 | ||||
-rw-r--r-- | modules/m_kick_ban.sh | 145 | ||||
-rw-r--r-- | modules/m_modules.sh | 178 | ||||
-rw-r--r-- | modules/m_nicktracking.sh | 316 | ||||
-rw-r--r-- | modules/m_ping.sh | 87 | ||||
-rw-r--r-- | modules/m_quote.sh | 85 | ||||
-rw-r--r-- | modules/m_rehash.sh | 74 | ||||
-rw-r--r-- | modules/m_say.sh | 98 | ||||
-rw-r--r-- | modules/m_seen.sh | 209 | ||||
-rw-r--r-- | modules/m_sendraw.sh | 53 | ||||
-rw-r--r-- | modules/m_services.sh | 87 | ||||
-rw-r--r-- | modules/m_sqlite3.sh | 89 | ||||
-rw-r--r-- | modules/m_umodes.sh | 63 | ||||
-rw-r--r-- | modules/m_uptime.sh | 56 |
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." +} |