iQQ 学习笔记2 :借助新浪微博输入验证码、远程控制退出

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将下线,程序自动退出。

原文地址:https://www.cnblogs.com/javawebsoa/p/3113109.html