« 编写自己的MSN机器人« »命令行下的-星球大战-经典电影收藏 »
MSN机器人梦工厂(收藏)

前几日看到网友“瞄瞄”的一个关于msn机器人的blog文章。然后在google上搜索了一下,原来,现在流行MSN机器人了!

由于MSN Messenger公开了它的通讯协议,使得很多人加入到编写自己的机器人的行列。msn协议全文共有20页,相比smtp协议来说是少得多了。但对于大多数开发者来说,根据原始协议来写机器人程序仍然是一件困难的事(更何况协议内容是英文的)。

也是在google上,我发现在Java领域,比较流行的机器人开发工具是一个韩国人写的一个jMSN开发包。它将复杂的协议封装成简单易用的Java API供使用者调用。(下载地址:http://sourceforge.net/projects/jmsn)

话虽这么说,使用起来还是有点阻碍的。整个jMSN开发包的文档全部是韩文写成,对于我们这些连英文都很吃力的人来说,这简直就是天书!

不过不要紧,欢迎大家进入机器人梦工厂,同我一起来探索MSN机器人的制造过程。


 

现在我们就开始实现自制MSN机器人梦想。

我们先不要去研究通篇韩文的开发文档。先跟笔者一起来研究一个有趣的机器人,看看它是怎么工作的。然后再一步步深入研究机器人的工作原理。

这是一个有趣的机器人,你对它说什么,它就应什么。我把它叫做应声虫机器人。来看看它的结构:

import java.util.Properties;

import rath.msnm.MSNMessenger;
import rath.msnm.SwitchboardSession;
import rath.msnm.UserStatus;
import rath.msnm.entity.MsnFriend;
import rath.msnm.event.MsnListener;
import rath.msnm.ftp.VolatileDownloader;
import rath.msnm.ftp.VolatileTransferServer;
import rath.msnm.msg.MimeMessage;

/**
* MSN应声虫机器人。
* @author Turbo Chen
* @create 2004-7-29
*/
public class YesmanRobot
{
public static void main(String[] args)
{
MSNMessenger msn = new MSNMessenger("yourname@hotmail.com", "xxxxxxxx");
msn.setInitialStatus(UserStatus.ONLINE);
msn.addMsnListener(new YesmanRobotAdapter(msn));
msn.login();
}


}


class YesmanRobotAdapter implements MsnListener
{

MSNMessenger msn;

public YesmanRobotAdapter(MSNMessenger msn)
{
this.msn = msn;
}

/**
* 收到消息事件。当收到消息时,会自动调用此方法。
*/
public void instantMessageReceived(SwitchboardSession ss, MsnFriend friend,
MimeMessage mmsg)
{
try
{
//发送相同的回复信息给发送者
MimeMessage newMsg = new MimeMessage(
"我是MSN应声虫,你说啥我应啥:" + mmsg.getMessage());
newMsg.setKind(MimeMessage.KIND_MESSAGE);
System.out.println(newMsg.getMessage());
msn.sendMessage(friend.getLoginName(), newMsg);
} catch (Exception e)
{
e.printStackTrace();
}
}

....
}

其中YesmanRobot是机器人的主类,为了让机器人工作,要先让它登入到MSN才行,相关代码如下:

MSNMessenger msn = new MSNMessenger("yourname@hotmail.com", "xxxxxxxx");
msn.setInitialStatus(UserStatus.ONLINE);
msn.addMsnListener(new YesmanRobotAdapter(msn));
msn.login();

在这里创建了一个MSNMessenger对象,传入登入帐号和密码,使用setInitialStatus方法设置它登入的的状态为'在线',最后是调用login方法登入。

为了使机器人可以达到“应声虫”的功能,在登入之前,我们为它添加了一个监听器。这个监听器是MSNListener的一个实现类。在这里我们实现了一个YesmanRobotAdapter类,它里面只实现一个instantMessageReceived方法,当有消息送给机器人时,会触发此方法,在这个方法里,我们的机器人将对方送过来的消息又送回给了对方。这样就实现的应声虫的功能。

在实际的完整例子中,你会发现MSNListener有多达28个接口,也就是说它除了可以监听收到消息的事件,还提供了许多其它的事件供我们使用。在以后的文章中,我们会慢慢的接触到这些事件。

通过这个应声虫机器人,我们知道,实现自己功能的机器人一点都不难。只需要在instantMessageReceived方法中处理收到的消息并回应,就变成你自己的机器人了。在后面的内容中,我们会更深入机器人内部,看看它的工作原理。


 

废话少说。接上回。

由上回的一个应声虫机器人的例子,我们初步了解到jmsn开发包的基本用法,我先画一个它的工作原理图给大家:

jmsn原理图

MSNMessenger为主程序。它包含一个NotificationProcessor,这个一个通讯事件的处理器,本质上它是一个独立运行的线程。MSNMessenger提供许多命名友好的方法来向其它联系人(透过服务器)发送通讯命令。发送通讯命令都是通过NotificationProcessor处理器在后台(独立线程)处理的。

所有的通讯命令都封装在命名友好的方法中发送,这样你就勿需了解MSNMessenger的底层通讯协议。MSNMessenger提供了许多实用的方法来调用。比如有登入和注销(login, logout),添加或删除好友(addFriend, removeFriend),建立和删除群组(addGroup, removeGroup),邀请友人聊天(doCall),或者邀请某人加入到现有的聊天中等等。有了这些现成的方法,你可以让你的机器人变成非常强大,当然,也完全可以用它做一个替代Microsoft Messenger的聊天工具。

命令通过处理器发送出去之后,并不是马上返回。它是在后台由处理器负责运行的。也就是说,你调用了登入命令之后,如果想马上添加某人为好友,这时程序会报错。登入过程可能是一个慢长的过程,它要与服务器通讯,要等待服务器返回回应信息。而处理器在后台做这些事情,做完后,它会通过你注册在MSNMessenger类中的MSNListener监听器通知你。

所以不要试者一连串的执行MSNMessenger中提供某些方法(主要是指前面提到的那些类似方法),你应该在自己的监听器中等待事件完成后的通知你后再执行相应命令。

MSNListener是一个接口,提供了多达28种事件通知方法。包括登入完成,好友上线或离线,开始会话或结束会话,收到新消息,有谁添加自己为好友等事件。

这次的说教就到这。最后我们来改进一下上次的应声虫程序,让它可以在某人上线后主动发一个消息给他打招呼(不然谁知道你是个应声虫:D)。

代码别的地方都一样,只是在YesmanRobotAdapter新实现了两个方法:

/**
* 好友上线时开始与其对话。
*/
public void userOnline(MsnFriend friend)
{
try
{
System.out.println("开始邀请"+friend.getLoginName()+"会话");
msn.doCall(friend.getLoginName());

} catch (IOException e)
{
// TODO 处理异常
e.printStackTrace();
}
}

/**
* 会话开始。
*/
public void switchboardSessionStarted( SwitchboardSession ss )
{
try
{

if ( ss==null ) return;

String toFriend = ss.getMsnFriend().getLoginName();

if ( msn.findSwitchboardSession(toFriend)!=null )
{
MimeMessage msg = new MimeMessage("你好,这是MSN应声虫机器人自动与你通话。欢迎提问");
msg.setKind(MimeMessage.KIND_MESSAGE);
boolean success = msn.sendMessage(toFriend, msg);
if ( success )
System.out.println("打完招呼.");
}


} catch (IOException ex)
{
// TODO 处理异常
ex.printStackTrace();
}
}

在这里,首先在用户上线的事件通知userOnline里调用了doCall方法。doCall方法是开始与友人的通讯。这时候还不能马上向其发消息,要等到会议开始之后,所以又实现了一个switchboardSessionStarted。当会话开始后,我们的机器人就可以在你上线的时候主动发出问题了。

MSN机器人梦工厂(4)---显示头像之基础篇 

各位,久违了。很久没一起探索MSN机器人了。

接下来的文章,我们一起来探索如果让机器人显示头像。

如要显示头像,机器人方和对方要经过一系列的通讯过程,其通讯过程如下:

1。对方发送一个INVITE信息给机器人。这种情况一发生在双方建立舒适的时候。

2。机器人收到一个信息后,将自己的BaseID发送给对方。

3。机器人接着再发出一个200 OK的信息给对方。

4。对方收到后,回应一个200 OK Ack信息。

5。收到应答后,机器人接下来就发送一个Data Preperation信息,表示数据准备好发送了。

6。对方收到后要回应一个Data Prep Ack表示应答。

7。如果没问题,机器人接下来就发送头像数据(如果头像数据大于1202bytes,要分多次发送)。

8。对方收完头像数据后,要发一个Data Ack的信息,表示收到所有数据了。

9。最后对方要再发一个BYE信息,结束通话。

通过解析网络通讯的数据包,我们发现,每次通讯的过程,发送的都是P2P信息。p2p信息格式有如下几部分构成:

MSG [TrId] D [msgLength]
MIME-Version: 1.0\r\n
Content-Type: application/x-msnmsgrp2p\r\n
P2P-Dest:
yourname@hotmail.com\r\n
\r\n
[Binary Header]
[Message Body]
[Binary Footer]

第一行中,TrId是通信用的Transaction Id,对于每次通话,如果是回应信息的话,TrId就应该是主叫信息的TrId。如果是主叫信息,此时必须是一个唯一ID, 对方回应时其信息中会返回此ID.而msgLength是此信息内容的字节数。
接下来的三行是p2p信息固有的, 最后以一个空白行\r\n结束.

接下来的部分我们称之为Binary Header,它是P2P消息中要发送数据的标头信息, Binary Header包含9个字段,每个字段定义如下:

MSN BinaryHeader
1. field1. 是包含了sessionId的Dword字段,  当正在开始邀请会话时,此时的值是0. 此字段只在发送数据
时使用.
2. field2. 也是一个Dword, 此条信息的标识(也称为BaseId).
第一条信息里包含了随机生成的原始BaseID.
之后的几次消息中分别用原始BaseID-3,BID-2, BID-1, BID+1
例如,以下是一个完整的P2P通讯的过程,大家注意看它的BID变化情况:
Me: Invite (baseID random)
58 A9 18 01 (18393432)(myBID)
You: BaseID (random)
3B 99 4F 02 (38771003)(yourBID)

You: 200 OK
38 99 4F 02 (38771000)(yourBID-3)
Me: 200OK ACK
55 A9 18 01 (18393429)(myBID-3)

You: Data Prep
39 99 4F 02 (38771001)(yourBID-2)
Me: Data Prep ACK
56 A9 18 01 (18393430)(myBI-2)

You: Send Data
3A 99 4F 02 (38771002)(yourBID-1)
Me: Data ACK
57 A9 18 01 (18393431)(myBID-1)

Me: Bye
59 A9 18 01 (18393433)(myBID+1)
You: Bye ACK
3C 99 4F 02 (38771004)(yourBID+1)

BaseID的范围从4 ~ 4 250 000 000). 它是随机生成的.

