#!/bin/sh # Filename: vooblystats.sh # License: CC-BY-SA 4.0 # Author: bgstack15@gmail.com # Startdate: 2020-01-24 08:57:56 # Title: # Purpose: # Package: # History: # Usage: # Reference: ftemplate.sh 2019-05-02a ; framework.sh 2018-05-02a # Improve: # write a separate, no-auth-needed tool that scrapes https://www.voobly.com/games/view/Age-of-Empires-II-The-Conquerors for lobby attendance, and put it in cronjob. # add debuglev ferror stuff # Dependencies: # framework.sh fiversion="2019-05-02a" vooblystatsversion="2020-01-28a" usage() { ${PAGER:-/usr/bin/less -F} >&2 </dev/null } html_encode() { # call: safevar="$( html_encode "unsafe;string" )" # reference: # https://stackoverflow.com/questions/296536/how-to-urlencode-data-for-curl-command/10797966#10797966 ___he_input="${1}" echo "${___he_input}" | curl -Gso /dev/null -w %{url_effective} --data-urlencode @- "" | sed -E 's/..(.*).../\1/' } auth_to_voobly() { # call: auth_to_voobly "${username}" "${password}" "${cookiefile}" ___atv_username="${1}" ___atv_password="${2}" ___atv_cookiefile="${3}" # transform password to html-safe ___atv_password_safe="$( html_encode "${___atv_password}" )" make_voobly_request "https://www.voobly.com/" "https://wwww.voobly.com" "true" "${___atv_cookiefile}" "false" "" "true" 1>"${VS_TMPDIR}/auth-response1" make_voobly_request "https://www.voobly.com/login" "https://wwww.voobly.com/" "true" "${___atv_cookiefile}" "false" "" "true" 1>"${VS_TMPDIR}/auth-response2" make_voobly_request "https://www.voobly.com/login/auth" "https://wwww.voobly.com/login" "true" "${___atv_cookiefile}" "true" "username=${___atv_username}&password=${___atv_password_safe}" "true" 1>"${VS_TMPDIR}/auth-response3" make_voobly_request "https://www.voobly.com/welcome" "https://wwww.voobly.com/login" "true" "${___atv_cookiefile}" "false" "" "true" 1>"${VS_TMPDIR}/auth-response4" make_voobly_request "https://www.voobly.com/profile/view/123989133" "https://wwww.voobly.com/welcome" "true" "${___atv_cookiefile}" "false" "" "true" 1>"${VS_TMPDIR}/auth-response5" } parse_game_page() { # call: parse_game_page "${inputfileORurl}" "${cookiefile}" # output: to stdout, a csv that originally was planned to resemble: # GAME,gameid,dateplayed,maptype,duration,playercount,gamemod # PLAYER,playerid,gameid,playercountnum,playername,playercountry,playernewrating,playerpoints,playerteam,playerciv,playerwonbool,militaryscore,economyscore,technologyscore,societyscore,totalscore,unitskilled,unitslost,buildingsrazed,buildingslost,unitsconverted,food,wood,stone,gold,tradeprofit,tributerec,tributesent,feudaltime,castletime,imptime,mapexplored,researchcount,researchpercent,totalwonders,totalcastles,reliccount,relicgold,villagerhigh ___pgp_infile="${1}" ___pgp_cf="${2}" if test "${___pgp_infile}" = "stdin" ; then fullpage="$( cat )" else # check if it contains a URL if echo "${___pgp_infile}" | grep -qiE '^https?:\/\/' ; then # need to retrieve it fullpage="$( make_voobly_request "${___pgp_infile}" "https://www.voobly.com/welcome" "true" "${___pgp_cf:-${VS_COOKIEFILE}}" "false" "" "false" )" else fullpage="$( cat "${___pgp_infile}" 2>/dev/null )" fi fi # test if valid page if ! echo "${fullpage}" | grep -qiE "economy score" ; then # if the page does not contain the expression "economy score" then it is invalid ferror "${scriptfile}: cannot parse game page ${___pgp_infile}. Skipping..." return fi # game info gameid="$( echo "${fullpage}" | _get_simple_value "Match Details" | tr -d '#' )" _reldate="$( echo "${fullpage}" | _get_simple_value "Date Played:" )" absolute_date="$( date -d "$( echo "${_reldate}" | tr -d ',-' )" -u "+%FT%TZ" )" map="$( echo "${fullpage}" | _get_simple_value "Map:" )" duration="$( echo "${fullpage}" | _get_simple_value "Duration:" )" playercount="$( echo "${fullpage}" | _get_simple_value "Players:" )" gamemod="$( echo "${fullpage}" | _get_simple_value "Game Mod:" )" gameladder="$( echo "${fullpage}" | awk '/Ladder:/' | grep -oE ">.*<" | tail -c +2 | head -c -2 )" echo "GAME,${gameid},${absolute_date},${map},${duration},${gameladder},${playercount},${gamemod}," # HEADERCSV # per player info x=0 while test $x -lt $playercount ; do x=$(( x + 1 )) _playerline="$( echo "${fullpage}" | grep -E "^$" | sed -n "${x}p" )" _playerline_num="$( echo "${fullpage}" | grep -n -E "^" | tr -dc '[0-9]' )" playername="$( echo "${_playerline}" | grep -oE "profile\/view/[0-9]{1,15}.?>.*<\/a><\/" | sed -r -e 's/<\/a>.*$//;' -e 's/.*view\/[0-9]{1,15}\">//;' )" playercountry="$( echo "${_playerline}" | grep -oE "res\/flags/[^\.]{1,20}\.(png|jpg)" | awk -F'/' '{print $NF}' | awk -F'.' '{print $1}' )" playernewrating="$( echo "${_playerline2}" | awk -F'[<>]' '{print $5}' )" playerpoints="$( echo "${_playerline2}" | awk -F'[<>]' '{print $11}' )" playerteam="$( echo "${_playerline2}" | awk -F'[<>]' '{print $17}' )" playerwinbool="$( echo "${_playerline}" | grep -qiE 'win.PNG' && echo "true" || echo "false" )" playerpremiumbool="$( echo "${_playerline}" | grep -qiE 'user-premium' && echo "true" || echo "false" )" playergamemvpbool="$( echo "${_playerline}" | grep -qiE 'high.PNG' && echo "true" || echo "false" )" playerclan="$( echo "${_playerline}" | grep -oE ".{1,50}<\/a>.*$//' -e 's/^.*\.voobly\.com>//' )" # if x > ( numplayers/2), then swap newrating and points if test "$( printf '%s;%s>(%s/2);\n' "scale=2" "${x}" "${playercount}" | bc )" = "1" ; then tempvar="${playernewrating}" playernewrating="${playerteam}" playerteam="${tempvar}" unset tempvar fi # in-game stats _playerstatslines="$( echo "${fullpage}" | grep -A4 -E "${playername}.*<\/a>(<\/b>|<\/span>)*$" | grep center )" # works on webpage game stats sections 1, 2, and 5. #echo "${_playerstatslines}" | sed -r -e 's/(<[^\>]+>)+/@/g;'| tr -d ',%' | awk -F'@' 'BEGIN{OFS="\t"} {print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10}' _playerstatslines2="$( echo "${_playerstatslines}" | sed -r -e 's/(<[^\>]+>)+/@/g;' | tr -d ',%' | awk -F'@' '{print $1,$2,$3,$4,$5,$6,$7,$8,$9,$10}' )" _playerstatslines3="$( echo "${_playerstatslines2}" | tr -d '\n' | sed -r -e 's/\s+/ /g;' )" playerciv="$( echo "${fullpage}" | grep -B4 -E "${playername}.*<\/a>(<\/b>|<\/span>)*$" | head -n1 | sed -r -e 's/(<[^\>]+>)+//g;' )" playercolorhex="$( echo "${fullpage}" | grep -B2 -E "${playername}.*<\/a>(<\/b>|<\/span>)*$" | head -n1 | tr -d ';' | awk '{print $3}' )" # 0054A6 00A651 00FFFF 92278F C0C0C0 FF0000 FF8000 FFFF00 playercolorname="nocolor" case "${playercolorhex###}" in 0054A6) playercolorname="blue" ;; FF0000) playercolorname="red" ;; FFFF00) playercolorname="yellow" ;; 00A651) playercolorname="green" ;; 00FFFF) playercolorname="cyan" ;; 92278F) playercolorname="purple" ;; C0C0C0) playercolorname="gray" ;; FF8000) playercolorname="orange" ;; esac # and because bash read is broken on devuan for an uknown reason... we have to use awk milscore="$( echo "${_playerstatslines3}" | awk '{print $1}' )" ecoscore="$( echo "${_playerstatslines3}" | awk '{print $2}' )" techscore="$( echo "${_playerstatslines3}" | awk '{print $3}' )" socscore="$( echo "${_playerstatslines3}" | awk '{print $4}' )" totscore="$( echo "${_playerstatslines3}" | awk '{print $5}' )" unitskilled="$( echo "${_playerstatslines3}" | awk '{print $6}' )" unitslost="$( echo "${_playerstatslines3}" | awk '{print $7}' )" buildingsrazed="$( echo "${_playerstatslines3}" | awk '{print $8}' )" buildingslost="$( echo "${_playerstatslines3}" | awk '{print $9}' )" unitsconverted="$( echo "${_playerstatslines3}" | awk '{print $10}' )" food="$( echo "${_playerstatslines3}" | awk '{print $11}' )" wood="$( echo "${_playerstatslines3}" | awk '{print $12}' )" stone="$( echo "${_playerstatslines3}" | awk '{print $13}' )" gold="$( echo "${_playerstatslines3}" | awk '{print $14}' )" tradeprofit="$( echo "${_playerstatslines3}" | awk '{print $15}' )" tributerec="$( echo "${_playerstatslines3}" | awk '{print $16}' )" tributesent="$( echo "${_playerstatslines3}" | awk '{print $17}' )" feudaltime="$( echo "${_playerstatslines3}" | awk '{print $18}' )" castletime="$( echo "${_playerstatslines3}" | awk '{print $19}' )" imptime="$( echo "${_playerstatslines3}" | awk '{print $20}' )" mapexplored="$( echo "${_playerstatslines3}" | awk '{print $21}' )" researchcount="$( echo "${_playerstatslines3}" | awk '{print $22}' )" researchpercent="$( echo "${_playerstatslines3}" | awk '{print $23}' )" totalwonders="$( echo "${_playerstatslines3}" | awk '{print $24}' )" totalcastles="$( echo "${_playerstatslines3}" | awk '{print $25}' )" reliccount="$( echo "${_playerstatslines3}" | awk '{print $26}' )" relicgold="$( echo "${_playerstatslines3}" | awk '{print $27}' )" villagerhigh="$( echo "${_playerstatslines3}" | awk '{print $28}' )" # end of player loop echo "PLAYER,${playerid},${gameid},${x},${playername},${playercolorhex},${playercolorname},${playercountry},${playerpremiumbool},${playerclan},${playernewrating},${playerpoints},${playerteam},${playerciv},${playerwinbool},${milscore},${ecoscore},${techscore},${socscore},${totscore},${unitskilled},${unitslost},${buildingsrazed},${buildingslost},${unitsconverted},${food},${wood},${stone},${gold},${tradeprofit},${tributerec},${tributesent},${feudaltime},${castletime},${imptime},${mapexplored},${researchcount},${researchpercent},${totalwonders},${totalcastles},${reliccount},${relicgold},${villagerhigh}," # HEADERCSV unset -v playerclan _playerline _playerline2 _playerline_num playerid playername playercountry playernewrating playerpoints done } # end of parse_game_page parse_profile_matchlist_page() { # call: parse_profile_matchlist_page "${inputfileORurl}" "${cookiefile}" # output: delimited list of match numbers found on the profile page. # reference: parse_game_page ___ppmp_infile="${1}" ___ppmp_cf="${2}" if test "${___ppmp_infile}" = "stdin" ; then fullpage="$( cat )" else # check if it contains a URL if echo "${___ppmp_infile}" | grep -qiE '^https?:\/\/' ; then # need to retrieve it fullpage="$( make_voobly_request "${___ppmp_infile}" "https://www.voobly.com/welcome" "true" "${___ppmp_cf:-${VS_COOKIEFILE}}" "false" "" "false" )" else fullpage="$( cat "${___ppmp_infile}" 2>/dev/null )" fi fi # test if valid page results="$( echo "${fullpage}" | grep -oE "match\/view\/[0-9]+" | awk -F'/' '!x[$3]++ {print $3}' )" if test "$( echo "${results}" | wc -l | tr -dc '[0-9]' )" -gt 0 ; then echo "${results}" else ferror "${scriptfile}: cannot parse matchlist page ${___ppmp_infile}. Skipping..." echo "invalid_matchlist_page" fi } # end parse_profile_matchlist_page get_match_url() { # call: get_match_url "${matchid}" ___gmu_gameid="${1}" ___gmu_gameurl="https://www.voobly.com/match/view/$( echo "${___gmu_gameid}" | grep -oE "match\/view\/[0-9]+" | awk -F'/' '{print $3}' )" echo "${___gmu_gameurl}" } get_profile_matchlist_url() { # call: get_profile_matchlist_url "123456789" "1" for page 1 # call: get_profile_matchlist_url "https://www.voobly.com/profile/view/123989133/" # call: get_profile_matchlist_url "https://www.voobly.com/profile/view/123989133/" "1" # output: url of page that lists matches, including a page number if requested. # we will use a cool shortcut i found, https://www.voobly.com/games/matches/user/123989133/0/0#pagebrowser1 ___gpmu_url="${1}" ___gpmu_pagenum="${2}" # if it is already a full page with a pagenumber, just return it if echo "${___gpmu_url}" | grep -qiE "\/0\/[0-9]+#pagebrowser" ; then : # make no changes to it else if echo "${___gpmu_url}" | grep -qiE "voobly.com" ; then # derive userid ___gpmu_url="$( echo "${___gpmu_url}" | grep -oE "(view|user)\/[0-9]+" | awk -F'/' '{print $2}' )" fi ___gpmu_url="https://www.voobly.com/games/matches/user/${___gpmu_url}" # https://www.voobly.com/games/matches/user/123989133/0/0#pagebrowser1 # https://www.voobly.com/games/matches/user/123989133/0/1#pagebrowser1 if test -n "${___gpmu_pagenum}" ; then ___gpmu_url="${___gpmu_url}/0/${___gpmu_pagenum}#pagebrowser1" fi fi echo "${___gpmu_url}" } get_user_matchlist() { # call: get_user_matchlist "useridORuserurl" "${mingamecount}" # output: on stdout, a list of gameid numbers. ___gum_userurl="${1}" ___gum_min="${2}" ; test -z "${___gum_min}" && ___gum_min=10 debuglev 9 && ferror "DEBUG9: get_user_matchlist $*" if echo "${___gum_userurl}" | grep -qoE "^[0-9]{1,15}$" ; then # just userid, so make url ___gum_userurl="https://www.voobly.com/profile/view/${___gum_userurl}/" fi results="" # start going through games, starting on page 1 x=0 ___gum_continue=0 ___gum_previous_count=-1 ___gum_count=0 while test ${___gum_continue} -eq 0 ; do x=$(( x + 1 )) # get game page loopresults="$( parse_profile_matchlist_page "$( get_profile_matchlist_url "${___gum_userurl}" "${x}" )" "${VS_COOKIEFILE}" )" debuglev 2 && ferror "DEBUG2: get_user_matchlist: $( echo "${loopresults}" | xargs )" results="${results} ${loopresults}" ___gum_count="$( echo "${results}" | wc -w )" test ${___gum_count} -ge ${___gum_min} && ___gum_continue=1 # if results have not increased, then we need to stop looping because we are not pulling any more gameids test ${___gum_previous_count} -ge ${___gum_count} && ___gum_continue=1 ___gum_previous_count="${___gum_count}" done # assume we are good now, where count >= min echo "${results}" | xargs -n1 } get_userid() { # call: get_userid "gimli" "${cookiefile}" # cookiefile is absolutely necessary for this function! # output to stdout: "123456789" ___gu_username="${1}" ___gu_cf="${2}" ; test -z "${___gu_cf}" && ___gu_cf="${VS_COOKIEFILE}" # if the input is already a userid, just return it if echo "${___gu_username}" | grep -qiE "^[0-9]+$" ; then echo "${___gu_username}" elif echo "${___gu_username}" | grep -qiE "profile\/view\/[0-9]+" ; then response="$( echo "${___gu_username}" | grep -oE 'profile\/view\/[0-9]+' | cut -d'/' -f3 | head -n1 )" echo "${response}" else # assume it is just a regular name to parse # we need to pass the sessionid in the post data, or else we get an error, even though it is in the cookie. sessionid="$( awk '/vbly_session1/{print $NF}' "${___gu_cf}" )" response="$( make_voobly_request "https://www.voobly.com/friends/browse/Search/Search" "https://www.voobly.com/Welcome" "true" "${VS_COOKIEFILE}" "true" "query=${___gu_username}&session1=${sessionid}" )" # the response will not be a real page; it will just be a redirect notice but it is good enough to get the userid if echo "${response}" | grep -qiE "no results found" ; then ferror "get_userid: unable to find a userid for ${___gu_username}" echo "invalid_userid" else response="$( echo "${response}" | grep -oE 'profile\/view\/[0-9]+' | cut -d'/' -f3 | head -n1 )" echo "${response}" fi fi } get_game_page() { # call: get_game_page "123456789" "${cookiefile}" | parse_game_page stdin # output to stdout: the html of the game page. ___ggp_number="${1}" ___ggp_cf="${2}" # do some validation to make sure this is a valid game number ___ggp_number_orig="${___ggp_number}" if echo "${___ggp_number}" | grep -qE "voobly\.com" ; then # somebody passed a whole URL, so let us split it ___ggp_number="${___ggp_number##*view/}" fi ___ggp_number="$( echo "${___ggp_number}" | grep -oE '^[0-9]+' )" debuglev 1 && ferror "Trying to read game ${___ggp_number}" response="$( make_voobly_request "https://www.voobly.com/match/view/${___ggp_number}" "https://www.voobly.com/" "true" "${____ggp_cf}" "no" "" "false" )" if echo "${response}" | grep -qiE "economy score" ; then # is valid game page echo "${response}" else ferror "${scriptfile}: ERROR! Invalid game page request: ${___ggp_number_orig}. Used match number ${___ggp_number}. Skipping..." echo "invalid_page_request" fi } _get_simple_value() { # call: echo "${fullpage}" | _get_simple_value "Match Details" grep -A1 "${1}<\/" | tail -n1 | awk -F'[<>]' '{print $3}' } display_csv_headers() { # output csv headers. #echo "GAME,gameid,absolute_date,map,duration,playercount,gamemod," #echo "PLAYER,playerid,gameid,x,playername,playercolorhex,playercolorname,playercountry,playerpremiumbool,playerclan,playernewrating,playerpoints,playerteam,playerciv,playerwinbool,milscore,ecoscore,techscore,socscore,totscore,unitskilled,unitslost,buildingsrazed,buildingslost,unitsconverted,food,wood,stone,gold,tradeprofit,tributerec,tributesent,feudaltime,castletime,imptime,mapexplored,researchcount,researchpercent,totalwonders,totalcastles,reliccount,relicgold,villagerhigh," sed -n -r -e '/HEADERCSV$/{ s/^.*echo "//;s/"\s*# HEADERCSV$//;s/\$\{//g;s/\}//g;p}' "${scriptfile}" } parse_matches_for_user() { # call: parse_matches_for_user "gimli" "10" "${cookiefile}" ___pmfu_username="${1}" ___pmfu_min="${2}" ___pmfu_cf="${3}" for word in $( get_user_matchlist "$( get_userid "${___pmfu_username}" )" "${___pmfu_min}" ) ; do debuglev 1 && ferror "DEBUG1: parse_matches_for_user: found gameid ${word}" get_game_page "${word}" "${___pmfu_cf}" | parse_game_page stdin done } cat_matchlist() { # call: cat_matchlist "gimli,frodo,bilbo" "${userlistdelimiter}" "${minimum}" "${cookiefile}" # call: echo "12345982 2985817 398282847" | cat_matchlist "stdin" ___cm_userlist="${1}" ___cm_delim="${2}" ___cm_min="${3}" ___cm_cf="${4}" if echo "${___cm_userlist}" | grep -qiE "^stdin$" ; then awk '!x[$0]++' else # parse list of users, and make a master list of unique entries results="$( { for word in $( echo "${___cm_userlist}" | tr "${___cm_delim}" '\n' ) ; do get_user_matchlist "$( get_userid "${word}" )" "${___cm_min}" done } | awk '!x[$0]++' )" # for sorting them. -n will go lowest to highest. results="$( echo "${results}" | sort -n )" echo "${results}" fi } # DEFINE TRAPS clean_vooblystats() { # use at end of entire script if you need to clean up tmpfiles # rm -f "${tmpfile1}" "${tmpfile2}" 2>/dev/null # Delayed cleanup if test -z "${VS_NO_CLEAN}" ; then nohup /bin/bash </dev/null 2>&1 & sleep "${VS_CLEANUP_SEC:-300}" ; /bin/rm -r "${VS_TMPDIR:-NOTHINGTODELETE}" 1>/dev/null 2>&1 ; EOF fi } CTRLC() { # use with: trap "CTRLC" 2 # useful for controlling the ctrl+c keystroke : } CTRLZ() { # use with: trap "CTRLZ" 18 # useful for controlling the ctrl+z keystroke : } parseFlag() { flag="$1" hasval=0 case ${flag} in # INSERT FLAGS HERE "d" | "debug" | "DEBUG" | "dd" ) setdebug ; ferror "debug level ${debug}" ; __debug_set_by_param=1 ;; "u" | "usage" | "help" | "h" ) usage ; exit 1 ;; "V" | "fcheck" | "version" ) ferror "${scriptfile} version ${vooblystatsversion}" ; exit 1 ;; #"i" | "infile" | "inputfile" ) getval ; infile1=${tempval} ;; "c" | "conf" | "conffile" | "config" ) getval ; conffile="${tempval}" ;; esac debuglev 10 && { test ${hasval} -eq 1 && ferror "flag: ${flag} = ${tempval}" || ferror "flag: ${flag}" ; } } # DETERMINE LOCATION OF FRAMEWORK f_needed=20181030 ___frameworkpath="$( find $( echo "${FRAMEWORKPATH}" | tr ':' ' ' ) -maxdepth 1 -mindepth 0 -name 'framework.sh' 2>/dev/null )" while read flocation ; do if test -e ${flocation} ; then __thisfver="$( sh ${flocation} --fcheck 2>/dev/null )" ; if test ${__thisfver:-0} -ge ${f_needed} ; then frameworkscript="${flocation}" ; break ; elif test -n "${___thisfver}" ; then printf "Obsolete: %s %s\n" "${flocation}" "${__thisfver}" 1>&2 ; fi ; fi ; done <&2 && exit 4 # INITIALIZE VARIABLES # variables set in framework: # today server thistty scriptdir scriptfile scripttrim # is_cronjob stdin_piped stdout_piped stderr_piped sendsh sendopts . ${frameworkscript} || echo "$0: framework did not run properly. Continuing..." 1>&2 infile1= outfile1= logfile=${scriptdir}/${scripttrim}.${today}.out define_if_new interestedparties "bgstack15@gmail.com" # SIMPLECONF define_if_new default_conffile "./vooblystats.conf" #define_if_new defuser_conffile ~/.config/vooblystats/vooblystats.conf define_if_new VS_TMPDIR "$( mktemp -d )" define_if_new VS_COOKIEFILE "./cookies" #tmpfile1="$( TMPDIR="${VS_TMPDIR}" mktemp )" #tmpfile2="$( TMPDIR="${VS_TMPDIR}" mktemp )" # REACT TO OPERATING SYSTEM TYPE case $( uname -s ) in Linux) : ;; FreeBSD) : ;; *) echo "${scriptfile}: 3. Indeterminate OS: $( uname -s )" 1>&2 && exit 3 ;; esac ## REACT TO ROOT STATUS #case ${is_root} in # 1) # proper root # : ;; # sudo) # sudo to root # : ;; # "") # not root at all # #ferror "${scriptfile}: 5. Please run as root or sudo. Aborted." # #exit 5 # : # ;; #esac # SET CUSTOM SCRIPT AND VALUES #setval 1 sendsh sendopts<&2 } # MAIN LOOP #{ # Initial authentication and download a random game page rm -f "${VS_COOKIEFILE}" # need to auth at least one for any other voobly function to work correctly. auth_to_voobly "${VS_USERNAME}" "${VS_PASSWORD}" "${VS_COOKIEFILE}" #get_game_page "${opt1}" "${VS_COOKIEFILE}" | parse_game_page stdin #parse_matches_for_user "frodo" "300" "${VS_COOKIEFILE}" display_csv_headers #get_user_matchlist "123989133" "150" { for word in $( cat_matchlist "${VS_MATCHLIST:-frodo,bilbo,merry,pippin}" "," "300" "${VS_COOKIEFILE}" ) do get_game_page "${word}" | parse_game_page stdin done } | sort -k1,3 #} | tee -a ${logfile} # EMAIL LOGFILE #${sendsh} ${sendopts} "${server} ${scriptfile} out" ${logfile} ${interestedparties} ## STOP THE READ CONFIG FILE #return_code 0 #fi ; done ; }