2 # -*- coding: utf-8 -*-
6 """This library provides useful quassel related tools
8 Documentation bogus here"""
17 dbpath
=("~/.config/quassel-irc.org/quassel-storage.sqlite", "~/.quassel/quassel-storage.sqlite")
21 0 InvalidBuffer = 0x00,
22 1 StatusBuffer = 0x01,
23 2 ChannelBuffer = 0x02,
41 8192 DayChange = 0x2000
43 Message Flags (mostly internal, but may be useful)
51 class QuasselError(exceptions
.Exception):
52 """base class for all quassel related exceptions"""
55 class NotFound(QuasselError
):
56 """Generic error when something was not found in a getter
58 methods that throw this should be clear enough to guess the meaning"""
62 """Template class for log formatters, do not use directly
64 row is a list with elements (type, flags, time, sender, message)
65 type is message type, mostly for internal use
66 flags is unneeded currently
67 time is the time as unix timestamp
68 sender is the sender as nick!ident@hostmask
69 message is the message
71 All output producing methods must return strings
73 if you don't override methods, they will be forwarded to others()
74 which can be used like an else-statement"""
77 """if overriding, always call parent unless you know what you're doing
79 NOTE: Includes language specfic code for an english core"""
80 #TODO: these strings are language specific for an english core
81 self
.topicchangere
= re
.compile('(\S+) has changed topic for (\S+) to: "(.*)"$')
82 self
.topicjoinre
= re
.compile('Topic for (\S+) is "(.*)"$')
83 self
.topicjoin2re
= re
.compile('Topic set by (\S+) on (\S+ \S+ \d+ \S+)')
85 def format(self
, row
):
86 """wrapper for the methods, most child classes should not touch this
88 in most cases, you will call this method with a row to get a meaningful representation of that row"""
90 return self
.message(row
)
92 return self
.notice(row
)
94 return self
.action(row
)
100 return self
.join(row
)
102 return self
.part(row
)
104 return self
.quit(row
)
106 return self
.kick(row
)
108 return self
._server
(row
)
110 return self
.info(row
)
112 return self
.error(row
)
114 return self
.daychange(row
)
116 return self
.others(row
)
118 def _server(self
, row
):
119 match
= self
.topicchangere
.search(row
[4])
120 if not match
is None:
121 (who
, where
, what
) = match
.groups()
122 return self
.topicchange(row
[2], who
, where
, what
)
124 match
= self
.topicjoinre
.search(row
[4])
125 if not match
is None:
126 (where
, what
) = match
.groups()
127 return self
.topicjoin1(row
[2], where
, what
)
129 match
= self
.topicjoin2re
.search(row
[4])
130 if not match
is None:
131 (who
, when
) = match
.groups()
132 return self
.topicjoin2(row
[2], who
, when
)
134 return self
.server(row
)
136 def message(self
, row
):
137 """defines format of regular messages"""
138 return self
.others(row
)
140 def notice(self
, row
):
141 """defines format of notices"""
142 return self
.others(row
)
144 def action(self
, row
):
145 """defines format of actions (/me)"""
146 return self
.others(row
)
149 """defines format of nickchanges"""
150 return self
.others(row
)
153 """defines format of modechanges"""
154 return self
.others(row
)
157 """defines format of joins (including self)"""
158 return self
.others(row
)
161 """defines format of parts (including self)"""
162 return self
.others(row
)
165 """defines format of quits (including self)"""
166 return self
.others(row
)
169 """defines format of kicks (including self)"""
170 return self
.others(row
)
172 def server(self
, row
):
173 """defines format of server messages not including topic related messages"""
174 return self
.others(row
)
176 def topicchange(self
, time
, user
, channel
, topic
):
177 """defines format of generic topic changes"""
178 return self
.others(row
)
180 def topicjoin1(self
, time
, channel
, topic
):
181 """defines format of channel join topic message, part one"""
182 return self
.others(row
)
184 def topicjoin2(self
, time
, user
, logtime
):
185 """defines format of channel join topic message, part two"""
186 return self
.others(row
)
189 """defines format of info lines"""
190 return self
.others(row
)
192 def error(self
, row
):
193 """defines format of error lines"""
194 return self
.others(row
)
196 def daychange(self
, row
):
197 """defines format of daychange message, mostly quassel specific"""
198 return self
.others(row
)
200 def others(self
, row
):
201 """defines format of all types that you didn't override in your child, used to catch unhandled types"""
204 class Logformat_mIRC (Logformat
):
205 def __init__(self
, buffer):
208 Logformat
.__init
__(self
)
211 def _now(self
, timestamp
):
212 return time
.strftime("[%H:%M.%S]", time
.localtime(timestamp
))
214 def _now2(self
, timestamp
):
215 return time
.strftime("%a %b %d %H:%M:%S %Y", time
.localtime(timestamp
))
217 def message(self
, row
):
218 sender
= row
[3].split("!")
219 return "%s <%s> %s\n"%(self
._now
(row
[2]), sender
[0], row
[4])
221 def notice(self
, row
):
222 sender
= row
[3].split("!")
223 return "%s -%s- %s\n"%(self
._now
(row
[2]), sender
[0], row
[4])
225 def action(self
, row
):
226 sender
= row
[3].split("!")
227 return "%s * %s %s\n"%(self
._now
(row
[2]), sender
[0], row
[4])
232 if self
.mynick
== "":
234 return "\nSession Start: %s\nSession Ident: %s\n"%(self
._now
2(row
[2]), self
.buffer)
236 return "%s *** %s is now known as %s\n"%(self
._now
(row
[2]), self
.mynick
, row
[4])
240 sender
= row
[3].split("!")
241 return "%s *** %s is now known as %s\n"%(self
._now
(row
[2]), sender
[0], row
[4])
245 sender
= row
[3].split("!")
246 return "%s *** %s sets mode: %s\n"%(self
._now
(row
[2]), sender
[0], " ".join(row
[4].split(" ")[1:]))
249 sender
= row
[3].split("!")
250 if self
.mynick
== "":
252 self
.mynick
= sender
[0]
253 return "\nSession Start: %s\nSession Ident: %s\n"%(self
._now
2(row
[2]), self
.buffer)
255 return "%s *** %s (%s) has joined %s\n"%(self
._now
(row
[2]), sender
[0], sender
[1], row
[4])
258 sender
= row
[3].split("!")
259 if sender
[0] == self
.mynick
:
261 return "Session Close: %s\n"%self
._now
2(row
[2])
263 return "%s *** %s (%s) has left %s\n"%(self
._now
(row
[2]), sender
[0], sender
[1], self
.buffer)
266 sender
= row
[3].split("!")
267 if sender
[0] == self
.mynick
:
269 return "Session Close: %s\n"%self
._now
2(row
[2])
271 return "%s *** %s (%s) Quit (%s)\n"%(self
._now
(row
[2]), sender
[0], sender
[1], row
[4])
274 sender
= row
[3].split("!")
275 msg
= row
[4].split(" ")
276 reason
= " ".join(msg
[1:])
277 return "%s *** %s was kicked by %s (%s)\n"%(self
._now
(row
[2]), msg
[0], sender
[0], reason
)
279 def server(self
, row
):
280 return "%s *** %s\n"%(self
._now
(row
[2]), row
[4])
282 def topicchange(self
, time
, user
, channel
, topic
):
283 return "%s *** %s changes topic to '%s'\n"%(self
._now
(time
), user
, topic
)
285 def topicjoin1(self
, time
, channel
, topic
):
286 return "%s *** Topic is '%s\0'\n"%(self
._now
(time
), topic
)
288 def topicjoin2(self
, time
, user
, logtime
):
289 return "%s *** Set by %s on %s\n"%(self
._now
(time
), user
, logtime
)
291 def mirc_color(num
, num2
=None):
292 newnum
= str(num
).rjust(2, "0")
294 return "%c%s"%(3, newnum
)
296 new2
= str(num2
).rjust(2, "0")
297 return "%c%s.%s"%(3, newnum
, new2
)
302 def __init__(self
, dbpath
=""):
303 """returns a new Logutil instance.
304 DB connection is shared between all logutils
305 if dbpath is empty, try default paths (should work on most unixes)"""
306 #if Logutil._con is None:
310 if Logutil
._dbpath
is None:
311 Logutil
._dbpath
= os
.path
.expanduser(dbpath
)
312 self
.dbpath
= os
.path
.expanduser(dbpath
)
313 #self.con = sqlite3.connect(os.path.expanduser(dbpath))
314 #print (Logutil._dbpath)
318 #TODO: POS Code, replace with something sane when sober
320 for path
in _conf
.dbpath
:
321 if os
.path
.isfile(os
.path
.expanduser(path
)):
322 if Logutil
._dbpath
is None:
323 Logutil
._dbpath
= os
.path
.expanduser(path
)
324 self
.dbpath
= os
.path
.expanduser(path
)
327 def _time_to_unix(self
, timestr
):
335 match
= re
.compile("(\d{2,4})-(\d{2}).(\d{2})").search(timestr
)
336 if not match
is None:
337 (year
, month
, day
) = match
.groups()
339 match
= re
.compile("(\d{2}):(\d{2}):(\d{2})").search(timestr
)
340 if not match
is None:
341 (hour
, minute
, second
) = match
.groups()
342 return time
.mktime((int(year
), int(month
), int(day
), int(hour
), int(minute
), int(second
), -1, 0, -1))
345 """for thread safety, we need to do many connects for now"""
346 con
= sqlite3
.connect(self
.dbpath
)
350 query
="""SELECT username FROM quasseluser"""
353 return [str(user
[0]) for user
in cur
.fetchall()]
355 def is_user(self
, username
):
357 return username
in self
.get_users()
361 def get_networks(self
, username
):
365 JOIN quasseluser ON network.userid = quasseluser.userid
369 cur
.execute(query
, (username
, ))
370 return [str(network
[0]) for network
in cur
.fetchall()]
372 def is_network(self
, username
, network
):
374 return network
in self
.get_networks(username
)
378 def get_buffers(self
, username
, network
):
382 JOIN network ON buffer.networkid = network.networkid
383 , quasseluser ON buffer.userid = quasseluser.userid
390 JOIN quasseluser ON network.userid = quasseluser.userid
396 for nw
in cur
.execute(nwquery
, (username
, )):
397 cur
.execute(query
, (username
, nw
[0]))
398 result
[str(nw
[0])] = [str(buffer[0]) for buffer in cur
.fetchall()]
400 cur
.execute(query
, (username
, network
))
401 result
= [str(buf
[0]) for buf
in cur
.fetchall()]
404 def is_buffer(self
, username
, network
, buffer):
406 return buffer in self
.get_buffers(username
, network
)
410 def getlog(self
, username
, network
, buffer, outstream
, fmt
=Logformat_mIRC(buffer), counter
=None):
412 outstream must support .write() for strings
413 fmt must be a Logformat child"""
416 SELECT type, flags, time, sender, message
418 JOIN sender ON backlog.senderid = sender.senderid
419 , buffer ON backlog.bufferid = buffer.bufferid
420 , network ON buffer.networkid = network.networkid
421 , quasseluser ON buffer.userid = quasseluser.userid
426 cur
.execute(query
, (username
, network
, buffer))
428 if not counter
is None:
429 counter
.set_max(len(all
))
432 if not counter
is None:
434 outstream
.write(fmt
.format(row
))
435 #print fmt.format(row),