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

转自:http://www.cnblogs.com/Bird/archive/2007/08/14/854763.html

内容记:

MSN Robot 就是MSN机器人, 也可以喊成MSN聊天机器人.再解释就是会自动和你聊天的MSN,再再解释就是……啊, 我短路了(耳朵冒烟中)
本随笔是MSN Robot随笔系列第三篇, 内容是关于MSN Robot信息发送和接收.

 
 



前记:
 
香吉士:我想要找到 “ALL BLUE”
鲁飞:我要当海贼王 !!
索隆:我要成为一个最伟大的剑客 !!
娜美:我要画完全世界的地图 !!
骗人布:我 ... ... 我要成为一个勇敢的海上战士 !!
大家:出发吧 ! 进入 伟大的航路 ”!!
 
不管大家看到这部随笔的过程是怎样的 , 但咱们一定是追随着梦想的 !
不管太阳轮回东西 , 月明星暗 , 我们的旗帜都永远飘扬 !
 
絮絮了几句 , 咱们今天来看看 Message J 这是咱们 MSN Robot 的重头戏
 
Message 比较大头 , 计划分成两部分 , 一部分实现 , 一部分调用 , 今天咱们先来实现了消息发送和接收代码吧 J 这样咱们的 Robot 已经焊接到了胸部 , 明天加上几行调用 , 宇宙银河系地球中国北京 XX XX 号中间一个屋子里边的一台机器中的无敌变形金刚就出现了 ! 应该给它起个什么名字好呢 ? 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;
            }
 
            //SwitchboardProcessor 为null
            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);
 
            // 转换Sender成SBMessageHandler
            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)
这句上出现了永远过不去的, 对象没有绑定到实例. 这时候SwitchboardProcessor为null
 
为什么会这样子?! 为什么会出现永恒的, 突然的, 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
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值