diff options
-rw-r--r-- | sleekxmpp/plugins/xep_0202.py | 117 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0202/__init__.py | 11 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0202/stanza.py | 126 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0202/time.py | 90 |
4 files changed, 227 insertions, 117 deletions
diff --git a/sleekxmpp/plugins/xep_0202.py b/sleekxmpp/plugins/xep_0202.py deleted file mode 100644 index 3b31c97a..00000000 --- a/sleekxmpp/plugins/xep_0202.py +++ /dev/null @@ -1,117 +0,0 @@ -"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2010 Nathanael C. Fritz
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-from datetime import datetime, tzinfo
-import logging
-import time
-
-from . import base
-from .. stanza.iq import Iq
-from .. xmlstream.handler.callback import Callback
-from .. xmlstream.matcher.xpath import MatchXPath
-from .. xmlstream import ElementBase, ET, JID, register_stanza_plugin
-
-
-log = logging.getLogger(__name__)
-
-
-class EntityTime(ElementBase):
- name = 'time'
- namespace = 'urn:xmpp:time'
- plugin_attrib = 'entity_time'
- interfaces = set(('tzo', 'utc'))
- sub_interfaces = set(('tzo', 'utc'))
-
- #def get_tzo(self):
- # TODO: Right now it returns a string but maybe it should
- # return a datetime.tzinfo object or maybe a datetime.timedelta?
- #pass
-
- def set_tzo(self, tzo):
- if isinstance(tzo, tzinfo):
- td = datetime.now(tzo).utcoffset() # What if we are faking the time? datetime.now() shouldn't be used here'
- seconds = td.seconds + td.days * 24 * 3600
- sign = ('+' if seconds >= 0 else '-')
- minutes = abs(seconds // 60)
- tzo = '{sign}{hours:02d}:{minutes:02d}'.format(sign=sign, hours=minutes//60, minutes=minutes%60)
- elif not isinstance(tzo, str):
- raise TypeError('The time should be a string or a datetime.tzinfo object.')
- self._set_sub_text('tzo', tzo)
-
- def get_utc(self):
- # Returns a datetime object instead the string. Is this a good idea?
- value = self._get_sub_text('utc')
- if '.' in value:
- return datetime.strptime(value, '%Y-%m-%dT%H:%M:%S.%fZ')
- else:
- return datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
-
- def set_utc(self, tim=None):
- if isinstance(tim, datetime):
- if tim.utcoffset():
- tim = tim - tim.utcoffset()
- tim = tim.strftime('%Y-%m-%dT%H:%M:%SZ')
- elif isinstance(tim, time.struct_time):
- tim = time.strftime('%Y-%m-%dT%H:%M:%SZ', tim)
- elif not isinstance(tim, str):
- raise TypeError('The time should be a string or a datetime.datetime or time.struct_time object.')
-
- self._set_sub_text('utc', tim)
-
-
-class xep_0202(base.base_plugin):
- """
- XEP-0202 Entity Time
- """
- def plugin_init(self):
- self.description = "Entity Time"
- self.xep = "0202"
-
- self.xmpp.registerHandler(
- Callback('Time Request',
- MatchXPath('{%s}iq/{%s}time' % (self.xmpp.default_ns,
- EntityTime.namespace)),
- self.handle_entity_time_query))
- register_stanza_plugin(Iq, EntityTime)
-
- self.xmpp.add_event_handler('entity_time_request', self.handle_entity_time)
-
-
- def post_init(self):
- base.base_plugin.post_init(self)
-
- self.xmpp.plugin['xep_0030'].add_feature('urn:xmpp:time')
-
- def handle_entity_time_query(self, iq):
- if iq['type'] == 'get':
- log.debug("Entity time requested by %s" % iq['from'])
- self.xmpp.event('entity_time_request', iq)
- elif iq['type'] == 'result':
- log.debug("Entity time result from %s" % iq['from'])
- self.xmpp.event('entity_time', iq)
-
- def handle_entity_time(self, iq):
- iq = iq.reply()
- iq.enable('entity_time')
- tzo = time.strftime('%z') # %z is not on all ANSI C libraries
- tzo = tzo[:3] + ':' + tzo[3:]
- iq['entity_time']['tzo'] = tzo
- iq['entity_time']['utc'] = datetime.utcnow()
- iq.send()
-
- def get_entity_time(self, jid):
- iq = self.xmpp.makeIqGet()
- iq.enable('entity_time')
- iq.attrib['to'] = jid
- iq.attrib['from'] = self.xmpp.boundjid.full
- id = iq.get('id')
- result = iq.send()
- if result and result is not None and result.get('type', 'error') != 'error':
- return {'utc': result['entity_time']['utc'], 'tzo': result['entity_time']['tzo']}
- else:
- return False
diff --git a/sleekxmpp/plugins/xep_0202/__init__.py b/sleekxmpp/plugins/xep_0202/__init__.py new file mode 100644 index 00000000..82338d3a --- /dev/null +++ b/sleekxmpp/plugins/xep_0202/__init__.py @@ -0,0 +1,11 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +from sleekxmpp.plugins.xep_0202 import stanza +from sleekxmpp.plugins.xep_0202.stanza import EntityTime +from sleekxmpp.plugins.xep_0202.time import xep_0202 diff --git a/sleekxmpp/plugins/xep_0202/stanza.py b/sleekxmpp/plugins/xep_0202/stanza.py new file mode 100644 index 00000000..bb27692a --- /dev/null +++ b/sleekxmpp/plugins/xep_0202/stanza.py @@ -0,0 +1,126 @@ +""" + SleekXMPP: The Sleek XMPP Library + Copyright (C) 2010 Nathanael C. Fritz + This file is part of SleekXMPP. + + See the file LICENSE for copying permission. +""" + +import datetime as dt +from dateutil.tz import tzoffset, tzutc + +from sleekxmpp.xmlstream import ElementBase +from sleekxmpp.plugins import xep_0082 + + +class EntityTime(ElementBase): + + """ + The <time> element represents the local time for an XMPP agent. + The time is expressed in UTC to make synchronization easier + between entities, but the offset for the local timezone is also + included. + + Example <time> stanzas: + <iq type="result"> + <time xmlns="urn:xmpp:time"> + <utc>2011-07-03T11:37:12.234569</utc> + <tzo>-07:00</tzo> + </time> + </iq> + + Stanza Interface: + time -- The local time for the entity (updates utc and tzo). + utc -- The UTC equivalent to local time. + tzo -- The local timezone offset from UTC. + + Methods: + get_time -- Return local time datetime object. + set_time -- Set UTC and TZO fields. + del_time -- Remove both UTC and TZO fields. + get_utc -- Return datetime object of UTC time. + set_utc -- Set the UTC time. + get_tzo -- Return tzinfo object. + set_tzo -- Set the local timezone offset. + """ + + name = 'time' + namespace = 'urn:xmpp:time' + plugin_attrib = 'entity_time' + interfaces = set(('tzo', 'utc', 'time')) + sub_interfaces = interfaces + + def set_time(self, value): + """ + Set both the UTC and TZO fields given a time object. + + Arguments: + value -- A datetime object or properly formatted + string equivalent. + """ + date = value + if not isinstance(value, dt.datetime): + date = xep_0082.parse(value) + self['utc'] = date + self['tzo'] = date.tzinfo + + def get_time(self): + """ + Return the entity's local time based on the UTC and TZO data. + """ + date = self['utc'] + tz = self['tzo'] + return date.astimezone(tz) + + def del_time(self): + """Remove both the UTC and TZO fields.""" + del self['utc'] + del self['tzo'] + + def get_tzo(self): + """ + Return the timezone offset from UTC as a tzinfo object. + """ + tzo = self._get_sub_text('tzo') + if tzo == '': + tzo = 'Z' + time = xep_0082.parse('00:00:00%s' % tzo) + return time.tzinfo + + def set_tzo(self, value): + """ + Set the timezone offset from UTC. + + Arguments: + value -- Either a tzinfo object or the number of + seconds (positive or negative) to offset. + """ + time = xep_0082.time(offset=value) + if xep_0082.parse(time).tzinfo == tzutc(): + self._set_sub_text('tzo', 'Z') + else: + self._set_sub_text('tzo', time[-6:]) + + def get_utc(self): + """ + Return the time in UTC as a datetime object. + """ + value = self._get_sub_text('utc') + if value == '': + return xep_0082.parse(xep_0082.datetime()) + return xep_0082.parse('%sZ' % value) + + def set_utc(self, value): + """ + Set the time in UTC. + + Arguments: + value -- A datetime object or properly formatted + string equivalent. + """ + date = value + if not isinstance(value, dt.datetime): + date = xep_0082.parse(value) + date = date.astimezone(tzutc()) + value = xep_0082.format_datetime(date)[:-1] + self._set_sub_text('utc', value) diff --git a/sleekxmpp/plugins/xep_0202/time.py b/sleekxmpp/plugins/xep_0202/time.py new file mode 100644 index 00000000..f944bcb3 --- /dev/null +++ b/sleekxmpp/plugins/xep_0202/time.py @@ -0,0 +1,90 @@ +"""
+ SleekXMPP: The Sleek XMPP Library
+ Copyright (C) 2010 Nathanael C. Fritz
+ This file is part of SleekXMPP.
+
+ See the file LICENSE for copying permission.
+"""
+
+import logging
+
+from sleekxmpp.stanza.iq import Iq
+from sleekxmpp.xmlstream import register_stanza_plugin
+from sleekxmpp.xmlstream.handler import Callback
+from sleekxmpp.xmlstream.matcher import StanzaPath
+from sleekxmpp.plugins.base import base_plugin
+from sleekxmpp.plugins import xep_0082
+from sleekxmpp.plugins.xep_0202 import stanza
+
+
+log = logging.getLogger(__name__)
+
+
+class xep_0202(base_plugin):
+
+ """
+ XEP-0202: Entity Time
+ """
+
+ def plugin_init(self):
+ """Start the XEP-0203 plugin."""
+ self.xep = '0202'
+ self.description = 'Entity Time'
+ self.stanza = stanza
+
+ # As a default, respond to time requests with the
+ # local time returned by XEP-0082. However, a
+ # custom function can be supplied which accepts
+ # the JID of the entity to query for the time.
+ self.local_time = self.config.get('local_time', None)
+ if not self.local_time:
+ self.local_time = lambda x: xep_0082.datetime()
+
+ self.xmpp.registerHandler(
+ Callback('Entity Time',
+ StanzaPath('iq/entity_time'),
+ self._handle_time_request))
+ register_stanza_plugin(Iq, stanza.EntityTime)
+
+ def post_init(self):
+ """Handle cross-plugin interactions."""
+ base_plugin.post_init(self)
+ self.xmpp['xep_0030'].add_feature('urn:xmpp:time')
+
+
+ def _handle_time_request(self, iq):
+ """
+ Respond to a request for the local time.
+
+ The time is taken from self.local_time(), which may be replaced
+ during plugin configuration with a function that maps JIDs to
+ times.
+
+ Arguments:
+ iq -- The Iq time request stanza.
+ """
+ iq.reply()
+ iq['entity_time']['time'] = self.local_time(iq['to'])
+ iq.send()
+
+ def get_entity_time(self, to, ifrom=None, **iqargs):
+ """
+ Request the time from another entity.
+
+ Arguments:
+ to -- JID of the entity to query.
+ ifrom -- Specifiy the sender's JID.
+ block -- If true, block and wait for the stanzas' reply.
+ timeout -- The time in seconds to block while waiting for
+ a reply. If None, then wait indefinitely.
+ callback -- Optional callback to execute when a reply is
+ received instead of blocking and waiting for
+ the reply.
+ """
+ iq = self.xmpp.Iq()
+ iq['type'] = 'get'
+ iq['to'] = 'to'
+ if ifrom:
+ iq['from'] = 'ifrom'
+ iq.enable('entity_time')
+ return iq.send(**iqargs)
|