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

Mittwoch, 23. Oktober 2013

Ermitteln wann Chat Partner im Facebook Chat tippt

Heute möchte ich nur ganz kurz zeigen, wie man mit C# ermittelt, wann ein Chat Partner im Facebook Chat tippt - so wie dies zum Beispiel auch auf Facebook angezeigt wird.
Grundlage hierfür ist der vorige Post über das Empfangen von Nachrichten. Von da aus muss nur eine Kleinigkeit geändert werden, denn das "Tipp" Ereignis wird wie eine Chat Nachricht versendet: Die Nachricht hat dann keinen Body, dafür steht die Eigenschaft Chatstate auf Composing. Hört der Benutzer auf zu tippen, wird eine Nachricht mit Chatstate active gesendet.
Eine beispielhafte MessageCallback Funktion könnte dann so aussehen:

private void MessageCallBack(object sender, agsXMPP.protocol.client.Message msg, object data)
{
    if (msg.Body != null)
    {
        MessageBox.Show(msg.From + ": " + msg.Body);
    }
    else
    {
        if (msg.Chatstate == agsXMPP.protocol.extensions.chatstates.Chatstate.composing)
            MessageBox.Show(msg.From + ": Started Typing");
        if (msg.Chatstate == agsXMPP.protocol.extensions.chatstates.Chatstate.active)
            MessageBox.Show(msg.From + ": Stopped Typing");
    }
}
Dazu ist aber noch zu erwähnen, dass diese Funktion leider nicht wirklich gut funktioniert. Der Beginn einer Nachrichteneingabe wird einwandfrei übermittelt, das Ende leider öfter nicht. Deswegen habe ich darüber nachgedacht, mit einem Timer den Status "Tippt" zurückzusetzen. Falls jemand einen besseren Vorschlag hat, würde ich mich über Kommentare freuen.

Dienstag, 22. Oktober 2013

Rudimentärer 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.

Heute möchte ich die vorigen Posts kombinieren und einen rudimentären Client für den Facebook Chat vorstellen.
Beim Laden des Formulars wird die Verbindung hergestellt und alle Freunde aus der Kontaktliste in dem Dictionary Friends gespeichert. Zusätzlich wird für jeden ein MessageCallback angelegt, sodass jegliche ankommenden Nachrichten empfangen werden.
Ist dieser Vorgang abgeschlossen, wird das Formular angezeigt.
Es enthält 2 Textboxes und einen Button. In der textBox1 kann der Empfänger angegeben werden, in der textBox2 der Nachrichtentext. Per Klick auf den Button wird die Nachricht dann gesendet. Eingehende Nachrichten werden jeweils mit einer MessageBox angezeigt.
Als Empfänger muss der vollständige Facebook Name angegeben werden, das heißt so, wie dieser bei Facebook zum Beispiel in der Chat Leiste angezeigt wird.
Das Programm wandelt diesen Namen dann mit dem Dictionary Friends in die entsprechende Jid um, bei eingehenden Nachrichten die Jid in den Namen.

Da alle Code Teile aus den vorigen Posts bekannt sind, hier direkt der Code:

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 System.Net;
using System.IO;
using Newtonsoft.Json.Linq;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {

        public Form1()
        {
            InitializeComponent();
        }

        XmppClientConnection xmpp = new XmppClientConnection("chat.facebook.com");
        Dictionary<string, string> Friends = new Dictionary<string, string>();
        bool StartCollectingContacts = false;
        bool CollectingContacts = true;

        private void Form1_Load(object sender, EventArgs e)
        {
            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)
        {
            if (msg.Body != null)
            {
                MessageBox.Show(Friends[msg.From.ToString()] + ": " + msg.Body);
            }
        }

        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 button1_Click(object sender, EventArgs e)
        {
            string ReceiverId = Friends.First(x => x.Value == textBox1.Text).Key;
            SendMessage(textBox2.Text, ReceiverId);
        }

    }
}

Montag, 21. Oktober 2013

Facebook Nachrichten empfangen

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.

