Fix up this crummy plugin as well

This commit is contained in:
Dennis Kaarsemaker 2007-09-18 01:43:05 +02:00
parent 6ec9cd85da
commit 393fb71d98
5 changed files with 161 additions and 162 deletions

View File

@ -1,5 +1,5 @@
###
# Copyright (c) 2005,2006 Dennis Kaarsemaker
# Copyright (c) 2005-2007 Dennis Kaarsemaker
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
@ -18,15 +18,17 @@ Update the topic according to an iCal schedule
import supybot
import supybot.world as world
__version__ = "0.2"
__version__ = "0.3"
__author__ = supybot.Author("Dennis Kaarsemaker","Seveas","dennis@kaarsemaker.net")
__contributors__ = {}
__url__ = 'http://bugbot.ubuntulinux.nl/'
__url__ = 'http://ubotu.ubuntu-nl.org'
import config
reload(config)
import plugin
reload(plugin)
import ical
reload(ical)
if world.testing:
import test

View File

@ -1,5 +1,5 @@
###
# Copyright (c) 2005,2006 Dennis Kaarsemaker
# Copyright (c) 2005-2007 Dennis Kaarsemaker
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
@ -29,4 +29,4 @@ conf.registerChannelValue(conf.supybot.plugins.Webcal, 'topic',
conf.registerChannelValue(conf.supybot.plugins.Webcal, 'doTopic',
registry.Boolean(False,"""Whether to manage the topic"""))
conf.registerGlobalValue(conf.supybot.plugins.Webcal, 'defaultChannel',
registry.String('',"""Default channel for /msg replies"""))
registry.String('',"""Default channel to determine schedule for /msg replies"""))

View File

@ -1,6 +1,8 @@
#!/usr/bin/python
# Slightly modified version of the iCal module found at
# http://www.devoesquared.com/Software/iCal_Module
# http://www.random-ideas.net/Software/iCal_Module
# Original file doesn't come with a license but is public domain according to
# above website
import os
import os.path
@ -10,6 +12,8 @@ import time
import pytz # pytz can be found on http://pytz.sourceforge.net
SECONDS_PER_DAY=24*60*60
def seconds(timediff):
return SECONDS_PER_DAY * timediff.days + timediff.seconds
class ICalReader:
@ -132,6 +136,43 @@ class ICalEvent:
def startTime(self):
return self.startDate
def schedule(self, timezone=None):
if not timezone:
return "%s UTC: %s" % (self.startDate.strftime("%d %b %H:%M"), self.summary.replace('Meeting','').strip())
return "%s: %s" % (self.startDate.astimezone(pytz.timezone(timezone)).strftime("%d %b %H:%M"), self.summary.replace('Meeting','').strip())
def is_on(self):
return self.startDate < datetime.datetime.now(pytz.UTC) and self.endDate > datetime.datetime.now(pytz.UTC)
def has_passed(self):
return self.endDate < datetime.datetime.now(pytz.UTC)
def seconds_to_go(self):
return seconds(self.startDate - datetime.datetime.now(pytz.UTC))
def seconds_ago(self):
return seconds(datetime.datetime.now(pytz.UTC) - self.endDate)
def time_to_go(self):
if self.endDate < datetime.datetime.now(pytz.UTC):
return False
delta = self.startDate - datetime.datetime.now(pytz.UTC)
s = ''
if delta.days:
if delta.days != 1:
s = 's'
return '%d day%s' % (delta.days, s)
h = ''
if delta.seconds > 7200:
s = 's'
if delta.seconds > 3600:
h = '%d hour%s ' % (int(delta.seconds/3600),s)
s = ''
minutes = (delta.seconds % 3600) / 60
if minutes != 1:
s = 's'
return '%s%d minute%s' % (h,minutes,s)
class DateSet:
def __init__(self, startDate, endDate, rule):
self.startDate = startDate

View File

