📄 qqclient.java
字号:
private synchronized void setListenerChanged(boolean b) {
listenerChanged = b;
}
/**
* 得到listener changed标志
*
* @return
* true表示有新的listener添加了进来
*/
private synchronized boolean isListenerChanged() {
return listenerChanged;
}
/**
* 检查监听器是否已经改变,如果是则更新监听器
*/
private synchronized void checkListenerChange() {
if(isListenerChanged()) {
qqListenersBackup.clear();
qqListenersBackup.addAll(qqListeners);
setListenerChanged(false);
}
}
/**
* 触发QQ事件
* @param e QQEvent
*/
public void fireQQEvent(QQEvent e) {
checkListenerChange();
int size = qqListenersBackup.size();
for(int i = 0; i < size; i++)
qqListenersBackup.get(i).qqEvent(e);
}
/**
* 登陆,使用缺省端口
*
* @throws Exception
* 如果登录出错
*/
public void login() throws Exception {
if (user != null && !logging && loginServer != null) {
// 登陆
if(user.isUdp())
login(loginServer, QQ.QQ_PORT_UDP);
else
login(loginServer, tcpLoginPort);
}
}
/**
* 用指定的服务器和端口登录
*
* @param server 登陆服务器
* @param p 登陆端口
* @throws Exception
* 如果登录出错
*/
protected void login(String server, int p) throws Exception {
// 检查是否设置了用户名和密码
if (user != null) {
// 停止以前的线程,如果有的话
logout();
// 创建Selector
porter = new Porter();
// 创建端口管理器
gate = new PortGate(this, porter);
// 设置状态为正在登陆
logging = true;
// 检查是什么登陆模式,udp还是tcp
try {
IPort port = null;
if (user.isUdp()) {
//log.debug("使用UDP服务器: " + server + " 端口:" + p);
// 创建端口类
if(proxyType == QQ.QQ_PROXY_SOCKS5) {
//log.debug("使用Socks 5代理 " + proxyAddress);
port = installUDPSocks5Port(MAIN_PORT, proxyAddress, new InetSocketAddress(server, p), proxyUsername, proxyPassword, false);
} else
port = installUDPPort(MAIN_PORT, new InetSocketAddress(server, p), false);
} else {
//log.debug("使用TCP服务器: " + server + " 端口: " + p);
// 创建发送类和接收类
if(proxyType == QQ.QQ_PROXY_SOCKS5) {
//log.debug("使用Socks 5代理 " + proxyAddress);
port = installTCPSocks5Port(MAIN_PORT, proxyAddress, new InetSocketAddress(server, p), proxyUsername, proxyPassword, false);
} else if(proxyType == QQ.QQ_PROXY_HTTP) {
//log.debug("使用HTTP代理 " + proxyAddress);
port = installTCPHttpPort(MAIN_PORT, proxyAddress, new InetSocketAddress(server, p), proxyUsername, proxyPassword, false);
} else
port = installTCPPort(MAIN_PORT, new InetSocketAddress(server, p), false);
}
// 如果登录令牌还是空,就构造请求登录令牌包,如果不是,直接登录
OutPacket packet = null;
if(user.getLoginToken() == null)
packet = new RequestLoginTokenPacket(user);
else
packet = new LoginPacket(user);
// 启动端口
porter.start();
port.start();
// 发送请求包
gate.send(MAIN_PORT, packet, false);
} catch (UnknownHostException e) {
throw new Exception("服务器名称无法解析");
} catch (UnresolvedAddressException e) {
throw new Exception("无法解析服务器地址");
} catch (Exception e) {
throw e;
}
}
}
/**
* 退出QQ会话
*/
public void logout() {
// 得到当前登录状态
boolean isLoggedIn = user.isLoggedIn();
// 设置用户状态
if(!loginRedirect)
user.setLoginToken(null);
user.setLoggedIn(false);
// 设置正在登录标志为false
logging = false;
loginRedirect = false;
if(gate != null) {
if(gate.hasPort(MAIN_PORT)) {
// 清空发送队列
gate.clearSendQueue(MAIN_PORT);
// 如果已经登录了,则发送logout包
if(isLoggedIn) {
LogoutPacket packet = new LogoutPacket(user);
//发4次
for(int i=0;i<4;i++)
gate.send(MAIN_PORT, packet, false);
}
}
// 清空包监视缓冲
gate.clearMonitorCache();
}
// 执行清理工作
if(keepAliveFuture != null) {
keepAliveFuture.cancel(false);
keepAliveFuture = null;
}
resendTrigger.clear();
clean(gate, porter);
gate = null;
porter = null;
}
/**
* 释放这个QQClient,如果不继续使用QQClient,则必须释放此客户端以便
* SingleExecutor释放资源
*/
public void release() {
executor.decreaseClient();
}
/**
* 处理Keep Alive成功信息,对Keep Alive Success,QQClient不作什么
* 进一步的分析,只是顺便刷新一下好友状态
*
* @param e
*/
private void processKeepAliveSuccess(QQEvent e) {
GetOnlineOpPacket packet = new GetOnlineOpPacket(user);
sendPacket(packet);
}
/**
* 处理登陆成功事件
*/
private void processLoginSuccess() {
if(!user.isLoggedIn()) {
user.setLoggedIn(true);
this.logging = false;
// 请求密钥
//requestKey(QQ.QQ_REQUEST_UNKNOWN_KEY);
//requestKey(QQ.QQ_REQUEST_FILE_AGENT_KEY);
// 得到我的信息
GetUserInfoPacket guiPacket = new GetUserInfoPacket(user);
sendPacket(guiPacket);
// 手动更改我的在线状态,虽然服务器可能也会给我发送这样的信息
ChangeStatusPacket csPacket = new ChangeStatusPacket(user);
csPacket.setShowFakeCam(user.isShowFakeCam());
sendPacket(csPacket);
// 调度keep alive动作
keepAliveFuture = executor.scheduleWithFixedDelay(keepAliveTrigger, QQ.QQ_INTERVAL_KEEP_ALIVE, QQ.QQ_INTERVAL_KEEP_ALIVE, TimeUnit.MILLISECONDS);
}
}
/**
* 处理普通消息事件,只是发一个确认而已
*
* @param e
*/
private void processReceiveIM(QQEvent e) {
// 得到包
ReceiveIMPacket packet = (ReceiveIMPacket)e.getSource();
// 生成确认包
ReceiveIMReplyPacket reply = new ReceiveIMReplyPacket(packet.reply, user);
reply.setSequence(packet.getSequence());
gate.send(MAIN_PORT, reply, false);
}
/* (non-Javadoc)
* @see edu.tsinghua.lumaqq.QQListener#qqEvent(edu.tsinghua.lumaqq.QQEvent)
*/
public void qqEvent(QQEvent e) {
switch(e.type) {
case QQEvent.QQ_GET_LOGIN_TOKEN_SUCCESS:
processGetLoginTokenSuccess();
break;
case QQEvent.QQ_GET_LOGIN_TOKEN_FAIL:
//processGetLoginTokenFail();
break;
case QQEvent.QQ_LOGIN_SUCCESS:
processLoginSuccess();
break;
case QQEvent.QQ_KEEP_ALIVE_SUCCESS:
processKeepAliveSuccess(e);
break;
case QQEvent.QQ_RECEIVE_NORMAL_IM:
processNormalIM(e);
break;
case QQEvent.QQ_KICKED_OUT_BY_SYSTEM:
case QQEvent.QQ_RECEIVE_CLUSTER_IM:
case QQEvent.QQ_RECEIVE_TEMP_CLUSTER_IM:
case QQEvent.QQ_RECEIVE_UNKNOWN_CLUSTER_IM:
case QQEvent.QQ_REQUEST_SEND_FILE:
case QQEvent.QQ_ACCEPT_SEND_FILE:
case QQEvent.QQ_REJECT_SEND_FILE:
case QQEvent.QQ_CANCEL_SEND_FILE:
case QQEvent.QQ_NOTIFY_FILE_TRANSFER_ARGS:
case QQEvent.QQ_REQUEST_ME_CONNECT:
case QQEvent.QQ_UNKNOWN_TYPE_IM:
case QQEvent.QQ_REQUEST_JOIN_CLUSTER:
case QQEvent.QQ_REQUEST_JOIN_CLUSTER_APPROVED:
case QQEvent.QQ_REQUEST_JOIN_CLUSTER_REJECTED:
case QQEvent.QQ_I_AM_ADDED_TO_CLUSTER:
case QQEvent.QQ_I_AM_REMOVED_FROM_CLUSTER:
case QQEvent.QQ_RECEIVE_DUPLICATED_IM:
case QQEvent.QQ_ADMIN_ENTITLED:
case QQEvent.QQ_ADMIN_WITHDRAWED:
case QQEvent.QQ_FRIEND_SIGNATURE_CHANGED:
case QQEvent.QQ_RECEIVE_SMS:
case QQEvent.QQ_RECEIVE_TEMP_SESSION_IM:
processReceiveIM(e);
break;
case QQEvent.QQ_OPERATION_TIMEOUT:
switch(e.operation) {
case QQ.QQ_CMD_LOGIN:
case QQ.QQ_CMD_REQUEST_LOGIN_TOKEN:
processLoginTimeout();
break;
}
break;
case QQEvent.QQ_CONNECTION_LOST:
processConnectionLost();
break;
}
}
/**
* 处理请求登录令牌成功事件
*/
private void processGetLoginTokenSuccess() {
LoginPacket packet = new LoginPacket(user);
gate.send(MAIN_PORT, packet, false);
}
/**
* 处理普通消息事件,核心层的处理是检查机器人设置,如果机器人打开了,则自动返回消息
*
* @param e
* QQEvent
*/
private void processNormalIM(QQEvent e) {
// 先返回确认
processReceiveIM(e);
// 得到消息包
ReceiveIMPacket packet = (ReceiveIMPacket)e.getSource();
doRobot(packet);
}
/**
* 检查机器人设置,如果机器人打开了,发送一条机器人消息
*
* @param packet
* 接受消息包
* @param receiver
* 接收者
*/
private void doRobot(ReceiveIMPacket packet) {
// 检查机器人设置
if(isRobotMode()) {
if(robot != null) {
String reply = robot.getReply(packet);
if(reply != null)
sendIM(packet.header.sender, reply);
}
}
}
/**
* 处理连接丢失事件
*/
private void processConnectionLost() {
user.setLoggedIn(false);
logout();
}
/**
* 处理登陆超时事件
*/
private void processLoginTimeout() {
logging = false;
logout();
}
/**
* 发送临时会话消息
*
* @param receiver
* @param message
*/
public void sendTempSessionIM(int receiver, String message, String myNick) {
sendTempSessionIM(receiver,
message,
myNick,
"宋体",
false,
false,
false,
9,
0,
0,
0);
}
/**
* 发送临时会话消息
*
* @param receiver
* @param message
* @param fontName
* @param bold
* @param italic
* @param underline
* @param size
* @param red
* @param green
* @param blue
*/
public void sendTempSessionIM(int receiver,
String message,
String myNick,
String fontName,
boolean bold,
boolean italic,
boolean underline,
int size,
int red,
int green,
int blue) {
if(user.isLoggedIn()) {
TempSessionOpPacket packet = new TempSessionOpPacket(user);
packet.setSubCommand(QQ.QQ_SUB_CMD_SEND_TEMP_SESSION_IM);
packet.setReceiver(receiver);
packet.setMessage(message);
packet.setFontName(fontName);
packet.setFontSize((byte)(size & 0xFF));
packet.setRed((byte)(red & 0xFF));
packet.setGreen((byte)(green & 0xFF));
packet.setBlue((byte)(blue & 0xFF));
packet.setBold(bold);
packet.setItalic(italic);
packet.setUnderline(underline);
gate.send(MAIN_PORT, packet, true);
}
}
/**
* 发送一个消息,用给定的字体
* @param receiver 接受者QQ号
* @param message 消息
* @param fontName 字体
*/
public void sendIM(int receiver, String message) {
this.sendIM(receiver, message, QQ.QQ_IM_NORMAL_REPLY);
}
/**
* 发送一条消息,可以指定回复类型
* @param receiver 接收者QQ号
* @param message 消息
* @param fontName 字体名
* @param replyType 回复方式,正常回复还是自动回复
*/
public void sendIM(int receiver, String message, byte replyType) {
this.sendIM(receiver, message, "宋体", false, false, false, 9, 0, 0, 0, replyType);
}
/**
* 发送一个消息,指定字体和字体颜色
*
* @param receiver 接受者QQ号
* @param message 消息
* @param fontName 字体
* @param bold 是否粗体
* @param italic 是否斜体
* @param underline 是否下划线
* @param size 字体大小,最大是31
* @param red 颜色Red
* @param green 颜色Green
* @param blue 颜色Blue
* @param replyType 回复类型,自动回复还是正常回复
*/
public void sendIM(
int receiver,
String message,
String fontName,
boolean bold,
boolean italic,
boolean underline,
int size,
i
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -