使用地址: ymlkl.jsp.fjjsp.net/wchat/index.do
使用的微信网页版接口来源自 gaosen的python 脚本 ,地址: https://github.com/0x5e/wechat-deleted-friends
gaosen的python代码中在拉取微信通讯录的好友接口上没有加上相关的认证参数,导致该python代码查找结果不正确,读取不了微信通讯录,始终显示 “通讯录0个好友”
此java版改正了这个错误
同时为了方便使用,避免安装java环境的限制,做成了网页版 ,PC上打开网页扫码就可以使用(查找时间较长,前端使用ajax轮询来更新后台的查找信息)
使用地址: ymlkl.jsp.fjjsp.net/wchat/index.do
查找原理为通过微信网页版接口新建群聊并自动拉取好友,加不进来就是删除了你
(不在群聊里讲话,别人是看不见的~并不打扰好友)
主要接口通信源码:
package com.toltech.wchat.process;
import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.apache.http.client.ClientProtocolException;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.input.SAXBuilder;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.toltech.wchat.bean.Member;
import com.toltech.wchat.utils.HttpUtils;
public class Client {
private final static int MAX_GROUP_NUM = 35; // 每组人数
private final static int INTERFACE_CALLING_INTERVAL = 30000; // 接口调用时间间隔,
// 间隔太短容易出现"操作太频繁",
// 会被限制操作半小时左右
private final static int MAX_PROGRESS_LEN = 50;
static int tip = 0;
static String uuid;
static String base_uri;
static String redirect_uri;
static String push_uri;
static String skey;
static String wxsid;
static String wxuin;
static String pass_ticket;
static String deviceId = "e000000000000000";
static String baserequest;
static List<Member> memberList;
static String my;
static List<Member> allDeletedList =new ArrayList<Member>();
public static void main(String[] args) throws Exception {
try {
if (getUUID()) {
showQRImage();
waitForLogin();
while (!"200".equals(waitForLogin())) {
}
if (login()) {
if (webwxinit()) {
webwxgetcontact();
int memberCount =memberList.size();
System.err.println("通讯录共有"+memberCount+"好友");
System.out.println("开始查找...");
String chatRoomName =null;
int group_num =memberCount/MAX_GROUP_NUM;
if(memberCount% MAX_GROUP_NUM !=0)
group_num+=1;
for (int i=0;i<group_num;i++) {
Map<String, Object> map=null;
List<Member> userNames =new ArrayList<Member>();
for(int j=0;j<MAX_GROUP_NUM;j++){
if (i * MAX_GROUP_NUM + j >= memberCount)
break;
Member member = memberList.get(i * MAX_GROUP_NUM + j);
userNames.add(member);
}
//新建群组/添加成员
if(chatRoomName ==null){
map = createChatroom(userNames);
chatRoomName = map.get("chatRoomName").toString();
}
else
map =addMember(chatRoomName, userNames);
List<Member> deletedList = (List<Member>) map.get("deletedList");
allDeletedList.addAll(deletedList);
// 删除成员
deleteMember(chatRoomName, userNames);
// 进度条
System.out.println("新发现你被" +deletedList.size()+"人删除");
if (i != group_num - 1){
System.out.println("正在继续查找,请耐心等待...");
//下一次进行接口调用需要等待的时间
Thread.sleep(INTERFACE_CALLING_INTERVAL);
}
}
int deleteCount =allDeletedList.size();
System.out.println("结果汇总完毕,20s后可以重试");
System.out.println("被删除的好友列表,共"+deleteCount +"人");
for (Member member : allDeletedList) {
for (Member mem : memberList) {
if(member.getUserName().equals(mem.getUserName()))
System.err.println(mem.getNickName()+"("+mem.getRemarkName()+")");
}
}
}
}
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static boolean getUUID() throws Exception {
StringBuffer url = new StringBuffer();
url.append("https://login.weixin.qq.com/jslogin?");
url.append("appid=wx782c26e4c19acffb");
url.append("&fun=new");
url.append("&lang=zh_CN");
url.append("&_=" + String.valueOf(System.currentTimeMillis()));
String data = HttpUtils.httpGet(url.toString());
if (StringUtils.isNotBlank(data)) {
String code = data.substring(data.indexOf("= ") + 1,
data.indexOf(";"));
uuid = data.substring(data.indexOf("\"") + 1,
data.lastIndexOf("\""));
System.out.println("code:" + code + ";uuid:" + uuid);
if ("200".equals(code.trim()))
return true;
}
return false;
}
public static void showQRImage() throws IOException {
String url = "https://login.weixin.qq.com/qrcode/" + uuid;
url = url + "?t=webwx&_=";
url = url + String.valueOf(System.currentTimeMillis());
String filename = HttpUtils.getImage(url);
tip = 1;
Runtime.getRuntime().exec(
"rundll32 c:\\Windows\\System32\\shimgvw.dll,ImageView_Fullscreen "
+ filename);
}
public static String waitForLogin() throws Exception {
StringBuffer url = new StringBuffer();
url.append("https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?");
url.append("tip=" + tip);
url.append("&uuid=" + uuid);
url.append("&_=" + String.valueOf(System.currentTimeMillis()));
String data = HttpUtils.httpGet(url.toString());
String code = data.substring(data.indexOf("=") + 1, data.indexOf(";"))
.trim();
System.err.println(code);
if ("201".equals(code)) {
System.out.println("成功扫描,请在手机上点击确认以登录");
} else if ("200".equals(code)) {
System.out.println("正在登陆...");
redirect_uri = data.substring(data.indexOf("\"") + 1,
data.lastIndexOf("\""))
+ "&fun=new";
base_uri = redirect_uri.substring(0, redirect_uri.lastIndexOf("/"));
String[][] services = { { "wx2.qq.com", "webpush2.weixin.qq.com" },
{ "qq.com", "webpush.weixin.qq.com" },
{ "web1.wechat.com", "webpush1.wechat.com" },
{ "web2.wechat.com", "webpush2.wechat.com" },
{ "wechat.com", "webpush.wechat.com" },
{ "web1.wechatapp.com", "webpush1.wechatapp.com" } };
push_uri = base_uri;
for (String[] strings : services) {
if (base_uri.indexOf(strings[0]) > -1) {
push_uri = "https://" + strings[1] + "/cgi-bin/mmwebwx-bin";
break;
}
}
}
return code;
}
public static boolean login() throws Exception {
System.out.println("登录:" + redirect_uri);
String data = HttpUtils.httpGet(redirect_uri);
SAXBuilder saxBuilder = new SAXBuilder();
StringReader reader = new StringReader(data);
Document document = saxBuilder.build(reader);
Element root = document.getRootElement();
Element ret = root.getChild("ret");
if ("0".equals(ret.getText())) {
wxuin = root.getChild("wxuin").getText();
wxsid = root.getChild("wxsid").getText();
skey = root.getChild("skey").getText();
pass_ticket = root.getChild("pass_ticket").getText();
JSONObject j = new JSONObject();
j.put("Uin", wxuin);
j.put("Sid", wxsid);
j.put("Skey", skey);
j.put("DeviceID", deviceId);
baserequest = j.toString();
return true;
}
return false;
}
public static boolean webwxinit() throws Exception {
String json = "{\"BaseRequest\":" + baserequest + "}";
StringBuffer url = new StringBuffer();
url.append(base_uri);
url.append("/webwxinit?pass_ticket=" + pass_ticket);
url.append("&skey=" + skey);
url.append("&r=" + String.valueOf(System.currentTimeMillis()));
String data = HttpUtils.postJson(url.toString(), json);
System.out.println(data);
JSONObject result = JSON.parseObject(data);
my = result.getString("User");
if ("0".equals(JSON.parseObject(result.getString("BaseResponse"))
.getString("Ret")))
return true;
return false;
}
public static void webwxgetcontact() throws ClientProtocolException,
IOException {
String json = "{\"BaseRequest\":" + baserequest + "}";
StringBuffer url = new StringBuffer();
url.append(base_uri);
url.append("/webwxgetcontact?pass_ticket=" + pass_ticket);
url.append("&skey=" + skey);
url.append("&r=" + String.valueOf(System.currentTimeMillis()));
String data = HttpUtils.postJson(url.toString(), json);
String memberListString = JSONObject.parseObject(data).getString(
"MemberList");
memberList = JSON.parseArray(memberListString, Member.class);
// 倒序遍历,不然删除的时候出问题..
String[] specialUsers = { "newsapp", "fmessage", "filehelper", "weibo",
"qqmail", "tmessage", "qmessage", "qqsync", "floatbottle",
"lbsapp", "shakeapp", "medianote", "qqfriend", "readerapp",
"blogapp", "facebookapp", "masssendapp", "meishiapp",
"feedsapp", "voip", "blogappweixin", "weixin",
"brandsessionholder", "weixinreminder", "wxid_novlwrv3lqwv11",
"gh_22b87fa7cb3c", "officialaccounts", "notification_messages",
"wxitil", "userexperience_alarm" };
Iterator<Member> it = memberList.iterator();
while(it.hasNext()){
Member member = it.next();
if((member.getVerifyFlag() & 8) != 0)//# 公众号/服务号
it.remove();
else if(member.getUserName().indexOf("@@")>-1)//# 群聊
it.remove();
else if(member.getUserName().equals(JSONObject.parseObject(my).getString("UserName")))//自己
it.remove();
else{
for (String string : specialUsers) {
if(member.getUserName().equals(string)){
it.remove();
break;
}
}
}
}
}
public static Map<String,Object> createChatroom(List<Member> userNames) throws ClientProtocolException, IOException{
Map<String,Object> map = new HashMap<String,Object>();
StringBuffer memberList=new StringBuffer();
for (Member member : userNames) {
memberList.append("{\"UserName\":\""+ member.getUserName()+"\"},");
}
StringBuffer url = new StringBuffer();
url.append(base_uri);
url.append("/webwxcreatechatroom?pass_ticket=" + pass_ticket);
url.append("&skey=" + skey);
url.append("&r=" + String.valueOf(System.currentTimeMillis()));
String json = "{\"BaseRequest\":" + baserequest + "," + "\"MemberCount\":"+userNames.size() +",\"MemberList\":["+
memberList.toString().substring(0, memberList.toString().length()-1) +"],\"Topic\": \"\"}";
System.err.println(json);
String data = HttpUtils.postJson(url.toString(), json);
String chatRoomName = JSONObject.parseObject(data).getString("ChatRoomName");
System.out.println(data);
map.put("chatRoomName", chatRoomName);
List<Member> memlists = JSON.parseArray(JSONObject.parseObject(data).getString("MemberList"),Member.class);
List<Member> deletedList = new ArrayList<Member>();
List<Member> blockedList = new ArrayList<Member>();
if(memlists!=null && memlists.size()>0){
for (Member member : memlists) {
if(member.getMemberStatus()==4)// 被对方删除了
deletedList.add(member);
else if(member.getMemberStatus()==3)// 被加入黑名单
blockedList.add(member);
}
}
map.put("deletedList", deletedList);
map.put("blockedList", blockedList);
return map;
}
public static boolean deleteMember(String chatRoomName, List<Member> userNames) throws ClientProtocolException, IOException{
StringBuffer url = new StringBuffer();
url.append(base_uri);
url.append("/webwxupdatechatroom?fun=delmember&pass_ticket=" + pass_ticket);
StringBuffer members = new StringBuffer();
for (Member member : userNames) {
members.append(userNames+",");
}
String memberlist = members.toString().substring(0,members.toString().length()-1);
String json = "{\"BaseRequest\":" + baserequest + "," + "\"ChatRoomName\":" +chatRoomName +",\"DelMemberList\":" +memberlist +"}";
String data = HttpUtils.postJson(url.toString(), json);
JSONObject result = JSON.parseObject(data);
if ("0".equals(JSON.parseObject(result.getString("BaseResponse"))
.getString("Ret")))
return true;
return false;
}
public static Map<String,Object> addMember(String chatRoomName, List<Member> userNames) throws ClientProtocolException, IOException{
Map<String,Object> map = new HashMap<String,Object>();
StringBuffer url = new StringBuffer();
url.append(base_uri);
url.append("/webwxupdatechatroom?fun=addmember&pass_ticket=" + pass_ticket);
StringBuffer members = new StringBuffer();
for (Member member : userNames) {
members.append(member.getUserName()+",");
}
String memberlist = members.toString().substring(0,members.toString().length()-1);
String json = "{\"BaseRequest\":" + baserequest + "," + "\"ChatRoomName\":" +chatRoomName +",\"AddMemberList\":" +memberlist +"}";
String data = HttpUtils.postJson(url.toString(), json);
List<Member> memlists = JSON.parseArray(JSONObject.parseObject(data).getString("MemberList"),Member.class);
List<Member> deletedList = new ArrayList<Member>();
List<Member> blockedList = new ArrayList<Member>();
if(memlists!=null && memlists.size()>0){
for (Member member : memlists) {
if(member.getMemberStatus()==4)// 被对方删除了
deletedList.add(member);
else if(member.getMemberStatus()==3)// 被加入黑名单
blockedList.add(member);
}
}
map.put("deletedList", deletedList);
map.put("blockedList", blockedList);
return map;
}
}
接口通信用到的http post 发送josn数据方法
public static String postJson(String url, String parameters) throws ClientProtocolException, IOException {
DefaultHttpClient client = new DefaultHttpClient() ;
client.getParams()
.setParameter(
CoreProtocolPNames.USER_AGENT,
"Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.2) Gecko/20090803 Fedora/3.5.2-2.fc11 Firefox/3.5.2");
HttpPost httpPost = new HttpPost(url);
System.out.println(url);
if(parameters!=null){
StringEntity s = new StringEntity(parameters);
s.setContentEncoding("UTF-8");
s.setContentType("application/json");//发送json数据需要设置contentType
httpPost.setEntity(s);
}
HttpResponse response = client.execute(httpPost);
HttpEntity entity = response.getEntity();
return EntityUtils.toString(entity,"utf-8");
}
http get请求方法
public static String httpGet(String url) throws Exception {
DefaultHttpClient client = new DefaultHttpClient() ;
client.getParams()
.setParameter(
CoreProtocolPNames.USER_AGENT,
"Mozilla/5.0 (X11; U; Linux i686; zh-CN; rv:1.9.1.2) Gecko/20090803 Fedora/3.5.2-2.fc11 Firefox/3.5.2");
HttpGet get = new HttpGet(url);
HttpResponse response = client.execute(get);
HttpEntity entity = response.getEntity();
return EntityUtils.toString(entity,"utf-8");
}