3. Field3是一个QWord, 包含了你要发送的数据的偏移位置(offset),
如果数据大小超过1202, 例如 如果你送2404 bytes数据, 那么第一次的信息中这个字段为0值(因为你还没
送出任何数据),第二条信息里此字段为1202,因为你已发送了1202 bytes.
如果没有数据发磅,此字段为0.
4. field4也是一个QWord, 包含了要发送整个数据的大小。如果没数据则为0并且通常field6要设为2.
5. field5是一个DWord, 包含了本次要发送数据的大小。例如,要发送一个5000bytes的文件,
则field4的值为5000, 而此字段的值为1202.因为每个发送最大不能超过1202个字节。
6. field6是一个DWord, 一个Flag字段. 如果没有Flag则值为0。
如果此信息为回应到另一个信息(ACK),则它的值是2, 如果在接收到的信息的binaryHeader有
错误发生则值为8. 如果是发送的Display Pictures(DP)或者Custom Emoticons(CE)
这个字段为32(0x20), 
完整的参数值见下表:
    0x00 - No flags
    0x01 - Unknown
    0x02 - Acknowledgement
    0x04 - Waiting for a reply
    0x08 - Error (Possibly binary level)
    0x10 - Unknown
    0x20 - Data for DP/CE
    0x40 - Bye ack (He who got BYE)
    0x80 - Bye ack (He who sent BYE)
    0x1000030 - Data for FT

