]>
Commit | Line | Data |
---|---|---|
aaa8eb8d JR |
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 | # Note: this module doesn't do any kind of authentication, so anyone who can connect to the bound port can spam you. | |
6 | ||
7 | # module info | |
8 | modinfo = { | |
9 | 'author': 'Erebus Team', | |
10 | 'license': 'public domain', | |
11 | 'compatible': [0], | |
12 | 'depends': [], | |
13 | 'softdeps': [], | |
14 | } | |
15 | ||
16 | # preamble | |
17 | import modlib | |
18 | lib = modlib.modlib(__name__) | |
19 | def modstart(parent, *args, **kwargs): | |
20 | gotParent(parent) | |
21 | return lib.modstart(parent, *args, **kwargs) | |
22 | modstop = lib.modstop | |
23 | ||
24 | # module code | |
25 | ||
26 | def gotParent(parent): | |
27 | for bindto, channel in parent.cfg.items('sockets'): | |
28 | @lib.bind(bindto, data=channel) | |
29 | class BasicServer(object): | |
30 | def __init__(self, sock, data): | |
b5e5c447 JR |
31 | # NB neither directly referencing `channel`, nor trying to pass it through a default-arg-to-a-lambda like the python docs suggest, works here. |
32 | # Yay python. At least passing it via bind works. | |
aaa8eb8d | 33 | self.chan = data |
aaa8eb8d JR |
34 | self.buffer = b'' |
35 | self.sock = sock | |
36 | ||
37 | def getdata(self): | |
38 | recvd = self.sock.recv(8192) | |
39 | if recvd == b"": # EOF | |
40 | if len(self.buffer) != 0: | |
41 | # Process what's left in the buffer. We'll get called again after. | |
42 | remaining_buf = self.buffer.decode('utf-8', 'backslashreplace') | |
43 | self.buffer = b"" | |
44 | return [remaining_buf] | |
45 | else: | |
46 | # Nothing left in the buffer. Return None to signal the core to close this socket. | |
47 | return None | |
48 | self.buffer += recvd | |
49 | lines = [] | |
50 | ||
51 | while b"\n" in self.buffer: | |
52 | pieces = self.buffer.split(b"\n", 1) | |
53 | s = pieces[0].decode('utf-8', 'backslashreplace').rstrip("\r") | |
54 | lines.append(pieces[0].decode('utf-8', 'backslashreplace')) | |
55 | self.buffer = pieces[1] | |
56 | ||
57 | return lines | |
58 | ||
59 | def parse(self, line): | |
33af27c6 JR |
60 | try: |
61 | bot = lib.parent.channel(self.chan).bot | |
62 | except AttributeError: # <class 'AttributeError'> 'NoneType' object has no attribute 'bot' | |
63 | bot = lib.parent.randbot() | |
aaa8eb8d JR |
64 | maxlen = bot.maxmsglen() - len("PRIVMSG :") - len(self.chan) |
65 | while len(line) > maxlen: | |
66 | cutat = line.rfind(' ', 0, maxlen) | |
67 | if cutat == -1: | |
68 | cutat = maxlen | |
69 | bot.msg(self.chan, line[0:cutat]) | |
70 | line = line[cutat:].strip() | |
71 | bot.msg(self.chan, line) | |
72 | ||
73 | def send(self, line): | |
74 | if lib.parent.parent.cfg.getboolean('debug', 'io'): | |
75 | lib.parent.log(str(self), 'O', line) | |
76 | self.sock.sendall(line.encode('utf-8', 'backslashreplace')+b"\r\n") | |
77 | ||
78 | def _getsockerr(self): | |
79 | try: # SO_ERROR might not exist on all platforms | |
80 | return self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) | |
81 | except: | |
82 | return None | |
83 | ||
84 | def __str__(self): | |
85 | return '%s#%d' % (__name__, self.sock.fileno()) | |
86 | def __repr__(self): | |
87 | return '<%s #%d %s:%d>' % ((__name__, self.sock.fileno())+self.sock.getpeername()) |