]> jfr.im git - z_archive/Ophion.git/blame - classes.py
Cache.hookcmd() prototype changes
[z_archive/Ophion.git] / classes.py
CommitLineData
b069ba10
JR
1import socket, random, select
2from collections import deque
3
4from util import *
5
6class User:
7 def __init__(self, nick):
8 self.nick = nick
9 self.auth = ''
10 self.access = 0
11 self.alist = []
12 self.commonchans = []
13 self.host = None
14 def __str__(self): return self.nick
15 def __repr__(self): return "<User: %s (%d)>" % (self.nick, self.access)
16 def authed(self, username):
17 self.auth = username
18 if username in cache.admins:
19 self.access = cache.admins[username]
20 def joined(self, chan):
21 self.commonchans.append(chan)
22 def parted(self, chan):
23 if chan in self.commonchans: self.commonchans.remove(chan)
24 if self.commonchans == []:
25 self.auth = ''
26 self.access = 0
27
28class Channel:
29 def __init__(self, name, bot, id):
30 self.id = id
31 self.name = name
32 self.bot = bot
33
34 self.triggers = {}
35
36 self.alist = {}
37 curs = cache.dbc.cursor()
38 curs.execute("SELECT authname, level FROM chusers WHERE chid = %s", (self.id,))
39 row = curs.fetchone()
40 while row is not None:
41 self.alist[row['authname']] = row['level']
42 row = curs.fetchone()
43
44 self.ops = []
45 self.voices = []
46 self.users = []
47 def joined(self, user):
48 self.users.append(user)
49 user.joined(self.name)
50 def parted(self, user):
51 if user in self.users: self.users.remove(user)
52 if user in self.ops: self.ops.remove(user)
53 if user in self.voices: self.voices.remove(user)
54 user.parted(self.name)
55 def opped(self, user):
56 self.ops.append(user)
57 def voiced(self, user):
58 self.voices.append(user)
59 def __str__(self): return self.name
60 def __repr__(self): return "<Chan: %s>" % (self.name)
61
62class Bot:
63 def __init__(self, id):
64 self.id = id
65 self.online = False
66 self.chans = {}
67 self.mainbot = False
68 self.rawqueue = deque()
69
70 def get(self):
71 buf = ""
72 chin = self.s.recv(1)
73 while chin != "\n":
74 buf += chin
75 chin = self.s.recv(1)
76 buf = buf.strip()
77 print "<%d<%s" % (self.id, buf)
78 return buf
79
80 def sendRaws(self, count=2):
81 if self.online:
82 for i in range(count):
83 try: line = self.rawqueue.popleft()
84 except IndexError: return
85 self.rawnow(line)
86 def raw(self, line):
87 self.rawqueue.append(line)
88 def rawnow(self, line):
89 self.s.sendall(line+"\r\n")
90 print ">%d>%s" % (self.id, line)
91
92 def msg(self, target, msg):
93 msgs = msg.split("\n")
94 for msg in msgs:
95 if target[0] == '#':
96 self.raw("PRIVMSG %s :%s" % (target, msg))
97 else:
98 self.raw("NOTICE %s :%s" % (target, msg))
99 def cmsg(self, cmtype, msg, id=None):
100 if id is None: id = self.id
101 self.msg(cache.ctrl, cache.cmsgs[cmtype] % {'id': id, 'msg': msg})
102
103 def joined(self, chname, nick, chlevel=None):
104 chname = chname.lower()
105 if chname not in self.chans and chname not in cache.chans:
106 return
107
108 self.raw("WHO %s n%%nah" % (nick))
109 if nick not in cache.users: cache.users[nick] = User(nick)
110 self.chans[chname].joined(cache.users[nick])
111 if chlevel == "@":
112 self.chans[chname].opped(cache.users[nick])
113 if chlevel == "+":
114 self.chans[chname].voiced(cache.users[nick])
115 def parted(self, chname, nick):
116 chname = chname.lower()
117 if nick == self.nick:
118 del self.chans[chname]
119 del cache.chans[chname]
120 elif chname in self.chans:
121 self.chans[chname].parted(cache.users[nick])
122 def quit(self, nick):
123 if nick in cache.users:
124 user = cache.users[nick]
125 for ch in user.commonchans:
126 cache.chans[ch].parted(user)
127 del cache.users[nick]
128
129 def disconnect(self):
130 try: cache.mainBot.cmsg('warn', 'Disconnected!', self.id)
131 except: pass
132 try: self.rawnow("QUIT :Disconnected")
133 except: pass
134 try: self.s.shutdown(socket.SHUT_RDWR)
135 except: pass
136 try: self.s.close()
137 except: pass
138 for ch in self.chans.values():
139 for user in ch.users:
140 ch.parted(user)
141 cache.poll.unregister(self.s.fileno())
142 self.online = False
143
144 def joinChans(self):
145 curs = cache.dbc.cursor()
146 curs.execute("SELECT id, chname FROM chans WHERE botid = %s", (self.id,))
147 row = curs.fetchone()
148 while row is not None:
149 self.raw("JOIN %s" % (row['chname']))
150 chan = Channel(row['chname'].lower(), self, row['id'])
151 self.chans[row['chname'].lower()] = chan
152 cache.chans[row['chname']] = chan
153 row = curs.fetchone()
154 curs.close()
155 def connect(self, ident):
156 curs = cache.dbc.cursor()
157 curs.execute("SELECT irchost, ircport, ircpass, nick, vhost, realname, authname, authpass FROM bots WHERE id = %s", (self.id,))
158 row = curs.fetchone()
159 self.nick = row['nick']
160 self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
161 self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
550b7560
JR
162 if row['vhost'] is not None:
163 self.s.bind((row['vhost'], 0))
b069ba10
JR
164 self.s.connect((row['irchost'], row['ircport']))
165 self.rawnow("NICK %s" % (self.nick))
166 self.rawnow("USER %s * * :%s" % (ident, row['realname']))
167 curs.close()
168 while True:
169 line = self.get()
170 pieces = line.split()
171 if pieces[0] == "PING":
172 self.rawnow("PONG %s" % (pieces[1]))
173 elif pieces[1] == "433":
174 self.nick = self.nick+str(random.randrange(0,9))
175 self.rawnow("NICK %s" % (self.nick))
176 elif pieces[0] == "ERROR":
177 self.online = False
178 return False
179 elif pieces[1] == "376" or pieces[1] == "422":
180 break
181 if row['authname'] is not None and row['authpass'] is not None:
182 self.rawnow("AUTH %s %s" % (row['authname'], row['authpass']))
183 self.rawnow("MODE %s +ix" % (self.nick))
184 cache.poll.register(self.s.fileno(), select.POLLIN)
185 cache.botsByFD[self.s.fileno()] = self
186 cache.botsByNick[self.nick] = self
187 self.joinChans()
188 self.online = True
189 return True
190 def __str__(self): return self.nick
191 def __repr__(self): return "<Bot%d: %s>" % (self.id, self.nick)
192
193class Cache:
550b7560
JR
194 # config
195 lshost = '0.0.0.0'
196 lsport = 13245
197 moduledata = '/home/bots/modules/'
198 trigger = '!'
199 cmsgs = { # %(id)d = bot id, %(msg)s = log message.
200 'debug': "\00303[\037DEBUG\037][%(id)d]: %(msg)s",
201 'info': "\00312[\037INFO\037][%(id)d]: %(msg)s",
202 'warn': "\00306[\037WARN\037][%(id)d]: %(msg)s",
203 'fatal': "\00304[\037FATAL\037][%(id)d]: %(msg)s",
204 }
205
206
207 # NOT config
b069ba10
JR
208 dbc = None
209 ls = None
210 admins = {}
211 bots = {}
212 botsByFD = {}
213 botsByNick = {}
214 mainBot = None
215 home = None # homechan
216 ctrl = None # ctrlchan
217 modules = {}
218 unmodules = {} # unloaded
219 timers = {}
220 quitting = False
221
222 currmod = None
223 cmds = {}
224 nums = {}
225
226 users = {}
227 chans = {}
228
b069ba10
JR
229 def __init__(self):
230 global cache
231 cache = self
232
233 def hooknum(self, num, func):
234 try: self.nums[num].append(func)
235 except: self.nums[num] = [func]
236 def unhooknum(self, num, func):
237 try: self.nums[num].remove(func)
238 except: return False
239 else: return True
240
555a2a31 241 def hookcmd(self, cmdname, level, cmdcallback, minparams, helpcallback, isadmin=False, reqchan=True):
b069ba10
JR
242 self.cmds[cmd.upper()] = {'module': cache.currmod, 'func': func, 'level': level, 'params': params, 'helpfunc': helpfunc, 'isadmin': isadmin, 'reqchan': reqchan}
243 def unhookcmd(self, cmd):
244 try: del self.cmds[cmd]
245 except: return False
246 else: return True
247
248 def gethelp(self, cmd, nick=None, user=None, access=None):
249 if cmd in self.cmds:
250 if self.cmds[cmd]['level'] == 0:
251 return self.cmds[cmd]['helpfunc']()
252 if nick is None and user is None:
253 return self.cmds[cmd]['helpfunc']()
254 elif nick is not None and nick in self.users and self.users[nick].access > self.cmds[cmd]['level']:
255 return self.cmds[cmd]['helpfunc']()
256 elif user is not None and user.access > self.cmds[cmd]['level']:
257 return self.cmds[cmd]['helpfunc']()
258 elif access is not None and access > self.cmds[cmd]['level']:
259 return self.cmds[cmd]['helpfunc']()
260 return None
261
262 def isbot(self, bid):
263 return (toint(bid) in self.bots)
264 def isonline(self, bid):
265 bid = toint(bid)
266 return (bid in self.bots and self.bots[bid].online)