自从微信网页版不能登录后,网上开源的微信消息库都不能用了。
本文是通过按键模拟的方式来实现微信消息机器人,这方法也适用于模拟其它软件。
1、用到的工具和库
(1)JNA,https://github.com/java-native-access/jna,用于获取微信程序,程序窗口置顶操作。
(2)tesseract OCR,主要用于图片文字识别,用法可以参考之前文章https://my.oschina.net/penngo/blog/4972818
(3)java.awt.Robot,JDK自带的类,可以模拟键盘操作、鼠标操作、以及截图。
2、模拟操作步骤
(1)检查当前处于置顶的活动窗口程序是不是微信,如果不是微信则设置微信为置顶。
(2)按下CTRL+F,把需要对话的用户名或群名,粘贴到搜索框,搜索结果最匹配的用户名显示在第一条
(3)按下回车键,会自动选中搜索结果的第一条,并打开对话框。
(4)把对话文本粘贴到对话框,按下回车键发送。
通过上边4个步骤把消息发送出去。
实现代码:
RobotUtil.java
package com.penngo.weixin;
import com.penngo.weixin.utils.Tess4jUtil;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.event.KeyEvent;
public class RobotUtil {
private static Robot robot;
private static Clipboard clip;
private static Toolkit kit;
public static void isWeixinOnTop(){
WinDef.HWND tophwnd = User32.INSTANCE.GetForegroundWindow();
int textLength = User32.INSTANCE.GetWindowTextLength(tophwnd);
char[] windowText = new char[textLength];
User32.INSTANCE.GetWindowText(tophwnd, windowText, textLength + 3);
String windowTitle = String.valueOf(windowText).trim();
if("微信".equals(windowTitle) == false){
WinDef.HWND hwnd = User32.INSTANCE.FindWindow(null, "微信");
if (hwnd == null) {
System.out.println("微信未运行");
System.exit(0);
} else {
User32.INSTANCE.ShowWindow(hwnd, 9);
User32.INSTANCE.SetForegroundWindow(hwnd);
}
}
}
/**
* 初始化全局变量
*
*/
public static void init() {
try {
robot = new Robot();
} catch (AWTException e) {
robot = null;
e.printStackTrace();
}
kit = Toolkit.getDefaultToolkit();
clip = kit.getSystemClipboard();
}
public static void findName(String name){
keyPressRelease(KeyEvent.VK_CONTROL, KeyEvent.VK_F);
copy(name);
paste();
delay(2000);
enter();
}
/**
* 把内容放到剪贴板
*/
public static void copy(String str){
Transferable tText = new StringSelection(str);
try{
clip.setContents(tText, null);
}
catch(Exception e){
e.printStackTrace();
clip = Toolkit.getDefaultToolkit().getSystemClipboard();
clip.setContents(tText, null);
}
}
/**
* 把内容放到剪贴板
*/
public static void copyImg(String imgPath){
Transferable tImg = new ImageSelection(kit.getImage(imgPath));
clip.setContents(tImg, null);
}
/**
* 粘贴
*/
public static void paste(){
keyPressRelease(KeyEvent.VK_CONTROL, KeyEvent.VK_V);
}
/**
* 按快捷键,按下并释放
* @param keys
*/
public static void keyPressRelease(int...keys){
for(int key:keys){
robot.keyPress(key);
}
delay(50);
for(int key:keys){
robot.keyRelease(key);
}
delay(50);
}
public static void delay(int ms){
robot.delay(ms);
}
public static void enter(){
keyPressRelease(KeyEvent.VK_ENTER);
}
}
WeiXinMessage.java
package com.penngo.weixin;
import com.penngo.weixin.utils.DateUtil;
import java.util.Date;
import java.util.Random;
public class WeiXinMessage {
public WeiXinMessage(){
RobotUtil.init();
RobotUtil.topWeChat();
RobotUtil.delay(2000);
}
/**
* 发送文本消息
* @param nickName 微信群内的用户昵称
* @param txtMsg 发送的文本消息
*/
public void sendTxt(String nickName, String txtMsg){
RobotUtil.isWeixinOnTop();
RobotUtil.findName(nickName);
RobotUtil.delay(2000);
RobotUtil.copy(txtMsg);
RobotUtil.paste();
RobotUtil.delay(2000);
RobotUtil.enter();
}
public void sendImg(String nickName, String imgMsg){
RobotUtil.findName(nickName);
RobotUtil.delay(2000);
RobotUtil.copyImg(imgMsg);
RobotUtil.paste();
RobotUtil.delay(2000);
RobotUtil.enter();
}
public static void main(String[] args) {
WeiXinMessage weiXin = new WeiXinMessage();
weiXin.sendTxt("文件传输助手", "hello,penngo");
// weiXin.sendImg("文件传输助手", "target\\test.jpg");
}
}
3、改进优化
(1)在使用的过程中,发现偶尔的情况,会出现消息发错用户的情况(出现机率不算高)。
引入tesseract OCR,识别当前用户是否准确。
实现代码
WinDef.HWND hwnd = User32.INSTANCE.FindWindow("WeChatMainWndForPC", null);
WinDef.RECT rect = new WinDef.RECT();
User32.INSTANCE.GetWindowRect(hwnd,rect);
User32.INSTANCE.GetWindowRect(hwnd, rect);
int qqwin_width = rect.right-rect.left;
int qqwin_height = rect.bottom-rect.top;
User32.INSTANCE.MoveWindow(hwnd, 0, 0, qqwin_width, qqwin_height, true);
Tess4jUtil tess4jUtil = new Tess4jUtil();
// 识别出来的用户名
String name = tess4jUtil.getText(0 + 300, 0, qqwin_width - 300, 60);
(2)使用netty提供http接口,这样第三方程序可以与机器人交互,通过http接口,把消息传给机器人,再由机器人发送给微信用户。