当前位置: 代码迷 >> Web前端 >> 公布WebQQ Client API库1.0.0正式版
  详细解决方案

公布WebQQ Client API库1.0.0正式版

热度:297   发布时间:2013-01-26 13:47:01.0
发布WebQQ Client API库1.0.0正式版

最近在研发一个JIRA的插件,具体场景如下:

当测试人员提交一个问题时,需要通过QQ通知到开发人员,并且当问题属于SIT测试BUG时,需要自动的打开SVN上对应主干源代码的写权限。当开发人员修复并关闭问题时,需要自动的关闭SVN上对应主干源代码的写权限。

基本实现思路如下:

  • 开发一个JIRA插件,基于ActiveMQ客户端,并监听JIRA的内部事件,如果是触发提交问题的事件,就将该事件发布到MQ主题中;
  • 开发一个服务器应用程序,订阅MQ上由JIRA插件发布的事件消息,并进行对应的处理,例如发送QQ消息或是修改SVN权限等。

实际上实现JIRA插件这一端的程序并不困难,关键是查阅JIRA SDK相关手册,基本上没什么难度,下一篇文章,我们就来看看如何实现这个JIRA插件。这篇文章主要是关注如何实现与QQ的通讯,着实花费了不少心思。通过Google,发现目前网上大部分是基于WebQQ实现的,然后下载了一个什么iQQ的实现代码,没什么设计思想,基本上就是代码的堆叠,不过可读性还算好,基本能看懂,于是乎就有了想搞一个WebQQ客户端API库的冲动,方便今后的使用和扩展。经过几天的整理和重构终于把WebQQ核心部分抽出来并形成一个API库,以下为API接口定义:

/**
 * WebQQ客户端接口
 * @author Administrator
 * @version $Id: WebQQClient.java, v 0.1 2013-1-21 下午2:58:32 Administrator Exp $
 */
public interface WebQQClient {
    /**
     * QQ登录
     * <blockquote>
     * QQ完成登录需要经过三次服务调用:
     * <ol>
     * <li>检查QQ账户,是否需要验证码输入;
     * <li>正式登录;
     * <li>渠道登录;
     * </ol>
     * </blockquote>
     * @param member 需要提供QQ号码和登录密码
     * @return true 登录成功 false 登录失败
     */
    boolean login(Member member);

    /**
     * 加载QQ好友详情
     * <blockquote>
     * 前置条件: 必须已经成功登录
     * </blockquote>
     * @param account 需要加载的好友QQ号码(QQ系统内部还有一个uin标识)
     * @return QQ会员详情
     */
    Member loadFriend(String account);

    /**
     * 加载QQ好友的个性化签名
     * <blockquote>
     * 前置条件: 必须已经成功登录
     * </blockquote>
     * @param account 需要加载的好友QQ号码(QQ系统内部还有一个uin标识)
     * @return
     */
    Member loadFriendSignature(String account);

    /**
     * 加载QQ好友的等级
     * <blockquote>
     * 前置条件: 必须已经成功登录
     * </blockquote>
     * @param account 需要加载的好友QQ号码(QQ系统内部还有一个uin标识)
     * @return
     */
    Member loadFriendLevel(String account);

    /**
     * 改变当前已登录QQ会员的状态
     * <blockquote>
     * 前置条件: 必须已经成功登录
     * </blockquote>
     * @param newStatus 当前的新状态
     */
    void changeStatus(Status newStatus);

    /**
     * 向指定的QQ好友发送消息
     * <blockquote>
     * 前置条件: 
     * <ol>
     * <li>必须已经成功登录;
     * <li>已经成功加载了好友列表,因为发送QQ消息需要好友的uin字段(一个内部的标识,每次登录该字段值都不一样),而非QQ号码;
     * <li>已经成功启动QQ心跳检测任务,否则无法发送QQ消息;
     * </ol>
     * </blockquote>
     * @param account 发送消息的好友的QQ号码
     * @param message 发送的消息内容
     * @return true 发送成功 false 发送失败
     */
    boolean sendMessageToFriend(String account, String message);

    /**
     * 向指定的QQ群发送消息
     * <blockquote>
     * 前置条件: 
     * <ol>
     * <li>必须已经成功登录;
     * <li>已经成功加载了好友群列表,因为发送QQ消息需要好友群的uin字段(一个内部的标识,每次登录该字段值都不一样),而非群号码;
     * <li>已经成功启动QQ心跳检测任务,否则无法发送QQ消息;
     * </ol>
     * </blockquote>
     * @param account 发送消息的QQ群号
     * @param message 发送的消息内容
     * @return
     */
    boolean sendMessageToGroup(String account, String message);

    /**
     * QQ登出
     * <blockquote>
     * 前置条件: 必须已经成功登录
     * </blockquote>
     * @return true 登出成功 false 登出失败
     */
    boolean logout();

    /**
     * 查询好友列表
     * <blockquote>
     * 前置条件: 必须已经成功登录
     * </blockquote>
     * @return 好友列表
     */
    List<Member> findFriends();

    /**
     * 查询在线的好友列表
     * <blockquote>
     * 前置条件: 必须已经成功登录
     * </blockquote>
     * @return 在线好友列表
     */
    List<Member> findOnlineFriends();

    /**
     * 查询QQ群列表
     * <blockquote>
     * 前置条件: 必须已经成功登录
     * </blockquote>
     * @return
     */
    List<Group> findGroups();
}
该API库的主要功能有:

  • QQ登录;
  • 查询QQ好友列表;
  • 查询QQ群列表;
  • 查询在线QQ好友列表;
  • 加载QQ好友详情;
  • 加载QQ好友个性化签名;
  • 加载QQ好友等级;
  • 更变当前登录状态;
  • 向QQ好友发送消息;
  • 向好友QQ群发送群消息;
  • QQ心跳检查线程任务,用于保持在线状态;
  • 提供QQ事件监听器接口,用于扩展QQ消息和QQ群消息的接收处理;
  • QQ登出;

当然因为是一个API库,所以和UI相关的功能基本上都没有实现,有待感兴趣的同学继续完善。

该API库的依赖库如下:

  • Apache Commons Lang
  • Apache Commons Collections
  • Apache Commons Beanutils
  • Apache Commons Logging
  • Spring Framework
  • Apache Log4j
  • JSON
  • Storevm Commons

如果是使用maven管理依赖的话,那就简单了,可以看我项目源代码中提供的pom.xml文件。

以下介绍使用的方法,由于该API是依赖Spring框架的,所以要做的就是配置几个Bean,以下假设您使用Maven工具以及Spring进行开发:

1. 在pom.xml中配置如下依赖:

<dependency>
    <groupId>org.storevm</groupId>
    <artifactId>webqq-client</artifactId>
    <version>1.0.0</version>
</dependency>
当然,这个Jar在Maven中央库是没有的,所以需要在pom.xml中添加上我的Maven私有库地址:

<repositories>
    <repository>
        <id>StoreVM-repo</id>
	<name>StoreVM Maven Repository</name>
	<url>http://mvn.storevm.org:8081/mvn/content/groups/public</url>
	<releases>
	    <enabled>true</enabled>
	</releases>
	<snapshots>
	    <enabled>true</enabled>
	</snapshots>
    </repository>
</repositories>
OK,最后执行

mvn eclipse:eclipse

回到Eclipse上,刷新一下自己的项目,就能看到新加入的库了。

2. 接着在您项目的Spring配置文件中定义如下的 Bean:

<bean id="webQQClientContainer" class="org.storevm.im.WebQQClientContainer">
    <property name="account" value="您的QQ号码"/>
    <property name="password" value="您的QQ登录密码"/>
</bean>
WebQQClientContainer类实现了InitializingBean和DisposableBean接口,所以在Bean加载的时候会执行QQ登录相关的操作,而在Bean卸载的时候会执行了QQ登出的操作,部分源代码如下:

    /** 
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        Member member = new Member();
        member.getAccount().setAccount(qqNumber);
        member.getAccount().setPassword(qqPassword);
        member.setStatus(Status.ONLINE);
        //登录WebQQ,登录之后就可以发送消息了
        boolean success = webQQClient.login(member);
        if (success) {
            //如果登录成功,则加载在线好友列表和QQ群列表
            List<Member> olineFriends = webQQClient.findOnlineFriends();
            List<Group> groups = webQQClient.findGroups();

            //启动心跳检测任务
            heartbeatTaskExecutor.startup();
            LogUtils.info(LOGGER, "启动WebQQ客户端容器完成. 在线好友数=[{0}], QQ群数=[{1}]", olineFriends.size(),
                groups.size());
        } else {
            LogUtils.warn(LOGGER, "启动WebQQ客户端失败, account={0}", qqNumber);
        }
    }

    /** 
     * @see org.springframework.beans.factory.DisposableBean#destroy()
     */
    @Override
    public void destroy() throws Exception {
        //当Bean销毁时,关闭WebQQ客户端管理容器
        heartbeatTaskExecutor.shutdown();
        boolean success = webQQClient.logout();
        if (success) {
            LogUtils.warn(LOGGER, "关闭WebQQ客户端完成, account={0}", qqNumber);
        } else {
            LogUtils.warn(LOGGER, "关闭WebQQ客户端失败, account={0}", qqNumber);
        }
    }

经过以上的配置,您就可以在程序运行的任何时刻调用WebQQClient接口发送QQ消息了。这里需要注意的是,Jar包中已经定义了相关bean的配置,无需在你自己的项目中重复定义Bean,不过需要通过设置classpath*:applicationContext.xml的方式来加载Jar包中的Spring配置文件。

如果您的应用程序是Web程序,我也写了一个ServletContextListener接口用于在Web容器启动时初始化WebQQ客户端:

1. 在web.xml中添加如下配置:

<context-param>
    <param-name>account</param-name>
    <param-value>您的QQ号码</param-value>
</context-param>
<context-param>
    <param-name>password</param-name>
    <param-value>您的QQ登录密码</param-value>
</context-param>
<listener>
    <listener-class>org.storevm.im.WebQQClinetListener</listener-class>
</listener>
通过上述配置,当web容器启动时会执行QQ登录操作,然后您就可以在应用程序的运行过程中发送QQ消息,当然在容器关闭时会执行QQ登出的操作。
关于QQ消息的接收,我采用事件监听器的方式进行实现,您需要做的就是实现WebQQEventListener接口以及InitializingBean和DisposableBean接口,以下为一个简单的事件监听器实现代码:

/**
 * 接收QQ消息的监听器实现
 * @author Administrator
 * @version $Id: ReceiveFriendMessageListener.java, v 0.1 2013-1-24 下午3:28:15 Administrator Exp $
 */
public class ReceiveMessageListener implements WebQQEventListener, InitializingBean, DisposableBean {
    /* logger */
    private static final Logger LOGGER = Logger.getLogger(ReceiveMessageListener.class);

    /* 事件发布器 */
    private WebQQEventPublisher eventPublisher;

    /**
     * 构造函数
     */
    public ReceiveMessageListener() {
    }

    /** 
     * @see org.springframework.beans.factory.DisposableBean#destroy()
     */
    @Override
    public void destroy() throws Exception {
        eventPublisher.unregister(this); //注销
    }

    /** 
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        eventPublisher.register(this); //注册监听器
    }

    /** 
     * @see org.storevm.im.core.event.WebQQEventListener#onWebQQEvent(org.storevm.im.core.event.WebQQEvent)
     */
    @Override
    public void onWebQQEvent(WebQQEvent event) {
        LogUtils.info(LOGGER, "接收到QQ事件, event.code={0}, body={1}", event.getEventCode(),
            event.getBody());
    }

    /**
     * Setter method for property <tt>eventPublisher</tt>.
     * 
     * @param eventPublisher value to be assigned to property eventPublisher
     */
    public void setEventPublisher(WebQQEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

}
以上代码的关键点是注册监听器和onWebQQEvent方法的实现。你也可以注册多个监听器,用于监听多个事件。更多的单元测试请下载源代码查看。

以下提供SVN源代码地址:

http://svn.storevm.org/svn/webqqclient/trunk

用户名和密码均为:reader

项目使用Maven进行管理,关于maven的使用请自行参考网上其他文章。最后希望对此有兴趣的同学可以相互交流经验技术。

  相关解决方案