]> jfr.im git - erebus.git/blob - modules/basic_socket.py
bfd6b72cd12ecca2cef85cb8cf3418d2b7adebc0
[erebus.git] / modules / basic_socket.py
1 # Erebus IRC bot - Author: Erebus Team
2 # vim: fileencoding=utf-8
3 # This file is released into the public domain; see http://unlicense.org/
4
5 # module info
6 modinfo = {
7 'author': 'Erebus Team',
8 'license': 'public domain',
9 'compatible': [0],
10 'depends': [],
11 'softdeps': ['help'],
12 }
13
14 # preamble
15 import modlib
16 lib = modlib.modlib(__name__)
17 modstart = lib.modstart
18 modstop = lib.modstop
19
20 # module code
21
22 # Note: bind_* does all of the following:
23 # - create a socket `sock = socket.socket()`
24 # - bind the socket `sock.bind()`
25 # - listen on the socket `sock.listen()`
26 # - accept `sock.accept()`
27 #
28 # Once a connection is accepted, your class is instantiated with the client socket.
29 # - When data comes in on the client socket, your `getdata` method will be called. It should return a list of strings.
30 # - For each element in the list returned by `getdata`, `parse` will be called.
31 # - When the socket is being closed by the bot (f.e. your module is unloaded), the optional method `closing` will be called.
32 # Then the bot will call `sock.shutdown()` and `sock.close()` for you.
33 # XXX error handling? what happens when the other side closes the socket?
34 #
35 # You can interact with the rest of the bot through `lib.parent`.
36
37 @lib.bind_tcp('0.0.0.0', 12543)
38 class BasicServer(object):
39 def __init__(self, sock):
40 self.chan = lib.parent.cfg.get('basic_socket', 'channel', '#')
41 self.buffer = b''
42 self.sock = sock
43
44 def getdata(self):
45 recvd = self.sock.recv(8192)
46 if recvd == b"":
47 if len(self.buffer) != 0:
48 # Process what's left in the buffer. We'll get called again after.
49 remaining_buf = self.buffer.decode('utf-8', 'backslashreplace')
50 self.buffer = b""
51 return [remaining_buf]
52 else:
53 # Nothing left in the buffer. Return None to signal the core to close this socket.
54 return None
55 self.buffer += recvd
56 lines = []
57
58 while b"\n" in self.buffer:
59 pieces = self.buffer.split(b"\n", 1)
60 s = pieces[0].decode('utf-8', 'backslashreplace').rstrip("\r")
61 lines.append(pieces[0].decode('utf-8', 'backslashreplace'))
62 self.buffer = pieces[1]
63
64 return lines
65
66 def parse(self, line):
67 peer = self.sock.getpeername()
68 bot = lib.parent.randbot()
69 maxlen = bot.maxmsglen() - len("PRIVMSG :") - len(self.chan)
70 bot.msg(self.chan, "%s:%d says:" % peer)
71 while len(line) > maxlen:
72 bot.msg(self.chan, line[0:maxlen])
73 line = line[maxlen:]
74 bot.msg(self.chan, line)
75
76 def send(self, line):
77 self.socket.sendall(line.encode('utf-8', 'backslashreplace')+b"\r\n")
78
79 def _getsockerr(self):
80 try: # SO_ERROR might not exist on all platforms
81 return self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR)
82 except:
83 return None