#! /bin/bash readonly PROCESS_EVENT_SOURCED=1 # This function reads tags of xml in the same way read normally reads lines. function rdom { local IFS=\> read -r -d \< element content } function format_cgit_title { if [[ "${url_to_get}" =~ /git.parabola.nu/([^/]+\.git)/tree/ ]] then repo="${BASH_REMATCH[1]}" title=$(sed 's|\\.* - '${repo}'| - '${repo}'|' <<< ${title}) fi } function tell_fact # (thing , channel) { thing="${1}" declare -l lower_case_thing="${thing}" channel="${2}" if [[ -s "info/${lower_case_thing}" ]] then while read line do send_msg "${channel}" "${thing} is: ${line}" done < <(cat "info/${lower_case_thing}") return 0 else return 1 fi } source common_codez function remember_fact { if (( $(wc -l "info/${thing}" 2> /dev/null | cut -d ' ' -f 1) + 0 >= 3 )) then send_msg "${channel_it_came_from}" "${personoslash}: Sorry, my pea-brain can not remember so many facts about any one topic." elif (( $(wc -c <<<${is}) + 0 >= 512 )) then send_msg "${channel_it_came_from}" "${personoslash}: Sorry, my pea-brain can not remember such a detailed fact. Could you try \"dumbing it down\" for me?" elif grep "^${is}$" "info/${thing}" &>/dev/null then send_msg "${channel_it_came_from}" "${personoslash}: Thanks, but I knew that already." else echo "${is}" >> "info/${thing}" send_msg "${channel_it_came_from}" "${personoslash}: OK, I will remember that." fi } function forget_fact { number_of_matching_lines=$(grep -c "^${isnt}" "info/${thing}") case "${number_of_matching_lines}" in 0 ) send_msg "${channel_it_came_from}" "${personoslash}: I know." ;; 1 ) grep -v "^${isnt}" "info/${thing}" | sponge "info/${thing}" send_msg "${channel_it_came_from}" "${personoslash}: OK, entry removed." ;; * ) send_msg "${channel_it_came_from}" "${personoslash}: Ambiguos." ;; esac } function process_event { my_own_name='pbot' person="${sender%%!*}" # Remove any forward slashes. personoslash="${person//\/}" declare -l personoslashlower="${personoslash}" channel_it_came_from="$( echo ${line} | cut -d ' ' -f 3 )" # If it's a private message [[ "${channel_it_came_from}" == "${my_own_name}" ]] && channel_it_came_from="${personoslash}" # is this am injected message [[ ${personoslash} == ${INJECT_NICK} ]] && is_injected_msg=1 || is_injected_msg=0 ###################### # Echo injected data # ###################### # Should send a message like: echo 'This is the message' > /tmp/un-provoked-message-store line_filtered=${line##*PRIVMSG +([![:space:]]) :} [[ "${line_filtered}" != 'Parabola Community Forum' ]] && \ (( ${is_injected_msg} )) && send_msg "${channel_it_came_from}" "${line_filtered}" # NOTE: 'is_injected_msg' is the IPC mechanism # used by ./pbot-say and the ./labs_change_detector script # see ./labs_change_detector, ./pbot-say , ./transport_plus_ipc.sh.inc # the 'send_msg' command above was originally mutually exclusive # with the remaining entirety of this function # and it was the only place where the 'injected_data' var was set # ('injected_data' is now 'is_injected_msg') # yet, that is tested for below in the 'Page title getter' section # so, presumably this 'injected_data' was intended to fall through? # or perhaps not, because the "Shared libraries error" matcher # does not filter out these messages, which it should # supressing furthur handling of such messages for now (( ${is_injected_msg} )) && return ############################################################### # This is a message from a user. Make preperations to process # ############################################################### the_time_now=$(date +%s) # Make this person a folder if they don't already have one. if ! [[ -d "announcements/people/${personoslashlower}" ]] then mkdir -p "announcements/people/${personoslashlower}" fi # Record that the person has been seen, and when. touch "announcements/people/${personoslashlower}/seen" shopt -s extglob # We want to get only the message part of the line sentence="${line#* }" sentence="${sentence#* }" sentence="${sentence#* :}" ########################## # Shared libraries error # ########################## # If someone complains about error while loading shared libraries error # then recomend them to the bug tracker. if [[ ${sentence} == *"error while loading shared libraries"* ]] && (( ! $is_injected_msg )) then # Make sure they have not already been recommended to the bug # tracker less than one day ago. sharlibsrecfile="announcements/people/${personoslashlower}/shared_libs" # Have we recommended them to the bug tracker before? if [[ -f ${sharlibsrecfile} ]] then last_time=$( stat -c %Y ${sharlibsrecfile} ) (( last_time > the_time_now - 86400 )) && rec_recent=1 || rec_recent=0 else rec_recent=0 fi if ! (( rec_recent )) then send_msg "${channel_it_came_from}" "${person}: please report a bug, specifying the exact error message, the name of the program, and your architecture: https://labs.parabola.nu/projects/issue-tracker/issues?set_filter=1&tracker_id=1" touch "${sharlibsrecfile}" fi fi ########## # Repeat # ########## [[ ${sentence} != ${i_repeated} ]] && say_again=yesyes # If two different people say the same thing in a row then say it again for fun; # unless it was directed at me. if [[ ${sentence} == ${lastline} ]] && [[ ${person} != ${lastsender} ]] && \ [[ ${say_again} != nono ]] && [[ ${sentence} != "${my_own_name}: "* ]] then send_msg "${channel_it_came_from}" "${sentence}" i_repeated="${sentence}" say_again=nono fi lastline="${sentence}" lastsender="${person}" ################# # Announcements # ################# # If someone has sent this person a message then echo it to them. if [[ -f "announcements/people/${personoslashlower}/messages" ]] then uniq "announcements/people/${personoslashlower}/messages" | while read line do # The first field is the time, in *nix seconds, that # the message was sent. The second is the name of the # sender. And the rest is the message. intermediate="${line#* }" sender_u="${intermediate%% *}" message_u="${intermediate#* }" time_sent="${line%% *}" seconds_ago_seen="$(( the_time_now - time_sent ))" minutes_ago_seen="$(( ( the_time_now - time_sent ) / 60 ))" hours_ago_seen="$(( ( the_time_now - time_sent ) / 3600 ))" days_ago_seen="$(( ( the_time_now - time_sent ) / 86400 ))" months_ago_seen="$(( ( the_time_now - time_sent ) / 2592000 ))" years_ago_seen="$(( ( the_time_now - time_sent ) / 31104000 ))" if (( seconds_ago_seen < 120 )) then units="${seconds_ago_seen} seconds" elif (( minutes_ago_seen < 120 )) then units="${minutes_ago_seen} minutes" elif (( hours_ago_seen < 48 )) then units="${hours_ago_seen} hours" elif (( days_ago_seen < 60 )) then units="${days_ago_seen} days" elif (( months_ago_seen < 24 )) then units="${months_ago_seen} months" else units="${years_ago_seen} years" fi send_msg "${channel_it_came_from}" "${personoslash}: ${sender_u} told me to tell you, (${units} ago): ${message_u}" done rm "announcements/people/${personoslashlower}/messages" rm "announcements/people/${personoslashlower}/you_have_mail" 2> /dev/null || true fi ##################### # Page title getter # ##################### # We don't want to get the page title if it's injected data # nor if it probably came from a spambot if [[ ${line} =~ http://[^\ ]+ ]] || [[ ${line} =~ https://[^\ ]+ ]] && (( ! $is_injected_msg )) then url_to_get="${BASH_REMATCH}" the_title=$( curl -L --compressed --max-time 5 "${url_to_get}" 2> /dev/null | while rdom do if [[ ${element} = title ]] || [[ ${element} = TITLE ]] then title="${content}" [[ "${url_to_get}" =~ /git.parabola.nu/ ]] && format_cgit_title sed 's/ / /g' <<< "${title}" | replace_wierd_html_chars fi done ) [[ -n "${the_title}" ]] && send_msg "${channel_it_came_from}" "Page title: '${the_title}'" fi ###################### # main chat triggers # ###################### case "${sentence}" in ######### # hello # ######### [Hh]@('ello'|'i'|'ey')*([[:punct:]]) ) [[ "${personoslashlower}" != 'parabola-user' ]] && \ [[ ! -f "announcements/people/${personoslashlower}/greeted" ]] && \ touch "announcements/people/${personoslashlower}/greeted" && \ send_msg "${channel_it_came_from}" "Hello ${person}! Is something on your mind? Feel free to comment or ask a question at any time. Be patient if no one responds immediately. That is quite normal on the IRC. Many people will read your message when they can, and perhaps reply, if you are still in the channel." ;; ###################### # you got that right # ###################### *" that right ${my_own_name}"* ) send_msg "${channel_it_came_from}" "You got that right, ${person}!" ;; ######## # Seen # ######## "${my_own_name}: when did you last see"* ) subject="${sentence##${my_own_name}: when did you last see }" subject="${subject##${my_own_name}: when did you last see: }" # If there's an `:', we can still handle it. subject="${subject%\?}" subject="${subject%% *}" declare -l subjectlower="${subject}" if [[ "${subject}" == ${my_own_name} ]] then send_msg "${channel_it_came_from}" "${my_own_name}? Sawry mait, oi never 'eard of 'em." elif [[ -f "announcements/people/${subjectlower}/seen" ]] then seconds_ago_seen="$(( the_time_now - $( stat -c %Y announcements/people/${subjectlower}/seen ) ))" minutes_ago_seen="$(( ( the_time_now - $( stat -c %Y announcements/people/${subjectlower}/seen ) ) / 60 ))" hours_ago_seen="$(( ( the_time_now - $( stat -c %Y announcements/people/${subjectlower}/seen ) ) / 3600 ))" days_ago_seen="$(( ( the_time_now - $( stat -c %Y announcements/people/${subjectlower}/seen ) ) / 86400 ))" months_ago_seen="$(( ( the_time_now - $( stat -c %Y announcements/people/${subjectlower}/seen ) ) / 2592000 ))" years_ago_seen="$(( ( the_time_now - $( stat -c %Y announcements/people/${subjectlower}/seen ) ) / 31104000 ))" if (( seconds_ago_seen < 120 )) then send_msg "${channel_it_came_from}" "I last saw ${subject} chatting ${seconds_ago_seen} seconds ago." elif (( minutes_ago_seen < 120 )) then send_msg "${channel_it_came_from}" "I last saw ${subject} chatting ${minutes_ago_seen} minutes ago." elif (( hours_ago_seen < 48 )) then send_msg "${channel_it_came_from}" "I last saw ${subject} chatting ${hours_ago_seen} hours ago." elif (( days_ago_seen < 60 )) then send_msg "${channel_it_came_from}" "I last saw ${subject} chatting ${days_ago_seen} days ago." elif (( months_ago_seen < 24 )) then send_msg "${channel_it_came_from}" "I last saw ${subject} chatting ${months_ago_seen} months ago." else send_msg "${channel_it_came_from}" "I last saw ${subject} chatting ${years_ago_seen} years ago." fi else send_msg "${channel_it_came_from}" "I have never seen ${subject} chatting." fi ;; ######## # tell # ######## "${server_nick_current}: tell "+([![:space:]:])?(:)+([[:space:]])+([![:space:]])* ) # The line will be something such as: # pbot: tell fauno: you suck # pbot: tell xylon i have heard that lemons are yummy local query="${sentence}" local bot_nick=${server_nick_current} local receiver=$(expr match "$query" ${bot_nick}': tell \([^\ :]*\):\? ') local message=$( expr match "$query" ${bot_nick}': tell [^\ ]* \(.*\)' ) if [[ "${receiver}" == "${bot_nick}" ]] then send_msg "${channel_it_came_from}" "${bot_nick}: ${personoslash} told me to tell you, (0 seconds ago): ${message}" else declare -l receiver_lower=${receiver//\/} local receiver_dir="announcements/people/${receiver_lower}" [[ -d "${receiver_dir}" ]] || mkdir -p "${receiver_dir}" # The time in *nix seconds is saved there so that # pbot can say how long ago the massage was sent # when he gives it to it's recipient. echo "$(date +%s) ${personoslash} ${message}" >> "${receiver_dir}/messages" local response="OK! I will do my best to relay your message to ${receiver}." send_msg "${channel_it_came_from}" "${personoslash}: ${response}" if ! [[ -f "${receiver_dir}/seen" ]] then send_msg "${channel_it_came_from}" "${personoslash}: WARNING: I HAVE NEVER SEEN \"${receiver}\" HERE BEFORE. CHECK YOUR SPELLING." fi fi ;; ############ # factoids # ############ # list factoids ',' | ',list' | ',factoids' | "${my_own_name}: list factoids" ) local factoids=( $(ls info/) ) local n_factoids=${#factoids[*]} local begin_msg="ask me about:" local end_msg="e.g. ${server_nick_current}: what is ${factoids[0]} (or ,${factoids[0]})" send_msg "${personoslash}" "${begin_msg}" for (( factoid_n = 0 ; factoid_n < n_factoids ; factoid_n = factoid_n + 10 )) do send_msg "${personoslash}" " ${factoids[*]:${factoid_n}:10}" sleep 1 done send_msg "${personoslash}" "${end_msg}" ;; # display factoid ','+([!/]) ) thing="${sentence#,}" tell_fact "${thing}" "${channel_it_came_from}" ;; "${my_own_name}: what is "+([![:space:]]) ) thing="${sentence#${my_own_name}: what is }" tell_fact "${thing}" "${channel_it_came_from}" ;; # set factoid "${my_own_name}: "+([!/])" is "+([![:space:]])* ) declare -l thing="${sentence#${my_own_name}: }" thing="${thing%% is *}" is="${sentence#* is }" remember_fact ;; "${my_own_name}: "+([!/])" is: "+([![:space:]])* ) declare -l thing="${sentence#${my_own_name}: }" thing="${thing%% is: *}" is="${sentence#* is: }" remember_fact ;; # delete factoid "${my_own_name}: "+([![:space:]])" isn't "+([![:space:]])* ) declare -l thing="${sentence#${my_own_name}: }" thing="${thing%% isn\'t *}" isnt="${sentence#* isn\'t }" forget_fact ;; "${my_own_name}: "+([![:space:]])" isn't: "+([![:space:]])* ) declare -l thing="${sentence#${my_own_name}: }" thing="${thing%% isn\'t: *}" isnt="${sentence#* isn\'t: }" forget_fact ;; ############# # Footnotes # ############# *\[[[:digit:]]\]* ) declare -a fn while read -d $'\0' file do #if match = grep "${file##*/}[ ]\?\[[[:digit:]]\]" <<< "${sentence}" filename="${file##*/}" declare -l lowersentence="${sentence}" if [[ " ${lowersentence}" =~ [^[:alnum:]]${filename}[\ ]?\[[[:digit:]]\] ]] then index="${BASH_REMATCH: -2:1}" fn[${index}]=$(head -1 "${file}") fi done < <(find info -print0) for (( n=0 ; n<50 ; n++ )) do str="${fn[${n}]}" [[ -z "${str}" ]] && continue send_msg "${channel_it_came_from}" "[${n}] ${str}" done ;; ######################## # unrecognised command # ######################## ${my_own_name}:* | ','* ) local module local modules_help='' local help_msg_line for module in ${config_modules} do case ${module} in 'translate' ) [[ "${sentence}" =~ ^"${my_own_name}: translate " ]] && return 0 modules_help="${modules_help} modules/m_translate.help.inc" ;; esac done while IFS='' read help_msg_line do help_msg_line=$(sed "s|_BOT_NICK_|${my_own_name}|" <<< ${help_msg_line}) send_msg "${personoslash}" "${help_msg_line}" ; sleep 1 ; done < <(cat process_event.help.inc ${modules_help}) ;; esac # TODO: add a birthday announcement feature, cointoss feature, timer feature. ######### # Tests # ######### #echo "${line}" }