#!/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 . #
# #
###########################################################################
#---------------------------------------------------------------------
## Simple factoids module using SQLite3
#---------------------------------------------------------------------
module_factoids_INIT() {
modinit_API='2'
modinit_HOOKS='after_load on_PRIVMSG'
commands_register "$1" 'learn' || return 1
commands_register "$1" 'forget' || return 1
commands_register "$1" 'lock_factoid' 'lock factoid' || return 1
commands_register "$1" 'unlock_factoid' 'unlock factoid' || return 1
commands_register "$1" 'whatis' || return 1
commands_register "$1" 'factoid_stats' 'factoid stats' || return 1
helpentry_module_factoids_description="Provides a factoid database."
helpentry_factoids_learn_syntax=' (as|is|are|=) '
helpentry_factoids_learn_description='Teach the bot a new factoid.'
helpentry_factoids_forget_syntax=''
helpentry_factoids_forget_description='Make the bot forget the factoid .'
helpentry_factoids_lock_factoid_syntax=''
helpentry_factoids_lock_factoid_description='Prevent normal users from changing the factoid .'
helpentry_factoids_unlock_factoid_syntax=''
helpentry_factoids_unlock_factoid_description='Allow changes to a previously locked factoid .'
helpentry_factoids_whatis_syntax=''
helpentry_factoids_whatis_description='Look up the factoid .'
helpentry_factoids_factoid_stats_syntax=''
helpentry_factoids_factoid_stats_description='Report some statistics on the factoid database.'
}
module_factoids_UNLOAD() {
# Ok this is a LOT. I hope I got all...
unset module_factoids_set module_factoids_remove module_factoids_parse_assignment
unset module_factoids_parse_key module_factoids_parse_value
unset module_factoids_set_INSERT_or_UPDATE module_factoids_send_factoid
unset module_factoids_get_count module_factoids_get_locked_count
unset module_factoids_is_locked module_factoids_lock module_factoids_unlock
unset module_factoids_SELECT module_factoids_INSERT module_factoids_UPDATE module_factoids_DELETE
}
module_factoids_REHASH() {
return 0
}
# Called after module has loaded.
module_factoids_after_load() {
modules_depends_register "factoids" "sqlite3" || {
# This error reporting is hackish, will fix later.
if ! list_contains "modules_loaded" "sqlite3"; then
log_error "The factoids module depends upon the SQLite3 module being loaded."
fi
return 1
}
if [[ -z $config_module_factoids_table ]]; then
log_error "Factiods table (config_module_factoids_table) must be set in config if you want to use factoids module."
return 1
fi
if ! module_sqlite3_table_exists "$config_module_factoids_table"; then
log_error "factoids module: $config_module_factoids_table does not exist in the database file."
log_error "factoids module: See comment in doc/factoids.sql for how to create the table."
fi
}
#---------------------------------------------------------------------
## Get an item from DB
## @Type Private
## @param Key
## @Stdout The result of the database query.
#---------------------------------------------------------------------
module_factoids_SELECT() {
#$ sqlite3 -list data/factoids.sqlite "SELECT value from factoids WHERE name='factoids';"
#A system that stores useful bits of information
module_sqlite3_exec_sql "SELECT value FROM $config_module_factoids_table WHERE name='$(module_sqlite3_clean_string "$1")';"
}
#---------------------------------------------------------------------
## Insert a new item into DB
## @Type Private
## @param key
## @param value
## @param hostmask of person who added it
#---------------------------------------------------------------------
module_factoids_INSERT() {
module_sqlite3_exec_sql \
"INSERT INTO $config_module_factoids_table (name, value, who) VALUES('$(module_sqlite3_clean_string "$1")', '$(module_sqlite3_clean_string "$2")', '$(module_sqlite3_clean_string "$3")');"
}
#---------------------------------------------------------------------
## Change the item in DB
## @Type Private
## @param key
## @param new value
## @param hostmask of person who changed it
#---------------------------------------------------------------------
module_factoids_UPDATE() {
module_sqlite3_exec_sql \
"UPDATE $config_module_factoids_table SET value='$(module_sqlite3_clean_string "$2")', who='$(module_sqlite3_clean_string "$3")' WHERE name='$(module_sqlite3_clean_string "$1")';"
}
#---------------------------------------------------------------------
## Remove an item
## @Type Private
## @param key
#---------------------------------------------------------------------
module_factoids_DELETE() {
module_sqlite3_exec_sql "DELETE FROM $config_module_factoids_table WHERE name='$(module_sqlite3_clean_string "$1")';"
}
#---------------------------------------------------------------------
## How many factoids are there
## @Type Private
## @Stdout Count of factoids.
#---------------------------------------------------------------------
module_factoids_get_count() {
module_sqlite3_exec_sql "SELECT COUNT(name) FROM $config_module_factoids_table;"
}
#---------------------------------------------------------------------
## How many locked factoids are there
## @Type Private
## @Stdout Count of locked factoids.
#---------------------------------------------------------------------
module_factoids_get_locked_count() {
module_sqlite3_exec_sql "SELECT COUNT(name) FROM $config_module_factoids_table WHERE is_locked='1';"
}
#---------------------------------------------------------------------
## Check if factoid is locked or not.
## @Type Private
## @param key
## @return 0 locked
## @return 1 not locked
#---------------------------------------------------------------------
module_factoids_is_locked() {
local lock="$(module_sqlite3_exec_sql "SELECT is_locked FROM $config_module_factoids_table WHERE name='$(module_sqlite3_clean_string "$1")';")"
if [[ $lock == "1" ]]; then
return 0
else
return 1
fi
}
#---------------------------------------------------------------------
## Lock a factoid against changes from non-owners
## @Type Private
## @param key
#---------------------------------------------------------------------
module_factoids_lock() {
module_sqlite3_exec_sql "UPDATE $config_module_factoids_table SET is_locked='1' WHERE name='$(module_sqlite3_clean_string "$1")';"
}
#---------------------------------------------------------------------
## Unlock a factoid from protection against non-owners
## @Type Private
## @param key
#---------------------------------------------------------------------
module_factoids_unlock() {
module_sqlite3_exec_sql "UPDATE $config_module_factoids_table SET is_locked='0' WHERE name='$(module_sqlite3_clean_string "$1")';"
}
#---------------------------------------------------------------------
## Wrapper, call either INSERT or UPDATE
## @Type Private
## @param key
## @param value
## @param hostmask of person set it
#---------------------------------------------------------------------
module_factoids_set_INSERT_or_UPDATE() {
if [[ $(module_factoids_SELECT "$1") ]]; then
module_factoids_UPDATE "$1" "$2" "$3"
else
module_factoids_INSERT "$1" "$2" "$3"
fi
}
#---------------------------------------------------------------------
## Wrapper, call either INSERT or UPDATE
## @Type Private
## @param key
## @param value
## @param sender
## @param channel
#---------------------------------------------------------------------
module_factoids_set() {
local key="$1"
local value="$2"
local sender="$3"
local channel="$4"
local sendernick
parse_hostmask_nick "$sender" 'sendernick'
if module_factoids_is_locked "$key"; then
if access_check_capab "factoid_admin" "$sender" "GLOBAL"; then
module_factoids_set_INSERT_or_UPDATE "$key" "$value" "$sender"
send_msg "$channel" "Ok ${sendernick}, I will remember, $key is $value"
else
access_fail "$sender" "change a locked factoid" "factoid_admin"
fi
else
module_factoids_set_INSERT_or_UPDATE "$key" "$value" "$sender"
send_msg "$channel" "Ok ${sendernick}, I will remember, $key is $value"
fi
}
#---------------------------------------------------------------------
## Wrapper, check access
## @Type Private
## @param key
## @param sender
## @param channel
#---------------------------------------------------------------------
module_factoids_remove() {
local key="$1"
local sender="$2"
local channel="$3"
local value="$(module_factoids_SELECT "$(tr '[:upper:]' '[:lower:]' <<< "$key")")"
if [[ "$value" ]]; then
if module_factoids_is_locked "$key"; then
if access_check_capab "factoid_admin" "$sender" "GLOBAL"; then
module_factoids_DELETE "$key"
send_msg "$channel" "I forgot $key"
else
access_fail "$sender" "remove a locked factoid" "factoid_admin"
fi
else
module_factoids_DELETE "$key"
send_msg "$channel" "I forgot $key"
fi
else
send_msg "$channel" "I didn't have a factoid matching \"$key\""
fi
}
#---------------------------------------------------------------------
## Send the factoid:
## @Type Private
## @param To where (channel or nick)
## @param What factoid.
#---------------------------------------------------------------------
module_factoids_send_factoid() {
local channel="$1"
local key="$2"
local value="$(module_factoids_SELECT "$(tr '[:upper:]' '[:lower:]' <<< "$key")")"
if [[ "$value" ]]; then
if [[ $value =~ ^\\ *(.*) ]]; then
send_msg "$channel" "${BASH_REMATCH[1]}"
elif [[ $value =~ ^\\ *(.*) ]]; then
send_ctcp "$channel" "ACTION ${BASH_REMATCH[1]}"
else
send_msg "$channel" "$key is $value"
fi
else
send_msg "$channel" "I don't know what \"$key\" is."
fi
}
#---------------------------------------------------------------------
## Parse assignment:
## @Type Private
## @param String to parse
## @Note Will return using Global variables
## @Globals $module_factoids_parse_key $module_factoids_parse_value
#---------------------------------------------------------------------
module_factoids_parse_assignment() {
local word key value
# Have we hit a separator yet?
local state=0
while read -rd ' ' word; do
case "$state" in
0)
# If state is 1 the rest is value
if [[ "$word" =~ ^(as|is|are|=)$ ]]; then
state=1
else
key+=" $word"
fi
;;
1)
value+=" $word"
;;
esac
# Extra space at end is intended, to make read work correctly.
done <<< "$1 "
# And clean spaces, fastest way
read -ra module_factoids_parse_key <<< "$key"
read -ra module_factoids_parse_value <<< "$value"
}
module_factoids_handler_learn() {
local sender="$1"
local sendernick
parse_hostmask_nick "$sender" 'sendernick'
local channel="$2"
if ! [[ $2 =~ ^# ]]; then
channel="$sendernick"
fi
local parameters="$3"
if [[ "$parameters" =~ ^(.+)\ (as|is|are|=)\ (.+) ]]; then
# Do the actual parsing elsewhere:
module_factoids_parse_assignment "$parameters"
local key="${module_factoids_parse_key[*]}"
local value="${module_factoids_parse_value[*]}"
unset module_factoids_parse_key module_factoids_parse_value
module_factoids_set "$(tr '[:upper:]' '[:lower:]' <<< "$key")" "$value" "$sender" "$channel"
else
feedback_bad_syntax "$sendernick" "learn" " (as|is|are|=) "
fi
return 1
}
module_factoids_handler_forget() {
local sender="$1"
local sendernick
parse_hostmask_nick "$sender" 'sendernick'
local channel="$2"
if ! [[ $2 =~ ^# ]]; then
channel="$sendernick"
fi
local parameters="$3"
if [[ "$parameters" =~ ^(.+) ]]; then
local key="${BASH_REMATCH[1]}"
module_factoids_remove "$(tr '[:upper:]' '[:lower:]' <<< "$key")" "$sender" "$channel"
else
feedback_bad_syntax "$sendernick" "forget" ""
fi
}
module_factoids_handler_lock_factoid() {
local sender="$1"
if access_check_capab "factoid_admin" "$sender" "GLOBAL"; then
local sendernick
parse_hostmask_nick "$sender" 'sendernick'
local channel="$2"
if ! [[ $2 =~ ^# ]]; then
channel="$sendernick"
fi
local parameters="$3"
if [[ "$parameters" =~ ^(.+) ]]; then
local key="${BASH_REMATCH[1]}"
module_factoids_lock "$(tr '[:upper:]' '[:lower:]' <<< "$key")"
send_msg "$channel" "Ok ${sendernick}, the factoid \"$key\" is now protected from changes"
else
feedback_bad_syntax "$sendernick" "lock" ""
fi
else
access_fail "$sender" "lock a factoid" "factoid_admin"
fi
}
module_factoids_handler_unlock_factoid() {
local sender="$1"
if access_check_capab "factoid_admin" "$sender" "GLOBAL"; then
local sendernick
parse_hostmask_nick "$sender" 'sendernick'
local channel="$2"
if ! [[ $2 =~ ^# ]]; then
channel="$sendernick"
fi
local parameters="$3"
if [[ "$parameters" =~ ^(.+) ]]; then
local key="${BASH_REMATCH[1]}"
module_factoids_unlock "$(tr '[:upper:]' '[:lower:]' <<< "$key")"
send_msg "$channel" "Ok ${sendernick}, the factoid \"$key\" is no longer protected from changes"
else
feedback_bad_syntax "$sendernick" "lock" ""
fi
else
access_fail "$sender" "lock a factoid" "factoid_admin"
fi
}
module_factoids_handler_whatis() {
local sender="$1"
local sendernick
parse_hostmask_nick "$sender" 'sendernick'
local channel="$2"
if ! [[ $2 =~ ^# ]]; then
channel="$sendernick"
fi
local parameters="$3"
if [[ "$parameters" =~ ^(.+) ]]; then
local key="${BASH_REMATCH[1]}"
module_factoids_send_factoid "$channel" "$key"
else
feedback_bad_syntax "$sendernick" "whatis" ""
fi
}
module_factoids_handler_factoid_stats() {
local sender="$1"
local channel="$2"
if ! [[ $2 =~ ^# ]]; then
parse_hostmask_nick "$sender" 'channel'
fi
local count="$(module_factoids_get_count)"
local lockedcount="$(module_factoids_get_locked_count)"
if [[ "$count" ]]; then
send_msg "$channel" "There are $count items in my factoid database. $lockedcount of the factoids are locked."
fi
}
module_factoids_on_PRIVMSG() {
local sender="$1"
local channel="$2"
if ! [[ $2 =~ ^# ]]; then
parse_hostmask_nick "$sender" 'channel'
fi
local query="$3"
# Answer question in channel if we got a factoid.
if [[ "$query" =~ ^((what|where|who|why|how)\ )?((is|are|were|was|to|can I find)\ )?([^\?]+)\?? ]]; then
local key="${BASH_REMATCH[@]: -1}"
local value="$(module_factoids_SELECT "$(tr '[:upper:]' '[:lower:]' <<< "$key")")"
if [[ "$value" ]]; then
module_factoids_send_factoid "$channel" "$key"
return 1
fi
fi
return 0
}