⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 qqclient.java

📁 MilyQQ是一个使用控制台的QQ客户端,基于LumaQQ的核心JQL包开发,只有一个可执行的jar包(MilyQQ.jar),携带方便,由于是Java程序,因此理论上应该可以运行于所有平台,不过基于
💻 JAVA
📖 第 1 页 / 共 5 页
字号:
/*
* LumaQQ - Java QQ Client
*
* Copyright (C) 2004 luma <stubma@163.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.nio.channels.UnresolvedAddressException;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

/**
 * QQ的客户端类,这个类为用户提供一个方便的接口,比如发送消息之类的,只需要调用一个
 * 方法便可以完成了。
 *
 * LumaQQ网络层通过NIO实现,每个Client分配一个Selector,每个Selector可以监听多个
 * Channel,channel包装在IPort的实现类中,对于一个Client,有一个Main Port,这个
 * Main Port定义为连接QQ登陆服务器的那个Port。系统登陆之初之有一个Port即Main Port,
 * 根据需要可以开启其他Port
 *
 * @author luma
 */
public class QQClient implements IQQListener {
    /**
     * <pre>
     * 执行清理任务的Runnable类
     * </pre>
     *
     * @author luma
     */
    private class CleanRunnable implements Runnable {
        private PortGate _gate;
        private Porter _porter;

        public CleanRunnable(PortGate gate, Porter porter) {
            this._gate = gate;
            this._porter = porter;
        }

        public void run() {
            if(_gate != null)          // 关闭所有port
                _gate.disposeAll();
            if(_porter != null)	 // 关闭porter
	            _porter.shutdown();
        }
    }

    // Log对象
    // private static Log log = LogFactory.getLog(QQClient.class);
    // 登陆的服务器IP
    private String loginServer;
    // TCP方式登录时的服务器端口,UDP方式时无用处
    private int tcpLoginPort;
    // 包处理器路由器
    private ProcessorRouter router;
    // QQ监听器
    private List<IQQListener> qqListeners, qqListenersBackup;
    // QQ用户
    private QQUser user;
    // 包帮助类
    private PacketHelper packetHelper;
    // 当前是否正在登陆
    private boolean logging;
    // 当前是否在重定向登录
    private boolean loginRedirect;
    // Port管理类
    private PortGate gate;
    // 代理类型
    private int proxyType;
    // 代理服务器地址
    private InetSocketAddress proxyAddress;
    // 代理服务器验证用户名,null表示不需要验证
    private String proxyUsername;
    // 代理服务器验证密码
    private String proxyPassword;
    // 是否打开了机器人功能
    private boolean robotMode;
    // 机器人实例
    private IRobot robot;

    // 是否监听器有改变,这是为了处理同步问题而设置的,在实际fireQQEvent时,
    // 不使用qqListeners,而是用backup,这样在事件触发期间如果要修改
    // 监听器也是可以的,不然有可能造成并发修改异常。如果有更好的办法当
    // 然,目前只想出这种办法
    private boolean listenerChanged;

    /**
     * 单线程执行器
     */
    protected static final SingleExecutor executor = new SingleExecutor();
    /** 包事件触发过程 */
    protected Callable<Object> packetEventTrigger;
    /** 重发过程 */
    protected ResendTrigger<Object> resendTrigger;
    /** Keep Alive过程 */
    protected Runnable keepAliveTrigger;
    protected ScheduledFuture keepAliveFuture;

	/** 接收队列 */
	protected Queue<InPacket> receiveQueue;

	/** Selector线程 */
	protected Porter porter;

	/** 主port名称 */
	public static final String MAIN_PORT = "main";

	/** 包处理器数目 */
	protected static final int PROCESSOR_COUNT = 2;

    /**
     * 构造函数
     */
    public QQClient() {
        router = new ProcessorRouter(PROCESSOR_COUNT);
        qqListeners = new Vector<IQQListener>();
        qqListenersBackup = new Vector<IQQListener>();
        receiveQueue = new LinkedList<InPacket>();
        logging = false;
        loginRedirect = false;
        robotMode = false;
        listenerChanged = false;
        qqListeners.add(this);
        qqListenersBackup.add(this);
        packetHelper = new PacketHelper();
        packetEventTrigger = new PacketEventTrigger<Object>(this);
        keepAliveTrigger = new KeepAliveTrigger(this);
        resendTrigger = new ResendTrigger<Object>(this);

        router.installProcessor(new BasicFamilyProcessor(this));
        router.installProcessor(new _05FamilyProcessor(this));

        executor.increaseClient();
    }

