Add initial files from envbot v0.1-beta1
This commit is contained in:
99
lib/access.sh
Normal file
99
lib/access.sh
Normal file
@@ -0,0 +1,99 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Access control library.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Check for owner access.
|
||||
## @Type API
|
||||
## @param n!u@h mask
|
||||
## @return 0 If access was granted
|
||||
## @return 1 If access was denied.
|
||||
#---------------------------------------------------------------------
|
||||
access_check_owner() {
|
||||
debug_log_caller "$@"
|
||||
security_assert_argc 1 1 "$@" || {
|
||||
log_error "Aiie! Access denied because of incorrect function call!"
|
||||
return 1
|
||||
}
|
||||
local index
|
||||
for index in ${!config_access_mask[*]}; do
|
||||
if [[ "$1" =~ ${config_access_mask[$index]} ]] && list_contains "config_access_capab[$index]" 'owner'; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Check for access in scope.
|
||||
## @Type API
|
||||
## @param Capability to check for.
|
||||
## @param n!u@h mask
|
||||
## @param What scope
|
||||
## @return 0 If access was granted
|
||||
## @return 1 If access was denied.
|
||||
#---------------------------------------------------------------------
|
||||
access_check_capab() {
|
||||
debug_log_caller "$@"
|
||||
security_assert_argc 3 3 "$@" || {
|
||||
log_error "Aiie! Access denied because of incorrect function call!"
|
||||
return 1
|
||||
}
|
||||
local index
|
||||
for index in ${!config_access_mask[*]}; do
|
||||
if [[ "$2" =~ ${config_access_mask[$index]} ]] && \
|
||||
[[ "$3" =~ ${config_access_scope[$index]} ]]; then
|
||||
if list_contains "config_access_capab[$index]" "$1" || \
|
||||
list_contains "config_access_capab[$index]" "owner"; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Used to log actions like "did a rehash" if access was granted.
|
||||
## @Type API
|
||||
## @param n!u@h mask
|
||||
## @param What happened.
|
||||
#---------------------------------------------------------------------
|
||||
access_log_action() {
|
||||
log_info_file owner.log "$1 performed the restricted action: $2"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Return error message about failed access to someone, and log it
|
||||
## @Type API
|
||||
## @param n!u@h mask
|
||||
## @param What they tried to do
|
||||
## @param What capability they need
|
||||
#---------------------------------------------------------------------
|
||||
access_fail() {
|
||||
log_error_file access.log "$1 tried to \"$2\" but lacks access."
|
||||
local nick=
|
||||
parse_hostmask_nick "$sender" 'nick'
|
||||
send_notice "$nick" "Permission denied. You need the capability \"$3\" to do this action."
|
||||
}
|
||||
125
lib/channels.sh
Normal file
125
lib/channels.sh
Normal file
@@ -0,0 +1,125 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Channel management.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Space separated list of current channels
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
channels_current=""
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Join a channel
|
||||
## @Type API
|
||||
## @param The channel to join.
|
||||
## @param Is a channel key, if any.
|
||||
#---------------------------------------------------------------------
|
||||
channels_join() {
|
||||
local channel="$1"
|
||||
local key=""
|
||||
[[ -n "$2" ]] && key=" $2"
|
||||
send_raw "JOIN ${channel}${key}"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Part a channel
|
||||
## @Type API
|
||||
## @param The channel to part
|
||||
## @param Is a reason.
|
||||
#---------------------------------------------------------------------
|
||||
channels_part() {
|
||||
local channel="$1"
|
||||
local reason=""
|
||||
[[ -n "$2" ]] && reason=" :$2"
|
||||
send_raw "PART ${channel}${reason}"
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
# Internal functions to core or this file below this line! #
|
||||
# Module authors: go away #
|
||||
###########################################################################
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Internal function!
|
||||
## Adds channels to the list
|
||||
## @Type Private
|
||||
## @param The channel to add
|
||||
#---------------------------------------------------------------------
|
||||
channels_add() {
|
||||
channels_current+=" $1"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Internal function!
|
||||
## Removes channels to the list
|
||||
## @Type Private
|
||||
## @param The channel to remove
|
||||
#---------------------------------------------------------------------
|
||||
channels_remove() {
|
||||
list_remove channels_current "$1" channels_current
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Check if we parted, called from main loop
|
||||
## @Type Private
|
||||
## @param n!u@h mask
|
||||
## @param Channel parted.
|
||||
## @param Reason (ignored).
|
||||
#---------------------------------------------------------------------
|
||||
channels_handle_part() {
|
||||
local whoparted=
|
||||
parse_hostmask_nick "$1" 'whoparted'
|
||||
if [[ $whoparted == $server_nick_current ]]; then
|
||||
channels_remove "$2"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Check if we got kicked, called from main loop
|
||||
## @Type Private
|
||||
## @param n!u@h mask of kicker
|
||||
## @param Channel kicked from.
|
||||
## @param Nick of kicked user
|
||||
## @param Reason (ignored).
|
||||
#---------------------------------------------------------------------
|
||||
channels_handle_kick() {
|
||||
local whogotkicked="$3"
|
||||
if [[ $whogotkicked == $server_nick_current ]]; then
|
||||
channels_remove "$2"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Check if we joined, called from main loop
|
||||
## @Type Private
|
||||
## @param n!u@h mask
|
||||
## @param Channel joined.
|
||||
#---------------------------------------------------------------------
|
||||
channels_handle_join() {
|
||||
local whojoined=
|
||||
parse_hostmask_nick "$1" 'whojoined'
|
||||
if [[ $whojoined == $server_nick_current ]]; then
|
||||
channels_add "$2"
|
||||
fi
|
||||
}
|
||||
265
lib/commands.sh
Normal file
265
lib/commands.sh
Normal file
@@ -0,0 +1,265 @@
|
||||
#!/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 registering of commands
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## List of commands (maps to function for the command), a hash
|
||||
## @Note Dummy variable to document the fact that it is a hash.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
commands_list=''
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## List of functions (by module), a hash
|
||||
## @Note Dummy variable to document the fact that it is a hash.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
commands_modules_functions=''
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## List of commands (by function), a hash
|
||||
## @Note Dummy variable to document the fact that it is a hash.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
commands_function_commands=''
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## List of commands (by module)
|
||||
## @Note Dummy variable to document the fact that it is a hash.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
commands_module_commands=''
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## List of modules (by command)
|
||||
## @Note Dummy variable to document the fact that it is a hash.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
commands_commands_module=''
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Comma separated list of all commands
|
||||
## @Type Semi-private
|
||||
#---------------------------------------------------------------------
|
||||
commands_commands=''
|
||||
|
||||
# Just unset dummy variables.
|
||||
unset commands_list commands_modules_functions commands_function_commands commands_module_commands commands_module_commands
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Register a command.
|
||||
## @Type API
|
||||
## @param Module name
|
||||
## @param Function name (Part after module_modulename_handler_)
|
||||
## @param Command name (on IRC, may contain spaces) (optional, defaults to same as function name, that is $2)
|
||||
## @return 0 If successful
|
||||
## @return 1 If failed for other reason
|
||||
## @return 2 If invalid command name
|
||||
## @return 3 If the command already exists (maybe from some other module)
|
||||
## @return 4 If the function already exists for other command.
|
||||
## @return 5 If the function in question is not declared.
|
||||
#---------------------------------------------------------------------
|
||||
commands_register() {
|
||||
# Speed isn't that important here, it is only called at module load after all.
|
||||
local module="$1"
|
||||
local function_name="$2"
|
||||
local command_name="$3"
|
||||
# Command name is optional
|
||||
if [[ -z $command_name ]]; then
|
||||
command_name="$function_name"
|
||||
fi
|
||||
# Check for valid command name
|
||||
if ! [[ $command_name =~ ^[a-zA-Z0-9] ]]; then
|
||||
log_error "commands_register_command: Module \"$module\" gave invalid command name \"$command_name\". First char of command must be alphanumeric."
|
||||
return 2
|
||||
fi
|
||||
if ! [[ $command_name =~ ^[a-zA-Z0-9][^\ ,]*( [^, ]+)?$ ]]; then
|
||||
log_error "commands_register_command: Module \"$module\" gave invalid command name \"$command_name\". A command can be at most 2 words and should have no trailing white space and may not contain a \",\" (comma)."
|
||||
return 2
|
||||
fi
|
||||
# Bail out if command is already registered.
|
||||
if hash_exists 'commands_list' "$command_name"; then
|
||||
log_error "commands_register_command: Failed to register command from \"$module\": a command with the name \"$command_name\" already exists."
|
||||
return 3
|
||||
fi
|
||||
# Bail out if the function already is mapped to some other command
|
||||
if hash_exists 'commands_function_commands' "$function_name"; then
|
||||
log_error "commands_register_command: Failed to register command from \"$module\": the function is already registered under another command name."
|
||||
return 4
|
||||
fi
|
||||
|
||||
# Does the function itself exist?
|
||||
local full_function_name="module_${module}_handler_${function_name}"
|
||||
if ! declare -F | grep -qe "^declare -f ${full_function_name}$"; then
|
||||
log_error "commands_register_command: Failed to register command from \"$module\": the function $full_function_name does not exist"
|
||||
return 5
|
||||
fi
|
||||
# So it was valid. Lets add it then.
|
||||
|
||||
# Store in module -> function mapping.
|
||||
hash_append 'commands_modules_functions' "$module" "$function_name" || {
|
||||
log_error "commands_register_command: module -> commands mapping failed: mod=\"$module\" func=\"$function_name\"."
|
||||
return 1
|
||||
}
|
||||
# Store in command -> function mapping
|
||||
hash_set 'commands_list' "$command_name" "$full_function_name" || {
|
||||
log_error "commands_register_command: command -> function mapping failed: cmd=\"$command_name\" full_func=\"$full_function_name\"."
|
||||
return 1
|
||||
}
|
||||
# Store in function -> command mapping
|
||||
hash_set 'commands_function_commands' "$function_name" "$command_name" || {
|
||||
log_error "commands_register_command: function -> command mapping failed: func=\"$function_name\" cmd=\"$command_name\"."
|
||||
return 1
|
||||
}
|
||||
# Store in command -> module mapping
|
||||
hash_set 'commands_commands_module' "$command_name" "$module" || {
|
||||
log_error "commands_register_command: command -> module mapping failed: cmd=\"$command_name\" mod=\"$module\"."
|
||||
return 1
|
||||
}
|
||||
# Store in module -> commands mapping (ick!)
|
||||
hash_append 'commands_module_commands' "$module" "$command_name" ',' || {
|
||||
log_error "commands_register_command: module -> command mapping failed: mod=\"$module\" cmd=\"$command_name\"."
|
||||
}
|
||||
# Store in comma-separated command list
|
||||
if [[ $commands_commands ]]; then
|
||||
commands_commands+=",$command_name" || return 1
|
||||
else
|
||||
commands_commands="$command_name" || return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Get what module provides a command.
|
||||
## @param Command to find.
|
||||
## @param Variable to return in
|
||||
## @Type Semi-private
|
||||
#---------------------------------------------------------------------
|
||||
commands_provides() {
|
||||
hash_get "commands_commands_module" "$1" "$2"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Get what commands exist in a module.
|
||||
## @param Command to find.
|
||||
## @param Variable to return comma separated list in
|
||||
## @Type Semi-private
|
||||
#---------------------------------------------------------------------
|
||||
commands_in_module() {
|
||||
hash_get "commands_module_commands" "$1" "$2"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Will remove all commands from a module and unset the functions in question.
|
||||
## @Type Private
|
||||
## @param Module
|
||||
## @return 0 If successful (or no commands exist for module)
|
||||
## @return 1 If error
|
||||
## @return 2 If fatal error
|
||||
#---------------------------------------------------------------------
|
||||
commands_unregister() {
|
||||
local module="$1"
|
||||
# Are there any commands for the module?
|
||||
hash_exists 'commands_modules_functions' "$module" || {
|
||||
return 0
|
||||
}
|
||||
local function_name full_function_name command_name functions
|
||||
# Get list of functions
|
||||
hash_get 'commands_modules_functions' "$module" 'functions' || return 2
|
||||
# Iterate through the functions
|
||||
for function_name in $functions; do
|
||||
# Get command name
|
||||
hash_get 'commands_function_commands' "$function_name" 'command_name' || return 2
|
||||
# Unset from function -> command hash
|
||||
hash_unset 'commands_function_commands' "$function_name" || return 2
|
||||
# Unset from command -> function hash
|
||||
hash_unset 'commands_list' "$command_name" || return 2
|
||||
# Unset from command -> module mapping
|
||||
hash_unset 'commands_commands_module' "$command_name" || return 2
|
||||
# Remove from command list.
|
||||
list_remove 'commands_commands' "$command_name" 'commands_commands' "," || return 1
|
||||
# Unset help strings (if any):
|
||||
unset helpentry_${module}_${function_name}_syntax
|
||||
unset helpentry_${module}_${function_name}_description
|
||||
# Unset function itself.
|
||||
full_function_name="module_${module}_handler_${function_name}"
|
||||
unset "$full_function_name" || return 2
|
||||
done
|
||||
# Unset the module -> commands mapping.
|
||||
hash_unset 'commands_module_commands' "$module" || return 2
|
||||
# Finally unset module -> functions mapping.
|
||||
hash_unset 'commands_modules_functions' "$module" || return 2
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Process a line finding what command it would be
|
||||
## @Type Private
|
||||
## @param Sender
|
||||
## @param Target
|
||||
## @param Query
|
||||
## @return 0 If not a command
|
||||
## @return 1 If it indeed was a command that we therefore handled.
|
||||
## @return 2 A command but that didn't exist.
|
||||
#---------------------------------------------------------------------
|
||||
commands_call_command() {
|
||||
local regex="${config_commands_listenregex}"
|
||||
# Not on a channel?
|
||||
if [[ ! $2 =~ ^# ]]; then
|
||||
# Should we treat it as a command anyway?
|
||||
if [[ $config_commands_private_always == 1 ]]; then
|
||||
local regex="(${config_commands_listenregex})?"
|
||||
fi
|
||||
fi
|
||||
# Check if it is a command.
|
||||
# (${config_commands_listenregex}, followed by an alphanumeric char.)
|
||||
if [[ "$3" =~ ^${regex}([a-zA-Z0-9].*) ]]; then
|
||||
local data="${BASH_REMATCH[@]: -1}"
|
||||
# Right, get the parts of the command
|
||||
if [[ $data =~ ^([a-zA-Z0-9][^ ]*)( [^, ]+)?( .*)? ]]; then
|
||||
local firstword="${BASH_REMATCH[1]}"
|
||||
local secondword="${BASH_REMATCH[2]}"
|
||||
local parameters="${BASH_REMATCH[3]}"
|
||||
|
||||
local function=
|
||||
# Check for two word commands first.
|
||||
hash_get 'commands_list' "${firstword}${secondword}" 'function'
|
||||
if [[ -z "$function" ]]; then
|
||||
# Maybe one word then?
|
||||
hash_get 'commands_list' "$firstword" 'function'
|
||||
if [[ "$function" ]]; then
|
||||
parameters="${secondword}${parameters}"
|
||||
# No, not that either
|
||||
else
|
||||
return 2
|
||||
fi
|
||||
fi
|
||||
|
||||
# So we got a command, now lets run it
|
||||
# (strip leading white spaces) from parameters.
|
||||
"$function" "$1" "$2" "${parameters## }"
|
||||
return 1
|
||||
fi
|
||||
return 2
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
219
lib/config.sh
Normal file
219
lib/config.sh
Normal file
@@ -0,0 +1,219 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Configuration management
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Rehash config file.
|
||||
## @Type API
|
||||
## @return 0 Success.
|
||||
## @return 2 Not same config version.
|
||||
## @return 3 Failed to source. The bot should not be in an undefined state.
|
||||
## @return 4 Config validation on faked source failed. The bot should not be in an undefined state.
|
||||
## @return 5 Failed to source. The bot may be in an undefined state.
|
||||
## @Note If config validation fails at REAL source, the bot may quit. However this should never happen.
|
||||
#---------------------------------------------------------------------
|
||||
config_rehash() {
|
||||
local new_conf_ver="$(grep -E '^config_version=' "$config_file")"
|
||||
if ! [[ $new_conf_ver =~ ^config_version=$config_current_version ]]; then
|
||||
log_error "REHASH: Not same config version. Rehash aborted."
|
||||
return 2
|
||||
fi
|
||||
# Try sourceing in a subshell first to catch errors
|
||||
# without causing bot to break
|
||||
( source "$config_file" )
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log_error "REHASH: Failed faked source. Rehash aborted. (TIP: Check for syntax errors in config and any message above this message.)"
|
||||
return 3
|
||||
fi
|
||||
# HACK: Subshell, then unset all but two config_ variables (one is readonly, the other is needed to validate)
|
||||
# Then source config file and run validation on it.
|
||||
( unset -v $(sed 's/ *config_current_version */ /g;s/ *config_file */ /g' <<<"${!config_*}")
|
||||
source "$config_file"
|
||||
config_validate && config_validate_transport )
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log_error "REHASH: Failed config validation on new config. Rehash aborted."
|
||||
return 4
|
||||
fi
|
||||
# Source for real if that worked
|
||||
source "$config_file"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log_error "REHASH: Failed real source. BOT MAY BE IN UNDEFINED STATE."
|
||||
return 5
|
||||
fi
|
||||
# Lets force command line -v, it may have been overwritten by config.
|
||||
if [[ $force_verbose -eq 1 ]]; then
|
||||
config_log_stdout='1'
|
||||
fi
|
||||
local status
|
||||
modules_load_from_config
|
||||
for module in $modules_loaded; do
|
||||
module_${module}_REHASH
|
||||
status=$?
|
||||
if [[ $status -eq 1 ]]; then
|
||||
log_error "Rehash of ${module} failed, trying to unload it."
|
||||
modules_unload "${module}" || {
|
||||
log_fatal "Unloading of ${module} after failed rehash failed."
|
||||
bot_quit "Fatal error in unload of module that failed to rehash"
|
||||
}
|
||||
fi
|
||||
if [[ $status -eq 2 ]]; then
|
||||
log_fatal "Rehash of ${module} failed in a FATAL way. Quitting"
|
||||
bot_quit "Fatal error in rehash of module"
|
||||
fi
|
||||
done
|
||||
log_info_stdout "Rehash successful"
|
||||
}
|
||||
|
||||
|
||||
###########################################################################
|
||||
# Internal functions to core or this file below this line! #
|
||||
# Module authors: go away #
|
||||
###########################################################################
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## This will call logging if logging is setup,
|
||||
## otherwise just print to STDOUT, with prefix
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
config_dolog_fatal() {
|
||||
if [[ $log_file ]]; then
|
||||
log_fatal "$1"
|
||||
else
|
||||
echo "FATAL ERROR: $1"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Returns an error if the variable in question is empty/not set
|
||||
## @Note Works only for non-array variables
|
||||
## @Type Private
|
||||
## @param Variable name
|
||||
## @param Extra error line(s) to append (optional, one parameter for each extra line)
|
||||
#---------------------------------------------------------------------
|
||||
config_validate_check_exists() {
|
||||
if [[ -z "${!1}" ]]; then
|
||||
config_dolog_fatal "YOU MUST SET $1 IN THE CONFIG"
|
||||
shift
|
||||
# Do the rest of the messages
|
||||
local line=
|
||||
for line in "$@"; do
|
||||
config_dolog_fatal "$line"
|
||||
done
|
||||
envbot_quit 2
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Validate config file
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
config_validate() {
|
||||
# Note: normal logging is not initialized yet at this point,
|
||||
# so we use config_dolog_fatal, that calls normal logging in case
|
||||
# logging is loaded (like rehash).
|
||||
|
||||
# General settings
|
||||
config_validate_check_exists config_firstnick
|
||||
config_validate_check_exists config_ident
|
||||
config_validate_check_exists config_gecos
|
||||
|
||||
# Server settings
|
||||
config_validate_check_exists config_server
|
||||
config_validate_check_exists config_server_port
|
||||
config_validate_check_exists config_server_ssl
|
||||
|
||||
# Logging
|
||||
config_validate_check_exists config_log_dir
|
||||
config_validate_check_exists config_log_stdout
|
||||
config_validate_check_exists config_log_raw
|
||||
config_validate_check_exists config_log_colors
|
||||
|
||||
# Commands
|
||||
config_validate_check_exists config_commands_listenregex
|
||||
config_validate_check_exists config_commands_private_always
|
||||
|
||||
# Feedback
|
||||
config_validate_check_exists config_feedback_unknown_commands
|
||||
|
||||
# Access
|
||||
if [[ -z "${config_access_mask[1]}" ]]; then
|
||||
config_dolog_fatal "YOU MUST SET AT LEAST ONE OWNER IN EXAMPLE CONFIG"
|
||||
config_dolog_fatal "AND THAT OWNER MUST BE THE FIRST ONE (config_access_mask[1] that is)."
|
||||
envbot_quit 1
|
||||
fi
|
||||
if ! list_contains "config_access_capab[1]" "owner"; then
|
||||
config_dolog_fatal "YOU MUST SET AT LEAST ONE OWNER IN EXAMPLE CONFIG"
|
||||
config_dolog_fatal "AND THAT OWNER MUST BE THE FIRST ONE (config_access_capab[1] that is)."
|
||||
envbot_quit 1
|
||||
fi
|
||||
|
||||
# Transports
|
||||
config_validate_check_exists "config_transport_dir"
|
||||
if [[ ! -d "${config_transport_dir}" ]]; then
|
||||
config_dolog_fatal "The transport directory ${config_transport_dir} doesn't seem to exist"
|
||||
envbot_quit 2
|
||||
fi
|
||||
config_validate_check_exists "config_transport"
|
||||
if [[ ! -r "${config_transport_dir}/${config_transport}.sh" ]]; then
|
||||
config_dolog_fatal "The transport ${config_transport} doesn't seem to exist"
|
||||
envbot_quit 2
|
||||
fi
|
||||
|
||||
# Modules
|
||||
config_validate_check_exists config_modules_dir
|
||||
if ! [[ -d "$config_modules_dir" ]]; then
|
||||
if ! list_contains transport_supports "bind"; then
|
||||
config_dolog_fatal "$config_modules_dir DOES NOT EXIST OR IS NOT A DIRECTORY."
|
||||
envbot_quit 1
|
||||
fi
|
||||
fi
|
||||
config_validate_check_exists config_modules
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Validate some settings from config file that can only be done after
|
||||
## transport was loaded.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
config_validate_transport() {
|
||||
# At this point logging is enabled, we can use it.
|
||||
if [[ $config_server_ssl -ne 0 ]]; then
|
||||
if ! list_contains transport_supports "ssl"; then
|
||||
log_fatal "THIS TRANSPORT DOES NOT SUPORT SSL"
|
||||
envbot_quit 1
|
||||
fi
|
||||
else
|
||||
if ! list_contains transport_supports "nossl"; then
|
||||
log_fatal "THIS TRANSPORT REQUIRES SSL"
|
||||
envbot_quit 1
|
||||
fi
|
||||
fi
|
||||
if [[ "$config_server_bind" ]]; then
|
||||
if ! list_contains transport_supports "bind"; then
|
||||
log_fatal "THIS TRANSPORT DOES NOT SUPORT BINDING AN IP"
|
||||
envbot_quit 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
84
lib/debug.sh
Normal file
84
lib/debug.sh
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Functions used during development for debugging.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Debugging function to check that right number of parameters were
|
||||
## provided.
|
||||
## @param Lowest allowed count of parameters.
|
||||
## @param Higest allowed count of parameters. (Optional, defaults to same as lower)
|
||||
#---------------------------------------------------------------------
|
||||
debug_assert_argc() {
|
||||
[[ $envbot_debugging ]] || return 0
|
||||
if [[ ${BASH_ARGC[1]} -lt $1 || ${BASH_ARGC[1]} -gt ${2:-$1} ]]; then
|
||||
log_debug "${FUNCNAME[1]} should have had $1 parameters but had ${BASH_ARGC[1]} instead"
|
||||
log_debug "${FUNCNAME[1]} was called from ${BASH_SOURCE[2]}:${BASH_LINENO[1]} ${FUNCNAME[2]}."
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Reports who called function and with what arguments.
|
||||
## @Type API
|
||||
## @param Should be "$@" at first line of function.
|
||||
#---------------------------------------------------------------------
|
||||
debug_log_caller() {
|
||||
[[ $envbot_debugging ]] || return 0
|
||||
log_debug "${FUNCNAME[1]} called from ${BASH_SOURCE[2]}:${BASH_LINENO[1]} ${FUNCNAME[2]} with arguments: $*"
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
# Internal functions to core or this file below this line! #
|
||||
# Module authors: go away #
|
||||
###########################################################################
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Enable debugging.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
debug_enable() {
|
||||
envbot_debugging=1
|
||||
shopt -s extdebug
|
||||
log_debug "Debugging enabled"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Disable debugging.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
debug_disable() {
|
||||
envbot_debugging=''
|
||||
shopt -u extdebug
|
||||
log_debug "Debugging disabled"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Enable or disable debugging at startup.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
debug_init() {
|
||||
if [[ "$envbot_debugging" ]]; then
|
||||
debug_enable
|
||||
fi
|
||||
}
|
||||
59
lib/feedback.sh
Normal file
59
lib/feedback.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## User feedback.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Return a message that syntax was bad and what the correct syntax is.
|
||||
## @Type API
|
||||
## @param To who (nick or channel)
|
||||
## @param From what command
|
||||
## @param Syntax help
|
||||
#---------------------------------------------------------------------
|
||||
feedback_bad_syntax() {
|
||||
send_notice "$1" "Syntax error. Correct syntax for $2 is $2 $3"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Return a message that something else was wrong in the command.
|
||||
## @Type API
|
||||
## @param To who (nick or channel)
|
||||
## @param From what function
|
||||
## @param Error message.
|
||||
#---------------------------------------------------------------------
|
||||
feedback_generic_error() {
|
||||
send_notice "$1" "$2: Error: $3"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Return a message that a command was unknown.
|
||||
## @Type Private
|
||||
## @param Sender of message (n!u@h)
|
||||
## @param To where (botnick or channel)
|
||||
## @param Query
|
||||
#---------------------------------------------------------------------
|
||||
feedback_unknown_command() {
|
||||
local sendernick
|
||||
parse_hostmask_nick "$sender" 'sendernick'
|
||||
send_notice "$sendernick" "Error: Not able to parse this command: \"$3\". Are you sure you spelled it correctly?"
|
||||
}
|
||||
312
lib/hash.sh
Normal file
312
lib/hash.sh
Normal file
@@ -0,0 +1,312 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Functions for working with associative arrays.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Convert a string to hex
|
||||
## @Type Private
|
||||
## @param String to convert
|
||||
## @param Name of variable to return result in.
|
||||
#---------------------------------------------------------------------
|
||||
hash_hexify() {
|
||||
# Res will contain full output string, hex current char.
|
||||
local hex i res=
|
||||
for ((i=0;i<${#1};i++)); do
|
||||
# The ' is not documented in bash but it works.
|
||||
# See http://www.opengroup.org/onlinepubs/009695399/utilities/printf.html
|
||||
# for documentation of the ' syntax for printf.
|
||||
printf -v hex '%x' "'${1:i:1}"
|
||||
# Add to string
|
||||
res+=$hex
|
||||
done
|
||||
# Print to variable.
|
||||
printf -v "$2" '%s' "$res"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Convert a string from hex to normal
|
||||
## @Type Private
|
||||
## @param String to convert
|
||||
## @param Name of variable to return result in.
|
||||
#---------------------------------------------------------------------
|
||||
hash_unhexify() {
|
||||
# Res will contain full output string, unhex current char.
|
||||
local unhex i=0 res=
|
||||
for ((i=0;i<${#1};i+=2)); do
|
||||
# Convert back from hex. 2 chars at a time
|
||||
# FIXME: This will break if output would be multibyte chars.
|
||||
printf -v unhex \\"x${1:i:2}"
|
||||
res+=$unhex
|
||||
done
|
||||
printf -v "$2" '%s' "$res"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Generate variable name for a item in the hash array.
|
||||
## @Type Private
|
||||
## @param Table name
|
||||
## @param Index
|
||||
## @param Name of variable to return result in.
|
||||
#---------------------------------------------------------------------
|
||||
hash_name_create() {
|
||||
local hexindex
|
||||
hash_hexify "$2" 'hexindex'
|
||||
printf -v "$3" '%s' "hsh_${1}_${hexindex}"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Translate a variable name to an entry index name.
|
||||
## @param Variable name
|
||||
## @param Return value for index
|
||||
#---------------------------------------------------------------------
|
||||
hash_name_getindex() {
|
||||
local unhexindex tablename indexname
|
||||
local IFS="_"
|
||||
read -r tablename indexname <<< "${1/hsh_//}"
|
||||
unset IFS
|
||||
hash_unhexify "$indexname" "$2"
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Sets (overwrites any older) a value in a hash array
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @param Index
|
||||
## @param Value
|
||||
#---------------------------------------------------------------------
|
||||
hash_set() {
|
||||
local varname
|
||||
# Get variable name
|
||||
hash_name_create "$1" "$2" 'varname'
|
||||
# Set it using the printf to variable
|
||||
printf -v "$varname" '%s' "$3"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Append a value to the end of an entry in a hash array
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @param Index
|
||||
## @param Value to append
|
||||
## @param Separator (optional, defaults to space)
|
||||
#---------------------------------------------------------------------
|
||||
hash_append() {
|
||||
local varname
|
||||
# Get variable name
|
||||
hash_name_create "$1" "$2" 'varname'
|
||||
# Append to end, or if empty just set.
|
||||
if [[ "${!varname}" ]]; then
|
||||
local sep=${4:-" "}
|
||||
printf -v "$varname" '%s' "${!varname}${sep}${3}"
|
||||
else
|
||||
printf -v "$varname" '%s' "$3"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Opposite of <@function hash_append>, removes a value from a list
|
||||
## in a hash entry
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @param Index
|
||||
## @param Value to remove
|
||||
## @param Separator (optional, defaults to space)
|
||||
#---------------------------------------------------------------------
|
||||
hash_substract() {
|
||||
local varname
|
||||
# Get variable name
|
||||
hash_name_create "$1" "$2" 'varname'
|
||||
# If not empty try to remove value
|
||||
if [[ "${!varname}" ]]; then
|
||||
local sep=${4:-" "}
|
||||
# FIXME: substrings of the entries in the list may match :/
|
||||
local list="${!varname}"
|
||||
list="${list//$3}"
|
||||
# Remove any double $sep caused by this.
|
||||
list="${list//$sep$sep/$sep}"
|
||||
printf -v "$varname" '%s' "$list"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Replace a value in list style hash entry.
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @param Index
|
||||
## @param Value to replace
|
||||
## @param Value to replace with
|
||||
## @param Separator (optional, defaults to space)
|
||||
#---------------------------------------------------------------------
|
||||
hash_replace() {
|
||||
local varname
|
||||
# Get variable name
|
||||
hash_name_create "$1" "$2" 'varname'
|
||||
# Append to end, or if empty just set.
|
||||
local sep=${5:-" "}
|
||||
if [[ "${!varname}" =~ (^|$sep)${3}($sep|$) ]]; then
|
||||
# FIXME: substrings of the entries in the list may match :/
|
||||
local list="${!varname}"
|
||||
list="${list//$3/$4}"
|
||||
printf -v "$varname" '%s' "$list"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Removes an entry (if it exists) from a hash array
|
||||
## @Note If the entry does not exist, nothing will happen
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @param Index
|
||||
#---------------------------------------------------------------------
|
||||
hash_unset() {
|
||||
local varname
|
||||
# Get variable name
|
||||
hash_name_create "$1" "$2" 'varname'
|
||||
unset "${varname}"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Gets a value (if it exists) from a hash array
|
||||
## @Note If value does not exist, the variable will be empty.
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @param Index
|
||||
## @param Name of variable to return result in.
|
||||
#---------------------------------------------------------------------
|
||||
hash_get() {
|
||||
local varname
|
||||
# Get variable name
|
||||
hash_name_create "$1" "$2" 'varname'
|
||||
# Now print out to variable using indirect ref to get the value.
|
||||
printf -v "$3" '%s' "${!varname}"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Check if a list style hash entry contains a specific value.
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @param Index
|
||||
## @param Value to check for
|
||||
## @param Separator (optional, defaults to space)
|
||||
## @return 0 Found
|
||||
## @return 1 Not found (or hash doesn't exist).
|
||||
#---------------------------------------------------------------------
|
||||
hash_contains() {
|
||||
local varname
|
||||
# Get variable name
|
||||
hash_name_create "$1" "$2" 'varname'
|
||||
|
||||
local sep=${4:-" "}
|
||||
if [[ "${sep}${!varname}${sep}" =~ ${sep}${3}${sep} ]]; then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Check if a any space separated entry in a hash array contains
|
||||
## a specific value.
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @param Value to check for
|
||||
## @return 0 Found
|
||||
## @return 1 Not found (or hash doesn't exist).
|
||||
#---------------------------------------------------------------------
|
||||
hash_search() {
|
||||
# Get variable names
|
||||
eval "local vars=\"\${!hsh_${1}_*}\""
|
||||
# Append to end, or if empty just set.
|
||||
if [[ $vars ]]; then
|
||||
local var
|
||||
# Extract index.
|
||||
for var in $vars; do
|
||||
[[ "${!varname}" =~ (^| )${2}( |$) ]] && return 0
|
||||
done
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Check if an entry exists in a hash array
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @param Index
|
||||
## @return 0 If the entry exists
|
||||
## @return 1 If the entry doesn't exist
|
||||
#---------------------------------------------------------------------
|
||||
hash_exists() {
|
||||
local varname
|
||||
hash_name_create "$1" "$2" 'varname'
|
||||
# This will return the return code we want.
|
||||
[[ "${!varname}" ]]
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Removes an entire hash array
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @return 0 Ok
|
||||
## @return 1 Other error
|
||||
## @return 2 Table not found
|
||||
#---------------------------------------------------------------------
|
||||
hash_reset() {
|
||||
# Get all variables with a prefix
|
||||
eval "local vars=\"\${!hsh_${1}_*}\""
|
||||
# If any variable, unset them.
|
||||
if [[ $vars ]]; then
|
||||
unset ${vars} || return 1
|
||||
else
|
||||
return 2
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Returns a space separated list of the indices of a hash array
|
||||
## @Type API
|
||||
## @param Table name
|
||||
## @param Name of variable to return result in.
|
||||
## @return 0 Ok
|
||||
## @return 1 Other error
|
||||
## @return 2 Table not found
|
||||
#---------------------------------------------------------------------
|
||||
hash_get_indices() {
|
||||
# Get all variables with a prefix
|
||||
eval "local vars=\"\${!hsh_${1}_*}\""
|
||||
# If any variable loop through and get the "normal" index.
|
||||
if [[ $vars ]]; then
|
||||
local var unhexname returnlist
|
||||
# Extract index.
|
||||
for var in $vars; do
|
||||
hash_name_getindex "$var" 'unhexname'
|
||||
returnlist+=" $unhexname"
|
||||
done
|
||||
# Return them in variable.
|
||||
printf -v "$2" '%s' "${returnlist}"
|
||||
return 0
|
||||
else
|
||||
return 2
|
||||
fi
|
||||
}
|
||||
285
lib/log.sh
Normal file
285
lib/log.sh
Normal file
@@ -0,0 +1,285 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Logging API
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log a fatal error to the main log file as well as STDOUT.
|
||||
## @Type API
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_fatal() {
|
||||
log "FATAL " "$log_color_fatal" "$1" 1
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log a fatal error to a specific log file as well as
|
||||
## the main log file and STDOUT.
|
||||
## @Type API
|
||||
## @param The extra log file (relative to the current log dir)
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_fatal_file() {
|
||||
log "FATAL " "$log_color_fatal" "$2" 1 "$1"
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log an error to the main log file as well as STDOUT.
|
||||
## @Type API
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_error() {
|
||||
log "ERROR " "$log_color_error" "$1" 1
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log an error to a specific log file as well as
|
||||
## the main log file and STDOUT.
|
||||
## @Type API
|
||||
## @param The extra log file (relative to the current log dir)
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_error_file() {
|
||||
log "ERROR " "$log_color_error" "$2" 1 "$1"
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log a warning to the main log file as well as STDOUT.
|
||||
## @Type API
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_warning() {
|
||||
log "WARNING " "$log_color_warning" "$1" 1
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log a warning to a specific log file as well as
|
||||
## the main log file and STDOUT.
|
||||
## @Type API
|
||||
## @param The extra log file (relative to the current log dir)
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_warning_file() {
|
||||
log "WARNING " "$log_color_warning" "$2" 1 "$1"
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log an info message to the main log file.
|
||||
## @Type API
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_info() {
|
||||
log "INFO " "$log_color_info" "$1" 0
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log an info message to the main log file and STDOUT.
|
||||
## Normally this shouldn't be used by modules.
|
||||
## It is used for things like "Connecting"
|
||||
## @Type API
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_info_stdout() {
|
||||
log "INFO " "$log_color_info" "$1" 1
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log an info message to a specific log file as well as
|
||||
## the main log file and STDOUT.
|
||||
## Normally this shouldn't be used by modules.
|
||||
## It is used for things like "Connecting"
|
||||
## @Type API
|
||||
## @param The extra log file (relative to the current log dir)
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_info_stdout_file() {
|
||||
log "INFO " "$log_color_info" "$2" 1 "$1"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log an info message to a specific log file as well as STDOUT.
|
||||
## @Type API
|
||||
## @param The extra log file (relative to the current log dir)
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_info_file() {
|
||||
log "INFO " "$log_color_info" "$2" 0 "$1"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log a debug message.
|
||||
## @Type API
|
||||
## @param The log message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_debug() {
|
||||
log "DEBUG " "" "$1" 0 debug.log
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
# Internal functions to core or this file below this line! #
|
||||
# Module authors: go away #
|
||||
###########################################################################
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Logging prefix
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
log_prefix="-"
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Get human readable date.
|
||||
## @Type Private
|
||||
## @Stdout Human readable date
|
||||
#---------------------------------------------------------------------
|
||||
log_get_date() {
|
||||
date +'%Y-%m-%d %k:%M:%S'
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Get escape codes from tput
|
||||
## @Type Private
|
||||
## @param capname
|
||||
## @param Return variable name
|
||||
## @return 0 OK
|
||||
## @return 1 Not supported or unknown cap
|
||||
## @Note Return variable will be unset if the value is not supported
|
||||
#---------------------------------------------------------------------
|
||||
log_check_cap() {
|
||||
tput $1 >/dev/null 2>&1
|
||||
if [[ $? -eq 0 ]]; then
|
||||
printf -v "$2" '%s' "$(tput $1)"
|
||||
else
|
||||
printf -v "$2" '%s' ''
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Log, internal to this file.
|
||||
## @Type Private
|
||||
## @param Level to log at (ERROR or such, aligned to space)
|
||||
## @param Color of level
|
||||
## @param The log message to log
|
||||
## @param Force log to stdout (0 or 1)
|
||||
## @param Optional extra file to log to.
|
||||
#---------------------------------------------------------------------
|
||||
log() {
|
||||
# Log file is set?
|
||||
[[ $log_file ]] || return 0
|
||||
# Log date.
|
||||
local logdate="$(log_get_date)"
|
||||
# ncm = No Color Message
|
||||
local ncm="$log_prefix $logdate ${1}${3}"
|
||||
echo "$ncm" >> "$log_file"
|
||||
# Extra log file?
|
||||
[[ $5 ]] && echo "$ncm" >> "$log_dir/$5"
|
||||
# STDOUT?
|
||||
if [[ $config_log_stdout -eq 1 || $4 -eq 1 ]]; then
|
||||
# Colors and then get rid of bell chars.
|
||||
echo "${log_color_std}${log_prefix}${log_color_none} $logdate ${2}${1}${log_color_none}${3//$'\007'}"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Used internally in core to log raw line
|
||||
## @Type Private
|
||||
## @param Line to log
|
||||
#---------------------------------------------------------------------
|
||||
log_raw_in() {
|
||||
[[ $config_log_raw = 1 ]] && log_raw "<" "$log_color_in" "$1"
|
||||
}
|
||||
#---------------------------------------------------------------------
|
||||
## Used internally in core to log raw line
|
||||
## @Type Private
|
||||
## @param Line to log
|
||||
#---------------------------------------------------------------------
|
||||
log_raw_out() {
|
||||
[[ $config_log_raw = 1 ]] && log_raw ">" "$log_color_out" "$1"
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Internal function to this file.
|
||||
## @Type Private
|
||||
## @param Prefix to use
|
||||
## @param Color of prefix
|
||||
## @param Message to log
|
||||
#---------------------------------------------------------------------
|
||||
log_raw() {
|
||||
# Log file is set?
|
||||
[[ $log_file ]] || return 0
|
||||
# No Color Message
|
||||
# Log date.
|
||||
local logdate="$(log_get_date)"
|
||||
# No colors for file
|
||||
echo "$1 $logdate $3" >> "$log_dir/raw.log"
|
||||
# STDOUT?
|
||||
if [[ $config_log_stdout -eq 1 ]]; then
|
||||
# Get rid of bell chars.
|
||||
echo "${2}${1}${log_color_none} $logdate RAW ${3//$'\007'}"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Create log file.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
log_init() {
|
||||
local now
|
||||
time_get_current 'now'
|
||||
# This creates log dir for this run:
|
||||
log_dir="${config_log_dir}/${now}"
|
||||
# Security, the log may contain passwords.
|
||||
mkdir -m 700 "$log_dir"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error: couldn't create log dir"
|
||||
envbot_quit 1
|
||||
fi
|
||||
log_file="${log_dir}/main.log"
|
||||
touch "$log_file"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error: couldn't create logfile"
|
||||
envbot_quit 1
|
||||
fi
|
||||
|
||||
# Should there be colors?
|
||||
if [[ $config_log_colors -eq 1 ]]; then
|
||||
local bold
|
||||
# Generate colors
|
||||
log_check_cap sgr0 log_color_none # No colour
|
||||
log_check_cap bold bold # Bold local
|
||||
log_check_cap 'setaf 1' log_color_error # Red
|
||||
log_color_fatal="${log_color_error}${bold}" # Red bold
|
||||
log_check_cap 'setaf 3' log_color_warning # Yellow
|
||||
log_check_cap 'setaf 2' log_color_info # Green
|
||||
log_check_cap 'setaf 4' log_color_std # Blue bold, for standard prefix
|
||||
log_color_std+="${bold}"
|
||||
log_check_cap 'setaf 5' log_color_in # Magenta, for prefix
|
||||
log_check_cap 'setaf 6' log_color_out # Cyan, for prefix
|
||||
fi
|
||||
|
||||
log_info_stdout "Log directory is $log_dir"
|
||||
}
|
||||
566
lib/main.sh
Normal file
566
lib/main.sh
Normal file
@@ -0,0 +1,566 @@
|
||||
#!/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 is the main file, it should be called with a wrapper (envbot)
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
|
||||
###################
|
||||
# #
|
||||
# Sanity checks #
|
||||
# #
|
||||
###################
|
||||
|
||||
# Error to fail with for old bash.
|
||||
fail_old_bash() {
|
||||
echo "Sorry your bash version is too old!"
|
||||
echo "You need at least version 3.2.10 of bash"
|
||||
echo "Please install a newer version:"
|
||||
echo " * Either use your distro's packages"
|
||||
echo " * Or see http://www.gnu.org/software/bash/"
|
||||
exit 2
|
||||
}
|
||||
|
||||
# Check bash version. We need at least 3.2.10
|
||||
# Lets not use anything like =~ here because
|
||||
# that may not work on old bash versions.
|
||||
if [[ "${BASH_VERSINFO[0]}${BASH_VERSINFO[1]}" -lt 32 ]]; then
|
||||
fail_old_bash
|
||||
elif [[ "${BASH_VERSINFO[0]}${BASH_VERSINFO[1]}" -eq 32 && "${BASH_VERSINFO[2]}" -lt 10 ]]; then
|
||||
fail_old_bash
|
||||
fi
|
||||
|
||||
# We should not run as root.
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
echo "ERROR: Don't run envbot as root. Please run it under a normal user. Really."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
######################
|
||||
# #
|
||||
# Set up variables #
|
||||
# #
|
||||
######################
|
||||
|
||||
# Version and URL
|
||||
#---------------------------------------------------------------------
|
||||
## Version of envbot.
|
||||
## @Type API
|
||||
## @Read_only Yes
|
||||
#---------------------------------------------------------------------
|
||||
declare -r envbot_version='0.1-beta1'
|
||||
#---------------------------------------------------------------------
|
||||
## Homepage of envbot.
|
||||
## @Type API
|
||||
## @Read_only Yes
|
||||
#---------------------------------------------------------------------
|
||||
declare -r envbot_homepage='http://envbot.org'
|
||||
|
||||
##############
|
||||
# #
|
||||
# Sane env #
|
||||
# #
|
||||
##############
|
||||
|
||||
# Set some variables to make bot work sane
|
||||
# For example tr + some LC_COLLATE = breaks in some cases.
|
||||
unset LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY
|
||||
unset LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS
|
||||
unset LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION
|
||||
export LC_ALL=C
|
||||
export LANG=C
|
||||
|
||||
# Some of these may be overkill, but better be on
|
||||
# safe side.
|
||||
set +amu
|
||||
set -f
|
||||
shopt -u sourcepath hostcomplete progcomp xpg_echo dotglob
|
||||
shopt -u nocasematch nocaseglob nullglob
|
||||
shopt -s extquote promptvars extglob
|
||||
|
||||
# If you need some other PATH, override in top of config...
|
||||
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
|
||||
|
||||
# To make set -x more usable
|
||||
export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]} : '
|
||||
|
||||
|
||||
# This is needed when we run the bot with env -i as recommended.
|
||||
declare -r tmp_home="$(mktemp -dt envbot.home.XXXXXXXXXX)"
|
||||
# I don't want to end up with rm -rf $HOME in case it is something
|
||||
# else at that point, so lets use another variable.
|
||||
|
||||
# Temp trap on ctrl-c until the next "stage" of trap gets loaded (at connect)
|
||||
trap 'rm -rvf "$tmp_home"; exit 1' TERM INT
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Now create a temp function to quit on problems in a way that cleans up
|
||||
## temp stuff until we have loaded enough to use the normal function bot_quit.
|
||||
## @param Return status of bot
|
||||
#---------------------------------------------------------------------
|
||||
envbot_quit() {
|
||||
rm -rf "$tmp_home"
|
||||
exit "$1"
|
||||
}
|
||||
|
||||
# And finally lets export this as $HOME
|
||||
export HOME="$tmp_home"
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Will be set to 1 if -v or --verbose is passed
|
||||
## on command line.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
force_verbose=0
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Store command line for later use
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
command_line=( "$@" )
|
||||
|
||||
# Some constants used in different places
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Current config version.
|
||||
## @Type API
|
||||
## @Read_only Yes
|
||||
#---------------------------------------------------------------------
|
||||
declare -r config_current_version=17
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## In progress of quitting? This is used to
|
||||
## work around the issue in bug 25.<br />
|
||||
## -1 means not even in main loop yet.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
envbot_quitting=-1
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## If empty debugging is turned off. If not empty it is on.
|
||||
#---------------------------------------------------------------------
|
||||
envbot_debugging=''
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Print help message
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
print_cmd_help() {
|
||||
echo 'envbot is an advanced modular IRC bot coded in bash.'
|
||||
echo ''
|
||||
echo 'Usage: envbot [OPTION]...'
|
||||
echo ''
|
||||
echo 'Options:'
|
||||
echo ' -c, --config file Use file instead of the default as config file.'
|
||||
echo ' -l, --libdir directory Use directory instead of the default as library directory.'
|
||||
echo ' -v, --verbose Force verbose output even if config_log_stdout is 0.'
|
||||
echo ' -d, --debug Enable debugging code. Most likely pointless to anyone'
|
||||
echo ' except envbot developers or module developers.'
|
||||
echo ' -h, --help Display this help and exit'
|
||||
echo ' -V, --version Output version information and exit'
|
||||
echo ''
|
||||
echo "Note that envbot can't handle short versions of options being written together like"
|
||||
echo "-vv currently."
|
||||
echo ''
|
||||
echo 'Exit status is 0 if OK, 1 if minor problems, 2 if serious trouble.'
|
||||
echo ''
|
||||
echo 'Examples:'
|
||||
echo ' envbot Runs envbot with default options.'
|
||||
echo ' envbot -c bot.config Runs envbot with the config bot.config.'
|
||||
echo ''
|
||||
echo "Report bugs to ${envbot_homepage}/trac/simpleticket"
|
||||
envbot_quit 0
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Print version message
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
print_version() {
|
||||
echo "envbot $envbot_version - An advanced modular IRC bot in bash."
|
||||
echo ''
|
||||
echo 'Copyright (C) 2007-2008 Arvid Norlander'
|
||||
echo 'Copyright (C) 2007-2008 EmErgE'
|
||||
echo 'This is free software; see the source for copying conditions. There is NO'
|
||||
echo 'warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.'
|
||||
echo ''
|
||||
echo 'Written by Arvid Norlander and EmErgE.'
|
||||
envbot_quit 0
|
||||
}
|
||||
|
||||
# Parse any command line arguments.
|
||||
if [[ $# -gt 0 ]]; then
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
'--help'|'-help'|'--usage'|'-usage'|'-h')
|
||||
print_cmd_help
|
||||
;;
|
||||
'--config'|'-c')
|
||||
config_file="$2"
|
||||
shift 2
|
||||
;;
|
||||
'--debug'|'-d')
|
||||
envbot_debugging=1
|
||||
shift 1
|
||||
;;
|
||||
'--libdir'|'-l')
|
||||
library_dir="$2"
|
||||
shift 2
|
||||
;;
|
||||
'--verbose'|'-v')
|
||||
force_verbose=1
|
||||
shift 1
|
||||
;;
|
||||
'--version'|'-V')
|
||||
print_version
|
||||
;;
|
||||
*)
|
||||
print_cmd_help
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
echo "Loading... Please wait"
|
||||
|
||||
if [[ -z "$config_file" ]]; then
|
||||
echo "ERROR: No config file set, you probably didn't use the wrapper program to start envbot"
|
||||
envbot_quit 1
|
||||
fi
|
||||
|
||||
if [[ ! -r "$config_file" ]]; then
|
||||
echo "ERROR: Can't read config file ${config_file}."
|
||||
echo "Check that it is really there and correct permissions are set."
|
||||
echo "If you used --config to specify name of config file, check that you spelled it correctly."
|
||||
envbot_quit 1
|
||||
fi
|
||||
|
||||
echo "Loading config"
|
||||
source "$config_file"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
echo "Error: couldn't load config from $config_file"
|
||||
envbot_quit 1
|
||||
fi
|
||||
|
||||
# This is hackish, it should be in config.sh (config_validate)
|
||||
# The reason is that we need to check some things before we can load config.sh
|
||||
if [[ -z "$config_version" ]]; then
|
||||
echo "ERROR: YOU MUST SET THE CORRECT config_version IN THE CONFIG"
|
||||
envbot_quit 2
|
||||
fi
|
||||
if [[ $config_version -ne $config_current_version ]]; then
|
||||
echo "ERROR: YOUR config_version IS $config_version BUT THE BOT'S CONFIG VERSION IS $config_current_version."
|
||||
echo "PLEASE UPDATE YOUR CONFIG. Check bot_settings.sh.example for current format."
|
||||
envbot_quit 2
|
||||
fi
|
||||
|
||||
# Force verbose output if -v or --verbose was on
|
||||
# command line.
|
||||
if [[ $force_verbose -eq 1 ]]; then
|
||||
config_log_stdout='1'
|
||||
fi
|
||||
|
||||
# Must be checked here and not in validate_config because of
|
||||
# loading order.
|
||||
if [[ -z "$library_dir" ]]; then
|
||||
echo "ERROR: No library directory set, you probably didn't use the wrapper program to start envbot"
|
||||
envbot_quit 1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$library_dir" ]]; then
|
||||
echo "ERROR: library directory $library_dir does not exist, is not a directory or can't be read for some other reason."
|
||||
echo "Check that it is really there and correct permissions are set."
|
||||
echo "If you used --libdir to specify location of library directory, check that you spelled it correctly."
|
||||
envbot_quit 2
|
||||
fi
|
||||
|
||||
echo "Loading library functions"
|
||||
# Load library functions.
|
||||
libraries="hash time log send feedback numerics channels parse \
|
||||
access misc config commands modules server debug"
|
||||
for library in $libraries; do
|
||||
source "${library_dir}/${library}.sh"
|
||||
done
|
||||
unset library
|
||||
|
||||
# Validate other config variables.
|
||||
config_validate
|
||||
time_init
|
||||
log_init
|
||||
debug_init
|
||||
|
||||
log_info_stdout "Loading transport"
|
||||
source "${config_transport_dir}/${config_transport}.sh"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log_fatal "Couldn't load transport. Couldn't load the file..."
|
||||
envbot_quit 2
|
||||
fi
|
||||
|
||||
if ! transport_check_support; then
|
||||
log_fatal "The transport reported it can't work on this system or with this configuration."
|
||||
log_fatal "Please read any other errors displayed above and consult documentation for the transport module you are using."
|
||||
envbot_quit 2
|
||||
fi
|
||||
|
||||
# Now logging functions can be used.
|
||||
|
||||
# Load modules
|
||||
|
||||
log_info_stdout "Loading modules"
|
||||
# Load modules
|
||||
modules_load_from_config
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## This can be used when the code does not need exact time.
|
||||
## It will be updated each time the bot get a new line of
|
||||
## data.
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
envbot_time=''
|
||||
server_connected_before=0
|
||||
while true; do
|
||||
# In progress of quitting? This is used to
|
||||
# work around the issue in bug 25.
|
||||
envbot_quitting=0
|
||||
for module in $modules_before_connect; do
|
||||
module_${module}_before_connect
|
||||
done
|
||||
|
||||
if [[ $server_connected_before -ne 0 ]]; then
|
||||
# We got here by being connected before and
|
||||
# loosing connection, keep retrying
|
||||
while true; do
|
||||
if server_connect; then
|
||||
server_connected_before=1
|
||||
break
|
||||
else
|
||||
log_error "Failed to reconnect, trying again in 20 seconds"
|
||||
sleep 20
|
||||
fi
|
||||
done
|
||||
else
|
||||
# In this case abort on failure to connect, likely bad config.
|
||||
# and most likely the user is present to fix it.
|
||||
# If someone disagrees I may change it.
|
||||
server_connect || {
|
||||
log_error "Connection failed"
|
||||
envbot_quit 1
|
||||
}
|
||||
server_connected_before=1
|
||||
fi
|
||||
trap 'bot_quit "Interrupted (Ctrl-C)"' INT
|
||||
trap 'bot_quit "Terminated (SIGTERM)"' TERM
|
||||
for module in $modules_after_connect; do
|
||||
module_${module}_after_connect
|
||||
done
|
||||
|
||||
while true; do
|
||||
line=
|
||||
transport_read_line
|
||||
transport_status="$?"
|
||||
# Still connected?
|
||||
if ! transport_alive; then
|
||||
break
|
||||
fi
|
||||
time_get_current 'envbot_time'
|
||||
|
||||
# Did we timeout waiting for data
|
||||
# or did we get data?
|
||||
if [[ $transport_status -ne 0 ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
log_raw_in "$line"
|
||||
for module in $modules_on_raw; do
|
||||
module_${module}_on_raw "$line"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
# TODO: Check that this does what it should.
|
||||
continue 2
|
||||
fi
|
||||
done
|
||||
if [[ $line =~ ^:${server_name}\ +([0-9]{3})\ +([^ ]+)\ +(.*) ]]; then
|
||||
# this is a numeric
|
||||
numeric="${BASH_REMATCH[1]}"
|
||||
numericdata="${BASH_REMATCH[3]}"
|
||||
server_handle_numerics "$numeric" "${BASH_REMATCH[2]}" "$numericdata"
|
||||
for module in $modules_on_numeric; do
|
||||
module_${module}_on_numeric "$numeric" "$numericdata"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +PRIVMSG\ +([^:]+)\ +:(.*) ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
target="${BASH_REMATCH[2]}"
|
||||
query="${BASH_REMATCH[3]}"
|
||||
# Check if there is a command.
|
||||
commands_call_command "$sender" "$target" "$query"
|
||||
# Check return code
|
||||
case $? in
|
||||
1)
|
||||
continue
|
||||
;;
|
||||
2)
|
||||
if [[ $config_feedback_unknown_commands -eq 0 ]]; then
|
||||
continue
|
||||
elif [[ $config_feedback_unknown_commands -eq 1 ]]; then
|
||||
feedback_unknown_command "$sender" "$target" "$query"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
for module in $modules_on_PRIVMSG; do
|
||||
module_${module}_on_PRIVMSG "$sender" "$target" "$query"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +NOTICE\ +([^:]+)\ +:(.*) ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
target="${BASH_REMATCH[2]}"
|
||||
query="${BASH_REMATCH[3]}"
|
||||
for module in $modules_on_NOTICE; do
|
||||
module_${module}_on_PRIVMSG "$sender" "$target" "$query"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +TOPIC\ +(#[^ ]+)(\ +:(.*))? ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
channel="${BASH_REMATCH[2]}"
|
||||
topic="${BASH_REMATCH[4]}"
|
||||
for module in $modules_on_TOPIC; do
|
||||
module_${module}_on_TOPIC "$sender" "$channel" "$topic"
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +MODE\ +(#[^ ]+)\ +(.+) ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
channel="${BASH_REMATCH[2]}"
|
||||
modes="${BASH_REMATCH[3]}"
|
||||
for module in $modules_on_channel_MODE ; do
|
||||
module_${module}_on_channel_MODE "$sender" "$channel" "$modes"
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +MODE\ +([^# ]+)\ +(.+) ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
target="${BASH_REMATCH[2]}"
|
||||
modes="${BASH_REMATCH[3]}"
|
||||
for module in $modules_on_user_MODE ; do
|
||||
module_${module}_on_user_MODE "$sender" "$target" "$modes"
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +INVITE\ +([^ ]+)\ +:?(.+) ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
target="${BASH_REMATCH[2]}"
|
||||
channel="${BASH_REMATCH[3]}"
|
||||
for module in $modules_on_INVITE; do
|
||||
module_${module}_on_INVITE "$sender" "$target" "$channel"
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +NICK\ +:?(.+) ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
newnick="${BASH_REMATCH[2]}"
|
||||
# Check if it was our own nick
|
||||
server_handle_nick "$sender" "$newnick"
|
||||
for module in $modules_on_NICK; do
|
||||
module_${module}_on_NICK "$sender" "$newnick"
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +JOIN\ +:?(.*) ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
channel="${BASH_REMATCH[2]}"
|
||||
# Check if it was our own nick that joined
|
||||
channels_handle_join "$sender" "$channel"
|
||||
for module in $modules_on_JOIN; do
|
||||
module_${module}_on_JOIN "$sender" "$channel"
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +PART\ +(#[^ ]+)(\ +:(.*))? ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
channel="${BASH_REMATCH[2]}"
|
||||
reason="${BASH_REMATCH[4]}"
|
||||
# Check if it was our own nick that parted
|
||||
channels_handle_part "$sender" "$channel" "$reason"
|
||||
for module in $modules_on_PART; do
|
||||
module_${module}_on_PART "$sender" "$channel" "$reason"
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +KICK\ +(#[^ ]+)\ +([^ ]+)(\ +:(.*))? ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
channel="${BASH_REMATCH[2]}"
|
||||
kicked="${BASH_REMATCH[3]}"
|
||||
reason="${BASH_REMATCH[5]}"
|
||||
# Check if it was our own nick that got kicked
|
||||
channels_handle_kick "$sender" "$channel" "$kicked" "$reason"
|
||||
for module in $modules_on_KICK; do
|
||||
module_${module}_on_KICK "$sender" "$channel" "$kicked" "$reason"
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +QUIT(\ +:(.*))? ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
reason="${BASH_REMATCH[3]}"
|
||||
for module in $modules_on_QUIT; do
|
||||
module_${module}_on_QUIT "$sender" "$reason"
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +KILL\ +([^ ]*)\ +:([^ ]*)\ +\((.*)\) ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
target="${BASH_REMATCH[2]}"
|
||||
path="${BASH_REMATCH[3]}"
|
||||
reason="${BASH_REMATCH[4]}"
|
||||
# I don't think we need to check if we were the target or not,
|
||||
# the bot doesn't need to care as far as I can see.
|
||||
for module in $modules_on_KILL; do
|
||||
module_${module}_on_KILL "$sender" "$target" "$path" "$reason"
|
||||
done
|
||||
elif [[ "$line" =~ ^:([^ ]*)\ +PONG\ +([^ ]*)\ +:?(.*)$ ]]; then
|
||||
sender="${BASH_REMATCH[1]}"
|
||||
server2="${BASH_REMATCH[2]}"
|
||||
data="${BASH_REMATCH[3]}"
|
||||
for module in $modules_on_PONG; do
|
||||
module_${module}_on_PONG "$sender" "$server2" "$data"
|
||||
done
|
||||
elif [[ $line =~ ^[^:] ]] ;then
|
||||
# ERROR?
|
||||
if [[ "$line" =~ ^ERROR\ +:(.*) ]]; then
|
||||
error="${BASH_REMATCH[1]}"
|
||||
log_error "Got ERROR from server: $error"
|
||||
for module in $modules_on_server_ERROR; do
|
||||
module_${module}_on_server_ERROR "$error"
|
||||
done
|
||||
# If we get an ERROR we can assume we are disconnected.
|
||||
break
|
||||
# PING? If not report as unhandled
|
||||
elif ! server_handle_ping "$line"; then
|
||||
log_info_file unknown_data.log "A non-sender prefixed line that didn't match any hook: $line"
|
||||
fi
|
||||
else
|
||||
log_info_file unknown_data.log "Something that didn't match any hook: $line"
|
||||
fi
|
||||
done
|
||||
if [[ $envbot_quitting -ne 0 ]]; then
|
||||
# Hm, a trap got aborted it seems.
|
||||
# Trying to handle this.
|
||||
log_info "Quit trap got aborted: envbot_quitting=${envbot_quitting}. Recovering"
|
||||
bot_quit
|
||||
break
|
||||
fi
|
||||
log_error 'DIED FOR SOME REASON'
|
||||
transport_disconnect
|
||||
server_connected=0
|
||||
for module in $modules_after_disconnect; do
|
||||
module_${module}_after_disconnect
|
||||
done
|
||||
# Don't reconnect right away. We might get throttled and other nasty stuff.
|
||||
sleep 10
|
||||
done
|
||||
rm -rf "$tmp_home"
|
||||
267
lib/misc.sh
Normal file
267
lib/misc.sh
Normal file
@@ -0,0 +1,267 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Misc functions.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
|
||||
# Some codes for IRC formatting
|
||||
#---------------------------------------------------------------------
|
||||
## IRC formatting: Bold
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
format_bold=$'\002'
|
||||
#---------------------------------------------------------------------
|
||||
## IRC formatting: Underline
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
format_underline=$'\037'
|
||||
#---------------------------------------------------------------------
|
||||
## IRC formatting: Color
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
format_color=$'\003'
|
||||
#---------------------------------------------------------------------
|
||||
## IRC formatting: Inverse
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
format_inverse=$'\026'
|
||||
#---------------------------------------------------------------------
|
||||
## IRC formatting: Restore to normal
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
format_normal=$'\017'
|
||||
#---------------------------------------------------------------------
|
||||
## IRC formatting: ASCII bell
|
||||
## Please. Don't. Abuse. This.
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
format_bell=$'\007'
|
||||
|
||||
# Color table:
|
||||
# white 0
|
||||
# black 1
|
||||
# blue 2
|
||||
# green 3
|
||||
# red 4
|
||||
# darkred 5
|
||||
# purple 6
|
||||
# darkyellow 7
|
||||
# yellow 8
|
||||
# brightgreen 9
|
||||
# darkaqua 10
|
||||
# aqua 11
|
||||
# lightblue 12
|
||||
# brightpurple 13
|
||||
# darkgrey 14
|
||||
# lightgrey 15
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## This will add colors around this text.
|
||||
## @Type API
|
||||
## @param Foreground color
|
||||
## @param Background color
|
||||
## @param String to colorise
|
||||
#---------------------------------------------------------------------
|
||||
format_colorise() {
|
||||
echo "${format_color}${1},${2}${3}${format_normal}"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Quits the bot in a graceful way.
|
||||
## @Type API
|
||||
## @param Reason to quit (optional)
|
||||
## @param Return status (optional, if not given, then exit 0).
|
||||
#---------------------------------------------------------------------
|
||||
bot_quit() {
|
||||
# Yes this function is odd but there is a reason.
|
||||
# If this is called from a trap like Ctrl-C we must be able to
|
||||
# resume.
|
||||
# Keep track of in what state we are
|
||||
while true; do
|
||||
case "$envbot_quitting" in
|
||||
0)
|
||||
for module in $modules_before_disconnect; do
|
||||
module_${module}_before_disconnect
|
||||
done
|
||||
(( envbot_quitting++ ))
|
||||
;;
|
||||
1)
|
||||
local reason="$1"
|
||||
send_quit "$reason"
|
||||
sleep 1
|
||||
(( envbot_quitting++ ))
|
||||
;;
|
||||
2)
|
||||
server_connected=0
|
||||
for module in $modules_after_disconnect; do
|
||||
module_${module}_after_disconnect
|
||||
done
|
||||
(( envbot_quitting++ ))
|
||||
;;
|
||||
3)
|
||||
for module in $modules_FINALISE; do
|
||||
module_${module}_FINALISE
|
||||
done
|
||||
(( envbot_quitting++ ))
|
||||
;;
|
||||
4)
|
||||
log_info_stdout "Bot quit gracefully"
|
||||
transport_disconnect
|
||||
(( envbot_quitting++ ))
|
||||
;;
|
||||
# -1 is before main loop entered,
|
||||
# may happen during module loading
|
||||
5|-1)
|
||||
rm -rvf "$tmp_home"
|
||||
if [[ $2 ]]; then
|
||||
exit $2
|
||||
else
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
log_error "Um. bot_quit() and envbot_quitting is $envbot_quitting. This shouldn't happen."
|
||||
log_error "Please report a bug including the last 40 lines or so of log and what you did to cause it."
|
||||
# Quit and clean up temp files.
|
||||
envbot_quit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Restart the bot in a graceful way. I hope.
|
||||
## @Type API
|
||||
## @param Reason to restart (optional)
|
||||
#---------------------------------------------------------------------
|
||||
bot_restart() {
|
||||
for module in $modules_before_disconnect; do
|
||||
module_${module}_before_disconnect
|
||||
done
|
||||
local reason="$1"
|
||||
send_quit "$reason"
|
||||
sleep 1
|
||||
server_connected=0
|
||||
for module in $modules_after_disconnect; do
|
||||
module_${module}_after_disconnect
|
||||
done
|
||||
for module in $modules_FINALISE; do
|
||||
module_${module}_FINALISE
|
||||
done
|
||||
log_info_stdout "Bot quit gracefully"
|
||||
transport_disconnect
|
||||
rm -rvf "$tmp_home"
|
||||
exec env -i TERM="$TERM" "$(type -p bash)" $0 "${command_line[@]}"
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Strip leading/trailing spaces.
|
||||
## @Type API
|
||||
## @Note Before this function was deprecated, but it has been recoded
|
||||
## @Note in a much faster way. This version is not compatible with old
|
||||
## @Note version.
|
||||
## @param String to strip
|
||||
## @param Variable to return in
|
||||
#---------------------------------------------------------------------
|
||||
misc_clean_spaces() {
|
||||
# Fastest way that is still secure
|
||||
local array
|
||||
read -ra array <<< "$1"
|
||||
printf -v "$2" '%s' "${array[*]}"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Strip leading/trailing separator.
|
||||
## @Type API
|
||||
## @param String to strip
|
||||
## @param Variable to return in
|
||||
## @param Separator
|
||||
#---------------------------------------------------------------------
|
||||
misc_clean_delimiter() {
|
||||
local sep="$3" array
|
||||
local IFS="$sep"
|
||||
# Fastest way that is still secure
|
||||
read -ra array <<< "$1"
|
||||
local tmp="${array[*]}"
|
||||
printf -v "$2" '%s' "${tmp#${sep}}"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Remove a value from a space (or other delimiter) separated list.
|
||||
## @Type API
|
||||
## @param List to remove from.
|
||||
## @param Value to remove.
|
||||
## @param Variable to return new list in.
|
||||
## @param Separator (optional, defaults to space)
|
||||
#---------------------------------------------------------------------
|
||||
list_remove() {
|
||||
local sep=${4:-" "}
|
||||
local oldlist="${sep}${!1}${sep}"
|
||||
local newlist="${oldlist//${sep}${2}${sep}/${sep}}"
|
||||
misc_clean_delimiter "$newlist" "$3" "$sep" # Get rid of the unneeded spaces.
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Checks if a space separated list contains a value.
|
||||
## @Type API
|
||||
## @param List to check.
|
||||
## @param Value to check for.
|
||||
## @return 0 If found.
|
||||
## @return 1 If not found.
|
||||
#---------------------------------------------------------------------
|
||||
list_contains() {
|
||||
[[ " ${!1} " = *" $2 "* ]]
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
# Internal functions to core or this file below this line! #
|
||||
# Module authors: go away #
|
||||
###########################################################################
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Like debug_assert_argc but works without debugging on.
|
||||
## For use in sensitive functions in core.
|
||||
## @Type Private
|
||||
## @param Minimum count of parameters
|
||||
## @param Maximum count of parameters
|
||||
## @param All the rest of the parameters as "$@"
|
||||
## @Example For example this could be called as:
|
||||
## @Example <pre>
|
||||
## @Example foo() {
|
||||
## @Example security_assert_argc 2 2 "$@"
|
||||
## @Example ... rest of function ...
|
||||
## @Example }
|
||||
## @Example </pre>
|
||||
#---------------------------------------------------------------------
|
||||
security_assert_argc() {
|
||||
local min="$1" max="$2"
|
||||
shift 2
|
||||
if [[ $# -lt $min || $# -gt $max ]]; then
|
||||
log_error "Security sensitive function ${FUNCNAME[1]} should have had between $min and $max parameters but had $# instead."
|
||||
log_error "Security sensitive function ${FUNCNAME[1]} was called from ${BASH_SOURCE[2]}:${BASH_LINENO[1]} ${FUNCNAME[2]} with these parameters: $*"
|
||||
log_error "This should be reported as a bug."
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
447
lib/modules.sh
Normal file
447
lib/modules.sh
Normal file
@@ -0,0 +1,447 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Modules management
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## List of loaded modules. Don't change from other code.
|
||||
## @Type Semi-private
|
||||
#---------------------------------------------------------------------
|
||||
modules_loaded=""
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Current module API version.
|
||||
#---------------------------------------------------------------------
|
||||
declare -r modules_current_API=2
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Call from after_load with a list of modules that you depend on
|
||||
## @Type API
|
||||
## @param What module you are calling from.
|
||||
## @param Space separated list of modules you depend on
|
||||
## @return 0 Success
|
||||
## @return 1 Other error. You should return 1 from after_load.
|
||||
## @return 2 One or several of the dependencies could found. You should return 1 from after_load.
|
||||
## @return 3 Not all of the dependencies could be loaded (modules exist but did not load correctly). You should return 1 from after_load.
|
||||
#---------------------------------------------------------------------
|
||||
modules_depends_register() {
|
||||
local callermodule="$1"
|
||||
local dep
|
||||
for dep in $2; do
|
||||
if [[ $dep == $callermodule ]]; then
|
||||
log_error_file modules.log "To the module author of $callermodule: You can't list yourself as a dependency of yourself!"
|
||||
log_error_file modules.log "Aborting!"
|
||||
return 1
|
||||
fi
|
||||
if ! list_contains "modules_loaded" "$dep"; then
|
||||
log_info_file modules.log "Loading dependency of $callermodule: $dep"
|
||||
modules_load "$dep"
|
||||
local status="$?"
|
||||
if [[ $status -eq 4 ]]; then
|
||||
return 2
|
||||
elif [[ $status -ne 0 ]]; then
|
||||
return 3
|
||||
fi
|
||||
fi
|
||||
if list_contains "modules_depends_${dep}" "$callermodule"; then
|
||||
log_warning_file modules.log "Dependency ${callermodule} already listed as depending on ${dep}!?"
|
||||
fi
|
||||
# Use printf not eval here.
|
||||
local listname="modules_depends_${dep}"
|
||||
printf -v "modules_depends_${dep}" '%s' "${!listname} $callermodule"
|
||||
done
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Call from after_load or INIT with a list of modules that you
|
||||
## depend on optionally.
|
||||
## @Type API
|
||||
## @param What module you are calling from.
|
||||
## @param The module you want to depend on optionally.
|
||||
## @return 0 Success, module loaded
|
||||
## @return 1 User didn't list it as loaded, don't use the features in question
|
||||
## @return 2 Other error. You should return 1 from after_load.
|
||||
## @return 3 One or several of the dependencies could found. You should return 1 from after_load.
|
||||
## @return 4 Not all of the dependencies could be loaded (modules exist but did not load correctly). You should return 1 from after_load.
|
||||
#---------------------------------------------------------------------
|
||||
modules_depends_register_optional() {
|
||||
local callermodule="$1"
|
||||
local dep="$2"
|
||||
if ! list_contains "modules_loaded" "$dep"; then
|
||||
# So not loaded, now we need to find out if we should load it or not
|
||||
# We use $config_modules for it
|
||||
if ! list_contains 'config_modules' "$dep"; then
|
||||
log_info_file modules.log "Optional dependency of $callermodule ($dep) not loaded."
|
||||
return 1
|
||||
fi
|
||||
log_info_file modules.log "Loading optional dependency of $callermodule: ($dep)"
|
||||
fi
|
||||
# Ah we should load it then? Call modules_depends_register
|
||||
modules_depends_register "$@"
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Semi internal!
|
||||
## List modules that depend on another module.
|
||||
## @Type Semi-private
|
||||
## @param Module to check
|
||||
## @Stdout List of modules that depend on this.
|
||||
#---------------------------------------------------------------------
|
||||
modules_depends_list_deps() {
|
||||
# This is needed to be able to use indirect refs
|
||||
local deplistname="modules_depends_${1}"
|
||||
# Clean out spaces, fastest way
|
||||
echo ${!deplistname}
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
# Internal functions to core or this file below this line! #
|
||||
# Module authors: go away #
|
||||
# See doc/module_api.txt instead #
|
||||
###########################################################################
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Used by unload to unregister from depends system
|
||||
## (That is: remove from list of "depended on by" of other modules)
|
||||
## @Type Private
|
||||
## @param Module to unregister
|
||||
#---------------------------------------------------------------------
|
||||
modules_depends_unregister() {
|
||||
local module newval
|
||||
for module in $modules_loaded; do
|
||||
if list_contains "modules_depends_${module}" "$1"; then
|
||||
list_remove "modules_depends_${module}" "$1" "modules_depends_${module}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Check if a module can be unloaded
|
||||
## @Type Private
|
||||
## @param Name of module to check
|
||||
## @return Can be unloaded
|
||||
## @return Is needed by some other module.
|
||||
#---------------------------------------------------------------------
|
||||
modules_depends_can_unload() {
|
||||
# This is needed to be able to use indirect refs
|
||||
local deplistname="modules_depends_${1}"
|
||||
# Not empty/only whitespaces?
|
||||
if ! [[ ${!deplistname} =~ ^\ *$ ]]; then
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Add hooks for a module
|
||||
## @Type Private
|
||||
## @param Module name
|
||||
## @param MODULE_BASE_PATH, exported to INIT as a part of the API
|
||||
## @return 0 Success
|
||||
## @return 1 module_modulename_INIT returned non-zero
|
||||
## @return 2 Module wanted to register an unknown hook.
|
||||
#---------------------------------------------------------------------
|
||||
modules_add_hooks() {
|
||||
local module="$1"
|
||||
local modinit_HOOKS
|
||||
local modinit_API
|
||||
local MODULE_BASE_PATH="$2"
|
||||
module_${module}_INIT "$module"
|
||||
[[ $? -ne 0 ]] && { log_error_file modules.log "Failed to get initialize module \"$module\""; return 1; }
|
||||
# Check if it didn't set any modinit_API, in that case it is a API 1 module.
|
||||
if [[ -z $modinit_API ]]; then
|
||||
log_error "Please upgrade \"$module\" to new module API $modules_current_API. This old API is obsolete and no longer supported."
|
||||
return 1
|
||||
elif [[ $modinit_API -ne $modules_current_API ]]; then
|
||||
log_error "Current module API version is $modules_current_API, but the API version of \"$module\" is $module_API."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local hook
|
||||
for hook in $modinit_HOOKS; do
|
||||
case $hook in
|
||||
"FINALISE")
|
||||
modules_FINALISE+=" $module"
|
||||
;;
|
||||
"after_load")
|
||||
modules_after_load+=" $module"
|
||||
;;
|
||||
"before_connect")
|
||||
modules_before_connect+=" $module"
|
||||
;;
|
||||
"on_connect")
|
||||
modules_on_connect+=" $module"
|
||||
;;
|
||||
"after_connect")
|
||||
modules_after_connect+=" $module"
|
||||
;;
|
||||
"before_disconnect")
|
||||
modules_before_disconnect+=" $module"
|
||||
;;
|
||||
"after_disconnect")
|
||||
modules_after_disconnect+=" $module"
|
||||
;;
|
||||
"on_module_UNLOAD")
|
||||
modules_on_module_UNLOAD+=" $module"
|
||||
;;
|
||||
"on_server_ERROR")
|
||||
modules_on_server_ERROR+=" $module"
|
||||
;;
|
||||
"on_NOTICE")
|
||||
modules_on_NOTICE+=" $module"
|
||||
;;
|
||||
"on_PRIVMSG")
|
||||
modules_on_PRIVMSG+=" $module"
|
||||
;;
|
||||
"on_TOPIC")
|
||||
modules_on_TOPIC+=" $module"
|
||||
;;
|
||||
"on_channel_MODE")
|
||||
modules_on_channel_MODE+=" $module"
|
||||
;;
|
||||
"on_user_MODE")
|
||||
modules_on_user_MODE+=" $module"
|
||||
;;
|
||||
"on_INVITE")
|
||||
modules_on_INVITE+=" $module"
|
||||
;;
|
||||
"on_JOIN")
|
||||
modules_on_JOIN+=" $module"
|
||||
;;
|
||||
"on_PART")
|
||||
modules_on_PART+=" $module"
|
||||
;;
|
||||
"on_KICK")
|
||||
modules_on_KICK+=" $module"
|
||||
;;
|
||||
"on_QUIT")
|
||||
modules_on_QUIT+=" $module"
|
||||
;;
|
||||
"on_KILL")
|
||||
modules_on_KILL+=" $module"
|
||||
;;
|
||||
"on_NICK")
|
||||
modules_on_NICK+=" $module"
|
||||
;;
|
||||
"on_numeric")
|
||||
modules_on_numeric+=" $module"
|
||||
;;
|
||||
"on_PONG")
|
||||
modules_on_PONG+=" $module"
|
||||
;;
|
||||
"on_raw")
|
||||
modules_on_raw+=" $module"
|
||||
;;
|
||||
*)
|
||||
log_error_file modules.log "Unknown hook $hook requested. Module may malfunction. Module will be unloaded"
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## List of all the optional hooks.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
modules_hooks="FINALISE after_load before_connect on_connect after_connect before_disconnect after_disconnect on_module_UNLOAD on_server_ERROR on_NOTICE on_PRIVMSG on_TOPIC on_channel_MODE on_user_MODE on_INVITE on_JOIN on_PART on_KICK on_QUIT on_KILL on_NICK on_numeric on_PONG on_raw"
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Unload a module
|
||||
## @Type Private
|
||||
## @param Module name
|
||||
## @return 0 Unloaded
|
||||
## @return 2 Module not loaded
|
||||
## @return 3 Can't unload, some other module depends on this.
|
||||
## @Note If the unload fails for other reasons the bot will quit.
|
||||
#---------------------------------------------------------------------
|
||||
modules_unload() {
|
||||
local module="$1"
|
||||
local hook newval to_unset
|
||||
if ! list_contains "modules_loaded" "$module"; then
|
||||
log_warning_file modules.log "No such module as $1 is loaded."
|
||||
return 2
|
||||
fi
|
||||
if ! modules_depends_can_unload "$module"; then
|
||||
log_error_file modules.log "Can't unload $module because these module(s) depend(s) on it: $(modules_depends_list_deps "$module")"
|
||||
return 3
|
||||
fi
|
||||
|
||||
# Remove hooks from list first in case unloading fails so we can do quit hooks if something break.
|
||||
for hook in $modules_hooks; do
|
||||
# List so we can unset.
|
||||
if list_contains "modules_${hook}" "$module"; then
|
||||
to_unset+=" module_${module}_${hook}"
|
||||
fi
|
||||
list_remove "modules_${hook}" "$module" "modules_${hook}"
|
||||
done
|
||||
commands_unregister "$module" || {
|
||||
log_fatal_file modules.log "Could not unregister commands for ${module}"
|
||||
bot_quit "Fatal error in module unload, please see log"
|
||||
}
|
||||
module_${module}_UNLOAD || {
|
||||
log_fatal_file modules.log "Could not unload ${module}, module_${module}_UNLOAD returned ${?}!"
|
||||
bot_quit "Fatal error in module unload, please see log"
|
||||
}
|
||||
unset module_${module}_UNLOAD
|
||||
unset module_${module}_INIT
|
||||
unset module_${module}_REHASH
|
||||
# Unset from list created above.
|
||||
for hook in $to_unset; do
|
||||
unset "$hook" || {
|
||||
log_fatal_file modules.log "Could not unset the hook $hook of module $module!"
|
||||
bot_quit "Fatal error in module unload, please see log"
|
||||
}
|
||||
done
|
||||
modules_depends_unregister "$module"
|
||||
list_remove "modules_loaded" "$module" "modules_loaded"
|
||||
|
||||
# Call any hooks for unloading modules.
|
||||
local othermodule
|
||||
for othermodule in $modules_on_module_UNLOAD; do
|
||||
module_${othermodule}_on_module_UNLOAD "$module"
|
||||
done
|
||||
|
||||
# Unset help string
|
||||
unset helpentry_module_${module}_description
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Generate awk script to validate module functions.
|
||||
## @param Module name
|
||||
## @Type Private
|
||||
## @return 0 If the file is OK
|
||||
## @return 1 If the file lacks one of more of the functions.
|
||||
#---------------------------------------------------------------------
|
||||
modules_check_function() {
|
||||
local module="$1"
|
||||
# This is a one liner. Well mostly. ;)
|
||||
# We check that the needed functions exist.
|
||||
awk "function check_found() { if (init && unload && rehash) exit 0 }
|
||||
/^declare -f module_${module}_INIT$/ { init=1; check_found() }
|
||||
/^declare -f module_${module}_UNLOAD$/ { unload=1; check_found() }
|
||||
/^declare -f module_${module}_REHASH$/ { rehash=1; check_found() }
|
||||
END { if (! (init && unload && rehash)) exit 1 }"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Load a module
|
||||
## @Type Private
|
||||
## @param Name of module to load
|
||||
## @return 0 Loaded Ok
|
||||
## @return 1 Other errors
|
||||
## @return 2 Module already loaded
|
||||
## @return 3 Failed to source it in safe subshell
|
||||
## @return 4 Failed to source it
|
||||
## @return 5 No such module
|
||||
## @return 6 Getting hooks failed
|
||||
## @return 7 after_load failed
|
||||
## @Note If the load fails in a fatal way the bot will quit.
|
||||
#---------------------------------------------------------------------
|
||||
modules_load() {
|
||||
local module="$1"
|
||||
if list_contains "modules_loaded" "$module"; then
|
||||
log_warning_file modules.log "Module ${module} is already loaded."
|
||||
return 2
|
||||
fi
|
||||
# modulebase is exported as MODULE_BASE_PATH
|
||||
# with ${config_modules_dir} prepended to the
|
||||
# INIT function, useful for multi-file
|
||||
# modules, but available for other modules too.
|
||||
local modulefilename modulebase
|
||||
if [[ -f "${config_modules_dir}/m_${module}.sh" ]]; then
|
||||
modulefilename="m_${module}.sh"
|
||||
modulebase="${modulefilename}"
|
||||
elif [[ -d "${config_modules_dir}/m_${module}" && -f "${config_modules_dir}/m_${module}/__main__.sh" ]]; then
|
||||
modulefilename="m_${module}/__main__.sh"
|
||||
modulebase="m_${module}"
|
||||
else
|
||||
log_error_file modules.log "No such module as ${module} exists."
|
||||
return 5
|
||||
fi
|
||||
( source "${config_modules_dir}/${modulefilename}" )
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log_error_file modules.log "Could not load ${module}, failed to source it in safe subshell."
|
||||
return 3
|
||||
fi
|
||||
( source "${config_modules_dir}/${modulefilename}" && declare -F ) | modules_check_function "$module"
|
||||
if [[ $? -ne 0 ]]; then
|
||||
log_error_file modules.log "Could not load ${module}, it lacks some important functions it should have."
|
||||
return 3
|
||||
fi
|
||||
source "${config_modules_dir}/${modulefilename}"
|
||||
if [[ $? -eq 0 ]]; then
|
||||
modules_loaded+=" $module"
|
||||
modules_add_hooks "$module" "${config_modules_dir}/${modulebase}" || \
|
||||
{
|
||||
log_error_file modules.log "Hooks failed for $module"
|
||||
# Try to unload.
|
||||
modules_unload "$module" || {
|
||||
log_fatal_file modules.log "Failed Unloading of $module (that failed to load)."
|
||||
bot_quit "Fatal error in module unload of failed module load, please see log"
|
||||
}
|
||||
return 6
|
||||
}
|
||||
if grep -qw "$module" <<< "$modules_after_load"; then
|
||||
module_${module}_after_load
|
||||
if [[ $? -ne 0 ]]; then
|
||||
modules_unload ${module} || {
|
||||
log_fatal_file modules.log "Unloading of $module that failed after_load failed."
|
||||
bot_quit "Fatal error in module unload of failed module load (after_load), please see log"
|
||||
}
|
||||
return 7
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_error_file modules.log "Could not load ${module}, failed to source it."
|
||||
return 4
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Load modules from the config
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
modules_load_from_config() {
|
||||
local module
|
||||
IFS=" "
|
||||
for module in $modules_loaded; do
|
||||
if ! list_contains config_modules "$module"; then
|
||||
modules_unload "$module"
|
||||
fi
|
||||
done
|
||||
unset IFS
|
||||
for module in $config_modules; do
|
||||
if [[ -f "${config_modules_dir}/m_${module}.sh" || -d "${config_modules_dir}/m_${module}" ]]; then
|
||||
if ! list_contains modules_loaded "$module"; then
|
||||
modules_load "$module"
|
||||
fi
|
||||
else
|
||||
log_warning_file modules.log "$module doesn't exist! Removing it from list"
|
||||
fi
|
||||
done
|
||||
}
|
||||
348
lib/numerics.sh
Normal file
348
lib/numerics.sh
Normal file
@@ -0,0 +1,348 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
|
||||
###########################################################################
|
||||
# #
|
||||
# WARNING THIS FILE IS AUTOGENERATED. ANY CHANGES WILL BE OVERWRITTEN! #
|
||||
# See the source in tools/numerics.txt for comments about some numerics #
|
||||
# This file was generated with tools/build_numerics.sh #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Auto-generated list of numerics from tools/numerics.txt<br />
|
||||
## This file contains a list of numerics that we currently use.
|
||||
## It is therefore incomplete.<br />
|
||||
## Because the list of variables in this file is so long, please see
|
||||
## it's source for more details.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
##########################
|
||||
# Name -> number mapping #
|
||||
##########################
|
||||
|
||||
numeric_RPL_WELCOME='001'
|
||||
numeric_RPL_YOURHOST='002'
|
||||
numeric_RPL_CREATED='003'
|
||||
numeric_RPL_MYINFO='004'
|
||||
numeric_RPL_ISUPPORT='005'
|
||||
numeric_RPL_MAP='006'
|
||||
numeric_RPL_MAPEND='007'
|
||||
numeric_RPL_SNOMASK='008'
|
||||
numeric_RPL_TRACEUSER='205'
|
||||
numeric_RPL_STATSCLINE='213'
|
||||
numeric_RPL_ENDOFSTATS='219'
|
||||
numeric_RPL_UMODEIS='221'
|
||||
numeric_RPL_STATSELINE='223'
|
||||
numeric_RPL_RULES='232'
|
||||
numeric_RPL_STATSUPTIME='242'
|
||||
numeric_RPL_STATSCONN='250'
|
||||
numeric_RPL_LUSERCLIENT='251'
|
||||
numeric_RPL_LUSEROP='252'
|
||||
numeric_RPL_LUSERUNKNOWN='253'
|
||||
numeric_RPL_LUSERCHANNELS='254'
|
||||
numeric_RPL_LUSERME='255'
|
||||
numeric_RPL_ADMINME='256'
|
||||
numeric_RPL_ADMINLOC1='257'
|
||||
numeric_RPL_ADMINLOC2='258'
|
||||
numeric_RPL_ADMINEMAIL='259'
|
||||
numeric_RPL_TRYAGAIN='263'
|
||||
numeric_RPL_LOCALUSERS='265'
|
||||
numeric_RPL_GLOBALUSERS='266'
|
||||
numeric_RPL_SILELIST='271'
|
||||
numeric_RPL_ENDOFSILELIST='272'
|
||||
numeric_RPL_AWAY='301'
|
||||
numeric_RPL_USERHOST='302'
|
||||
numeric_RPL_ISON='303'
|
||||
numeric_RPL_TEXT='304'
|
||||
numeric_RPL_UNAWAY='305'
|
||||
numeric_RPL_UNAWAY='306'
|
||||
numeric_RPL_WHOISREGNICK='307'
|
||||
numeric_RPL_RULESSTART='308'
|
||||
numeric_RPL_ENDOFRULES='309'
|
||||
numeric_RPL_WHOISHELPOP='310'
|
||||
numeric_RPL_WHOISUSER='311'
|
||||
numeric_RPL_WHOISSERVER='312'
|
||||
numeric_RPL_WHOISOPERATOR='313'
|
||||
numeric_RPL_WHOWASUSER='314'
|
||||
numeric_RPL_ENDOFWHO='315'
|
||||
numeric_RPL_WHOISIDLE='317'
|
||||
numeric_RPL_ENDOFWHOIS='318'
|
||||
numeric_RPL_WHOISCHANNELS='319'
|
||||
numeric_RPL_WHOISSPECIAL='320'
|
||||
numeric_RPL_LISTSTART='321'
|
||||
numeric_RPL_LIST='322'
|
||||
numeric_RPL_LISTEND='323'
|
||||
numeric_RPL_CHANNELMODEIS='324'
|
||||
numeric_RPL_CREATIONTIME='329'
|
||||
numeric_RPL_WHOISACCOUNT='330'
|
||||
numeric_RPL_NOTOPIC='331'
|
||||
numeric_RPL_TOPIC='332'
|
||||
numeric_RPL_TOPICWHOTIME='333'
|
||||
numeric_RPL_USERIP='340'
|
||||
numeric_RPL_INVITING='341'
|
||||
numeric_RPL_INVITELIST='346'
|
||||
numeric_RPL_ENDOFINVITELIST='347'
|
||||
numeric_RPL_EXCEPTLIST='348'
|
||||
numeric_RPL_ENDOFEXCEPTLIST='349'
|
||||
numeric_RPL_VERSION='351'
|
||||
numeric_RPL_WHOREPLY='352'
|
||||
numeric_RPL_NAMREPLY='353'
|
||||
numeric_RPL_LINKS='364'
|
||||
numeric_RPL_ENDOFLINKS='365'
|
||||
numeric_RPL_ENDOFNAMES='366'
|
||||
numeric_RPL_BANLIST='367'
|
||||
numeric_RPL_ENDOFBANLIST='368'
|
||||
numeric_RPL_ENDOFWHOWAS='369'
|
||||
numeric_RPL_INFO='371'
|
||||
numeric_RPL_MOTD='372'
|
||||
numeric_RPL_ENDOFINFO='374'
|
||||
numeric_RPL_MOTDSTART='375'
|
||||
numeric_RPL_ENDOFMOTD='376'
|
||||
numeric_RPL_WHOISHOST='378'
|
||||
numeric_RPL_YOUREOPER='381'
|
||||
numeric_RPL_REHASHING='382'
|
||||
numeric_RPL_TIME='391'
|
||||
numeric_RPL_HOSTHIDDEN='396'
|
||||
numeric_ERR_NOSUCHNICK='401'
|
||||
numeric_ERR_NOSUCHSERVER='402'
|
||||
numeric_ERR_NOSUCHCHANNEL='403'
|
||||
numeric_ERR_CANNOTSENDTOCHAN='404'
|
||||
numeric_ERR_TOOMANYCHANNELS='405'
|
||||
numeric_ERR_WASNOSUCHNICK='406'
|
||||
numeric_ERR_TOOMANYTARGETS='407'
|
||||
numeric_ERR_NOTEXTTOSEND='412'
|
||||
numeric_ERR_TOOMANYMATCHES='416'
|
||||
numeric_ERR_UNKNOWNCOMMAND='421'
|
||||
numeric_ERR_NOMOTD='422'
|
||||
numeric_ERR_ERRONEUSNICKNAME='432'
|
||||
numeric_ERR_NICKNAMEINUSE='433'
|
||||
numeric_ERR_NICKTOOFAST='438'
|
||||
numeric_ERR_USERNOTINCHANNEL='441'
|
||||
numeric_ERR_NOTONCHANNEL='442'
|
||||
numeric_ERR_USERONCHANNEL='443'
|
||||
numeric_ERR_SUMMONDISABLED='445'
|
||||
numeric_ERR_USERSDISABLED='446'
|
||||
numeric_ERR_NONICKCHANGE='447'
|
||||
numeric_ERR_NOTFORHALFOPS='460'
|
||||
numeric_ERR_NEEDMOREPARAMS='461'
|
||||
numeric_ERR_ALREADYREGISTERED='462'
|
||||
numeric_ERR_ONLYSERVERSCANCHANGE='468'
|
||||
numeric_ERR_LINKCHANNEL='470'
|
||||
numeric_ERR_CHANNELISFULL='471'
|
||||
numeric_ERR_UNKNOWNMODE='472'
|
||||
numeric_ERR_INVITEONLYCHAN='473'
|
||||
numeric_ERR_BANNEDFROMCHAN='474'
|
||||
numeric_ERR_BADCHANNELKEY='475'
|
||||
numeric_ERR_NEEDREGGEDNICK='477'
|
||||
numeric_ERR_BANLISTFULL='478'
|
||||
numeric_ERR_CANNOTKNOCK='480'
|
||||
numeric_ERR_NOPRIVILEGES='481'
|
||||
numeric_ERR_CHANOPRIVSNEEDED='482'
|
||||
numeric_ERR_ATTACKDENY='484'
|
||||
numeric_ERR_SECUREONLYCHAN='489'
|
||||
numeric_ERR_ALLMUSTUSESSL='490'
|
||||
numeric_ERR_NOOPERHOST='491'
|
||||
numeric_ERR_NOREJOINONKICK='495'
|
||||
numeric_ERR_CHANOWNPRIVNEEDED='499'
|
||||
numeric_ERR_UMODEUNKNOWNFLAG='501'
|
||||
numeric_ERR_USERSDONTMATCH='502'
|
||||
numeric_RPL_LOGON='600'
|
||||
numeric_RPL_LOGOFF='601'
|
||||
numeric_RPL_WATCHOFF='602'
|
||||
numeric_RPL_NOWON='604'
|
||||
numeric_RPL_NOWOFF='605'
|
||||
numeric_RPL_WATCHLIST='606'
|
||||
numeric_RPL_ENDOFWATCHLIST='607'
|
||||
numeric_RPL_WHOISSECURE='671'
|
||||
numeric_RPL_MODULES='900'
|
||||
numeric_RPL_ENDOFMODULES='901'
|
||||
numeric_RPL_COMMANDS='902'
|
||||
numeric_RPL_ENDOFCOMMANDS='903'
|
||||
numeric_ERR_CENSORED='936'
|
||||
numeric_ERR_ALREDYCENSORED='937'
|
||||
numeric_ERR_NOTCENSORED='938'
|
||||
numeric_ERR_SPAMFILTERLISTFULL='939'
|
||||
numeric_RPL_ENDOFSPAMFILTER='940'
|
||||
numeric_RPL_SPAMFILTER='941'
|
||||
numeric_ERR_INVALIDNICK='942'
|
||||
numeric_RPL_SILENCEREMOVED='950'
|
||||
numeric_RPL_SILENCEADDED='951'
|
||||
numeric_ERR_ALREADYSILENCE='952'
|
||||
numeric_ERR_CANNOTDOCOMMAND='972'
|
||||
numeric_ERR_CANNOTCHANGECHANMODE='974'
|
||||
|
||||
##########################
|
||||
# Number -> name mapping #
|
||||
##########################
|
||||
|
||||
numerics[1]='RPL_WELCOME'
|
||||
numerics[2]='RPL_YOURHOST'
|
||||
numerics[3]='RPL_CREATED'
|
||||
numerics[4]='RPL_MYINFO'
|
||||
numerics[5]='RPL_ISUPPORT'
|
||||
numerics[6]='RPL_MAP'
|
||||
numerics[7]='RPL_MAPEND'
|
||||
numerics[8]='RPL_SNOMASK'
|
||||
numerics[205]='RPL_TRACEUSER'
|
||||
numerics[213]='RPL_STATSCLINE'
|
||||
numerics[219]='RPL_ENDOFSTATS'
|
||||
numerics[221]='RPL_UMODEIS'
|
||||
numerics[223]='RPL_STATSELINE'
|
||||
numerics[232]='RPL_RULES'
|
||||
numerics[242]='RPL_STATSUPTIME'
|
||||
numerics[250]='RPL_STATSCONN'
|
||||
numerics[251]='RPL_LUSERCLIENT'
|
||||
numerics[252]='RPL_LUSEROP'
|
||||
numerics[253]='RPL_LUSERUNKNOWN'
|
||||
numerics[254]='RPL_LUSERCHANNELS'
|
||||
numerics[255]='RPL_LUSERME'
|
||||
numerics[256]='RPL_ADMINME'
|
||||
numerics[257]='RPL_ADMINLOC1'
|
||||
numerics[258]='RPL_ADMINLOC2'
|
||||
numerics[259]='RPL_ADMINEMAIL'
|
||||
numerics[263]='RPL_TRYAGAIN'
|
||||
numerics[265]='RPL_LOCALUSERS'
|
||||
numerics[266]='RPL_GLOBALUSERS'
|
||||
numerics[271]='RPL_SILELIST'
|
||||
numerics[272]='RPL_ENDOFSILELIST'
|
||||
numerics[301]='RPL_AWAY'
|
||||
numerics[302]='RPL_USERHOST'
|
||||
numerics[303]='RPL_ISON'
|
||||
numerics[304]='RPL_TEXT'
|
||||
numerics[305]='RPL_UNAWAY'
|
||||
numerics[306]='RPL_UNAWAY'
|
||||
numerics[307]='RPL_WHOISREGNICK'
|
||||
numerics[308]='RPL_RULESSTART'
|
||||
numerics[309]='RPL_ENDOFRULES'
|
||||
numerics[310]='RPL_WHOISHELPOP'
|
||||
numerics[311]='RPL_WHOISUSER'
|
||||
numerics[312]='RPL_WHOISSERVER'
|
||||
numerics[313]='RPL_WHOISOPERATOR'
|
||||
numerics[314]='RPL_WHOWASUSER'
|
||||
numerics[315]='RPL_ENDOFWHO'
|
||||
numerics[317]='RPL_WHOISIDLE'
|
||||
numerics[318]='RPL_ENDOFWHOIS'
|
||||
numerics[319]='RPL_WHOISCHANNELS'
|
||||
numerics[320]='RPL_WHOISSPECIAL'
|
||||
numerics[321]='RPL_LISTSTART'
|
||||
numerics[322]='RPL_LIST'
|
||||
numerics[323]='RPL_LISTEND'
|
||||
numerics[324]='RPL_CHANNELMODEIS'
|
||||
numerics[329]='RPL_CREATIONTIME'
|
||||
numerics[330]='RPL_WHOISACCOUNT'
|
||||
numerics[331]='RPL_NOTOPIC'
|
||||
numerics[332]='RPL_TOPIC'
|
||||
numerics[333]='RPL_TOPICWHOTIME'
|
||||
numerics[340]='RPL_USERIP'
|
||||
numerics[341]='RPL_INVITING'
|
||||
numerics[346]='RPL_INVITELIST'
|
||||
numerics[347]='RPL_ENDOFINVITELIST'
|
||||
numerics[348]='RPL_EXCEPTLIST'
|
||||
numerics[349]='RPL_ENDOFEXCEPTLIST'
|
||||
numerics[351]='RPL_VERSION'
|
||||
numerics[352]='RPL_WHOREPLY'
|
||||
numerics[353]='RPL_NAMREPLY'
|
||||
numerics[364]='RPL_LINKS'
|
||||
numerics[365]='RPL_ENDOFLINKS'
|
||||
numerics[366]='RPL_ENDOFNAMES'
|
||||
numerics[367]='RPL_BANLIST'
|
||||
numerics[368]='RPL_ENDOFBANLIST'
|
||||
numerics[369]='RPL_ENDOFWHOWAS'
|
||||
numerics[371]='RPL_INFO'
|
||||
numerics[372]='RPL_MOTD'
|
||||
numerics[374]='RPL_ENDOFINFO'
|
||||
numerics[375]='RPL_MOTDSTART'
|
||||
numerics[376]='RPL_ENDOFMOTD'
|
||||
numerics[378]='RPL_WHOISHOST'
|
||||
numerics[381]='RPL_YOUREOPER'
|
||||
numerics[382]='RPL_REHASHING'
|
||||
numerics[391]='RPL_TIME'
|
||||
numerics[396]='RPL_HOSTHIDDEN'
|
||||
numerics[401]='ERR_NOSUCHNICK'
|
||||
numerics[402]='ERR_NOSUCHSERVER'
|
||||
numerics[403]='ERR_NOSUCHCHANNEL'
|
||||
numerics[404]='ERR_CANNOTSENDTOCHAN'
|
||||
numerics[405]='ERR_TOOMANYCHANNELS'
|
||||
numerics[406]='ERR_WASNOSUCHNICK'
|
||||
numerics[407]='ERR_TOOMANYTARGETS'
|
||||
numerics[412]='ERR_NOTEXTTOSEND'
|
||||
numerics[416]='ERR_TOOMANYMATCHES'
|
||||
numerics[421]='ERR_UNKNOWNCOMMAND'
|
||||
numerics[422]='ERR_NOMOTD'
|
||||
numerics[432]='ERR_ERRONEUSNICKNAME'
|
||||
numerics[433]='ERR_NICKNAMEINUSE'
|
||||
numerics[438]='ERR_NICKTOOFAST'
|
||||
numerics[441]='ERR_USERNOTINCHANNEL'
|
||||
numerics[442]='ERR_NOTONCHANNEL'
|
||||
numerics[443]='ERR_USERONCHANNEL'
|
||||
numerics[445]='ERR_SUMMONDISABLED'
|
||||
numerics[446]='ERR_USERSDISABLED'
|
||||
numerics[447]='ERR_NONICKCHANGE'
|
||||
numerics[460]='ERR_NOTFORHALFOPS'
|
||||
numerics[461]='ERR_NEEDMOREPARAMS'
|
||||
numerics[462]='ERR_ALREADYREGISTERED'
|
||||
numerics[468]='ERR_ONLYSERVERSCANCHANGE'
|
||||
numerics[470]='ERR_LINKCHANNEL'
|
||||
numerics[471]='ERR_CHANNELISFULL'
|
||||
numerics[472]='ERR_UNKNOWNMODE'
|
||||
numerics[473]='ERR_INVITEONLYCHAN'
|
||||
numerics[474]='ERR_BANNEDFROMCHAN'
|
||||
numerics[475]='ERR_BADCHANNELKEY'
|
||||
numerics[477]='ERR_NEEDREGGEDNICK'
|
||||
numerics[478]='ERR_BANLISTFULL'
|
||||
numerics[480]='ERR_CANNOTKNOCK'
|
||||
numerics[481]='ERR_NOPRIVILEGES'
|
||||
numerics[482]='ERR_CHANOPRIVSNEEDED'
|
||||
numerics[484]='ERR_ATTACKDENY'
|
||||
numerics[489]='ERR_SECUREONLYCHAN'
|
||||
numerics[490]='ERR_ALLMUSTUSESSL'
|
||||
numerics[491]='ERR_NOOPERHOST'
|
||||
numerics[495]='ERR_NOREJOINONKICK'
|
||||
numerics[499]='ERR_CHANOWNPRIVNEEDED'
|
||||
numerics[501]='ERR_UMODEUNKNOWNFLAG'
|
||||
numerics[502]='ERR_USERSDONTMATCH'
|
||||
numerics[600]='RPL_LOGON'
|
||||
numerics[601]='RPL_LOGOFF'
|
||||
numerics[602]='RPL_WATCHOFF'
|
||||
numerics[604]='RPL_NOWON'
|
||||
numerics[605]='RPL_NOWOFF'
|
||||
numerics[606]='RPL_WATCHLIST'
|
||||
numerics[607]='RPL_ENDOFWATCHLIST'
|
||||
numerics[671]='RPL_WHOISSECURE'
|
||||
numerics[900]='RPL_MODULES'
|
||||
numerics[901]='RPL_ENDOFMODULES'
|
||||
numerics[902]='RPL_COMMANDS'
|
||||
numerics[903]='RPL_ENDOFCOMMANDS'
|
||||
numerics[936]='ERR_CENSORED'
|
||||
numerics[937]='ERR_ALREDYCENSORED'
|
||||
numerics[938]='ERR_NOTCENSORED'
|
||||
numerics[939]='ERR_SPAMFILTERLISTFULL'
|
||||
numerics[940]='RPL_ENDOFSPAMFILTER'
|
||||
numerics[941]='RPL_SPAMFILTER'
|
||||
numerics[942]='ERR_INVALIDNICK'
|
||||
numerics[950]='RPL_SILENCEREMOVED'
|
||||
numerics[951]='RPL_SILENCEADDED'
|
||||
numerics[952]='ERR_ALREADYSILENCE'
|
||||
numerics[972]='ERR_CANNOTDOCOMMAND'
|
||||
numerics[974]='ERR_CANNOTCHANGECHANMODE'
|
||||
|
||||
# End of generated file.
|
||||
100
lib/parse.sh
Normal file
100
lib/parse.sh
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Data parsing
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Get parts of hostmask.
|
||||
## @Note In most cases you should use one of
|
||||
## @Note <@function parse_hostmask_nick>, <@function parse_hostmask_ident>
|
||||
## @Note or <@function parse_hostmask_host>. Only use this function
|
||||
## @Note if you want all several parts.
|
||||
## @Type API
|
||||
## @param n!u@h mask
|
||||
## @param Variable to return nick in
|
||||
## @param Variable to return ident in
|
||||
## @param Variable to return host in
|
||||
#---------------------------------------------------------------------
|
||||
parse_hostmask() {
|
||||
if [[ $1 =~ ^([^ !]+)!([^ @]+)@([^ ]+) ]]; then
|
||||
printf -v "$2" '%s' "${BASH_REMATCH[1]}"
|
||||
printf -v "$3" '%s' "${BASH_REMATCH[2]}"
|
||||
printf -v "$4" '%s' "${BASH_REMATCH[3]}"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Get nick from hostmask
|
||||
## @Type API
|
||||
## @param n!u@h mask
|
||||
## @param Variable to return result in
|
||||
#---------------------------------------------------------------------
|
||||
parse_hostmask_nick() {
|
||||
if [[ $1 =~ ^([^ !]+)! ]]; then
|
||||
printf -v "$2" '%s' "${BASH_REMATCH[1]}"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Get ident from hostmask
|
||||
## @Type API
|
||||
## @param n!u@h mask
|
||||
## @param Variable to return result in
|
||||
#---------------------------------------------------------------------
|
||||
parse_hostmask_ident() {
|
||||
if [[ $1 =~ ^[^\ !]+!([^ @]+)@ ]]; then
|
||||
printf -v "$2" '%s' "${BASH_REMATCH[1]}"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Get host from hostmask
|
||||
## @Type API
|
||||
## @param n!u@h mask
|
||||
## @param Variable to return result in
|
||||
#---------------------------------------------------------------------
|
||||
parse_hostmask_host() {
|
||||
if [[ $1 =~ ^[^\ !]+![^\ @]+@([^ ]+) ]]; then
|
||||
printf -v "$2" '%s' "${BASH_REMATCH[1]}"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## This is used to get data out of 005.
|
||||
## @Type API
|
||||
## @param Name of data to get
|
||||
## @param Variable to return result (if any result) in
|
||||
## @return 0 If found otherwise 1
|
||||
## @Note That if the variable doesn't have any data,
|
||||
## @Note but still exist it will return nothing on STDOUT
|
||||
## @Note but 0 as error code
|
||||
#---------------------------------------------------------------------
|
||||
parse_005() {
|
||||
if [[ $server_005 =~ ${1}(=([^ ]+))? ]]; then
|
||||
if [[ ${BASH_REMATCH[2]} ]]; then
|
||||
printf -v "$2" '%s' "${BASH_REMATCH[2]}"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
178
lib/send.sh
Normal file
178
lib/send.sh
Normal file
@@ -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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Functions for sending data to server
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Simple flood limiting.
|
||||
## Note that this doesn't handle this very well:<br />
|
||||
## seconds:milliseconds message<br />
|
||||
## 01:999 message<br />
|
||||
## 02:001 other message<br />
|
||||
## Then they get too close.<br />
|
||||
## I think this won't flood us off though.<br />
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
send_last=0
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Send a "raw" line to the server.
|
||||
## @Type API
|
||||
## @param Line to send
|
||||
#---------------------------------------------------------------------
|
||||
send_raw() {
|
||||
# Do the flood limiting
|
||||
local now=
|
||||
time_get_current 'now'
|
||||
if [[ "$send_last" == "$now" ]]; then
|
||||
sleep 1
|
||||
fi
|
||||
time_get_current 'send_last'
|
||||
send_raw_flood "$*"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Send a PRIVMSG
|
||||
## @Type API
|
||||
## @param Who (channel or nick)
|
||||
## @param Message
|
||||
#---------------------------------------------------------------------
|
||||
send_msg() {
|
||||
# Don't do anything if no message
|
||||
[[ -z $2 ]] && return 0
|
||||
send_raw "PRIVMSG ${1} :${2}"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Send a NOTICE
|
||||
## @Type API
|
||||
## @param Who (channel or nick)
|
||||
## @param Message
|
||||
#---------------------------------------------------------------------
|
||||
send_notice() {
|
||||
# Don't do anything if no message
|
||||
[[ -z $2 ]] && return 0
|
||||
send_raw "NOTICE ${1} :${2}"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Send a CTCP
|
||||
## @Type API
|
||||
## @param Who (channel or nick)
|
||||
## @param Message
|
||||
#---------------------------------------------------------------------
|
||||
send_ctcp() {
|
||||
# Don't do anything if no message
|
||||
[[ -z $2 ]] && return 0
|
||||
send_msg "$1" $'\1'"${2}"$'\1'
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Send a NCTCP (ctcp reply)
|
||||
## @Type API
|
||||
## @param Who (channel or nick)
|
||||
## @param Message
|
||||
#---------------------------------------------------------------------
|
||||
send_nctcp() {
|
||||
# Don't do anything if no message
|
||||
[[ -z $2 ]] && return 0
|
||||
send_notice "$1" $'\1'"${2}"$'\1'
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Send a NICK to change nick
|
||||
## @Type API
|
||||
## @param New nick
|
||||
#---------------------------------------------------------------------
|
||||
send_nick() {
|
||||
send_raw "NICK ${1}"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Send a MODE to change umodes.
|
||||
## @Type API
|
||||
## @param Modes to send
|
||||
#---------------------------------------------------------------------
|
||||
send_umodes() {
|
||||
send_raw "MODE $server_nick_current $1"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Send a MODE to change channel modes.
|
||||
## @Type API
|
||||
## @param Target channel
|
||||
## @param Modes to set
|
||||
#---------------------------------------------------------------------
|
||||
send_modes() {
|
||||
send_raw "MODE $1 $2"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Send a TOPIC to change channel topic.
|
||||
## @Type API
|
||||
## @param Channel to change topic of
|
||||
## @param New topic.
|
||||
#---------------------------------------------------------------------
|
||||
send_topic() {
|
||||
send_raw "TOPIC $1 :$2"
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## This is semi-internal only
|
||||
## This may flood ourself off. Use send_raw instead in most cases.
|
||||
## Also this doesn't log the actual line, so used for passwords.
|
||||
## @Type API
|
||||
## @param What to log instead (example could be: "NickServ IDENTIFY (password)")
|
||||
## @param The line to send
|
||||
#---------------------------------------------------------------------
|
||||
send_raw_flood_nolog() {
|
||||
log_raw_out "<hidden line from logs>: $1"
|
||||
transport_write_line "$2"$'\r'
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## This is semi-internal only
|
||||
## This may flood ourself off. Use send_raw instead in most cases.
|
||||
## Same syntax as send_raw
|
||||
## @Type Semi-private
|
||||
#---------------------------------------------------------------------
|
||||
send_raw_flood() {
|
||||
log_raw_out "$*"
|
||||
transport_write_line "$*"$'\r'
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
# Internal functions to core or this file below this line! #
|
||||
# Module authors: go away #
|
||||
###########################################################################
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Module authors: use the wrapper: bot_quit in misc.sh instead!
|
||||
## @Type Private
|
||||
## @param If set, a quit reason
|
||||
#---------------------------------------------------------------------
|
||||
send_quit() {
|
||||
local reason=""
|
||||
[[ -n "$1" ]] && reason=" :$1"
|
||||
send_raw_flood "QUIT${reason}"
|
||||
}
|
||||
337
lib/server.sh
Normal file
337
lib/server.sh
Normal file
@@ -0,0 +1,337 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Server connection.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
# Server info variables
|
||||
#---------------------------------------------------------------------
|
||||
## Name of server (example: server1.example.net)
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_name=""
|
||||
#---------------------------------------------------------------------
|
||||
## The 004 received from the server.
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_004=""
|
||||
#---------------------------------------------------------------------
|
||||
## The 005 received from the server. Use parse_005 to get data out of this.
|
||||
## @Type API
|
||||
## @Note See http://www.irc.org/tech_docs/005.html for an incomplete list of 005 values.
|
||||
#---------------------------------------------------------------------
|
||||
server_005=""
|
||||
# NAMES output with UHNAMES and NAMESX
|
||||
# :photon.kuonet-ng.org 353 envbot = #bots :@%+AnMaster!AnMaster@staff.kuonet-ng.org @ChanServ!ChanServ@services.kuonet-ng.org bashbot!rfc3092@1F1794B2:769091B3
|
||||
# NAMES output with NAMESX only:
|
||||
# :hurricane.KuoNET.org 353 envbot = #test :bashbot ~@Brain ~@EmErgE &@AnMaster/kng
|
||||
#---------------------------------------------------------------------
|
||||
## 1 if UHNAMES enabled, otherwise 0
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_UHNAMES=0
|
||||
#---------------------------------------------------------------------
|
||||
## 1 if NAMESX enabled, otherwise 0
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_NAMESX=0
|
||||
# These are passed in a slightly odd way in 005 so we do them here.
|
||||
#---------------------------------------------------------------------
|
||||
## The mode char (if any) for ban excepts (normally +e)
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_EXCEPTS=""
|
||||
#---------------------------------------------------------------------
|
||||
## The mode char (if any) for invite excepts (normally +I)
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_INVEX=""
|
||||
|
||||
# In case we don't get a 005, make some sane defaults.
|
||||
#---------------------------------------------------------------------
|
||||
## List channel modes supported by server.
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_CHMODES_LISTMODES="b"
|
||||
#---------------------------------------------------------------------
|
||||
## "Always parameters" channel modes supported by server.
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_CHMODES_ALWAYSPARAM="k"
|
||||
#---------------------------------------------------------------------
|
||||
## "Parameter on set" channel modes supported by server.
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_CHMODES_PARAMONSET="l"
|
||||
#---------------------------------------------------------------------
|
||||
## Simple channel modes supported by server.
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_CHMODES_SIMPLE="imnpst"
|
||||
#---------------------------------------------------------------------
|
||||
## Prefix channel modes supported by server.
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_PREFIX_modes="ov"
|
||||
#---------------------------------------------------------------------
|
||||
## Channel prefixes supported by server.
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_PREFIX_prefixes="@+"
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## What is our current nick?
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_nick_current=""
|
||||
#---------------------------------------------------------------------
|
||||
## 1 if we are connected, otherwise 0
|
||||
## @Type API
|
||||
#---------------------------------------------------------------------
|
||||
server_connected=0
|
||||
|
||||
###########################################################################
|
||||
# Internal functions to core or this file below this line! #
|
||||
# Module authors: go away #
|
||||
###########################################################################
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Get some common data out of 005, the whole will also be saved to
|
||||
## $server_005 for any module to use via parse_005().
|
||||
## This function is for cases that needs special action, like NAMESX
|
||||
## and UHNAMES.
|
||||
## This should be called directly after receiving a part of the 005!
|
||||
## @Type Private
|
||||
## @param The last part of the 005.
|
||||
#---------------------------------------------------------------------
|
||||
server_handle_005() {
|
||||
# Example from freenode:
|
||||
# :heinlein.freenode.net 005 envbot IRCD=dancer CAPAB CHANTYPES=# EXCEPTS INVEX CHANMODES=bdeIq,k,lfJD,cgijLmnPQrRstz CHANLIMIT=#:20 PREFIX=(ov)@+ MAXLIST=bdeI:50 MODES=4 STATUSMSG=@ KNOCK NICKLEN=16 :are supported by this server
|
||||
# :heinlein.freenode.net 005 envbot SAFELIST CASEMAPPING=ascii CHANNELLEN=30 TOPICLEN=450 KICKLEN=450 KEYLEN=23 USERLEN=10 HOSTLEN=63 SILENCE=50 :are supported by this server
|
||||
local line="$1"
|
||||
if [[ $line =~ EXCEPTS(=([^ ]+))? ]]; then
|
||||
# Some, but not all also send what char the modes for EXCEPTS is.
|
||||
# If it isn't sent, lets guess it is +e
|
||||
if [[ ${BASH_REMATCH[2]} ]]; then
|
||||
server_EXCEPTS="${BASH_REMATCH[2]}"
|
||||
else
|
||||
server_EXCEPTS="e"
|
||||
fi
|
||||
fi
|
||||
if [[ $line =~ INVEX(=([^ ]+))? ]]; then
|
||||
# Some, but not all also send what char the modes for INVEX is.
|
||||
# If it isn't sent, lets guess it is +I
|
||||
if [[ ${BASH_REMATCH[2]} ]]; then
|
||||
server_INVEX="${BASH_REMATCH[2]}"
|
||||
else
|
||||
server_INVEX="I"
|
||||
fi
|
||||
fi
|
||||
if [[ $line =~ PREFIX=(\(([^ \)]+)\)([^ ]+)) ]]; then
|
||||
server_PREFIX="${BASH_REMATCH[1]}"
|
||||
server_PREFIX_modes="${BASH_REMATCH[2]}"
|
||||
server_PREFIX_prefixes="${BASH_REMATCH[3]}"
|
||||
fi
|
||||
if [[ $line =~ CHANMODES=([^ ,]+),([^ ,]+),([^ ,]+),([^ ,]+) ]]; then
|
||||
server_CHMODES_LISTMODES="${BASH_REMATCH[1]}"
|
||||
server_CHMODES_ALWAYSPARAM="${BASH_REMATCH[2]}"
|
||||
server_CHMODES_PARAMONSET="${BASH_REMATCH[3]}"
|
||||
server_CHMODES_SIMPLE="${BASH_REMATCH[4]}"
|
||||
fi
|
||||
# Enable NAMESX is supported.
|
||||
if [[ $line =~ NAMESX ]]; then
|
||||
log_info "Enabled NAMESX support"
|
||||
send_raw_flood "PROTOCTL NAMESX"
|
||||
server_NAMESX=1
|
||||
fi
|
||||
# Enable UHNAMES if it is there.
|
||||
if [[ $line =~ UHNAMES ]]; then
|
||||
log_info "Enabled UHNAMES support"
|
||||
send_raw_flood "PROTOCTL UHNAMES"
|
||||
server_UHNAMES=1
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Respond to PING from server.
|
||||
## @Type Private
|
||||
## @param Raw line
|
||||
## @return 0 If it was a PING
|
||||
## @return 1 If it was not a PING
|
||||
#---------------------------------------------------------------------
|
||||
server_handle_ping() {
|
||||
if [[ "$1" =~ ^PING\ *:(.*) ]] ;then
|
||||
send_raw "PONG :${BASH_REMATCH[1]}"
|
||||
return 0
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Handle numerics from server.
|
||||
## @Type Private
|
||||
## @param Numeric
|
||||
## @param Target (self)
|
||||
## @param Data
|
||||
#---------------------------------------------------------------------
|
||||
server_handle_numerics() {
|
||||
# Slight sanity check
|
||||
if [[ "$2" != "$server_nick_current" ]]; then
|
||||
log_warning 'Own nick desynced!'
|
||||
log_warning "It should be $server_nick_current but server says it is $2"
|
||||
log_warning "Correcting own nick and lets hope that doesn't break anything"
|
||||
server_nick_current="$2"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Handle NICK messages from server
|
||||
## @Type Private
|
||||
## @param Sender
|
||||
## @param New nick
|
||||
#---------------------------------------------------------------------
|
||||
server_handle_nick() {
|
||||
local oldnick=
|
||||
parse_hostmask_nick "$1" 'oldnick'
|
||||
if [[ $oldnick == $server_nick_current ]]; then
|
||||
server_nick_current="$2"
|
||||
fi
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Handle nick in use.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
server_handle_nick_in_use() {
|
||||
if [[ $on_nick -eq 3 ]]; then
|
||||
log_error "Third nick is ALSO in use. I give up"
|
||||
bot_quit 2
|
||||
elif [[ $on_nick -eq 2 ]]; then
|
||||
log_warning "Second nick is ALSO in use, trying third"
|
||||
send_nick "$config_thirdnick"
|
||||
server_nick_current="$config_thirdnick"
|
||||
on_nick=3
|
||||
else
|
||||
log_info_stdout "First nick is in use, trying second"
|
||||
send_nick "$config_secondnick"
|
||||
on_nick=2
|
||||
# FIXME: THIS IS HACKISH AND MAY BREAK
|
||||
server_nick_current="$config_secondnick"
|
||||
fi
|
||||
sleep 1
|
||||
}
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Connect to IRC server.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
server_connect() {
|
||||
server_connected=0
|
||||
on_nick=1
|
||||
# Clear current channels:
|
||||
channels_current=""
|
||||
# HACK: Clean up if we are aborted, replaced after connect with one that sends QUIT
|
||||
trap 'transport_disconnect; rm -rvf "$tmp_home"; exit 1' TERM INT
|
||||
log_info_stdout "Connecting to \"${config_server}:${config_server_port}\"..."
|
||||
transport_connect "$config_server" "$config_server_port" "$config_server_ssl" "$config_server_bind" || return 1
|
||||
|
||||
[[ $config_server_passwd ]] && send_raw_flood_nolog "PASS $config_server_passwd"
|
||||
log_info_stdout "logging in as $config_firstnick..."
|
||||
send_nick "$config_firstnick"
|
||||
# FIXME: THIS IS HACKISH AND MAY BREAK
|
||||
server_nick_current="$config_firstnick"
|
||||
# If a server password is set, send it.
|
||||
send_raw_flood "USER $config_ident 0 * :${config_gecos}"
|
||||
while true; do
|
||||
line=
|
||||
transport_read_line
|
||||
local transport_status="$?"
|
||||
# Still connected?
|
||||
if ! transport_alive; then
|
||||
return 1
|
||||
fi
|
||||
# Did we timeout waiting for data
|
||||
# or did we get data?
|
||||
if [[ $transport_status -ne 0 ]]; then
|
||||
continue
|
||||
fi
|
||||
# Check with modules first, needed so we don't skip them.
|
||||
for module in $modules_on_connect; do
|
||||
module_${module}_on_connect "$line"
|
||||
done
|
||||
if [[ "$line" =~ ^:[^\ ]+\ +${numeric_RPL_MOTD} ]]; then
|
||||
continue
|
||||
fi
|
||||
log_raw_in "$line"
|
||||
if [[ "$line" =~ ^:[^\ ]+\ +([0-9]{3})\ +([^ ]+)\ +(.*) ]]; then
|
||||
local numeric="${BASH_REMATCH[1]}"
|
||||
# We use this to check below for our own nick.
|
||||
local numericnick="${BASH_REMATCH[2]}"
|
||||
local data="${BASH_REMATCH[3]}"
|
||||
case "$numeric" in
|
||||
"$numeric_RPL_MOTDSTART")
|
||||
log_info "Motd is not displayed in log";
|
||||
;;
|
||||
"$numeric_RPL_YOURHOST")
|
||||
if [[ $line =~ ^:([^ ]+) ]]; then # just to get the server name, this should always be true
|
||||
server_name="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
;;
|
||||
"$numeric_RPL_WELCOME")
|
||||
# This should work
|
||||
server_nick_current="$numericnick"
|
||||
;;
|
||||
# We don't care about these and don't want to show it as unhandled.
|
||||
"$numeric_RPL_CREATED"|"$numeric_RPL_LUSERCLIENT"|"$numeric_RPL_LUSEROP"|"$numeric_RPL_LUSERUNKNOWN"|"$numeric_RPL_LUSERCHANNELS"|"$numeric_RPL_LUSERME"|"$numeric_RPL_LOCALUSERS"|"$numeric_RPL_GLOBALUSERS"|"$numeric_RPL_STATSCONN")
|
||||
continue
|
||||
;;
|
||||
"$numeric_RPL_MYINFO")
|
||||
server_004="$data"
|
||||
server_004=$(tr -d $'\r\n' <<< "$server_004") # Get rid of ending newline
|
||||
;;
|
||||
"$numeric_RPL_ISUPPORT")
|
||||
server_005+=" $data"
|
||||
server_005=$(tr -d $'\r\n' <<< "$server_005") # Get rid of newlines
|
||||
server_005="${server_005/ :are supported by this server/}" # Get rid of :are supported by this server
|
||||
server_handle_005 "$line"
|
||||
;;
|
||||
"$numeric_ERR_NICKNAMEINUSE"|"$numeric_ERR_ERRONEUSNICKNAME")
|
||||
server_handle_nick_in_use
|
||||
;;
|
||||
"$numeric_RPL_ENDOFMOTD"|"$numeric_ERR_NOMOTD")
|
||||
sleep 1
|
||||
log_info_stdout 'Connected'
|
||||
server_connected=1
|
||||
break
|
||||
;;
|
||||
*)
|
||||
if [[ -z "${numerics[10#${numeric}]}" ]]; then
|
||||
log_info_file unknown_data.log "Unknown numeric during connect: $numeric Data: $data"
|
||||
else
|
||||
log_info_file unknown_data.log "Known but not handled numeric during connect: $numeric Data: $data"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
server_handle_ping "$line"
|
||||
done
|
||||
}
|
||||
110
lib/time.sh
Normal file
110
lib/time.sh
Normal file
@@ -0,0 +1,110 @@
|
||||
#!/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/>. #
|
||||
# #
|
||||
###########################################################################
|
||||
#---------------------------------------------------------------------
|
||||
## Functions for working with time.
|
||||
#---------------------------------------------------------------------
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Check if a set time has passed
|
||||
## @Type API
|
||||
## @param Unix timestamp to check against
|
||||
## @param Number of seconds
|
||||
## @return 0 If at least the given number of seconds has passed
|
||||
## @return 1 If it hasn't
|
||||
#---------------------------------------------------------------------
|
||||
time_check_interval() {
|
||||
local newtime=
|
||||
time_get_current 'newtime'
|
||||
(( ( newtime - $1 ) > $2 ))
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Get current time (seconds since 1970-01-01 00:00:00 UTC)
|
||||
## @Type API
|
||||
## @param Variable to return current timestamp in
|
||||
#---------------------------------------------------------------------
|
||||
time_get_current() {
|
||||
printf -v "$1" '%s' "$(( time_initial + SECONDS ))"
|
||||
}
|
||||
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Returns how long a time interval is in a human readable format.
|
||||
## @Type API
|
||||
## @param Time interval
|
||||
## @param Variable to return result in.
|
||||
## @Note Modified version of function posted by goedel at
|
||||
## @Note http://forum.bash-hackers.org/index.php?topic=59.0
|
||||
#---------------------------------------------------------------------
|
||||
time_format_difference() {
|
||||
local tdiv=$1
|
||||
local tmod i
|
||||
local output=""
|
||||
|
||||
for ((i=0; i < ${#time_format_units[@]}; ++i)); do
|
||||
# n means no limit.
|
||||
if [[ ${time_format_unitspan[i]} == n ]]; then
|
||||
tmod=$tdiv
|
||||
else
|
||||
(( tmod = tdiv % time_format_unitspan[i] ))
|
||||
(( tdiv = tdiv / time_format_unitspan[i] ))
|
||||
fi
|
||||
output="$tmod${time_format_units[i]} $output"
|
||||
[[ $tdiv = 0 ]] && break
|
||||
done
|
||||
|
||||
printf -v "$2" '%s' "${output% }"
|
||||
}
|
||||
|
||||
###########################################################################
|
||||
# Internal functions to core or this file below this line! #
|
||||
# Module authors: go away #
|
||||
###########################################################################
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Array used for time_format_difference
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
declare -r time_format_units=( s min h d mon )
|
||||
#---------------------------------------------------------------------
|
||||
## Array used for time_format_difference
|
||||
## @Type Private
|
||||
## @Note n means no limit.
|
||||
#---------------------------------------------------------------------
|
||||
declare -r time_format_unitspan=( 60 60 24 30 n )
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Initial timestamp that we use to get current time later on.
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
time_initial=''
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
## Set up time variables
|
||||
## @Type Private
|
||||
#---------------------------------------------------------------------
|
||||
time_init() {
|
||||
# Set up initial env
|
||||
time_initial="$(date -u +%s)"
|
||||
SECONDS=0
|
||||
}
|
||||
Reference in New Issue
Block a user