Initial commit
[bashbot.git] / bashbot.sh
1 #!/bin/bash
2
3 set -u
4
5 version="0.0.1"
6
7 # make us a fifo...
8 rm -f .bashbotfifo{in,out}
9 mkfifo .bashbotfifoin
10 mkfifo .bashbotfifoout
11
12 # Setup some fds to talk to IRC
13 exec 3<> .bashbotfifoin
14 exec 4<> .bashbotfifoout
15
16 irc_process=
17 keeprunning=1
18 declare -A reminders
19
20 cleanup() {
21     echo "QUIT :Going back to hell" >&4
22     keeprunning=0
23     sleep 0.2
24     if [[ $irc_process -gt 0 ]]; then
25         if (ps -p $irc_process > /dev/null); then
26             kill $irc_process
27         fi
28     fi
29     rm .bashbotfifo{in,out}
30     trap - EXIT
31     exit 0
32 }
33
34 my_hostname="$(hostname)"
35 my_irc_channel="#alug"
36 my_irc_password=""
37 my_irc_nick="bpbot"
38 my_irc_server="irc.alug.org.uk"
39 my_irc_port="6697"
40
41 # now if there's a settings file, source that...
42 if [ -e "$(dirname $(readlink -f $0))/.botsettings.sh" ]; then
43     . "$(dirname $(readlink -f $0))/.botsettings.sh"
44 fi
45
46 connect_to_server() {
47     gnutls-cli --crlf $my_irc_server:$my_irc_port <&4 >&3 2>&3 &
48     irc_process=$!
49     connected=0
50     while [ $connected == 0 ]; do
51         read -t 0.2 -u 3 line
52         if [[ "$line" =~ NOTICE\  ]]; then
53             echo $line
54             send_line PASS $my_irc_password
55             send_line NICK $my_irc_nick
56             send_line USER $my_irc_nick $my_hostname $my_irc_server $my_irc_nick
57             send_line JOIN $my_irc_channel
58             connected=1
59         fi
60     done
61 }
62
63 # Format of server messages
64 #:thingsendingmessage command params
65 # e.g. PRIVMSG
66 #:iDunno!~brettp@mail.ipv6.sommitrealweird.co.uk PRIVMSG #bp :hello.
67
68 process_irc() {
69     line="$@"
70     echo "$line"
71     ping_regex="^PING "
72     privmsg_regex="^[^ ]* PRIVMSG"
73     if [[ "${line}" =~ $ping_regex ]]; then
74         send_line PONG
75         echo PONG
76     elif [[ "${line}" =~ $privmsg_regex ]]; then
77         parse_privmsg "$line"
78     fi
79 }
80
81 parse_privmsg() {
82     line="$@"
83     echo "Processing $line"
84     read -a parts <<-EOF
85 $line
86 EOF
87
88     # last character of the last element is going to be a \r so remove it
89     parts[-1]=${parts[-1]%$'\r'}
90
91     if [[ "${parts[1]}" == "PRIVMSG" ]]; then
92         sender=${parts[0]}
93         channel=${parts[2]}
94         do_command=0
95         case $channel in
96             $my_irc_channel)
97                 if [[ ${parts[3]} =~ [:]*$my_irc_nick[:]*$ ]]; then
98                     do_command=1
99                 fi
100                 bot_command=${parts[4]}
101                 options="${parts[@]:5}"
102                 ;;
103             $my_irc_nick)
104                 bot_command=${parts[3]}
105                 bot_command=${bot_command#:}
106                 channel=${parts[0]%\!*}
107                 channel=${channel#:}
108                 options="${parts[@]:4}"
109                 do_command=1
110                 ;;
111         esac
112         if [[ $do_command -gt 0 ]]; then
113             case $bot_command in
114                 help)
115                     send_help_message $channel
116                     ;;
117                 quit)
118                     cleanup
119                     ;;
120                 version)
121                     send_version $channel
122                     ;;
123                 add)
124                     add_reminder $channel "$options"
125                     ;;
126                 list)
127                     list_reminders $channel
128                     ;;
129                 *)
130                     send_line "PRIVMSG $channel :Sorry, don't know how to $bot_command."
131                     ;;
132             esac
133         fi
134     fi
135 }
136
137 send_line() {
138     echo "$@" >&4
139     sleep 0.1
140 }
141
142 send_help_message() {
143     channel=$1
144     echo "Sending help!"
145     send_line "PRIVMSG $channel :Help:"
146     send_line "PRIVMSG $channel :  An IRC bot written in bash!"
147     send_line "PRIVMSG $channel :  help - display this message"
148     send_line "PRIVMSG $channel :  version - display version number"
149     send_line "PRIVMSG $channel :  quit - make the bot quit IRC"
150     send_line "PRIVMSG $channel :  add YYYY-mm-dd HH:MM reminder text"
151     send_line "PRIVMSG $channel :  list - list reminders"
152 }
153
154 send_version() {
155     channel=$1
156     send_line "PRIVMSG $channel :Version: $version"
157 }
158
159 add_reminder() {
160     channel="$1"
161     data="$2"
162     read -a dataparts <<-EOF
163 $data
164 EOF
165     # we now have a list of parts, they should be of the form
166     # YYYY-mm-dd HH:MM the text of the reminder
167     timestamp=$(date +"%s" --date="${dataparts[0]} ${dataparts[1]}")
168     if [[ $? -ne 0 ]]; then
169         send_line "PRIVMSG $channel :Couldn't parse date/time ${dataparts[0]} ${dataparts[1]}"
170     else
171         send_line "PRIVMSG $channel :Added reminder for ${dataparts[0]} ${dataparts[1]}: ${dataparts[@]:2}"
172         reminders[$timestamp]="${dataparts[@]:2}"
173     fi
174 }
175
176 check_reminders() {
177     current_timestamp="$(date +"%s")"
178     # loop through the reminders keys to see if it's past and alert if so
179     for ts in ${!reminders[@]}; do
180         if [[ $ts -le $current_timestamp ]]; then
181             send_line "PRIVMSG $my_irc_channel :${reminders[$ts]}"
182             # we need to remove this from the array now
183             unset "reminders[$ts]"
184         fi
185     done
186 }
187
188 list_reminders() {
189     channel=$1
190     for ts in ${!reminders[@]}; do
191         date_stamp="$(date --date="@$ts" +"%Y-%m-%d %H:%M")"
192         send_line "PRIVMSG $channel :$date_stamp ${reminders[$ts]}"
193     done
194 }
195
196 connect_to_server
197 trap cleanup EXIT SIGINT
198
199 while true; do
200     # see if there's anything to read
201     read -t 0.2 -u 3 aline
202     if [ $? == 0 ]; then
203         process_irc "$aline"
204         sleep 0.2
205     fi
206     check_reminders
207     # check that the irc_process is still running, restart it if not
208     if ( ! kill -0 $irc_process ); then
209         connect_to_server
210     fi
211 done