3G QQ接口
无意间看到有人用传说中的3G QQ聊:http://pt.3g.qq.com/ 自己体验了一把,发现确实非常原始。都是通过刷新获得新的消息,所以感觉很容易模拟。于是用了两个晚上研究了一下。最后实现了qq登陆/收消息/发消息的功能。 运行下面的代码,会返回消息+笑脸表情。 基于jdk1.7.05。没有任何依赖。可以直接运行。 代码在下面:
import java.awt.BorderLayout;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class QQClient {
String username, password;
long lastActiveTime;
HttpClient client;
String sid;
static Logger log = Logger.getAnonymousLogger();
HashMap<String, LinkedList<String>> friends = new HashMap<>();
public QQClient(String username, String password) {
super();
this.username = username;
this.password = password;
this.client = new HttpClient();
System.setProperty("proxySet", "true");
System.setProperty("proxyHost", "116.226.239.208");
System.setProperty("proxyPort", "10086");
}
public static void main(String[] args) {
QQClient qq = new QQClient("sbnith@qq.com", "shibang416");
qq.login();
while (true) {
LinkedList<Msg> msgs = qq.getMsg();
for (Msg msg : msgs) {
System.out.println(msg);
String m = msg.content + "\\t" + msg.time + "\\t"
+ msg.sendername + "/:)";
System.out.println("\\n我\\n" + m);
System.out.println(qq.sendMsg(msg.senderid, m));
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void login() {
try {
String ret = client.http("http://pt.3g.qq.com/", null, null);
Pattern p = Pattern.compile("action=\\"(.*?)\\"");
Matcher m = p.matcher(ret);
String posturl = null;
if (m.find())
posturl = m.group(1).replace("&", "&");
HashMap<String, String> header = new HashMap<>();
header.put("Referer", "http://pt.3g.qq.com/");
ret = client
.http(posturl,
header,
"login_url=http%3A%2F%2Fpt.3g.qq.com%2Fs%3Faid%3DnLogin&sidtype=1&q_from=&loginTitle=%E6%89%8B%E6%9C%BA%E8%85%BE%E8%AE%AF%E7%BD%91&bid=0&qq="
+ this.username
+ "&pwd="
+ this.password
+ "&loginType=3&loginsubmit=%E7%99%BB%E5%BD%95");
if (ret.contains("验证码")) {
p = Pattern.compile("src=\\"(.*?)\\"");
m = p.matcher(ret);
String imageurl = null;
if (m.find())
imageurl = m.group(1);
StringBuffer body = new StringBuffer("verify="
+ deCaptcha(imageurl, posturl));
header.put("Referer", posturl);
String form = ret.substring(
ret.indexOf("action=\\"/handleLogin")).substring(8);
posturl = "http://pt.3g.qq.com"
+ form.substring(0, form.indexOf('"')).replace("&",
"&");
log.info(posturl);
p = Pattern.compile("name=\\"(.*?)\\" value=\\"(.*?)\\"/>");
m = p.matcher(form);
while (m.find()) {
body.append("&" + m.group(1) + "="
+ URLEncoder.encode(m.group(2), "UTF-8"));
}
ret = client.http(posturl, header, body.toString());
}
p = Pattern.compile("sid=(.*?)&");
m = p.matcher(ret);
if (m.find())
sid = m.group(1);
log.info("sid:" + sid);
if (sid != null) {
header.put("Referer",
"http://pt.3g.qq.com/s?aid=nLogin3gqq&auto=1&s_it=1&g_f=286&sid="
+ sid);
client.http(
"http://pt.3g.qq.com/s?aid=nLogin3gqqbysid&r=67656557",
header, "3gqqsid=" + sid + "&auto=1&loginType=1");
}
} catch (Throwable e) {
e.printStackTrace();
}
}
private boolean sendMsg(String to, String msg) {
log.fine(to + "\\n" + msg);
if (sid == null) {
log.warning("please login and get sid");
return false;
}
try {
msg = client.http("http://q16.3g.qq.com/g/s?sid=" + sid
+ "&aid=sendmsg&tfor=qq&referer=", null, "msg="
+ URLEncoder.encode(msg, "UTF-8") + "&u=" + to
+ "&saveURL=0&do=send&on=1&saveURL=0");
} catch (Throwable e) {
e.printStackTrace();
}
return msg.contains("消息发送成功");
}
private LinkedList<Msg> getMsg() {
LinkedList<Msg> ret = new LinkedList<>();
if (sid == null) {
log.warning("please login and get sid");
return ret;
}
String str = client.http("http://q32.3g.qq.com/g/s?sid=" + sid
+ "&3G_UIN=&saveURL=0&aid=nqqChat", null, null);
Pattern content = Pattern.compile(
"<div class=\\"main-module bm-blue\\">(.*?)</div>",
Pattern.DOTALL);
Pattern sendername = Pattern.compile("<title>与(.*?)聊天");
Pattern senderid = Pattern.compile("name=\\"u\\" value=\\"(.*?)\\"");
Pattern time = Pattern.compile(" (.*?)</p>", Pattern.DOTALL);
Pattern msgcontent = Pattern.compile("<p>(.*?)</p>", Pattern.DOTALL);
Matcher m = null;
boolean hasMore = true;
while (hasMore) {
String name = null, id = null;
m = sendername.matcher(str);
if (m.find())
name = m.group(1);
m = senderid.matcher(str);
if (m.find())
id = m.group(1);
int index = str.indexOf("<div class=\\"bg-blue line-h\\">");
if (index == -1)
return ret;
str = str.substring(0, index);
m = content.matcher(str);
hasMore = false;
while (m.find()) {
hasMore = true;
String temp = m.group(1);
Matcher m1 = time.matcher(temp);
String t = null;
if (m1.find())
t = m1.group(1);
m1 = msgcontent.matcher(temp);
String c = null;
if (m1.find())
c = m1.group(1);
ret.add(new Msg(id, name, t, c));
}
if (hasMore)
str = client.http("http://q32.3g.qq.com/g/s?sid=" + sid
+ "&3G_UIN=&saveURL=0&aid=nqqChat", null, null);
}
return ret;
}
private String deCaptcha(String url, String referer) {
try {
URLConnection con = new URL(url).openConnection();
con.addRequestProperty("Referer", referer);
InputStream in = con.getInputStream();
ByteArrayOutputStream bo = new ByteArrayOutputStream();
int b = -1;
while ((b = in.read()) != -1)
bo.write(b);
in.close();
JFrame f = new JFrame("输入验证码");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setAlwaysOnTop(true);
f.setBounds(200, 200, 100, 100);
f.add(new JLabel(new ImageIcon(bo.toByteArray())),
BorderLayout.CENTER);
bo.close();
f.setVisible(true);
System.out.println("输入看到的验证码");
Scanner scan = new Scanner(System.in);
String cap = scan.nextLine().trim();
while (cap.length() != 4) {
System.out.println("验证码是4位的");
cap = scan.nextLine().trim();
}
scan.close();
f.dispose();
return cap;
} catch (Exception e) {
e.printStackTrace();
System.exit(0);// 如果识别验证码出错。则从来
}
return null;
}
class Msg {
String senderid;
String sendername;
String time;
String content;
public Msg(String senderid, String sendername, String time,
String content) {
super();
this.senderid = senderid;
this.sendername = sendername;
this.time = time;
this.content = content;
}
@Override
public String toString() {
return sendername + "(" + senderid + ")\\t" + time + "\\n" + content;
}
}
class HttpClient {
HashMap<String, String> cookies = new HashMap<String, String>();
private String http(String url, HashMap<String, String> header,
String body) {
try {
log.fine(url);
URLConnection con = new URL(url).openConnection();
if (cookies.size() > 0) {
StringBuffer cookieStr = new StringBuffer();
for (String key : cookies.keySet()) {
cookieStr.append("; " + key + ":" + cookies.get(key));
}
con.addRequestProperty("Cookie", cookieStr.substring(2));
}
con.addRequestProperty(
"User-Agent",
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.29 Safari/525.13");
if (header != null)
for (String key : header.keySet()) {
con.addRequestProperty(key, header.get(key));
}
con.setConnectTimeout(10000);
con.setReadTimeout(30000);
if (body != null) {
con.setDoOutput(true);
byte[] b = body.getBytes("UTF-8");
OutputStream out = con.getOutputStream();
out.write(b);
out.flush();
out.close();
}
InputStream in = con.getInputStream();
ByteArrayOutputStream ba = new ByteArrayOutputStream();
int b = -1;
while ((b = in.read()) != -1) {
ba.write(b);
}
String ret = new String(ba.toByteArray(), "UTF-8");
ba.close();
in.close();
// cookie
if (con.getHeaderField("Set-Cookie") != null) {
String[] setCookie = con.getHeaderField("Set-Cookie")
.split("\\n");
for (String line : setCookie) {
String[] kv = line.substring(0, line.indexOf(';'))
.split("=");
cookies.put(kv[0], kv.length > 1 ? kv[1] : "");
}
}
return ret;
} catch (Throwable e) {
e.printStackTrace();
log.warning(e.getLocalizedMessage());
}
return null;
}
}
}