Improve reminders indexing
[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     send_line "QUIT :Going back to hell"
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="bashbot"
38 my_irc_server="irc.oftc.net"
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[@]} -lt 4 ]]; then
92         return
93     fi
94
95     if [[ "${parts[1]}" == "PRIVMSG" ]]; then
96         sender=${parts[0]}
97         channel=${parts[2]}
98         do_command=0
99         case $channel in
100             $my_irc_channel)
101                 if [[ ${parts[3]} =~ [:]*$my_irc_nick[:]*$ ]]; then
102                     do_command=1
103                 else
104                     return
105                 fi
106                 bot_command=${parts[4]}
107                 options="${parts[@]:5}"
108                 ;;
109             $my_irc_nick)
110                 bot_command=${parts[3]}
111                 bot_command=${bot_command#:}
112                 channel=${parts[0]%\!*}
113                 channel=${channel#:}
114                 options="${parts[@]:4}"
115                 do_command=1
116                 ;;
117         esac
118         if [[ $do_command -gt 0 ]]; then
119             case $bot_command in
120                 help)
121                     send_help_message $channel
122                     ;;
123                 quit)
124                     cleanup
125                     ;;
126                 version)
127                     send_version $channel
128                     ;;
129                 add)
130                     add_reminder $channel "$options"
131                     ;;
132                 del)
133                     del_reminder $channel "$options"
134                     ;;
135                 list)
136                     list_reminders $channel
137                     ;;
138                 *)
139                     send_action $channel "is sorry, they don't know how to $bot_command."
140                     ;;
141             esac
142         fi
143     fi
144 }
145
146 send_line() {
147     echo "$@" >&4
148     sleep 0.1
149 }
150
151 send_privmsg() {
152     channel="$1"
153     content="$2"
154
155     send_line "PRIVMSG $channel :$content"
156 }
157
158 send_action() {
159     channel="$1"
160     content="$2"
161     action_wrapper=$'\001'
162
163     content="${action_wrapper}ACTION ${content}${action_wrapper}"
164     send_privmsg "$channel" "$content"
165 }
166
167 send_help_message() {
168     channel=$1
169     echo "Sending help!"
170     send_privmsg $channel "Help:"
171     send_privmsg $channel "  An IRC bot written in bash!"
172     send_privmsg $channel "  help - display this message"
173     send_privmsg $channel "  version - display version number"
174     send_privmsg $channel "  quit - make the bot quit IRC"
175     send_privmsg $channel "  add YYYY-mm-dd HH:MM reminder text"
176     send_privmsg $channel "  del md5sum"
177     send_privmsg $channel "  list - list reminders"
178 }
179
180 send_version() {
181     channel=$1
182     send_privmsg $channel "Version: $version"
183 }
184
185 add_reminder() {
186     channel="$1"
187     data="$2"
188     read -a dataparts <<-EOF
189 $data
190 EOF
191     # we now have a list of parts, they should be of the form
192     # YYYY-mm-dd HH:MM the text of the reminder
193     timestamp=$(date +"%s" --date="${dataparts[0]} ${dataparts[1]}")
194     if [[ $? -ne 0 ]]; then
195         send_privmsg $channel "Couldn't parse date/time ${dataparts[0]} ${dataparts[1]}"
196     else
197         text="${dataparts[@]:2}"
198         md5sum=$(echo -n "$timestamp $text" | md5sum | sed -e 's#[ ][ ]*-##;')
199         send_privmsg $channel "Added reminder for [$md5sum] ${dataparts[0]} ${dataparts[1]}: $text"
200         reminders[$timestamp-$md5sum]="$text"
201     fi
202 }
203
204 check_reminders() {
205     current_timestamp="$(date +"%s")"
206     # loop through the reminders keys to see if it's past and alert if so
207     for tsmd5 in ${!reminders[@]}; do
208         ts=${tsmd5%-*}
209         if [[ $ts -le $current_timestamp ]]; then
210             send_privmsg $my_irc_channel "${reminders[$tsmd5]}"
211             # we need to remove this from the array now
212             unset "reminders[$tsmd5]"
213         fi
214     done
215 }
216
217 list_reminders() {
218     channel=$1
219     count=0
220     for tsmd5 in ${!reminders[@]}; do
221         ts=${tsmd5%-*}
222         md5sum=${tsmd5#*-}
223         date_stamp="$(date --date="@$ts" +"%Y-%m-%d %H:%M %Z")"
224         send_privmsg $channel "[$md5sum] $date_stamp ${reminders[$tsmd5]}"
225         count=$((count+1))
226     done
227
228     if [[ $count -eq 0 ]]; then
229         send_privmsg $channel "There are currently no reminders set, use add to add one."
230     fi
231 }
232
233 del_reminder() {
234     channel="$1"
235     md5todelete="$2"
236     found=0
237     for tsmd5 in ${!reminders[@]}; do
238         md5sum=${tsmd5#*-}
239         if [[ "$md5sum" == "$md5todelete" ]]; then
240             ts=${tsmd5%-*}
241             date_stamp="$(date --date="@$ts" +"%Y-%m-%d %H:%M %Z")"
242             send_privmsg $channel "Removed reminder [$md5sum] $date_stamp ${reminders[$tsmd5]}"
243             unset "reminders[$tsmd5]"
244             found=1
245         fi
246     done
247     if [[ $found -eq 0 ]]; then
248         send_privmsg $channel "Couldn't find reminder $md5todelete"
249     fi
250 }
251
252 connect_to_server
253 trap cleanup EXIT SIGINT
254
255 while true; do
256     # see if there's anything to read
257     read -t 0.2 -u 3 aline
258     if [ $? == 0 ]; then
259         process_irc "$aline"
260         sleep 0.2
261     fi
262     check_reminders
263     # check that the irc_process is still running, restart it if not
264     if ( ! kill -0 $irc_process ); then
265         connect_to_server
266     fi
267 done