In diesem Post möchte ich zeigen, wie man mit C# Nachrichten des Facebook Chat empfängt. Da "normale" Nachrichten und Chat Nachrichten jedoch über das gleiche Prinzip versendet werden, können hiermit also nicht nur Chat Nachrichten empfangen werden.
Voraussetzung ist eine aufgebaute Verbindung zum Facebook Chat mittels XMPP, weiterhin könnte der vorige Post nützlich sein, in dem gezeigt wird, wie die Jids der Freunde ausgelesen werden, welche zum Empfangen benötigt werden.

Um Nachrichten von Kontakten empfangen zu können, muss für jeden dieser ein sogenanntes MessageCallback angelegt werden:

xmpp.MessageGrabber.Add(new Jid("-100006476847029@chat.facebook.com"), new BareJidComparer(), new MessageCB(MessageCallBack), null);

Wenn nun von dem entsprechenden Kontakt eine Nachricht ankommt, wird die Funktion MessageCallBack() aufgerufen.
Diese kann zum Beispiel so aussehen:

private void MessageCallBack(object sender, agsXMPP.protocol.client.Message msg, object data)
{
    if (msg.Body != null)
    {
        MessageBox.Show(msg.From + ": " + msg.Body);
    }
}

Der komplette Code:

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;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        XmppClientConnection xmpp = new XmppClientConnection("chat.facebook.com");

        private void Form1_Load(object sender, EventArgs e)
        {
            xmpp.OnLogin += new ObjectHandler(OnLogin);
            xmpp.Open("name", "password");
        }

        private void OnLogin(object sender)
        {
            xmpp.MessageGrabber.Add(new Jid("-100006476847029@chat.facebook.com"), new BareJidComparer(), new MessageCB(MessageCallBack), null);
        }

        private void MessageCallBack(object sender, agsXMPP.protocol.client.Message msg, object data)
        {
            if (msg.Body != null)
            {
                MessageBox.Show(msg.From + ": " + msg.Body);
            }
        }
    }
}

Sonntag, 20. Oktober 2013

Facebook Nachricht senden

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.

In diesem Post möchte ich zeigen, wie man mit C# Nachrichten über den Facebook Chat versendet. Da "normale" Nachrichten und Chat Nachrichten jedoch über das gleiche Prinzip versendet werden, können hiermit also nicht nur Chat Nachrichten versendet werden.
Voraussetzung ist eine aufgebaute Verbindung zum Facebook Chat mittels XMPP, weiterhin könnte der vorige Post nützlich sein, in dem gezeigt wird, wie die Jids der Freunde ausgelesen werden, welche zum Senden benötigt werden.

Ist die Verbindung zu Facebook aufgebaut, lassen sich Nachrichten mit einer Codezeile senden, wir verpacken diese in einer Funktion:

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

Beachtet, dass die Jid des Empfängers mit einem "-" beginnen muss. Folgende Zeile schickt beispielsweise eine Nachricht an mich:

SendMessage("Hallo Oliver.", "-100006476847029@chat.facebook.com");

Der komplette Code:

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;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        XmppClientConnection xmpp = new XmppClientConnection("chat.facebook.com");

        private void Form1_Load(object sender, EventArgs e)
        {
            xmpp.OnLogin += new ObjectHandler(OnLogin);
            xmpp.Open("name", "password");
        }

        private void OnLogin(object sender)
        {
            SendMessage("Hallo Oliver.", "-100006476847029@chat.facebook.com");
        }

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

Samstag, 19. Oktober 2013

Facebook Freundesliste auslesen

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.

Die Freundesliste könnte man auch über die Graph API abfragen, ähnlich zu dem Prinzip in diesem Post. Dafür braucht man allerdings ein Access Token welches öfter mal abläuft und muss JSON Objekte verarbeiten.
Deswegen benutze ich eine fertige Funktion des XMPP Protokoll. Vorrausetzung hierfür ist eine stehende Verbindung zum Facebook Chat, wie ich sie im vorigen Post beschrieben habe.
Wir müssen nun lediglich nur noch die Verabeitung des OnRosterItem Ereignisses hinzufügen, welches für jeden Kontakt in der Kontaktliste einmal aufgerufen wird, und können so alle Kontakte auslesen.
Wir können dies zum Beispiel beim erfolgreichen Login tun:

