You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
7.8 KiB
188 lines
7.8 KiB
""" |
|
.. module:: Tell |
|
:synopsis: Deliver a message to a user when they're next seen |
|
|
|
.. moduleauthor:: Dave Pedu <dave@davepedu.com> |
|
|
|
""" |
|
|
|
from pyircbot.modulebase import ModuleBase, command, hook |
|
import datetime |
|
from time import mktime |
|
from pyircbot.modules.ModInfo import info |
|
|
|
|
|
class Tell(ModuleBase): |
|
def __init__(self, bot, moduleName): |
|
ModuleBase.__init__(self, bot, moduleName) |
|
|
|
self.db = None |
|
serviceProviders = self.bot.getmodulesbyservice("sqlite") |
|
if len(serviceProviders) == 0: |
|
self.log.error("Tell: Could not find a valid sqlite service provider") |
|
else: |
|
self.log.info("Tell: Selecting sqlite service provider: %s" % serviceProviders[0]) |
|
self.db = serviceProviders[0].opendb("tell.db") |
|
|
|
if not self.db.tableExists("tells"): |
|
self.log.info("Remind: Creating table: tells") |
|
self.db.query("""CREATE TABLE IF NOT EXISTS `tells` ( |
|
`id` INTEGER PRIMARY KEY, |
|
`sender` varchar(64), |
|
`channel` varchar(64), |
|
`when` INTEGER, |
|
`recip` varchar(64), |
|
`message` varchar(2048) |
|
) ;""").close() |
|
|
|
# Purge expired tells |
|
self.db.query("DELETE FROM `tells` WHERE `when`<?", |
|
(int(mktime(datetime.datetime.now().timetuple())) - self.config["maxage"],)).close() |
|
|
|
@hook("PRIVMSG", "JOIN") |
|
def showtell(self, msg, cmd): |
|
# Look for tells for this person |
|
c = self.db.query("SELECT * FROM `tells` WHERE `recip`=?", (msg.prefix.nick,)) |
|
tells = c.fetchall() |
|
c.close() |
|
for tell in tells: |
|
agostr = Tell.timesince(datetime.datetime.fromtimestamp(tell["when"])) |
|
recip = None |
|
if tell["channel"] == "": |
|
recip = msg.prefix.nick |
|
else: |
|
recip = tell["channel"] |
|
self.bot.act_PRIVMSG(recip, "%s: %s said %s ago: %s" % ( |
|
msg.prefix.nick, |
|
tell["sender"], |
|
agostr, |
|
tell["message"] |
|
)) |
|
# Delete |
|
self.db.query("DELETE FROM `tells` WHERE `id`=?", (tell["id"],)) |
|
|
|
@info("tell <person> <message> relay a message when the target is online", cmds=["tell"]) |
|
@command("tell", allow_private=True) |
|
def tellcmds(self, msg, cmd): |
|
if len(cmd.args) < 2: |
|
self.bot.act_PRIVMSG(msg.args[0], "%s: .tell <person> <message> - Tell someone something the next time " |
|
"they're seen. Example: .tell antiroach Do your homework!" % msg.prefix.nick) |
|
return |
|
|
|
recip = cmd.args[0] |
|
message = ' '.join(cmd.args[1:]).strip() |
|
|
|
c = self.db.query("SELECT COUNT(*) as `cnt` FROM `tells` WHERE `recip`=?;", (recip, )) |
|
user_total = c.fetchall()[0]['cnt'] |
|
c.close() |
|
|
|
if user_total >= self.config.get("max", 3): |
|
return |
|
|
|
self.db.query("INSERT INTO `tells` (`sender`, `channel`, `when`, `recip`, `message`) VALUES " |
|
"(?, ?, ?, ?, ?);", (msg.prefix.nick, |
|
msg.args[0] if "#" in msg.args[0] else "", |
|
int(mktime(datetime.datetime.now().timetuple())), |
|
recip, |
|
message)).close() |
|
|
|
self.bot.act_PRIVMSG(msg.args[0], "%s: I'll pass that along." % msg.prefix.nick) |
|
|
|
# Copyright (c) Django Software Foundation and individual contributors. |
|
# All rights reserved. |
|
# |
|
# Redistribution and use in source and binary forms, with or without |
|
# modification, are permitted provided that the following conditions are met: |
|
# |
|
# 1. Redistributions of source code must retain the above copyright notice, |
|
# this list of conditions and the following disclaimer. |
|
# |
|
# 2. 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. |
|
# |
|
# 3. Neither the name of Django nor the names of its contributors may be used |
|
# to endorse or promote products derived from this software without |
|
# specific prior written permission. |
|
# |
|
#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. |
|
|
|
@staticmethod |
|
def timesince(d, now=None): |
|
""" |
|
Takes two datetime objects and returns the time between d and now |
|
as a nicely formatted string, e.g. "10 minutes". If d occurs after now, |
|
then "0 minutes" is returned. |
|
|
|
Units used are years, months, weeks, days, hours, and minutes. |
|
Seconds and microseconds are ignored. Up to two adjacent units will be |
|
displayed. For example, "2 weeks, 3 days" and "1 year, 3 months" are |
|
possible outputs, but "2 weeks, 3 hours" and "1 year, 5 days" are not. |
|
|
|
Adapted from http://blog.natbat.co.uk/archive/2003/Jun/14/time_since |
|
""" |
|
chunks = ((60 * 60 * 24 * 365, ('year', 'years')), |
|
(60 * 60 * 24 * 30, ('month', 'months')), |
|
(60 * 60 * 24 * 7, ('week', 'weeks')), |
|
(60 * 60 * 24, ('day', 'days')), |
|
(60 * 60, ('hour', 'hours')), |
|
(60, ('minute', 'minutes'))) |
|
|
|
# Convert int or float (unix epoch) to datetime.datetime for comparison |
|
if isinstance(d, int) or isinstance(d, float): |
|
d = datetime.datetime.fromtimestamp(d) |
|
|
|
# Convert datetime.date to datetime.datetime for comparison. |
|
if not isinstance(d, datetime.datetime): |
|
d = datetime.datetime(d.year, d.month, d.day) |
|
if now and not isinstance(now, datetime.datetime): |
|
now = datetime.datetime(now.year, now.month, now.day) |
|
|
|
if not now: |
|
now = datetime.datetime.now() |
|
|
|
# ignore microsecond part of 'd' since we removed it from 'now' |
|
delta = now - (d - datetime.timedelta(0, 0, d.microsecond)) |
|
since = delta.days * 24 * 60 * 60 + delta.seconds |
|
if since <= 0: |
|
# d is in the future compared to now, stop processing. |
|
return u'0 ' + 'minutes' |
|
for i, (seconds, name) in enumerate(chunks): |
|
count = since // seconds |
|
if count != 0: |
|
break |
|
|
|
if count == 1: |
|
s = '%(number)d %(type)s' % {'number': count, 'type': name[0]} |
|
else: |
|
s = '%(number)d %(type)s' % {'number': count, 'type': name[1]} |
|
|
|
if i + 1 < len(chunks): |
|
# Now get the second item |
|
seconds2, name2 = chunks[i + 1] |
|
count2 = (since - (seconds * count)) // seconds2 |
|
if count2 != 0: |
|
if count2 == 1: |
|
s += ', %d %s' % (count2, name2[0]) |
|
else: |
|
s += ', %d %s' % (count2, name2[1]) |
|
return s |
|
|
|
@staticmethod |
|
def timeuntil(d, now=None): # not used? |
|
""" |
|
Like timesince, but returns a string measuring the time until |
|
the given time. |
|
""" |
|
if not now: |
|
now = datetime.datetime.now() |
|
return Tell.timesince(now, d)
|
|
|