    /**
     * 设置代理类型
     * @param type
     */
    public void setProxyType(String type) {
        if(type.equalsIgnoreCase("None"))
            proxyType = QQ.QQ_PROXY_NONE;
        else if(type.equalsIgnoreCase("Socks5"))
            proxyType = QQ.QQ_PROXY_SOCKS5;
        else if(type.equalsIgnoreCase("Http"))
            proxyType = QQ.QQ_PROXY_HTTP;
        else
            proxyType = QQ.QQ_PROXY_NONE;
    }

    /**
     * 设置代理服务器地址
     * @param proxyAddress
     */
    public void setProxy(InetSocketAddress proxyAddress) {
        this.proxyAddress = proxyAddress;
    }

    /**
     * 发送keep alive包
     */
    public void keepAlive() {
        if(user.isLoggedIn()) {
			KeepAlivePacket packet = new KeepAlivePacket(user);
			gate.send(MAIN_PORT, packet, false);
        }
    }

    /**
     * 共享我的地理位置
     */
    public void shareMyGeography() {
    	if(user.isLoggedIn()) {
    		PrivacyDataOpPacket packet = new PrivacyDataOpPacket(user);
    		packet.setSubCommand(QQ.QQ_SUB_CMD_SHARE_GEOGRAPHY);
    		packet.setOpCode(QQ.QQ_VALUE_SET);
    		gate.send(MAIN_PORT, packet, false);
    	}
    }

    /**
     * 不共享我的地理位置
     */
    public void unshareMyGeography() {
    	if(user.isLoggedIn()) {
    		PrivacyDataOpPacket packet = new PrivacyDataOpPacket(user);
    		packet.setSubCommand(QQ.QQ_SUB_CMD_SHARE_GEOGRAPHY);
    		packet.setOpCode(QQ.QQ_VALUE_UNSET);
    		gate.send(MAIN_PORT, packet, false);
    	}
    }

    /**
     * 设置仅能通过QQ号找到我
     */
    public void setSearchMeByQQOnly() {
    	if(user.isLoggedIn()) {
    		PrivacyDataOpPacket packet = new PrivacyDataOpPacket(user);
    		packet.setSubCommand(QQ.QQ_SUB_CMD_SEARCH_ME_BY_QQ_ONLY);
    		packet.setOpCode(QQ.QQ_VALUE_SET);
    		gate.send(MAIN_PORT, packet, false);
    	}
    }

    /**
     * 取消设置仅能通过QQ号找到我
     */
    public void unsetSearchMeByQQOnly() {
    	if(user.isLoggedIn()) {
    		PrivacyDataOpPacket packet = new PrivacyDataOpPacket(user);
    		packet.setSubCommand(QQ.QQ_SUB_CMD_SEARCH_ME_BY_QQ_ONLY);
    		packet.setOpCode(QQ.QQ_VALUE_UNSET);
    		gate.send(MAIN_PORT, packet, false);
    	}
    }

    /**
     * 请求天气预报
     *
     * @param ip
     */
    public char getWeather(byte[] ip) {
    	if(user.isLoggedIn()) {
    		WeatherOpPacket packet = new WeatherOpPacket(user);
    		packet.setIp(ip);
    		gate.send(MAIN_PORT, packet, false);
    		return packet.getSequence();
    	}
    	return 0;
    }

    /**
     * 请求自己这里的天气预报
     *
     * @param ip
     */
    public char getWeather() {
    	if(user.isLoggedIn()) {
    		WeatherOpPacket packet = new WeatherOpPacket(user);
    		packet.setIp(user.getIp());
    		gate.send(MAIN_PORT, packet, false);
    		return packet.getSequence();
    	}
    	return 0;
    }

    /**
     * 上传好友备注信息
     *
     * @param qqNum
     * 		好友的QQ号
     * @param remark
     * 		备注类
     */
    public void uploadFriendRemark(int qqNum, FriendRemark remark) {
    	if(user.isLoggedIn()) {
    		FriendDataOpPacket packet = new FriendDataOpPacket(user);
    		packet.setRemark(remark);
    		packet.setQQ(qqNum);
    		gate.send(MAIN_PORT, packet, true);
    	}
    }

