#!/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