2005-02-02 05:51:55 +01:00
###
# Copyright (c) 2004, Jeremiah Fincher
2012-09-01 16:16:48 +02:00
# Copyright (c) 2010, James McCoy
2021-08-01 21:47:37 +02:00
# Copyright (c) 2010-2021, The Limnoria Contributors
2005-02-02 05:51:55 +01:00
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# * Redistributions of source code must retain the above copyright notice,
# this list of conditions, and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions, and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author of this software nor the name of
# contributors to this software may be used to endorse or promote products
# derived from this software without specific prior written consent.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
###
import csv
import time
2018-06-27 11:10:14 +02:00
import codecs
2010-04-30 02:04:51 +02:00
import datetime
2005-02-02 05:51:55 +01:00
import supybot . log as log
import supybot . conf as conf
import supybot . utils as utils
from supybot . commands import *
2010-01-28 14:14:44 +01:00
import supybot . ircmsgs as ircmsgs
2005-02-02 05:51:55 +01:00
import supybot . ircutils as ircutils
import supybot . callbacks as callbacks
2010-10-17 15:36:26 +02:00
from supybot . i18n import PluginInternationalization , internationalizeDocstring
_ = PluginInternationalization ( ' Later ' )
2005-02-02 05:51:55 +01:00
2016-01-31 12:11:04 +01:00
class QueueIsFull ( Exception ) :
pass
2005-02-09 08:04:04 +01:00
class Later ( callbacks . Plugin ) :
2021-04-05 17:29:49 +02:00
"""
Used to do things later ; currently , it only allows the sending of
2005-02-02 05:51:55 +01:00
nick - based notes . Do note ( haha ! ) that these notes are * not * private
and don ' t even pretend to be; if you want such features, consider using the
2021-04-05 17:29:49 +02:00
Note plugin .
Use the ` ` later tell ` ` command to leave a message to a user .
If you sent the message by accident or want to cancel it ,
you can use the ` later undo ` command to remove the latest later ,
which you have sent .
You can also see the people who have notes waiting for them by using
the ` later notes ` command . If you specify a nickname in ` ` later notes ` `
command , you will see the notes , which are waiting for the nickname .
Privacy
- - - - - - -
As you probably noticed from above , this plugin isn ' t private.
Everyone can see notes sent by anyone and the laters are sent on channel
by default and as the " plugin help later " says : :
Used to do things later ; currently , it only allows the sending of nick - based notes . Do note ( haha ! ) that these notes are * not * private and don ' t even pretend to be; if you want such features, consider using the Note plugin.
The Note plugin identifies people by username instead of nickname
and allows only users to send notes .
The only people who are able to read notes are the sender , receiver ,
and the owner .
"""
2005-02-02 05:51:55 +01:00
def __init__ ( self , irc ) :
self . __parent = super ( Later , self )
self . __parent . __init__ ( irc )
self . _notes = ircutils . IrcDict ( )
self . wildcards = [ ]
self . filename = conf . supybot . directories . data . dirize ( ' Later.db ' )
self . _openNotes ( )
def die ( self ) :
self . _flushNotes ( )
def _flushNotes ( self ) :
fd = utils . file . AtomicFile ( self . filename )
writer = csv . writer ( fd )
2015-08-08 22:20:14 +02:00
for ( nick , notes ) in self . _notes . items ( ) :
2005-02-02 05:51:55 +01:00
for ( time , whence , text ) in notes :
writer . writerow ( [ nick , time , whence , text ] )
fd . close ( )
def _openNotes ( self ) :
try :
2018-06-27 11:10:14 +02:00
fd = codecs . open ( self . filename , encoding = ' utf8 ' )
2014-01-20 15:49:15 +01:00
except EnvironmentError as e :
2005-02-02 05:51:55 +01:00
self . log . warning ( ' Couldn \' t open %s : %s ' , self . filename , e )
return
reader = csv . reader ( fd )
for ( nick , time , whence , text ) in reader :
self . _addNote ( nick , whence , text , at = float ( time ) , maximum = 0 )
fd . close ( )
def _timestamp ( self , when ) :
#format = conf.supybot.reply.format.time()
2013-08-11 12:22:12 +02:00
diff = when - time . time ( )
2005-02-02 05:51:55 +01:00
try :
2013-08-11 12:22:12 +02:00
return utils . timeElapsed ( diff , seconds = False )
2005-02-02 05:51:55 +01:00
except ValueError :
2010-10-17 15:36:26 +02:00
return _ ( ' just now ' )
2005-02-07 18:26:42 +01:00
2005-02-02 05:51:55 +01:00
def _addNote ( self , nick , whence , text , at = None , maximum = None ) :
if at is None :
at = time . time ( )
if maximum is None :
maximum = self . registryValue ( ' maximum ' )
try :
notes = self . _notes [ nick ]
if maximum and len ( notes ) > = maximum :
2016-01-31 12:11:04 +01:00
raise QueueIsFull ( )
2005-02-02 05:51:55 +01:00
else :
notes . append ( ( at , whence , text ) )
except KeyError :
self . _notes [ nick ] = [ ( at , whence , text ) ]
2018-07-05 19:48:43 +02:00
if set ( ' ?*!@ ' ) & set ( nick ) :
if nick not in self . wildcards :
self . wildcards . append ( nick )
2005-02-02 05:51:55 +01:00
self . _flushNotes ( )
2005-02-07 18:26:42 +01:00
2010-04-30 02:04:51 +02:00
def _deleteExpired ( self ) :
expiry = self . registryValue ( ' messageExpiry ' )
curtime = time . time ( )
nickremovals = [ ]
2015-08-08 22:20:14 +02:00
for ( nick , notes ) in self . _notes . items ( ) :
2010-04-30 02:04:51 +02:00
removals = [ ]
for ( notetime , whence , text ) in notes :
td = datetime . timedelta ( seconds = ( curtime - notetime ) )
if td . days > expiry :
removals . append ( ( notetime , whence , text ) )
for note in removals :
notes . remove ( note )
if len ( notes ) == 0 :
nickremovals . append ( nick )
for nick in nickremovals :
del self . _notes [ nick ]
self . _flushNotes ( )
## Note: we call _deleteExpired from 'tell'. This means that it's possible
## for expired notes to remain in the database for longer than the maximum,
## if no tell's are called.
## However, the whole point of this is to avoid crud accumulation in the
## database, so it's fine that we only delete expired notes when we try
## adding new ones.
2010-10-17 15:36:26 +02:00
@internationalizeDocstring
2015-09-23 11:23:21 +02:00
def tell ( self , irc , msg , args , nicks , text ) :
""" <nick1[,nick2[,...]]> <text>
2005-02-02 05:51:55 +01:00
2015-09-23 11:23:21 +02:00
Tells each < nickX > < text > the next time < nickX > is seen . < nickX > can
2005-02-02 05:51:55 +01:00
contain wildcard characters , and the first matching nick will be
given the note .
"""
2010-04-30 02:04:51 +02:00
self . _deleteExpired ( )
2015-09-23 11:23:21 +02:00
validnicks = [ ]
2016-08-19 19:20:17 +02:00
for nick in set ( nicks ) : # Ignore duplicates
2015-09-23 11:23:21 +02:00
if ircutils . strEqual ( nick , irc . nick ) :
irc . error ( _ ( ' I can \' t send notes to myself. ' ) )
return
2018-07-05 19:48:43 +02:00
validnicks . append ( nick )
2015-09-23 11:23:21 +02:00
full_queues = [ ]
for validnick in validnicks :
try :
2019-03-06 15:29:09 +01:00
self . _addNote ( validnick , msg . prefix , text )
2016-01-31 12:11:04 +01:00
except QueueIsFull :
2015-09-23 11:23:21 +02:00
full_queues . append ( validnick )
if full_queues :
irc . error ( format (
_ ( ' These recipients \' message queue are already full: % L ' ) ,
full_queues ) )
else :
2005-02-02 05:51:55 +01:00
irc . replySuccess ( )
2018-07-05 19:48:43 +02:00
tell = wrap ( tell , [ commalist ( first ( ' nick ' , ' hostmask ' ) ) , ' text ' ] )
2005-02-02 05:51:55 +01:00
2010-10-17 15:36:26 +02:00
@internationalizeDocstring
2005-02-02 05:51:55 +01:00
def notes ( self , irc , msg , args , nick ) :
""" [<nick>]
If < nick > is given , replies with what notes are waiting on < nick > ,
otherwise , replies with the nicks that have notes waiting for them .
"""
if nick :
if nick in self . _notes :
notes = [ self . _formatNote ( when , whence , note )
for ( when , whence , note ) in self . _notes [ nick ] ]
irc . reply ( format ( ' % L ' , notes ) )
else :
2010-10-17 15:36:26 +02:00
irc . error ( _ ( ' I have no notes for that nick. ' ) )
2005-02-02 05:51:55 +01:00
else :
nicks = self . _notes . keys ( )
2005-02-07 18:26:42 +01:00
if nicks :
utils . sortBy ( ircutils . toLower , nicks )
2010-10-17 15:36:26 +02:00
irc . reply ( format ( _ ( ' I currently have notes waiting for % L. ' ) ,
2005-02-07 18:26:42 +01:00
nicks ) )
else :
2010-10-17 15:36:26 +02:00
irc . error ( _ ( ' I have no notes waiting to be delivered. ' ) )
2005-02-02 05:51:55 +01:00
notes = wrap ( notes , [ additional ( ' something ' ) ] )
2005-02-07 18:26:42 +01:00
2010-10-17 15:36:26 +02:00
@internationalizeDocstring
2007-10-23 05:33:50 +02:00
def remove ( self , irc , msg , args , nick ) :
""" <nick>
Removes the notes waiting on < nick > .
"""
try :
del self . _notes [ nick ]
self . _flushNotes ( )
irc . replySuccess ( )
except KeyError :
2010-10-17 15:36:26 +02:00
irc . error ( _ ( ' There were no notes for %r ' ) % nick )
2007-10-23 05:33:50 +02:00
remove = wrap ( remove , [ ( ' checkCapability ' , ' admin ' ) , ' something ' ] )
2011-07-16 13:59:49 +02:00
@internationalizeDocstring
def undo ( self , irc , msg , args , nick ) :
""" <nick>
Removes the latest note you sent to < nick > .
"""
if nick not in self . _notes :
irc . error ( _ ( ' There are no note waiting for %s . ' ) % nick )
return
self . _notes [ nick ] . reverse ( )
for note in self . _notes [ nick ] :
2019-03-06 15:29:09 +01:00
if ircutils . nickFromHostmask ( note [ 1 ] ) == msg . nick :
2011-07-16 13:59:49 +02:00
self . _notes [ nick ] . remove ( note )
if len ( self . _notes [ nick ] ) == 0 :
del self . _notes [ nick ]
self . _flushNotes ( )
irc . replySuccess ( )
return
irc . error ( _ ( ' There are no note from you waiting for %s . ' ) % nick )
undo = wrap ( undo , [ ' something ' ] )
2005-02-02 05:51:55 +01:00
def doPrivmsg ( self , irc , msg ) :
2010-01-28 14:14:44 +01:00
if ircmsgs . isCtcp ( msg ) and not ircmsgs . isAction ( msg ) :
return
2005-02-02 05:51:55 +01:00
notes = self . _notes . pop ( msg . nick , [ ] )
# Let's try wildcards.
removals = [ ]
for wildcard in self . wildcards :
2018-07-05 19:48:43 +02:00
if ircutils . hostmaskPatternEqual ( wildcard , msg . prefix ) :
2005-02-02 05:51:55 +01:00
removals . append ( wildcard )
notes . extend ( self . _notes . pop ( wildcard ) )
for removal in removals :
self . wildcards . remove ( removal )
if notes :
2015-05-19 00:50:26 +02:00
old_repliedto = msg . repliedTo
2005-02-02 05:51:55 +01:00
irc = callbacks . SimpleProxy ( irc , msg )
private = self . registryValue ( ' private ' )
for ( when , whence , note ) in notes :
s = self . _formatNote ( when , whence , note )
2014-01-13 18:27:52 +01:00
irc . reply ( s , private = private , prefixNick = not private )
2005-02-02 05:51:55 +01:00
self . _flushNotes ( )
2016-01-22 21:13:22 +01:00
msg . tag ( ' repliedTo ' , old_repliedto )
2005-02-02 05:51:55 +01:00
def _formatNote ( self , when , whence , note ) :
2019-03-06 15:29:09 +01:00
if not self . registryValue ( ' format.senderHostname ' ) :
whence = ircutils . nickFromHostmask ( whence )
2010-10-17 15:36:26 +02:00
return _ ( ' Sent %s : < %s > %s ' ) % ( self . _timestamp ( when ) , whence , note )
2010-04-09 19:34:39 +02:00
2010-04-11 22:25:07 +02:00
def doJoin ( self , irc , msg ) :
if self . registryValue ( ' tellOnJoin ' ) :
self . doPrivmsg ( irc , msg )
2010-10-26 09:32:12 +02:00
Later = internationalizeDocstring ( Later )
2005-02-02 05:51:55 +01:00
Class = Later
2006-02-11 16:52:51 +01:00
# vim:set shiftwidth=4 softtabstop=4 expandtab textwidth=79: