Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.github.biezhi.wechat.api.WeChatApiImpl Maven / Gradle / Ivy
package io.github.biezhi.wechat.api;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import io.github.biezhi.wechat.WeChatBot;
import io.github.biezhi.wechat.api.annotation.Bind;
import io.github.biezhi.wechat.api.constant.Constant;
import io.github.biezhi.wechat.api.constant.StateCode;
import io.github.biezhi.wechat.api.enums.AccountType;
import io.github.biezhi.wechat.api.enums.MsgType;
import io.github.biezhi.wechat.api.model.*;
import io.github.biezhi.wechat.api.request.BaseRequest;
import io.github.biezhi.wechat.api.request.FileRequest;
import io.github.biezhi.wechat.api.request.JsonRequest;
import io.github.biezhi.wechat.api.request.StringRequest;
import io.github.biezhi.wechat.api.response.*;
import io.github.biezhi.wechat.exception.WeChatException;
import io.github.biezhi.wechat.utils.*;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static io.github.biezhi.wechat.api.constant.Constant.*;
/**
* 微信API实现
*
* @author biezhi
* @date 2018/1/21
*/
@Slf4j
public class WeChatApiImpl implements WeChatApi {
private static final Pattern UUID_PATTERN = Pattern.compile("window.QRLogin.code = (\\d+); window.QRLogin.uuid = \"(\\S+?)\";");
private static final Pattern CHECK_LOGIN_PATTERN = Pattern.compile("window.code=(\\d+)");
private static final Pattern PROCESS_LOGIN_PATTERN = Pattern.compile("window.redirect_uri=\"(\\S+)\";");
private static final Pattern SYNC_CHECK_PATTERN = Pattern.compile("window.synccheck=\\{retcode:\"(\\d+)\",selector:\"(\\d+)\"}");
private String uuid;
private boolean logging;
private int memberCount;
private final WeChatBot bot;
private final Map> mapping = new ConcurrentHashMap>();
/**
* 所有账号
*/
@Getter
private Map accountMap = new HashMap();
/**
* 特殊账号
*/
@Getter
private List specialUsersList;
/**
* 公众号、服务号
*/
@Getter
private List publicUsersList;
/**
* 好友
*/
@Getter
private List contactList;
/**
* 群组
*/
@Getter
private List groupList;
public WeChatApiImpl(WeChatBot bot) {
this.bot = bot;
Method[] methods = bot.getClass().getMethods();
for (Method method : methods) {
Bind bind = method.getAnnotation(Bind.class);
if (null != bind) {
MsgType[] msgTypes = bind.msgType();
for (MsgType msgType : msgTypes) {
List invokes = mapping.get(msgType);
if (null == mapping.get(msgType)) {
invokes = new ArrayList();
}
invokes.add(new Invoke(method, Arrays.asList(bind.accountType())));
log.info("绑定函数 [{}] - [{}]", method.getName(), msgType);
mapping.put(msgType, invokes);
}
}
}
}
@Override
public void login() {
if (bot.isRunning() || logging) {
log.warn("微信已经登录");
return;
}
this.logging = true;
while (logging) {
this.uuid = pushLogin();
if (null == this.uuid) {
while (null == this.getUUID()) {
DateUtils.sleep(10);
}
log.info("开始下载二维码");
this.getQrImage(this.uuid, bot.config().showTerminal());
log.info("请使用手机扫描屏幕二维码");
}
Boolean isLoggedIn = false;
while (null == isLoggedIn || !isLoggedIn) {
String status = this.checkLogin(this.uuid);
if (StateCode.SUCCESS.equals(status)) {
isLoggedIn = true;
} else if ("201".equals(status)) {
if (null != isLoggedIn) {
log.info("请在手机上确认登录");
isLoggedIn = null;
}
} else if ("408".equals(status)) {
break;
}
DateUtils.sleep(300);
}
if (null != isLoggedIn && isLoggedIn) {
break;
}
if (logging) {
log.info("登录超时,重新加载二维码");
}
}
this.webInit();
this.statusNotify();
this.loadContact(0);
log.info("应有 {} 个联系人,读取到联系人 {} 个", this.memberCount, this.accountMap.size());
System.out.println();
log.info("共有 {} 个群 | {} 个直接联系人 | {} 个特殊账号 | {} 公众号或服务号",
this.groupList.size(), this.contactList.size(),
this.specialUsersList.size(), this.publicUsersList.size());
this.loadGroupList();
log.info("[{}] 登录成功.", bot.session().getNickName());
this.startRevice();
logging = false;
}
/**
* 获取UUID
*
* @return
*/
private String getUUID() {
log.info("获取二维码UUID");
// 登录
ApiResponse response = bot.execute(new StringRequest("https://login.weixin.qq.com/jslogin")
.add("appid", "wx782c26e4c19acffb").add("fun", "new"));
Matcher matcher = UUID_PATTERN.matcher(response.getRawBody());
if (matcher.find() && StateCode.SUCCESS.equals(matcher.group(1))) {
this.uuid = matcher.group(2);
}
return this.uuid;
}
/**
* 读取二维码图片
*
* @param uuid
* @param terminalShow 是否在终端显示输出
*/
private void getQrImage(String uuid, boolean terminalShow) {
String uid = null != uuid ? uuid : this.uuid;
String imgDir = bot.config().assetsDir();
FileResponse fileResponse = bot.download(
new FileRequest(String.format("%s/qrcode/%s", Constant.BASE_URL, uid)));
InputStream inputStream = fileResponse.getInputStream();
File qrCode = WeChatUtils.saveFile(inputStream, imgDir, "qrcode.png");
DateUtils.sleep(500);
QRCodeUtils.showQrCode(qrCode, terminalShow);
}
/**
* 检查是否登录
*
* @param uuid
* @return
*/
private String checkLogin(String uuid) {
String uid = null != uuid ? uuid : this.uuid;
String url = String.format("%s/cgi-bin/mmwebwx-bin/login", Constant.BASE_URL);
Long time = System.currentTimeMillis();
ApiResponse response = bot.execute(new StringRequest(url)
.add("loginicon", true).add("uuid", uid)
.add("tip", "1").add("_", time)
.add("r", (int) (-time / 1000) / 1579)
.timeout(30));
Matcher matcher = CHECK_LOGIN_PATTERN.matcher(response.getRawBody());
if (matcher.find()) {
if (StateCode.SUCCESS.equals(matcher.group(1))) {
if (!this.processLoginSession(response.getRawBody())) {
return StateCode.FAIL;
}
return StateCode.SUCCESS;
}
return matcher.group(1);
}
return StateCode.FAIL;
}
/**
* 处理登录session
*
* @param loginContent
* @return
*/
private boolean processLoginSession(String loginContent) {
LoginSession loginSession = bot.session();
Matcher matcher = PROCESS_LOGIN_PATTERN.matcher(loginContent);
if (matcher.find()) {
loginSession.setUrl(matcher.group(1));
}
ApiResponse response = bot.execute(new StringRequest(loginSession.getUrl()).noRedirect());
loginSession.setUrl(loginSession.getUrl().substring(0, loginSession.getUrl().lastIndexOf("/")));
String body = response.getRawBody();
List fileUrl = new ArrayList();
List syncUrl = new ArrayList();
for (int i = 0; i < FILE_URL.size(); i++) {
fileUrl.add(String.format("https://%s/cgi-bin/mmwebwx-bin", FILE_URL.get(i)));
syncUrl.add(String.format("https://%s/cgi-bin/mmwebwx-bin", WEBPUSH_URL.get(i)));
}
boolean flag = false;
for (int i = 0; i < FILE_URL.size(); i++) {
String indexUrl = INDEX_URL.get(i);
if (loginSession.getUrl().contains(indexUrl)) {
loginSession.setFileUrl(fileUrl.get(i));
loginSession.setSyncUrl(syncUrl.get(i));
flag = true;
break;
}
}
if (!flag) {
loginSession.setFileUrl(loginSession.getUrl());
loginSession.setSyncUrl(loginSession.getUrl());
}
loginSession.setDeviceId("e" + String.valueOf(System.currentTimeMillis()));
BaseRequest baseRequest = new BaseRequest();
loginSession.setBaseRequest(baseRequest);
loginSession.setSKey(WeChatUtils.match("(\\S+) ", body));
loginSession.setWxSid(WeChatUtils.match("(\\S+) ", body));
loginSession.setWxUin(WeChatUtils.match("(\\S+) ", body));
loginSession.setPassTicket(WeChatUtils.match("(\\S+) ", body));
baseRequest.setSkey(loginSession.getSKey());
baseRequest.setSid(loginSession.getWxSid());
baseRequest.setUin(loginSession.getWxUin());
baseRequest.setDeviceID(loginSession.getDeviceId());
return true;
}
/**
* 推送登录
*
* @return
*/
private String pushLogin() {
String uin = bot.client().cookie("wxUin");
if (StringUtils.isEmpty(uin)) {
return null;
}
String url = String.format("%s/cgi-bin/mmwebwx-bin/webwxpushloginurl?uin=%s",
Constant.BASE_URL, uin);
JsonResponse jsonResponse = bot.execute(new JsonRequest(url));
return jsonResponse.getString("uuid");
}
/**
* 开启状态通知
*
* @return
*/
private void statusNotify() {
log.info("开启状态通知");
String url = String.format("%s/webwxstatusnotify?lang=zh_CN&pass_ticket=%s",
bot.session().getUrl(), bot.session().getPassTicket());
bot.execute(new JsonRequest(url).post().jsonBody()
.add("BaseRequest", bot.session().getBaseRequest())
.add("Code", 3)
.add("FromUserName", bot.session().getUserName())
.add("ToUserName", bot.session().getUserName())
.add("ClientMsgId", System.currentTimeMillis() / 1000));
}
/**
* web 初始化
*/
private WebInitResponse webInit() {
log.info("微信初始化...");
int r = (int) (-System.currentTimeMillis() / 1000) / 1579;
String url = String.format("%s/webwxinit?r=%d&pass_ticket=%s",
bot.session().getUrl(), r, bot.session().getPassTicket());
JsonResponse response = bot.execute(new JsonRequest(url).post().jsonBody()
.add("BaseRequest", bot.session().getBaseRequest()));
WebInitResponse webInitResponse = response.parse(WebInitResponse.class);
List contactList = webInitResponse.getContactList();
this.syncRecentContact(contactList);
Account account = webInitResponse.getAccount();
SyncKey syncKey = webInitResponse.getSyncKey();
bot.session().setInviteStartCount(webInitResponse.getInviteStartCount());
bot.session().setAccount(account);
bot.session().setUserName(account.getUserName());
bot.session().setNickName(account.getNickName());
bot.session().setSyncKey(syncKey);
return webInitResponse;
}
private void startRevice() {
bot.setRunning(true);
Thread thread = new Thread(new ChatLoop(bot));
thread.setName("wechat-listener");
thread.setDaemon(true);
thread.start();
}
/**
* 心跳检查
*
* @return
*/
@Override
public SyncCheckRet syncCheck() {
String url = String.format("%s/synccheck", bot.session().getSyncOrUrl());
try {
ApiResponse response = bot.execute(new StringRequest(url)
.add("r", System.currentTimeMillis())
.add("skey", bot.session().getSKey())
.add("sid", bot.session().getWxSid())
.add("uin", bot.session().getWxUin())
.add("deviceid", bot.session().getDeviceId())
.add("synckey", bot.session().getSynckeyStr())
.add("_", System.currentTimeMillis())
);
Matcher matcher = SYNC_CHECK_PATTERN.matcher(response.getRawBody());
if (matcher.find()) {
if (!"0".equals(matcher.group(1))) {
log.debug("Unexpected sync check result: {}", response.getRawBody());
return new SyncCheckRet(1100, 0);
}
return new SyncCheckRet(Integer.valueOf(matcher.group(1)), Integer.valueOf(matcher.group(2)));
}
return null;
} catch (Exception e) {
return new SyncCheckRet(0, 0);
}
}
/**
* 获取消息
*/
@Override
public WebSyncResponse webSync() {
String url = String.format("%s/webwxsync?sid=%s&sKey=%s&passTicket=%s",
bot.session().getUrl(), bot.session().getWxSid(),
bot.session().getSKey(), bot.session().getPassTicket());
JsonResponse response = bot.execute(new JsonRequest(url).post().jsonBody()
.add("BaseRequest", bot.session().getBaseRequest())
.add("SyncKey", bot.session().getSyncKey())
.add("rr", ~(System.currentTimeMillis() / 1000)));
WebSyncResponse webSyncResponse = response.parse(WebSyncResponse.class);
if (!webSyncResponse.success()) {
log.warn("获取消息失败");
return webSyncResponse;
}
bot.session().setSyncKey(webSyncResponse.getSyncKey());
return webSyncResponse;
}
/**
* 退出登录
*
* @return
*/
@Override
public void logout() {
if (bot.isRunning()) {
String url = String.format("%s/webwxlogout", bot.session().getUrl());
bot.execute(new StringRequest(url)
.add("redirect", 1)
.add("type", 1)
.add("sKey", bot.session().getSKey()));
bot.setRunning(false);
}
this.logging = false;
bot.client().cookies().clear();
}
/**
* 加载联系人信息
*
* @return
*/
@Override
public void loadContact(int seq) {
log.info("开始获取联系人信息");
while (true) {
String url = String.format("%s/webwxgetcontact?r=%s&seq=%s&skey=%s",
bot.session().getUrl(), System.currentTimeMillis(),
seq, bot.session().getSKey());
JsonResponse response = bot.execute(new JsonRequest(url).jsonBody());
JsonObject jsonObject = response.toJsonObject();
seq = jsonObject.get("Seq").getAsInt();
this.memberCount += jsonObject.get("MemberCount").getAsInt();
List memberList = WeChatUtils.fromJson(WeChatUtils.toJson(jsonObject.getAsJsonArray("MemberList")), new TypeToken>() {});
for (Account account : memberList) {
if (null == account.getUserName()) {
accountMap.put(account.getUserName(), account);
}
}
// 查看seq是否为0,0表示好友列表已全部获取完毕,若大于0,则表示好友列表未获取完毕,当前的字节数(断点续传)
if (seq == 0) {
break;
}
}
this.contactList = new ArrayList(this.getAccountByType(AccountType.TYPE_FRIEND));
this.publicUsersList = new ArrayList(this.getAccountByType(AccountType.TYPE_MP));
this.specialUsersList = new ArrayList(this.getAccountByType(AccountType.TYPE_SPECIAL));
this.groupList = new ArrayList(this.getAccountByType(AccountType.TYPE_GROUP));
}
/**
* 加载群信息
*/
public void loadGroupList() {
log.info("加载群聊信息");
// 群账号
List> list = new ArrayList>(groupList.size());
for (Account account : groupList) {
Map map = new HashMap();
map.put("UserName", account.getUserName());
map.put("EncryChatRoomId", "");
list.add(map);
}
String url = String.format("%s/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s",
bot.session().getUrl(), System.currentTimeMillis() / 1000, bot.session().getPassTicket());
// 加载群信息
JsonResponse jsonResponse = bot.execute(new JsonRequest(url).post().jsonBody()
.add("BaseRequest", bot.session().getBaseRequest())
.add("Count", groupList.size())
.add("List", list)
);
List groups = WeChatUtils.fromJson(WeChatUtils.toJson(jsonResponse.toJsonObject().getAsJsonArray("ContactList")), new TypeToken>() {});
}
/**
* 根据UserName查询Account
*
* @param id
* @return
*/
@Override
public Account getAccountById(String id) {
return accountMap.get(id);
}
private String getUserRemarkName(String id) {
String name = id.contains("@@") ? "未知群" : "陌生人";
if (id.equals(this.bot.session().getUserName())) {
return this.bot.session().getNickName();
}
Account account = accountMap.get(id);
if (null == account) {
return name;
}
String nickName = StringUtils.isNotEmpty(account.getRemarkName()) ? account.getRemarkName() : account.getNickName();
return StringUtils.isNotEmpty(nickName) ? nickName : name;
}
private List getNameById(String id) {
String url = String.format("%s/webwxbatchgetcontact?type=ex&r=%s&pass_ticket=%s",
bot.session().getUrl(), System.currentTimeMillis() / 1000, bot.session().getPassTicket());
List> list = new ArrayList>();
Map map = new HashMap();
map.put("UserName", id);
map.put("EncryChatRoomId", id);
list.add(map);
JsonResponse response = bot.execute(new JsonRequest(url).post().jsonBody()
.add("BaseRequest", bot.session().getBaseRequest())
.add("Count", bot.session().getBaseRequest())
.add("List", list));
return WeChatUtils.fromJson(WeChatUtils.toJson(response.toJsonObject().getAsJsonObject("")), new TypeToken>() {});
}
/**
* 根据账号类型筛选
*
* @param accountType
* @return
*/
public Set getAccountByType(AccountType accountType) {
Set accountSet = new HashSet();
for (Account account : accountMap.values()) {
if (account.getAccountType().equals(accountType)) {
accountSet.add(account);
}
}
return accountSet;
}
/**
* 同步最近联系人
*
* 避免新建群聊无法同步
*
* @param contactList
*/
public void syncRecentContact(List contactList) {
if (null != contactList && contactList.size() > 0) {
for (Account account : contactList) {
accountMap.put(account.getUserName(), account);
}
}
}
/**
* 处理新消息
*
* @param webSyncResponse
*/
@Override
public void handleMsg(WebSyncResponse webSyncResponse) {
List addMessageList = webSyncResponse.getAddMessageList();
if (null != addMessageList && addMessageList.size() > 0) {
log.info("你有新的消息");
for (Message message : addMessageList) {
this.processMsg(message);
}
}
}
private void processMsg(Message message) {
Integer msgType = message.getMsgType();
String name = this.getUserRemarkName(message.getFromUserName());
String content = message.getContent().replace("<", "<")
.replace(">", ">");
if (message.isGroup()) {
if (content.contains(": ")) {
content = content.split(": ")[1];
}
}
String msgId = message.getId();
Account fromAccount = this.getAccountById(message.getFromUserName());
if (null == fromAccount) {
log.warn("未知消息类型: {}", WeChatUtils.toJson(message));
return;
}
log.debug("收到消息JSON: {}", WeChatUtils.toJson(message));
WeChatMessage.WeChatMessageBuilder weChatMessageBuilder = WeChatMessage.builder()
.raw(message)
.fromNickName(fromAccount.getNickName())
.fromRemarkName(fromAccount.getRemarkName())
.fromUserName(message.getFromUserName())
.text(content);
List invokes = mapping.get(MsgType.ALL);
if (null != invokes && invokes.size() > 0) {
this.callBack(invokes, weChatMessageBuilder.build());
}
switch (msgType) {
case 1:
if (bot.autoReply()) {
this.sendText("自动回复: " + content, message.getFromUserName());
} else {
invokes = mapping.get(MsgType.TEXT);
if (null != invokes && invokes.size() > 0) {
this.callBack(invokes, weChatMessageBuilder.build());
}
}
break;
// 聊天图片
case 3:
String imgPath = this.downloadMsgImg(msgId);
invokes = mapping.get(MsgType.IMAGE);
if (null != invokes && invokes.size() > 0) {
this.callBack(invokes, weChatMessageBuilder.imagePath(imgPath).build());
}
break;
// 语音
case 34:
String voicePath = this.downloadVoice(msgId);
invokes = mapping.get(MsgType.VOICE);
if (null != invokes && invokes.size() > 0) {
this.callBack(invokes, weChatMessageBuilder.voicePath(voicePath).build());
}
break;
// 名片
case 42:
log.info("{} 发送了一张名片: ", name);
log.info("=========================");
log.info("= 昵称: {}", message.getRecommend().getNickName());
log.info("= 微信号: {}", message.getRecommend().getAlias());
log.info("= 地区: {}-{}", message.getRecommend().getProvince(), message.getRecommend().getCity());
log.info("= 性别: {}", message.getRecommend().getSex());
log.info("=========================");
invokes = mapping.get(MsgType.PERSON_CARD);
if (null != invokes && invokes.size() > 0) {
this.callBack(invokes, weChatMessageBuilder.build());
}
break;
// 视频
case 43:
String videoPath = this.downloadVideo(msgId);
invokes = mapping.get(MsgType.VIDEO);
if (null != invokes && invokes.size() > 0) {
this.callBack(invokes, weChatMessageBuilder.videoPath(videoPath).build());
}
break;
// 动画表情
case 47:
log.info("{} 发了一个动画表情");
imgPath = this.downloadMsgImg(message.getNewMsgId().toString());
invokes = mapping.get(MsgType.IMAGE);
if (null != invokes && invokes.size() > 0) {
this.callBack(invokes, weChatMessageBuilder.imagePath(imgPath).build());
}
break;
// 分享
case 49:
break;
// 联系人初始化
case 51:
break;
// 视频
case 62:
videoPath = this.downloadVideo(msgId);
invokes = mapping.get(MsgType.VIDEO);
if (null != invokes && invokes.size() > 0) {
this.callBack(invokes, weChatMessageBuilder.videoPath(videoPath).build());
}
break;
// 邀请好友进群
case 10000:
log.info("{}", content);
break;
// 撤回消息
case 10002:
log.info("{} 撤回了一条消息: {}", name, content);
invokes = mapping.get(MsgType.REVOKE);
if (null != invokes && invokes.size() > 0) {
this.callBack(invokes, weChatMessageBuilder.build());
}
break;
default:
log.info("该消息类型为: {}, 可能是表情,图片, 链接或红包: %s", msgType, WeChatUtils.toJson(message));
break;
}
}
/**
* 回调微信消息给客户端、存储器
*
* @param invokes
* @param message
*/
private void callBack(List invokes, WeChatMessage message) {
if (null != bot.storageMessage()) {
bot.storageMessage().save(message);
}
for (Invoke invoke : invokes) {
invoke.call(bot, message);
}
}
/**
* 下载图片到本地
*
* @param msgId
*/
private String downloadMsgImg(String msgId) {
String url = String.format("%s/webwxgetmsgimg?msgid=%s&skey=%s", bot.session().getUrl(), msgId, bot.session().getSKey());
FileResponse response = bot.download(new FileRequest(url));
InputStream inputStream = response.getInputStream();
String id = msgId + ".jpg";
File file = WeChatUtils.saveFile(inputStream, bot.config().assetsDir() + "/images", id);
return file.getPath();
}
/**
* 下载表情到本地
*
* @param msgId
*/
private String downloadIconImg(String msgId) {
String url = String.format("%s/webwxgeticon?username=%s&skey=%s", bot.session().getUrl(), msgId, bot.session().getSKey());
FileResponse response = bot.download(new FileRequest(url));
InputStream inputStream = response.getInputStream();
String id = msgId + ".jpg";
return WeChatUtils.saveFile(inputStream, bot.config().assetsDir() + "/icons", id).getPath();
}
/**
* 下载头像到本地
*
* @param userName
*/
private String downloadHeadImg(String userName) {
String url = String.format("%s/webwxgetheadimg?username=%s&skey=%s", bot.session().getUrl(), userName, bot.session().getSKey());
FileResponse response = bot.download(new FileRequest(url));
InputStream inputStream = response.getInputStream();
String id = userName + ".jpg";
return WeChatUtils.saveFile(inputStream, bot.config().assetsDir() + "/head", id).getPath();
}
/**
* 下载视频到本地
*
* @param msgId
*/
private String downloadVideo(String msgId) {
String url = String.format("%s/webwxgetvideo?msgid=%s&skey=%s", bot.session().getUrl(), msgId, bot.session().getSKey());
FileResponse response = bot.download(new FileRequest(url));
InputStream inputStream = response.getInputStream();
String id = msgId + ".mp4";
return WeChatUtils.saveFile(inputStream, bot.config().assetsDir() + "/video", id).getPath();
}
/**
* 下载音频到本地
*
* @param msgId
*/
private String downloadVoice(String msgId) {
String url = String.format("%s/webwxgetvoice?msgid=%s&skey=%s", bot.session().getUrl(), msgId, bot.session().getSKey());
FileResponse response = bot.download(new FileRequest(url));
InputStream inputStream = response.getInputStream();
String id = msgId + ".mp3";
return WeChatUtils.saveFile(inputStream, bot.config().assetsDir() + "/video", id).getPath();
}
/**
* 上传附件
*
* @param toUser
* @param filePath
* @return
*/
public MediaResponse uploadMedia(String toUser, String filePath) {
File file = new File(filePath);
if (!file.exists()) {
throw new WeChatException("文件[" + filePath + "]不存在");
}
long size = file.length();
String mimeType = "image/jpeg";
String url = String.format("%s/webwxuploadmedia?f=json", bot.session().getFileUrl());
String mediaId = System.currentTimeMillis() / 1000 + StringUtils.random(6);
Map uploadMediaRequest = new HashMap();
uploadMediaRequest.put("UploadType", 2);
uploadMediaRequest.put("BaseRequest", bot.session().getBaseRequest());
uploadMediaRequest.put("ClientMediaId", mediaId);
uploadMediaRequest.put("TotalLen", size);
uploadMediaRequest.put("StartPos", 0);
uploadMediaRequest.put("DataLen", size);
uploadMediaRequest.put("MediaType", 4);
uploadMediaRequest.put("FromUserName", bot.session().getUserName());
uploadMediaRequest.put("ToUserName", toUser);
uploadMediaRequest.put("FileMd5", MD5Checksum.getMD5Checksum(file.getPath()));
String dataTicket = bot.client().cookie("webwx_data_ticket");
if (StringUtils.isEmpty(dataTicket)) {
throw new WeChatException("缺少了附件Cookie");
}
ApiResponse response = bot.execute(new StringRequest(url).post().multipart()
.fileName(file.getName())
.add("id", "WU_FILE_0")
.add("name", filePath)
.add("type", mimeType)
.add("lastModifieDate", new SimpleDateFormat("yyyy MM dd HH:mm:ss").format(new Date()))
.add("size", String.valueOf(size))
.add("mediatype", mimeType)
.add("uploadmediarequest", WeChatUtils.toJson(uploadMediaRequest))
.add("webwx_data_ticket", dataTicket)
.add("pass_ticket", bot.session().getPassTicket())
.add("filename", RequestBody.create(MediaType.parse("image/jpeg"), file)));
MediaResponse mediaResponse = response.parse(MediaResponse.class);
if (!mediaResponse.success()) {
log.warn("上传附件失败: {}", mediaResponse.getMsg());
}
return mediaResponse;
}
@Override
public void sendFile(String toUserName, String filePath) {
String mediaId = this.uploadMedia(toUserName, filePath).getMediaId();
if (StringUtils.isEmpty(mediaId)) {
log.warn("Media为空");
return;
}
String url = String.format("%s/webwxsendmsgimg?fun=async&f=json&pass_ticket=%s",
bot.session().getUrl(), bot.session().getPassTicket());
String msgId = System.currentTimeMillis() / 1000 + StringUtils.random(6);
Map msg = new HashMap();
msg.put("Type", 3);
msg.put("MediaId", mediaId);
msg.put("FromUserName", bot.session().getUserName());
msg.put("ToUserName", toUserName);
msg.put("LocalID", msgId);
msg.put("ClientMsgId", msgId);
bot.execute(new JsonRequest(url).post().jsonBody()
.add("BaseRequest", bot.session().getBaseRequest())
.add("Msg", msg)
);
}
/**
* 发送文本消息
*
* @param msg
* @param toUserName
*/
@Override
public void sendText(String toUserName, String msg) {
String url = String.format("%s/webwxsendmsg?pass_ticket=%s", bot.session().getUrl(), bot.session().getPassTicket());
String msgId = System.currentTimeMillis() / 1000 + StringUtils.random(6);
bot.execute(new JsonRequest(url).post().jsonBody()
.add("BaseRequest", bot.session().getBaseRequest())
.add("Msg", new SendMessage(1, msg, bot.session().getUserName(), toUserName, msgId, msgId))
);
}
}