@ -23,29 +23,6 @@ import pytz
import ical
import datetime, shelve, re
import cPickle as pickle
reload(ical)
def _event_to_string(event, timezone):
if not timezone:
return "%s UTC: %s" % (event.startDate.strftime("%d %b %H:%M"), event.summary)
return "%s: %s" % (event.startDate.astimezone(pytz.timezone(timezone)).strftime("%d %b %H:%M"), event.summary)
def diff(delta):
s = ''
if delta.days:
if delta.days != 1:
s = 's'
return '%d day%s' % (delta.days, s)
h = ''
if delta.seconds > 7200:
s = 's'
if delta.seconds > 3600:
h = '%d hour%s ' % (int(delta.seconds/3600),s)
s = ''
minutes = (delta.seconds % 3600) / 60
if minutes != 1:
s = 's'
return '%s%d minute%s' % (h,minutes,s)
class Webcal(callbacks.Plugin):
"""@schedule <timezone>: display the schedule in your timezone"""
@ -54,113 +31,102 @@ class Webcal(callbacks.Plugin):
def __init__(self, irc):
callbacks.Privmsg.__init__(self, irc)
self.irc = irc
schedule.addPeriodicEvent(self._refresh_cache, 60 * 20, name=self.name())
schedule.addPeriodicEvent(self._autotopics, 60, name=self.name() + 'b')
try:
schedule.removeEvent(self.name())
except:
pass
try:
schedule.removeEvent(self.name() + 'b')
except:
pass
schedule.addPeriodicEvent(self.refresh_cache, 60 * 20, name=self.name())
schedule.addPeriodicEvent(self.autotopics, 60, name=self.name() + 'b')
self.cache = {}
self.subs = shelve.open('/home/dennis/ubotu/data/subscriptions.db')
self.firstevent = {}
def die(self):
schedule.removeEvent(self.name())
schedule.removeEvent(self.name() + 'b')
self.cache.clear()
self.subs.close()
def reset(self):
self.cache.clear()
def _filter(self, event, channel, now):
fword = self.registryValue('filter', channel)
if fword.lower() not in event.raw_data.lower():
return False
delta = event.endDate - now
return delta.days >= 0 or (delta.days == -1 and abs(delta).seconds < 30 * 60)
def _gettopic(self, url, channel, timezone=None, no_topic=False, num_events=6):
if url not in self.cache.keys():
self._refresh_cache(url)
now = datetime.datetime.now(pytz.UTC)
events = filter(lambda x: self._filter(x,channel,now),self.cache[url])[:num_events]
preamble = ''
if len(events):
# The standard slack of 30 minutes after the meeting will be an
# error if there are 2 conscutive meetings, so remove the first
# one in that case
if len(events) > 1 and events[1].startDate < now:
events = events[1:]
ev0 = events[0]
delta = abs(ev0.startDate - now)
if ev0.startDate < now or (delta.days == 0 and delta.seconds < 10 * 60):
preamble = 'Current meeting: %s' % ev0.summary.replace('Meeting','').strip()
if num_events == 1:
return preamble
events = events[1:]
preamble += ' | '
# n_e = 1 -> next meeting
# n_t = T -> n_t
if num_events == 1:
if not events:
return "No meetings scheduled"
return 'Next meeting: %s in %s' % (events[0].summary.replace('Meeting','').strip(), diff(delta))
events = map(lambda x: _event_to_string(x,timezone), events)
newtopic = ' | '.join(events).replace(' Meeting','')
template = self.registryValue('topic', channel)
if '%s' in template and not no_topic:
newtopic = template % str(newtopic)
return preamble + newtopic
def update(self, url):
data = utils.web.getUrl(url)
parser = ical.ICalReader(data)
self.cache[url] = parser.events
def _meeting_in_progress(self, url, channel):
return False
if url not in self.cache.keys():
self._refresh_cache(url)
now = datetime.datetime.now(pytz.UTC)
events = filter(lambda x: self._filter(x,channel,now),self.cache[url])[:1]
if len(events):
if len(events) and events[0].endDate < now:
events = events[1:]
if not events:
return False
ev0 = events[0]
#print now, ev0.startDate, ev0.endDate
delta = abs(ev0.startDate - now)
if ev0.startDate < now and delta.seconds > 10 * 5:
return True
return False
def _autotopics(self):
def refresh_cache(self):
for c in self.irc.state.channels:
url = self.registryValue('url', c)
if url:
self.update(url)
def autotopics(self):
for c in self.irc.state.channels:
url = self.registryValue('url', c)
if self._meeting_in_progress(url, c):
continue
if url and self.registryValue('doTopic', c):
newtopic = self._gettopic(url, c)
if newtopic and not (newtopic.strip() == self.irc.state.getTopic(c).strip()):
if url not in self.cache:
self.update(url)
events = self.filter(self.cache[url], c)
if events[0].is_on() and self.firstevent[c].summary == events[0].summary:
continue
newtopic = self.maketopic(c, template=self.registryValue('topic',c))
if newtopic.strip() != self.irc.state.getTopic(c).strip():
self.irc.queueMsg(ircmsgs.topic(c, newtopic))
def _refresh_cache(self,url=None):
if url:
data = utils.web.getUrl(url)
parser = ical.ICalReader(data)
self.cache[url] = parser.events
else:
for c in self.irc.state.channels:
url = self.registryValue('url', c)
if url:
data = utils.web.getUrl(url)
parser = ical.ICalReader(data)
self.cache[url] = parser.events
def filter(self, events, channel):
now = datetime.datetime.now(pytz.UTC)
fword = self.registryValue('filter', channel)
return [x for x in events if fword.lower() in x.raw_data.lower() and x.seconds_ago() < 1800]
def maketopic(self, c, tz=None, template='%s', num_events=6):
url = self.registryValue('url',c)
if url not in self.cache.keys():
self.update(url)
now = datetime.datetime.now(pytz.UTC)
events = self.filter(self.cache[url],c)[:num_events]
preamble = ''
if not len(events):
return template % "No meetings scheduled"
# The standard slack of 30 minutes after the meeting will be an
# error if there are 2 conscutive meetings, so remove the first
# one in that case
if len(events) > 1 and events[1].startDate < now:
events = events[1:]
ev0 = events[0]
if ev0.seconds_to_go < 600:
preamble = 'Current meeting: %s' % ev0.summary.replace('Meeting','').strip()
if num_events == 1:
return preamble + (template % '')
events = events[1:]
if num_events == 1:
ev = events[0]
return template % ('Next meeting: %s in %s' % (ev.summary.replace(' Meeting','').strip(), ev.time_to_go()))
events = [x.schedule(tz) for x in events]
return preamble + (template % ' | '.join(events))
# Now the commands
def topic(self, irc, msg, args):
url = self.registryValue('url', msg.args[0])
if not url or not self.registryValue('doTopic',channel=msg.args[0]):
c = msg.args[0]
url = self.registryValue('url', c)
if not url or not self.registryValue('doTopic',channel=c):
return
self._refresh_cache(url)
if self._meeting_in_progress(url, msg.args[0]):
irc.error("Can't update topic while a meeting is in progress")
self.update(url)
events = self.filter(self.cache[url], c)
if events[0].is_on():
irc.error("Won't update topic while a meeting is in progress")
return
newtopic = self._gettopic(url, msg.args[0])
# Only change topic if it actually is different!
if not (newtopic.strip() == irc.state.getTopic(msg.args[0]).strip()):
irc.queueMsg(ircmsgs.topic(msg.args[0], newtopic))
newtopic = self.maketopic(c, template=self.registryValue('topic',c))
if not (newtopic.strip() == irc.state.getTopic(c).strip()):
irc.queueMsg(ircmsgs.topic(c, newtopic))
topic = wrap(topic)
def _tzfilter(self, tz, ud):
@ -198,64 +164,54 @@ class Webcal(callbacks.Plugin):
return
tzs = filter(lambda x: self._tzfilter(x.lower(),tz.lower()), pytz.all_timezones)
if not tzs or 'gmt' in tz.lower():
irc.error('Unknown timezone: %s - Full list: http://bugbot.ubuntulinux.nl/timezones.html' % tz)
irc.error('Unknown timezone: %s - Full list: http://ubotu.ubuntu-nl.org/timezones.html' % tz)
return
newtopic = self.maketopic(c,tz=tzs[0])
events = self.filter(self.cache[url], msg.args[0])
if events[0].is_on():
irc.error('Please don\'t use @schedule during a meeting')
irc.reply('Schedule for %s: %s' % (tzs[0], newtopic), private=True)
else:
if self._meeting_in_progress(url, c):
irc.error('Please don\'t use @schedule during a meeting')
irc.reply('Schedule for %s: %s' % (tzs[0],self._gettopic(url, c, timezone=tzs[0], no_topic=True)), private=True)
else:
irc.reply('Schedule for %s: %s' % (tzs[0],self._gettopic(url, c, timezone=tzs[0], no_topic=True)))
irc.reply('Schedule for %s: %s' % (tzs[0], newtopic))
schedule = wrap(schedule, [additional('text')])
def now(self, irc, msg, args, tz):
""" Display the current time """
now = datetime.datetime.now(pytz.UTC)
if not tz:
tz = 'utc'
if irc.isChannel(msg.args[0]):
c = msg.args[0]
else:
c = self.registryValue('defaultChannel')
if not c:
return
url = self.registryValue('url', c)
if not url:
return
tzs = filter(lambda x: self._tzfilter(x.lower(),tz.lower()), pytz.all_timezones)
if not tzs or 'gmt' in tz.lower():
irc.error('Unknown timezone: %s - Full list: http://bugbot.ubuntulinux.nl/timezones.html' % tz)
irc.error('Unknown timezone: %s - Full list: http://ubotu.ubuntu-nl.org/timezones.html' % tz)
return
now = datetime.datetime.now(pytz.UTC)
newtopic = self.maketopic(c,tz=tzs[0],num_events=1)
events = self.filter(self.cache[url], msg.args[0])
newtopic = 'Current time in %s: %s - %s' % \
(tzs[0], datetime.datetime.now(pytz.UTC).astimezone(pytz.timezone(tzs[0])).strftime("%B %d %Y, %H:%M:%S"), newtopic)
if events[0].is_on():
irc.error('Please don\'t use @schedule during a meeting')
irc.reply(newtopic, private=True)
else:
if irc.isChannel(msg.args[0]):
c = msg.args[0]
else:
c = self.registryValue('defaultChannel')
if not c:
return
meeting = ''
url = self.registryValue('url', c)
if url:
meeting = self._gettopic(url, c, timezone=tzs[0], no_topic = True, num_events = 1)
if meeting:
meeting = ' - ' + meeting
irc.reply('Current time in %s: %s%s' % (tzs[0],
now.astimezone(pytz.timezone(tzs[0])).strftime("%B %d %Y, %H:%M:%S"),meeting))
irc.reply(newtopic)
now = wrap(now, [additional('text')])
time = now
def subscribe(self, irc, msg, args, regex):
try:
rx = re.compile(regex)
except:
irc.error("Invalid reguar expression")
return
self.subs[msg.nick.lower()] = (regex, rx)
self.subs.sync()
irc.reply("Subscription succesful")
subscribe = wrap(subscribe, ['text'])
def subscription(self, irc, msg, args):
n = msg.nick.lower()
if n in self.subs:
irc.reply("Your subscription: %s" % self.subs[n][0])
else:
irc.reply("You haven't subscribed yet")
subscription = wrap(subscription)
# Warn people that you manage the topic
def doTopic(self, irc, msg):
if not self.registryValue('doTopic'):
c = msg.args[0]
if not self.registryValue('doTopic', c):
return
irc.reply("The topic of %s is managed by me and filled with the contents of %s - please don't change manually" % (msg.args[0],url), private=True)
url = self.registryValue('url', c)
irc.reply("The topic of %s is managed by me and filled with the contents of %s - please don't change manually" %
(msg.args[0],url), private=True)
Class = Webcal

View File

@ -1,13 +1,13 @@
<html>
<head>
<title>Ubugtu and Ubotu</title>
<title>Ubotu</title>
<link rel="stylesheet" href="/bot.css" />
</head>
<body>
<div class="home">
<h1>Timezones <img src="xchat.png" alt="Hi!" /></h1>
<p>
These are all timezones known by Ubugtu. You don't need to use the full
These are all timezones known by Uboto. You don't need to use the full
zone name, the last part is enough. When this is ambiguous, the
alphabetic first timezone will be used. Timezones are not case sensitive
</p>