iQQ 学习笔记声明
本文仅供学习研究使用,不得用于任何非法及侵权用途。
转贴请注明原发位置:
http://xuekaiyuan.com/forum.php?mod=viewthread&tid=5
讨论请加QQ群:306320259
iQQ 学习笔记2说明 :借助新浪微博输入验证码、远程控制退出
在第1个案例中实现了iQQ的登录、验证码和收消息,其中有两处需要人工参与,第一处是需要打开验证码图片,然后输入验证码,第二处是退出程序需要强制退出。验证码暂时还不能自动识别,不过可以改进交互方式,本例中将借助新浪微博实现显示验证码图片和输入验证码。退出程序改成收到QQ消息后按消息内容操作。
iQQ 学习笔记2程序 :借助新浪微博输入验证码、远程控制退出
这是主程序,其中的username常量的值应替换为QQ号,password常量的值应替换为该QQ号对应的QQ密码。
package test_2; import java.util.logging.Logger; import iqq.im.QQClient; import iqq.im.QQException; import iqq.im.QQNotifyListener; import iqq.im.WebQQClient; import iqq.im.actor.ThreadActorDispatcher; import iqq.im.bean.QQMsg; import iqq.im.bean.QQStatus; import iqq.im.bean.content.ContentItem; import iqq.im.bean.content.TextItem; import iqq.im.event.QQActionEvent; import iqq.im.event.QQActionFuture; import iqq.im.event.QQNotifyEvent; import iqq.im.event.QQNotifyEventArgs; import iqq.im.event.QQActionEvent.Type; public class Test_2 { private static final String username = "**********"; private static final String password = "**********"; private static final Logger logger = java.util.logging.Logger.getLogger(""); private static QQClient client; public static void main(String[] args) { client = new WebQQClient(username, password, new QQNotifyListener() { @Override public void onNotifyEvent(QQNotifyEvent event) { if (event.getType() == QQNotifyEvent.Type.CHAT_MSG) { if (event.getTarget() instanceof QQMsg) { QQMsg msg = (QQMsg) event.getTarget(); for (ContentItem contentItem : msg.getContentList()) { if (contentItem instanceof TextItem) { TextItem textItem = (TextItem) contentItem; logger.info(textItem.getContent()); if (textItem.getContent().startsWith("logout ")) { client.logout(null); } } } } } else if (event.getType() == QQNotifyEvent.Type.CAPACHA_VERIFY) { if (event.getTarget() instanceof QQNotifyEventArgs.ImageVerify) { QQNotifyEventArgs.ImageVerify imageVerify = (QQNotifyEventArgs.ImageVerify) event.getTarget(); String code = ImageVerifyQuestioners.ImageVeiryQuestion(imageVerify); client.submitVerify(code, event); } else { logger.info(event.getTarget().getClass().getName()); } } else { logger.info("TODO QQNotifyEvent: " + event.getType() + ", " + event.getTarget()); } } }, new ThreadActorDispatcher()); QQActionFuture future = client.login(QQStatus.ONLINE, null); try { QQActionEvent event = future.waitFinalEvent(); if (event.getType() == Type.EVT_OK) { client.beginPollMsg(); } } catch (QQException e) { e.printStackTrace(); } } }
为了便于实现多种图片验证码的输入方式,设计一个图片验证码接口。
package test_2; import iqq.im.event.QQNotifyEventArgs.ImageVerify; public abstract class ImageVerifyQuestioner { private ImageVerify imageVerify; private String Code; public ImageVerifyQuestioner(ImageVerify imageVerify) { this.imageVerify = imageVerify; this.Code = ""; } protected final ImageVerify getImageVerify() { return imageVerify; } public final String getCode() { return Code; } protected final void setCode(String code) { Code = code; } public abstract void loop (); }
通过一个调度程序管理多种图片验证码的输入方式,当一种输入方式获取验证码时,其他输入方式退出。
package test_2; import java.util.ArrayList; import java.util.List; import iqq.im.event.QQNotifyEventArgs.ImageVerify; public class ImageVerifyQuestioners { public static String ImageVeiryQuestion (ImageVerify imageVerify) { String code = ""; List<ImageVerifyQuestioner> imageVerifyQuestionerList = new ArrayList<ImageVerifyQuestioner>(); imageVerifyQuestionerList.add(new ImageVerifyConsoleQuestioner(imageVerify)); imageVerifyQuestionerList.add(new ImageVerifyWeiboQuestioner(imageVerify)); boolean alive = true; while (alive) { for(ImageVerifyQuestioner imageVerifyQuestioner : imageVerifyQuestionerList) { if (true == alive) { imageVerifyQuestioner.loop(); if (imageVerifyQuestioner.getCode() != "") { code = imageVerifyQuestioner.getCode(); alive = false; } } } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } return code; } }
保持原有的命令行交互输入图片验证码的输入方式。
/** * */ package test_2; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import iqq.im.event.QQNotifyEventArgs.ImageVerify; public class ImageVerifyConsoleQuestioner extends ImageVerifyQuestioner { private boolean isPrompt; private StringBuilder codeStringBuilder = new StringBuilder(); public ImageVerifyConsoleQuestioner(ImageVerify imageVerify) { super(imageVerify); } private void prompt () { try { ImageIO.write(this.getImageVerify().image, "png", new File("verify.png")); System.out.println(this.getImageVerify().reason); System.out.print("请输入在项目根目录下 verify.png 图片里面的验证码: "); isPrompt = true; } catch (IOException e) { e.printStackTrace(); } } private void read () { try { int available = System.in.available(); if (0 < available) { int inChar = System.in.read(); switch (Character.getType(inChar)) { case Character.UPPERCASE_LETTER: case Character.LOWERCASE_LETTER: case Character.DECIMAL_DIGIT_NUMBER: codeStringBuilder.append((char) inChar); break; case Character.CONTROL: this.setCode(codeStringBuilder.toString()); break; } } } catch (IOException e) { e.printStackTrace(); } } @Override public void loop() { if (false == this.isPrompt) { this.prompt(); } if (this.getCode() == "") { this.read(); } } }
新增通过新浪微博输入图片验证码的输入方式,其中的accessTokenFile的值应替换为保存新浪微博Access Token的路径。
/** * */ package test_2; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.text.SimpleDateFormat; import java.util.Date; import java.util.logging.Logger; import javax.imageio.ImageIO; import javax.net.ssl.HttpsURLConnection; import org.json.JSONObject; import iqq.im.event.QQNotifyEventArgs.ImageVerify; public class ImageVerifyWeiboQuestioner extends ImageVerifyQuestioner { private static final String accessTokenFile = "**********"; private static final Logger logger = java.util.logging.Logger.getLogger(""); private String accessToken; private long weiboId; private long lastCheckTime; public ImageVerifyWeiboQuestioner(ImageVerify imageVerify) { super(imageVerify); } private void readAccessToken () { FileReader fileReaderAccessToken; try { fileReaderAccessToken = new FileReader(accessTokenFile); BufferedReader bufferedReaderAccessToken = new BufferedReader(fileReaderAccessToken); accessToken = bufferedReaderAccessToken.readLine(); bufferedReaderAccessToken.close(); fileReaderAccessToken.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void uploadVerifyImage () { try { //Status StringBuilder statusStringBuilder = new StringBuilder(); statusStringBuilder.append("请@胡争辉 输入验证码 "); statusStringBuilder.append((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")).format(new Date())); //URL StringBuilder urlStringBuilder = new StringBuilder(); urlStringBuilder.append("https://api.weibo.com/2/statuses/upload.json?access_token="); urlStringBuilder.append(accessToken); URL url = new URL(urlStringBuilder.toString()); URLConnection urlConnection =url.openConnection(); if (urlConnection instanceof HttpsURLConnection) { String BOUNDARY = "---------------------------" + String.valueOf(System.currentTimeMillis()); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); byteArrayOutputStream.write(("--" + BOUNDARY + "\r\n").getBytes()); byteArrayOutputStream.write(("Content-Disposition: form-data; name=\"pic\"; filename=\"verifyimage.png\"\r\n").getBytes()); byteArrayOutputStream.write(("Content-Type: image/png\r\n").getBytes()); byteArrayOutputStream.write(("\r\n").getBytes()); ImageIO.write(this.getImageVerify().image, "png", byteArrayOutputStream); byteArrayOutputStream.write(("\r\n").getBytes()); byteArrayOutputStream.write(("--" + BOUNDARY + "\r\n").getBytes()); byteArrayOutputStream.write(("Content-Disposition: form-data; name=\"status\"\r\n").getBytes()); byteArrayOutputStream.write(("\r\n").getBytes()); byteArrayOutputStream.write(statusStringBuilder.toString().getBytes()); byteArrayOutputStream.write(("\r\n--" + BOUNDARY + "--\r\n").getBytes()); HttpsURLConnection httpsURLConnection = (HttpsURLConnection) urlConnection; httpsURLConnection.setRequestMethod("POST"); httpsURLConnection.addRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); httpsURLConnection.setDoOutput(true); httpsURLConnection.setDoInput(true); OutputStream outputStream = httpsURLConnection.getOutputStream(); outputStream.write(byteArrayOutputStream.toByteArray()); outputStream.flush(); outputStream.close(); StringBuilder response = new StringBuilder(); InputStreamReader inputStreamReader = new InputStreamReader(httpsURLConnection.getInputStream()); int readChar = inputStreamReader.read(); while (-1 != readChar) { response.append((char) readChar); readChar = inputStreamReader.read(); } inputStreamReader.close(); httpsURLConnection.disconnect(); logger.info(response.toString()); JSONObject responseJson = new JSONObject(response.toString()); this.weiboId = responseJson.getLong("id"); logger.info("Weibo Id = " + String.valueOf(this.weiboId)); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } private void checkComment () { try { StringBuilder commentStringBuilder = new StringBuilder(); commentStringBuilder.append("https://api.weibo.com/2/comments/show.json?filter_by_author=1&access_token="); commentStringBuilder.append(accessToken); commentStringBuilder.append("&id="); commentStringBuilder.append(weiboId); URL comment = new URL(commentStringBuilder.toString()); URLConnection commentConnection = comment.openConnection(); if (commentConnection instanceof HttpsURLConnection) { HttpsURLConnection commentHttpsURLConnection = (HttpsURLConnection) commentConnection; commentHttpsURLConnection.setDoInput(true); InputStreamReader commentStreamReader = new InputStreamReader(commentHttpsURLConnection.getInputStream()); StringBuilder commentResponse = new StringBuilder(); int commentChar = commentStreamReader.read(); while (-1 != commentChar) { commentResponse.append((char) commentChar); commentChar = commentStreamReader.read(); } commentStreamReader.close(); commentHttpsURLConnection.disconnect(); JSONObject commentsJson = new JSONObject(commentResponse.toString()); int total_number = commentsJson.getInt("total_number"); logger.info("total_number = " + String.valueOf(total_number)); if (0 < total_number) { JSONObject commentJson = commentsJson.getJSONArray("comments").getJSONObject(0); logger.info("text=" + commentJson.getString("text")); this.setCode(commentJson.getString("text")); } } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @Override public void loop () { if (null == accessToken) { this.readAccessToken(); } if ((null != accessToken) && (0 == weiboId)) { this.uploadVerifyImage(); lastCheckTime = System.currentTimeMillis(); } if ((0 < weiboId) && (this.getCode() == "") && (10000 < System.currentTimeMillis() - lastCheckTime)) { this.checkComment(); lastCheckTime = System.currentTimeMillis(); } } }
iQQ 学习笔记2测试 :借助新浪微博输入验证码、远程控制退出
测试本程序需要注册两个QQ号,并互相加为好友。保持一个QQ使用客户端登录,另一个QQ的QQ号和密码填写在程序中。
需要注册一个新浪微博APP帐号,需要注册两个新浪微博帐号,其中一个通过该新浪微博APP验证后保存Access Token,设定这两个帐号互相关注。
运行程序后不仅出现命令行提示,还将用一个新浪微博帐号发一条微博,另一个新浪微博帐号会显示该条微博,微博上包含有验证码图片,用另一个新浪微博帐号回复该条微博,内容为验证码图片上的文字。
在QQ上可以看到该QQ上线,如果输入logout,QQ将下线,程序自动退出。