-
Notifications
You must be signed in to change notification settings - Fork 0
/
irc.sh
180 lines (163 loc) · 4.75 KB
/
irc.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
#!/bin/bash
# -*- indent-tabs-mode: nil -*-
shopt -s extglob
declare gToServer
declare gFromServer
# This just fires off a netcat and sets appropriate globals with the file
# descriptors for the coprocess. If the bash you use supports /dev/tcp, you
# could replace this with a bidirectional redirection to
# /dev/tcp/${pServer:?}/${pPort:?}.
connect() {
coproc nc "${pServer:?}" "${pPort:?}"
gToServer="${COPROC[1]}"
gFromServer="${COPROC[0]}"
}
# This sends a command to the server. The final argument is permitted to
# have whitespace, and will be prefixed with a : appropriately in-protocol.
sendToServer() {
local line="$1"
shift
while [[ $# -gt 1 ]]; do
line+=" $1"
shift
done
line+=" :$1\r\n"
echo -en "$line" >&$gToServer
} 2>/dev/null
# Takes a raw line as read from the server and breaks it apart into the pieces
# of the prefix, the command, and its arguments. The second argument is the
# callback to call with the bits of the message as arguments.
decode() {
local line="$1"
local callback="$2"
local nick
local user
local host
local -a body
local -i bodyidx=0
local char
local innick
local inuser
local inhost
local inspace=true
local inlast
if [[ "${line::1}" == ':' ]]; then
innick=true
line="${line:1}"
fi
while [[ ${#line} -ne 0 ]]; do
char="${line::1}"
line="${line:1}"
[[ "$char" == $'\r' ]] && break
if [[ $innick ]]; then
if [[ "$char" == '!' ]]; then
innick=
inuser=true
elif [[ "$char" == '@' ]]; then
innick=
inhost=true
elif [[ "$char" == ' ' ]]; then
innick=
else
nick+="$char"
fi
elif [[ $inuser ]]; then
if [[ "$char" == '@' ]]; then
inuser=
inhost=true
elif [[ "$char" == ' ' ]]; then
inuser=
else
user+="$char"
fi
elif [[ $inhost ]]; then
if [[ "$char" == ' ' ]]; then
inhost=
else
host+="$char"
fi
elif [[ $inspace ]]; then
if [[ "$char" == ':' ]]; then
inspace=
inlast=true
elif [[ "$char" != ' ' ]]; then
inspace=
body[bodyidx]+="$char"
fi
elif [[ $inlast ]]; then
body[bodyidx]+="$char"
else
if [[ "$char" == ' ' ]]; then
inspace=true
bodyidx+=1
else
body[bodyidx]+="$char"
fi
fi
done
[[ $innick || $inuser || $inhost ]] && return 1
$callback "$nick" "$user" "$host" "${body[@]}"
}
# Read a line from the server, decode it, and call the given callback with the
# bits of the incoming message.
readFromServer() {
local callback="$1"
local lineraw
read -u $gFromServer lineraw || return $?
decode "$lineraw" "$1"
} 2>/dev/null
# A default on-connect script that should be sufficient for most bot needs,
# it requests an identify-msg capability to allow easy NickServ authentication
# and sends along the configured user, nickname, and realname.
onConnect() {
[[ -n "$pPassword" ]] && sendToServer PASS "$pPassword"
sendToServer CAP LS
sendToServer CAP REQ identify-msg
sendToServer USER "${pUser:?}" 0 "*" "${pRealname:?}"
sendToServer NICK "${pNick:?}"
sendToServer CAP END
echo "We're in"
}
# Called after connected and registered to join channels or do whatever other
# initialization one may wish to do.
onStartup() {
return 0
}
# Called whenever a message comes in. By default, figures out what command
# or status code came in and calls the appropriate onCOMMAND callback if
# present, or onUnknown otherwise.
onMessage() {
[[ $# -lt 4 ]] && return
local command="$4"
[[ "$command" != "${command##!(+([A-Z0-9]))}" ]] && return
local callback="on$command"
local callbackType="$(type -t "$callback")"
if [[ "$callbackType" != "function" ]]; then
onUnknown "$@"
else
"$callback" "$@"
fi
}
# Called whenever onMessage encounters an unknown/unhandled message. By
# default, just prints out some useful spew to the terminal.
onUnknown() {
local nick="$1"
shift 3
if [[ $nick ]]; then
echo -n "$nick>"
else
echo -n "SERVER>"
fi
while [[ $# -ne 0 ]]; do
echo -n " $1"
shift
done
echo
}
# Called by onMessage when the server sends a PING, immediately responds with
# a PONG to avoid getting timed out.
onPING() {
[[ $# -ne 5 ]] && return
local message="$5"
sendToServer PONG "$message"
}