]> jfr.im git - z_archive/Ophion.git/blame - classes.py
Initialize repo
[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)
162 self.s.bind((row['vhost'], 0))
163 self.s.connect((row['irchost'], row['ircport']))
164 self.rawnow("NICK %s" % (self.nick))
165 self.rawnow("USER %s * * :%s" % (ident, row['realname']))
166 curs.close()
167 while True:
168 line = self.get()
169 pieces = line.split()
170 if pieces[0] == "PING":
171 self.rawnow("PONG %s" % (pieces[1]))
172 elif pieces[1] == "433":
173 self.nick = self.nick+str(random.randrange(0,9))
174 self.rawnow("NICK %s" % (self.nick))
175 elif pieces[0] == "ERROR":
176 self.online = False
177 return False
178 elif pieces[1] == "376" or pieces[1] == "422":
179 break
180 if row['authname'] is not None and row['authpass'] is not None:
181 self.rawnow("AUTH %s %s" % (row['authname'], row['authpass']))
182 self.rawnow("MODE %s +ix" % (self.nick))
183 cache.poll.register(self.s.fileno(), select.POLLIN)
184 cache.botsByFD[self.s.fileno()] = self
185 cache.botsByNick[self.nick] = self
186 self.joinChans()
187 self.online = True
188 return True
189 def __str__(self): return self.nick
190 def __repr__(self): return "<Bot%d: %s>" % (self.id, self.nick)
191
192class Cache:
193 dbc = None
194 ls = None
195 admins = {}
196 bots = {}
197 botsByFD = {}
198 botsByNick = {}
199 mainBot = None
200 home = None # homechan
201 ctrl = None # ctrlchan
202 modules = {}
203 unmodules = {} # unloaded
204 timers = {}
205 quitting = False
206
207 currmod = None
208 cmds = {}
209 nums = {}
210
211 users = {}
212 chans = {}
213
214 ## CONFIG
215 lshost = '0.0.0.0'
216 lsport = 13245
217 triviapath = '/home/ophion/modules/trivia/'
218 trigger = '!'
219 cmsgs = {
220 'debug': "\00303[\037DEBUG\037][%(id)d]: %(msg)s",
221 'info': "\00312[\037INFO\037][%(id)d]: %(msg)s",
222 'warn': "\00306[\037WARN\037][%(id)d]: %(msg)s",
223 'fatal': "\00304[\037FATAL\037][%(id)d]: %(msg)s",
224 }
225
226 def __init__(self):
227 global cache
228 cache = self
229
230 def hooknum(self, num, func):
231 try: self.nums[num].append(func)
232 except: self.nums[num] = [func]
233 def unhooknum(self, num, func):
234 try: self.nums[num].remove(func)
235 except: return False
236 else: return True
237
238 def hookcmd(self, cmd, level, func, params, helpfunc, isadmin=False, reqchan=True):
239 self.cmds[cmd.upper()] = {'module': cache.currmod, 'func': func, 'level': level, 'params': params, 'helpfunc': helpfunc, 'isadmin': isadmin, 'reqchan': reqchan}
240 def unhookcmd(self, cmd):
241 try: del self.cmds[cmd]
242 except: return False
243 else: return True
244
245 def gethelp(self, cmd, nick=None, user=None, access=None):
246 if cmd in self.cmds:
247 if self.cmds[cmd]['level'] == 0:
248 return self.cmds[cmd]['helpfunc']()
249 if nick is None and user is None:
250 return self.cmds[cmd]['helpfunc']()
251 elif nick is not None and nick in self.users and self.users[nick].access > self.cmds[cmd]['level']:
252 return self.cmds[cmd]['helpfunc']()
253 elif user is not None and user.access > self.cmds[cmd]['level']:
254 return self.cmds[cmd]['helpfunc']()
255 elif access is not None and access > self.cmds[cmd]['level']:
256 return self.cmds[cmd]['helpfunc']()
257 return None
258
259 def isbot(self, bid):
260 return (toint(bid) in self.bots)
261 def isonline(self, bid):
262 bid = toint(bid)
263 return (bid in self.bots and self.bots[bid].online)