Files
stormbot/triggers/keywords/keywords.sh
2025-10-25 01:30:02 -04:00

146 lines
5.6 KiB
Bash
Executable File

#!/usr/bin/env bash
[ -f functions.sh ] && source functions.sh
who="${1%!*}"
who="${who//:}"
shift
# shellcheck disable=SC2034 # Used in action strings via execute_action
chan="$1"
shift
keywordsFile="triggers/keywords/keywords.cfg"
# Function to process random selection syntax: {random:opt1|opt2|opt3}
process_random() {
local text="$1"
while [[ "$text" =~ \{random:([^}]+)\} ]]; do
local options="${BASH_REMATCH[1]}"
IFS='|' read -ra optArray <<< "$options"
local selected="${optArray[$((RANDOM % ${#optArray[@]}))]}"
text="${text/\{random:$options\}/$selected}"
done
echo "$text"
}
# Safe execution function - only allows predefined IRC functions
execute_action() {
local action="$1"
# Parse the action to extract function name and arguments
if [[ "$action" =~ ^msg[[:space:]]+(\"[^\"]+\"|[^[:space:]]+)[[:space:]]+(.+)$ ]]; then
local target="${BASH_REMATCH[1]}"
local message="${BASH_REMATCH[2]}"
# Remove quotes from target and message if present
target="${target//\"/}"
# Safely expand only $chan and $who variables - NO EVAL
message="${message//\"\$chan\"/$chan}"
message="${message//\$chan/$chan}"
message="${message//\"\$who\"/$who}"
message="${message//\$who/$who}"
message="${message//\$\{chan\}/$chan}"
message="${message//\$\{who\}/$who}"
message="$(process_random "$message")"
msg "$target" "$message"
elif [[ "$action" =~ ^act[[:space:]]+(\"[^\"]+\"|[^[:space:]]+)[[:space:]]+(.+)$ ]]; then
local target="${BASH_REMATCH[1]}"
local message="${BASH_REMATCH[2]}"
# Remove quotes from target and message if present
target="${target//\"/}"
# Safely expand only $chan and $who variables - NO EVAL
message="${message//\"\$chan\"/$chan}"
message="${message//\$chan/$chan}"
message="${message//\"\$who\"/$who}"
message="${message//\$who/$who}"
message="${message//\$\{chan\}/$chan}"
message="${message//\$\{who\}/$who}"
message="$(process_random "$message")"
act "$target" "$message"
elif [[ "$action" =~ ^reply[[:space:]]+(\"[^\"]+\"|[^[:space:]]+)[[:space:]]+(.+)$ ]]; then
local target="${BASH_REMATCH[1]}"
local message="${BASH_REMATCH[2]}"
# Remove quotes from target and message if present
target="${target//\"/}"
# Safely expand only $chan and $who variables - NO EVAL
message="${message//\"\$chan\"/$chan}"
message="${message//\$chan/$chan}"
message="${message//\"\$who\"/$who}"
message="${message//\$who/$who}"
message="${message//\$\{chan\}/$chan}"
message="${message//\$\{who\}/$who}"
message="$(process_random "$message")"
reply "$target" "$message"
fi
}
# Load keywords from config file into associative array
declare -A keywords
if [[ -f "$keywordsFile" ]]; then
while IFS='|' read -r keyword action percentage || [[ -n "$keyword" ]]; do
# Skip comments and empty lines
[[ "$keyword" =~ ^[[:space:]]*# ]] && continue
[[ -z "$keyword" ]] && continue
# Trim whitespace
keyword="${keyword#"${keyword%%[![:space:]]*}"}"
keyword="${keyword%"${keyword##*[![:space:]]}"}"
action="${action#"${action%%[![:space:]]*}"}"
action="${action%"${action##*[![:space:]]}"}"
percentage="${percentage#"${percentage%%[![:space:]]*}"}"
percentage="${percentage%"${percentage##*[![:space:]]}"}"
# Store in array (key includes ~ prefix for multi-word triggers)
if [[ -n "$percentage" ]]; then
keywords["$keyword"]="$action $percentage"
else
keywords["$keyword"]="$action"
fi
done < "$keywordsFile"
fi
# Process single-word triggers
wordList="$(echo "${@,,}" | tr '[:space:]' $'\n' | sort -u)"
for w in ${wordList//[[:punct:]]/} ; do
if [[ -n "${keywords[${w,,}]}" && "$lastWordMatch" != "${keywords[${w,,}]}" ]]; then
keywordAction="${keywords[${w,,}]}"
# Check if the last element is a percentage
if [[ "$keywordAction" =~ (.*)\ ([0-9]+)%$ ]]; then
command="${BASH_REMATCH[1]}"
percentage="${BASH_REMATCH[2]}"
# Generate random number between 1-100 and only respond if within percentage
randomNum=$((RANDOM % 100 + 1))
if [[ $randomNum -le $percentage ]]; then
execute_action "$command"
fi
else
# No percentage specified, always respond
execute_action "$keywordAction"
fi
lastWordMatch="${keywords[${w,,}]}"
fi
done
# Process multi-word triggers (those starting with ~)
wordList="$(echo "${@,,}" | tr -d '[:space:]')"
for trigger in "${!keywords[@]}"; do
if [[ "$trigger" =~ ^~ ]]; then
# Remove the ~ prefix for matching
triggerPattern="${trigger#\~}"
if [[ "${wordList,,}" =~ .*${triggerPattern}.* ]]; then
keywordAction="${keywords[$trigger]}"
# Check if the last element is a percentage
if [[ "$keywordAction" =~ (.*)\ ([0-9]+)%$ ]]; then
command="${BASH_REMATCH[1]}"
percentage="${BASH_REMATCH[2]}"
# Generate random number between 1-100 and only respond if within percentage
randomNum=$((RANDOM % 100 + 1))
if [[ $randomNum -le $percentage ]]; then
execute_action "$command"
fi
else
# No percentage specified, always respond
execute_action "$keywordAction"
fi
fi
fi
done