#!/usr/bin/env bash # Word tracking trigger - monitors messages and tracks word usage # shellcheck disable=SC1091 [ -f functions.sh ] && source functions.sh # shellcheck disable=SC1091 [ -f triggers/wordtrack/categories.sh ] && source triggers/wordtrack/categories.sh name="$1" channelName="$2" shift 2 message="$*" # Sanitize username (extract just the nickname, max 30 chars per IRC RFC) # Remove any IRC protocol remnants, hostmask info, etc. name="${name%%!*}" # Remove hostmask if present name="${name%%[[:space:]]*}" # Remove any trailing data name="${name//[^a-zA-Z0-9_\[\]\{\}\\|\^-]/}" # Only allow valid IRC nick chars name="${name:0:30}" # Limit to max IRC nickname length # If name is empty or invalid after sanitization, skip if [[ -z "$name" ]]; then exit 0 fi # Sanitize channel name (remove any IRC protocol remnants) channelName="${channelName%%[[:space:]]*}" channelName="${channelName//[^a-zA-Z0-9#_-]/}" # Only process if we have a valid channel name starting with # if [[ ! "$channelName" =~ ^#[a-zA-Z0-9_-]+$ ]]; then exit 0 fi # Convert message to lowercase for case-insensitive matching messageLower="${message,,}" # Create data directory for this channel if it doesn't exist dataDir="triggers/wordtrack/data/${channelName}" mkdir -p "$dataDir" # User data file userDataFile="${dataDir}/${name}.dat" # Load existing user data declare -A userCounts if [[ -f "$userDataFile" ]]; then while IFS='=' read -r category count; do userCounts["$category"]="$count" done < "$userDataFile" fi # Track which categories had level-ups declare -a levelUps # Process each category # shellcheck disable=SC2154 for category in "${categories[@]}"; do # Get the word array and levels array for this category levelsArrayName="${category}Levels" # Check if message contains any words from this category wordCount=0 wordsArrayNameClean="${category}Words" declare -n wordsRef="$wordsArrayNameClean" for word in "${wordsRef[@]}"; do # Count all occurrences of this word in the message wordLower="${word,,}" tempMessage="$messageLower" # Parse wildcard markers to determine match type # * prefix = allow match at end of larger word (e.g., *game matches endgame) # * suffix = allow match at start of larger word (e.g., coffee* matches coffeehouse) # both = allow match anywhere (e.g., *play* matches gameplay) # none = exact word match only (e.g., tea only matches tea) prefixWild=false suffixWild=false if [[ "$wordLower" == \** ]]; then prefixWild=true wordLower="${wordLower#\*}" fi if [[ "$wordLower" == *\* ]]; then suffixWild=true wordLower="${wordLower%\*}" fi # Build regex pattern based on wildcard markers if $prefixWild && $suffixWild; then # Match anywhere in text pattern="$wordLower" elif $prefixWild; then # Match at end of word or standalone pattern="$wordLower([[:space:][:punct:]]|$)" elif $suffixWild; then # Match at start of word or standalone pattern="(^|[[:space:][:punct:]])$wordLower" else # Exact word match only pattern="(^|[[:space:][:punct:]])$wordLower([[:space:][:punct:]]|$)" fi # Use word boundary matching based on pattern while [[ "$tempMessage" =~ $pattern ]]; do ((wordCount++)) # Remove the matched word to find more occurrences # Use the actual matched portion to avoid removing partial matches matchedWord="${BASH_REMATCH[0]}" tempMessage="${tempMessage/"$matchedWord"/ }" done done unset -n wordsRef # If words were found, update the counter if ((wordCount > 0)); then oldCount="${userCounts[$category]:-0}" newCount=$((oldCount + wordCount)) userCounts["$category"]="$newCount" # Check for level-up oldLevel="" newLevel="" # Get the thresholds for this category using nameref declare -n levelsRef="$levelsArrayName" # Get old level for threshold in $(printf '%s\n' "${!levelsRef[@]}" | sort -n); do if ((oldCount >= threshold)); then oldLevel="${levelsRef[$threshold]}" fi done # Get new level for threshold in $(printf '%s\n' "${!levelsRef[@]}" | sort -n); do if ((newCount >= threshold)); then newLevel="${levelsRef[$threshold]}" fi done # If level changed, record the level-up if [[ -n "$newLevel" && "$newLevel" != "$oldLevel" ]]; then levelUps+=("$category:$newLevel:$newCount") fi # Clean up nameref unset -n levelsRef fi done # Save updated user data : > "$userDataFile" for category in "${!userCounts[@]}"; do echo "${category}=${userCounts[$category]}" >> "$userDataFile" done # Announce level-ups for levelUp in "${levelUps[@]}"; do IFS=':' read -r category level count <<< "$levelUp" msg "$channelName" "$name just leveled up in ${category}! You are now a ${level} with ${count} words!" done