From 86a640ebe89180d5a42d87d1a52ef7ca4c6f771a Mon Sep 17 00:00:00 2001 From: Storm Dragon Date: Thu, 13 Nov 2025 14:57:45 -0500 Subject: [PATCH] Attempt to cut down on spam from greet and bye triggers. Add ping/pong to help deal with flakey connections.? --- bot.cfg.example | 6 +++ bot.sh | 95 ++++++++++++++++++++++++++++++++++++++++- triggers/bye/bye.sh | 22 +++++++++- triggers/greet/greet.sh | 22 +++++++++- 4 files changed, 141 insertions(+), 4 deletions(-) diff --git a/bot.cfg.example b/bot.cfg.example index 6dc1160..d729513 100644 --- a/bot.cfg.example +++ b/bot.cfg.example @@ -30,3 +30,9 @@ botCaller=",.!+?" # People who are allowed to private message the bot separate multiple names with | allowList="" ignoreList="storm_bot" +# Interval in seconds to send PING to server for connection health check (default: 120) +# Set to 0 to disable proactive ping checks (not recommended) +pingInterval=120 +# Timeout in seconds to wait for PONG response before considering connection dead (default: 30) +# If no PONG received within this time after sending PING, bot will reconnect +pongTimeout=30 diff --git a/bot.sh b/bot.sh index 7f5278a..8965b03 100755 --- a/bot.sh +++ b/bot.sh @@ -78,6 +78,10 @@ export nick export quitMessage export intentionalExit +# Set default values for ping/pong monitoring if not configured +pingInterval="${pingInterval:-120}" +pongTimeout="${pongTimeout:-30}" + # Check for critical dependencies needed by the bot core coreDependencies=("socat" "tail" "shuf" "grep" "sed" "tr" "cut" "date") missingCore=() @@ -105,6 +109,74 @@ rm_input() { fi } +# Function to stop ping monitor process +stop_ping_monitor() { + if [[ -n "$pingMonitorPid" ]] && kill -0 "$pingMonitorPid" 2>/dev/null; then + kill "$pingMonitorPid" 2>/dev/null + wait "$pingMonitorPid" 2>/dev/null + fi + # Clean up ping/pong tracking files + rm -f "${input}.lastping" "${input}.lastpong" 2>/dev/null +} + +# Start background ping monitor process +# Sends PING at regular intervals and monitors for PONG responses +start_ping_monitor() { + # Only start if ping interval is greater than 0 + if [[ "$pingInterval" -le 0 ]]; then + return + fi + + # Stop any existing monitor + stop_ping_monitor + + # Capture variables for the background process + local monitorInput="$input" + local monitorLog="$log" + local monitorServer="$server" + local monitorPort="$port" + local monitorDateFormat="$dateFormat" + local monitorPingInterval="$pingInterval" + local monitorPongTimeout="$pongTimeout" + + # Start ping monitor in background + ( + while true; do + sleep "$monitorPingInterval" + # Send PING with timestamp + currentTime=$(date +%s) + echo "PING :healthcheck_${currentTime}" >> "$monitorInput" + echo "$currentTime" > "${monitorInput}.lastping" + + # Wait for pong timeout period and check if we got a response + sleep "$monitorPongTimeout" + + # Check if we received a PONG + if [[ -f "${monitorInput}.lastpong" ]]; then + lastPongTime=$(cat "${monitorInput}.lastpong") + # If last pong is newer than last ping, we're good + if [[ "$lastPongTime" -ge "$currentTime" ]]; then + continue + fi + fi + + # No PONG received within timeout - connection is likely dead + echo "No PONG received within ${monitorPongTimeout}s. Connection appears dead. [$(date "+$monitorDateFormat")]" >> "$monitorLog" + # Kill the socat process to trigger reconnection + # Escape regex metacharacters in server and port to prevent injection + safeServer="${monitorServer//./\\.}" + safeServer="${safeServer//[/\\[}" + safeServer="${safeServer//]/\\]}" + safeServer="${safeServer//\*/\\*}" + safePort="${monitorPort//[^0-9]/}" + # Find and kill the socat process for this bot + pkill -f "socat.*${safeServer}:${safePort}" 2>/dev/null + exit 0 + done + ) & + pingMonitorPid=$! +} + # Function to trim log file to prevent unbounded growth # Keeps log between 800-1000 lines by trimming oldest entries when limit reached trim_log() { @@ -127,8 +199,12 @@ trim_log() { fi } -# Trap exiting ffrom the program to remove the temporary input file. -trap rm_input EXIT +# Trap exiting from the program to remove the temporary input file and stop ping monitor +cleanup_on_exit() { + stop_ping_monitor + rm_input +} +trap cleanup_on_exit EXIT # Flag to track intentional shutdown (set by exit module) intentionalExit=false @@ -159,6 +235,9 @@ while true; do # Counter for log trimming (check every 50 messages) logTrimCounter=0 + # Start ping monitor for this connection + start_ping_monitor + tail -f "$input" | socat -,ignoreeof "$socatAddress" | while read -r result ; do # Strip carriage return from IRC protocol (CRLF line endings) result="${result%$'\r'}" @@ -190,6 +269,15 @@ while true; do PING*) echo "${result/I/O}" >> "$input" ;; + # track pong responses for connection health monitoring + # Match PONG as IRC command, not user messages containing "pong" + *" PONG "*|PONG*) + # Only process if NOT a user PRIVMSG (prevents spoofing) + if [[ ! "$result" =~ PRIVMSG ]]; then + # Update last pong timestamp + date +%s > "${input}.lastpong" + fi + ;; # for pings on nick/user *"You have not"*) for channelName in "${channels[@]}"; do @@ -338,6 +426,9 @@ while true; do done # If we reach here, the connection was dropped + # Stop the ping monitor for this connection + stop_ping_monitor + # Check if this was an intentional exit if [[ "$intentionalExit" == "true" ]]; then echo "Bot shutdown requested. Exiting. [$(date "+$dateFormat")]" | tee -a "$log" diff --git a/triggers/bye/bye.sh b/triggers/bye/bye.sh index cbf6935..ace9fec 100755 --- a/triggers/bye/bye.sh +++ b/triggers/bye/bye.sh @@ -3,8 +3,28 @@ farewellsFile="triggers/bye/farewells.txt" +# Time tracking to prevent spam from flaky connections +# Track last bye time for each user (in seconds since epoch) +declare -A lastByeTime + +# Only say bye if user hasn't been farewelled in the last hour (3600 seconds) +currentTime=$(date +%s) +userName="${1,,}" +cooldownPeriod=3600 + +if [[ -n "${lastByeTime[$userName]}" ]]; then + timeSinceLastBye=$((currentTime - lastByeTime[$userName])) + if [[ $timeSinceLastBye -lt $cooldownPeriod ]]; then + # User was farewelled recently, skip bye message + exit 0 + fi +fi + +# Update last bye time for this user +lastByeTime[$userName]=$currentTime + # All names to match are completely lowercase. -case "${1,,}" in +case "$userName" in storm_dragon) msg "${2%% :*}" "NOOOOOOOOOO!!! $1: come back!!!" ;; diff --git a/triggers/greet/greet.sh b/triggers/greet/greet.sh index f2da742..a0d86f5 100755 --- a/triggers/greet/greet.sh +++ b/triggers/greet/greet.sh @@ -3,8 +3,28 @@ greetingsFile="triggers/greet/greetings.txt" +# Time tracking to prevent spam from flaky connections +# Track last greet time for each user (in seconds since epoch) +declare -A lastGreetTime + +# Only greet if user hasn't been greeted in the last hour (3600 seconds) +currentTime=$(date +%s) +userName="${1,,}" +cooldownPeriod=3600 + +if [[ -n "${lastGreetTime[$userName]}" ]]; then + timeSinceLastGreet=$((currentTime - lastGreetTime[$userName])) + if [[ $timeSinceLastGreet -lt $cooldownPeriod ]]; then + # User was greeted recently, skip greeting + exit 0 + fi +fi + +# Update last greet time for this user +lastGreetTime[$userName]=$currentTime + # All names to match are completely lowercase. -case "${1,,}" in +case "$userName" in storm_dragon) msg "$2" "my lord, $1: how may I serve you?" ;;