private void OnLogin(object sender)
{
    xmpp.OnRosterItem += new XmppClientConnection.RosterHandler(xmpp_OnRosterItem);
}

Zur bequemen Speicherung der Freunde bietet sich ein Dictionary an:

Dictionary<string, string> Friends = new Dictionary<string, string>();

In der Verarbeitung von OnRosterItem speichern wir dann alle Kontakte in diesem Dictionary:

private void xmpp_OnRosterItem(object sender, agsXMPP.protocol.iq.roster.RosterItem item)
{
    Friends.Add(item.GetAttribute("jid").ToString(), item.GetAttribute("name").ToString());
}

Zum Senden / Empfangen von Nachrichten wird die Jid des Gesprächpartners benötigt, weswegen wir diese speichern, den Namen benutzen wir zur Umsetzung dieser in den bekannten Namen.
Jid und Name können dann folgendermaßen abgefragt werden:

string JidBloggeroliver = Friends.First(x => x.Value == "Olli Michael").Key;
string NameBloggeroliver = Friends["-100006476847029@chat.facebook.com"];

Nun der komplette Code. Ich habe nach dem Öffnen der Verbindung eine Schleife eingeführt, welche wartet, bis alle Kontakte geladen sind.

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 System.Net;
using System.IO;

using agsXMPP;
using agsXMPP.protocol.client;
using agsXMPP.protocol.iq.roster;
using agsXMPP.Collections;

namespace WindowsFormsApplication2
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        XmppClientConnection xmpp = new XmppClientConnection("chat.facebook.com");
        bool StartCollectingContacts = false;
        bool CollectingContacts = true;
        Dictionary<string, string> Friends = new Dictionary<string, string>();

        private void Form1_Load(object sender, EventArgs e)
        {
            xmpp.OnLogin += new ObjectHandler(OnLogin);
            xmpp.Open("name", "password");

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

            string JidBloggeroliver = Friends.First(x => x.Value == "Olli Michael").Key;
            string NameBloggeroliver = Friends["-100006476847029@chat.facebook.com"];
        }

        private void OnLogin(object sender)
        {
            xmpp.OnRosterItem += new XmppClientConnection.RosterHandler(xmpp_OnRosterItem);
        }

        private void xmpp_OnRosterItem(object sender, agsXMPP.protocol.iq.roster.RosterItem item)
        {
            StartCollectingContacts = true;
            CollectingContacts = true;
            Friends.Add(item.GetAttribute("jid").ToString(), item.GetAttribute("name").ToString());
        }
    }

}

Freitag, 18. Oktober 2013

Mit Facebook Chat verbinden

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.

In den vorigen beiden Posts (Daten von Facebook auslesen und Facebook Posts veröffentlichen) ging es um die Verbindung zu Facebook mittels der Graph API. Diese ist eine einfache HTTP basierte API, über die mit HTTP Abfragen auf die Daten von Facebook zugegriffen werden kann.
Heute möchte ich zeigen, wie man sich mittels C# in den Facebook Chat einloggt, welcher auf dem XMPP (früher Jabber) Protokoll basiert.
XMPP ist ein auf XML basierendes Kommunikationsprotokoll, welches hauptsächlich zum Instant Messaging genutzt wird.
Einige bekannte Programme, wie Google Talk und WhatsApp benutzen dieses Protokoll, viele andere, wie z.B. ICQ, Skype und eben Facebook bieten eine Möglichkeit zur Anbindung an.
Das XMPP Protkoll ist sehr verständlich und strukturiert aufgebaut, sodass wir uns ohne viel Code über C# im Facebook Chat einloggen können und Nachrichten schicken und empfangen können. Da der Chat und die "normale" Nachrichtenfunktion nicht mehr getrennt sind wie früher, können wir mit dem Code auch herkömmliche Nachrichten senden und empfangen.
Ein Auslesen der Kontaktliste ist ebenfalls möglich.
Ich werde den Chat kleinschrittig einführen. Heute werde ich die Einbindung von XMPP zeigen und das Anmelden beim Facebook Service.
In den nächsten Tagen werden dann die folgenden Posts online kommen:

Auslesen der Kontaktliste
Senden von Nachrichten
Empfangen von Nachrichten
Vollständiger Chat Client
Anzeigen des "Schreibt" Status
Verbesserter Facebook Chat

