167 lines
5.2 KiB
Bash
Executable File
167 lines
5.2 KiB
Bash
Executable File
#!/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
|