text: #!/usr/bin/env python3
  
  # Slixmpp: The Slick XMPP Library
  # Copyright (C) 2010  Nathanael C. Fritz
  # This file is part of Slixmpp.
  # See the file LICENSE for copying permission.
  
  import sys
  import logging
  from getpass import getpass
  from argparse import ArgumentParser
  
  try:
      from httplib import HTTPSConnection
      from urllib import urlencode
  except ImportError:
      from urllib.parse import urlencode
      from http.client import HTTPSConnection
  
  import slixmpp
  from slixmpp.xmlstream import JID
  
  
  class ThirdPartyAuthBot(slixmpp.ClientXMPP):
  
      """
      A simple Slixmpp bot that will echo messages it
      receives, along with a short thank you message.
  
      This version uses a thirdpary service for authentication,
      such as Facebook or Google.
      """
  
      def __init__(self, jid, password):
          slixmpp.ClientXMPP.__init__(self, jid, password)
  
          # The X-GOOGLE-TOKEN mech is ranked lower than PLAIN
          # due to Google only allowing a single SASL attempt per
          # connection. So PLAIN will be used for TLS connections,
          # and X-GOOGLE-TOKEN for non-TLS connections. To use
          # X-GOOGLE-TOKEN with a TLS connection, explicitly select
          # it using:
          #
          # slixmpp.ClientXMPP.__init__(self, jid, password,
          #                               sasl_mech="X-GOOGLE-TOKEN")
  
          # 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 message event is triggered whenever a message
          # stanza is received. Be aware that that includes
          # MUC messages and error messages.
          self.add_event_handler("message", self.message)
  
      async 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.send_presence()
          await self.get_roster()
  
      def message(self, msg):
          """
          Process incoming message stanzas. Be aware that this also
          includes MUC messages and error messages. It is usually
          a good idea to check the messages's type before processing
          or sending replies.
  
          Arguments:
              msg -- The received message stanza. See the documentation
                     for stanza objects and the Message stanza to see
                     how it may be used.
          """
          if msg['type'] in ('chat', 'normal'):
              msg.reply("Thanks for sending\n%(body)s" % msg).send()
  
  
  if __name__ == '__main__':
      # Setup the command line arguments.
      parser = ArgumentParser()
  
      # Output verbosity options.
      parser.add_argument("-q", "--quiet", help="set logging to ERROR",
                          action="store_const", dest="loglevel",
                          const=logging.ERROR, default=logging.INFO)
      parser.add_argument("-d", "--debug", help="set logging to DEBUG",
                          action="store_const", dest="loglevel",
                          const=logging.DEBUG, default=logging.INFO)
  
      # JID and password options.
      parser.add_argument("-j", "--jid", dest="jid",
                          help="JID to use")
      parser.add_argument("-p", "--password", dest="password",
                          help="password to use")
  
      args = parser.parse_args()
  
      # Setup logging.
      logging.basicConfig(level=args.loglevel,
                          format='%(levelname)-8s %(message)s')
  
      if args.jid is None:
          args.jid = input("Username: ")
      if args.password is None:
          args.password = getpass("Password: ")
  
  
      access_token = None
  
      # Since documentation on how to work with Google tokens
      # can be difficult to find, we'll demo a basic version
      # here. Note that responses could refer to a Captcha
      # URL that would require a browser.
  
      # Using Facebook or MSN's custom authentication requires
      # a browser, but the process is the same once a token
      # has been retrieved.
  
      # Request an access token from Google:
      try:
          conn = HTTPSConnection('www.google.com')
      except:
          print('Could not connect to Google')
          sys.exit()
  
      params = urlencode({
          'accountType': 'GOOGLE',
          'service': 'mail',
          'Email': JID(args.jid).bare,
          'Passwd': args.password
      })
      headers = {
          'Content-Type': 'application/x-www-form-urlencoded'
      }
      try:
          conn.request('POST', '/accounts/ClientLogin', params, headers)
          resp = conn.getresponse().read()
          data = {}
          for line in resp.split():
              k, v = line.split(b'=', 1)
              data[k] = v
      except Exception as e:
          print('Could not retrieve login data')
          sys.exit()
  
      if b'SID' not in data:
          print('Required data not found')
          sys.exit()
  
  
      params = urlencode({
          'SID': data[b'SID'],
          'LSID': data[b'LSID'],
          'service': 'mail'
      })
      try:
          conn.request('POST', '/accounts/IssueAuthToken', params, headers)
          resp = conn.getresponse()
          data = resp.read().split()
      except:
          print('Could not retrieve auth data')
          sys.exit()
  
      if not data:
          print('Could not retrieve token')
          sys.exit()
  
      access_token = data[0]
  
  
      # Setup the ThirdPartyAuthBot and register plugins. Note that while plugins
      # may have interdependencies, the order in which you register them does not
      # matter.
  
      # If using MSN, the JID should be "user@messenger.live.com", which will
      # be overridden on session bind.
  
      # We're using an access token instead of a password, so we'll use `''` as
      # a password argument filler.
  
      xmpp = ThirdPartyAuthBot(args.jid, '')
      xmpp.credentials['access_token'] = access_token
  
      # The credentials dictionary is used to provide additional authentication
      # information beyond just a password.
  
      xmpp.register_plugin('xep_0030') # Service Discovery
      xmpp.register_plugin('xep_0004') # Data Forms
      xmpp.register_plugin('xep_0060') # PubSub
  
      # MSN will kill connections that have been inactive for even
      # short periods of time. So use pings to keep the session alive;
      # whitespace keepalives do not work.
      xmpp.register_plugin('xep_0199', {'keepalive': True, 'frequency': 60})
  
      # If you are working with an OpenFire server, you may need
      # to adjust the SSL version used:
      # xmpp.ssl_version = ssl.PROTOCOL_SSLv3
  
      # If you want to verify the SSL certificates offered by a server:
      # xmpp.ca_certs = "path/to/ca/cert"
  
      # Connect to the XMPP server and start processing XMPP stanzas.
      # Google only allows one SASL attempt per connection, so in order to
      # enable the X-GOOGLE-TOKEN mechanism, we'll disable TLS.
      xmpp.connect()
      xmpp.process()
  