qq聊天机器人 群发工具 (java版) (二)

上一篇介绍了如何借用webqq协议登陆qq,这一篇主要讲下如何实现群发消息。就目前我所知的消息类型有3种,分别是好友消息,群消息以及临时会话消息(这个一般是往群组成员群发)。3种消息分别对应3种方法(3个post方法),下面依次介绍。

1.群发好友消息

要想群发好友消息,首先要获取消息对象,也即好友列表。只有获取了每个QQ好友的标识,才知道往谁去发消息。所以,群发的第一步其实就是获取对象,这里也即获取好友列表。下面是获取好友列表的post请求信息:

Request URL:http://s.web2.qq.com/api/get_user_friends2
Request Method:POST
Content-Type:application/x-www-form-urlencoded
Referer:http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1
Form-Data:r={"vfwebqq":"23aedade67a2278b9a807015eb4bcad5771671c3a7c5a5e7c017034b9ba9d3f28407f179e5f34566","hash":"51C8576A0E1B5A7D"}
url地址很简单,看名字也知道意思,post方法。一定要记住要设置请求头Content-Type为application/x-www-form-urlencoded(不设置post请求就不成功即返回的retcode不为0)。同时也要设置请求头Referer,不然也会失败(后续很多方法都要设置Referer)。再就是表单数据,同之前一样,json字符串之前一定要带上"r="。vfwebqq即上篇中我所提到的,单独获取vfwebqq返回的值,而非第二次登陆成功所返回的值。hash是通过某种方式加密后获取到的值,同密码一样,是通过js文件加密的。这边先附上js的链接:http://0.web.qstatic.com/webqqpic/pubapps/0/50/eqq.all.js。加密方法就在这个js里面,将代码格式化后找到P = function(b, j) 这个方法(参数可能不是b,j,方法每隔一段时间就会变),只将这个方法提取出来就好,加密用到的就是这个方法。b即登陆的QQ账号,j即之前登陆时获取的ptwebqq。下面先贴出我提取出后更改函数名后的hash加密方法(至少今天还是这个方法,以后即使方法换了,js文件地址也不变,方法结构大致也差不多,即使不懂如何加密的,在js文件里找类似的方法也容易):

function getHash(b, j) {
	for (var a = [], i = 0; i < j.length; i++) a[i % 4] ^= j.charCodeAt(i);
	var w = ["EC", "OK"],
	d = [];
	d[0] = b >> 24 & 255 ^ w[0].charCodeAt(0);
	d[1] = b >> 16 & 255 ^ w[0].charCodeAt(1);
	d[2] = b >> 8 & 255 ^ w[1].charCodeAt(0);
	d[3] = b & 255 ^ w[1].charCodeAt(1);
	w = [];
	for (i = 0; i < 8; i++) w[i] = i % 2 == 0 ? a[i >> 1] : d[i >> 1];
	a = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
	d = "";
	for (i = 0; i < w.length; i++) d += a[w[i] >> 4 & 15],
	d += a[w[i] & 15];
	return d
};
下面附上Java调用js的方法:

/**
 * 获取QQ好友列表的post参数hash
 * 
 * @param paras
 * @return
 * @throws ScriptException
 * @throws FileNotFoundException
 * @throws NoSuchMethodException
 */
public static String getHash(String QQ, String ptwebqq) {
	Object t = null;
	try {
		ScriptEngineManager m = new ScriptEngineManager();
		ScriptEngine se = m.getEngineByName("javascript");
		se.eval(new FileReader(new File("resources/js/eqq.all.js")));
		t = se.eval("getHash(\"" + QQ + "\",\"" + ptwebqq + "\")");
		return t.toString();
	} catch (Exception e) {
		e.printStackTrace();
	}
	return t.toString();
}
最后附上发送post请求的方法,首先拼装好发送的json字符串,这里切记, json数据要经过url转码(外面的"r="不需要转码)

getFriendListStr = "{\"vfwebqq\":\"" + vfwebqq + "\",\"hash\":\""
				+ hash + "\"}";
		getFriendListStr = "r="
				+ URLEncoder.encode(getFriendListStr, "utf-8");
下面是post方法:

