+#!/bin/bash
+
+set -u
+
+version="0.0.1"
+
+# make us a fifo...
+rm -f .bashbotfifo{in,out}
+mkfifo .bashbotfifoin
+mkfifo .bashbotfifoout
+
+# Setup some fds to talk to IRC
+exec 3<> .bashbotfifoin
+exec 4<> .bashbotfifoout
+
+irc_process=
+keeprunning=1
+declare -A reminders
+
+cleanup() {
+ echo "QUIT :Going back to hell" >&4
+ keeprunning=0
+ sleep 0.2
+ if [[ $irc_process -gt 0 ]]; then
+ if (ps -p $irc_process > /dev/null); then
+ kill $irc_process
+ fi
+ fi
+ rm .bashbotfifo{in,out}
+ trap - EXIT
+ exit 0
+}
+
+my_hostname="$(hostname)"
+my_irc_channel="#alug"
+my_irc_password=""
+my_irc_nick="bpbot"
+my_irc_server="irc.alug.org.uk"
+my_irc_port="6697"
+
+# now if there's a settings file, source that...
+if [ -e "$(dirname $(readlink -f $0))/.botsettings.sh" ]; then
+ . "$(dirname $(readlink -f $0))/.botsettings.sh"
+fi
+
+connect_to_server() {
+ gnutls-cli --crlf $my_irc_server:$my_irc_port <&4 >&3 2>&3 &
+ irc_process=$!
+ connected=0
+ while [ $connected == 0 ]; do
+ read -t 0.2 -u 3 line
+ if [[ "$line" =~ NOTICE\ ]]; then
+ echo $line
+ send_line PASS $my_irc_password
+ send_line NICK $my_irc_nick
+ send_line USER $my_irc_nick $my_hostname $my_irc_server $my_irc_nick
+ send_line JOIN $my_irc_channel
+ connected=1
+ fi
+ done
+}
+
+# Format of server messages
+#:thingsendingmessage command params
+# e.g. PRIVMSG
+#:iDunno!~brettp@mail.ipv6.sommitrealweird.co.uk PRIVMSG #bp :hello.
+
+process_irc() {
+ line="$@"
+ echo "$line"
+ ping_regex="^PING "
+ privmsg_regex="^[^ ]* PRIVMSG"
+ if [[ "${line}" =~ $ping_regex ]]; then
+ send_line PONG
+ echo PONG
+ elif [[ "${line}" =~ $privmsg_regex ]]; then
+ parse_privmsg "$line"
+ fi
+}
+
+parse_privmsg() {
+ line="$@"
+ echo "Processing $line"
+ read -a parts <<-EOF
+$line
+EOF
+
+ # last character of the last element is going to be a \r so remove it
+ parts[-1]=${parts[-1]%$'\r'}
+
+ if [[ "${parts[1]}" == "PRIVMSG" ]]; then
+ sender=${parts[0]}
+ channel=${parts[2]}
+ do_command=0
+ case $channel in
+ $my_irc_channel)
+ if [[ ${parts[3]} =~ [:]*$my_irc_nick[:]*$ ]]; then
+ do_command=1
+ fi
+ bot_command=${parts[4]}
+ options="${parts[@]:5}"
+ ;;
+ $my_irc_nick)
+ bot_command=${parts[3]}
+ bot_command=${bot_command#:}
+ channel=${parts[0]%\!*}
+ channel=${channel#:}
+ options="${parts[@]:4}"
+ do_command=1
+ ;;
+ esac
+ if [[ $do_command -gt 0 ]]; then
+ case $bot_command in
+ help)
+ send_help_message $channel
+ ;;
+ quit)
+ cleanup
+ ;;
+ version)
+ send_version $channel
+ ;;
+ add)
+ add_reminder $channel "$options"
+ ;;
+ list)
+ list_reminders $channel
+ ;;
+ *)
+ send_line "PRIVMSG $channel :Sorry, don't know how to $bot_command."
+ ;;
+ esac
+ fi
+ fi
+}
+
+send_line() {
+ echo "$@" >&4
+ sleep 0.1
+}
+
+send_help_message() {
+ channel=$1
+ echo "Sending help!"
+ send_line "PRIVMSG $channel :Help:"
+ send_line "PRIVMSG $channel : An IRC bot written in bash!"
+ send_line "PRIVMSG $channel : help - display this message"
+ send_line "PRIVMSG $channel : version - display version number"
+ send_line "PRIVMSG $channel : quit - make the bot quit IRC"
+ send_line "PRIVMSG $channel : add YYYY-mm-dd HH:MM reminder text"
+ send_line "PRIVMSG $channel : list - list reminders"
+}
+
+send_version() {
+ channel=$1
+ send_line "PRIVMSG $channel :Version: $version"
+}
+
+add_reminder() {
+ channel="$1"
+ data="$2"
+ read -a dataparts <<-EOF
+$data
+EOF
+ # we now have a list of parts, they should be of the form
+ # YYYY-mm-dd HH:MM the text of the reminder
+ timestamp=$(date +"%s" --date="${dataparts[0]} ${dataparts[1]}")
+ if [[ $? -ne 0 ]]; then
+ send_line "PRIVMSG $channel :Couldn't parse date/time ${dataparts[0]} ${dataparts[1]}"
+ else
+ send_line "PRIVMSG $channel :Added reminder for ${dataparts[0]} ${dataparts[1]}: ${dataparts[@]:2}"
+ reminders[$timestamp]="${dataparts[@]:2}"
+ fi
+}
+
+check_reminders() {
+ current_timestamp="$(date +"%s")"
+ # loop through the reminders keys to see if it's past and alert if so
+ for ts in ${!reminders[@]}; do
+ if [[ $ts -le $current_timestamp ]]; then
+ send_line "PRIVMSG $my_irc_channel :${reminders[$ts]}"
+ # we need to remove this from the array now
+ unset "reminders[$ts]"
+ fi
+ done
+}
+
+list_reminders() {
+ channel=$1
+ for ts in ${!reminders[@]}; do
+ date_stamp="$(date --date="@$ts" +"%Y-%m-%d %H:%M")"
+ send_line "PRIVMSG $channel :$date_stamp ${reminders[$ts]}"
+ done
+}
+
+connect_to_server
+trap cleanup EXIT SIGINT
+
+while true; do
+ # see if there's anything to read
+ read -t 0.2 -u 3 aline
+ if [ $? == 0 ]; then
+ process_irc "$aline"
+ sleep 0.2
+ fi
+ check_reminders
+ # check that the irc_process is still running, restart it if not
+ if ( ! kill -0 $irc_process ); then
+ connect_to_server
+ fi
+done