Compare commits
9 Commits
b61d6c673c
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c3328e092d | ||
|
|
6e85ced88c | ||
|
|
2a25355c58 | ||
|
|
fbce315cf6 | ||
|
|
d53c44cc76 | ||
|
|
2cd8f3e99d | ||
|
|
9b7a786a96 | ||
|
|
d6825c4a92 | ||
|
|
278acc4d8f |
@@ -1,19 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
[ -f functions.sh ] && source functions.sh
|
|
||||||
|
|
||||||
# Dependencies required by this module
|
|
||||||
dependencies=("curl" "w3m" "grep" "iconv")
|
|
||||||
|
|
||||||
# Check dependencies before running
|
|
||||||
if ! check_dependencies "${dependencies[@]}"; then
|
|
||||||
msg "$2" "$1: This module requires: ${dependencies[*]}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
fml="$(curl -Ls --connect-timeout 5 https://fmylife.com/random | grep -m1 -A1 '<a class="article-link" href="/article/.*>' | tail -1 | w3m -dump -T text/html | iconv -f utf-8 -t ascii | tr '[:space:]' ' ')"
|
|
||||||
|
|
||||||
if [[ ${#fml} -gt 10 ]]; then
|
|
||||||
msg "$2" "${fml}"
|
|
||||||
else
|
|
||||||
msg "$2" "I couldn't get any fmls. fml"
|
|
||||||
fi
|
|
||||||
@@ -7,7 +7,7 @@ lastLine="$(grep -n '^exit 0' $0)"
|
|||||||
lastLine=${lastLine%%:*}
|
lastLine=${lastLine%%:*}
|
||||||
lastLine=$((lastLine + 1))
|
lastLine=$((lastLine + 1))
|
||||||
# display a line from the end of the file after the script
|
# display a line from the end of the file after the script
|
||||||
msg "$2" "$(tail +${lastLine} "$0" | sed '/^$/d' | shuf -n1)":
|
msg "$2" "$(tail +${lastLine} "$0" | sed '/^$/d' | shuf -n1)"
|
||||||
|
|
||||||
# Put jokes after the exit, one per line.
|
# Put jokes after the exit, one per line.
|
||||||
exit 0
|
exit 0
|
||||||
|
|||||||
@@ -15,8 +15,16 @@ name="$1"
|
|||||||
channelName="$2"
|
channelName="$2"
|
||||||
shift 2
|
shift 2
|
||||||
|
|
||||||
|
# Parse arguments into array to handle subcommands properly
|
||||||
|
# When bot.sh calls this with "$will", it arrives as a single quoted argument
|
||||||
|
# e.g., "set Ferguson North Carolina" comes as one $1, not multiple args
|
||||||
|
# shellcheck disable=SC2206
|
||||||
|
request=($*)
|
||||||
|
|
||||||
# Database file for user locations and cached geocoding
|
# Database file for user locations and cached geocoding
|
||||||
weatherDb="data/weather.db"
|
weatherDb="data/weather.db"
|
||||||
|
weatherDbLock="${weatherDb}.lock"
|
||||||
|
nominatimRateLimitFile="data/nominatim_last_request"
|
||||||
mkdir -p "$(dirname "$weatherDb")"
|
mkdir -p "$(dirname "$weatherDb")"
|
||||||
touch "$weatherDb"
|
touch "$weatherDb"
|
||||||
|
|
||||||
@@ -73,6 +81,10 @@ save_user_location() {
|
|||||||
local lon="$4"
|
local lon="$4"
|
||||||
local formatted="$5"
|
local formatted="$5"
|
||||||
|
|
||||||
|
# Use flock to prevent race conditions
|
||||||
|
(
|
||||||
|
flock -x 200
|
||||||
|
|
||||||
# Remove old entry if exists
|
# Remove old entry if exists
|
||||||
if [[ -f "$weatherDb" ]]; then
|
if [[ -f "$weatherDb" ]]; then
|
||||||
grep -v "^USER|${user}|" "$weatherDb" > "${weatherDb}.tmp"
|
grep -v "^USER|${user}|" "$weatherDb" > "${weatherDb}.tmp"
|
||||||
@@ -81,16 +93,54 @@ save_user_location() {
|
|||||||
|
|
||||||
# Add new entry
|
# Add new entry
|
||||||
echo "USER|${user}|${location}|${lat}|${lon}|${formatted}" >> "$weatherDb"
|
echo "USER|${user}|${location}|${lat}|${lon}|${formatted}" >> "$weatherDb"
|
||||||
|
|
||||||
|
) 200>"$weatherDbLock"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to delete user location
|
# Function to delete user location
|
||||||
delete_user_location() {
|
delete_user_location() {
|
||||||
local user="$1"
|
local user="$1"
|
||||||
|
|
||||||
|
# Use flock to prevent race conditions
|
||||||
|
(
|
||||||
|
flock -x 200
|
||||||
|
|
||||||
if [[ -f "$weatherDb" ]]; then
|
if [[ -f "$weatherDb" ]]; then
|
||||||
grep -v "^USER|${user}|" "$weatherDb" > "${weatherDb}.tmp"
|
grep -v "^USER|${user}|" "$weatherDb" > "${weatherDb}.tmp"
|
||||||
mv "${weatherDb}.tmp" "$weatherDb"
|
mv "${weatherDb}.tmp" "$weatherDb"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
) 200>"$weatherDbLock"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to maintain cache size
|
||||||
|
maintain_cache() {
|
||||||
|
# Use flock to prevent race conditions
|
||||||
|
(
|
||||||
|
flock -x 200
|
||||||
|
|
||||||
|
if [[ ! -f "$weatherDb" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local lineCount
|
||||||
|
lineCount=$(wc -l < "$weatherDb")
|
||||||
|
|
||||||
|
# If file has 2000 or more lines, keep only the most recent 1500
|
||||||
|
if [[ $lineCount -ge 2000 ]]; then
|
||||||
|
# Separate USER and CACHE entries
|
||||||
|
grep "^USER|" "$weatherDb" > "${weatherDb}.tmp.user" 2>/dev/null || true
|
||||||
|
grep "^CACHE|" "$weatherDb" | tail -n 1000 > "${weatherDb}.tmp.cache" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Combine them back (keep all user entries, trim cache entries)
|
||||||
|
cat "${weatherDb}.tmp.user" "${weatherDb}.tmp.cache" > "${weatherDb}.tmp" 2>/dev/null
|
||||||
|
mv "${weatherDb}.tmp" "$weatherDb"
|
||||||
|
|
||||||
|
# Clean up temp files
|
||||||
|
rm -f "${weatherDb}.tmp.user" "${weatherDb}.tmp.cache"
|
||||||
|
fi
|
||||||
|
|
||||||
|
) 200>"$weatherDbLock"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function to cache geocode result
|
# Function to cache geocode result
|
||||||
@@ -106,10 +156,45 @@ cache_location() {
|
|||||||
local cached
|
local cached
|
||||||
cached=$(get_cached_location "$query")
|
cached=$(get_cached_location "$query")
|
||||||
if [[ -z "$cached" ]]; then
|
if [[ -z "$cached" ]]; then
|
||||||
|
# Use flock to prevent race conditions
|
||||||
|
(
|
||||||
|
flock -x 200
|
||||||
echo "CACHE|${queryLower}|${location}|${lat}|${lon}|${formatted}" >> "$weatherDb"
|
echo "CACHE|${queryLower}|${location}|${lat}|${lon}|${formatted}" >> "$weatherDb"
|
||||||
|
) 200>"$weatherDbLock"
|
||||||
|
|
||||||
|
# Maintain cache size after adding new entry
|
||||||
|
maintain_cache
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Function to enforce Nominatim rate limit (1 request per second)
|
||||||
|
rate_limit_nominatim() {
|
||||||
|
local rateLimitLock="${nominatimRateLimitFile}.lock"
|
||||||
|
|
||||||
|
(
|
||||||
|
flock -x 200
|
||||||
|
|
||||||
|
# Check if rate limit file exists and read last request time
|
||||||
|
if [[ -f "$nominatimRateLimitFile" ]]; then
|
||||||
|
local lastRequest
|
||||||
|
lastRequest=$(cat "$nominatimRateLimitFile" 2>/dev/null || echo "0")
|
||||||
|
local currentTime
|
||||||
|
currentTime=$(date +%s)
|
||||||
|
local timeSinceLastRequest=$((currentTime - lastRequest))
|
||||||
|
|
||||||
|
# If less than 1 second has passed, sleep for the remaining time
|
||||||
|
if [[ $timeSinceLastRequest -lt 1 ]]; then
|
||||||
|
local sleepTime=$((1 - timeSinceLastRequest))
|
||||||
|
sleep "$sleepTime"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update last request time
|
||||||
|
date +%s > "$nominatimRateLimitFile"
|
||||||
|
|
||||||
|
) 200>"$rateLimitLock"
|
||||||
|
}
|
||||||
|
|
||||||
# Function to format location (City, State or City, Country)
|
# Function to format location (City, State or City, Country)
|
||||||
format_location() {
|
format_location() {
|
||||||
local address="$1"
|
local address="$1"
|
||||||
@@ -176,12 +261,45 @@ geocode_location() {
|
|||||||
local userAgent="stormbot-weather/1.0"
|
local userAgent="stormbot-weather/1.0"
|
||||||
local response
|
local response
|
||||||
|
|
||||||
|
# Check if query contains common US indicators (case-insensitive)
|
||||||
|
local countryCode=""
|
||||||
|
local queryLower="${query,,}"
|
||||||
|
local isUsZipCode=false
|
||||||
|
|
||||||
|
# Common US state names and abbreviations
|
||||||
|
local usStates="alabama|alaska|arizona|arkansas|california|colorado|connecticut|delaware|florida|georgia|hawaii|idaho|illinois|indiana|iowa|kansas|kentucky|louisiana|maine|maryland|massachusetts|michigan|minnesota|mississippi|missouri|montana|nebraska|nevada|new hampshire|new jersey|new mexico|new york|north carolina|north dakota|ohio|oklahoma|oregon|pennsylvania|rhode island|south carolina|south dakota|tennessee|texas|utah|vermont|virginia|washington|west virginia|wisconsin|wyoming"
|
||||||
|
|
||||||
|
# Check if this is a US ZIP code query
|
||||||
|
if [[ "$query" =~ ^[0-9]{5}([[:space:]]|$) ]]; then
|
||||||
|
isUsZipCode=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$queryLower" =~ (usa|united states) ]] || \
|
||||||
|
[[ "$query" =~ [[:space:]][A-Z]{2}$ ]] || \
|
||||||
|
[[ "$queryLower" =~ [[:space:]](${usStates})$ ]] || \
|
||||||
|
[[ "$isUsZipCode" == true ]]; then
|
||||||
|
# Query mentions USA, ends with state abbreviation, ends with state name, or is a zip code
|
||||||
|
countryCode="&countrycodes=us"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build the query URL
|
||||||
|
local apiUrl
|
||||||
|
if [[ "$isUsZipCode" == true ]]; then
|
||||||
|
# For ZIP codes, use postalcode parameter for better accuracy
|
||||||
|
local zipCode="${query%% *}" # Extract just the ZIP code (remove any trailing text)
|
||||||
|
apiUrl="${url}?postalcode=${zipCode}&country=us&format=json&limit=1&addressdetails=1"
|
||||||
|
else
|
||||||
# URL encode the query (replace spaces with +)
|
# URL encode the query (replace spaces with +)
|
||||||
local encodedQuery="${query// /+}"
|
local encodedQuery="${query// /+}"
|
||||||
|
apiUrl="${url}?q=${encodedQuery}&format=json&limit=1&addressdetails=1${countryCode}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Enforce rate limit before making API request
|
||||||
|
rate_limit_nominatim
|
||||||
|
|
||||||
response=$(curl -s --connect-timeout 5 --max-time 10 \
|
response=$(curl -s --connect-timeout 5 --max-time 10 \
|
||||||
-H "User-Agent: ${userAgent}" \
|
-H "User-Agent: ${userAgent}" \
|
||||||
"${url}?q=${encodedQuery}&format=json&limit=1&addressdetails=1")
|
"${apiUrl}")
|
||||||
|
|
||||||
if [[ -z "$response" || "$response" == "[]" ]]; then
|
if [[ -z "$response" || "$response" == "[]" ]]; then
|
||||||
return 1
|
return 1
|
||||||
@@ -217,7 +335,7 @@ get_weather() {
|
|||||||
|
|
||||||
local url="https://api.open-meteo.com/v1/forecast"
|
local url="https://api.open-meteo.com/v1/forecast"
|
||||||
local params="latitude=${lat}&longitude=${lon}"
|
local params="latitude=${lat}&longitude=${lon}"
|
||||||
params+="¤t=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m"
|
params+="¤t=temperature_2m,apparent_temperature,relative_humidity_2m,weather_code,wind_speed_10m"
|
||||||
params+="&daily=weather_code,temperature_2m_max,temperature_2m_min"
|
params+="&daily=weather_code,temperature_2m_max,temperature_2m_min"
|
||||||
params+="&timezone=auto&forecast_days=3&temperature_unit=fahrenheit&wind_speed_unit=mph"
|
params+="&timezone=auto&forecast_days=3&temperature_unit=fahrenheit&wind_speed_unit=mph"
|
||||||
|
|
||||||
@@ -239,17 +357,35 @@ format_weather() {
|
|||||||
|
|
||||||
# Parse current weather
|
# Parse current weather
|
||||||
local temp
|
local temp
|
||||||
|
local feelsLike
|
||||||
local humidity
|
local humidity
|
||||||
local windSpeed
|
local windSpeed
|
||||||
local weatherCode
|
local weatherCode
|
||||||
temp=$(echo "$weatherData" | jq -r '.current.temperature_2m // "N/A"')
|
temp=$(echo "$weatherData" | jq -r '.current.temperature_2m // "N/A"')
|
||||||
|
feelsLike=$(echo "$weatherData" | jq -r '.current.apparent_temperature // "N/A"')
|
||||||
humidity=$(echo "$weatherData" | jq -r '.current.relative_humidity_2m // "N/A"')
|
humidity=$(echo "$weatherData" | jq -r '.current.relative_humidity_2m // "N/A"')
|
||||||
windSpeed=$(echo "$weatherData" | jq -r '.current.wind_speed_10m // "N/A"')
|
windSpeed=$(echo "$weatherData" | jq -r '.current.wind_speed_10m // "N/A"')
|
||||||
weatherCode=$(echo "$weatherData" | jq -r '.current.weather_code // 0')
|
weatherCode=$(echo "$weatherData" | jq -r '.current.weather_code // 0')
|
||||||
local conditions="${weatherCodes[$weatherCode]:-Unknown}"
|
local conditions="${weatherCodes[$weatherCode]:-Unknown}"
|
||||||
|
|
||||||
# Build message
|
# Build message
|
||||||
local message="Weather for ${locationName}: ${temp}°F, ${conditions}"
|
local message="Weather for ${locationName}: ${temp}°F"
|
||||||
|
|
||||||
|
# Add feels-like if different from actual temp (round to nearest degree for comparison)
|
||||||
|
if [[ "$feelsLike" != "N/A" && "$temp" != "N/A" ]]; then
|
||||||
|
local tempRounded
|
||||||
|
local feelsLikeRounded
|
||||||
|
tempRounded=$(printf "%.0f" "$temp" 2>/dev/null || echo "$temp")
|
||||||
|
feelsLikeRounded=$(printf "%.0f" "$feelsLike" 2>/dev/null || echo "$feelsLike")
|
||||||
|
|
||||||
|
# Only show feels-like if it differs by at least 3 degrees
|
||||||
|
local diff=$((tempRounded - feelsLikeRounded))
|
||||||
|
if [[ ${diff#-} -ge 3 ]]; then
|
||||||
|
message+=" (feels like ${feelsLike}°F)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
message+=", ${conditions}"
|
||||||
|
|
||||||
if [[ "$humidity" != "N/A" ]]; then
|
if [[ "$humidity" != "N/A" ]]; then
|
||||||
message+=", Humidity: ${humidity}%"
|
message+=", Humidity: ${humidity}%"
|
||||||
@@ -323,23 +459,20 @@ format_forecast() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# Main command logic
|
# Main command logic
|
||||||
subcommand="${1:-}"
|
# Check if first element is a subcommand
|
||||||
|
subcommand="${request[0]:-}"
|
||||||
# If subcommand is the module name itself (w or weather), treat as no argument
|
|
||||||
if [[ "$subcommand" == "w" || "$subcommand" == "weather" ]]; then
|
|
||||||
subcommand=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$subcommand" in
|
case "$subcommand" in
|
||||||
set)
|
set)
|
||||||
# Set user's location
|
# Set user's location
|
||||||
shift
|
# Remove 'set' from the array and get the rest as location
|
||||||
if [[ $# -eq 0 ]]; then
|
if [[ ${#request[@]} -lt 2 ]]; then
|
||||||
msg "$channelName" "$name: Usage: weather -set <location>"
|
msg "$channelName" "$name: Usage: weather set <location>"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
location="$*"
|
# Get everything after 'set' as the location
|
||||||
|
location="${request[*]:1}"
|
||||||
|
|
||||||
# Validate location length
|
# Validate location length
|
||||||
if [[ ${#location} -gt 100 ]]; then
|
if [[ ${#location} -gt 100 ]]; then
|
||||||
@@ -364,6 +497,7 @@ case "$subcommand" in
|
|||||||
save_user_location "$name" "$fullAddress" "$lat" "$lon" "$formattedLocation"
|
save_user_location "$name" "$fullAddress" "$lat" "$lon" "$formattedLocation"
|
||||||
|
|
||||||
msg "$channelName" "$name: Your location has been set to: ${formattedLocation}"
|
msg "$channelName" "$name: Your location has been set to: ${formattedLocation}"
|
||||||
|
exit 0
|
||||||
;;
|
;;
|
||||||
|
|
||||||
del|delete)
|
del|delete)
|
||||||
@@ -376,7 +510,7 @@ case "$subcommand" in
|
|||||||
# Show weather (default action)
|
# Show weather (default action)
|
||||||
if [[ -n "$subcommand" ]]; then
|
if [[ -n "$subcommand" ]]; then
|
||||||
# Weather for specified location
|
# Weather for specified location
|
||||||
location="$*"
|
location="${request[*]}"
|
||||||
|
|
||||||
# Validate location length
|
# Validate location length
|
||||||
if [[ ${#location} -gt 100 ]]; then
|
if [[ ${#location} -gt 100 ]]; then
|
||||||
|
|||||||
@@ -1,16 +1,34 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
[ -f functions.sh ] && source functions.sh
|
[ -f functions.sh ] && source functions.sh
|
||||||
# All names to match are completely lowercase.
|
|
||||||
exitName="${1%% *}"
|
|
||||||
|
|
||||||
|
farewellsFile="triggers/bye/farewells.txt"
|
||||||
|
|
||||||
|
# All names to match are completely lowercase.
|
||||||
case "${1,,}" in
|
case "${1,,}" in
|
||||||
storm_dragon)
|
storm_dragon)
|
||||||
msg "${2%% :*}" "NOOOOOOOOOO!!! $1: come back!!!"
|
msg "${2%% :*}" "NOOOOOOOOOO!!! $1: come back!!!"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
bye=(
|
# Read farewells from file into array
|
||||||
"Bye $1."
|
if [[ -f "$farewellsFile" ]]; then
|
||||||
"Alas $1, you will be missed."
|
mapfile -t farewell < "$farewellsFile"
|
||||||
)
|
else
|
||||||
msg "${2%% :*}" "${bye[$(($RANDOM % ${#bye[@]}))]}"
|
# Fallback if file doesn't exist
|
||||||
|
farewell=("Goodbye" "Farewell!")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Replace placeholders with actual values
|
||||||
|
selectedFarewell="${farewell[$((RANDOM % ${#farewell[@]}))]}"
|
||||||
|
selectedFarewell="${selectedFarewell//\{channel\}/${2%% :*}}"
|
||||||
|
|
||||||
|
# Check if farewell contains {name} placeholder
|
||||||
|
if [[ "$selectedFarewell" == *"{name}"* ]]; then
|
||||||
|
# Replace {name} with actual name, don't add prefix
|
||||||
|
selectedFarewell="${selectedFarewell//\{name\}/$1}"
|
||||||
|
msg "${2%% :*}" "$selectedFarewell"
|
||||||
|
else
|
||||||
|
# No {name} placeholder, use traditional "name: farewell" format
|
||||||
|
msg "${2%% :*}" "$1: $selectedFarewell"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
7
triggers/bye/farewells.txt.example
Normal file
7
triggers/bye/farewells.txt.example
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Goodbye!
|
||||||
|
Farewell {name}, until we meet again!
|
||||||
|
Alas, you will be missed.
|
||||||
|
See you later!
|
||||||
|
Bye {name}!
|
||||||
|
Safe travels from {channel}!
|
||||||
|
Catch you later {name}!
|
||||||
@@ -17,10 +17,18 @@ else
|
|||||||
greeting=("Greetings" "Welcome!")
|
greeting=("Greetings" "Welcome!")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Replace {channel} placeholder with actual channel name
|
# Replace placeholders with actual values
|
||||||
selectedGreeting="${greeting[$((RANDOM % ${#greeting[@]}))]}"
|
selectedGreeting="${greeting[$((RANDOM % ${#greeting[@]}))]}"
|
||||||
selectedGreeting="${selectedGreeting//\{channel\}/$2}"
|
selectedGreeting="${selectedGreeting//\{channel\}/$2}"
|
||||||
|
|
||||||
|
# Check if greeting contains {name} placeholder
|
||||||
|
if [[ "$selectedGreeting" == *"{name}"* ]]; then
|
||||||
|
# Replace {name} with actual name, don't add prefix
|
||||||
|
selectedGreeting="${selectedGreeting//\{name\}/$1}"
|
||||||
|
msg "$2" "$selectedGreeting"
|
||||||
|
else
|
||||||
|
# No {name} placeholder, use traditional "name: greeting" format
|
||||||
msg "$2" "$1: $selectedGreeting"
|
msg "$2" "$1: $selectedGreeting"
|
||||||
|
fi
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
Greetings
|
Greetings
|
||||||
Howdy, welcome to {channel}!
|
Howdy {name}, welcome to {channel}!
|
||||||
Wazzup Moe Fugger!
|
|
||||||
Welcome to {channel}!
|
Welcome to {channel}!
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# Word tracking categories configuration
|
|
||||||
# Add your own categories by following the pattern below
|
|
||||||
|
|
||||||
# Category: coffee
|
|
||||||
# Words that trigger tracking for coffee category
|
|
||||||
coffeeWords=("coffee" "espresso" "latte" "mocha" "cappuccino" "americano" "frappuccino" "macchiato" "cortado" "affogato")
|
|
||||||
|
|
||||||
# Level thresholds and reward names for coffee category
|
|
||||||
# Array key is the threshold (word count needed), value is the level name
|
|
||||||
declare -A coffeeLevels=(
|
|
||||||
[10]="Coffee Newbie"
|
|
||||||
[25]="Coffee Drinker"
|
|
||||||
[50]="Coffee Lover"
|
|
||||||
[100]="Coffee Addict"
|
|
||||||
[200]="Coffee Fiend"
|
|
||||||
[500]="Coffee God"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Category: tea
|
|
||||||
teaWords=("tea" "matcha" "chai" "oolong" "earl" "green tea" "black tea" "herbal" "chamomile" "rooibos")
|
|
||||||
|
|
||||||
declare -A teaLevels=(
|
|
||||||
[10]="Tea Sipper"
|
|
||||||
[25]="Tea Enthusiast"
|
|
||||||
[50]="Tea Connoisseur"
|
|
||||||
[100]="Tea Master"
|
|
||||||
[200]="Tea Guru"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Category: gaming
|
|
||||||
gamingWords=("game" "gaming" "play" "played" "console" "steam" "xbox" "playstation" "nintendo" "pc gaming")
|
|
||||||
|
|
||||||
declare -A gamingLevels=(
|
|
||||||
[10]="Casual Gamer"
|
|
||||||
[25]="Regular Player"
|
|
||||||
[50]="Dedicated Gamer"
|
|
||||||
[100]="Hardcore Gamer"
|
|
||||||
[200]="Gaming Enthusiast"
|
|
||||||
[500]="Gaming Legend"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Words that trigger tracking for drugs category
|
|
||||||
drugsWords=("kratom" "gummy" "hemp" "nicotine")
|
|
||||||
|
|
||||||
# Level thresholds and reward names for drugs category
|
|
||||||
# Array key is the threshold (word count needed), value is the level name
|
|
||||||
declare -A drugsLevels=(
|
|
||||||
[10]="Adict"
|
|
||||||
[20]="Junky"
|
|
||||||
[40]="Burnout"
|
|
||||||
[80]="Dope Fiend"
|
|
||||||
[160]="Intervention Candidate"
|
|
||||||
[320]="Drug Lord"
|
|
||||||
[640]="Pickled"
|
|
||||||
)
|
|
||||||
|
|
||||||
# List all active categories (must match the prefix of your arrays above)
|
|
||||||
# This is used by the trigger to know which categories to track
|
|
||||||
categories=("coffee" "tea" "gaming" "drugs")
|
|
||||||
@@ -69,10 +69,48 @@ for category in "${categories[@]}"; do
|
|||||||
# Count all occurrences of this word in the message
|
# Count all occurrences of this word in the message
|
||||||
wordLower="${word,,}"
|
wordLower="${word,,}"
|
||||||
tempMessage="$messageLower"
|
tempMessage="$messageLower"
|
||||||
while [[ "$tempMessage" =~ $wordLower ]]; do
|
|
||||||
|
# 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++))
|
((wordCount++))
|
||||||
# Remove the matched word to find more occurrences
|
# Remove the matched word to find more occurrences
|
||||||
tempMessage="${tempMessage/$wordLower/}"
|
# Use the actual matched portion to avoid removing partial matches
|
||||||
|
matchedWord="${BASH_REMATCH[0]}"
|
||||||
|
tempMessage="${tempMessage/"$matchedWord"/ }"
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user