# tutorial_3.cpp

## C++ Tutorial Step 3: Receiving and sending chat messages

In this example, we will write a simple SkypeKit-based auto-reply bot. The bot will:

- Take a skypename and password as commmand-line arguments
- Log in
- Catch all incoming text chat messages
- Send a canned reply message

### Code Walkthrough

To detect incoming chat messages, we can use Conversation::OnMessage callback.

For this we need to write our own Conversation class, like this:

    class MyConversation : public Conversation
    {
    public:
      typedef DRef<MyConversation, Conversation> Ref;
      typedef DRefs<MyConversation, Conversation> Refs;
      MyConversation(unsigned int oid, SERootObject* root) : Conversation(oid, root) {};

      void OnMessage(const MessageRef & message);
    };

    void MyConversation::OnMessage(const MessageRef & message)
    {
      // Message handling goes here
    };

..and then add the new construction method to our Skype class:

    Conversation* newConversation(int oid) {return new MyConversation(oid, this);}

Now, this should be it. Except that it isn't. To get the OnMessage callback for any given conversation object, that object needs to be in the wrapper's object cache. If your client has not referenced an object and requested information from that object, events and property updates for that object will not be fired. See "SkypeKit Object Lifecycle" section in the reference manual for more information.

In our particular case, we must:

- Retrieve a list of recently active conversation objects at startup;
- hold "live" references to these objects in scope, for entire duration of our client;
- update this list of recently active conversations, as activity can occur in conversations that have not been active recently.

Let's name this "recently active conversation list" our inbox. A good place to hold this list would be in our Skype class, which would then end up looking like this:

    class MySkype : public Skype
    {
    public:
      Account*      newAccount(int oid)       {return new MyAccount(oid, this);}
      Conversation* newConversation(int oid)  {return new MyConversation(oid, this);}

      MyConversation::Refs inbox;
    };

At some point after a successful login, we will need to populate this list.

    skype->GetConversationList(skype->inbox, Conversation::INBOX_CONVERSATIONS);
    fetch(skype->inbox);

Note the use of the fetch function! It is necessary. Simply retrieving a reference list is not enough.

At this point, our client will already be mostly functional. MyConversation::OnMessage callback will (most likely) be fired on incoming messages. Most likely, because if the message happens to occur in a conversation object that did not exist when we first populated our inbox list, or did not pass the INBOX_CONVERSATIONS filter at the time, then we still don't have that conversation in the wrapper object cache.

The way around that is to detect when new conversations get added to the INBOX_CONVERSATIONS filter. To do this, we will need to override another event callback - Skype::OnConversationListChange

    void MySkype::OnConversationListChange(
      const ConversationRef &conversation,
      const Conversation::LIST_TYPE &type,
      const bool &added)
    {
      if ( (type == Conversation::INBOX_CONVERSATIONS) && (added) && (!inbox.contains(conversation)) )
      {
        inbox.append(conversation);
      };
    };

This function will keep our Skype::inbox list nicely up to date for duration of our client execution.

Now, let's get back to handling our incoming messages in the MyConversation::OnMessage callback. What we may want to do here is to produce some sort of output for incoming messages and then reply to them.

The first thing to know is that the Message objects are not only used for text messages. There can also be Message objects corresponding to conversation live status changes (calls), when somebody changes the conversation topic, etc. In a full client UI, all message types would need to be properly handled. However, for purposes of this example, we are only interested in messages of type POSTED_TEXT.

    void MyConversation::OnMessage(const MessageRef & message)
    {
      Message::TYPE messageType;
      message->GetPropType(messageType);

      if (messageType == Message::POSTED_TEXT)
      {
        SEIntList propIds;
        SEIntDict propValues;
        propIds.append(Message::P_AUTHOR);
        propIds.append(Message::P_BODY_XML);
        propValues = message->GetProps(propIds);

        if (propValues[0] != myAccountName)
        {
          printf("%s : %s\n",
          (const char*)propValues[0],   // P_AUTHOR
          (const char*)propValues[1]);  // P_BODY_XML

          Message::Ref reply;
          this->PostText("This is an automated reply.", reply, false);
        };
      };
    };

Note the check for message author not being equal to currently logged in account identity. OnMessage event will fire for all the messages, including our own. As we are about to send out replies for those messages, without that check this would go into infinite loop.

