Donnerstag, 24. Oktober 2013

Erweiterter Facebook Chat Client

Hinweis: Seit der Veröffentlichung der Facebook API 2.0 im April 2014 ist die Chatfunktion deaktiviert, und damit leider dieser Code nicht mehr lauffähig.

Als vorerst letzten Post zum Thema Facebook möchte ich heute einen etwas erweiterten Chat Client vorstellen.
Er benutzt alle Techniken aus den vorigen Posts.
Dieser sieht wie folgt aus:



Name und Passwort werden im Quellcode festgelegt. Beim Starten des Programms lädt dieses die Freundesliste.
Fängt der Benutzer an, einen Namen in die Combobox einzugeben, füllt sich diese mit passenden Freunden. Nach einem Klick auf "Start Chat" wird ein neues Chat Fenster geöffnet.
In diesem erscheinen die ankommenden Nachrichten des Kontaktes, außerdem können Nachrichten an diesen mit einem Klick auf "Send" oder per Drücken der Entertaste gesendet werden. Mittels "Strg + Tab" kann zwischen den Chatfenstern gewechselt werden.
Natürlich öffnet sich auch ein Chat Fenster, wenn ein Kontakt eine Nachricht schreibt. Kommt eine neue Nachricht an, wird ein Benachrichtigungssound abgespielt, außerdem blinkt der Name sowie das Icon in der Taskleiste ändert sich.
Vorrausetzung hierfür ist die Einbindung des Windows API Codepacks.
Tippt ein Chatkontakt, wird dies durch die Einblendung eines Tastaturicons angezeigt.

Nun zum Code, bzw. zuerst der Downloadlink, über welchen der komplette Quellcode inklusive ausführbarer Datei heruntergeladen werden kann.

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;
    }
}

Kommentare:

  1. Hallo,
    leider kriege ich deinen Client nicht zum Laufen. Win Api habe ich runtergeladen und auch eingebunden.Es kommt auch so richtig kein Fehler.Nur im "Ausgabe"-Fenster kommt
    Der Thread 0xfa8 hat mit Code 259 (0x103) geendet.
    Ich vermute ja mal,dass Facebook die Art der anmeldedaten geändert hat.Ich finde in keiner URL meinen Nmaen.Dort finde ich nur die ID und diese habe ich auch im Code geschrieben.
    Könntest du evtl. sagen,woran es liegen kann ,dass ich das ganze nicht kompiliert kriege?
    Vielen Dank!

    AntwortenLöschen
    Antworten
    1. Hallo Anna,
      tut mir leid, der Fehler sagt mir jetzt so nix, bei mir funktioniert der gleiche Code aber noch, an der Anmeldung hat und sollte sich eigentlich auch nix ändern, ich vermute eher es ist ein Verbindungsfehler o.ä.
      Hast du denn mal die vorigen Posts ausprobiert? Dort bauen wir uns ja sehr kleinschrittig einen solchen Client zusammen, dann kannst du viel einfacher sehen, woran es liegt.

      Löschen