]>
jfr.im git - irc/rizon/acid.git/blob - pyva/src/main/python/quotes/quotes.py
1 #!/usr/bin/python pseudoserver.py
3 # based on psm_limitserv.py written by celestin - martin <martin@rizon.net>
8 from istring
import istring
9 from pseudoclient
import sys_log
, sys_options
, sys_channels
, inviteable
17 import cmd_admin
, sys_auth
21 inviteable
. InviteablePseudoclient
25 def start_threads ( self
):
30 def bind_function ( self
, function
):
31 func
= types
. MethodType ( function
, self
, quotes
)
32 setattr ( quotes
, function
.__ name
__ , func
)
35 def bind_admin_commands ( self
):
36 list = cmd_admin
. get_commands ()
37 self
. commands_admin
= []
40 self
. commands_admin
. append (( command
, { 'permission' : 'j' , 'callback' : self
. bind_function ( list [ command
][ 0 ]),
41 'usage' : list [ command
][ 1 ]}))
44 AcidPlugin
.__ init
__ ( self
)
47 self
. log
= logging
. getLogger ( __name__
)
50 self
. nick
= istring ( self
. config
. get ( 'quotes' , 'nick' ))
51 except Exception , err
:
52 self
. log
. exception ( "Error reading 'quotes:nick' configuration option: %s " % err
)
56 self
. chan
= istring ( self
. config
. get ( 'quotes' , 'channel' ))
57 except Exception , err
:
58 self
. log
. exception ( "Error reading 'quotes:channel' configuration option: %s " % err
)
61 self
. bind_admin_commands ()
65 self
. dbp
. execute ( "CREATE TABLE IF NOT EXISTS quotes_chans (id INT(10) NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(200) NOT NULL, UNIQUE KEY(name)) ENGINE=MyISAM;" )
66 self
. dbp
. execute ( "CREATE TABLE IF NOT EXISTS quotes_quotes (id INT(10) NOT NULL, quote VARCHAR(512) NOT NULL, nick VARCHAR(30) NOT NULL, time TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL, channel INT NOT NULL, KEY `chan_index` (`channel`)) ENGINE=MyISAM;" )
67 except Exception , err
:
68 self
. log
. exception ( "Error creating table for quotes module ( %s )" % err
)
72 AcidPlugin
. start ( self
)
73 inviteable
. InviteablePseudoclient
. start ( self
)
75 self
. options
= sys_options
. OptionManager ( self
)
76 self
. elog
= sys_log
. LogManager ( self
)
77 self
. auth
= sys_auth
. QuotesAuthManager ( self
)
78 self
. channels
= sys_channels
. ChannelManager ( self
)
79 except Exception , err
:
80 self
. log
. exception ( 'Error initializing subsystems for quotes module ( %s )' % err
)
83 for channel
in self
. channels
. list_valid ():
84 self
. join ( channel
. name
)
86 self
. elog
. debug ( 'Joined channels.' )
90 except Exception , err
:
91 self
. log
. exception ( 'Error starting threads for quotes module ( %s )' % err
)
94 self
. initialized
= True
95 self
. elog
. debug ( 'Started threads.' )
99 for channel
in self
. channels
. list_valid ():
100 self
. join ( channel
. name
)
102 self
. elog
. debug ( 'Joined channels.' )
105 if hasattr ( self
, 'auth' ):
108 if hasattr ( self
, 'channels' ):
110 self
. channels
. force ()
113 self
. channels
. db_close ()
115 if hasattr ( self
, 'options' ):
120 self
. options
. db_close ()
122 def join ( self
, channel
):
123 me
= self
. inter
. findUser ( self
. nick
)
125 self
. dbp
. execute ( "INSERT IGNORE INTO quotes_chans(name) VALUES( %s )" , ( str ( channel
),))
127 def part ( self
, channel
):
128 me
= self
. inter
. findUser ( self
. nick
)
131 self
. dbp
. execute ( "DELETE FROM quotes_quotes WHERE channel= %s " , ( self
. get_cid ( channel
),))
132 self
. dbp
. execute ( "DELETE FROM quotes_chans WHERE name= %s " , ( str ( channel
),))
134 def msg ( self
, target
, message
):
136 self
. inter
. privmsg ( self
. nick
, target
, format_ascii_irc ( message
))
138 def multimsg ( self
, target
, count
, intro
, separator
, pieces
, outro
= '' ):
141 while cur
< len ( pieces
):
142 self
. msg ( target
, intro
+ separator
. join ( pieces
[ cur
: cur
+ count
]) + outro
)
145 def notice ( self
, target
, message
):
147 self
. inter
. notice ( self
. nick
, target
, format_ascii_irc ( message
))
149 def get_cid ( self
, cname
):
150 """Fetches the channel id for a given channel name."""
151 self
. dbp
. execute ( "SELECT id FROM quotes_chans WHERE name = %s " , ( str ( cname
),))
152 cid
= self
. dbp
. fetchone ()[ 0 ]
155 def read_quote ( self
, qid
, cid
, cname
):
156 """Fetches the quote id qid from the channel with id cid and displays
157 the result in the channel cname. cname is to be passed on so we can
158 avoid querying the db about the name, which we have in all scenarios
159 where read_quote is called anyway."""
161 num
= self
. dbp
. execute ( "SELECT nick, quote, time FROM quotes_quotes WHERE id = %s AND channel = %s " ,
165 self
. msg ( cname
, "[Quote] " + qid
+ " does not exist!" )
168 nick
, quote
, time
= self
. dbp
. fetchone ()
169 tdelta
= int (( datetime
. now () - time
). total_seconds ())
171 years
= weeks
= days
= hours
= minutes
= 0
173 while tdelta
> 31540000 :
176 while tdelta
> 604800 :
179 while tdelta
> 86400 :
190 tdeltastr
+= str ( years
) + " year"
195 tdeltastr
+= str ( weeks
) + " week"
200 tdeltastr
+= str ( days
) + " day"
205 tdeltastr
+= str ( hours
) + " hour"
210 tdeltastr
+= str ( minutes
) + " minute"
215 tdeltastr
+= str ( tdelta
) + " second"
219 self
. msg ( cname
, "[Quote] # %s added by %s %s ago." % ( qid
, nick
, tdeltastr
))
220 self
. inter
. privmsg ( self
. nick
, cname
, '[Quote] %s ' % quote
)
223 def onPrivmsg ( self
, source
, target
, message
):
224 # Parse ADD/DEL requests
225 if not self
. initialized
:
229 # if inviteable didn't catch the command, it means we can handle it here instead
230 if not inviteable
. InviteablePseudoclient
. onPrivmsg ( self
, source
, target
, message
):
233 myself
= self
. inter
. findUser ( self
. nick
)
236 userinfo
= self
. inter
. findUser ( source
)
237 sender
= userinfo
[ 'nick' ]
239 msg
= message
. strip ()
240 index
= msg
. find ( ' ' )
246 command
= msg
[: index
]
247 arg
= msg
[ index
+ 1 :]
249 command
= command
. lower ()
251 if self
. channels
. is_valid ( channel
) and command
. startswith ( "." ): # a channel message
252 command
= command
[ 1 :]
253 if command
== 'help' and arg
== '' :
254 self
. notice ( sender
, "Quotes: .help quotes - for quote commands." )
255 elif command
== 'help' and arg
. startswith ( 'quotes' ):
256 self
. notice ( sender
, "Quotes: .quote add <quote> - adds given quote to database, must have voice or higher on channel." )
257 self
. notice ( sender
, "Quotes: .quote del <number> - removes quote number from database, must be channel founder." )
258 self
. notice ( sender
, "Quotes: .quote search <word> - searches database for given word." )
259 self
. notice ( sender
, "Quotes: .quote read <number> - messages quote matching given number." )
260 self
. notice ( sender
, "Quotes: .quote random - messages a random quote." )
261 self
. notice ( sender
, "Quotes: .quote total - messages number of quotes in database." )
262 elif command
== 'quote' :
263 args
= arg
. split ( ' ' )
264 cid
= self
. get_cid ( channel
)
267 c
= self
. inter
. findChannel ( channel
)
268 # Voice is the lowest rank, i.e., we can just check for emptiness
269 if not c
or c
. getModes ( sender
) == "" :
270 self
. notice ( sender
, "You must have voice or higher on the channel to add quotes." )
272 quote
= istring ( ' ' . join ( args
[ 1 :]))
274 # Extremely inefficient. If possible, somehow merge into
275 # just one MySQL query
276 # MAX(id) may return NULL if (and only if) there are no results, i.e., 0
277 # quotes. Work around that with IFNULL(expr,elsevalue).
278 self
. dbp
. execute ( "SELECT IFNULL(MAX(id)+1,1) FROM quotes_quotes WHERE channel = %s " , ( cid
,))
279 qid
= self
. dbp
. fetchone ()[ 0 ]
281 self
. dbp
. execute ( "INSERT INTO quotes_quotes(id, quote, nick, channel) VALUES( %s , %s , %s , %s )" ,
282 ( qid
, str ( quote
), sender
, cid
))
284 self
. msg ( channel
, "[Quote] Added quote # %d by %s " % ( qid
, sender
))
285 self
. inter
. privmsg ( self
. nick
, channel
, '[Quote] %s ' % quote
)
288 if len ( args
) > 1 and not args
[ 1 ]. isdigit ():
289 self
. notice ( sender
, args
[ 1 ] + " is not a valid quote number." )
292 self
. notice ( sender
, "Checking if you are the channel founder." )
293 self
. auth
. request ( sender
, channel
, 'delete_quote' + args
[ 1 ])
295 if args
[ 0 ] == 'search' :
299 num
= self
. dbp
. execute ( "SELECT id FROM quotes_quotes WHERE channel = %s AND quote LIKE CONCAT(' %% ', %s ,' %% ')" ,
301 res
= self
. dbp
. fetchall ()
303 self
. msg ( channel
, "[Quote] No quotes found." )
308 ids
. append ( str ( row
[ 0 ]))
309 self
. msg ( channel
, "[Quote] %d matches found: # %s " % ( num
, ',' . join ( ids
)))
312 self
. read_quote ( res
[ 0 ][ 0 ], cid
, channel
)
314 if args
[ 0 ] == 'read' :
318 self
. read_quote ( args
[ 1 ], cid
, channel
)
320 if args
[ 0 ] == 'random' :
321 self
. dbp
. execute ( "SELECT id FROM quotes_quotes WHERE channel = %s ORDER BY RAND() LIMIT 1" , ( cid
,))
322 res
= self
. dbp
. fetchall ()
325 self
. msg ( channel
, "[Quote] No quotes found!" )
328 self
. read_quote ( res
[ 0 ][ 0 ], cid
, channel
)
330 if args
[ 0 ] == 'total' :
331 self
. dbp
. execute ( "SELECT COUNT(id) FROM quotes_quotes WHERE channel = %s " , ( cid
,))
332 qtotal
= self
. dbp
. fetchone ()[ 0 ]
335 self
. msg ( channel
, "[Quote] %d quotes in total" % qtotal
)
337 self
. msg ( channel
, "[Quote] %d quote in total" % qtotal
)
339 if args
[ 0 ] == 'last' :
340 self
. dbp
. execute ( "SELECT id FROM quotes_quotes where channel = %s ORDER BY id DESC LIMIT 1" , ( cid
,))
341 res
= self
. dbp
. fetchall ()
344 self
. msg ( channel
, "[Quote] No quotes found!" )
347 self
. read_quote ( res
[ 0 ][ 0 ], cid
, channel
)
351 def delete_quote ( self
, channel
, user
, qid
):
352 cid
= self
. get_cid ( channel
)
354 self
. dbp
. execute ( "DELETE FROM quotes_quotes WHERE id = %s AND channel = %s " , ( qid
, cid
))
355 self
. notice ( user
, "Deleted quote # %d " % qid
)
358 def onChanModes ( self
, prefix
, channel
, modes
):
359 if not self
. initialized
:
362 if not modes
== '-z' :
365 if channel
in self
. channels
:
366 self
. channels
. remove ( channel
)
368 self
. dbp
. execute ( "DELETE FROM quotes_quotes WHERE channel = %s " , ( self
. get_cid ( channel
),))
371 self
. dbp
. execute ( "DELETE FROM quotes_chans WHERE name = %s " , ( channel
,))
372 self
. elog
. request ( 'Channel @b %s @b was dropped. Deleting it.' % channel
)
374 def getCommands ( self
):
375 return self
. commands_admin