Moving again
[nil.git] / nil / irc.py
blob392c84ba78af2d513f3b74d01576d6951d6401b4
1 import socket, time, random
3 colon = ':'
4 delimiter = ' '
6 def do_nothing(*arguments):
7 pass
9 def strip_tags(input):
10 output = ''
12 i = 0
13 last_index = len(input) - 1
14 while i <= last_index:
15 current_char = input[i]
16 if ord(current_char) < ord(' '):
17 if current_char == '\x03' and last_index - i >= 2:
18 next_char = input[i + 1]
19 next_char_value = ord(next_char)
20 if next_char == '1':
21 i += 1
22 second_digit = ord(input[i + 1])
23 if second_digit >= ord('0') and second_digit <= ord('5'):
24 i += 1
25 elif next_char == '0':
26 i += 2
27 elif next_char_value >= ord('2') and next_char_value <= ord('9'):
28 i += 1
29 else:
30 output += current_char
31 i += 1
32 return output
34 class irc_user:
35 def __init__(self, input):
36 self.raw = input
37 self.error = False
39 tokens = input[1 : ].split('!')
40 if len(tokens) != 2:
41 self.error = True
42 return
44 self.nick = tokens[0]
46 tokens = tokens[1].split('@')
47 if len(tokens) != 2:
48 self.error = True
49 return
51 self.ident = tokens[0]
52 self.address = tokens[1]
54 class irc_client:
55 def __init__(self):
56 self.on_raw_packet = do_nothing
57 self.on_failed_connect = do_nothing
58 self.on_connect = do_nothing
59 self.on_disconnect = do_nothing
60 self.on_entry = do_nothing
61 self.on_notice = do_nothing
62 self.on_join = do_nothing
63 self.on_invite = do_nothing
64 self.on_channel_message = do_nothing
65 self.on_private_message = do_nothing
66 self.on_nick_name_in_use = self.change_nick
67 self.on_quit = do_nothing
69 self.auto_reconnect = True
70 self.auto_reconnect_delay = 5
71 self.read_size = 1024
73 def connect(self, server, port, nick, user, local_host, real_name):
74 self.server = server
75 self.port = port
76 self.nick = nick
77 self.user = user
78 self.local_host = local_host
79 self.real_name = real_name
81 while True:
82 self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
83 try:
84 self.socket.connect((server, port))
85 self.on_connect()
86 break
87 except:
88 self.on_failed_connect()
89 if self.auto_reconnect:
90 time.sleep(self.auto_reconnect_delay)
91 else:
92 return
94 self.send_line('NICK %s' % nick)
95 self.send_line('USER %s "%s" "%s" :%s' % (user, local_host, server, real_name))
96 self.receive_data()
98 def reconnect(self):
99 self.connect(self.server, self.port, self.nick, self.user, self.local_host, self.real_name)
101 def send_line(self, line):
102 self.socket.send('%s\r\n' % line)
104 def join(self, channel):
105 self.send_line('JOIN %s' % channel)
107 def message(self, target, message):
108 self.send_line('PRIVMSG %s :%s' % (target, message))
110 def got_disconnected(self):
111 self.on_disconnect()
112 time.sleep(self.auto_reconnect_delay)
113 self.reconnect()
115 def receive_data(self):
116 self.buffer = ''
117 while True:
118 new_data = self.socket.recv(self.read_size)
119 if len(new_data) == 0:
120 self.got_disconnected()
121 return
122 self.buffer += new_data
123 self.process_new_data()
125 def process_new_data(self):
126 while True:
127 offset = self.buffer.find('\n')
128 if offset == -1:
129 break
130 line = self.buffer[0 : offset].replace('\r', '')
131 self.buffer = self.buffer[offset + 1 : ]
132 self.process_line(line)
134 def process_line(self, line):
135 self.on_raw_packet(line)
137 if len(line) == 0:
138 return
139 offset = line.find(colon, 1)
140 if offset == -1:
141 tokens = line.split(delimiter)
142 else:
143 tokens = line[0 : offset - 1].split(delimiter)
144 tokens.append(line[offset + 1 : ])
146 self.process_command(tokens)
148 def process_command(self, tokens):
149 commands = [
150 ('376', self.event_end_of_motd),
151 ('422', self.event_end_of_motd),
152 ('433', self.event_nick_name_in_use),
153 ('NOTICE', self.event_notice),
154 ('INVITE', self.event_invite),
155 ('JOIN', self.event_join),
156 ('PRIVMSG', self.event_message),
157 ('MODE', self.event_mode),
158 ('QUIT', self.event_quit)
161 if tokens[0] == 'PING' and len(tokens) == 2:
162 self.send_line('PONG %s' % tokens[1])
163 return
165 if len(tokens) < 3:
166 return
168 for command, function in commands:
169 if command == tokens[1]:
170 function(tokens)
171 break
173 def change_nick(self):
174 new_nick = '%s%d' % (self.nick, random.randint(10, 99))
175 self.send_line('NICK %s' % new_nick)
177 def event_end_of_motd(self, tokens):
178 self.nick = tokens[2]
179 self.on_entry()
181 def event_nick_name_in_use(self, tokens):
182 self.on_nick_name_in_use()
184 def event_notice(self, tokens):
185 user = irc_user(tokens[0])
186 if user.error:
187 return
188 text = tokens[-1]
189 self.on_notice(user, text)
191 def event_invite(self, tokens):
192 user = irc_user(tokens[0])
193 if user.error:
194 return
195 channel = tokens[-1]
196 self.on_invite(user, channel)
198 def event_join(self, tokens):
199 user = irc_user(tokens[0])
200 if user.error:
201 return
202 own_join = (user.nick == self.nick)
203 channel = tokens[-1]
204 self.on_join(channel, user, own_join)
206 def event_message(self, tokens):
207 user = irc_user(tokens[0])
208 if user.error:
209 return
210 target = tokens[2]
211 message = tokens[-1]
212 if target == self.nick:
213 self.on_private_message(user, message)
214 else:
215 self.on_channel_message(target, user, message)
217 def event_mode(self, tokens):
218 pass
220 def event_quit(self, tokens):
221 user = irc_user(tokens[0])
222 if user.error:
223 return
224 message = tokens[-1]
225 self.on_quit(user, message)