summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sleekxmpp/plugins/xep_0202.py117
-rw-r--r--sleekxmpp/plugins/xep_0202/__init__.py11
-rw-r--r--sleekxmpp/plugins/xep_0202/stanza.py126
-rw-r--r--sleekxmpp/plugins/xep_0202/time.py90
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)