--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/thirdparty/google_appengine/google/appengine/api/xmpp/__init__.py Sun Sep 06 23:31:53 2009 +0200
@@ -0,0 +1,332 @@
+#!/usr/bin/env python
+#
+# Copyright 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""XMPP API.
+
+This module allows AppEngine apps to interact with a bot representing that app
+on the Google Talk network.
+
+Functions defined in this module:
+ get_presence: Gets the presence for a JID.
+ send_message: Sends a chat message to any number of JIDs.
+ send_invite: Sends an invitation to chat to a JID.
+
+Classes defined in this module:
+ Message: A class to encapsulate received messages.
+"""
+
+
+
+from google.appengine.api import apiproxy_stub_map
+from google.appengine.api.xmpp import xmpp_service_pb
+from google.appengine.runtime import apiproxy_errors
+
+
+NO_ERROR = xmpp_service_pb.XmppMessageResponse.NO_ERROR
+INVALID_JID = xmpp_service_pb.XmppMessageResponse.INVALID_JID
+OTHER_ERROR = xmpp_service_pb.XmppMessageResponse.OTHER_ERROR
+
+
+MESSAGE_TYPE_NONE = ""
+MESSAGE_TYPE_CHAT = "chat"
+MESSAGE_TYPE_ERROR = "error"
+MESSAGE_TYPE_GROUPCHAT = "groupchat"
+MESSAGE_TYPE_HEADLINE = "headline"
+MESSAGE_TYPE_NORMAL = "normal"
+
+_VALID_MESSAGE_TYPES = frozenset([MESSAGE_TYPE_NONE, MESSAGE_TYPE_CHAT,
+ MESSAGE_TYPE_ERROR, MESSAGE_TYPE_GROUPCHAT,
+ MESSAGE_TYPE_HEADLINE, MESSAGE_TYPE_NORMAL])
+
+
+class Error(Exception):
+ """Base error class for this module."""
+
+
+class InvalidJidError(Error):
+ """Error that indicates a request for an invalid JID."""
+
+
+class InvalidTypeError(Error):
+ """Error that indicates a send message request has an invalid type."""
+
+
+class InvalidXmlError(Error):
+ """Error that indicates a send message request has invalid XML."""
+
+
+class NoBodyError(Error):
+ """Error that indicates a send message request has no body."""
+
+
+class InvalidMessageError(Error):
+ """Error that indicates a received message was invalid or incomplete."""
+
+
+def get_presence(jid, from_jid=None):
+ """Gets the presence for a JID.
+
+ Args:
+ jid: The JID of the contact whose presence is requested.
+ from_jid: The optional custom JID to use for sending. Currently, the default
+ is <appid>@appspot.com. This is supported as a value. Custom JIDs can be
+ of the form <anything>@<appid>.appspotchat.com.
+
+ Returns:
+ bool, Whether the user is online.
+
+ Raises:
+ InvalidJidError if any of the JIDs passed are invalid.
+ Error if an unspecified error happens processing the request.
+ """
+ if not jid:
+ raise InvalidJidError()
+
+ request = xmpp_service_pb.PresenceRequest()
+ response = xmpp_service_pb.PresenceResponse()
+
+ request.set_jid(_to_str(jid))
+ if from_jid:
+ request.set_from_jid(_to_str(from_jid))
+
+ try:
+ apiproxy_stub_map.MakeSyncCall("xmpp",
+ "GetPresence",
+ request,
+ response)
+ except apiproxy_errors.ApplicationError, e:
+ if (e.application_error ==
+ xmpp_service_pb.XmppServiceError.INVALID_JID):
+ raise InvalidJidError()
+ else:
+ raise Error()
+
+ return bool(response.is_available())
+
+
+def send_invite(jid, from_jid=None):
+ """Sends an invitation to chat to a JID.
+
+ Args:
+ jid: The JID of the contact to invite.
+ from_jid: The optional custom JID to use for sending. Currently, the default
+ is <appid>@appspot.com. This is supported as a value. Custom JIDs can be
+ of the form <anything>@<appid>.appspotchat.com.
+
+ Raises:
+ InvalidJidError if the JID passed is invalid.
+ Error if an unspecified error happens processing the request.
+ """
+ if not jid:
+ raise InvalidJidError()
+
+ request = xmpp_service_pb.XmppInviteRequest()
+ response = xmpp_service_pb.XmppInviteResponse()
+
+ request.set_jid(_to_str(jid))
+ if from_jid:
+ request.set_from_jid(_to_str(from_jid))
+
+ try:
+ apiproxy_stub_map.MakeSyncCall("xmpp",
+ "SendInvite",
+ request,
+ response)
+ except apiproxy_errors.ApplicationError, e:
+ if (e.application_error ==
+ xmpp_service_pb.XmppServiceError.INVALID_JID):
+ raise InvalidJidError()
+ else:
+ raise Error()
+
+ return
+
+
+def send_message(jids, body, from_jid=None, message_type=MESSAGE_TYPE_CHAT,
+ raw_xml=False):
+ """Sends a chat message to a list of JIDs.
+
+ Args:
+ jids: A list of JIDs to send the message to, or a single JID to send the
+ message to.
+ from_jid: The optional custom JID to use for sending. Currently, the default
+ is <appid>@appspot.com. This is supported as a value. Custom JIDs can be
+ of the form <anything>@<appid>.appspotchat.com.
+ body: The body of the message.
+ message_type: Optional type of the message. Should be one of the types
+ specified in RFC 3921, section 2.1.1. An empty string will result in a
+ message stanza without a type attribute. For convenience, all of the
+ valid types are in the MESSAGE_TYPE_* constants in this file. The
+ default is MESSAGE_TYPE_CHAT. Anything else will throw an exception.
+ raw_xml: Optionally specifies that the body should be interpreted as XML. If
+ this is false, the contents of the body will be escaped and placed inside
+ of a body element inside of the message. If this is true, the contents
+ will be made children of the message.
+
+ Returns:
+ list, A list of statuses, one for each JID, corresponding to the result of
+ sending the message to that JID. Or, if a single JID was passed in,
+ returns the status directly.
+
+ Raises:
+ InvalidJidError if there is no valid JID in the list.
+ InvalidTypeError if the type argument is invalid.
+ InvalidXmlError if the body is malformed XML and raw_xml is True.
+ NoBodyError if there is no body.
+ Error if another error occurs processing the request.
+ """
+ request = xmpp_service_pb.XmppMessageRequest()
+ response = xmpp_service_pb.XmppMessageResponse()
+
+ if not body:
+ raise NoBodyError()
+
+ if not jids:
+ raise InvalidJidError()
+
+ if not message_type in _VALID_MESSAGE_TYPES:
+ raise InvalidTypeError()
+
+ single_jid = False
+ if isinstance(jids, basestring):
+ single_jid = True
+ jids = [jids]
+
+ for jid in jids:
+ if not jid:
+ raise InvalidJidError()
+ request.add_jid(_to_str(jid))
+
+ request.set_body(_to_str(body))
+ request.set_type(_to_str(message_type))
+ request.set_raw_xml(raw_xml)
+ if from_jid:
+ request.set_from_jid(_to_str(from_jid))
+
+ try:
+ apiproxy_stub_map.MakeSyncCall("xmpp",
+ "SendMessage",
+ request,
+ response)
+ except apiproxy_errors.ApplicationError, e:
+ if (e.application_error ==
+ xmpp_service_pb.XmppServiceError.INVALID_JID):
+ raise InvalidJidError()
+ elif (e.application_error ==
+ xmpp_service_pb.XmppServiceError.INVALID_TYPE):
+ raise InvalidTypeError()
+ elif (e.application_error ==
+ xmpp_service_pb.XmppServiceError.INVALID_XML):
+ raise InvalidXmlError()
+ elif (e.application_error ==
+ xmpp_service_pb.XmppServiceError.NO_BODY):
+ raise NoBodyError()
+ raise Error()
+
+ if single_jid:
+ return response.status_list()[0]
+ return response.status_list()
+
+
+class Message(object):
+ """Encapsulates an XMPP message received by the application."""
+
+ def __init__(self, vars):
+ """Constructs a new XMPP Message from an HTTP request.
+
+ Args:
+ vars: A dict-like object to extract message arguments from.
+ """
+ try:
+ self.__sender = vars["from"]
+ self.__to = vars["to"]
+ self.__body = vars["body"]
+ except KeyError, e:
+ raise InvalidMessageError(e[0])
+ self.__command = None
+ self.__arg = None
+
+ @property
+ def sender(self):
+ return self.__sender
+
+ @property
+ def to(self):
+ return self.__to
+
+ @property
+ def body(self):
+ return self.__body
+
+ def __parse_command(self):
+ if self.__arg != None:
+ return
+
+ body = self.__body
+ if body.startswith('\\'):
+ body = '/' + body[1:]
+
+ self.__arg = ''
+ if body.startswith('/'):
+ parts = body.split(' ', 1)
+ self.__command = parts[0][1:]
+ if len(parts) > 1:
+ self.__arg = parts[1].strip()
+ else:
+ self.__arg = self.__body.strip()
+
+ @property
+ def command(self):
+ self.__parse_command()
+ return self.__command
+
+ @property
+ def arg(self):
+ self.__parse_command()
+ return self.__arg
+
+ def reply(self, body, message_type=MESSAGE_TYPE_CHAT, raw_xml=False,
+ send_message=send_message):
+ """Convenience function to reply to a message.
+
+ Args:
+ body: str: The body of the message
+ message_type, raw_xml: As per send_message.
+ send_message: Used for testing.
+
+ Returns:
+ A status code as per send_message.
+
+ Raises:
+ See send_message.
+ """
+ return send_message([self.sender], body, from_jid=self.to,
+ message_type=message_type, raw_xml=raw_xml)
+
+
+def _to_str(value):
+ """Helper function to make sure unicode values converted to utf-8
+
+ Args:
+ value: str or unicode to convert to utf-8.
+
+ Returns:
+ UTF-8 encoded str of value, otherwise value unchanged.
+ """
+ if isinstance(value, unicode):
+ return value.encode('utf-8')
+ return value