    /**
     * 下载好友备注信息
     *
     * @param qqNum
     * 		好友的QQ号
     */
    public void downloadFriendRemark(int qqNum) {
    	if(user.isLoggedIn()) {
    		FriendDataOpPacket packet = new FriendDataOpPacket(user);
    		packet.setSubCommand(QQ.QQ_SUB_CMD_DOWNLOAD_FRIEND_REMARK);
    		packet.setQQ(qqNum);
    		gate.send(MAIN_PORT, packet, false);
    	}
    }

    /**
     * 批量下载好友备注
     *
     * @param page
     * 		页号
     */
    public void batchDownloadFriendRemark(int page) {
    	if(user.isLoggedIn()) {
    		FriendDataOpPacket packet = new FriendDataOpPacket(user);
    		packet.setSubCommand(QQ.QQ_SUB_CMD_BATCH_DOWNLOAD_FRIEND_REMARK);
    		packet.setPage(page);
    		gate.send(MAIN_PORT, packet, false);
    	}
    }

    /**
     * @param page
     * @param online
     * @param hasCam
     * @param provinceIndex
     * @param cityIndex
     * @param ageIndex
     * @param genderIndex
     * @return
     */
    public char searchUserAdvanced(
            int page,
            boolean online,
            boolean hasCam,
            int provinceIndex,
            int cityIndex,
            int ageIndex,
            int genderIndex) {
        if(user.isLoggedIn()) {
            AdvancedSearchUserPacket packet = new AdvancedSearchUserPacket(user);
            packet.setPage((char)page);
            packet.setSearchOnline(online);
            packet.setHasCam(hasCam);
            packet.setProvinceIndex((char)provinceIndex);
            packet.setCityIndex((char)cityIndex);
            packet.setAgeIndex((byte)ageIndex);
            packet.setGenderIndex((byte)genderIndex);
    		gate.send(MAIN_PORT, packet, false);
            return packet.getSequence();
        } else
            return 0;
    }

    /**
     * 搜索所有的在线用户
     * @param page 页号,从0开始
     * @return
     * 		包序号
     */
    public char searchUser(int page) {
    	if(user.isLoggedIn()) {
    		SearchUserPacket packet = new SearchUserPacket(user);
    		packet.setPage(page);
    		gate.send(MAIN_PORT, packet, false);
	        return packet.getSequence();
    	} else
    	    return 0;
    }

    /**
     * 自定义搜索用户
     * @param page 页号
     * @param qqNum 要搜索的QQ号字符串形式
     * @param nick 要搜索的昵称
     * @param email 要搜索的email
     * @param matchEntire 字符串是否完全匹配
     * @return
     * 		包序号
     */
    public char searchUser(int page, String qqStr, String nick, String email) {
    	if(user.isLoggedIn()) {
    		SearchUserPacket packet = new SearchUserPacket(user);
    		packet.setSearchType(QQ.QQ_SEARCH_CUSTOM);
    		packet.setPage(page);
    		packet.setQQStr(qqStr);
    		packet.setNick(nick);
    		packet.setEmail(email);
    		gate.send(MAIN_PORT, packet, false);
	        return packet.getSequence();
    	} else
    	    return 0;
    }

    /**
     * 添加一个好友
     * @param qqNum 要添加的人的QQ号
     * @return 发送出的包的序号
     */
    public char addFriend(int qqNum) {
    	if(user.isLoggedIn()) {
	        AddFriendExPacket packet = new AddFriendExPacket(user);
	        packet.setTo(qqNum);
    		gate.send(MAIN_PORT, packet, false);
	        return packet.getSequence();
    	}
    	return 0;
    }

    /**
     * 删除一个好友
     * @param qqNum 要删除的好友的QQ号
     * @return 如果包发送成功则返回包序号,否则返回0
     */
    public char deleteFriend(int qqNum) {
    	if(user.isLoggedIn()) {
	        DeleteFriendPacket packet = new DeleteFriendPacket(user);
	        packet.setTo(qqNum);
    		gate.send(MAIN_PORT, packet, true);
	        return packet.getSequence();
    	}
    	return 0;
    }

    /**
     * 把某人的好友列表中的自己删除
     * @param qqNum 我想把我自己删除的好友的QQ号
     */
    public void removeSelfFrom(int qqNum) {
    	if(user.isLoggedIn()) {
	        RemoveSelfPacket packet = new RemoveSelfPacket(user);
	        packet.setRemoveFrom(qqNum);
    		gate.send(MAIN_PORT, packet, true);
    	}
    }

    /**
     * 把好友从服务器端的好友列表中删除
     *
     * @param qqNum
     * 		要删除的好友QQ号
     */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -