github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/google.golang.org/appengine/xmpp/xmpp.go (about)

     1  // Copyright 2011 Google Inc. All rights reserved.
     2  // Use of this source code is governed by the Apache 2.0
     3  // license that can be found in the LICENSE file.
     4  
     5  /*
     6  Package xmpp provides the means to send and receive instant messages
     7  to and from users of XMPP-compatible services.
     8  
     9  To send a message,
    10  	m := &xmpp.Message{
    11  		To:   []string{"kaylee@example.com"},
    12  		Body: `Hi! How's the carrot?`,
    13  	}
    14  	err := m.Send(c)
    15  
    16  To receive messages,
    17  	func init() {
    18  		xmpp.Handle(handleChat)
    19  	}
    20  
    21  	func handleChat(c context.Context, m *xmpp.Message) {
    22  		// ...
    23  	}
    24  */
    25  package xmpp
    26  
    27  import (
    28  	"errors"
    29  	"fmt"
    30  	"net/http"
    31  
    32  	"golang.org/x/net/context"
    33  
    34  	"google.golang.org/appengine"
    35  	"google.golang.org/appengine/internal"
    36  	pb "google.golang.org/appengine/internal/xmpp"
    37  )
    38  
    39  // Message represents an incoming chat message.
    40  type Message struct {
    41  	// Sender is the JID of the sender.
    42  	// Optional for outgoing messages.
    43  	Sender string
    44  
    45  	// To is the intended recipients of the message.
    46  	// Incoming messages will have exactly one element.
    47  	To []string
    48  
    49  	// Body is the body of the message.
    50  	Body string
    51  
    52  	// Type is the message type, per RFC 3921.
    53  	// It defaults to "chat".
    54  	Type string
    55  
    56  	// RawXML is whether the body contains raw XML.
    57  	RawXML bool
    58  }
    59  
    60  // Presence represents an outgoing presence update.
    61  type Presence struct {
    62  	// Sender is the JID (optional).
    63  	Sender string
    64  
    65  	// The intended recipient of the presence update.
    66  	To string
    67  
    68  	// Type, per RFC 3921 (optional). Defaults to "available".
    69  	Type string
    70  
    71  	// State of presence (optional).
    72  	// Valid values: "away", "chat", "xa", "dnd" (RFC 3921).
    73  	State string
    74  
    75  	// Free text status message (optional).
    76  	Status string
    77  }
    78  
    79  var (
    80  	ErrPresenceUnavailable = errors.New("xmpp: presence unavailable")
    81  	ErrInvalidJID          = errors.New("xmpp: invalid JID")
    82  )
    83  
    84  // Handle arranges for f to be called for incoming XMPP messages.
    85  // Only messages of type "chat" or "normal" will be handled.
    86  func Handle(f func(c context.Context, m *Message)) {
    87  	http.HandleFunc("/_ah/xmpp/message/chat/", func(_ http.ResponseWriter, r *http.Request) {
    88  		f(appengine.NewContext(r), &Message{
    89  			Sender: r.FormValue("from"),
    90  			To:     []string{r.FormValue("to")},
    91  			Body:   r.FormValue("body"),
    92  		})
    93  	})
    94  }
    95  
    96  // Send sends a message.
    97  // If any failures occur with specific recipients, the error will be an appengine.MultiError.
    98  func (m *Message) Send(c context.Context) error {
    99  	req := &pb.XmppMessageRequest{
   100  		Jid:    m.To,
   101  		Body:   &m.Body,
   102  		RawXml: &m.RawXML,
   103  	}
   104  	if m.Type != "" && m.Type != "chat" {
   105  		req.Type = &m.Type
   106  	}
   107  	if m.Sender != "" {
   108  		req.FromJid = &m.Sender
   109  	}
   110  	res := &pb.XmppMessageResponse{}
   111  	if err := internal.Call(c, "xmpp", "SendMessage", req, res); err != nil {
   112  		return err
   113  	}
   114  
   115  	if len(res.Status) != len(req.Jid) {
   116  		return fmt.Errorf("xmpp: sent message to %d JIDs, but only got %d statuses back", len(req.Jid), len(res.Status))
   117  	}
   118  	me, any := make(appengine.MultiError, len(req.Jid)), false
   119  	for i, st := range res.Status {
   120  		if st != pb.XmppMessageResponse_NO_ERROR {
   121  			me[i] = errors.New(st.String())
   122  			any = true
   123  		}
   124  	}
   125  	if any {
   126  		return me
   127  	}
   128  	return nil
   129  }
   130  
   131  // Invite sends an invitation. If the from address is an empty string
   132  // the default (yourapp@appspot.com/bot) will be used.
   133  func Invite(c context.Context, to, from string) error {
   134  	req := &pb.XmppInviteRequest{
   135  		Jid: &to,
   136  	}
   137  	if from != "" {
   138  		req.FromJid = &from
   139  	}
   140  	res := &pb.XmppInviteResponse{}
   141  	return internal.Call(c, "xmpp", "SendInvite", req, res)
   142  }
   143  
   144  // Send sends a presence update.
   145  func (p *Presence) Send(c context.Context) error {
   146  	req := &pb.XmppSendPresenceRequest{
   147  		Jid: &p.To,
   148  	}
   149  	if p.State != "" {
   150  		req.Show = &p.State
   151  	}
   152  	if p.Type != "" {
   153  		req.Type = &p.Type
   154  	}
   155  	if p.Sender != "" {
   156  		req.FromJid = &p.Sender
   157  	}
   158  	if p.Status != "" {
   159  		req.Status = &p.Status
   160  	}
   161  	res := &pb.XmppSendPresenceResponse{}
   162  	return internal.Call(c, "xmpp", "SendPresence", req, res)
   163  }
   164  
   165  var presenceMap = map[pb.PresenceResponse_SHOW]string{
   166  	pb.PresenceResponse_NORMAL:         "",
   167  	pb.PresenceResponse_AWAY:           "away",
   168  	pb.PresenceResponse_DO_NOT_DISTURB: "dnd",
   169  	pb.PresenceResponse_CHAT:           "chat",
   170  	pb.PresenceResponse_EXTENDED_AWAY:  "xa",
   171  }
   172  
   173  // GetPresence retrieves a user's presence.
   174  // If the from address is an empty string the default
   175  // (yourapp@appspot.com/bot) will be used.
   176  // Possible return values are "", "away", "dnd", "chat", "xa".
   177  // ErrPresenceUnavailable is returned if the presence is unavailable.
   178  func GetPresence(c context.Context, to string, from string) (string, error) {
   179  	req := &pb.PresenceRequest{
   180  		Jid: &to,
   181  	}
   182  	if from != "" {
   183  		req.FromJid = &from
   184  	}
   185  	res := &pb.PresenceResponse{}
   186  	if err := internal.Call(c, "xmpp", "GetPresence", req, res); err != nil {
   187  		return "", err
   188  	}
   189  	if !*res.IsAvailable || res.Presence == nil {
   190  		return "", ErrPresenceUnavailable
   191  	}
   192  	presence, ok := presenceMap[*res.Presence]
   193  	if ok {
   194  		return presence, nil
   195  	}
   196  	return "", fmt.Errorf("xmpp: unknown presence %v", *res.Presence)
   197  }
   198  
   199  // GetPresenceMulti retrieves multiple users' presence.
   200  // If the from address is an empty string the default
   201  // (yourapp@appspot.com/bot) will be used.
   202  // Possible return values are "", "away", "dnd", "chat", "xa".
   203  // If any presence is unavailable, an appengine.MultiError is returned
   204  func GetPresenceMulti(c context.Context, to []string, from string) ([]string, error) {
   205  	req := &pb.BulkPresenceRequest{
   206  		Jid: to,
   207  	}
   208  	if from != "" {
   209  		req.FromJid = &from
   210  	}
   211  	res := &pb.BulkPresenceResponse{}
   212  
   213  	if err := internal.Call(c, "xmpp", "BulkGetPresence", req, res); err != nil {
   214  		return nil, err
   215  	}
   216  
   217  	presences := make([]string, 0, len(res.PresenceResponse))
   218  	errs := appengine.MultiError{}
   219  
   220  	addResult := func(presence string, err error) {
   221  		presences = append(presences, presence)
   222  		errs = append(errs, err)
   223  	}
   224  
   225  	anyErr := false
   226  	for _, subres := range res.PresenceResponse {
   227  		if !subres.GetValid() {
   228  			anyErr = true
   229  			addResult("", ErrInvalidJID)
   230  			continue
   231  		}
   232  		if !*subres.IsAvailable || subres.Presence == nil {
   233  			anyErr = true
   234  			addResult("", ErrPresenceUnavailable)
   235  			continue
   236  		}
   237  		presence, ok := presenceMap[*subres.Presence]
   238  		if ok {
   239  			addResult(presence, nil)
   240  		} else {
   241  			anyErr = true
   242  			addResult("", fmt.Errorf("xmpp: unknown presence %q", *subres.Presence))
   243  		}
   244  	}
   245  	if anyErr {
   246  		return presences, errs
   247  	}
   248  	return presences, nil
   249  }
   250  
   251  func init() {
   252  	internal.RegisterErrorCodeMap("xmpp", pb.XmppServiceError_ErrorCode_name)
   253  }