Nun aber zum ersten Teil. Theoretisch könnten wir uns auch selber ein XMPP Framework schreiben, welches dann über Sockets etc. die erforderlichen Daten an den Server schickt und von diesem empfängt. Ich verwende jedoch eine fertige Library, nämlich agsXMPP, welche uns einiges an Arbeit abnimmt.
Diese bitte zuerst einmal herunterladen und installieren.
Möchten wir diese im Projekt benutzen, müssen wir einen Verweis darauf einbinden. Dazu klicken wir auf "Verweis hinzufügen - Durchsuchen" und navigieren zum Installationsverzeichnis \agsxmpp\bin\(passende .Net Version, wahrscheinlich 3.5)\(Debug | Release)\agsXMPP.dll.
In den nächsten Posts brauchen wir dann die folgenden using Direktiven:


using agsXMPP;
using agsXMPP.protocol.client;
using agsXMPP.protocol.iq.roster;
using agsXMPP.Collections;

Dann können wir die Verbindung zum Facebook Chat Server (chat.facebook.com) vorbereiten über:

XmppClientConnection xmpp = new XmppClientConnection("chat.facebook.com");

Vor dem eigentlichen Anmelden hinterlegen wir eine Funktion zur Reaktion auf das OnLogin Ereignis. Die Anmeldung läuft nämlich asynchron ab, sobald sie erfolgreich abgeschlossen wurde, wird das Ereignis aufgerufen:

xmpp.OnLogin += new ObjectHandler(OnLogin);

Die Funktion sieht folgendermaßen aus, im Prinzip sind alle Anweisungen darin optional, die letzten 3 zeigen unseren Anmeldestatus als Online an:

private void OnLogin(object sender)
{
    MessageBox.Show("Login successful.");
    Presence p = new Presence(ShowType.chat, "Online");
    p.Type = PresenceType.available;
    xmpp.Send(p);
}

Danach können wir die eigentliche Verbindung mit unseren Daten herstellen. Hierfür wird der Facebook Name (der Name in der URL des Profils, z.B. für https://www.facebook.com/hans.wurst hans.wurst) sowie das Passwort benötigt:

xmpp.Open("name", "password");

Der vollständige Code könnte z.B. so aussehen:
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;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        XmppClientConnection xmpp = new XmppClientConnection("chat.facebook.com");

        private void Form1_Load(object sender, EventArgs e)
        {
            xmpp.OnLogin += new ObjectHandler(OnLogin);
            xmpp.Open("", "");
        }

        private void OnLogin(object sender)
        {
            MessageBox.Show("Login successful.");
            Presence p = new Presence(ShowType.chat, "Online");
            p.Type = PresenceType.available;
            xmpp.Send(p);
        }
    }
}

Dienstag, 15. Oktober 2013

Binomialkoeffizient berechnen

In diesem Post möchte ich fertigen Code zum Berechnen des Binomialkoeffizientens vorstellen, welcher z.B. in der Kombinatorik ein wichtiges Hilfsmittel ist.
Der Binomialkoeffizient zweier Zahlen n und k wird dargestellt als

und berechnet sich zu n!/(k! * (n-k)!), wobei x! die Fakultät bezeichnet.
Anstatt diese Formel direkt in C# umzusetzen, berechne ich zuerst den Teil n!/k! = n * (n - 1) * ... * (k + 1), was die benutzten Zahlen wesentlich verkleinert, die Geschwindigkeit erhöht und Überläufe erst später auftreten lässt.
Der Code, ich benutzte die Funktion Factorial() aus dem vorigen Post zur Fakultätsberechnung:

        public ulong BinC(ulong n, ulong k)
        {
            if (n < k)
                return 0;

            ulong Numerator = 1;
            for (ulong i = n; i > k; i--)
            {
                Numerator *= i;
            }
            ulong Demoninator = Factorial(n - k);
            return (Numerator / Demoninator);
        }

        public ulong Factorial(ulong n)
        {
            ulong Result = 1;
            for (ulong i = 1; i <= n; i++)
            {
                Result *= i;
            }
            return Result;
        }