Skip to content

Instantly share code, notes, and snippets.

@waggz81
Last active February 27, 2021 22:50
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save waggz81/0355b4bed02dd214b16afb032acb17df to your computer and use it in GitHub Desktop.
Save waggz81/0355b4bed02dd214b16afb032acb17df to your computer and use it in GitHub Desktop.
Valheim Server Discord Alerts
#!/bin/bash
##########################
# Change these variables #
##########################
FILE="/path/to/console.log"
WEBHOOK_URL="CHANGEME"
STEAMKEY="CHANGEME"
##########################
### script logic ###
# array to store steam ids and map to player names
declare -A SteamID
# initialize global variables
lastSteamID=0
havePlayerName=0
# begin watching the log file and check each new line
tail -Fn0 $FILE | \
while read -r line ; do
# check for the matching lines we want to look into further
if grep -iq "Closing socket\|Got handshake from client\|Got character ZDOID from" <<< "$line"
then
# tokenize the line sp;it by colons so easier to process
IFS=':' tokens=( $line )
# we're only interested in the text of the log entry
entry=$(echo "${tokens[3]}" | xargs)
# get rid of windows carriage return
entry="$(echo $entry | sed 's/\r//g')"
if grep -iq "Got handshake from client" <<< "$entry"
then
# grab everything in the entry past the matching text and mark that we have a recent steam id
lastSteamID=$(sed -n -e 's/^.*Got handshake from client //p' <<< "$entry")
haveSteamID=1
elif grep -iq "Got character ZDOID from" <<< "$entry"
then
# grab everything in the entry past the matching text and mark that we have a recent player name
playerName=$(sed -n -e 's/^.*Got character ZDOID from //p' <<< "$entry")
havePlayerName=1
# add the name and id to the array for recall later
SteamID[$lastSteamID]=$playerName
# if we have both a recent name and recent steam id go ahead and assume we have a new client that connected and send alert
if [[ havePlayerName -eq 1 && haveSteamID -eq 1 ]]
then
# grab the steam id profile url and name
response=$(curl -Gs -d "steamids=${lastSteamID}" -d "key=${STEAMKEY}" https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/)
profile=$(jq '.response.players[0].profileurl' <<< $response | tr -d "\"")
personaname=$(jq '.response.players[0].personaname' <<< $response | tr -d "\"")
# output to terminal
echo "Player ${playerName} (probably ${lastSteamID}) has joined the server - Steam profile ${personaname} (${profile})"
# send to Discord
curl -X POST --data "{\"content\": \"Player ${playerName} (probably ${lastSteamID}) has joined the server - Steam profile ${personaname} (${profile})\"}" --header "Content-Type:application/json" "$WEBHOOK_URL"
# reset variables so we're not sending alerts without it being a new connection
havePlayerName=0
haveSteamID=0
fi
elif grep -iq "Closing socket" <<< "$entry"
then
# grab everything in the entry past the matching text
socket=$(sed -n -e 's/^.*Closing socket //p' <<< "$entry")
# check that it's an actual socket closing
if [ "$socket" != "0" ]
then
# output to terminal
echo "SteamID ${socket} left the server (was probably ${SteamID[$socket]})"
# send to Discord
curl -X POST --data "{\"content\": \"SteamID ${socket} left the server (was probably ${SteamID[$socket]})\"}" --header "Content-Type:application/json" "$WEBHOOK_URL"
fi
else
# this should never happen but is included for failover
echo "unhandled entry:" "$entry"
fi
fi
done
@waggz81
Copy link
Author

waggz81 commented Feb 16, 2021

This script monitors a Valheim server log (in my case generated by linuxgsm, but could be saved via redirection when starting the server in normal scenarios) and triggers a Discord webhook when someone joins or leaves. It's not exactly accurate at all times because there are no log entries on join or part that match, so we have to guess using the most recent steamid handshake with the player name, then remember it for later when the steamid closes the socket and we can guess who it was.

I found there were player connection refreshes without new handshakes so I made it not report unless both were collected and then clearing those values after a discord alert is sent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment