Thursday, October 24, 2013

Advanced Facebook Chat

Notice: Since the release of the Facebook API 2.0 in April 2014 the chat function is disabled, which means that this code unfortunately is no longer runnable.

As for now the last post regarding the topic Facebook I want to present you today a a little bit more advanced chat client.
It uses all the techniques presented in the previous posts and looks as follows:



Name and password are set in the source code. When starting the program it loads the friends list.
If the user starts inputting a name in the Combobox, it fills with matching friends. After a click on "Start Chat" a new chat window is opened.
In this incoming messages of the contact are shown, furthermore own messages can be send by clicking "Send" or pressing Enter. With "Ctrl + Tab" one can change between the chat windows.
Of course a window also opens when receiving a message. If a new message is received,a notification sound is played, also the name starts blinking and the icon in the taskbar changes.
Requirement for this is the inclusion of the Windows API Codepack.
If a chat contact is typing, this is shown by displaying a keyboard icon.

Now to the code, but first a download link, over which the complete source code including the executable file can be downloaded.


Form1.cs:



using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using agsXMPP;
using agsXMPP.protocol.client;
using agsXMPP.protocol.iq.roster;
using agsXMPP.Collections;
using Microsoft.WindowsAPICodePack.Taskbar;

namespace WindowsFormsApplication1
{

    public partial class Form1 : Form
    {
        XmppClientConnection xmpp = new XmppClientConnection("chat.facebook.com");
        Dictionary<string, string> Friends = new Dictionary<string, string>();
        bool StartCollectingContacts = false;
        bool CollectingContacts = true;
        Dictionary<string, Chat> Chats = new Dictionary<string, Chat>();
        TaskbarManager tm = TaskbarManager.Instance;

        public Form1()
        {
            InitializeComponent();
        }


        private void Form1_Load(object sender, EventArgs e)
        {
            tabControl1.ImageList = new ImageList();
            tabControl1.ImageList.Images.Add(WindowsFormsApplication1.Properties.Resources.keyboard);
            xmpp.OnLogin += new ObjectHandler(OnLogin);
            xmpp.Open("name", "password");

            while (CollectingContacts)
            {
                if (StartCollectingContacts)
                    CollectingContacts = false;
                System.Threading.Thread.Sleep(1000);
            }
        }

        private void OnLogin(object sender)
        {
            Presence p = new Presence(ShowType.chat, "Online");
            p.Type = PresenceType.available;
            xmpp.Send(p);
            xmpp.OnRosterItem += new XmppClientConnection.RosterHandler(xmpp_OnRosterItem);
        }

        private void xmpp_OnRosterItem(object sender, agsXMPP.protocol.iq.roster.RosterItem item)
        {
            try
            {
                StartCollectingContacts = true;
                CollectingContacts = true;
                Friends.Add(item.GetAttribute("jid").ToString(), item.GetAttribute("name").ToString());
                xmpp.MessageGrabber.Add(new Jid(item.GetAttribute("jid").ToString()), new BareJidComparer(), new MessageCB(MessageCallBack), null);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        private void MessageCallBack(object sender, agsXMPP.protocol.client.Message msg, object data)
        {
            string JidSender = msg.From.ToString();
            string NameSender = Friends[JidSender];
            Chat Dummy;
            bool ChatExisting = Chats.TryGetValue(NameSender, out Dummy);

            if (msg.Body != null)
            {
                if (!ChatExisting || !Chats[NameSender].Active)
                {
                    ProvideChat(NameSender);
                }

                Chats[NameSender].NewMessage = true;
                Chats[NameSender].ChatWindow.Invoke(new Action(() =>
                    {
                        Chats[NameSender].ChatWindow.Text += NameSender + ": " + msg.Body + Environment.NewLine;
                        Chats[NameSender].ChatWindow.SelectionStart = Chats[NameSender].ChatWindow.TextLength;
                        Chats[NameSender].ChatWindow.ScrollToCaret();
                    }));
                Notify();
            }
            else if (msg.Chatstate == agsXMPP.protocol.extensions.chatstates.Chatstate.composing && ChatExisting)
            {
                Chats[NameSender].ChatWindow.Invoke(new Action(() =>
                {
                    Chats[NameSender].ChatPage.ImageIndex = 0;
                    Chats[NameSender].Composing = true;
                }));
            }
            else if (msg.Chatstate == agsXMPP.protocol.extensions.chatstates.Chatstate.active && ChatExisting)
            {
                Chats[NameSender].ChatWindow.Invoke(new Action(() =>
                {
                    Chats[NameSender].ChatPage.ImageIndex = -1;
                }));
            }
        }

        public void Notify()
        {
            System.Media.SoundPlayer ExamplePlayer = new System.Media.SoundPlayer(Properties.Resources.cell_phone_flip_1);
            ExamplePlayer.Play();
        }

        public void SendMessage(string msg, string receiverName)
        {
            xmpp.Send(new agsXMPP.protocol.client.Message(new Jid(receiverName), agsXMPP.protocol.client.MessageType.chat, msg));
        }

        private void button2_Click(object sender, EventArgs e)
        {
            ProvideChat(comboBox1.Text);
        }

        private void ProvideChat(string name)
        {
            int Matches = Friends.Where(pair => pair.Value.ToLower().Contains(name.ToLower())).Count();

            Chat Dummy;
            if (Matches > 0 && !Chats.TryGetValue(name, out Dummy))
            {
                TabPage NewPage = new TabPage(name);

                TextBox ChatWindow = new TextBox();
                ChatWindow.Left = 10;
                ChatWindow.Top = 10;
                ChatWindow.Width = 532;
                ChatWindow.Height = 180;
                ChatWindow.Multiline = true;
                ChatWindow.ScrollBars = ScrollBars.Vertical;
                ChatWindow.ReadOnly = false;
                NewPage.Controls.Add(ChatWindow);

                TextBox SendBox = new TextBox();
                SendBox.Left = 10;
                SendBox.Top = 200;
                SendBox.Width = 450;
                NewPage.Controls.Add(SendBox);
                SendBox.Name = "snd" + name;
                SendBox.Click += new EventHandler(SendBox_Click);
                SendBox.TextChanged += new EventHandler(SendBox_TextChanged);

                Button SendButton = new Button();
                SendButton.Left = 470;
                SendButton.Top = 200;
                SendButton.Text = "Send";
                SendButton.Name = "btn" + name;
                SendButton.Click += new EventHandler(SendButton_Click);
                NewPage.Controls.Add(SendButton);

                Chat NewChat = new Chat();
                NewChat.ChatWindow = ChatWindow;
                NewChat.SendBox = SendBox;
                NewChat.ChatPage = NewPage;
                NewChat.Partner = name;
                NewChat.SendButton = SendButton;

                NewPage.Name = "tpg" + name;
                tabControl1.SelectedIndexChanged += new EventHandler(tabControl1_SelectedIndexChanged);

                Chats.Add(name, NewChat);

                this.AcceptButton = NewChat.SendButton;

                if (tabControl1.InvokeRequired)
                {
                    tabControl1.Invoke(new Action(() =>
                        {
                            tabControl1.TabPages.Add(NewPage);
                            tabControl1.SelectedTab = NewPage;
                        }));
                }
                else
                {
                    tabControl1.TabPages.Add(NewPage);
                    tabControl1.SelectedTab = NewPage;
                }

                this.ActiveControl = NewChat.SendBox;
            }
            else if (Chats.TryGetValue(name, out Dummy) && !Dummy.Active)
            {
                if (tabControl1.InvokeRequired)
                {
                    tabControl1.Invoke(new Action(() =>
                        {
                            tabControl1.TabPages.Add(Dummy.ChatPage);
                            tabControl1.SelectedTab = Dummy.ChatPage;
                        }));
                }
                else
                {
                    tabControl1.TabPages.Add(Dummy.ChatPage);
                    tabControl1.SelectedTab = Dummy.ChatPage;
                }
            }
        }

        private void tabControl1_SelectedIndexChanged(Object sender, EventArgs e)
        {
            if (((TabControl)sender).SelectedIndex != -1)
            {
                string Receiver = ((TabControl)sender).TabPages[((TabControl)sender).SelectedIndex].Name.ToString().Substring(3, ((TabControl)sender).TabPages[((TabControl)sender).SelectedIndex].Name.ToString().Length - 3);
                this.AcceptButton = Chats[Receiver].SendButton;
                this.ActiveControl = Chats[Receiver].SendBox;
            }
        }

        private void SendBox_Click(Object sender, EventArgs e)
        {
            string Receiver = ((TextBox)sender).Name.Substring(3, ((TextBox)sender).Name.Length - 3);
            Chat CurrentChat = Chats[Receiver];
            CurrentChat.NewMessage = false;
            CurrentChat.ChatPage.Text = CurrentChat.Partner;
        }

        private void SendBox_TextChanged(Object sender, EventArgs e)
        {
            string Receiver = ((TextBox)sender).Name.Substring(3, ((TextBox)sender).Name.Length - 3);
            Chat CurrentChat = Chats[Receiver];
            CurrentChat.NewMessage = false;
            CurrentChat.ChatPage.Text = CurrentChat.Partner;
        }

        private void SendButton_Click(Object sender, EventArgs e)
        {
            string Receiver = ((Button)sender).Name.Substring(3, ((Button)sender).Name.Length - 3);
            Chat CurrentChat = Chats[Receiver];
            SendMessage(CurrentChat.SendBox.Text, Friends.First(x => x.Value == Receiver).Key);
            CurrentChat.ChatWindow.Text += "Ich: " + CurrentChat.SendBox.Text + Environment.NewLine;
            CurrentChat.SendBox.Text = "";
            Chats[Receiver].ChatWindow.SelectionStart = Chats[Receiver].ChatWindow.TextLength;
            Chats[Receiver].ChatWindow.ScrollToCaret();
        }

        private void comboBox1_TextUpdate(object sender, EventArgs e)
        {
            var Matches = Friends.Where(pair => pair.Value.ToLower().Contains(comboBox1.Text.ToLower())).Select(pair => pair.Value);
            comboBox1.Items.Clear();
            foreach (string s in Matches)
            {
                comboBox1.Items.Add(s);
            }
            comboBox1.Select(comboBox1.Text.Length, 0);
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            bool NewMessages = false;
            foreach (Chat c in Chats.Values)
            {
                if (c.NewMessage)
                {
                    NewMessages = true;
                    string BlankName = "";
                    BlankName = BlankName.PadLeft(c.Partner.Length, ' ');
                    if (c.ChatPage.Text == BlankName)
                        c.ChatPage.Text = c.Partner;
                    else
                        c.ChatPage.Text = BlankName;
                }
            }
            if (NewMessages)
            {
                tm.SetOverlayIcon(WindowsFormsApplication1.Properties.Resources.newmessage, "New Messages");  
            }
            else
            {
                tm.SetOverlayIcon(null, null);
            }
        }

        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == (Keys.Control | Keys.Tab))
            {
                int NextIndex = tabControl1.SelectedIndex + 1;
                if (NextIndex > tabControl1.TabCount )
                    NextIndex = 0;
                tabControl1.SelectedIndex = NextIndex;
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            if (tabControl1.SelectedIndex != -1)
            {
                string Current = tabControl1.TabPages[tabControl1.SelectedIndex].Name.ToString().Substring(3, tabControl1.TabPages[tabControl1.SelectedIndex].Name.ToString().Length - 3);
                Chats[Current].Active = false;
                tabControl1.TabPages.RemoveAt(tabControl1.SelectedIndex);
            }
        }

    }

    public class Chat
    {
        public TextBox ChatWindow;
        public TextBox SendBox;
        public bool NewMessage = false;
        public TabPage ChatPage;
        public string Partner;
        public bool Composing = false;
        public Button SendButton;
        public bool Active = true;
    }
}

Form1.Designer.cs:

namespace WindowsFormsApplication1
{
    partial class Form1
    {
        /// <summary>
       /// Erforderliche Designervariable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary>
       /// Verwendete Ressourcen bereinigen.
        /// </summary>
        /// <param name="disposing">True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Vom Windows Form-Designer generierter Code

        /// <summary>
       /// Erforderliche Methode für die Designerunterstützung.
       /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden.
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.tabControl1 = new System.Windows.Forms.TabControl();
            this.button2 = new System.Windows.Forms.Button();
            this.comboBox1 = new System.Windows.Forms.ComboBox();
            this.timer1 = new System.Windows.Forms.Timer(this.components);
            this.button1 = new System.Windows.Forms.Button();
            this.SuspendLayout();
            //
            // tabControl1
            //
            this.tabControl1.Dock = System.Windows.Forms.DockStyle.Bottom;
            this.tabControl1.Location = new System.Drawing.Point(0, 52);
            this.tabControl1.Name = "tabControl1";
            this.tabControl1.SelectedIndex = 0;
            this.tabControl1.Size = new System.Drawing.Size(562, 259);
            this.tabControl1.TabIndex = 0;
            //
            // button2
            //
            this.button2.Location = new System.Drawing.Point(153, 9);
            this.button2.Name = "button2";
            this.button2.Size = new System.Drawing.Size(110, 23);
            this.button2.TabIndex = 2;
            this.button2.Text = "Start Chat";
            this.button2.UseVisualStyleBackColor = true;
            this.button2.Click += new System.EventHandler(this.button2_Click);
            //
            // comboBox1
            //
            this.comboBox1.FormattingEnabled = true;
            this.comboBox1.Location = new System.Drawing.Point(12, 11);
            this.comboBox1.Name = "comboBox1";
            this.comboBox1.Size = new System.Drawing.Size(121, 21);
            this.comboBox1.TabIndex = 3;
            this.comboBox1.TextUpdate += new System.EventHandler(this.comboBox1_TextUpdate);
            //
            // timer1
            //
            this.timer1.Enabled = true;
            this.timer1.Interval = 1000;
            this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
            //
            // button1
            //
            this.button1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
            this.button1.Location = new System.Drawing.Point(528, 9);
            this.button1.Name = "button1";
            this.button1.Size = new System.Drawing.Size(22, 23);
            this.button1.TabIndex = 4;
            this.button1.Text = "X";
            this.button1.UseVisualStyleBackColor = true;
            this.button1.Click += new System.EventHandler(this.button1_Click);
            //
            // Form1
            //
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(562, 311);
            this.Controls.Add(this.button1);
            this.Controls.Add(this.comboBox1);
            this.Controls.Add(this.button2);
            this.Controls.Add(this.tabControl1);
            this.DoubleBuffered = true;
            this.KeyPreview = true;
            this.Name = "Form1";
            this.Text = "Facebook Chat";
            this.Load += new System.EventHandler(this.Form1_Load);
            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.Form1_KeyDown);
            this.ResumeLayout(false);

        }

        #endregion

        private System.Windows.Forms.TabControl tabControl1;
        private System.Windows.Forms.Button button2;
        private System.Windows.Forms.ComboBox comboBox1;
        private System.Windows.Forms.Timer timer1;
        private System.Windows.Forms.Button button1;
    }
}

14 comments:

  1. hi! Good work!
    I really appreciate the work you done!
    i have tried to download the given link but it wont redirect to the file will you please mail me the Facebook Chat related Samples..!
    Email: shekharshete1987@gmail.com

    One more thing can we use same for ASP.NET Web Applications?
    Please write some blogs on FacebookChat in Asp.net and let me know..!

    Thanks,
    www.shekharshetemcts.wordpress.com

    ReplyDelete
  2. Good work man! I really needed this :)

    ReplyDelete
  3. Thanks for your interest and nice feedback!
    For me the download link still works, but I send you the project by email.

    ReplyDelete
    Replies
    1. Seems though your email is not valid, I cannot send sorry.

      Delete
  4. Hi Oliver,
    Thank your for the code.
    I downloaded the code and when I ran it after entering my facebook username and password, It did not login.
    Even form 1 did not open.

    Could you help me understand what could be the problem?


    Regards,
    Ashish

    ReplyDelete
    Replies
    1. I checked and found that it is not able to come out of the while loop :

      while (CollectingContacts)
      {
      if (StartCollectingContacts)
      CollectingContacts = false;
      System.Threading.Thread.Sleep(10);
      }

      Please help.

      Delete
    2. Hey,
      I am sorry, like this I cannot really tell you, what the error is.
      But the first thing I want to say is, that the login can take some time, a couple seconds for me. Secondly I am waiting 1000 instead of 10 ms like you in the loop, maybe this increases your waiting time.
      Then, for me your error (form never shows up) occurs, if the connection or login data is incorrect.
      If you copied my code 1:1, it should work, so are you sure your username and password are correct? The username is not the username you use for your Facebook login! I posted about that in this introductory post: http://csharp-tricks-en.blogspot.de/2013/10/connect-to-facebook-chat.html
      Furthermore, if this does not work, I recommend starting with the little examples from the previous posts and slowly increasing the complexity.

      Hope this helps.
      Best
      Oliver

      Delete
  5. Hey, Oliver This is Really dam good article great work man,
    can you please send me the project by e-mail...
    my mail id is: bagchineil518@gmail.com

    ReplyDelete
  6. Hi Oliver!

    Thanks for code, but its not work for me. I recive this error :

    "agxXMPP.dllagsXMPP.xmppClientConnection.OnRosterIQ(agsXMPP.protocol.client.IQ iq)" and the list friend not show.

    it happens when execute function void xmpp_OnRosterItem(...)

    Can you help me solve it?

    Thanks!

    ReplyDelete
    Replies
    1. I m using VS2010 . Net Framework 4.0

      Delete
    2. Hey, sorry Wilson, you are right, I just checked and found out that since the release of the Facebook API 2.0 this function is no longer supported.

      Delete
  7. hi oliver,
    i am not login to your chat app. it wll not come out in while loop of while (CollectingContacts) and dosen't give me response please help me

    ReplyDelete
    Replies
    1. Hey, Prashant, yes that's unfortunately true, please check the comments above.

      Delete