## Full code of this example

    /****************************************************************************

    Getting Started With SkypeKit. Tutorial Application, Step 3.

    In this example, we will write a simple SkypeKit-based auto-reply bot. The bot will:
     1. Take a skypename and password as commmand-line arguments
     2. Log in
     3. Catch all incoming text chat messages
     4. Send a canned reply message
    **/

    #include "skype-embedded_2.h"
    #include "keypair.h"
    #include "tutorial_common.h"

    using namespace Sid;

    SEString myAccountName;
    SEString myAccountPsw;

    //----------------------------------------------------------------------------
    // Interface section

    class MyConversation : public Conversation
    {
    public:
      typedef DRef<MyConversation, Conversation> Ref;
      typedef DRefs<MyConversation, Conversation> Refs;
      MyConversation(unsigned int oid, SERootObject* root) : Conversation(oid, root) {};

      void OnMessage(const MessageRef & message);
    };

    class MySkype : public Skype
    {
    public:
      Account*      newAccount(int oid)       {return new MyAccount(oid, this);}
      Conversation* newConversation(int oid)  {return new MyConversation(oid, this);}

      MyConversation::Refs inbox;
      void OnConversationListChange(const ConversationRef &conversation, const Conversation::LIST_TYPE &type, const bool &added);
    };

    //----------------------------------------------------------------------------
    // Implementation section

    void MyConversation::OnMessage(const MessageRef & message)
    {
      Message::TYPE messageType;
      message->GetPropType(messageType);

      if (messageType == Message::POSTED_TEXT)
      {
        SEIntList propIds;
        SEIntDict propValues;
        propIds.append(Message::P_AUTHOR);
        propIds.append(Message::P_BODY_XML);
        propValues = message->GetProps(propIds);

        if (propValues[0] != myAccountName)
        {
          printf("%s : %s\n",
          (const char*)propValues[0],   // P_AUTHOR
          (const char*)propValues[1]);  // P_BODY_XML

          Message::Ref reply;
          this->PostText("This is an automated reply.", reply, false);
        };
      };
    };

    void MySkype::OnConversationListChange(
      const ConversationRef &conversation,
      const Conversation::LIST_TYPE &type,
      const bool &added)
    {
      // if conversation was added to inbox and was not in there already (just in case..) -> append to our own inbox list
      if ( (type == Conversation::INBOX_CONVERSATIONS) && (added) && (!inbox.contains(conversation)) )
      {
        inbox.append(conversation);
      };
    };

    //----------------------------------------------------------------------------
    // Main

    MySkype* skype;

    int main(int argc, char * argv[])
    {
      printf("*****************************************************************\n");
      printf(" SkypeKit Tutorial, Step 3. - Getting and sending chat messages.\n");
      printf("*****************************************************************\n");

      if (argc < 3)
      {
        printf("usage: tutorial_3 <skypename> <password>\n");
        return 0;
      };

      myAccountName   = argv[1];
      myAccountPsw    = argv[2];

      printf("Creating skype ..\n");
      skype = new MySkype();

      printf("Submitting application token..\n");
      getKeyPair ();
      skype->init(keyBuf, inetAddr, portNum, "streamlog.txt");
      skype->start();

      printf("Retrieving account ..\n");
      MyAccount::Ref account;

      if (skype->GetAccount(myAccountName, account))
      {
        printf("Logging in..\n");
        account->LoginWithPassword(myAccountPsw, false, true);
        account->BlockWhileLoggingIn();

        if (account->loggedIn)
        {
          printf("Loggin complete.\n");

          skype->GetConversationList(skype->inbox, Conversation::INBOX_CONVERSATIONS);
          fetch(skype->inbox);

          printf("Now accepting incoming chat messages.\n");
          printf("Press ENTER to quit.\n");
          getchar();

          printf("Logging out..\n");
          account->Logout(false);
          account->BlockWhileLoggingOut();
          printf("Logout complete.\n");
        };
      }
      else
      {
        printf("Account does not exist\n");
      };

      printf("Cleaning up.\n");
      skype->stop();
      delete skype;
      printf("Done.\n");
      return 0;
    };

All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

(c) Skype Technologies S.A. Confidential/Proprietary

Last updated: Fri Jan 27 2012