• 当机器人遇到咱们, 就要聊聊天. Message! DotMSN2.0 Robot(三) [原鸟]




    内容记:

    MSN Robot就是MSN机器人, 也可以喊成MSN聊天机器人.再解释就是会自动和你聊天的MSN,再再解释就是……, 我短路了(耳朵冒烟中)

    本随笔是MSN Robot随笔系列第三篇, 内容是关于MSN Robot信息发送和接收.


     

     



    前记:

     

    香吉士:我想要找到“ALL BLUE”
    鲁飞:我要当海贼王!!
    索隆:我要成为一个最伟大的剑客!!
    娜美:我要画完全世界的地图!!
    骗人布:我......我要成为一个勇敢的海上战士!!

    大家:出发吧!进入伟大的航路”!!

     

    不管大家看到这部随笔的过程是怎样的, 但咱们一定是追随着梦想的!

    不管太阳轮回东西, 月明星暗, 我们的旗帜都永远飘扬!

     

    絮絮了几句, 咱们今天来看看Message J 这是咱们MSN Robot的重头戏

     

    Message比较大头, 计划分成两部分, 一部分实现, 一部分调用, 今天咱们先来实现了消息发送和接收代码吧 J 这样咱们的Robot已经焊接到了胸部, 明天加上几行调用, 宇宙银河系地球中国北京XXXX号中间一个屋子里边的一台机器中的无敌变形金刚就出现了! 应该给它起个什么名字好呢? J ( 陷入沉思中 )

     

    我的建议把:

    Switchboard_SessionClosed

    Switchboard_ContactJoined

    Switchboard_ContactLeft

    Conversation_Closing

    Switchboard_TextMessageReceived

    SendInput

    几个与消息亲密的函数包装成一个类. 当然, 这样做是有好处的 J 不然咱们明天在处理多用户服务的时候会遇到相当麻烦的小槛 J

     

    只听半空 “dang” 的一声, 一个看起来很可爱的类就掉下来了. .. 我多希望它是机器猫呀….

     

    using System;

    using System.Drawing;

    using System.Collections;

    using System.ComponentModel;

    using System.Windows.Forms;

    using XihSolutions.DotMSN;

    using System.Data;

    using System.Data.SqlClient;

    using System.Text;

    using System.Net;

    using System.Net.Sockets;

     

     

    class Message

    {

     

            // conversation

            private Conversation _conversation;

     

            //主画面

            DotMSNClient.ClientForm from;

     

            //ContactJoined标记

            //true:  成功加入

            //flase: 尚未加入

            private bool bRun = false;

     

            //IP and Port

            private IPEndPoint iepSeverAddress=null;

     

            //数据片最大长度

            public static long SLICE_MAX_LENGTH = 800;

     

            //信息记录

            private const string STR_CFG_CONTACT_LEFT = "用户已经退出会话, 消息发送不成功\r\n";

            private const string STR_CFG_REVERT_TIMEOVER = "* 回复超时\r\n";

            private const string STR_CFG_QUESTION_OK = "* 回复答案\r\n";

            private const string STR_CFG_SBPROCESSOR_NULL = "Conversation SwitchboardProcessor为空\r\n";

            private const string STR_CFG_MESSAGEEMPTY = "欲发送消息为空\r\n";

            private const string STR_CFG_OVERTIME = "操作超时, 请稍候再试\r\n";

            private const string STR_CFG_QUESTION_OVER = "* 回复答案太长\r\n";

            private const string STR_CFG_QUESTION_NO = "* 回复没找到\r\n";

     

            //用户消息

            string STR_USER_TIMEOVER = "操作超时, 请稍候再试";

            string STR_USER_ADD = ", 忘记告诉你... 你教我的我看不懂... 我不太懂中文... Yeah~ 我忘记了";

            string STR_USER_HELP = "询问格式: ans 今天天气如何?\r\n" +

                "寻求帮助help\r\n\r\n" +

                "如果需要与客服聊天请加入MSN:zhangyv1234@hotmail.co.jp\r\n" +

                "请不要和机器人吵架!不要说shit...";

            string STR_USER_SEVERCLOSE = "后台服务器没有开哟, 请稍候再试";

            string STR_USER_NOANSWER = "呵呵, 没找到";

            string STR_USER_MESSAGELONG = "消息太长了.. 咱缓缓吧...累死我了...";

     

     

     

            /// <summary>

            /// The conversation object which is associated

            /// </summary>

            public Conversation Conversation

            {

                get { return _conversation; }

            }

     

     

            public Message(Conversation conversation, ClientForm thisFrom)

             {

     

                  _conversation = conversation;

     

                from = thisFrom;

     

                //获得用户配置的说话

                STR_USER_TIMEOVER = from.STR_USER_TIMEOVER;

                STR_USER_ADD = from.STR_USER_ADD;

                STR_USER_HELP = from.STR_USER_HELP;

                STR_USER_SEVERCLOSE = from.STR_USER_SEVERCLOSE;

                STR_USER_NOANSWER = from.STR_USER_NOANSWER;

                STR_USER_MESSAGELONG = from.STR_USER_MESSAGELONG;

              

                //消息接收事件

                  Conversation.Switchboard.TextMessageReceived += new TextMessageReceivedEventHandler(Switchboard_TextMessageReceived);

                 

                //SessionCloes事件

                Conversation.Switchboard.SessionClosed += new SBChangedEventHandler(Switchboard_SessionClosed);

                 

                //用户加入连接事件

                Conversation.Switchboard.ContactJoined += new ContactChangedEventHandler(Switchboard_ContactJoined);

                 

                //用户离开连接事件

                Conversation.Switchboard.ContactLeft   += new ContactChangedEventHandler(Switchboard_ContactLeft);          

             }

     

            // 判断是否超时

            private bool GetTimeOverTimeSpan()

            {

                DateTime tBeginTime = DateTime.Now;

                DateTime tCurrTime;

     

                //等待ContactJoined事件先行触发, Contact进入Conversation后才能继续处理.

                while (!bRun)

                {

                    tCurrTime = DateTime.Now;

                    TimeSpan dif = tCurrTime - tBeginTime;

     

                    // 计算超时, 如果秒后Contact尚未进入, 就断定超时退出处理.

                    if (dif.Seconds > 10)

                    {

                        //from.richTextBox3.AppendText(STR_CFG_OVERTIME);

                        return false;

                    }

                }

                return true;

            }

     

            //消息发送

            public void SendInput(string strMessage)

            {

     

                string inputTextBox = strMessage;

     

                // 判断是否超时

                if (!GetTimeOverTimeSpan())

                {

                    from.richTextBox3.AppendText(STR_CFG_OVERTIME);

                    return;

                }

     

                //消息为空

                if (inputTextBox.Length == 0)

                {

                    from.richTextBox3.AppendText(STR_CFG_MESSAGEEMPTY);

                    return;

                }

     

                //SwitchboardProcessornull

                if (Conversation.SwitchboardProcessor == null)

                {

                    from.richTextBox1.AppendText(STR_CFG_SBPROCESSOR_NULL);

                    return;

                }

     

                // if there is no switchboard available, request a new switchboard session

                if (Conversation.SwitchboardProcessor.Connected == false)

                {

                    Conversation.Messenger.Nameserver.RequestSwitchboard(Conversation.Switchboard, this);

                }

     

                // Contacts, 用户已经left会话

                if (Conversation.Switchboard.Contacts.Count == 0)

                {

                    from.richTextBox1.AppendText(STR_CFG_CONTACT_LEFT);

                    return;

                }

        

     

                // 准备消息

                TextMessage message = new TextMessage(inputTextBox);

     

                /* You can optionally change the message's font, charset, color here.

                 * For example:

                 * message.Color = Color.Red;

                 * message.Decorations = TextDecorations.Bold;

                 */

     

                // 终于可以发送了

                Conversation.Switchboard.SendTextMessage(message);

     

            }

     

            /// <summary>

            /// 把消息显示在界面

            /// </summary>

            /// <param name="name"></param>

            /// <param name="text"></param>

            private void PrintText(string name, string text)

            {

                from.richTextBox3.AppendText(name + ": \r\n" + text + "\r\n");

                from.richTextBox3.ScrollToCaret();

            }

     

     

            /// <summary>

            /// 接收到消息事件

            /// </summary>

            /// <param name="sender"></param>

            /// <param name="e"></param>

            private void Switchboard_TextMessageReceived(object sender, TextMessageEventArgs e)

            {

     

                // 得到用户命令的小写串

                string cmd = e.Message.Text.ToLower();

     

                // 用户命令存放字符串

                string strShirtCmd = string.Empty;

     

                // 显示接收到的消息到界面

                PrintText(e.Sender.Name, e.Message.Text);

     

                // 解析用户命令

                if (cmd.Length > 3)

                    strShirtCmd = cmd.Substring(0, 3);

     

                // 转换SenderSBMessageHandler

                SBMessageHandler SBSender = (SBMessageHandler)sender;

                

                string strMail="";

     

                // 发送正在编辑信息

                SBSender.SendTypingMessage();

     

                // 判断是否超时

                if (!GetTimeOverTimeSpan())

                {

                    from.richTextBox3.AppendText(STR_CFG_OVERTIME);

                    return;

                }

     

                // 用户请求help时候处理

                if (cmd == "help")

                {

                    OnHelp(SBSender, e.Sender.Name, e.Message.Text);

                }

     

                // 用户询问处理

                else if (strShirtCmd == "ans")

                {

                    OnAnswerByDBSearch(SBSender, e.Sender.Name, e.Message.Text);

                    //OnAnswerBySocketConn(SBSender, e.Sender.Name, e.Message.Text);

                }

               

                // 用户知识库添加处理

                else if (strShirtCmd == "add")

                {

                    SBSender.SendTextMessage(new TextMessage(STR_USER_ADD));

                }

     

                // 用户骂人.... 回骂...

                else if (cmd.StartsWith("shit") || cmd.StartsWith("fuck"))

                {

                    for (int x = 0; x < 5; x++)

                    {

                        SBSender.SendTextMessage(new TextMessage("!@#!@#%$#%$^&^%$@$@%$#%#$"));

                    }

                }

     

                // 默认处理, Help

                else

                {

                    OnHelp(SBSender, e.Sender.Name, e.Message.Text);

                }

     

            }

     

     

            /// <summary>

            /// Help消息回复

            /// </summary>

            /// <param name="con"></param>

            /// <param name="Mail"></param>

            /// <param name="cmd"></param>

            void OnHelp(SBMessageHandler con, string Mail, string cmd)

            {

                con.SendTextMessage(new TextMessage(STR_USER_HELP));

            }

     

            /// <summary>

            /// 测试数据库处理

            /// </summary>

            /// <param name="SBSender"></param>

            /// <param name="name"></param>

            /// <param name="text"></param>

            void OnAnswerByDBSearch(SBMessageHandler SBSender, string name, string text)

            {

     

     

                text = prepare(text);

     

                DotMSN.DBClass db = new DotMSN.DBClass(@"Server=localhost;Integrated Security=True;" + "Database=Spider");

     

                SqlDataReader dbReader = db.GetSqlDataReader("select title,url from page where title like '%" + text + "%'");

     

                StringBuilder strBuilder = new StringBuilder();

                string strResult = "";

     

                while (dbReader.Read())

                {

     

                    strResult += dbReader["title"].ToString().Trim() + "\r\n" + dbReader["url"].ToString().Trim() + "\r\n";

     

                }

     

                dbReader.Close();

                db.Close();

     

     

                SendAnswer(SBSender, strResult);

     

            }

     

     

            /// <summary>

            /// 将答案传回

            /// </summary>

            /// <param name="SBSender"></param>

            /// <param name="text"></param>

            private void SendAnswer(SBMessageHandler SBSender, string text)

            {

     

                // 没有找到答案

                if (string.IsNullOrEmpty(text))

                {

                    SBSender.SendTextMessage(new TextMessage(STR_USER_NOANSWER));

                    from.richTextBox3.AppendText(STR_CFG_QUESTION_NO);

                }

                else

                {

     

                    SBSender.SendTextMessage(new TextMessage("hallo,World"));

     

                    //记录"* 回复答案"

                    from.richTextBox3.AppendText(STR_CFG_QUESTION_OK);

                }

            }

     

            /// <summary>

            /// 命令准备

            /// </summary>

            /// <param name="text"></param>

            /// <returns></returns>

            private string prepare(string text)

            {

                if (text.Length > 3)

                {

                    text = text.Substring(3, text.Length - 3);

                }

                else

                {

                    //SBSender.SendTextMessage(new TextMessage("命令错误"));

                    return text;

                }

                text = text.Trim();

                return text;

            }

    }

     

     

    通览这篇文章准备呛我行的你肯定第一眼就看到了这两个函数. 乃是整个Robot核心之核心, 重点之重点.

    Ok, 讲解完了. 大家自己看吧……….( 我是很邪恶很自私的说L )

     
     Switchboard_TextMessageReceived

     SendInput


    我是下边讲解的例子


     


    也许咱们已经对照着DotMSN来试着实现消息的接收和发送了, 又也许你会遇到这样一个问题, 就是发送消息和接收消息会出现失败的情况, 或者是发送出去的消息对方没有收到, 又或者是对方发来的消息自己没有收到. 有的时候这种情况会很明显, 有的时候又靠着rp问题才能重现. 再也许你会奇怪另一个问题, 就是很哲理的.. 这个消息是怎么发出去的? Ok, 它们是一个答案, 关于后一个问题咱们先来解决它的表象吧
    J 好吗?

     

    DotMSN, 消息发送是分为这样几步的.

     

    , 取到用户Contact

    比如:

    Contact selectedContact = (Contact)ContactListView.SelectedItems[0].Tag;

     

    , 创建Conversation, 也可以叫创建会话.

    比如:

    Conversation Cconversation = messenger.CreateConversation();

     

    , 将用户加入到会话中

    Conversation.Invite(selectedContact);

     

    , 发送消息

    Conversation.Switchboard.SendTextMessage(message);

     

    J 是不是很简单? 行云流水丝绸一样飘过. 消息就这样发送出去了.

     

    等等! 是不是发送成功了呢? 在这样天鹅绒光滑的水面下有没有什么杀人的石头呢?

     

    , 应该是有, 不然科幻小说的作者就没有写下去的动力了.

     

    比如, 最不幸的就是本人, 某一天月黑风高在早晨可以像小舟一样划过水面的程序, 下午就触礁了.

     

    在消息发送时

    if (Conversation.SwitchboardProcessor.Connected == false)

    这句上出现了永远过不去的, 对象没有绑定到实例. 这时候SwitchboardProcessornull

     

    为什么会这样子?! 为什么会出现永恒的, 突然的, null的地狱呢? 这是诅咒还是宿命?!

    路人甲: 这是rp问题.

     

    Ok, 本人是不能允许我这样风华绝代大帅哥出现rp问题的. 抄刀灭之!

     

    这时候突然想到曾经神仙老爷爷说过的话, 发送消息之前一定要双方进入会话.

     

    顺藤摸瓜赶紧查, yi, 为什么在Invite好友之后程序没有马上触发Switchboard_ContactJoined事件通知咱们用户进入对话呢?

     

    是不是在Invite之后要等待服务器的通知? , 一定是这样.

     

    再敬仰的去看DotMSN的程序 yi, 为什么在Invite好友之后打了一个很二的时间差呢? 如果Invite的时候有网络延迟, 运行到send的时候对方还没有加入会话呢?! o, null hell, send fail.

     

     

    于是在咱们的相关消息函数中有这样一段代码:

     

    // 判断是否超时

    // 这里是非常关键的地方, 可以看下边的解释

    if (!GetTimeOverTimeSpan())

    {

        from.richTextBox3.AppendText(STR_CFG_OVERTIME);

        return;

    }

     

    可以看一看它的函数本体, , 是一个很白痴的while循环! 它在一直等待一个名字叫bRun的白痴看不懂是什么的标记变成true

     

    // 判断是否超时

    private bool GetTimeOverTimeSpan()

    {

    DateTime tBeginTime = DateTime.Now;

    DateTime tCurrTime;

     

    //等待ContactJoined事件先行触发, Contact进入Conversation后才能继续处理.

    while (!bRun)

    {

    tCurrTime = DateTime.Now;

    TimeSpan dif = tCurrTime - tBeginTime;

     

    // 计算超时, 如果秒后Contact尚未进入, 就断定超时退出处理.

    if (dif.Seconds > 10)

    {

    //from.richTextBox3.AppendText(STR_CFG_OVERTIME);

    return false;

    }

    }

    return true;

    }

     

     

    你会指着我的鼻子喊: 这个叫bRun的白痴标记在什么时候才能变成true?!

    答案是 在好友已经加入对话的事件里.

    private void Switchboard_ContactJoined(object sender, ContactEventArgs e)

    {

    bRun = true;

    }




     

    , 结论是, 世界终于和平, 人类终于美好了~~ 王子和公主幸福的生活在一起, 相知相伴, 永不分离.

     

    流氓: 等等! 还没结束, 在程序的后台又发生了什么呢??!!

     

    可怜的俺: 大佬.. 饶了我吧.. 今天打了一下午字( 大于等于1 ), 手都麻了~~~ 而且, 而且这个时候我是下班的可怜人呐 L 55555.




     

    转上几个字, 希望接着了解后台可以顺着读一下, 门清~

    MSN里的即时通讯是基于session的。想进行对话的两个人必须在session模式当中。除非我们同其他用户开始一个聊天session,否则我们是不能发送/接受信息的。
    基本上有两种途径可以使一个用户处于一个聊天session
    .1
    用户向另一个用户发送一个聊天session请求
    2
    用户接收从另一个用户那里发送来的聊天session请求
    接下来将分别详细介绍这两种途径

    用户向另一个用户发送一个聊天session请求
    客户端(用户)向服务器发送一个命令,以获取接线总机(SwitchBoard)服务器的地址.所有的即时通讯交谈都必须通过接线总机服务器实现。

    XFR9 SB
    此接线总机服务器返回此服务器的ip地址,连接端口,和一个CKI杂列。CKI 是一个安全包,用户必须使用此CKI杂列连接上接线总机服务器。

    XFR9 SB 64.4.13.88:1863 CKI1989487642.2070896604
    现在这次我们将向接线总机服务器进行一次新的连接。而且我们上次对MSN即时通服务器的连接必须要保持,否则我们将会登出。

    在我们连接上接线总机服务器之后,我们将向此接线总机服务器发送以下命令:
    USR 1 venky_dude@hotmail.com 989487642.2070896604
    如果我们发送的这个CKI杂列正确的话,接线服务器将会返回
    USR 1 OK venky_dude@hotmail.com venkat
    当以上做完之后,接下来这个用户要做的就是要把另一个用户叫到此聊天session中了,这可以通过发送下面的命令完成
    CAL 2 deadxxx@hotmail.com
    服务器将会向此用户回应一个session,同时也会将此session号发送给另一个用户。
    如果另一个用户准备好聊天,并向做出回应时,服务器将向我们发送如下命令

    JOI deadlee@hotmail.com Venkatesh
    这条命令表示另一个用户加入了聊天当中,我们现在可以接受和发送信息了

     

    , 一直到这里, 咱们的Invite终于执行到ContactJoined!


    用户接收从另一个用户那里发送来的聊天session请求
    当我们被一个用户邀请到一个聊天session中时,认证服务器将向我们发送如下的信息.
    RNG 11742066 64.4.13.74:1863 CKI 989495494.750408580 deaxxxx@hotmail.com Venkatesh
    上面的命令包含了session,接线服务器的ip地址,端口,CKI杂列及向我们发出交谈请求的用户信息。
    现在我们将向接线服务器进行一次新的连接。同样我们上次对MSN即时通服务器的连接必须要保持,否则我们将会登出。

    我们连接上接线服务器,并且发送如下命令

    ANS 1 venky_dude@hotmail.com 989495494.750408580 11742066
    上面的命令包含了我们的登陆名,我们接收到的CKI杂列和session.
    服务器将回应如下信息
    IRO 1 1 1 deaxxxx@hotmail.com Venkatesh
    ANS 1 OK
    我们现在就可以发送和接收信息了。

    在我们发送和接收信息之前,让我们了解一下信息是如何创建的
    我们发送信息时,将首先建立一个头信息如下所示
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    X-MMS-IM-Format: FN=Microsoft%20Sans%20Serif; EF=; CO=0; CS=0; PF=22

    然后使用如下的方式发送
    MSG2 N137 MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    X-MMS-IM-Format: FN=Microsoft%20Sans%20Serif; EF=; CO=0; CS=0; PF=22

    hello
    其中2是测试号,我们每发送一次信息,此号就会随着增加。137是指我们发送信息的长度。在上面的例子中就是头信息和我们发送的信息”hello”的长度。

    我们发送的信息和上面的例子是差不多的。

    下面是我们接受到的一个信息的例子
    MSG deaxxxx@hotmail.com Venkatesh 137
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    X-MMS-IM-Format: FN=Microsoft%20Sans%20Serif; EF=; CO=0; CS=0; PF=22

    hello

    当另一用户正在输入信息时,我们会收到如下信息
    MSG deaxxxx@hotmail.com Venkatesh 100
    MIME-Version: 1.0
    Content-Type: text/x-msmsgscontrol
    TypingUser: deaxxxx@hotmail.com


  • 相关阅读:
    第四周JSP作业
    jsp第二次作业
    3.4软件管理与测试作业
    jsp3月3日作业
    课后listview作业
    安卓sql
    activity带数据跳转
    answers
    阿里云ESC无法使用python发送邮件的问题
    Ubuntu 更改时区
  • 原文地址:https://www.cnblogs.com/Bird/p/854763.html
Copyright © 2020-2023  润新知