7. field7, DWord. 如果本次binary header中不包含sessionID, 也不包含在发送的数据中,
则此字段值为对方上次信息发来的BaseID. 如果sessionID已包含在信息中,则此字段为一个随机数。
8. field8, 此字段包含对方上次传来的信息中第7个字段的值.
9. field9, QWord, 如果你正在送一个ACK信息,此字段包含对方上次信息的第4个字段的值.其它情况下此字段为0.

位于Binary Header和Binary Footer之间的就是我们要发送的数据,在不同的信息响应中,Message Body包含了不同的内容,这个在后面会做解释。

Binary Footer,也是一个DWord. 此字段是跟随在发送的数据之后, 因此称之为Footer. 当没有数据(DP, 或CE等)被发送时这个字段的值为0. 当发送DP时值为1, CE时是其它值, 发送Ink(手写笔迹信息,它来自于Tablet PC)时值为3. 其它的值现还在研究。

所以根据Binary Header信息,我们就可以知道此p2p信息一些基本内容,并作出相应处理。

参考资源:http://siebe.bot2k3.com/docs/p4p/?url=home.html

 


Tags: MSN  QQ  机器人   |

原创文章如转载,请注明:转载自:巴士飞扬-技术BLOG : http://www.busfly.net/

本文链接地址:http://www.busfly.net/post/187.html

如果你喜欢本文,请顶一下,支持我,你的支持是我继续发好文章的最大动力。谢谢。
好东西需要分享,快把本文发给你的朋友吧~!~

     
相关文章:




◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。
网站分类
分类最近文章
最近发表
最新评论及回复
最近留言
热文排行
随机推荐文章
Powered By Z-Blog   STYLE by busfly . FatMouse
Copyright © 2007 巴士飞扬技术博客. . 沪ICP备07027972号. 会员群1(J2EE为主):3769186.