le robot eo
This commit is contained in:
commit
c9d1675946
|
@ -0,0 +1,255 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import caldav
|
||||
from datetime import datetime, date, timedelta
|
||||
from dateutil import rrule
|
||||
import icalendar
|
||||
import logging
|
||||
from pytz import timezone
|
||||
import sleekxmpp
|
||||
import sys
|
||||
|
||||
LOGIN = sys.argv[1]
|
||||
PASSWORD = sys.argv[2]
|
||||
|
||||
JID = '%s@im.libre-entreprise.com' % LOGIN
|
||||
ROOM = 'test@conference.im.libre-entreprise.com'
|
||||
NICK = 'robeo'
|
||||
|
||||
TZ = timezone('Europe/Paris')
|
||||
USERS = ('bdauvergne', 'tnoel', 'pcros', 'mates', 'vclaudet', 'bmallet', 'eshowk', 'smihai',
|
||||
'jkouka', 'ecazenave', 'fpeters', 'csiraut', 'pmarillonnet', 'slaget')
|
||||
CALENDARS_URL = ['https://calendar.entrouvert.org/groupdav.php/%s/calendar/' %
|
||||
user for user in USERS]
|
||||
DAV_URL = 'https://%s:%s@calendar.entrouvert.org/groupdav.php/' % (LOGIN, PASSWORD)
|
||||
|
||||
|
||||
# egroupware
|
||||
|
||||
def get_attendees(ical_component):
|
||||
ret = []
|
||||
for attendee in ical_component.get('attendee') or []:
|
||||
cn = attendee.params.get('cn')
|
||||
if '@' in attendee:
|
||||
ret.append('%s <%s>' % (cn, attendee))
|
||||
else:
|
||||
ret.append('%s' % cn)
|
||||
return sorted(ret, key=lambda s: '@' in s)
|
||||
|
||||
|
||||
def get_all_start_end(ical_component):
|
||||
dtstart = ical_component.get('dtstart')
|
||||
dtend = ical_component.get('dtend')
|
||||
if not dtstart or not dtend:
|
||||
return []
|
||||
dtstart = dtstart.dt
|
||||
dtend = dtend.dt
|
||||
if not isinstance(dtstart, datetime): # date -> datetime
|
||||
dtstart = datetime(dtstart.year, dtstart.month, dtstart.day, tzinfo=TZ)
|
||||
if not isinstance(dtend, datetime): # date -> datetime (end of day)
|
||||
dtend = datetime(dtend.year, dtend.month, dtend.day, tzinfo=TZ) - timedelta(seconds=1)
|
||||
ical_rrule = ical_component.get('rrule')
|
||||
if not ical_rrule:
|
||||
return [(dtstart, dtend)]
|
||||
|
||||
# fasten your seat belt...
|
||||
rrule_string = ical_rrule.to_ical().decode('utf-8')
|
||||
rules = rrule.rrulestr(rrule_string, dtstart=dtstart, forceset=True)
|
||||
excludes = ical_component.get('exdate')
|
||||
if excludes:
|
||||
if not isinstance(excludes, list):
|
||||
excludes = [excludes]
|
||||
for exclude in excludes:
|
||||
for dts in exclude.dts:
|
||||
rules.exdate(dts.dt)
|
||||
delta = dtend - dtstart
|
||||
start = datetime.now(tz=TZ) - timedelta(days=1)
|
||||
end = start + timedelta(days=31)
|
||||
return [(dtstart, dtstart + delta) for dtstart in rules.between(start, end)]
|
||||
|
||||
|
||||
def get_events(ical_component, owners):
|
||||
if ical_component.name != 'VEVENT':
|
||||
return {}
|
||||
uid = '%s' % ical_component.get('uid')
|
||||
attendees = get_attendees(ical_component) or owners
|
||||
events = {}
|
||||
for dtstart, dtend in get_all_start_end(ical_component):
|
||||
events['%s#%s-%s' % (uid, dtstart.isoformat(), dtend.isoformat())] = {
|
||||
'summary': '%s' % ical_component.get('summary'),
|
||||
'attendees': attendees,
|
||||
'dtstart': dtstart,
|
||||
'dtend': dtend,
|
||||
}
|
||||
return events
|
||||
|
||||
|
||||
_future_events_cache = None
|
||||
def get_future_events(force_update=False):
|
||||
global _future_events_cache
|
||||
if not force_update and _future_events_cache:
|
||||
return _future_events_cache
|
||||
client = caldav.DAVClient(DAV_URL)
|
||||
start_date = datetime.today() - timedelta(1)
|
||||
events = {}
|
||||
for url in CALENDARS_URL:
|
||||
calendar = caldav.objects.Calendar(client, url)
|
||||
owners = calendar.get_properties([caldav.elements.cdav.CalendarDescription()]).values()
|
||||
for event in calendar.date_search(start=start_date):
|
||||
ical = icalendar.Calendar.from_ical(event.data)
|
||||
for component in ical.walk():
|
||||
events.update(get_events(component, owners))
|
||||
_future_events_cache = sorted(events.values(), key=lambda v: v['dtstart'])
|
||||
return _future_events_cache
|
||||
|
||||
|
||||
def get_today_events(force_update=False):
|
||||
today = date.today()
|
||||
events = get_future_events(force_update=force_update)
|
||||
return [event for event in events
|
||||
if event['dtstart'].date() <= today <= event['dtend'].date()]
|
||||
|
||||
|
||||
def display_events(events):
|
||||
ret = []
|
||||
for event in events:
|
||||
dtstart, dtend = event['dtstart'], event['dtend']
|
||||
if dtstart.date() == dtend.date():
|
||||
s = '%2d/%.2d' % (dtstart.day, dtstart.month)
|
||||
if (dtstart.hour, dtstart.minute) != (0, 0):
|
||||
s += ' de %2dh%.2d à %2dh%.2d' % (dtstart.hour, dtstart.minute,
|
||||
dtend.hour, dtend.minute)
|
||||
else:
|
||||
s = 'du %d/%d' % (dtstart.day, dtstart.month)
|
||||
if (dtstart.hour, dtstart.minute, dtend.hour, dtend.minute) != (0, 0, 23, 59):
|
||||
s += ' %2dh%.2d' % (dtstart.hour, dtstart.minute)
|
||||
s += ' au %d/%d' % (dtend.day, dtend.month)
|
||||
if (dtstart.hour, dtstart.minute, dtend.hour, dtend.minute) != (0, 0, 23, 59):
|
||||
s += ' %2dh%.2d' % (dtend.hour, dtend.minute)
|
||||
s += ' : %s — %s' % (event['summary'], ', '.join(event['attendees']))
|
||||
ret.append(s)
|
||||
return '\n'.join(ret)
|
||||
|
||||
|
||||
# xmpp robot
|
||||
|
||||
class Robeo(sleekxmpp.ClientXMPP):
|
||||
|
||||
"""
|
||||
A simple SleekXMPP bot that will greets those
|
||||
who enter the room, and acknowledge any messages
|
||||
that mentions the bot's nickname.
|
||||
"""
|
||||
|
||||
def __init__(self, jid, password, room, nick):
|
||||
sleekxmpp.ClientXMPP.__init__(self, jid, password)
|
||||
|
||||
self.room = room
|
||||
self.nick = nick
|
||||
|
||||
# The session_start event will be triggered when
|
||||
# the bot establishes its connection with the server
|
||||
# and the XML streams are ready for use. We want to
|
||||
# listen for this event so that we we can initialize
|
||||
# our roster.
|
||||
self.add_event_handler("session_start", self.start)
|
||||
|
||||
# The groupchat_message event is triggered whenever a message
|
||||
# stanza is received from any chat room. If you also also
|
||||
# register a handler for the 'message' event, MUC messages
|
||||
# will be processed by both handlers.
|
||||
self.add_event_handler("groupchat_message", self.muc_message)
|
||||
|
||||
# The groupchat_presence event is triggered whenever a
|
||||
# presence stanza is received from any chat room, including
|
||||
# any presences you send yourself. To limit event handling
|
||||
# to a single room, use the events muc::room@server::presence,
|
||||
# muc::room@server::got_online, or muc::room@server::got_offline.
|
||||
self.add_event_handler("muc::%s::got_online" % self.room,
|
||||
self.muc_online)
|
||||
|
||||
def start(self, event):
|
||||
"""
|
||||
Process the session_start event.
|
||||
|
||||
Typical actions for the session_start event are
|
||||
requesting the roster and broadcasting an initial
|
||||
presence stanza.
|
||||
|
||||
Arguments:
|
||||
event -- An empty dictionary. The session_start
|
||||
event does not provide any additional
|
||||
data.
|
||||
"""
|
||||
self.get_roster()
|
||||
self.send_presence()
|
||||
self.plugin['xep_0045'].joinMUC(self.room, self.nick, wait=True)
|
||||
|
||||
def muc_message(self, msg):
|
||||
"""
|
||||
Process incoming message stanzas from any chat room. Be aware
|
||||
that if you also have any handlers for the 'message' event,
|
||||
message stanzas may be processed by both handlers, so check
|
||||
the 'type' attribute when using a 'message' event handler.
|
||||
|
||||
Whenever the bot's nickname is mentioned, respond to
|
||||
the message.
|
||||
|
||||
IMPORTANT: Always check that a message is not from yourself,
|
||||
otherwise you will create an infinite loop responding
|
||||
to your own messages.
|
||||
|
||||
This handler will reply to messages that mention
|
||||
the bot's nickname.
|
||||
|
||||
Arguments:
|
||||
msg -- The received message stanza. See the documentation
|
||||
for stanza objects and the Message stanza to see
|
||||
how it may be used.
|
||||
"""
|
||||
for_me = msg['body'].startswith(('%s:' % self.nick, '@%s' % self.nick))
|
||||
if for_me and msg['mucnick'] != self.nick:
|
||||
self.show_events()
|
||||
|
||||
def muc_online(self, presence):
|
||||
"""
|
||||
Process a presence stanza from a chat room. In this case,
|
||||
presences from users that have just come online are
|
||||
handled by sending a welcome message that includes
|
||||
the user's nickname and role in the room.
|
||||
|
||||
Arguments:
|
||||
presence -- The received presence stanza. See the
|
||||
documentation for the Presence stanza
|
||||
to see how else it may be used.
|
||||
"""
|
||||
if presence['muc']['nick'] != self.nick:
|
||||
self.send_message(mto=presence['from'].bare,
|
||||
mbody="Hello, %s %s" % (presence['muc']['role'],
|
||||
presence['muc']['nick']),
|
||||
mtype='groupchat')
|
||||
|
||||
def show_events(self):
|
||||
self.send_message(mto=self.room,
|
||||
mbody='/me interroge calendar, un peu de patience ...',
|
||||
mtype='groupchat')
|
||||
events = display_events(get_today_events())
|
||||
self.send_message(mto=self.room,
|
||||
mbody=events,
|
||||
mtype='groupchat')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
logging.basicConfig(level=logging.INFO, format='%(levelname)-8s %(message)s')
|
||||
|
||||
xmpp = Robeo(JID, PASSWORD, ROOM, NICK)
|
||||
xmpp.register_plugin('xep_0030') # Service Discovery
|
||||
xmpp.register_plugin('xep_0045') # Multi-User Chat
|
||||
xmpp.register_plugin('xep_0199') # XMPP Ping
|
||||
|
||||
# Connect to the XMPP server and start processing XMPP stanzas.
|
||||
if xmpp.connect(('labs.libre-entreprise.org', 5222)):
|
||||
xmpp.process(block=True)
|
||||
print("Done")
|
||||
else:
|
||||
print("Unable to connect.")
|
Reference in New Issue