// 获取好友列表
public static boolean getFriendList() throws Exception {
	// post 请求
	DefaultHttpClient client = new DefaultHttpClient();
	HttpPost postjson = new HttpPost(
			"http://s.web2.qq.com/api/get_user_friends2");

	postjson.setHeader("Referer",
			"http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1");

	HttpClientParams.setCookiePolicy(client.getParams(),
			CookiePolicy.BROWSER_COMPATIBILITY);

	client.getParams().setParameter(
			CoreConnectionPNames.CONNECTION_TIMEOUT, 5000);
	client.getParams().setParameter(CoreConnectionPNames.SO_TIMEOUT, 5000);

	StringEntity entity = new StringEntity(getFriendListStr);
	entity.setContentType("application/x-www-form-urlencoded");
	postjson.setEntity(entity);

	// 设置CookieStore
	if (cs != null) {
		client.setCookieStore(cs);
	}

	// 获得返回的json数据包
	HttpResponse httpResponse = client.execute(postjson);
	HttpEntity httpent = httpResponse.getEntity();

	// 保存CookieStore
	cs = client.getCookieStore();

	String line;
	StringBuffer sb = new StringBuffer();

	if (httpent != null) {
		BufferedReader br = new BufferedReader(new InputStreamReader(
				httpent.getContent(), "UTF-8"));
		while ((line = br.readLine()) != null) {
			sb.append(line);
		}
		br.close();
	}
	JSONObject obj = new JSONObject(sb.toString());
	if (obj.getInt("retcode") == 0) {
		JSONObject res = obj.getJSONObject("result");
		QQMsgHandler.getInstance().getFriendList(res);// 处理信息,得到好友列表
		return true;
	} else {
		return false;
	}
}
请求成功返回的json数据如下:

{"retcode":0,"result":{"friends":[{"flag":0,"uin":2741681712,"categories":0},{"flag":0,"uin":2117029336,"categories":0}],"marknames":[],"categories":[],"vipinfo":[{"vip_level":0,"u":2741681712,"is_vip":0},{"vip_level":0,"u":2117029336,"is_vip":0}],"info":[{"face":303,"flag":294126146,"nick":"               ScumVirus","uin":2741681712},{"face":564,"flag":8388608,"nick":"ScumVirus","uin":2117029336}]}}
在friends中有categories这个属性,可能是好友分组的标志位,不过这里我没研究,因为我只做了群发所有好友消息。有兴趣的朋友可以深入分析下好友分组,这样可以指定只给某一分组的好友发送消息,这样那些生活工作通用一个QQ的人群发消息会比较方便。info跟friends类似,不过没有带上分组信息,多了用户昵称。所以我只截取了info中的数据作为好友列表,uin即每一个好友的标识,nick即昵称。face不知道是什么,我没深入研究,flag也是一个标志位,但是具体有什么作用我也不知道,因为实现群发功能uin作为标识足矣。中间的vipinfo应该是与qq会员相关的信息,不过一般做QQ机器人都没有这方面需求,我直接无视了。

接下来就要发送好友消息了。其实群发消息也就是给每一个好友都发送一条消息(我没试过在json里以数组形式写上所有消息,不知道能不能实现一次群发)。

Request URL:http://d.web2.qq.com/channel/send_buddy_msg2
Request Method:POST
Referer:http://d.web2.qq.com/proxy.html?v=20130916001&callback=1&id=2
Form-Data:r={"to":2741681712,"content":"[\"123\",[\"font\",{\"name\":\"宋体\",\"size\":10,\"style\":[0,0,0],\"color\":\"000000\"}]]","face":564,"clientid":53999199,"msg_id":38600001,"psessionid":"8368046764001d636f6e6e7365727665725f77656271714031302e3133392e372e313630000054bd00000bcd026e04003654298d6d0000000a404e364b386b707858676d000000286740432cf4f2628258046027ab71e7c932587058acf842d2c727a076b663ff32b4082a088f3c4521"}
依旧是post请求,不过要带上Referer,json字符串格式如上所示。to是发送对象即好友的uin,content里带的是发送文字的相关信息,我发的是最普通的消息,所有参数都是默认的,如果你想发点与众不同的, 改下参数即可。clientid同登陆一样,随机8-9位,随便写,我程序里这个参数都是写的固定的默认值。msg_id为发送消息的标识,随便写个数字,然后每发一条自增加1作为区分即可(即使每次都一样好像也没什么影响吧,不过最好+1吧),psessionid也是登陆成功返回的参数。代码同之前没什么区别,就不贴了。


2.群发群消息

同上,先获取对象,即群列表。·获取群消息的post请求如下:

Request URL:http://s.web2.qq.com/api/get_group_name_list_mask2
Request Method:POST
Content-Type:application/x-www-form-urlencoded
Referer: http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1
Form-Data:r={"vfwebqq":"23aedade67a2278b9a807015eb4bcad5771671c3a7c5a5e7c017034b9ba9d3f28407f179e5f34566","hash":"51C8576A0E1B5A7D"}

方法大致同获取好友列表时,只有url地址不一样,其他都一样(包括json数据)。返回的json数据如下:

{"retcode":0,"result":{"gmasklist":[],"gnamelist":[{"flag":1090520065,"name":"芭蕉躲在被窝不给h包","gid":4065055661,"code":818309331},{"flag":17826817,"name":"ScumVirus","gid":1068943425,"code":1244601995},{"flag":16778241,"name":"永丶夜","gid":818744232,"code":171813541},{"flag":16778241,"name":"ScumVirus机器人","gid":2646811562,"code":3760910371}],"gmarklist":[]}}
gid即群标识,也即我们发送群消息的对象。不过code也是群的一种标示,不过不同于gid。

这里简单讲解一下uin和gid分别是好友标识和群标识,针对同一个对象(1个好友或者1个群),同一账号每次登陆获取到的标识是一样的,但是不同账号获取的不同。比如说a,b在群c中,但是a登陆后获取到c的gid为1,b登陆后获取到c的gid可能就是2,反正不同于a获取到的。但是code不一样,每个人每次登陆获取到同一个群的gid是一样的,比如如上的永丶夜这个群,我2个账号登陆获取到的gid都是818744232这个值,一直不变。gid只能作为群消息发送的标识,若要获取群组成员的信息,则是code作为群组标识,这个在发送临时会话消息时会提到。

发送群消息的请求如下:

Request URL:http://d.web2.qq.com/channel/send_qun_msg2
Request Method:POST
Content-Type:application/x-www-form-urlencoded
Referer:http://d.web2.qq.com/proxy.html?v=20130916001&callback=1&id=2
Form-Data:r={"group_uin":1068943425,"content":"[\"123\",[\"font\",{\"name\":\"宋体\",\"size\":10,\"style\":[0,0,0],\"color\":\"000000\"}]]","face":564,"clientid":53999199,"msg_id":38600002,"psessionid":"8368046764001d636f6e6e7365727665725f77656271714031302e3133392e372e313630000054bd00000bcd026e04003654298d6d0000000a404e364b386b707858676d000000286740432cf4f2628258046027ab71e7c932587058acf842d2c727a076b663ff32b4082a088f3c4521"}
基本同发送好友消息一致,json数据中group_uin即之前获取到的gid,其他同发送好友消息无区别。


3.发送临时会话消息

这边的临时会话仅指和已有群的群内成员发送会话。毕竟发送消息要获取对象的标识,这个标识又不是qq账号,而是uin,如果你只知道一个人的QQ号,但是和他又不是好友,又不在同一个群里,那么是没法获取到他相对于你的uin的,即发送消息不成立。

回归正题,如上所说,我们是要通过群来获取群成员相对于自身的uin。首先,我们要先获取群内所有成员的信息,这里要用到之前提到的code。post请求如下:

Request URL:http://s.web2.qq.com/api/get_group_info_ext2?gcode=1244601995&vfwebqq=670f701a39ab1b21c05007564cf8936d6000b1f5a34093689aaa7ff67e8a3ede932d3ecb1b98f27c&t=1433068633058
Request Method:GET
Referer:http://s.web2.qq.com/proxy.html?v=20130916001&callback=1&id=1
这里很简单,仅仅一个get方法就获取到的一个群内的所有成员信息,gcode即之前说的code,vfwebqq不谈了,t即时间戳,直接用System.currentTimeMillis()获取就好。

返回的json数据如下:

{"retcode":0,"result":{"stats":[{"client_type":41,"uin":2368295990,"stat":10}],"minfo":[{"nick":"               ScumVirus","province":"湖北","gender":"male","uin":2741681712,"country":"中国","city":"武汉"},{"nick":"ScumVirus","province":"湖北","gender":"male","uin":570904041,"country":"中国","city":"武汉"},{"nick":"No Game No Bot","province":"湖北","gender":"female","uin":2368295990,"country":"中国","city":"武汉"},{"nick":"ScumVirus","province":"湖北","gender":"male","uin":2117029336,"country":"中国","city":"武汉"}],"ginfo":{"face":0,"memo":"yy108371257","class":25,"fingermemo":"","code":1244601995,"createtime":1321090723,"flag":17826817,"level":0,"name":"ScumVirus","gid":1068943425,"owner":2741681712,"members":[{"muin":2741681712,"mflag":192},{"muin":570904041,"mflag":0},{"muin":2368295990,"mflag":0},{"muin":2117029336,"mflag":0}],"option":2},"vipinfo":[{"vip_level":0,"u":2741681712,"is_vip":0},{"vip_level":0,"u":570904041,"is_vip":0},{"vip_level":0,"u":2368295990,"is_vip":0},{"vip_level":0,"u":2117029336,"is_vip":0}]}}
这里只关注minfo即可,里面既有用户昵称,居住地,性别,也有我们要的uin标识。

接下来就是发送临时消息了,不过在这个方法比之前的2种方法要多出一步,他要获取group_sid这个参数才能发送临时会话消息,不过这个参数也很好拿到,一个get请求就行了。请求如下:

Request URL:http://d.web2.qq.com/channel/get_c2cmsg_sig2?id=<span style="font-family: Arial, Helvetica, sans-serif;">1068943425</span>&to_uin=570904041&clientid=53999199&psessionid=8368046764001d636f6e6e7365727665725f77656271714031302e3133392e372e313630000054bd00000bcd026e04003654298d6d0000000a404e364b386b707858676d000000285f293205986c0c9381ea5471ac8bdfd996fb8ca4765bcebab0230c3b0fb85cf513378825ebe2ade8&service_type=0&t=1433069146558
Request Method:GET
Referer:http://d.web2.qq.com/proxy.html?v=20130916001&callback=1&id=2
id即群标识gid,to_uin即你要发送对象的uin标识,service_type我也不知道是什么,默认填0也不会错,其他参数之前都提过不多说了。

返回的json数据如下:

{"retcode":0,"result":{"type":0,"value":"b1a949941bc21ea0e56529d6c71b9065083e0ab74ca8ee8cedb0135440fe17b54fb8a807e202a0e491f40cf403d5e22e","flags":{"text":1,"pic":1,"file":1,"audio":1,"video":1}}}

只取其中的value,即是我们要的group_sig。

下面就到发送消息了,请求如下:

Request URL:http://d.web2.qq.com/channel/send_sess_msg2
Request Method:POST
Referer:http://d.web2.qq.com/proxy.html?v=20130916001&callback=1&id=2
Form-Data:r={"to":570904041,"content":"[\"123\",[\"font\",{\"name\":\"宋体\",\"size\":10,\"style\":[0,0,0],\"color\":\"000000\"}]]","face":564,"clientid":53999199,"msg_id":93440002,"psessionid":"8368046764001d636f6e6e7365727665725f77656271714031302e3133392e372e313630000054bd00000bcd026e04003654298d6d0000000a404e364b386b707858676d00000028d173ff0fccee9875162f70075476dfb3712e59456440efca2a0db0fb063216d74084919fd7185f16","group_sig":"b1a949941bc21ea0e56529d6c71b9065083e0ab74ca8ee8cedb0135440fe17b54fb8a807e202a0e491f40cf403d5e22e","service_type":0}
to即目标uin,group_sig即我们刚获取到的参数,service_type默认填0吧,反正不会错。

3种消息发送都有返回值,只判断recode是否为0就可知道是否发送成功。以上即是本篇介绍的所有内容。

注:若消息内容带回车,则可能发送失败。需要替换文本,将'\n'替换为'\\n',方法如下:

//消息处理 将文本中的换行符替换为\\n
private String replaceText(String str){
	String [] arr = str.split("\n");
	String s = arr[0] + "\\\\n" + arr[1];
	return s;
}

再稍微说点其他的。

目前我见到过2种QQ机器人,一种是酷Q,它是仿QQ老版界面做的,可以看到好友列表(这个上面我也提到过如何获取好友列表,它只是将列表显示出来了而已,群也一样),如果想给指定的某些人或群发送消息,那么你也可以做个列表,每个item前加上个jcheckbox让用户选择即可。另外一种是晨风QQ机器人。不过那个跟我做的差不多,没有列表展示,只是一堆功能设置放在上面。不过方法都有了,界面如何设计就看用户的需求了。









1068943425
  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值