聊天机器人's profile聊天机器人BlogLists Tools Help

Blog


    June 12

    如何为LumaQQ添加聊天机器人

    摘要
    LumaQQ 2005年1月21日的修正中,添加了对聊天机器人的支持。LumaQQ核心层提供了一个简单的接口IRobot来封装机器人功能,同时通过一个配置文件配置机器人的信息。LumaQQ的机器人支持简单,方便,并且支持多种机器人,方便切换。添加新机器人时,只需要拷贝你的jar文件到LumaQQ的lib目录下,重启LumaQQ后即可使用。本文介绍了添加一个机器人的基本步骤,希望对这方面感兴趣的朋友有所帮助。

    Luma, 清华大学
    更新时间: 2005/01/29

    修改历史:
    2005-05-24 机器人接口修改


    LumaQQ没有提供功能十足的机器人给你用,所以,实现一个机器人需要靠你自己,LumaQQ只为你提供了一个简单的框架,本文就是向你介绍如何在LumaQQ中插入自己的机器人的。

    第一步:实现自己的机器人

    缺省情况下,LumaQQ提供了一个机器人,但是这个机器人基本上什么也没做,而且,缺省也是没有打开的。这个机器人的代码在edu.tsinghua.lumaqq.qq.robot.DummyRobot.java中,它纯粹是一个演示目的,如果你想看看效果,可以在LumaQQ的xml目录下面找到一个robots.xml文件,这个文件是机器人的配置文件,你可以看到里面我写了一些注释,你把那些注释去掉,保存,然后重启LumaQQ就可以了。使用机器人的方法是打开系统菜单,如果系统配置了至少一个机器人的话,系统菜单中会有一项“聊天机器人”,它的子菜单里面列出了所有存在的机器人,另外包含一个开始/停止菜单,我就不多说了,想来你应该会用。

    为了实现一个聊天机器人,你必须实现edu.tsinghua.lumaqq.qq.robot.IRobot接口,这个接口目前相当简单,只包含了一个方法,如下:

    public interface IRobot {
    	/**
    	 * 根据message得到一条回复消息
    	 * 
    	 * @param packet
    	 * 		接受消息包
    	 * @return
    	 * 		回复的消息,返回null表示不响应这条消息
    	 */
    	public String getReply(ReceiveIMPacket packet);
    }

    而DummyRobot的实现则是:

    public class DummyRobot implements IRobot {
    	/* (non-Javadoc)
    	 * @see edu.tsinghua.lumaqq.qq.robot.IRobot#getReply(edu.tsinghua.lumaqq.qq.packets.in.ReceiveIMPacket)
    	 */
    	public String getReply(ReceiveIMPacket packet) {
    		/*
    		 * Example:
    		 * 1. 如何得到发送者QQ号
    		 * packet.header.sender
    		 * 
    		 * 2. 如何得到消息内容
    		 * packet.normalIM.messageBytes是消息的字节数组内容,如果需要得到字符串形式
    		 * new String(packet.normalIM.messageBytes, QQ.QQ_CHARSET_DEFAULT)
    		 * 对于消息格式,参见edu.tsinghua.lumaqq.qq.beans.NormalIM
    		 * 
    		 * 3. 如何判断这个消息是一个大消息中的分片?
    		 * if(packet.normalIM.totalFragments > 1) {
    		 * 		// 做你的处理,怎么处理,你决定,你可以把他缓存起来等待所有分片都收到为止
    		 * }
    		 * 
    		 * 4. 如何知道这个消息的分片序号?
    		 * packet.normalIM.fragmentSequence
    		 * 
    		 * 5. 如何知道这个消息的id?消息id也是用在分片情况时,同一个消息的分片,消息id相同
    		 * packet.normalIM.messageId
    		 * 
    		 * 6. 如何知道一个消息是不是自动回复?
    		 * if(packet.normalIM.replyType != QQ.QQ_IM_AUTO_REPLY) {
    		 * 		// 做你想做的
    		 * }
    		 * 
    		 * 更多内容和可用字段
    		 * 参考edu.tsinghua.lumaqq.qq.beans.NormalIMHeader
    		 * 参考edu.tsinghua.lumaqq.qq.beans.ReceiveIMHeader
    		 */
    		if(packet.normalIM.replyType != QQ.QQ_IM_AUTO_REPLY)
    			return "Hello, I am robot";
    		else
    			return null;
    	}
    }
    

    所以你也看到了,DummyRobot基本上啥也没干,就是老返回一句相同的话而已,不过它会判断一下是不是自动回复,如果是就不处理了,免得碰到自动回复的时候没完没了。你看到了,DummyRobot里面的有不少注释,应该对你的帮助很直接了。

    第二步:部署机器人程序

    添加一个机器人并不需要修改LumaQQ的源代码,你只要将你的机器人程序打包成jar文件,然后copy到LumaQQ的lib目录下就可以了,这样的好处自然就是你可以随便添加机器人,而且并不一定需要有LumaQQ的源代码,当然,你在编写你的机器人程序的时候,需要导入lumaqq.jar到工程中,不然找不到IRobot接口定义了。

    第三步:修改机器人配置文件

    机器人配置文件位于LumaQQ的xml目录下,这个文件自然是新增的,如果不存在这个文件的话,那么LumaQQ就会认为是没有机器人可用,系统菜单中也就不会有机器人的菜单项了。这个文件的格式也很简单,假设我要配置DummyRobot,那么就是下面这个样子:

    <?xml version="1.0" encoding="UTF-8"?>
    <Robots>
    	<Robot>
    		<Name>Dummy Robot</Name>
    		<Implementation>edu.tsinghua.lumaqq.qq.robot.DummyRobot</Implementation>
    	</Robot>
    </Robots>
    

    See? 仅仅就是配置一个机器人的名字,另外配置一下IRobot的实现类即可。如果你有多个机器人,你可以随便添加Robot元素

    总结

    添加一个机器人的工作就结束了,喜欢玩就玩吧。

    LumaQQ is a Java QQ client which has a reusable pure Java core and SWT-based GUI

    来源:http://lumaqq.linuxsir.org/article/how_to_add_robot.html

    聊天机器人,呻吟(Saying)和友邻

    前几天利用Smack API(Java XMPP client library)写了一个Google Talk Bot,基本实现了Twitter IM Bot指令,还加了个简单的管理指令,允许指定的管理员查看联系人列表。
    不过写完后后悔了,觉得浪费时间,根本不符合国情,用这个Google Talk的人太少了,国内还是QQ和MSN Messenger的天下,甚至还不如写个Skype Bot呢,不过年前时我看Anothr(Skype RSS Bot)好像才1000多个联系人而已,也没啥意思。
    写QQ Bot可以利用LumaQQ的Library,写MSN Messenger可以利用Java MSN Messenger Library(都是中国程序员做的,佩服!),不过,我暂时还没有写QQ或MSN Messenger Bot的想法,但那个Google Talk Bot的包结构是这样的:com.terac.talkbot.TalkBotG.java,说不定哪天就有TalkBotQ.java, TalkBotM.java:)
    其实IM Bot还是比较容易写的,关键还是看应用,目前比较好的还是国产的小iTwitterRenkooImified
    顺便说一下维众社区的呻吟(Saying):虽然它和Twitter的表现形式差不多,但我把呻吟(Saying)设计成是迷你博客与在线博客阅读工具的结合,而不是简单的回答“What are you doing”,也不限于朋友圈内的互动(谢谢foxmachia)。
    另外,维众社区的友邻和豆瓣的一样(Mixi更强,包含マイミクシィ和お気に入り两种连接人的方式),是单向的,我加你表示我关注你,比成为朋友关系的成本要低,比朋友圈范围要广。做个假设,如果老徐维众社区呻吟(Saying),你可以加她为友邻,关注她,但你能期望她也加你为友邻,关注你吗?

    来源:http://blog.terac.com/andy/e_1412.html

    March 06

    终于明白孙悟空为何不自己搞定妖怪了

       我相信大家都看过西游记,但是不知道大家思考过这么一个问题没有:孙悟空为什么不自己搞定妖怪而老是求救?孙悟空号称齐天大圣,72变,本事大是不用置疑,大闹天宫的时候只有如来才可以制服,但是取经路上,碰到几个小妖就搞不定,每次不是求神仙就是求观音求如来!答案只有一个:孙悟空是个自私的老奸巨猾的高手!


      其中道理我慢慢说来:


      1、先说自私。大闹天宫的时候,孙悟空是为个人争权力争名号争福利,闹的真是不亦乐乎,那个惊天地泣鬼神,最后还好如来收服了他,要不然估计就要霸占天宫了。有句话说“会哭的孩子有奶吃”,但是哭过头了自然只有吃苦头了,孙悟空大闹天宫的时候还年轻不懂事,就是吃了这个亏,但是还好购聪明,在以后的日子里及时吸取教训,变得异常老奸巨猾,不愧为知错就改的楷模。说远了,大闹天宫是为自己争福利,所以竭尽全力拼了小命地争;但是后来保唐僧去西天取经,这个属于工作性质,所以他就没有那么积极了,没有下血本拼老命了,所以才会求这求那。

     
      2、为什么要求这求那呢?其实是孙悟空深明关系的作用!不是打不过,实在是不能打不好打,而且还有更好的解决办法。你想想,那些妖怪不是这个神仙的宝贝就是那个菩萨的坐骑,妖怪们手上的独门宝贝其实就是和神仙菩萨门的关系啊,所以威力巨大!关系复杂啊,俗话说“打狗也要看主人”,那个时候天庭就好比政府权力机构和统治阶层啊,孙悟空就是深明这一点,所以才不下手啊,求他们呢,一来给了那些神仙面子,本来没关联,现在联系上了,还攀了点关系,以后好办事啊;二来呢那些神仙也欠他个人情,因为是神仙们的失误,被孙悟空抓住把柄了;三来呢跑一趟动动嘴皮子就可以解决,当然比拼命好多了,而点还可以不时捞点外块,比如吃的啊宝贝啊,这利害关系一对比,就知道其中得失了吧,可见孙悟空不是一般的老奸巨猾。


      3、未知对方虚实,稳妥为主。孙悟空被压了500年,500年啊,30岁一代也有十七代了,时代变迁,也不知道那些天兵天将啊妖怪啊500年能力提升了多少,不好贸然开打,所以每次看到孙悟空都只是开始的时候装模作样的打打妖怪小兵,一碰到妖怪头就蔫了逃跑了,跑去找管妖怪的神仙去了。所以孙悟空他是深明“不怕官只怕管”这个道理,不是一般的奸诈啊。


      4、一下就把妖怪头打死,不但会惹恼上头关系,还会造成大群小妖失业,把领导们放在外面的创收机构整没了,这个问题可严重,所以有时候还是要从广大基层小妖考虑,为了不被小妖狗急跳墙惹来不必要的麻烦,为了“群众利益”,所以还是去求吧,还可以获得小妖们的好感,再说还有上面那么多好处等着呢。


      5、其实最私心的呢是,你唐僧不是没事好坏不分会念紧箍咒整我对吧,我让你也吃吃苦头慢慢来,我去慢慢求啊求,叫你没事念加威胁。而且,万一唐僧挂了,那就更爽了,自己又没事不痛的,还少了个念咒的,还可以回花果山当大王消遥自在,多好啊。


      从另一个角度来说,制度害死人啊,500年前,无后顾之忧,无拘无束,大闹天宫,能力超水平发挥。500年后,多了一个唐僧累赘,凡事束手束脚,一个小妖小困难就难倒了当年的英雄好汗,再换个角度,官僚形式主义害死人啊。所以创业要趁早,要置之死地而后生,要没有心理负担。


      这里面,不是教大家奸诈之道,而是有很多可以学习的正面的处世之道和管理之道。

      ※原帖来自于:来福岛爆笑娱乐网http://www.laifu.org

    August 27

    测试Windows Live Writer好不好用

    今天看到WLW,测试下好不好用。

    August 19

    DotMSN Example 解读(一) Coversationform.cs

    Coversationform.cs要完成的是通讯功能,首先声明了一个类 ConversationForm ,定义了一个属性_conversation,在构造方法中初始化界面,并注册了几个监视函数。
     
     
     
    using System;
    using System.Drawing;
    using System.Collections;
    using System.ComponentModel;
    using System.Windows.Forms;
    using XihSolutions.DotMSN;
     
    namespace DotMSNClient
    {
     /// <summary>
     /// Summary description for ConversationForm.
     /// </summary>
     public class ConversationForm : System.Windows.Forms.Form
     {
      /// <summary>
      /// Required designer variable.
      /// </summary>
      private System.ComponentModel.Container components = null;
      private System.Windows.Forms.TextBox conversationTextBox;
      private System.Windows.Forms.Panel panel1;
      private System.Windows.Forms.TextBox inputTextBox;
      private System.Windows.Forms.Button sendButton;
      /// <summary>
      /// </summary>
      private Conversation _conversation;
      /// <summary>
      /// The conversation object which is associated with the form.
      /// </summary>
      public Conversation Conversation
      {
       get { return _conversation; }
      }
      public ConversationForm(Conversation conversation)
      {
       _conversation = conversation;
       //
       // Required for Windows Form Designer support
       //
       InitializeComponent();
       Conversation.Switchboard.TextMessageReceived += new TextMessageReceivedEventHandler(Switchboard_TextMessageReceived);
       Conversation.Switchboard.SessionClosed += new SBChangedEventHandler(Switchboard_SessionClosed);
       Conversation.Switchboard.ContactJoined += new ContactChangedEventHandler(Switchboard_ContactJoined);
       Conversation.Switchboard.ContactLeft   += new ContactChangedEventHandler(Switchboard_ContactLeft);   
      }  
      /// <summary>
      /// Clean up any resources being used.
      /// </summary>
      protected override void Dispose( bool disposing )
      {
       if( disposing )
       {
        if(components != null)
        {
         components.Dispose();
        }
       }
       base.Dispose( disposing );
      }
      #region Windows Form Designer generated code
      /// <summary>
      /// Required method for Designer support - do not modify
      /// the contents of this method with the code editor.
      /// </summary>
      private void InitializeComponent()
      {
                this.conversationTextBox = new System.Windows.Forms.TextBox();
                this.panel1 = new System.Windows.Forms.Panel();
                this.sendButton = new System.Windows.Forms.Button();
                this.inputTextBox = new System.Windows.Forms.TextBox();
                this.panel1.SuspendLayout();
                this.SuspendLayout();
                //
                // conversationTextBox
                //
                this.conversationTextBox.Dock = System.Windows.Forms.DockStyle.Fill;
                this.conversationTextBox.Location = new System.Drawing.Point(0, 0);
                this.conversationTextBox.Multiline = true;
                this.conversationTextBox.Name = "conversationTextBox";
                this.conversationTextBox.Size = new System.Drawing.Size(464, 334);
                this.conversationTextBox.TabIndex = 0;
                //
                // panel1
                //
                this.panel1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(163)))), ((int)(((byte)(163)))), ((int)(((byte)(186)))));
                this.panel1.Controls.Add(this.sendButton);
                this.panel1.Controls.Add(this.inputTextBox);
                this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
                this.panel1.Location = new System.Drawing.Point(0, 256);
                this.panel1.Name = "panel1";
                this.panel1.Size = new System.Drawing.Size(464, 78);
                this.panel1.TabIndex = 1;
                //
                // sendButton
                //
                this.sendButton.Location = new System.Drawing.Point(451, 26);
                this.sendButton.Name = "sendButton";
                this.sendButton.Size = new System.Drawing.Size(90, 25);
                this.sendButton.TabIndex = 1;
                this.sendButton.Text = "Send";
                this.sendButton.Click += new System.EventHandler(this.sendButton_Click);
                //
                // inputTextBox
                //
                this.inputTextBox.Location = new System.Drawing.Point(10, 9);
                this.inputTextBox.Multiline = true;
                this.inputTextBox.Name = "inputTextBox";
                this.inputTextBox.Size = new System.Drawing.Size(412, 60);
                this.inputTextBox.TabIndex = 0;
                this.inputTextBox.KeyDown += new System.Windows.Forms.KeyEventHandler(this.inputTextBox_KeyDown);
                //
                // ConversationForm
                //
                this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
                this.ClientSize = new System.Drawing.Size(464, 334);
                this.Controls.Add(this.panel1);
                this.Controls.Add(this.conversationTextBox);
                this.Name = "ConversationForm";
                this.Text = "Conversation - DotMSN";
                this.Closing += new System.ComponentModel.CancelEventHandler(this.ConversationForm_Closing);
                this.Load += new System.EventHandler(this.ConversationForm_Load);
                this.panel1.ResumeLayout(false);
                this.panel1.PerformLayout();
                this.ResumeLayout(false);
                this.PerformLayout();
      }
      #endregion
      private bool _typingMessageSended = false;
      private void SendInput()
      {   
       // check whether there is input
       if(inputTextBox.Text.Length == 0) return;
       // if there is no switchboard available, request a new switchboard session
       if(Conversation.SwitchboardProcessor.Connected == false)
       {
        Conversation.Messenger.Nameserver.RequestSwitchboard(Conversation.Switchboard, this);
       }
                // note: you can add some code here to catch the event where the remote contact lefts due to being idle too long
                // in that case Conversation.Switchboard.Contacts.Count equals 0.           
       TextMessage message = new TextMessage(inputTextBox.Text);
       /* 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);
       _typingMessageSended = false;
       inputTextBox.Text = "";
       conversationTextBox.Text += "You say: " + message.Text + "\r\n";
      }

      private void sendButton_Click(object sender, System.EventArgs e)
      {
       SendInput();
      }
      private void inputTextBox_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
      {
       if(_typingMessageSended == false)
       {
        Conversation.Switchboard.SendTypingMessage();
        _typingMessageSended = true;
       }
       if(e.KeyCode == Keys.Enter)
        SendInput();
      }
      private delegate void MakeVisibleDelegate();
      private void MakeVisible()
      {
       Show();
      }
            private delegate void PrintTextDelegate(string name, string text);
            private void PrintText(string name, string text)
            {
                conversationTextBox.Text += name + " says: " + text + "\r\n";
            }
      private void Switchboard_TextMessageReceived(object sender, TextMessageEventArgs e)
      {   
       // use Invoke() because this method is not run in the form's thread, but in the DotMSN (worker)thread
                if(Visible == false)
       {
        this.Invoke(new MakeVisibleDelegate(MakeVisible));
       }
                Invoke(new PrintTextDelegate(PrintText), new object[] { e.Sender.Name, e.Message.Text });             
      }
      private void Switchboard_SessionClosed(object sender, EventArgs e)
      {
       conversationTextBox.Text += "* Session was closed\r\n";
      }
      private void Switchboard_ContactJoined(object sender, ContactEventArgs e)
      {
       conversationTextBox.Text += "* " + e.Contact.Name + " joined the conversation\r\n";
      }
      private void Switchboard_ContactLeft(object sender, ContactEventArgs e)
      {
       conversationTextBox.Text += "* " + e.Contact.Name + " left the conversation\r\n";
      }
      private void ConversationForm_Closing(object sender, System.ComponentModel.CancelEventArgs e)
      {
       Conversation.Switchboard.Close();
      }
            private void ConversationForm_Load(object sender, EventArgs e)
            {
            }
     }
    }
    August 18

    MSN Messenger协议

    原作:bhw98

    前 言

    MSN Messenger 是Microsoft开发的聊天工具,目前在国内拥有很大的用户群。使用MSN Messenger可以与他人进行文字聊天,语音对话,视频会议等即时交流,还可以通过此软件来查看联系人是否联机等。该软件的最新版本是6.1。

    1999年,Microsoft向IETF提交了一份"MSN Messenger Service 1.0 Protocol"草案,这是最初版本的MSN Messenger协议。在以后几年,该公司不再公开有关MSN Messenger协议的升级、修改细节的官方文档。但无论是开发第三方的聊天客户端软件(如Gaim, MyIM等),还是做协议分析,必须对其通信协议有深入了解。正因为如此,有一些民间人士对这些协议开展了研究。

    当我们还在感叹"这世界,变化快"的时候,MSN Messenger的协议已经到了第10版,简称MSNP10,对应于MSN Messenger 6.1。MSN Messenger 6.0则使用MSNP9。现在Microsoft强迫MSN Messenger用户升级到6.0或6.1版,因为服务器对MSNP8以下的版本不再支持。本文及后续文章所描述的MSN Messenger协议主要针对MSNP9/MSNP10。

    1. 连 接

    MSN Messenger协议建立在TCP/IP之上。除了文件传输和语音聊天是直接的"点对点"通信之外,其它所有的情形全部通过服务器进行。

    在逻辑上,一共有三种类型的服务器,各司其职:

  • 派遣服务器(Dispatch Server, DS) - 客户端最初连接的服务器。负责给客户端分配合适的通知服务器。域名是messenger.hotmail.com,标准服务端口是1863。完成派遣任务后,切断TCP连接。
  • 通知服务器(Notification Server, NS) - 客户端需要一直保持连接的服务器。很多任务要在这个会话内完成,包括登录、改变状态、获取用户列表、修改用户信息、发起聊天、接受呼叫、邮件通知、退出等等。服务端口由派遣服务器指定,通常也是1863。
  • 接线服务器(Switchboard Server, SS) - 客户端之间聊天使用的中转服务器。每开一个聊天窗口,客户端和服务器就建立一个TCP会话。当客户端之间需要进行文件传输或语音聊天时,发送系统消息,建立"点对点"会话通道(可能转为使用UDP)。服务端口通常也是1863。"点对点" 通信使用的端口由客户端自动协商决定,如文件传输通常使用6891端口。

    2. 命 令

    MSN Messenger命令使用纯ASCII码。对非ASCII码字符使用URL编码。命令的语法是

    XXX[<SP>TrID<SP>PARAM1<SP>PARAM2…]<CRLF>

    其中,<SP>是空白字符,<CRLF>是回车换行,XXX是一个3字符的命令串,TrID是一个流水号,PARAMx是参数,[ ]内是可选项。最简单的命令没有流水号和参数。为了方便起见,下面讨论时用" "代表<SP>,"\r\n"代表<CRLF>,"\x??"代表一个值为0x??字节。红色表示由客户端发出,蓝色表示由服务器发出。一个MSN Messenger命令的例子如下:

    USR 18 TWN I example@hotmail.com\r\n

    3. 错 误

    无论是由于客户端发出的命令无效,参数无效,还是其他什么原因,服务器可以返回一个错误。格式为

    XXX[<SP>TrID]<CRLF>

    其中,XXX是一个3位数字的串。如

    ADD 21 AL non_existent@passport.com non_existent@passport.com\r\n
    205 21\r\n

    上例中,non_existent@passport.com是一个不存在的账号。

    4. 消 息

    MSN Messenger消息符合MIME 1.0标准,由消息头与消息体组成。通常使用UTF-8编码,消息头中也需要URL编码格式,消息体则直接用二进制数据。

    一个MSN Messenger消息的例子如下:

    MIME-Version: 1.0\r\n
    Content-Type: text/plain; charset=UTF-8\r\n
    X-MMS-IM-Format: FN=%E5%AE%8B%E4%BD%93; EF=; CO=0; CS=86; PF=0\r\n
    \r\n
    bhw98\xE4\xBD\xA0\xE5\xA5\xBD\xEF\xBC\x81

    经简单分析可知,"%E5%AE%8B%E4%BD%93"是"宋体"的UTF-8加URL编码,而"bhw98\xE4\xBD\xA0\xE5\xA5\xBD\xEF\xBC\x81"是"bhw98你好!"的UTF-8编码。

    命令一览

    命令 来源 去向 说明 备注
    ACK SS Client 确认,做出肯定回答。 acknowledgement
    ADD Client NS 发出添加新联系人到列表的请求。 add user
    NS Client 返回添加新联系人请求的应答。
    ADG Client NS 发出添加新联系人组请求。 add group
    NS Client 返回添加新联系人组请求的应答。
    ANS Client SS 接受聊天连接请求。 answer
    BLP Client NS 设置对尚未列入明确允许/禁止的联系人列表的保密策略。 block list privacy
    NS Client 返回设置保密策略请求的应答。
    BYE SS Client 通知客户端结束会话。 bye
    CAL Client SS 发出建立聊天连接的请求。 call
    SS Client 返回建立聊天连接请求的应答。
    CHG Client NS 发出改变状态的请求。 change state
    NS Client 返回改变状态的应答。
    CHL NS Client 服务器发出验证要求。 challenge
    SS Client
    CVR Client NS 发出客户端的OS、语言、MSN Messenger版本等信息。 client version
    Client SS
    NS Client 返回推荐的MSN Messenger版本、升级软件需要的下载地址等信息。
    SS Client
    FLN NS Client 通知有联系人列表中的用户下线。 off-line
    GTC Client NS 设置当有联系人列表中的用户状态改变时给出的提示。 greeting to changes?
    NS Client 返回设置请求的应答。
    INF Client NS 询问服务器所支持的认证方式。 information?
    Client SS
    NS Client 返回服务器所支持的认证方式。
    SS Client
    ILN NS Client 当客户端登录或添加联系人到列表时,通知列表中的联系人的状态。 initial online state
    IRO SS Client 当有新用户加入聊天连接时,通知客户端该连接中的用户名单。 initial roster information
    JIO SS Client 通知客户端已经同另外的用户建立了聊天连接。 jion
    LSG Client NS 发出获取联系人组列表的请求。 list groups
    NS Client 返回获取联系人组列表请求的应答。
    LST Client NS 发出获取联系人列表的请求。 list
    NS Client 返回获取联系人列表请求的应答。
    MSG Client SS 发送消息到其他用户(聊天对象)。 message
    NS Client 传递服务器(系统) 的消息到客户端。
    SS Client 传递其他用户(聊天对象)的消息到客户端。
    NAK SS Client 做出否定回答。 negative acknowledgement
    NLN NS Client 通知客户端联系人上线或改变状态。 on-line
    OUT All All 结束客户端-服务器的连接。 out
    PNG Client NS 测试TCP连接状态。 ping
    Client SS
    PRP Client NS 发出设置个人电话号码的请求。 personal phone number
    NS Client 返回设置请求的应答
    PNG Client NS 测试TCP连接状态。 ping
    Client SS
    QNG NS Client 返回测试TCP连接状态的应答。 quiz ping?
    SS Client
    QRY Client NS 客户端回答服务器的验证要求。 quiz reply?
    Client SS
    REA Client NS 发出修改用户昵称的请求。 rename nickname
    NS Client 返回修改用户昵称请求的应答。
    REG Client NS 发出修改联系人组的请求。 rename group
    NS Client 返回修改联系人组请求的应答。
    REM Client NS 发出从联系人列表中删除用户的请求。 rename user
    NS Client 返回删除用户请求的应答。
    RMG Client NS 发出删除联系人组的请求。 remove group
    NS Client 返回删除联系人组请求的应答。
    RNG NS Client 通知客户端有人要建立聊天连接。 ring
    SYN Client NS 客户端-服务器同步。 synchronization
    NS Client
    URL Client NS 发出获取MSN服务URL的请求。 URL
    NS Client 返回获取URL请求的应答。
    USR All All 声明、传递、鉴别用户身份。 user
    VER Client DS 协商MSN Messenger协议版本。 version
    Client NS
    DS Client
    NS Client
    XFR DS Client 向客户端分配NS(通知客户端转向连接指定的NS)。 transfer
    Client NS 发出分配SS的请求。
    NS Client 返回分配SS请求的应答。

    MSN Messenger协议--错误代码一览

     

    代码 含义 可能的命令 错误示例 备注
    200 非法命令   ABC 18\r\n
    200 18\r\n
     
    201 非法参数   CHG 19 FLN 0\r\n
    201 19\r\n
    ADD 20 AL aaa@bbb@ccc aaa@bbb@ccc\r\n
    201 20\r\n
     
    205 用户不存在 ADD ADD 21 AL none@hotmail.com none@hotmail.com\r\n
    205 21\r\n
     
    206 缺少域名      
    207 已经登录 USR USR 20 TWN I example@hotmail.com\r\n
    207 20\r\n
    USR 21 TWN S 8d30fc782aa25ec9e1293fdda13cab42\r\n
    207 21\r\n
     
    208 非法用户名 CAL CAL 2 @@hotmail.com\r\n
    208 2\r\n
     
    209 非法用户昵称 REA REA 18 one@hotmail.com kill%20microsoft\r\n
    209 18\r\n
     
    210 用户太多 ADD ADD 23 FL one@hotmail.com one@hotmail.com 0\r\n
    210 23\r\n
    最大150
    215 用户已在列表中 ADD ADD 36 FL one@hotmail.com one@hotmail.com 2\r\n
    ADD 36 825 FL one@hotmail.com one@hotmail.com 2\r\n
    ADD 37 FL one@hotmail.com one@hotmail.com 2\r\n
    215 37\r\n
     
    CAL CAL 5 two@hotmail.com\r\n
    CAL 5 RINGING 213697\r\n
    CAL 6 two@hotmail.com\r\n
    215 6\r\n
    216 用户不在列表中 REM REM 14 FL three@hotmail.com\r\n
    216 14\r\n
     
    REA REA 48 three@hotmail.com three\r\n
    216 48\r\n
    CAL CAL 2 three@hotmail.com\r\n
    216 2\r\n
    217 用户不在线 CAL CAL 2 two@hotmail.com\r\n
    217 2\r\n
     
    218 已在指定模式 BLP BLP 17 BL\r\n
    BLP 17 151 BL\r\n
    BLP 18 BL\r\n
    218 18\r\n
     
    GTC GTC 19 A\r\n
    GTC 19 152 A\r\n
    GTC 20 A\r\n
    218 20\r\n
    219 用户已经在相反的列表中 ADD ADD 15 AL four@hotmail.com four@hotmail.com\r\n
    ADD 15 AL 56 four@hotmail.com four\r\n
    ADD 16 BL four@hotmail.com four@hotmail.com\r\n
    219 16\r\n
     
    223 用户组太多 ADG ADG 28 new%20group 0\r\n
    223 28\r\n
    最大30
    224 非法组号 ADD RMG 24 3\r\n
    RMG 24 11506 3\r\n
    ADD 25 FL five@hotmail.com 3\r\n
    224 25\r\n
     
    REM RMG 24 3\r\n
    RMG 24 11506 3\r\n
    REM 25 FL five@hotmail.com 3\r\n
    224 25\r\n
    REG RMG 24 3\r\n
    RMG 24 11506 3\r\n
    REG 25 3 New%20Name 0\r\n
    224 25\r\n
    RMG RMG 24 3\r\n
    RMG 24 11506 3\r\n
    RMG 25 3\r\n
    224 25\r\n
    225 用户不在组中 REM REM 26 FL six@hotmail.com 3\r\n
    225 26\r\n
     
    229 组名太长 ADG ADG 27 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 0\r\n
    229 27\r\n
    最大60
    230 不能删除组0 RMG RMG 28 0\r\n
    230 28\r\n
     
    300 缺少必要的参数      
    302  尚未登录      
    500 服务器内部错误      
    540 验证应答错误      
    600 服务器忙      
    707 无法建立连接      
    910 服务器忙      
    911 身份验证失败      
    ?

    过去的MSN Messenger版本(MSNP8以下),简单地使用MD5等Hash算法对用户身份进行认证。MSNP9/MSNP10使用一种TWN(Tweener)认证方式,通过SSL/TLS连接到login.passport.com和loginnet.passport.com等服务器,借助于HTTP协议输入账号和密码,认证通过后,才能取得“入场券”。

    真正意义上的身份认证,发生在客户端与通知服务器(NS)之间。客户端登陆NS时,首先交换版本信息。双方均支持MSNP8以上版本时,才能进行认证过程。如果客户端版本较低,不支持TWN,咋办?服务器会将你一脚揣出去,信不信?

    好了,废话少说,现在拿一个成功认证的例子看看。在下面的例子中,账号是“example@passport.com”,密码是“password”。

    VER 4 MSNP10 MSNP9 CVR0 \r\n
    VER 4 MSNP9 CVR0 \r\n
    CVR 5 0x0804 winnt 5.0 i386 MSNMSGR 6.1.0203 MSMSGS example@passport.com \r\n
    CVR 5 6.0.0602 6.0.0602 5.0.0527 http://download.microsoft.com/download/d/4/f/d4f560d5-6dc6-4901-b149-a568415561d7/SETUPNT.EXE http://messenger.msn.com/cn \r\n
    USR 6 TWN I example@passport.com \r\n
    USR 6 TWN Slc=1033,id=507,tw=40,fs=1,ru=http%3A%2F%2Fmessenger%2Emsn%2Ecom,ct=1073355862,kpp=1,kv=5,ver=2.1.0173.1, tpf=ed1c2f217a21c191c61251eb8b73bb60 \r\n
    (此时通过SSL进行身份认证,获得“入场券”)
    USR 7 TWN S t=4m1wWfEupDgUNb53qys5gJdw8OTJEtT82fcuDbS3U672gTymOOs6cgKeafj7WjgZNcufAQggxqHRRXko02DoflZA$$ &p=4QXNnX9rFDDgki9ZqvqPZGDGJa2Mrd5H13Zfl0NNjh4I78qPyfpzmkZPZEe0nxJTkzZSNDYtk!57cVqiYVfO86KgCRYWhi2kudS0M !7bdi82EDA1FYp3WboHD!sCQ17OZh7lPQI7fozrgsSMZwgSzRi2FNTPxf13oDNIfDCKCG!2guDvZKEpk78A$$ \r\n
    USR 7 OK example@passport.com example@passport.com 1 0 \r\n

    第一回合(TrID=4),双方协商MSN版本号。客户端说“我能支持MSNP9和MSNP10”,NS说“行,就MSNP9吧”。

    第二回合(TrID=5),客户端报告本机信息:OS = windows 2000 (NT 5.0), 语言 = 简体中文,MSN Messenger版本 = 6.1.0203, 账号 = example@passport.com。NS给出了推荐的版本号,能够使用的最老的版本号,新版本下载地址,官方网站地址等信息。

    第三回合(TrID=6),客户端要求身份认证(I = Initial),NS则给出所需要的一长串信息(S = Subsequent)。其中tpf相当于challenge,参与Hash运算,能保证每次认证返回的串是不同的。

    第四回合(TrID=7),客户端出示从认证服务器得到的“入场券”,NS放行(OK)。

    第三、四回合之间,通过SSL的认证过程如下:

    首先在HTTPS端口443向login.passport.com发送一个GET请求,将账号、密码和NS给定的一长串信息送出

    GET /login2.srf HTTP/1.1 \r\n
    Authorization: Passport1.4 OrgVerb=GET,OrgURL=http%3A%2F%2Fmessenger%2Emsn%2Ecom,sign-in=example%40passport.com,pwd=password, lc=1033,id=507,tw=40,fs=1,ru=http%3A%2F%2Fmessenger%2Emsn%2Ecom,ct=1073355862,kpp=1,kv=5,ver=2.1.0173.1, tpf=ed1c2f217a21c191c61251eb8b73bb60 \r\n
    Host: login.passport.com \r\n \r\n

    根据情况,会重定向到不同的URL。本例中,重定向到"https://loginnet.passport.com/login2.srf?lc=1033",服务器应答

    HTTP/1.1 302 Found \r\n Server: Microsoft-IIS/5.0 \r\n
    Date: Mon, 22 Dec 2003 21:10:05 GMT \r\n
    PPServer: H: LAWPPLOG5C006 \r\n
    Connection: close \r\n
    Content-Type: text/html \r\n
    Expires: Mon, 22 Jun 2003 21:09:05 GMT \r\n
    Cache-Control: no-cache \r\n
    cachecontrol: no-store \r\n Pragma: no-cache \r\n
    P3P: CP="DSP CUR OTPi IND OTRi ONL FIN" \r\n
    Authentication-Info: Passport1.4 da-status=redir \r\n
    Location: https://loginnet.passport.com/login2.srf?lc=1033 \r\n
    \r\n ... ...

    然后,重新向指定的URL发出请求,得到如下响应

    HTTP/1.1 200 OK \r\n
    Server: Microsoft-IIS/5.0 \r\n
    Date: Mon, 22 Dec 2003 21:10:07 GMT \r\n
    PPServer: H: LAWPPIIS6B061 \r\n
    Connection: close \r\n Content-Type: text/html \r\n
    Expires: Mon, 22 Dec 2003 21:09:07 GMT \r\n
    Cache-Control: no-cache \r\n
    cachecontrol: no-store \r\n
    Pragma: no-cache \r\n
    P3P: CP="DSP CUR OTPi IND OTRi ONL FIN" \r\n
    Set-Cookie: ... ... \r\n
    Authentication-Info: Passport1.4 da-status=success,tname=MSPAuth,tname=MSPProf,tname=MSPSec, from-PP='t=4m1wWfEupDgUNb53qys5gJdw8OTJEtT82fcuDbS3U672gTymOOs6cgKeafj7WjgZNcufAQggxqHRRXko02DoflZA$$ &p=4QXNnX9rFDDgki9ZqvqPZGDGJa2Mrd5H13Zfl0NNjh4I78qPyfpzmkZPZEe0nxJTkzZSNDYtk!57cVqiYVfO86KgCRYWhi2kudS0M !7bdi82EDA1FYp3WboHD!sCQ17OZh7lPQI7fozrgsSMZwgSzRi2FNTPxf13oDNIfDCKCG!2guDvZKEpk78A$$', ru=http://messenger.msn.com \r\n
    Content-Length: 0 \r\n
    \r\n

    开始时直接向loginnet.passport.com发出正确的请求,也是可以的。不难看出,在服务器认证成功的返回信息中,Authentication-Info字段的from-PP串值,就是所谓的“入场券”。

    如果认证失败,服务器返回401错误

    HTTP/1.1 401 Unauthorized \r\n ... ...

    这样,就无法拿到“入场券”,自然不能在第四回合中输入合法的串。

    如果英文够好可以参考:www.hypothetic.org  这里的信息非常全面

  • DotMSN

    DotMSN

    DotMSN is an open-source stand-alone class library to provide connectivity with the MSN Messenger service. The library is built in C# and can therefore be used by all languages the .NET environment supports. Because of a clean natural Object-Oriented approach the library is easy to use and implement. DotMSN is designed to be a flexible, robust and lightweight enhancement to any application. Applications of DotMSN vary from creating messaging robots to creating custom clients. If your application needs to communicate through the messenger service, DotMSN is your tool.

    Features

    The library currently supports the following features:

    • Connecting / disconnecting with the MSN Messenger service
    • Setting presence status
    • Event-based design to notify contact presence status changes
    • Personal data of a contact
    • Owner profile details
    • Automatically synchronizes contact lists and keeps contactlist data up to date
    • Creating and changing contactgroups
    • Convenient enumerating through contacts in different lists
    • Request or receive conversations
    • Multiple users in one conversation
    • Sending and receiving decorated text messages
    • Sending typing messages
    • Privacy settings of the contactlist owner
    • Hotmail mailbox status
    • Notifications of new mail
    • Flexible tracing of warnings and errors
    • File transfers
    • User display images
    • Proxy support and custom servers
    • P2P framework support
    • Custom extendable DotMSN framework
    • MSNP9 protocol
    • Source code provided

    DotMSN is a stand-alone library. This means there is no interaction with the official MSN Messenger client and therefore it does not need to be installed when using DotMSN. Multiple instances of DotMSN can be executed on the same machine, or even in the same process. Thus making it easy to deploy applications on a single server.

    How does DotMSN integrate with your application? The following diagram makes clear how DotMSN interacts with your application as well as remote parties.

    DotMSN collaboration diagram

    Get DotMSN

    You can download DotMSN at the download page.

    Resources

    Here is a list of resources that have helped the development of DotMSN tremendously.

    March 04

    AIML Overview

    AIML Overview

    By Dr. Richard S. Wallace

    AIML, or Artificial Intelligence Mark-up Language enables people to input knowledge into chat-bots based on the A.L.I.C.E free software technology.

    AIML was developed by the Alicebot free software community and I during 1995-2000. It was originally adapted from a non-XML grammar also called AIML, and formed the basis for the first Alicebot, A.L.I.C.E., the Artificial Linguistic Internet Computer Entity.

    AIML, describes a class of data objects called AIML objects and partially describes the behavior of computer programs that process them. AIML objects are made up of units called topics and categories, which contain either parsed or unparsed data.

    Parsed data is made up of characters, some of which form character data, and some of which form AIML elements. AIML elements encapsulate the stimulus-response knowledge contained in the document. Character data within these elements is sometimes parsed by an AIML interpreter, and sometimes left unparsed for later processing by a Responder.

    CATEGORIES

    The basic unit of knowledge in AIML is called a category. Each category consists of an input question,an output answer, and an optional context. The question, or stimulus, is called the pattern. Theanswer, or response, is called the template. The two types of optional context are called "that" and"topic." The AIML pattern language is simple, consisting only of words, spaces, and the wildcard symbols _ and *. The words may consist of letters and numerals, but no other characters. The pattern language is case invariant. Words are separated by a single space, and the wildcard characters function like words.

    The first versions of AIML allowed only one wild card character per pattern. The AIML 1.01 standard permits multiple wildcards in each pattern, but the language is designed to be as simple as possible for the task at hand, simpler even than regular expressions. The template is the AIML respons or reply. In its simplest form, the template consists of only plain, unmarked text.

    More generally, AIML tags transform the reply into a mini computer program which can save data, activate other programs, give conditional responses, and recursively call the pattern matcher to insert the responses from other categories. Most AIML tags in fact belong to this template side sublanguage.

    AIML currently supports two ways to interface other languages and systems. The <system> tag executes any program accessible as an operating system shell command, and inserts the results in the reply. Similarly, the <javascript> tag allows arbitrary scripting inside the templates. The optional context portion of the category consists of two variants, called <that> and <topic>. The <that> tag appears inside the category, and its pattern must match the robot’s last utterance. Remembering one last utterance is important if the robot asks a question. The <topic> tag appears outside the category, and collects a group of categories together. The topic may be set inside any template.

    AIML is not exactly the same as a simple database of questions and answers. The pattern matching "query" language is much simpler than something like SQL. But a category template may contain the recursive <srai> tag, so that the output depends not only on one matched category, but also any others recursively reached through <srai>.

    RECURSION

    AIML implements recursion with the <srai> operator. No agreement exists about the meaning of the acronym. The "A.I." stands for artificial intelligence, but "S.R." may mean "stimulus-response," "syntactic rewrite," "symbolic reduction," "simple recursion," or "synonym resolution." The disagreement over the acronym reflects the variety of applications for <srai> in AIML. Each of these is described in more detail in a subsection below:

        (1). Symbolic Reduction: Reduce complex grammatic forms to simpler ones.

        (2). Divide and Conquer: Split an input into two or more subparts, and combine the responses to each.

        (3). Synonyms: Map different ways of saying the same thing to the same reply.

        (4). Spelling or grammar corrections.

        (5). Detecting keywords anywhere in the input.

        (6). Conditionals: Certain forms of branching may be implemented with <srai>.

        (7). Any combination of (1)-(6).

    The danger of <srai> is that it permits the botmaster to create infinite loops. Though posing some risk to novice programmers, we surmised that including <srai> was much simpler than any of the iterative block structured control tags which might have replaced it.

    (1). Symbolic Reduction

    Symbolic reduction refers to the process of simplifying complex grammatical forms into simpler ones. Usually, the atomic patterns in categories storing robot knowledge are stated in the simplest possible terms, for example we tend to prefer patterns like "WHO IS SOCRATES" to ones like "DO YOU KNOW WHO SOCRATES IS" when storing biographical information about Socrates.

    Many of the more complex forms reduce to simpler forms using AIML categories designed for symbolic reduction:

    <category>
    <pattern>DO YOU KNOW WHO * IS</pattern>
    <template><srai>WHO IS <star/></srai></template>
    </category>

    Whatever input matched this pattern, the portion bound to the wildcard * may be inserted into the reply with the markup <star/>. This category reduces any input of the form "Do you know who X is?" to "Who is X?"

    (2). Divide and Conquer

    Many individual sentences may be reduced to two or more subsentences, and the reply formed by combining the replies to each. A sentence beginning with the word "Yes" for example, if it has more than one word, may be treated as the subsentence "Yes." plus whatever follows it.

    <category>
    <pattern>YES *</pattern>
    <template><srai>YES</srai> <sr/></template>
    </category>

    The markup <sr/> is simply an abbreviation for <srai><star/></srai>.

    (3). Synonyms

    The AIML 1.01 standard does not permit more than one pattern per category. Synonyms are perhaps the most common application of <srai>. Many ways to say the same thing reduce to one category, which contains the reply:

    <category>
    <pattern>HELLO</pattern>
    <template>Hi there!</template>
    </category>

    <category>
    <pattern>HI</pattern>
    <template><srai>HELLO</srai></template>
    </category>

    <category>
    <pattern>HI THERE</pattern>
    <template><srai>HELLO</srai></template>
    </category>

    <category>
    <pattern>HOWDY</pattern>
    <template><srai>HELLO</srai></template>
    </category>

    <category>
    <pattern>HOLA</pattern>
    <template><srai>HELLO</srai></template>
    </category>

    (4). Spelling and Grammar correction

    The single most common client spelling mistake is the use of "your" when "you’re" or "you are" is intended. Not every occurrence of "your" however should be turned into "you’re." A small amount of grammatical context is usually necessary to catch this error:

    <category>
    <pattern>YOUR A *</pattern>
    <template>I think you mean "you’re" or "you are" not "your."
    <srai>YOU ARE A <star/></srai>
    </template>
    </category>

    Here the bot both corrects the client input and acts as a language tutor.

    (5). Keywords

    Frequently we would like to write an AIML template which is activated by the appearance of a keyword anywhere in the input sentence. The general format of four AIML categories is illustrated by this example borrowed from ELIZA:

    <category>
    <pattern>MOTHER</pattern>
    <template> Tell me more about your family. </template>
    </category>

    <category>
    <pattern>_ MOTHER</pattern>
    <template><srai>MOTHER</srai></template>
    </category>

    <category>
    <pattern>MOTHER _</pattern>
    <template><srai>MOTHER</srai></template>
    </category>

    <category>
    <pattern>_ MOTHER *</pattern>
    <template><srai>MOTHER</srai></template>
    </category>

    The first category both detects the keyword when it appears by itself, and provides the generic response. The second category detects the keyword as the suffix of a sentence. The third detects it as the prefix of an input sentence, and finally the last category detects the keyword as an infix. Each of the last three categories uses <srai> to link to the first, so that all four cases produce the same reply, but it needs to be written and stored only once.

    (6). Conditionals

    It is possible to write conditional branches in AIML, using only the <srai> tag. Consider three categories:

    <category>
    <pattern>WHO IS HE</pattern>
    <template><srai>WHOISHE <get name="he"/></srai></template>
    </category>

    <category>
    <pattern>WHOISHE *</pattern>
    <template>He is <get name="he"/>.</template>
    </category>

    <category>
    <pattern>WHOISHE UNKNOWN</pattern>
    <template>I don’t know who he is.</template>
    </category>

    Provided that the predicate "he" is initialized to "Unknown," the categories execute a conditional branch depending on whether "he" has been set. As a convenience to the botmaster, AIML also provides the equivalent function through the <condition> tag.

    TARGETING

    Bot personalities are created and shaped through a cyclical process of supervised learning called Targeting. Targeting is a cycle incorporating client, bot, and botmaster, wherein client inputs that find no complete match among the categories are logged by the bot and delivered as Targets the botmaster, who then creates suitable responses, starting with the most common queries. The Targeting cycle produces a progressively more refined bot personality.

    The art of AIML writing is most apparent in creating default categories, which provide noncommittal replies to a wide range of inputs. Here is a very nice example provided found on the alicebot-general mailing list:

    User: Where do I find the webpage with Australopithecus?

    Alice: Where do you want to find it?

    User: Bad Answer Alice, time to Learn a new Line

    Alice: How would you answer "Where do I find the webpage with Australopithecus?"

    User: I would say: "http://www.ancientskulls.net/australopithecus.html"

    Alice: Thanks, human. Try asking me again.

    User: Where do I find the webpage with Australopithecus?

    Alice: http://www.ancientskulls.net/australopithecus.html

    This is a very nice example. This is exactly the same as targeting when the botmaster is the teacher. Or I should say, it is an AIML interface to targeting. Of course this scenario raises many problems, such as how to weed out hackers and other garbage givers, which might mean only turning on the Learn function while in a restricted sandbox, though it is the free labor of many chatters the web that could help the bot grow wise quickly.

    That is the crux of the learning problem in a nutshell. It all comes down to editing. Either the botmaster edits good quality replies to begin with, or if the bot is "open to the public" then the teacher has to edit out all the crappy answers supplied by all that "free" labor. My personal view is that, compared with a hypothetical learning machine capable of learning language like a child, the AIML targeting approach is more efficient.

    CONTEXT

    The keyword "that" in AIML refers to the robot’s previous utterance. Specifically, if the robot responds with a multiple sentence paragraph, the value of that is set to the last sentence in the sequence. The choice of the keyword "that" is motivated by its use in ordinary language:

    R: Today is yesterday.

    C: That makes no sense.

    R: The answer is 3.1412926 approximately.

    C: That is cool.

    In AIML the syntax <that>...</that> encloses a pattern that matches the robot’s previous utterance. A common application of <that> is found in yes-no questions:

    <category>
    <pattern>YES</pattern>
    <that>DO YOU LIKE MOVIES</that>
    <template>What is your favorite movie?</template>
    </category>

    This category is activated when the client says YES. The robot must find out what is he saying "yes" to. If the robot asked, "Do you like movies?," this category matches, and the response, "What is your favorite movie?," continues the conversation along the same lines.

    One interesting application of <that> are categories that enable the robot to respond to knock-knock jokes. The categories:

    <category>
    <pattern>KNOCK KNOCK</pattern>
    <template>Who is there?</template>
    </category>

    <category>
    <pattern>*</pattern>
    <that>WHO IS THERE</that>
    <template><person/> who?</template>
    </category>

    <category>
    <pattern>*</pattern>
    <that>* WHO</that>
    <template>Ha ha very funny, <get name="name"/>.</template>
    </category>

    produce the following dialogue:

    C: Knock knock.

    R: Who’s there?

    C: Banana.

    R: Banana who?

    C: Knock knock.

    R: Who’s there?

    C: Banana.

    R: Banana who?

    C: Knock knock.

    R: Who’s there?

    C: Orange.

    R: Orange who?

    C: Orange you glad I didn’t say banana.

    R: Ha ha very funny, Nancy.

    Internally the AIML interpreter stores the input pattern, that pattern and topic pattern along a single path, like: INPUT <that> THAT <topic> TOPIC.  When the values of <that> or <topic> are not specified, the program implicitly sets the values of the corresponding THAT or TOPIC pattern to the wildcard *.

    The first part of the path to match is the input. If more than one category have the same input pattern, the program may distinguish between them depending on the value of <that>. If two or more categories have the same <pattern> and <that>, the final step is to choose the reply based on the <topic>.

    This structure suggests a design rule: never use <that> unless you have written two categories with the same <pattern>, and never use <topic> unless you write two categories with the same <pattern> and <that>. Still, one of the most useful applications for <topic> is to create subject-dependent "pickup lines," like:

    <topic name="CARS">

    <category>
    <pattern>*</pattern>
    <template>
    <random>
    <li>What’s your favorite car?</li>
    <li>What kind of car do you drive?</li>
    <li>Do you get a lot of parking tickets?</li>
    <li>My favorite car is one with a driver.</li>
    </random>
    </template>

    Considering the vast size of the set of things people could say that are grammatically correct or semantically meaningful, the number of things people actually do say is surprisingly small. Steven Pinker,in his book How the Mind Works wrote, "Say you have ten choices for the first word to begin a sentence, ten choices for the second word (yielding 100 two-word beginnings), ten choices for the third word (yielding a thousand three-word beginnings), and so on. (Ten is in fact the approximate geometric mean of the number of word choices available at each point in assembling a grammatical and sensible sentence). A little arithmetic shows that the number of sentences of 20 words or less (not an unusual length) is about 1020."

    Fortunately for chat robot programmers, Pinker’s calculations are way off. Our experiments with A.L.I.C.E. indicate that the number of choices for the "first word" is more than ten, but it is only about two thousand. Specifically, about 2000 words covers 95% of all the first words input to A.L.I.C.E.. The number of choices for the second word is only about two. To be sure, there are some first words ("I" and "You" for example) that have many possible second words, but the overall average is just under two words. The average branching factor decreases with each successive word.

    We have plotted some beautiful images of the A.L.I.C.E. brain contents represented by this graph (http://alice.sunlitsurf.com/documentation/gallery/). More than just elegant pictures of the A.L.I.C.E. brain, these spiral images (see more) outline a territory of language that has been effectively "conquered" by A.L.I.C.E. and AIML.

    No other theory of natural language processing can better explain or reproduce the results within our territory. You don’t need a complex theory of learning, neural nets, or cognitive models to explain how to chat within the limits of A.L.I.C.E.’s 25,000 categories. Our stimulus-response model is as good a theory as any other for these cases, and certainly the simplest. If there is any room left for "higher" natural language theories, it lies outside the map of the A.L.I.C.E. brain.

    Academics are fond of concocting riddles and linguistic paradoxes that supposedly show how difficult the natural language problem is. "John saw the mountains flying over Zurich" or "Fruit flies like a banana" reveal the ambiguity of language and the limits of an A.L.I.C.E.-style approach (though not these particular examples, of course, A.L.I.C.E. already knows about them). In the years to come we will only advance the frontier further. The basic outline of the spiral graph may look much the same, for we have found all of the "big trees" from "A *" to "YOUR *". These trees may become bigger, but unless language itself changes we won’t find any more big trees (except of course in foreign languages). The work of those seeking to explain natural language in terms of something more complex than stimulus response will take place beyond our frontier, increasingly in the hinterlands occupied by only the rarest forms of language. Our territory of language already contains the highest population of sentences that people use. Expanding the borders even more we will continue to absorb the stragglers outside, until the very last human critic cannot think of one sentence to "fool" A.L.I.C.E..

     

    February 27

    Blobsy 2 RC 6 released!


    Home Page
    Blobsy 2

    04/02/2006: Blobsy 2 RC 6 released! Grab it here: Blobsy2 RC6

    18/11/2005: This site is under (re)construction. Sections may offer insufficient information or maybe incomplete. Please bear with us while the site is brought up-to-date.

    Blobsy is an Open Source MSN Messenger bot, designed for easy setup, flexibility and ease of use. It is highly customizable and adding more functionality is extremely easy. It is freely distributable under the GNU General Public License (GPL).

    The current version of Blobsy currently supports the following features:

    • Easy setup and configuration
    • Runs on Linux and Windows
    • MSNP12 support
    • Extendable with PHP scripts

    Requirements:

    • PHP 4.3.0 / 5
    • Linux / Windows

    MSN Messenger新增两聊天机器人!

    微软上周发布了两个新的聊天机器人,采取了与众不同的方向推荐给用户。而微软的竞争对手AOL,在本月为用户密友列表自动添加了两个AIM的聊天机器人后,却引起了用户的连连抱怨。

      微软这两个聊天机器人是参考微软的Encarta百科全书和BBC的电视列表来回答问题的。用户可以用这项自动化的服务通过说“hello”并且按照提示进行会话。那些有兴趣的用户可以把encarta@conversagent.combbcbackstage@hotmail.com添加到他们的联系人列表中,这样就可以测试这两个聊天机器人了。

      BBC的电视列表机器人好像对国内用户用处不大,笔者添加了encarta@conversagent.com,问它:who is michael jordan?它居然回答:目前对这个问题的答案不可用,请给它点时间。不过如果问它某个地方的话,它会在聊天窗口右边用地图显示出这个地方来,以下是笔者问它广州在哪里的答案:

     

    来源:huazi817的BLOG http://blog.sina.com.cn/u/45788636010000v2

    用.NET开发MSN聊天机器人 - (中文分词)

     写在前面:
    我不是开发人员,不是高手,就是自己比较爱玩。在技术上,没有什么喜欢摸索的精神,而是喜欢投机取巧。在这篇文章里,你也不能"少劳而获"地通过我的机器人修改出一个自己的机器人,因为自己觉得程序写的比较臭,所以不会open source。但是,如果你对.net或者C#有点了解的话,相信从这篇文章里,你可以找到一切所需的资源,来开发一个自己的,绝对可用的msn机器人。要和我的机器人聊天,可以加tbot01@hotmail.com,名字叫“塔奇克马”,是从动画片攻壳机动队得名。同时,你也可以去http://www.guanqun.com,那里有一个和这个MSN机器人一样的网页聊天机器人,可以先聊聊看,尽量用中文聊。

    这不是一篇新手入门的文章,如果你不知道什么是.NET,不了解数据库方面的哪怕是一丁点东西,建议你先看看。同时,也希望真正的高手不要笑话打击我,毕竟一个普通的,非开发人员的电脑爱好者通过摸索,并告诉大家怎样做一个好玩的东西,不是一件错事。

    一、为什么要做MSN聊天机器人

    1 我能想到的原因

    最重要的是因为很好玩。你的MSN机器人说的话,一定体现你的性格(如果你希望这样的话)。当然,这是我的理由,作这个机器人的初衷仅仅是突然有一天自己想做。也许你也希望你的机器人可以帮助你做一些事情,类似一个专家系统或是客服系统等等。

    2 现在的MSN聊天机器人

    现在MSN机器人有很多,如果你加过MSN机器人,我想你列表上最多的是一个叫做“小布”或者是他兄弟姐妹一大堆的家伙们(http://www.9zi.com),可能基于负载的考虑,每次上线你都可能被他们一家子的一堆加入好友的请求包围。还有一些所谓的“免费短信”机器人,我一直就是做SP的,我直接说,为了不耽误你们赚钱,我不对这种机器人加以什么评论。可以提一下MsgerAI(msgerai@hotmail.com)这个机器人,开发它的这位老兄非常希望做一个可以像人一样具有智能的东西,虽然可能在他有生之年都无法完成,不过我还是祝他成功。毕竟有梦想就是好的,而且这个机器人现在也可以为他完成些工作(http://www.funnyok.net/nlp)。还有一些其他的MSN机器人,比如专门提供信息查询服务的,帮你搜索google的等等。MSN进行时里面有列表(http://www.msning.com),自己去看看就好了。

    二、为什么用.NET

    其实理由很简单。C#和Java很像,但是Java我实在找不出一个非常好用的,符合自己使用习惯的IDE来。而C#就不同,Vs.NET(http://msdn.microsoft.com/vstudio/) 当然最好用,C# Builder(http://www.borland.com/csharpbuilder/)也不错,连SharpDevelop(http://www.icsharpcode.net/OpenSource/SD/)用起来都相当舒服。所以选择.NET比较好。

    另外,.NET在开发上非常方便,只要你有一点点开发基础,用.NET写程序就不是很难。我是站在一个使用者而非开发者的角度,不用去钻研太多技术层面,或是优化的东西,我没那能力也不想进微软研究院。

    建议你使用最新版Visual Studio.NET,可以省去很多麻烦的事儿。

    同时,.NET开发可以找到的资源也有很多,我们接下来会提。

    三、你要一个什么样的聊天机器人

    1 开发前的设想

    我这里讨论的就是“聊天机器人”这个概念,意思是,他能做的就是陪你聊天。你要有一个程序去“教”他说话,同时要让他明白话语中包含的大概意义,还能够做基本上不怎么离谱的回答。

    2 还可以让他做什么

    你还可以让他做很多其他的事情,比如查询ip,手机号码,注册号,航班号,或者直接让他去查google,帮你搜索。这些也都不是什么麻烦的事情,只要你想。

    四、先让机器人开口说话

    不管你的机器人聪不聪明,让他能在MSN上象摸象样地回答是最重要的。所以,你需要有一个MSN帐号,连接到MSN服务器,取得各种服务器的消息,同时发送消息回服务器。

    当然,你可以分析MSN的协议(http://www.hypothetic.org/docs/msn/index.php),自己写通讯部分。不过我提过,我是个喜欢投机取巧的人,所以,找个能用的接口用就好了。所以,我找了些MSN的开发接口。

    MSNHelper:
    http://sourceforge.net/projects/msnphelper/

    dotMSN:
    http://members.home.nl/b.geertsema/dotMSN/

    这两个都是为.NET开发的,我用dotMSN,它使用MSNP8协议。注意dotMSN不要用sourceforge上的版本,要用上面给出的地址。
    接下来,下载这个例子:
    http://members.home.nl/b.geertsema/dotMSN/...ple/Example.zip

    用vs.net打开,编译,执行。

    看懂了吧。登录之后随便双击列表上的某个人,会发送给这个人一句"Hello world!"。你已经可以不通过MSN原来的程序,而直接和人说话了。

    这部分的代码是这样的:

    private void ContactJoined(Conversation sender, ContactEventArgs e)
    {
    // someone joined our conversation! remember that this also occurs when you are
    // only talking to 1 other person. Log this event.
    Log.Text += e.Contact.Name + " joined the conversation.\r\n";

    // now say something back. You can send messages using the Conversation object.
    sender.SendMessage("Hello world!");
    }

    意思是当对方加入聊天后,你就给他发一个"Hello world!"的消息过去。这时候如果你列表上的人双击你的名字,也同样会收到一个Hello World!。

    五、让机器人懂中文

    1 数据库

    因为我们要做中文聊天机器人,语料库的大小直接关系到你的机器人是不是聪明。由于自己的习惯,我用了mysql作为存放语料库和中文分词库的数据库。而且mysql速度极快。当然,你要用Access或者Sql Server,完全可以,而且更容易些。.NET调用Mysql的库可以在这里找到MySQL Driver CS
    http://sourceforge.net/projects/mysqldrivercs/


    2 整句匹配

    整句匹配这个概念很简单。聊天嘛,不认识的人一般都会要上来就说“你好”,或者“hi~~”之类的。这种话通常很简单,而且没有什么太多的变化,直接让机器人回答就行了。比如对方说“你好”,机器人看到这个“你好”,就直接回答“你好”,就可以了。或者对方说"88",你可以让机器人说“再见”,或者88什么的。。这就叫整句匹配。就是机器人拿到整个的句子,在库里面一查,啊,库里有这句话怎么回答,挑出一句回答过去,对方不会觉得这个机器人笨。

    甚至如果对方说“你好笨”,你让机器人回答“我才不笨呢”,对方一定会觉得,这个机器人还行,还知道别人说他笨。

    3 中文分词

    一个聊天机器人当然要懂些中文。中文处理的基础就是中文分词。分词是什么?“分词就是将连续的字序列按照一定的规范重新组合成词序列的过程。”这个定义是我抄的。请查阅这篇文章:http://www.hylanda.com/center/knowledge.htm 他们做中文分词应该有一定成绩。国内的分词系统,ICTCLAS做得也比较好。有VC的源代码,可以下来看看。
    http://www.nlp.org.cn/project/project.php?proj_id=6

    有人会说,这个东西我不懂,我也没研究过。其实我也不懂。只不过,如果不做中文分词,聊天机器人就只能停留在证据匹配的地步。我们可以使用最大匹配法,对聊天机器人接收到的话做简单的分词。关于算法,请参阅詹卫东先生的这个讲义,估计你一看就明白了。

    课程名称:中文信息处理基础

    http://ccl.pku.edu.cn/doubtfire/Course/Chi...2002_2003_1.htm

    下载这个PPT讲义:http://ccl.pku.edu.cn/doubtfire/Course/Chinese%20Information%20Processing/contents/Chapter_07_1.ppt

    分词算法不需要太复杂,简单点就好。

    另外分词算法需要一个中文分词库。我提供了一个mysql的,这里可以下载。导入到你的mysql里面就可以。其他数据库其实把sql语句简单改改也可以用。
    中文分词库下载:http://www.guanqun.com/down/wordlist.rar

    4 词语的匹配

    仅仅会分词还不够,如果真的要让机器人了解人说的话,肯定需要一些人工智能的算法。我们就是做个机器人玩玩,没必要研究的那么深。人工智能走到现在,太聪明的聊天机器人也少之又少。而且,让专业的研究人员去研究就好了,我们仅仅就是玩玩。所以呢……我们就用一个最简单的办法。我们的方法是,让机器人找这一句话的关键词,这句话大概的词性搭配,再去语料库里面找到符合这样规则的回答的话。

    举个简单的例子:
    比如对方说:

    “你真好玩”

    我们先用分词算法,把这句话分成

    “你 真 好玩”,

    然后找出关键词“好玩”。同时把这句话的词性搭配也记录下来。 这样,当找到关键词“好玩”在语料库中的时候,我们再来找是否有类似这这句话词性搭配的回答,如果有,随机回答出一句:“哈哈。。。我就喜欢你这么说。”,这样,可以给聊天者比较好的感觉。

    那么问题来了,如何找出关键词呢? 我的方法是……(比较烂,但是通常有效),找出这句话中长度最长的词作为关键词。没有为什么,因为这样速度会快些。如果一句话中所有的词都被扫描成为关键词,再去查库,会出现些匹配上的问题。(不科学,但是通常有效)。

    五、让机器人再“聪明”些

    1 整句匹配语料库的设计

    第一步当然是做你的整句匹配语料库。语料库都要自己写的,不要偷懒。找出别人最常说的话,比如你好谢谢对不起什么的,多放些回答在里面,免得每次回答都是一样的,然后要回答的时候,先写一句sql来查询,如

    select * from reply where `key` = '"+sentense+"' order by rand() limit 1

    把找到的话直接回复过去就可以了。如果找不到整句匹配,再做分词处理。

    2 分词匹配语料库的设计

    因为我们分词算法也没有经过什么优化,同时,我们找出关键字的办法也不是那么的好,所以,你给出的回答一定要不那么清晰。说白了就是,回答的话要有些“含糊不清”才可以。目标就是,让人觉得机器人对他说的话已经理解了,回答出来的还算比较“对路”。不要求100%对路,只要有40%以上对路,聊天的人基本就可能会接受。同时,回答的语料库,最好可以引导对方再次回答的时候,可以说出你语料库里面有的,最好是可以整句匹配的句子。

    举个好玩的例子:

    问题:你是男的还是女的?/你是男的还是女的/你是男的还是女的? (是否有标点符号没关系,我们要记录句子的词性搭配,同时,要对标点符号做些处理)

    像这样一句话,我们可以通过分词,找出关键词:“还是”,而且通过判断词性,可以知道,这是一句问句。而且问的是在两种情况之间选择。(当然,我们通过简单的算法,没法知道这句话其实是问性别)

    对于这样的问题,你的机器人怎么回答?其实很简单,首先,回答要“对路”,尽量不让人觉得答非所问,至少让人觉得,你的机器人是知道对方在问什么的。所以,我的机器人这样回答:

    机器人回答:都是。。。哈哈

    因为回答的是聊天用语,而且带点开玩笑的意味,所以会使聊天者觉得,这个机器人还不是那么笨。

    这只是一个简单的例子。很多具体的句子还得你自己去分析。当然,语料库越多,机器人懂的就越多,也就越聪明了。

    3 匹配不到关键字怎么办

    语料库不是很多的情况下,很可能我们的分词算法匹配不到合适的回答来应付。所以我们还要另外做一个语料库,用来在实在匹配不到关键词的时候,进行回答。这样的回答比较需要类似“算卦”的人的回答技巧,因为对方可能说任何话,而我们的机器人不明白。所以,要想办法“蒙混过关”,同时,尽量引导对方向你机器人可能回答的方面说。你可以试着跟“小布”聊聊天,会发现它回答不出来的时候,就会随便挑一句“佛经”来说。

    其实最重要的一个技巧就是,学习一下算卦的人说的话,都是云里雾里的,让人摸不到头脑,还觉得可能是对的。我们就要让机器人学习这种技巧,来达到看上去“聪明”的目的。


    最后的话:
    其实写这样的一个机器人程序很快的,如果熟悉些的话,估计一天应该就可以写出来。我大概用了一天半,还加上准备些语料库的时间。如果你真想做个稍稍“聪明些”的机器人来玩,这篇文章应该可以为你剩下至少3-5个小时的找资料的时间。如果你懒得自己研究,也有别的公司做的只能整句匹配的程序可以下载,自己下一个玩玩也就算了。
    来源:zhiming的BLOG http://blog.sina.com.cn/u/5478ba0a0100008j

    dotnet+xml开发简单聊天机器人

     做一个智能的聊天机器人并不容易,我这里只是实现了一个很简易的聊天机器人。
       当你和这个机器人聊天的时候,每次机器人会根据你说的话的关键词找到回答的语句。如果找不到就随机的说一句默认语言。数据存储格式是xml。
       以下是xml的原文件:
    <?xml version="1.0" encoding="UTF-8"?>
    <chat>
      <!--默认的聊天语句-->
      <default>
        <content>你在哪里?</content>
        <content>你还是学生吗?</content>
       .........
     </default>
     <!--回答指定关键词的语句序列-->
    <answer>
        <content key="怪"> 不怪</content>
        <content key="慢">是啊,慢</content>
        <content key="喂">什么事?</content>
        <content key="88">再见</content>
        <content key="谢">没什么好谢的</content>
        <content key="滚">我不会滚,我会走</content>
    ......
    <answer>
    </chat>
    ////////////////////////////////////////////////////////////////////
    以下是主要的源代码:
    Imports System.Xml
    Public Class Form1
        Inherits System.Windows.Forms.Form
    #Region " Windows 窗体设计器生成的代码 "
        Public Sub New()
            MyBase.New()
            '该调用是 Windows 窗体设计器所必需的。
            InitializeComponent()
            '在 InitializeComponent() 调用之后添加任何初始化
        End Sub
        '窗体重写 dispose 以清理组件列表。
        Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
            If disposing Then
                If Not (components Is Nothing) Then
                    components.Dispose()
                End If
            End If
            MyBase.Dispose(disposing)
        End Sub
        'Windows 窗体设计器所必需的
        Private components As System.ComponentModel.IContainer
        '注意: 以下过程是 Windows 窗体设计器所必需的
        '可以使用 Windows 窗体设计器修改此过程。
        '不要使用代码编辑器修改它。
        Friend WithEvents RichTextBox1 As System.Windows.Forms.RichTextBox
        Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
        Friend WithEvents Button1 As System.Windows.Forms.Button
        <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
            Dim resources As System.Resources.ResourceManager = New System.Resources.ResourceManager(GetType(Form1))
            Me.RichTextBox1 = New System.Windows.Forms.RichTextBox
            Me.TextBox1 = New System.Windows.Forms.TextBox
            Me.Button1 = New System.Windows.Forms.Button
            Me.SuspendLayout()
            '
            'RichTextBox1
            '
            Me.RichTextBox1.Location = New System.Drawing.Point(0, 0)
            Me.RichTextBox1.Name = "RichTextBox1"
            Me.RichTextBox1.ReadOnly = True
            Me.RichTextBox1.Size = New System.Drawing.Size(560, 304)
            Me.RichTextBox1.TabIndex = 2
            Me.RichTextBox1.Text = ""
            '
            'TextBox1
            '
            Me.TextBox1.Location = New System.Drawing.Point(0, 312)
            Me.TextBox1.Name = "TextBox1"
            Me.TextBox1.Size = New System.Drawing.Size(456, 21)
            Me.TextBox1.TabIndex = 0
            Me.TextBox1.Text = ""
            '
            'Button1
            '
            Me.Button1.Location = New System.Drawing.Point(472, 312)
            Me.Button1.Name = "Button1"
            Me.Button1.Size = New System.Drawing.Size(72, 24)
            Me.Button1.TabIndex = 1
            Me.Button1.Text = "Enter"
            '
            'Form1
            '
            Me.AutoScaleBaseSize = New System.Drawing.Size(6, 14)
            Me.ClientSize = New System.Drawing.Size(560, 341)
            Me.Controls.Add(Me.Button1)
            Me.Controls.Add(Me.TextBox1)
            Me.Controls.Add(Me.RichTextBox1)
            Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
            Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon)
            Me.MaximizeBox = False
            Me.Name = "Form1"
            Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
            Me.Text = "青蛙王子"
            Me.ResumeLayout(False)
        End Sub
    #End Region
        
        Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
            readxml()
        End Sub

        Dim xmlFile As String = "./robot.xml"
        Dim chatList As New ArrayList
        Dim answerList As New Hashtable
        Dim random As New System.Random
        Private Sub readxml()
            Try
                Dim doc As XmlDocument = New XmlDocument
                doc.Load(xmlFile)
                Dim nodeList As XmlNodeList
                Dim root As XmlElement = doc.DocumentElement
                '--默认的聊天语句--
                nodeList = root.SelectNodes("/chat/default/content")
                Dim node As XmlNode
                For Each node In nodeList
                    chatList.Add(node.InnerText)
                Next
                '回答指定关键词的语句序列--
                nodeList = root.SelectNodes("/chat/answer/content")
                For Each node In nodeList
                    answerList.Add(node.Attributes("key").Value, node.InnerText)
                Next
            Catch ex As Exception
                MsgBox(ex.Message)
            End Try
        End Sub

        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            'RichTextBox1.SelectionBullet = True
            Dim Content$ = TextBox1.Text.Trim
            If (Content = "") Then
                RichTextBox1.AppendText("请不要欺骗我的感情,谢谢!" + ControlChars.Lf)
                Exit Sub
            End If
            If (Content.IndexOf(":") <> -1) Then
                If learnNewWord(Content) Then
                    RichTextBox1.AppendText("我又学会了新的东西,谢谢!" + ControlChars.Lf)
                End If
                Exit Sub
            End If
            RichTextBox1.AppendText(Content + ControlChars.Lf)
            Dim aStr$ = getSimilarContent(Content)
            If (aStr = Nothing) Then
                Dim i% = random.Next(1, chatList.Count)
                aStr = chatList.Item(i)
            End If
            RichTextBox1.AppendText(aStr.Trim + ControlChars.Lf)
            RichTextBox1.Refresh()
        End Sub
        '得到相似的字符串
        Function getSimilarContent(ByVal content As String) As String
            Dim keys As System.Collections.ICollection = answerList.Keys
            Dim enumR As System.Collections.IEnumerator = keys.GetEnumerator
            While (enumR.MoveNext)
                Dim str$ = enumR.Current
                If content.Equals(str) Then
                    Return answerList(str)
                End If
            End While
            enumR.Reset()
            While (enumR.MoveNext)
                Dim str$ = enumR.Current
                If (content.IndexOf(str) <> -1) Or (str.IndexOf(content) <> -1) Then
                    Return answerList(str)
                End If
            End While
            Return Nothing
        End Function
        '添加新的语句
        Function learnNewWord(ByVal content As String) As Boolean
            Try
                Dim doc As XmlDocument = New XmlDocument
                Dim i% = content.IndexOf(":")
                Dim str1$ = content.Substring(0, i)
                Dim str2$ = content.Substring(i + 1)
                doc.Load(xmlFile)
                Dim elem As XmlElement = doc.CreateElement("content")
                Dim attr As XmlAttribute = doc.CreateAttribute("key")
                attr.Value = str1
                elem.InnerText = str2
                elem.Attributes.Append(attr)
                '添加新的语句--
                Dim root As XmlElement = doc.DocumentElement
                Dim xmlNode As XmlNode = root.SelectSingleNode("/chat/answer")
                xmlNode.AppendChild(elem)
                answerList.Add(str1, str2)
                doc.Save(xmlFile)
                Return True
            Catch ex As Exception
                MsgBox(ex.Message)
                Return False
            End Try
        End Function
        Private Sub TextBox1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles TextBox1.KeyPress
            If e.KeyChar.Equals(ControlChars.Cr) Then
                Button1_Click(Nothing, Nothing)
            End If
        End Sub
    End Class