init v1.0.0
BIN
image/07.png
|
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 547 KiB |
BIN
image/08.png
|
Before Width: | Height: | Size: 532 KiB After Width: | Height: | Size: 222 KiB |
BIN
image/10.png
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 111 KiB |
BIN
image/11.png
|
Before Width: | Height: | Size: 3.4 MiB After Width: | Height: | Size: 139 KiB |
28
pom.xml
@@ -10,7 +10,7 @@
|
||||
|
||||
<name>ruoyi-ai</name>
|
||||
<url>https://gitee.com/ageerle/ruoyi-ai</url>
|
||||
<description>AI助手后台管理系统</description>
|
||||
<description>AI助手</description>
|
||||
|
||||
<properties>
|
||||
<revision>1.0.0</revision>
|
||||
@@ -242,16 +242,16 @@
|
||||
<version>${tencent.sms.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-starter-server</artifactId>
|
||||
<version>${spring-boot-admin.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-starter-client</artifactId>
|
||||
<version>${spring-boot-admin.version}</version>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>de.codecentric</groupId>-->
|
||||
<!-- <artifactId>spring-boot-admin-starter-server</artifactId>-->
|
||||
<!-- <version>${spring-boot-admin.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>de.codecentric</groupId>-->
|
||||
<!-- <artifactId>spring-boot-admin-starter-client</artifactId>-->
|
||||
<!-- <version>${spring-boot-admin.version}</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<!--redisson-->
|
||||
<dependency>
|
||||
@@ -318,6 +318,12 @@
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>ruoyi-midjourney</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>ruoyi-generator</artifactId>
|
||||
|
||||
@@ -59,6 +59,11 @@
|
||||
<artifactId>ruoyi-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>ruoyi-midjourney</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 代码生成-->
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
@@ -71,10 +76,10 @@
|
||||
<artifactId>ruoyi-demo</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>de.codecentric</groupId>
|
||||
<artifactId>spring-boot-admin-starter-client</artifactId>
|
||||
</dependency>
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>de.codecentric</groupId>-->
|
||||
<!-- <artifactId>spring-boot-admin-starter-client</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -94,7 +94,7 @@ public class CaptchaController {
|
||||
String code = RandomUtil.randomNumbers(4);
|
||||
RedisUtils.setCacheObject(key, code, Duration.ofMinutes(Constants.CAPTCHA_EXPIRATION));
|
||||
try {
|
||||
MailUtils.sendText(emailRequest.getUsername(), "【GPT助手】登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
|
||||
MailUtils.sendText(emailRequest.getUsername(), "【熊猫助手】登录验证码", "您本次验证码为:" + code + ",有效性为" + Constants.CAPTCHA_EXPIRATION + "分钟,请尽快填写。");
|
||||
} catch (Exception e) {
|
||||
log.error("验证码短信发送异常 => {}", e.getMessage());
|
||||
return R.fail(e.getMessage());
|
||||
|
||||
@@ -3,7 +3,10 @@ package com.xmzs.controller;
|
||||
|
||||
import com.xmzs.common.chat.domain.request.ChatRequest;
|
||||
import com.xmzs.common.chat.domain.request.Dall3Request;
|
||||
import com.xmzs.common.chat.domain.request.MjTaskRequest;
|
||||
import com.xmzs.common.chat.entity.Tts.TextToSpeech;
|
||||
import com.xmzs.common.chat.entity.images.Item;
|
||||
import com.xmzs.common.chat.entity.whisper.WhisperResponse;
|
||||
import com.xmzs.common.core.domain.R;
|
||||
import com.xmzs.common.core.domain.model.LoginUser;
|
||||
import com.xmzs.common.core.exception.base.BaseException;
|
||||
@@ -13,19 +16,30 @@ import com.xmzs.common.satoken.utils.LoginHelper;
|
||||
import com.xmzs.system.domain.bo.ChatMessageBo;
|
||||
import com.xmzs.system.domain.vo.ChatMessageVo;
|
||||
import com.xmzs.system.service.IChatMessageService;
|
||||
import com.xmzs.system.service.SseService;
|
||||
import com.xmzs.system.service.ISseService;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.core.io.InputStreamResource;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* 描述:
|
||||
*
|
||||
@@ -37,38 +51,58 @@ import java.util.List;
|
||||
@RequiredArgsConstructor
|
||||
public class ChatController {
|
||||
|
||||
private final SseService sseService;
|
||||
private final ISseService ISseService;
|
||||
|
||||
private final IChatMessageService chatMessageService;
|
||||
private final IChatMessageService chatMessageService;
|
||||
|
||||
/**
|
||||
* 聊天接口
|
||||
*/
|
||||
@PostMapping("/chat")
|
||||
@ResponseBody
|
||||
public SseEmitter sseChat(@RequestBody @Valid ChatRequest chatRequest) {
|
||||
if("gpt-4-all".equals(chatRequest.getModel())
|
||||
|| chatRequest.getModel().startsWith("gpt-4-gizmo")
|
||||
|| chatRequest.getModel().startsWith("net-")
|
||||
){
|
||||
return sseService.transitChat(chatRequest);
|
||||
}
|
||||
if("azure-gpt-3.5".equals(chatRequest.getModel())){
|
||||
return sseService.azureChat(chatRequest);
|
||||
}
|
||||
return sseService.sseChat(chatRequest);
|
||||
public SseEmitter sseChat(@RequestBody @Valid ChatRequest chatRequest, HttpServletResponse response) {
|
||||
return ISseService.sseChat(chatRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* 语音转文本
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
@PostMapping("/audio")
|
||||
@ResponseBody
|
||||
public WhisperResponse audio(@RequestParam("file") MultipartFile file) {
|
||||
WhisperResponse whisperResponse = ISseService.speechToTextTranscriptionsV2(file);
|
||||
return whisperResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本转语音
|
||||
*
|
||||
* @param textToSpeech
|
||||
*/
|
||||
@PostMapping("/speech")
|
||||
@ResponseBody
|
||||
public ResponseEntity<Resource> speech(@RequestBody TextToSpeech textToSpeech) {
|
||||
return ISseService.textToSpeed(textToSpeech);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping("/dall3")
|
||||
@ResponseBody
|
||||
public R<List<Item>> dall3(@RequestBody @Valid Dall3Request request) {
|
||||
return R.ok(sseService.dall3(request));
|
||||
return R.ok(ISseService.dall3(request));
|
||||
}
|
||||
|
||||
/**
|
||||
* 扣除mj绘图费用
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@PostMapping("/mjTask")
|
||||
@ResponseBody
|
||||
public R<String> mjTask() {
|
||||
sseService.mjTask();
|
||||
public R<String> mjTask(@RequestBody MjTaskRequest mjTaskRequest) {
|
||||
ISseService.mjTask(mjTaskRequest);
|
||||
return R.ok();
|
||||
}
|
||||
|
||||
@@ -77,7 +111,7 @@ public class ChatController {
|
||||
*/
|
||||
@PostMapping("/chatList")
|
||||
@ResponseBody
|
||||
public R<TableDataInfo<ChatMessageVo>> list(@RequestBody @Valid ChatMessageBo chatRequest,@RequestBody PageQuery pageQuery) {
|
||||
public R<TableDataInfo<ChatMessageVo>> list(@RequestBody @Valid ChatMessageBo chatRequest, @RequestBody PageQuery pageQuery) {
|
||||
// 默认查询当前登录用户消息记录
|
||||
LoginUser loginUser = LoginHelper.getLoginUser();
|
||||
if (loginUser == null) {
|
||||
|
||||
@@ -73,7 +73,6 @@ public class PayController {
|
||||
return R.ok(paymentOrdersVo);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 跳转通知地址
|
||||
*
|
||||
@@ -138,6 +137,9 @@ public class PayController {
|
||||
BeanUtil.copyProperties(paymentOrdersVo,paymentOrdersBo);
|
||||
paymentOrdersService.updateByBo(paymentOrdersBo);
|
||||
SysUserVo sysUserVo = userService.selectUserById(paymentOrdersVo.getUserId());
|
||||
if(money>9.9){
|
||||
money = money*2;
|
||||
}
|
||||
sysUserVo.setUserBalance(sysUserVo.getUserBalance()+money);
|
||||
SysUserBo sysUserBo = new SysUserBo();
|
||||
BeanUtil.copyProperties(sysUserVo,sysUserBo);
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.xmzs.controller;
|
||||
|
||||
import cn.dev33.satoken.annotation.SaIgnore;
|
||||
import com.xmzs.common.wechat.Wechat;
|
||||
import com.xmzs.system.cofing.KeywordConfig;
|
||||
import com.xmzs.system.cofing.QqConfig;
|
||||
import com.xmzs.system.cofing.WechatConfig;
|
||||
import com.xmzs.system.handler.WechatMessageHandler;
|
||||
import com.xmzs.system.service.ISseService;
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 个人微信扩展控制器
|
||||
*
|
||||
* @author WangLe
|
||||
*/
|
||||
@SaIgnore
|
||||
@Slf4j
|
||||
@Validated
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
public class WeChatController {
|
||||
|
||||
@Getter
|
||||
private Wechat wechatBot;
|
||||
|
||||
private final WechatConfig wechatConfig;
|
||||
|
||||
private final ISseService sseService;
|
||||
|
||||
private final KeywordConfig keywordConfig;
|
||||
|
||||
/**
|
||||
* 获取微信登录二维码
|
||||
*
|
||||
*/
|
||||
@PostMapping("/getQr")
|
||||
public void getQr() {
|
||||
//微信
|
||||
if (wechatConfig.getEnable()){
|
||||
log.info("正在登录微信,请按提示操作:");
|
||||
wechatBot = new Wechat(new WechatMessageHandler(sseService, keywordConfig), wechatConfig.getQrPath());
|
||||
wechatBot.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ spring:
|
||||
# rewriteBatchedStatements=true 批处理优化 大幅提升批量插入更新删除性能(对数据库有性能损耗 使用批量操作应考虑性能问题)
|
||||
url: jdbc:mysql://127.0.0.1:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
||||
username: ry-vue
|
||||
password: ry-vue
|
||||
password: xxx
|
||||
|
||||
# 从库数据源
|
||||
# slave:
|
||||
|
||||
@@ -220,11 +220,11 @@ mail:
|
||||
# 是否需要用户名密码验证
|
||||
auth: true
|
||||
# 发送方,遵循RFC-822标准
|
||||
from: xxx@163.com
|
||||
from: ageerle@163.com
|
||||
# 用户名(注意:如果使用foxmail邮箱,此处user为qq号)
|
||||
user: xxx@163.com
|
||||
user: ageerle@163.com
|
||||
# 密码(填写授权码)
|
||||
pass: pass
|
||||
pass: TOGXBVPYFVPFRQMQ
|
||||
# 使用 STARTTLS安全连接,STARTTLS是对纯文本通信协议的扩展。
|
||||
starttlsEnable: true
|
||||
# 使用SSL安全连接
|
||||
@@ -318,14 +318,10 @@ websocket:
|
||||
path: ''
|
||||
# 设置访问源地址
|
||||
allowedOrigins: '*'
|
||||
# AI助手配置信息
|
||||
# chatgpt配置信息
|
||||
chat:
|
||||
apiKey: ''
|
||||
apiHost: ''
|
||||
# 中转接口
|
||||
transit:
|
||||
apiKey: ''
|
||||
apiHost: 'https://api.gptgod.online/'
|
||||
apiKey: 'sk-uMCP3lTg1dQ9L7Xs2bF352Fa216a4c9280577b205dE67e12'
|
||||
apiHost: 'https://api.pandarobot.chat/'
|
||||
# 微信小程序配置信息
|
||||
wx:
|
||||
miniapp:
|
||||
@@ -343,3 +339,37 @@ baidu:
|
||||
apiKey: '' # apiKey
|
||||
secretKey: '' # secretKey
|
||||
|
||||
wechat:
|
||||
# 是否使用微信 true/false
|
||||
enable: true
|
||||
# 生成的登录二维码路径 默认与项目同级
|
||||
qrPath: "./"
|
||||
|
||||
keyword:
|
||||
# 重置会话指令
|
||||
reset: "重置会话"
|
||||
# ai画图指令(DALL·E模型 https://platform.openai.com/docs/models/dall-e)
|
||||
# generation 根据关键词生成图片(https://platform.openai.com/docs/guides/images/generations)
|
||||
image: "ai画图"
|
||||
# ai语音指令(TTS模型 https://platform.openai.com/docs/api-reference/audio)
|
||||
audio: "ai语音"
|
||||
mj:
|
||||
api-secret:
|
||||
task-store:
|
||||
type: in_memory
|
||||
timeout: 30d
|
||||
translate-way: gpt
|
||||
# proxy:
|
||||
# host: 127.0.0.1
|
||||
# port: 10809
|
||||
ng-discord:
|
||||
server: https://discord.pandarobot.chat/
|
||||
cdn: https://app.pandarobot.chat/
|
||||
wss: https://gateway.pandarobot.chat/
|
||||
openai:
|
||||
gpt-api-url: 'https://api.pandarobot.chat/'
|
||||
gpt-api-key: 'sk-xxx'
|
||||
accounts:
|
||||
- guild-id: 'xxxxx'
|
||||
channel-id: 'xxxxx'
|
||||
user-token: 'xx.xx'
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
<module>ruoyi-common-tenant</module>
|
||||
<module>ruoyi-common-chat</module>
|
||||
<module>ruoyi-common-pay</module>
|
||||
<module>ruoyi-common-wechat</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
|
||||
@@ -166,6 +166,13 @@
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 微信模块 -->
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>ruoyi-common-wechat</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 支付模块 -->
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
|
||||
@@ -70,13 +70,6 @@
|
||||
<version>0.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- azure-ai -->
|
||||
<dependency>
|
||||
<groupId>com.azure</groupId>
|
||||
<artifactId>azure-ai-openai</artifactId>
|
||||
<version>1.0.0-beta.6</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
|
||||
@@ -12,15 +12,17 @@ public class OpenAIConst {
|
||||
|
||||
public final static int SUCCEED_CODE = 200;
|
||||
|
||||
public final static double GPT3_COST = 0.03;
|
||||
public final static double GPT3_COST = 0.05;
|
||||
|
||||
public final static double GPT4_COST = 0.3;
|
||||
|
||||
public final static double GPT4_ALL_COST = 0.3;
|
||||
|
||||
/** 绘图费用 */
|
||||
public final static double DALL3_COST = 0.3;
|
||||
public final static double DALL3_COST = 0.4;
|
||||
|
||||
/** 绘图费用-高清 */
|
||||
public final static double DALL3_HD_COST = 0.6;
|
||||
public final static double DALL3_HD_COST = 0.8;
|
||||
|
||||
/** mdjourney绘图费用 */
|
||||
public final static double MJ_COST = 0.3;
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.xmzs.common.chat.domain.request;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* mj任务请求实体类
|
||||
*
|
||||
* @author WangLe
|
||||
*/
|
||||
@Data
|
||||
public class MjTaskRequest {
|
||||
|
||||
private String prompt;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.xmzs.common.chat.entity.Tts;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.*;
|
||||
|
||||
@Data
|
||||
@Builder
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TextToSpeech {
|
||||
|
||||
@Builder.Default
|
||||
private String model = Model.TTS_1.getName();
|
||||
/**
|
||||
* 音频声音源
|
||||
*
|
||||
* @see TtsVoice
|
||||
*/
|
||||
private String voice;
|
||||
/**
|
||||
* 输入内容
|
||||
*/
|
||||
private String input;
|
||||
/**
|
||||
* 输出音频文件格式
|
||||
*
|
||||
* @see TtsFormat
|
||||
*/
|
||||
@JsonProperty("response_format")
|
||||
private String responseFormat;
|
||||
/**
|
||||
* 速度调节,默认是1,取值范围0.25——4.0
|
||||
*/
|
||||
private Double speed;
|
||||
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum Model {
|
||||
TTS_1("tts-1"),
|
||||
TTS_1_HD("tts-1-hd"),
|
||||
;
|
||||
private final String name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.xmzs.common.chat.entity.Tts;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum TtsFormat {
|
||||
MP3("mp3"),
|
||||
OPUS("opus"),
|
||||
AAC("aac"),
|
||||
FLAC("flac"),
|
||||
;
|
||||
private final String name;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package com.xmzs.common.chat.entity.Tts;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 生成不同声音的音频
|
||||
* <p>具体语音效果参考:https://platform.openai.com/docs/guides/text-to-speech</p>
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum TtsVoice {
|
||||
|
||||
ALLOY("alloy"),
|
||||
ECHO("echo"),
|
||||
FABLE("fable"),
|
||||
ONYX("onyx"),
|
||||
NOVA("nova"),
|
||||
SHIMMER("shimmer"),
|
||||
;
|
||||
|
||||
private final String name;
|
||||
}
|
||||
@@ -174,6 +174,10 @@ public class BaseChatCompletion implements Serializable {
|
||||
* gpt-3.5-turbo-16k-0613 超长上下文 支持函数
|
||||
*/
|
||||
GPT_3_5_TURBO_16K_0613("gpt-3.5-turbo-16k-0613"),
|
||||
/**
|
||||
* gpt-3.5-turbo-0125 超长上下文 支持函数
|
||||
*/
|
||||
GPT_3_5_TURBO_0125("gpt-3.5-turbo-0125"),
|
||||
/**
|
||||
* GPT4.0
|
||||
*/
|
||||
@@ -209,6 +213,10 @@ public class BaseChatCompletion implements Serializable {
|
||||
* 支持图片
|
||||
*/
|
||||
GPT_4_VISION_PREVIEW("gpt-4-vision-preview"),
|
||||
/**
|
||||
* gpt-4-0613,支持函数
|
||||
*/
|
||||
GPT_4_0125_PREVIEW("gpt-4-0125-preview"),
|
||||
;
|
||||
private final String name;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.xmzs.common.chat.openai;
|
||||
|
||||
import com.xmzs.common.chat.entity.Tts.TextToSpeech;
|
||||
import com.xmzs.common.chat.entity.chat.ChatCompletionWithPicture;
|
||||
import io.reactivex.Single;
|
||||
import okhttp3.MultipartBody;
|
||||
@@ -33,6 +34,7 @@ import com.xmzs.common.chat.entity.models.ModelResponse;
|
||||
import com.xmzs.common.chat.entity.moderations.Moderation;
|
||||
import com.xmzs.common.chat.entity.moderations.ModerationResponse;
|
||||
import com.xmzs.common.chat.entity.whisper.WhisperResponse;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
@@ -340,4 +342,15 @@ public interface OpenAiApi {
|
||||
*/
|
||||
@POST("v1/chat/completions")
|
||||
Single<ChatCompletionResponse> chatCompletionWithPicture(@Body ChatCompletionWithPicture chatCompletion);
|
||||
|
||||
/**
|
||||
* 文本转语音
|
||||
*
|
||||
* @param textToSpeech 参数
|
||||
* @return ResponseBody body
|
||||
* @since 1.1.2
|
||||
*/
|
||||
@POST("v1/audio/speech")
|
||||
@Streaming
|
||||
Call<ResponseBody> textToSpeech(@Body TextToSpeech textToSpeech);
|
||||
}
|
||||
|
||||
@@ -668,7 +668,6 @@ public class OpenAiClient {
|
||||
return this.chatCompletion(chatCompletion);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 语音翻译:目前仅支持翻译为英文
|
||||
*
|
||||
|
||||
@@ -5,6 +5,7 @@ import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.http.ContentType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.xmzs.common.chat.config.LocalCache;
|
||||
import com.xmzs.common.chat.entity.Tts.TextToSpeech;
|
||||
import com.xmzs.common.chat.entity.billing.BillingUsage;
|
||||
import com.xmzs.common.chat.entity.billing.KeyInfo;
|
||||
import com.xmzs.common.chat.entity.billing.Subscription;
|
||||
@@ -37,10 +38,12 @@ import com.xmzs.common.chat.entity.chat.ChatCompletion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
|
||||
import retrofit2.converter.jackson.JacksonConverterFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
@@ -344,6 +347,67 @@ public class OpenAiStreamClient {
|
||||
Transcriptions transcriptions = Transcriptions.builder().build();
|
||||
return this.speechToTextTranscriptions(file, transcriptions);
|
||||
}
|
||||
/**
|
||||
* 文本转语音(异步)
|
||||
*
|
||||
* @param textToSpeech 参数
|
||||
* @param callback 返回值接收
|
||||
* @since 1.1.2
|
||||
*/
|
||||
public void textToSpeech(TextToSpeech textToSpeech, retrofit2.Callback callback) {
|
||||
retrofit2.Call<ResponseBody> responseBody = this.openAiApi.textToSpeech(textToSpeech);
|
||||
responseBody.enqueue(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本转语音(同步)
|
||||
*
|
||||
* @param textToSpeech 参数
|
||||
* @since 1.1.3
|
||||
*/
|
||||
public ResponseBody textToSpeech(TextToSpeech textToSpeech){
|
||||
Call<ResponseBody> responseBody = this.openAiApi.textToSpeech(textToSpeech);
|
||||
try {
|
||||
return responseBody.execute().body();
|
||||
} catch (IOException e) {
|
||||
throw new BaseException("文本转语音(同步)失败: "+e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 文本转语音(克隆)
|
||||
*
|
||||
* @param textToSpeech
|
||||
* @return
|
||||
*/
|
||||
public ResponseBody textToSpeechClone(TextToSpeech textToSpeech) {
|
||||
String baseUrl = "http://localhost:8081";
|
||||
String spk = "三月七";
|
||||
String text = textToSpeech.getInput();
|
||||
String lang = "zh";
|
||||
|
||||
// 创建OkHttpClient实例
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
|
||||
// 构建请求URL
|
||||
HttpUrl.Builder urlBuilder = HttpUrl.parse(baseUrl).newBuilder();
|
||||
urlBuilder.addQueryParameter("spk", spk);
|
||||
urlBuilder.addQueryParameter("text", text);
|
||||
urlBuilder.addQueryParameter("lang", lang);
|
||||
String url = urlBuilder.build().toString();
|
||||
|
||||
// 创建请求对象
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.build();
|
||||
// 发送请求并处理响应
|
||||
try {
|
||||
return client.newCall(request).execute().body();
|
||||
} catch (IOException e) {
|
||||
throw new BaseException("语音克隆失败!{}",e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构造
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.knuddels.jtokkit.api.Encoding;
|
||||
import com.knuddels.jtokkit.api.EncodingRegistry;
|
||||
import com.knuddels.jtokkit.api.EncodingType;
|
||||
import com.knuddels.jtokkit.api.ModelType;
|
||||
import com.xmzs.common.chat.entity.chat.BaseChatCompletion;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import com.xmzs.common.chat.entity.chat.ChatCompletion;
|
||||
@@ -39,11 +40,13 @@ public class TikTokensUtil {
|
||||
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_0613.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
||||
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_16K.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
||||
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_16K_0613.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
||||
modelMap.put(ChatCompletion.Model.GPT_3_5_TURBO_0125.getName(), registry.getEncodingForModel(ModelType.GPT_3_5_TURBO));
|
||||
modelMap.put(ChatCompletion.Model.GPT_4_32K.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||
modelMap.put(ChatCompletion.Model.GPT_4_0613.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||
modelMap.put(ChatCompletion.Model.GPT_4_32K_0613.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||
modelMap.put(ChatCompletion.Model.GPT_4_1106_PREVIEW.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||
modelMap.put(ChatCompletion.Model.GPT_4_VISION_PREVIEW.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||
modelMap.put(ChatCompletion.Model.GPT_4_0125_PREVIEW.getName(), registry.getEncodingForModel(ModelType.GPT_4));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,28 +10,33 @@ public class PayConfig {
|
||||
/**
|
||||
* 商户ID
|
||||
*/
|
||||
public static String pid = "xx";
|
||||
public static String pid = "xxx";
|
||||
|
||||
/**
|
||||
* 接口地址
|
||||
*/
|
||||
public static String payUrl = "https://pay-cloud.vip/mapi.php";
|
||||
|
||||
/**
|
||||
* 私钥
|
||||
*/
|
||||
public static String key = "xxx";
|
||||
|
||||
/**
|
||||
* 服务器异步通知地址
|
||||
*/
|
||||
public static String notify_url = "https://www.pandarobot.chat/pay/returnUrl";
|
||||
|
||||
/**
|
||||
* 页面跳转通知地址
|
||||
*/
|
||||
public static String return_url = "https://www.pandarobot.chat/pay/notifyUrl";
|
||||
|
||||
/**
|
||||
* 支付方式
|
||||
*/
|
||||
public static String type = "wxpay";
|
||||
|
||||
/**
|
||||
* 接口地址
|
||||
*/
|
||||
public static String payUrl = "https://pay.bluetuo.com/mapi.php";
|
||||
|
||||
/**
|
||||
* 服务器异步通知地址
|
||||
*/
|
||||
public static String notify_url = "http://xx/pay/returnUrl";
|
||||
|
||||
/**
|
||||
* 页面跳转通知地址
|
||||
*/
|
||||
public static String return_url = "http://xx/pay/notifyUrl";
|
||||
|
||||
/**
|
||||
* 设备类型
|
||||
*/
|
||||
@@ -40,12 +45,6 @@ public class PayConfig {
|
||||
/**
|
||||
* 加密方式默认MD5
|
||||
*/
|
||||
|
||||
public static String sign_type = "MD5";
|
||||
|
||||
/**
|
||||
* 私钥
|
||||
*/
|
||||
public static String key = "xx";
|
||||
|
||||
}
|
||||
|
||||
@@ -19,5 +19,4 @@ public interface PayService {
|
||||
* @return String
|
||||
**/
|
||||
String getPayUrl(String orderNo, String name, double money, String clientIp);
|
||||
|
||||
}
|
||||
|
||||
@@ -40,5 +40,4 @@ public class PayServiceImpl implements PayService {
|
||||
JSONObject jsonObject = new JSONObject(body);
|
||||
return (String) jsonObject.get("qrcode");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,8 +30,10 @@ public class AllUrlHandler implements InitializingBean {
|
||||
Map<RequestMappingInfo, HandlerMethod> map = mapping.getHandlerMethods();
|
||||
map.keySet().forEach(info -> {
|
||||
// 获取注解上边的 path 替代 path variable 为 *
|
||||
Objects.requireNonNull(info.getPathPatternsCondition().getPatterns())
|
||||
if(info.getPathPatternsCondition()!=null){
|
||||
Objects.requireNonNull(info.getPathPatternsCondition().getPatterns())
|
||||
.forEach(url -> set.add(ReUtil.replaceAll(url.getPatternString(), PATTERN, "*")));
|
||||
}
|
||||
});
|
||||
urls.addAll(set);
|
||||
}
|
||||
|
||||
51
ruoyi-common/ruoyi-common-wechat/pom.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>ruoyi-common</artifactId>
|
||||
<version>${revision}</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>ruoyi-common-wechat</artifactId>
|
||||
|
||||
<description>
|
||||
ruoyi-common-wechat 微信服务
|
||||
</description>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<!-- emoji -->
|
||||
<dependency>
|
||||
<groupId>com.vdurmont</groupId>
|
||||
<artifactId>emoji-java</artifactId>
|
||||
<version>3.2.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/javax.activation/activation -->
|
||||
<dependency>
|
||||
<groupId>javax.activation</groupId>
|
||||
<artifactId>activation</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- qq -->
|
||||
<dependency>
|
||||
<groupId>net.mamoe</groupId>
|
||||
<artifactId>mirai-core-jvm</artifactId>
|
||||
<version>2.16.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>ruoyi-common-json</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.xmzs.common.wechat;
|
||||
|
||||
import com.xmzs.common.wechat.controller.LoginController;
|
||||
import com.xmzs.common.wechat.core.MsgCenter;
|
||||
import com.xmzs.common.wechat.face.IMsgHandlerFace;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
|
||||
|
||||
public class Wechat {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Wechat.class);
|
||||
private IMsgHandlerFace msgHandler;
|
||||
|
||||
public Wechat(IMsgHandlerFace msgHandler, String qrPath) {
|
||||
System.setProperty("jsse.enableSNIExtension", "false"); // 防止SSL错误
|
||||
this.msgHandler = msgHandler;
|
||||
|
||||
// 登陆
|
||||
LoginController login = new LoginController();
|
||||
login.login(qrPath);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
LOG.info("+++++++++++++++++++开始消息处理+++++++++++++++++++++");
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
MsgCenter.handleMsg(msgHandler);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.xmzs.common.wechat.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.MediaType;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
|
||||
/**
|
||||
* 辅助工具类,该类暂时未用,请忽略
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年5月22日 下午10:34:46
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class AssistTools {
|
||||
private static OkHttpClient client = new OkHttpClient();
|
||||
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
|
||||
|
||||
public static boolean sendQrPicToServer(String username, String password, String uploadUrl, String localPath)
|
||||
throws IOException {
|
||||
File file = new File(localPath);
|
||||
RequestBody requestBody = new MultipartBody.Builder().addFormDataPart("username", username)
|
||||
.addFormDataPart("password", password)
|
||||
.addFormDataPart("file", file.getName(), RequestBody.create(MEDIA_TYPE_PNG, file)).build();
|
||||
Request request = new Request.Builder().url(uploadUrl).post(requestBody).build();
|
||||
Call call = client.newCall(request);
|
||||
try {
|
||||
Response response = call.execute();
|
||||
System.out.println(response.body().string());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,424 @@
|
||||
package com.xmzs.common.wechat.api;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.http.Consts;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.entity.ContentType;
|
||||
import org.apache.http.entity.mime.HttpMultipartMode;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||
import com.xmzs.common.wechat.beans.RecommendInfo;
|
||||
import com.xmzs.common.wechat.core.Core;
|
||||
import com.xmzs.common.wechat.utils.Config;
|
||||
import com.xmzs.common.wechat.utils.MyHttpClient;
|
||||
import com.xmzs.common.wechat.utils.enums.StorageLoginInfoEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.URLEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.VerifyFriendEnum;
|
||||
|
||||
/**
|
||||
* 消息处理类
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年4月23日 下午2:30:37
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class MessageTools {
|
||||
private static Logger LOG = LoggerFactory.getLogger(MessageTools.class);
|
||||
private static Core core = Core.getInstance();
|
||||
private static MyHttpClient myHttpClient = core.getMyHttpClient();
|
||||
|
||||
/**
|
||||
* 根据UserName发送文本消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月4日 下午11:17:38
|
||||
* @param msg
|
||||
* @param toUserName
|
||||
*/
|
||||
private static void sendMsg(String text, String toUserName) {
|
||||
if (text == null) {
|
||||
return;
|
||||
}
|
||||
LOG.info(String.format("发送消息 %s: %s", toUserName, text));
|
||||
webWxSendMsg(1, text, toUserName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据ID发送文本消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月6日 上午11:45:51
|
||||
* @param text
|
||||
* @param id
|
||||
*/
|
||||
public static void sendMsgById(String text, String id) {
|
||||
if (text == null) {
|
||||
return;
|
||||
}
|
||||
sendMsg(text, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据NickName发送文本消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月4日 下午11:17:38
|
||||
* @param text
|
||||
* @param nickName
|
||||
*/
|
||||
public static boolean sendMsgByNickName(String text, String nickName) {
|
||||
if (nickName != null) {
|
||||
String toUserName = WechatTools.getUserNameByNickName(nickName);
|
||||
if (toUserName != null) {
|
||||
webWxSendMsg(1, text, toUserName);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息发送
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月23日 下午2:32:02
|
||||
* @param msgType
|
||||
* @param content
|
||||
* @param toUserName
|
||||
*/
|
||||
public static void webWxSendMsg(int msgType, String content, String toUserName) {
|
||||
String url = String.format(URLEnum.WEB_WX_SEND_MSG.getUrl(), core.getLoginInfo().get("url"));
|
||||
Map<String, Object> msgMap = new HashMap<String, Object>();
|
||||
msgMap.put("Type", msgType);
|
||||
msgMap.put("Content", content);
|
||||
msgMap.put("FromUserName", core.getUserName());
|
||||
msgMap.put("ToUserName", toUserName == null ? core.getUserName() : toUserName);
|
||||
msgMap.put("LocalID", new Date().getTime() * 10);
|
||||
msgMap.put("ClientMsgId", new Date().getTime() * 10);
|
||||
Map<String, Object> paramMap = core.getParamMap();
|
||||
paramMap.put("Msg", msgMap);
|
||||
paramMap.put("Scene", 0);
|
||||
try {
|
||||
String paramStr = JSON.toJSONString(paramMap);
|
||||
HttpEntity entity = myHttpClient.doPost(url, paramStr);
|
||||
EntityUtils.toString(entity, Consts.UTF_8);
|
||||
} catch (Exception e) {
|
||||
LOG.error("webWxSendMsg", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传多媒体文件到 微信服务器,目前应该支持3种类型: 1. pic 直接显示,包含图片,表情 2.video 3.doc 显示为文件,包含PDF等
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月7日 上午12:41:13
|
||||
* @param filePath
|
||||
* @return
|
||||
*/
|
||||
private static JSONObject webWxUploadMedia(String filePath) {
|
||||
File file = new File(filePath);
|
||||
if (!file.exists() && file.isFile()) {
|
||||
LOG.info("file is not exist");
|
||||
return null;
|
||||
}
|
||||
String url = String.format(URLEnum.WEB_WX_UPLOAD_MEDIA.getUrl(), core.getLoginInfo().get("fileUrl"));
|
||||
String mimeType = new MimetypesFileTypeMap().getContentType(file);
|
||||
String mediaType = "";
|
||||
if (mimeType == null) {
|
||||
mimeType = "text/plain";
|
||||
} else {
|
||||
mediaType = mimeType.split("/")[0].equals("image") ? "pic" : "doc";
|
||||
}
|
||||
String lastModifieDate = new SimpleDateFormat("yyyy MM dd HH:mm:ss").format(new Date());
|
||||
long fileSize = file.length();
|
||||
String passTicket = (String) core.getLoginInfo().get("pass_ticket");
|
||||
String clientMediaId = new Date().getTime()
|
||||
+ String.valueOf(new Random().nextLong()).substring(0, 4);
|
||||
String webwxDataTicket = MyHttpClient.getCookie("webwx_data_ticket");
|
||||
if (webwxDataTicket == null) {
|
||||
LOG.error("get cookie webwx_data_ticket error");
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, Object> paramMap = core.getParamMap();
|
||||
|
||||
paramMap.put("ClientMediaId", clientMediaId);
|
||||
paramMap.put("TotalLen", fileSize);
|
||||
paramMap.put("StartPos", 0);
|
||||
paramMap.put("DataLen", fileSize);
|
||||
paramMap.put("MediaType", 4);
|
||||
|
||||
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
|
||||
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
|
||||
|
||||
builder.addTextBody("id", "WU_FILE_0", ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("name", filePath, ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("type", mimeType, ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("lastModifieDate", lastModifieDate, ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("size", String.valueOf(fileSize), ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("mediatype", mediaType, ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("uploadmediarequest", JSON.toJSONString(paramMap), ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("webwx_data_ticket", webwxDataTicket, ContentType.TEXT_PLAIN);
|
||||
builder.addTextBody("pass_ticket", passTicket, ContentType.TEXT_PLAIN);
|
||||
builder.addBinaryBody("filename", file, ContentType.create(mimeType), filePath);
|
||||
HttpEntity reqEntity = builder.build();
|
||||
HttpEntity entity = myHttpClient.doPostFile(url, reqEntity);
|
||||
if (entity != null) {
|
||||
try {
|
||||
String result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||
return JSON.parseObject(result);
|
||||
} catch (Exception e) {
|
||||
LOG.error("webWxUploadMedia 错误: ", e);
|
||||
}
|
||||
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据NickName发送图片消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月7日 下午10:32:45
|
||||
* @param nackName
|
||||
* @return
|
||||
*/
|
||||
public static boolean sendPicMsgByNickName(String nickName, String filePath) {
|
||||
String toUserName = WechatTools.getUserNameByNickName(nickName);
|
||||
if (toUserName != null) {
|
||||
return sendPicMsgByUserId(toUserName, filePath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户id发送图片消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月7日 下午10:34:24
|
||||
* @param userId
|
||||
* @param filePath
|
||||
* @return
|
||||
*/
|
||||
public static boolean sendPicMsgByUserId(String userId, String filePath) {
|
||||
JSONObject responseObj = webWxUploadMedia(filePath);
|
||||
if (responseObj != null) {
|
||||
String mediaId = responseObj.getString("MediaId");
|
||||
if (mediaId != null) {
|
||||
return webWxSendMsgImg(userId, mediaId);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送图片消息,内部调用
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月7日 下午10:38:55
|
||||
* @return
|
||||
*/
|
||||
private static boolean webWxSendMsgImg(String userId, String mediaId) {
|
||||
String url = String.format("%s/webwxsendmsgimg?fun=async&f=json&pass_ticket=%s", core.getLoginInfo().get("url"),
|
||||
core.getLoginInfo().get("pass_ticket"));
|
||||
Map<String, Object> msgMap = new HashMap<String, Object>();
|
||||
msgMap.put("Type", 3);
|
||||
msgMap.put("MediaId", mediaId);
|
||||
msgMap.put("FromUserName", core.getUserSelf().getString("UserName"));
|
||||
msgMap.put("ToUserName", userId);
|
||||
String clientMsgId = String.valueOf(new Date().getTime())
|
||||
+ String.valueOf(new Random().nextLong()).substring(1, 5);
|
||||
msgMap.put("LocalID", clientMsgId);
|
||||
msgMap.put("ClientMsgId", clientMsgId);
|
||||
Map<String, Object> paramMap = core.getParamMap();
|
||||
paramMap.put("BaseRequest", core.getParamMap().get("BaseRequest"));
|
||||
paramMap.put("Msg", msgMap);
|
||||
String paramStr = JSON.toJSONString(paramMap);
|
||||
HttpEntity entity = myHttpClient.doPost(url, paramStr);
|
||||
if (entity != null) {
|
||||
try {
|
||||
String result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||
return JSON.parseObject(result).getJSONObject("BaseResponse").getInteger("Ret") == 0;
|
||||
} catch (Exception e) {
|
||||
LOG.error("webWxSendMsgImg 错误: ", e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户id发送文件
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月7日 下午11:57:36
|
||||
* @param userId
|
||||
* @param filePath
|
||||
* @return
|
||||
*/
|
||||
public static boolean sendFileMsgByUserId(String userId, String filePath) {
|
||||
String title = new File(filePath).getName();
|
||||
Map<String, String> data = new HashMap<String, String>();
|
||||
data.put("appid", Config.API_WXAPPID);
|
||||
data.put("title", title);
|
||||
data.put("totallen", "");
|
||||
data.put("attachid", "");
|
||||
data.put("type", "6"); // APPMSGTYPE_ATTACH
|
||||
data.put("fileext", title.split("\\.")[1]); // 文件后缀
|
||||
JSONObject responseObj = webWxUploadMedia(filePath);
|
||||
if (responseObj != null) {
|
||||
data.put("totallen", responseObj.getString("StartPos"));
|
||||
data.put("attachid", responseObj.getString("MediaId"));
|
||||
} else {
|
||||
LOG.error("sednFileMsgByUserId 错误: ", data);
|
||||
}
|
||||
return webWxSendAppMsg(userId, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据用户昵称发送文件消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月10日 下午10:59:27
|
||||
* @param nickName
|
||||
* @param filePath
|
||||
* @return
|
||||
*/
|
||||
public static boolean sendFileMsgByNickName(String nickName, String filePath) {
|
||||
String toUserName = WechatTools.getUserNameByNickName(nickName);
|
||||
if (toUserName != null) {
|
||||
return sendFileMsgByUserId(toUserName, filePath);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内部调用
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月10日 上午12:21:28
|
||||
* @param userId
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
private static boolean webWxSendAppMsg(String userId, Map<String, String> data) {
|
||||
String url = String.format("%s/webwxsendappmsg?fun=async&f=json&pass_ticket=%s", core.getLoginInfo().get("url"),
|
||||
core.getLoginInfo().get("pass_ticket"));
|
||||
String clientMsgId = String.valueOf(new Date().getTime())
|
||||
+ String.valueOf(new Random().nextLong()).substring(1, 5);
|
||||
String content = "<appmsg appid='wxeb7ec651dd0aefa9' sdkver=''><title>" + data.get("title")
|
||||
+ "</title><des></des><action></action><type>6</type><content></content><url></url><lowurl></lowurl>"
|
||||
+ "<appattach><totallen>" + data.get("totallen") + "</totallen><attachid>" + data.get("attachid")
|
||||
+ "</attachid><fileext>" + data.get("fileext") + "</fileext></appattach><extinfo></extinfo></appmsg>";
|
||||
Map<String, Object> msgMap = new HashMap<String, Object>();
|
||||
msgMap.put("Type", data.get("type"));
|
||||
msgMap.put("Content", content);
|
||||
msgMap.put("FromUserName", core.getUserSelf().getString("UserName"));
|
||||
msgMap.put("ToUserName", userId);
|
||||
msgMap.put("LocalID", clientMsgId);
|
||||
msgMap.put("ClientMsgId", clientMsgId);
|
||||
/*
|
||||
* Map<String, Object> paramMap = new HashMap<String, Object>();
|
||||
*
|
||||
* @SuppressWarnings("unchecked") Map<String, Map<String, String>>
|
||||
* baseRequestMap = (Map<String, Map<String, String>>)
|
||||
* core.getLoginInfo() .get("baseRequest"); paramMap.put("BaseRequest",
|
||||
* baseRequestMap.get("BaseRequest"));
|
||||
*/
|
||||
|
||||
Map<String, Object> paramMap = core.getParamMap();
|
||||
paramMap.put("Msg", msgMap);
|
||||
paramMap.put("Scene", 0);
|
||||
String paramStr = JSON.toJSONString(paramMap);
|
||||
HttpEntity entity = myHttpClient.doPost(url, paramStr);
|
||||
if (entity != null) {
|
||||
try {
|
||||
String result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||
return JSON.parseObject(result).getJSONObject("BaseResponse").getInteger("Ret") == 0;
|
||||
} catch (Exception e) {
|
||||
LOG.error("错误: ", e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 被动添加好友
|
||||
*
|
||||
* @date 2017年6月29日 下午10:08:43
|
||||
* @param msg
|
||||
* @param accept
|
||||
* true 接受 false 拒绝
|
||||
*/
|
||||
public static void addFriend(BaseMsg msg, boolean accept) {
|
||||
if (!accept) { // 不添加
|
||||
return;
|
||||
}
|
||||
int status = VerifyFriendEnum.ACCEPT.getCode(); // 接受好友请求
|
||||
RecommendInfo recommendInfo = msg.getRecommendInfo();
|
||||
String userName = recommendInfo.getUserName();
|
||||
String ticket = recommendInfo.getTicket();
|
||||
// 更新好友列表
|
||||
// TODO 此处需要更新好友列表
|
||||
// core.getContactList().add(msg.getJSONObject("RecommendInfo"));
|
||||
|
||||
String url = String.format(URLEnum.WEB_WX_VERIFYUSER.getUrl(), core.getLoginInfo().get("url"),
|
||||
String.valueOf(System.currentTimeMillis() / 3158L), core.getLoginInfo().get("pass_ticket"));
|
||||
|
||||
List<Map<String, Object>> verifyUserList = new ArrayList<Map<String, Object>>();
|
||||
Map<String, Object> verifyUser = new HashMap<String, Object>();
|
||||
verifyUser.put("Value", userName);
|
||||
verifyUser.put("VerifyUserTicket", ticket);
|
||||
verifyUserList.add(verifyUser);
|
||||
|
||||
List<Integer> sceneList = new ArrayList<Integer>();
|
||||
sceneList.add(33);
|
||||
|
||||
JSONObject body = new JSONObject();
|
||||
body.put("BaseRequest", core.getParamMap().get("BaseRequest"));
|
||||
body.put("Opcode", status);
|
||||
body.put("VerifyUserListSize", 1);
|
||||
body.put("VerifyUserList", verifyUserList);
|
||||
body.put("VerifyContent", "");
|
||||
body.put("SceneListCount", 1);
|
||||
body.put("SceneList", sceneList);
|
||||
body.put("skey", core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()));
|
||||
|
||||
String result = null;
|
||||
try {
|
||||
String paramStr = JSON.toJSONString(body);
|
||||
HttpEntity entity = myHttpClient.doPost(url, paramStr);
|
||||
result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||
} catch (Exception e) {
|
||||
LOG.error("webWxSendMsg", e);
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(result)) {
|
||||
LOG.error("被动添加好友失败");
|
||||
}
|
||||
|
||||
LOG.debug(result);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
package com.xmzs.common.wechat.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.http.Consts;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import com.xmzs.common.wechat.core.Core;
|
||||
import com.xmzs.common.wechat.utils.enums.StorageLoginInfoEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.URLEnum;
|
||||
|
||||
/**
|
||||
* 微信小工具,如获好友列表等
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年5月4日 下午10:49:16
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class WechatTools {
|
||||
private static Logger LOG = LoggerFactory.getLogger(WechatTools.class);
|
||||
|
||||
private static Core core = Core.getInstance();
|
||||
|
||||
/**
|
||||
* 根据用户名发送文本消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月4日 下午10:43:14
|
||||
* @param msg
|
||||
* @param toUserName
|
||||
*/
|
||||
public static void sendMsgByUserName(String msg, String toUserName) {
|
||||
MessageTools.sendMsgById(msg, toUserName);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 通过RealName获取本次UserName
|
||||
* </p>
|
||||
* <p>
|
||||
* 如NickName为"yaphone",则获取UserName=
|
||||
* "@1212d3356aea8285e5bbe7b91229936bc183780a8ffa469f2d638bf0d2e4fc63",
|
||||
* 可通过UserName发送消息
|
||||
* </p>
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月4日 下午10:56:31
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public static String getUserNameByNickName(String nickName) {
|
||||
for (JSONObject o : core.getContactList()) {
|
||||
if (o.getString("NickName").equals(nickName)) {
|
||||
return o.getString("UserName");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回好友昵称列表
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月4日 下午11:37:20
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getContactNickNameList() {
|
||||
List<String> contactNickNameList = new ArrayList<String>();
|
||||
for (JSONObject o : core.getContactList()) {
|
||||
contactNickNameList.add(o.getString("NickName"));
|
||||
}
|
||||
return contactNickNameList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回好友完整信息列表
|
||||
*
|
||||
* @date 2017年6月26日 下午9:45:39
|
||||
* @return
|
||||
*/
|
||||
public static List<JSONObject> getContactList() {
|
||||
return core.getContactList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回群列表
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月5日 下午9:55:21
|
||||
* @return
|
||||
*/
|
||||
public static List<JSONObject> getGroupList() {
|
||||
return core.getGroupList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群ID列表
|
||||
*
|
||||
* @date 2017年6月21日 下午11:42:56
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getGroupIdList() {
|
||||
return core.getGroupIdList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取群NickName列表
|
||||
*
|
||||
* @date 2017年6月21日 下午11:43:38
|
||||
* @return
|
||||
*/
|
||||
public static List<String> getGroupNickNameList() {
|
||||
return core.getGroupNickNameList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据groupIdList返回群成员列表
|
||||
*
|
||||
* @date 2017年6月13日 下午11:12:31
|
||||
* @param groupId
|
||||
* @return
|
||||
*/
|
||||
public static JSONArray getMemberListByGroupId(String groupId) {
|
||||
return core.getGroupMemeberMap().get(groupId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出微信
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月18日 下午11:56:54
|
||||
*/
|
||||
public static void logout() {
|
||||
webWxLogout();
|
||||
}
|
||||
|
||||
private static boolean webWxLogout() {
|
||||
String url = String.format(URLEnum.WEB_WX_LOGOUT.getUrl(),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()));
|
||||
List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
|
||||
params.add(new BasicNameValuePair("redirect", "1"));
|
||||
params.add(new BasicNameValuePair("type", "1"));
|
||||
params.add(
|
||||
new BasicNameValuePair("skey", (String) core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey())));
|
||||
try {
|
||||
HttpEntity entity = core.getMyHttpClient().doGet(url, params, false, null);
|
||||
String text = EntityUtils.toString(entity, Consts.UTF_8); // 无消息
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LOG.debug(e.getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void setUserInfo() {
|
||||
for (JSONObject o : core.getContactList()) {
|
||||
core.getUserInfoMap().put(o.getString("NickName"), o);
|
||||
core.getUserInfoMap().put(o.getString("UserName"), o);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 根据用户昵称设置备注名称
|
||||
*
|
||||
* @date 2017年5月27日 上午12:21:40
|
||||
* @param userName
|
||||
* @param remName
|
||||
*/
|
||||
public static void remarkNameByNickName(String nickName, String remName) {
|
||||
String url = String.format(URLEnum.WEB_WX_REMARKNAME.getUrl(), core.getLoginInfo().get("url"),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey()));
|
||||
Map<String, Object> msgMap = new HashMap<String, Object>();
|
||||
Map<String, Object> msgMap_BaseRequest = new HashMap<String, Object>();
|
||||
msgMap.put("CmdId", 2);
|
||||
msgMap.put("RemarkName", remName);
|
||||
msgMap.put("UserName", core.getUserInfoMap().get(nickName).get("UserName"));
|
||||
msgMap_BaseRequest.put("Uin", core.getLoginInfo().get(StorageLoginInfoEnum.wxuin.getKey()));
|
||||
msgMap_BaseRequest.put("Sid", core.getLoginInfo().get(StorageLoginInfoEnum.wxsid.getKey()));
|
||||
msgMap_BaseRequest.put("Skey", core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()));
|
||||
msgMap_BaseRequest.put("DeviceID", core.getLoginInfo().get(StorageLoginInfoEnum.deviceid.getKey()));
|
||||
msgMap.put("BaseRequest", msgMap_BaseRequest);
|
||||
try {
|
||||
String paramStr = JSON.toJSONString(msgMap);
|
||||
HttpEntity entity = core.getMyHttpClient().doPost(url, paramStr);
|
||||
// String result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||
LOG.info("修改备注" + remName);
|
||||
} catch (Exception e) {
|
||||
LOG.error("remarkNameByUserName", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取微信在线状态
|
||||
*
|
||||
* @date 2017年6月16日 上午12:47:46
|
||||
* @return
|
||||
*/
|
||||
public static boolean getWechatStatus() {
|
||||
return core.isAlive();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.xmzs.common.wechat.beans;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* AppInfo
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年7月3日 下午10:38:14
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class AppInfo implements Serializable {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
private int type;
|
||||
private String appId;
|
||||
|
||||
public int getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(int type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getAppId() {
|
||||
return appId;
|
||||
}
|
||||
|
||||
public void setAppId(String appId) {
|
||||
this.appId = appId;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
package com.xmzs.common.wechat.beans;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 收到的微信消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年7月3日 下午10:28:06
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class BaseMsg implements Serializable {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
private int subMsgType;
|
||||
private int voiceLength;
|
||||
private String fileName;
|
||||
private int imgHeight;
|
||||
private String toUserName;
|
||||
private int hasProductId;
|
||||
private int imgStatus;
|
||||
private String url;
|
||||
private int imgWidth;
|
||||
private int forwardFlag;
|
||||
private int status;
|
||||
private String Ticket;
|
||||
/** 推荐消息报文 **/
|
||||
private RecommendInfo recommendInfo;
|
||||
private long createTime;
|
||||
private String newMsgId;
|
||||
/** 文本消息内容 **/
|
||||
private String text;
|
||||
/** 消息类型 **/
|
||||
private int msgType;
|
||||
/** 是否为群消息 **/
|
||||
private boolean groupMsg;
|
||||
private String msgId;
|
||||
private int statusNotifyCode;
|
||||
private AppInfo appInfo;
|
||||
private int appMsgType;
|
||||
private String Type;
|
||||
private int playLength;
|
||||
private String mediaId;
|
||||
private String content;
|
||||
private String statusNotifyUserName;
|
||||
/** 消息发送者ID **/
|
||||
private String fromUserName;
|
||||
private String oriContent;
|
||||
private String fileSize;
|
||||
|
||||
public int getSubMsgType() {
|
||||
return subMsgType;
|
||||
}
|
||||
|
||||
public void setSubMsgType(int subMsgType) {
|
||||
this.subMsgType = subMsgType;
|
||||
}
|
||||
|
||||
public int getVoiceLength() {
|
||||
return voiceLength;
|
||||
}
|
||||
|
||||
public void setVoiceLength(int voiceLength) {
|
||||
this.voiceLength = voiceLength;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public int getImgHeight() {
|
||||
return imgHeight;
|
||||
}
|
||||
|
||||
public void setImgHeight(int imgHeight) {
|
||||
this.imgHeight = imgHeight;
|
||||
}
|
||||
|
||||
public String getToUserName() {
|
||||
return toUserName;
|
||||
}
|
||||
|
||||
public void setToUserName(String toUserName) {
|
||||
this.toUserName = toUserName;
|
||||
}
|
||||
|
||||
public int getHasProductId() {
|
||||
return hasProductId;
|
||||
}
|
||||
|
||||
public void setHasProductId(int hasProductId) {
|
||||
this.hasProductId = hasProductId;
|
||||
}
|
||||
|
||||
public int getImgStatus() {
|
||||
return imgStatus;
|
||||
}
|
||||
|
||||
public void setImgStatus(int imgStatus) {
|
||||
this.imgStatus = imgStatus;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public int getImgWidth() {
|
||||
return imgWidth;
|
||||
}
|
||||
|
||||
public void setImgWidth(int imgWidth) {
|
||||
this.imgWidth = imgWidth;
|
||||
}
|
||||
|
||||
public int getForwardFlag() {
|
||||
return forwardFlag;
|
||||
}
|
||||
|
||||
public void setForwardFlag(int forwardFlag) {
|
||||
this.forwardFlag = forwardFlag;
|
||||
}
|
||||
|
||||
public int getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getTicket() {
|
||||
return Ticket;
|
||||
}
|
||||
|
||||
public void setTicket(String ticket) {
|
||||
Ticket = ticket;
|
||||
}
|
||||
|
||||
public RecommendInfo getRecommendInfo() {
|
||||
return recommendInfo;
|
||||
}
|
||||
|
||||
public void setRecommendInfo(RecommendInfo recommendInfo) {
|
||||
this.recommendInfo = recommendInfo;
|
||||
}
|
||||
|
||||
public long getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(long createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public String getNewMsgId() {
|
||||
return newMsgId;
|
||||
}
|
||||
|
||||
public void setNewMsgId(String newMsgId) {
|
||||
this.newMsgId = newMsgId;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public int getMsgType() {
|
||||
return msgType;
|
||||
}
|
||||
|
||||
public void setMsgType(int msgType) {
|
||||
this.msgType = msgType;
|
||||
}
|
||||
|
||||
public boolean isGroupMsg() {
|
||||
return groupMsg;
|
||||
}
|
||||
|
||||
public void setGroupMsg(boolean groupMsg) {
|
||||
this.groupMsg = groupMsg;
|
||||
}
|
||||
|
||||
public String getMsgId() {
|
||||
return msgId;
|
||||
}
|
||||
|
||||
public void setMsgId(String msgId) {
|
||||
this.msgId = msgId;
|
||||
}
|
||||
|
||||
public int getStatusNotifyCode() {
|
||||
return statusNotifyCode;
|
||||
}
|
||||
|
||||
public void setStatusNotifyCode(int statusNotifyCode) {
|
||||
this.statusNotifyCode = statusNotifyCode;
|
||||
}
|
||||
|
||||
public AppInfo getAppInfo() {
|
||||
return appInfo;
|
||||
}
|
||||
|
||||
public void setAppInfo(AppInfo appInfo) {
|
||||
this.appInfo = appInfo;
|
||||
}
|
||||
|
||||
public int getAppMsgType() {
|
||||
return appMsgType;
|
||||
}
|
||||
|
||||
public void setAppMsgType(int appMsgType) {
|
||||
this.appMsgType = appMsgType;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return Type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
Type = type;
|
||||
}
|
||||
|
||||
public int getPlayLength() {
|
||||
return playLength;
|
||||
}
|
||||
|
||||
public void setPlayLength(int playLength) {
|
||||
this.playLength = playLength;
|
||||
}
|
||||
|
||||
public String getMediaId() {
|
||||
return mediaId;
|
||||
}
|
||||
|
||||
public void setMediaId(String mediaId) {
|
||||
this.mediaId = mediaId;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getStatusNotifyUserName() {
|
||||
return statusNotifyUserName;
|
||||
}
|
||||
|
||||
public void setStatusNotifyUserName(String statusNotifyUserName) {
|
||||
this.statusNotifyUserName = statusNotifyUserName;
|
||||
}
|
||||
|
||||
public String getFromUserName() {
|
||||
return fromUserName;
|
||||
}
|
||||
|
||||
public void setFromUserName(String fromUserName) {
|
||||
this.fromUserName = fromUserName;
|
||||
}
|
||||
|
||||
public String getOriContent() {
|
||||
return oriContent;
|
||||
}
|
||||
|
||||
public void setOriContent(String oriContent) {
|
||||
this.oriContent = oriContent;
|
||||
}
|
||||
|
||||
public String getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(String fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.xmzs.common.wechat.beans;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* RecommendInfo
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年7月3日 下午10:35:14
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class RecommendInfo implements Serializable {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String ticket;
|
||||
private String userName;
|
||||
private int sex;
|
||||
private int attrStatus;
|
||||
private String city;
|
||||
private String nickName;
|
||||
private int scene;
|
||||
private String province;
|
||||
private String content;
|
||||
private String alias;
|
||||
private String signature;
|
||||
private int opCode;
|
||||
private int qQNum;
|
||||
private int verifyFlag;
|
||||
|
||||
public String getTicket() {
|
||||
return ticket;
|
||||
}
|
||||
|
||||
public void setTicket(String ticket) {
|
||||
this.ticket = ticket;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public int getSex() {
|
||||
return sex;
|
||||
}
|
||||
|
||||
public void setSex(int sex) {
|
||||
this.sex = sex;
|
||||
}
|
||||
|
||||
public int getAttrStatus() {
|
||||
return attrStatus;
|
||||
}
|
||||
|
||||
public void setAttrStatus(int attrStatus) {
|
||||
this.attrStatus = attrStatus;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getNickName() {
|
||||
return nickName;
|
||||
}
|
||||
|
||||
public void setNickName(String nickName) {
|
||||
this.nickName = nickName;
|
||||
}
|
||||
|
||||
public int getScene() {
|
||||
return scene;
|
||||
}
|
||||
|
||||
public void setScene(int scene) {
|
||||
this.scene = scene;
|
||||
}
|
||||
|
||||
public String getProvince() {
|
||||
return province;
|
||||
}
|
||||
|
||||
public void setProvince(String province) {
|
||||
this.province = province;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getAlias() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
public void setAlias(String alias) {
|
||||
this.alias = alias;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(String signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public int getOpCode() {
|
||||
return opCode;
|
||||
}
|
||||
|
||||
public void setOpCode(int opCode) {
|
||||
this.opCode = opCode;
|
||||
}
|
||||
|
||||
public int getqQNum() {
|
||||
return qQNum;
|
||||
}
|
||||
|
||||
public void setqQNum(int qQNum) {
|
||||
this.qQNum = qQNum;
|
||||
}
|
||||
|
||||
public int getVerifyFlag() {
|
||||
return verifyFlag;
|
||||
}
|
||||
|
||||
public void setVerifyFlag(int verifyFlag) {
|
||||
this.verifyFlag = verifyFlag;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.xmzs.common.wechat.controller;
|
||||
|
||||
|
||||
import com.xmzs.common.wechat.utils.SleepUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.xmzs.common.wechat.api.WechatTools;
|
||||
import com.xmzs.common.wechat.core.Core;
|
||||
import com.xmzs.common.wechat.service.ILoginService;
|
||||
import com.xmzs.common.wechat.service.impl.LoginServiceImpl;
|
||||
import com.xmzs.common.wechat.thread.CheckLoginStatusThread;
|
||||
|
||||
import com.xmzs.common.wechat.utils.tools.CommonTools;
|
||||
|
||||
/**
|
||||
* 登陆控制器
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年5月13日 下午12:56:07
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class LoginController {
|
||||
private static Logger LOG = LoggerFactory.getLogger(LoginController.class);
|
||||
private ILoginService loginService = new LoginServiceImpl();
|
||||
private static Core core = Core.getInstance();
|
||||
|
||||
public void login(String qrPath) {
|
||||
if (core.isAlive()) { // 已登陆
|
||||
LOG.info("itchat4j已登陆");
|
||||
return;
|
||||
}
|
||||
while (true) {
|
||||
for (int count = 0; count < 10; count++) {
|
||||
LOG.info("获取UUID");
|
||||
while (loginService.getUuid() == null) {
|
||||
LOG.info("1. 获取微信UUID");
|
||||
while (loginService.getUuid() == null) {
|
||||
LOG.warn("1.1. 获取微信UUID失败,两秒后重新获取");
|
||||
SleepUtils.sleep(2000);
|
||||
}
|
||||
}
|
||||
LOG.info("2. 获取登陆二维码图片");
|
||||
if (loginService.getQR(qrPath)) {
|
||||
break;
|
||||
} else if (count == 10) {
|
||||
LOG.error("2.2. 获取登陆二维码图片失败,系统退出");
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
LOG.info("3. 请扫描二维码图片,并在手机上确认");
|
||||
if (!core.isAlive()) {
|
||||
loginService.login();
|
||||
core.setAlive(true);
|
||||
LOG.info(("登陆成功"));
|
||||
break;
|
||||
}
|
||||
LOG.info("4. 登陆超时,请重新扫描二维码图片");
|
||||
}
|
||||
|
||||
LOG.info("5. 登陆成功,微信初始化");
|
||||
if (!loginService.webWxInit()) {
|
||||
LOG.info("6. 微信初始化异常");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
LOG.info("6. 开启微信状态通知");
|
||||
loginService.wxStatusNotify();
|
||||
|
||||
LOG.info("7. 清除。。。。");
|
||||
CommonTools.clearScreen();
|
||||
LOG.info(String.format("欢迎回来, %s", core.getNickName()));
|
||||
|
||||
LOG.info("8. 开始接收消息");
|
||||
loginService.startReceiving();
|
||||
|
||||
LOG.info("9. 获取联系人信息");
|
||||
loginService.webWxGetContact();
|
||||
|
||||
LOG.info("10. 获取群好友及群好友列表");
|
||||
loginService.WebWxBatchGetContact();
|
||||
|
||||
LOG.info("11. 缓存本次登陆好友相关消息");
|
||||
WechatTools.setUserInfo(); // 登陆成功后缓存本次登陆好友相关消息(NickName, UserName)
|
||||
|
||||
LOG.info("12.开启微信状态检测线程");
|
||||
new Thread(new CheckLoginStatusThread()).start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
package com.xmzs.common.wechat.core;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||
import com.xmzs.common.wechat.utils.MyHttpClient;
|
||||
import com.xmzs.common.wechat.utils.enums.parameters.BaseParaEnum;
|
||||
|
||||
/**
|
||||
* 核心存储类,全局只保存一份,单例模式
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年4月23日 下午2:33:56
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class Core {
|
||||
|
||||
private static Core instance;
|
||||
|
||||
private Core() {
|
||||
|
||||
}
|
||||
|
||||
public static Core getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (Core.class) {
|
||||
instance = new Core();
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
boolean alive = false;
|
||||
private int memberCount = 0;
|
||||
|
||||
private String indexUrl;
|
||||
|
||||
private String userName;
|
||||
private String nickName;
|
||||
private List<BaseMsg> msgList = new ArrayList<BaseMsg>();
|
||||
|
||||
private JSONObject userSelf; // 登陆账号自身信息
|
||||
private List<JSONObject> memberList = new ArrayList<JSONObject>(); // 好友+群聊+公众号+特殊账号
|
||||
private List<JSONObject> contactList = new ArrayList<JSONObject>();// 好友
|
||||
private List<JSONObject> groupList = new ArrayList<JSONObject>();; // 群
|
||||
private Map<String, JSONArray> groupMemeberMap = new HashMap<String, JSONArray>(); // 群聊成员字典
|
||||
private List<JSONObject> publicUsersList = new ArrayList<JSONObject>();;// 公众号/服务号
|
||||
private List<JSONObject> specialUsersList = new ArrayList<JSONObject>();;// 特殊账号
|
||||
private List<String> groupIdList = new ArrayList<String>(); // 群ID列表
|
||||
private List<String> groupNickNameList = new ArrayList<String>(); // 群NickName列表
|
||||
|
||||
private Map<String, JSONObject> userInfoMap = new HashMap<String, JSONObject>();
|
||||
|
||||
Map<String, Object> loginInfo = new HashMap<String, Object>();
|
||||
// CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
MyHttpClient myHttpClient = MyHttpClient.getInstance();
|
||||
String uuid = null;
|
||||
|
||||
boolean useHotReload = false;
|
||||
String hotReloadDir = "itchat.pkl";
|
||||
int receivingRetryCount = 5;
|
||||
|
||||
private long lastNormalRetcodeTime; // 最后一次收到正常retcode的时间,秒为单位
|
||||
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
public Map<String, Object> getParamMap() {
|
||||
return new HashMap<String, Object>(1) {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
{
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
for (BaseParaEnum baseRequest : BaseParaEnum.values()) {
|
||||
map.put(baseRequest.para(), getLoginInfo().get(baseRequest.value()).toString());
|
||||
}
|
||||
put("BaseRequest", map);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public boolean isAlive() {
|
||||
return alive;
|
||||
}
|
||||
|
||||
public void setAlive(boolean alive) {
|
||||
this.alive = alive;
|
||||
}
|
||||
|
||||
public List<JSONObject> getMemberList() {
|
||||
return memberList;
|
||||
}
|
||||
|
||||
public void setMemberList(List<JSONObject> memberList) {
|
||||
this.memberList = memberList;
|
||||
}
|
||||
|
||||
public Map<String, Object> getLoginInfo() {
|
||||
return loginInfo;
|
||||
}
|
||||
|
||||
public void setLoginInfo(Map<String, Object> loginInfo) {
|
||||
this.loginInfo = loginInfo;
|
||||
}
|
||||
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public void setUuid(String uuid) {
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
public int getMemberCount() {
|
||||
return memberCount;
|
||||
}
|
||||
|
||||
public void setMemberCount(int memberCount) {
|
||||
this.memberCount = memberCount;
|
||||
}
|
||||
|
||||
public boolean isUseHotReload() {
|
||||
return useHotReload;
|
||||
}
|
||||
|
||||
public void setUseHotReload(boolean useHotReload) {
|
||||
this.useHotReload = useHotReload;
|
||||
}
|
||||
|
||||
public String getHotReloadDir() {
|
||||
return hotReloadDir;
|
||||
}
|
||||
|
||||
public void setHotReloadDir(String hotReloadDir) {
|
||||
this.hotReloadDir = hotReloadDir;
|
||||
}
|
||||
|
||||
public int getReceivingRetryCount() {
|
||||
return receivingRetryCount;
|
||||
}
|
||||
|
||||
public void setReceivingRetryCount(int receivingRetryCount) {
|
||||
this.receivingRetryCount = receivingRetryCount;
|
||||
}
|
||||
|
||||
public MyHttpClient getMyHttpClient() {
|
||||
return myHttpClient;
|
||||
}
|
||||
|
||||
public List<BaseMsg> getMsgList() {
|
||||
return msgList;
|
||||
}
|
||||
|
||||
public void setMsgList(List<BaseMsg> msgList) {
|
||||
this.msgList = msgList;
|
||||
}
|
||||
|
||||
public void setMyHttpClient(MyHttpClient myHttpClient) {
|
||||
this.myHttpClient = myHttpClient;
|
||||
}
|
||||
|
||||
public List<String> getGroupIdList() {
|
||||
return groupIdList;
|
||||
}
|
||||
|
||||
public void setGroupIdList(List<String> groupIdList) {
|
||||
this.groupIdList = groupIdList;
|
||||
}
|
||||
|
||||
public List<JSONObject> getContactList() {
|
||||
return contactList;
|
||||
}
|
||||
|
||||
public void setContactList(List<JSONObject> contactList) {
|
||||
this.contactList = contactList;
|
||||
}
|
||||
|
||||
public List<JSONObject> getGroupList() {
|
||||
return groupList;
|
||||
}
|
||||
|
||||
public void setGroupList(List<JSONObject> groupList) {
|
||||
this.groupList = groupList;
|
||||
}
|
||||
|
||||
public List<JSONObject> getPublicUsersList() {
|
||||
return publicUsersList;
|
||||
}
|
||||
|
||||
public void setPublicUsersList(List<JSONObject> publicUsersList) {
|
||||
this.publicUsersList = publicUsersList;
|
||||
}
|
||||
|
||||
public List<JSONObject> getSpecialUsersList() {
|
||||
return specialUsersList;
|
||||
}
|
||||
|
||||
public void setSpecialUsersList(List<JSONObject> specialUsersList) {
|
||||
this.specialUsersList = specialUsersList;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getNickName() {
|
||||
return nickName;
|
||||
}
|
||||
|
||||
public void setNickName(String nickName) {
|
||||
this.nickName = nickName;
|
||||
}
|
||||
|
||||
public JSONObject getUserSelf() {
|
||||
return userSelf;
|
||||
}
|
||||
|
||||
public void setUserSelf(JSONObject userSelf) {
|
||||
this.userSelf = userSelf;
|
||||
}
|
||||
|
||||
public Map<String, JSONObject> getUserInfoMap() {
|
||||
return userInfoMap;
|
||||
}
|
||||
|
||||
public void setUserInfoMap(Map<String, JSONObject> userInfoMap) {
|
||||
this.userInfoMap = userInfoMap;
|
||||
}
|
||||
|
||||
public synchronized long getLastNormalRetcodeTime() {
|
||||
return lastNormalRetcodeTime;
|
||||
}
|
||||
|
||||
public synchronized void setLastNormalRetcodeTime(long lastNormalRetcodeTime) {
|
||||
this.lastNormalRetcodeTime = lastNormalRetcodeTime;
|
||||
}
|
||||
|
||||
public List<String> getGroupNickNameList() {
|
||||
return groupNickNameList;
|
||||
}
|
||||
|
||||
public void setGroupNickNameList(List<String> groupNickNameList) {
|
||||
this.groupNickNameList = groupNickNameList;
|
||||
}
|
||||
|
||||
public Map<String, JSONArray> getGroupMemeberMap() {
|
||||
return groupMemeberMap;
|
||||
}
|
||||
|
||||
public void setGroupMemeberMap(Map<String, JSONArray> groupMemeberMap) {
|
||||
this.groupMemeberMap = groupMemeberMap;
|
||||
}
|
||||
|
||||
public String getIndexUrl() {
|
||||
return indexUrl;
|
||||
}
|
||||
|
||||
public void setIndexUrl(String indexUrl) {
|
||||
this.indexUrl = indexUrl;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
package com.xmzs.common.wechat.core;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import com.xmzs.common.wechat.utils.enums.MsgCodeEnum;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import com.xmzs.common.wechat.api.MessageTools;
|
||||
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||
import com.xmzs.common.wechat.face.IMsgHandlerFace;
|
||||
|
||||
import com.xmzs.common.wechat.utils.enums.MsgTypeEnum;
|
||||
import com.xmzs.common.wechat.utils.tools.CommonTools;
|
||||
|
||||
/**
|
||||
* 消息处理中心
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年5月14日 下午12:47:50
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class MsgCenter {
|
||||
private static Logger LOG = LoggerFactory.getLogger(MsgCenter.class);
|
||||
|
||||
private static Core core = Core.getInstance();
|
||||
|
||||
/**
|
||||
* 接收消息,放入队列
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月23日 下午2:30:48
|
||||
* @param msgList
|
||||
* @return
|
||||
*/
|
||||
public static JSONArray produceMsg(JSONArray msgList) {
|
||||
JSONArray result = new JSONArray();
|
||||
for (int i = 0; i < msgList.size(); i++) {
|
||||
JSONObject msg = new JSONObject();
|
||||
JSONObject m = msgList.getJSONObject(i);
|
||||
m.put("groupMsg", false);// 是否是群消息
|
||||
if (m.getString("FromUserName").contains("@@") || m.getString("ToUserName").contains("@@")) { // 群聊消息
|
||||
if (m.getString("FromUserName").contains("@@")
|
||||
&& !core.getGroupIdList().contains(m.getString("FromUserName"))) {
|
||||
core.getGroupIdList().add((m.getString("FromUserName")));
|
||||
} else if (m.getString("ToUserName").contains("@@")
|
||||
&& !core.getGroupIdList().contains(m.getString("ToUserName"))) {
|
||||
core.getGroupIdList().add((m.getString("ToUserName")));
|
||||
}
|
||||
// 群消息与普通消息不同的是在其消息体(Content)中会包含发送者id及":<br/>"消息,这里需要处理一下,去掉多余信息,只保留消息内容
|
||||
if (m.getString("Content").contains("<br/>")) {
|
||||
String content = m.getString("Content").substring(m.getString("Content").indexOf("<br/>") + 5);
|
||||
m.put("Content", content);
|
||||
m.put("groupMsg", true);
|
||||
}
|
||||
} else {
|
||||
CommonTools.msgFormatter(m, "Content");
|
||||
}
|
||||
if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_TEXT.getCode())) { // words
|
||||
// 文本消息
|
||||
if (m.getString("Url").length() != 0) {
|
||||
String regEx = "(.+?\\(.+?\\))";
|
||||
Matcher matcher = CommonTools.getMatcher(regEx, m.getString("Content"));
|
||||
String data = "Map";
|
||||
if (matcher.find()) {
|
||||
data = matcher.group(1);
|
||||
}
|
||||
msg.put("Type", "Map");
|
||||
msg.put("Text", data);
|
||||
} else {
|
||||
msg.put("Type", MsgTypeEnum.TEXT.getType());
|
||||
msg.put("Text", m.getString("Content"));
|
||||
}
|
||||
m.put("Type", msg.getString("Type"));
|
||||
m.put("Text", msg.getString("Text"));
|
||||
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_IMAGE.getCode())
|
||||
|| m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_EMOTICON.getCode())) { // 图片消息
|
||||
m.put("Type", MsgTypeEnum.PIC.getType());
|
||||
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_VOICE.getCode())) { // 语音消息
|
||||
m.put("Type", MsgTypeEnum.VOICE.getType());
|
||||
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_VERIFYMSG.getCode())) {// friends
|
||||
// 好友确认消息
|
||||
// MessageTools.addFriend(core, userName, 3, ticket); // 确认添加好友
|
||||
m.put("Type", MsgTypeEnum.VERIFYMSG.getType());
|
||||
|
||||
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_SHARECARD.getCode())) { // 共享名片
|
||||
m.put("Type", MsgTypeEnum.NAMECARD.getType());
|
||||
|
||||
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_VIDEO.getCode())
|
||||
|| m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_MICROVIDEO.getCode())) {// viedo
|
||||
m.put("Type", MsgTypeEnum.VIEDO.getType());
|
||||
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_MEDIA.getCode())) { // 多媒体消息
|
||||
m.put("Type", MsgTypeEnum.MEDIA.getType());
|
||||
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_STATUSNOTIFY.getCode())) {// phone
|
||||
// init
|
||||
// 微信初始化消息
|
||||
|
||||
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_SYS.getCode())) {// 系统消息
|
||||
m.put("Type", MsgTypeEnum.SYS.getType());
|
||||
} else if (m.getInteger("MsgType").equals(MsgCodeEnum.MSGTYPE_RECALLED.getCode())) { // 撤回消息
|
||||
|
||||
} else {
|
||||
LOG.info("Useless msg");
|
||||
}
|
||||
LOG.info("收到消息一条,来自: " + m.getString("FromUserName"));
|
||||
result.add(m);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息处理
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月14日 上午10:52:34
|
||||
* @param msgHandler
|
||||
*/
|
||||
public static void handleMsg(IMsgHandlerFace msgHandler) {
|
||||
while (true) {
|
||||
if (core.getMsgList().size() > 0 && core.getMsgList().get(0).getContent() != null) {
|
||||
if (core.getMsgList().get(0).getContent().length() > 0) {
|
||||
BaseMsg msg = core.getMsgList().get(0);
|
||||
if (msg.getType() != null) {
|
||||
try {
|
||||
if (msg.getType().equals(MsgTypeEnum.TEXT.getType())) {
|
||||
String result = msgHandler.textMsgHandle(msg);
|
||||
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||
} else if (msg.getType().equals(MsgTypeEnum.PIC.getType())) {
|
||||
|
||||
String result = msgHandler.picMsgHandle(msg);
|
||||
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||
} else if (msg.getType().equals(MsgTypeEnum.VOICE.getType())) {
|
||||
String result = msgHandler.voiceMsgHandle(msg);
|
||||
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||
} else if (msg.getType().equals(MsgTypeEnum.VIEDO.getType())) {
|
||||
String result = msgHandler.viedoMsgHandle(msg);
|
||||
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||
} else if (msg.getType().equals(MsgTypeEnum.NAMECARD.getType())) {
|
||||
String result = msgHandler.nameCardMsgHandle(msg);
|
||||
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||
} else if (msg.getType().equals(MsgTypeEnum.SYS.getType())) { // 系统消息
|
||||
msgHandler.sysMsgHandle(msg);
|
||||
} else if (msg.getType().equals(MsgTypeEnum.VERIFYMSG.getType())) { // 确认添加好友消息
|
||||
String result = msgHandler.verifyAddFriendMsgHandle(msg);
|
||||
MessageTools.sendMsgById(result,
|
||||
core.getMsgList().get(0).getRecommendInfo().getUserName());
|
||||
} else if (msg.getType().equals(MsgTypeEnum.MEDIA.getType())) { // 多媒体消息
|
||||
String result = msgHandler.mediaMsgHandle(msg);
|
||||
MessageTools.sendMsgById(result, core.getMsgList().get(0).getFromUserName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
core.getMsgList().remove(0);
|
||||
}
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.xmzs.common.wechat.face;
|
||||
|
||||
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||
|
||||
/**
|
||||
* 消息处理接口
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年4月20日 上午12:13:49
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public interface IMsgHandlerFace {
|
||||
/**
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月20日 上午12:15:00
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public String textMsgHandle(BaseMsg msg);
|
||||
|
||||
/**
|
||||
* 处理图片消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月21日 下午11:07:06
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public String picMsgHandle(BaseMsg msg);
|
||||
|
||||
/**
|
||||
* 处理声音消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月22日 上午12:09:44
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public String voiceMsgHandle(BaseMsg msg);
|
||||
|
||||
/**
|
||||
* 处理小视频消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月23日 下午12:19:50
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public String viedoMsgHandle(BaseMsg msg);
|
||||
|
||||
/**
|
||||
* 处理名片消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月1日 上午12:50:50
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public String nameCardMsgHandle(BaseMsg msg);
|
||||
|
||||
/**
|
||||
* 处理系统消息
|
||||
*
|
||||
* @author Relyn
|
||||
* @date 2017年6月21日17:43:51
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public void sysMsgHandle(BaseMsg msg);
|
||||
|
||||
/**
|
||||
* 处理确认添加好友消息
|
||||
*
|
||||
* @date 2017年6月28日 下午10:15:30
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public String verifyAddFriendMsgHandle(BaseMsg msg);
|
||||
|
||||
/**
|
||||
* 处理收到的文件消息
|
||||
*
|
||||
* @date 2017年7月21日 下午11:59:14
|
||||
* @param msg
|
||||
* @return
|
||||
*/
|
||||
public String mediaMsgHandle(BaseMsg msg);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.xmzs.common.wechat.service;
|
||||
|
||||
/**
|
||||
* 登陆服务接口
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年5月13日 上午12:07:21
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public interface ILoginService {
|
||||
|
||||
/**
|
||||
* 登陆
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月13日 上午12:14:07
|
||||
* @return
|
||||
*/
|
||||
boolean login();
|
||||
|
||||
/**
|
||||
* 获取UUID
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月13日 上午12:21:40
|
||||
* @param qrPath
|
||||
* @return
|
||||
*/
|
||||
String getUuid();
|
||||
|
||||
/**
|
||||
* 获取二维码图片
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月13日 上午12:13:51
|
||||
* @param qrPath
|
||||
* @return
|
||||
*/
|
||||
boolean getQR(String qrPath);
|
||||
|
||||
/**
|
||||
* web初始化
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月13日 上午12:14:13
|
||||
* @return
|
||||
*/
|
||||
boolean webWxInit();
|
||||
|
||||
/**
|
||||
* 微信状态通知
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月13日 上午12:14:24
|
||||
*/
|
||||
void wxStatusNotify();
|
||||
|
||||
/**
|
||||
* 接收消息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月13日 上午12:14:37
|
||||
*/
|
||||
void startReceiving();
|
||||
|
||||
/**
|
||||
* 获取微信联系人
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月13日 下午2:26:18
|
||||
*/
|
||||
void webWxGetContact();
|
||||
|
||||
/**
|
||||
* 批量获取联系人信息
|
||||
*
|
||||
* @date 2017年6月22日 下午11:24:35
|
||||
*/
|
||||
void WebWxBatchGetContact();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,687 @@
|
||||
package com.xmzs.common.wechat.service.impl;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
import com.xmzs.common.wechat.utils.SleepUtils;
|
||||
import org.apache.http.Consts;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||
import com.xmzs.common.wechat.core.Core;
|
||||
import com.xmzs.common.wechat.core.MsgCenter;
|
||||
import com.xmzs.common.wechat.service.ILoginService;
|
||||
import com.xmzs.common.wechat.utils.Config;
|
||||
import com.xmzs.common.wechat.utils.MyHttpClient;
|
||||
|
||||
import com.xmzs.common.wechat.utils.enums.ResultEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.RetCodeEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.StorageLoginInfoEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.URLEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.parameters.BaseParaEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.parameters.LoginParaEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.parameters.StatusNotifyParaEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.parameters.UUIDParaEnum;
|
||||
import com.xmzs.common.wechat.utils.tools.CommonTools;
|
||||
|
||||
/**
|
||||
* 登陆服务实现类
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年5月13日 上午12:09:35
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class LoginServiceImpl implements ILoginService {
|
||||
private static Logger LOG = LoggerFactory.getLogger(LoginServiceImpl.class);
|
||||
|
||||
private Core core = Core.getInstance();
|
||||
private MyHttpClient httpClient = core.getMyHttpClient();
|
||||
|
||||
private MyHttpClient myHttpClient = core.getMyHttpClient();
|
||||
|
||||
public LoginServiceImpl() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean login() {
|
||||
|
||||
boolean isLogin = false;
|
||||
// 组装参数和URL
|
||||
List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
|
||||
params.add(new BasicNameValuePair(LoginParaEnum.LOGIN_ICON.para(), LoginParaEnum.LOGIN_ICON.value()));
|
||||
params.add(new BasicNameValuePair(LoginParaEnum.UUID.para(), core.getUuid()));
|
||||
params.add(new BasicNameValuePair(LoginParaEnum.TIP.para(), LoginParaEnum.TIP.value()));
|
||||
|
||||
// long time = 4000;
|
||||
while (!isLogin) {
|
||||
// SleepUtils.sleep(time += 1000);
|
||||
long millis = System.currentTimeMillis();
|
||||
params.add(new BasicNameValuePair(LoginParaEnum.R.para(), String.valueOf(millis / 1579L)));
|
||||
params.add(new BasicNameValuePair(LoginParaEnum._1.para(), String.valueOf(millis)));
|
||||
HttpEntity entity = httpClient.doGet(URLEnum.LOGIN_URL.getUrl(), params, true, null);
|
||||
|
||||
try {
|
||||
String result = EntityUtils.toString(entity);
|
||||
String status = checklogin(result);
|
||||
|
||||
if (ResultEnum.SUCCESS.getCode().equals(status)) {
|
||||
processLoginInfo(result); // 处理结果
|
||||
isLogin = true;
|
||||
core.setAlive(isLogin);
|
||||
break;
|
||||
}
|
||||
if (ResultEnum.WAIT_CONFIRM.getCode().equals(status)) {
|
||||
LOG.info("请点击微信确认按钮,进行登陆");
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("微信登陆异常!", e);
|
||||
}
|
||||
}
|
||||
return isLogin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUuid() {
|
||||
// 组装参数和URL
|
||||
List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
|
||||
params.add(new BasicNameValuePair(UUIDParaEnum.APP_ID.para(), UUIDParaEnum.APP_ID.value()));
|
||||
params.add(new BasicNameValuePair(UUIDParaEnum.REDIRECT_URL.para(),
|
||||
UUIDParaEnum.REDIRECT_URL.value()));
|
||||
params.add(new BasicNameValuePair(UUIDParaEnum.FUN.para(), UUIDParaEnum.FUN.value()));
|
||||
params.add(new BasicNameValuePair(UUIDParaEnum.LANG.para(), UUIDParaEnum.LANG.value()));
|
||||
params.add(new BasicNameValuePair(UUIDParaEnum._1.para(), String.valueOf(System.currentTimeMillis())));
|
||||
|
||||
HttpEntity entity = httpClient.doGet(URLEnum.UUID_URL.getUrl(), params, true, null);
|
||||
|
||||
try {
|
||||
String result = EntityUtils.toString(entity);
|
||||
String regEx = "window.QRLogin.code = (\\d+); window.QRLogin.uuid = \"(\\S+?)\";";
|
||||
Matcher matcher = CommonTools.getMatcher(regEx, result);
|
||||
if (matcher.find()) {
|
||||
if ((ResultEnum.SUCCESS.getCode().equals(matcher.group(1)))) {
|
||||
core.setUuid(matcher.group(2));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
return core.getUuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getQR(String qrPath) {
|
||||
qrPath = qrPath + File.separator + "QR.jpg";
|
||||
String qrUrl = URLEnum.QRCODE_URL.getUrl() + core.getUuid();
|
||||
HttpEntity entity = myHttpClient.doGet(qrUrl, null, true, null);
|
||||
try {
|
||||
OutputStream out = new FileOutputStream(qrPath);
|
||||
byte[] bytes = EntityUtils.toByteArray(entity);
|
||||
out.write(bytes);
|
||||
out.flush();
|
||||
out.close();
|
||||
try {
|
||||
CommonTools.printQr(qrPath); // 打开登陆二维码图片
|
||||
} catch (Exception e) {
|
||||
LOG.info(e.getMessage());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.info(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean webWxInit() {
|
||||
core.setAlive(true);
|
||||
core.setLastNormalRetcodeTime(System.currentTimeMillis());
|
||||
// 组装请求URL和参数
|
||||
String url = String.format(URLEnum.INIT_URL.getUrl(),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()),
|
||||
String.valueOf(System.currentTimeMillis() / 3158L),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey()));
|
||||
|
||||
Map<String, Object> paramMap = core.getParamMap();
|
||||
|
||||
// 请求初始化接口
|
||||
HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap));
|
||||
try {
|
||||
String result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||
JSONObject obj = JSON.parseObject(result);
|
||||
|
||||
JSONObject user = obj.getJSONObject(StorageLoginInfoEnum.User.getKey());
|
||||
JSONObject syncKey = obj.getJSONObject(StorageLoginInfoEnum.SyncKey.getKey());
|
||||
|
||||
core.getLoginInfo().put(StorageLoginInfoEnum.InviteStartCount.getKey(),
|
||||
obj.getInteger(StorageLoginInfoEnum.InviteStartCount.getKey()));
|
||||
core.getLoginInfo().put(StorageLoginInfoEnum.SyncKey.getKey(), syncKey);
|
||||
|
||||
JSONArray syncArray = syncKey.getJSONArray("List");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < syncArray.size(); i++) {
|
||||
sb.append(syncArray.getJSONObject(i).getString("Key") + "_"
|
||||
+ syncArray.getJSONObject(i).getString("Val") + "|");
|
||||
}
|
||||
// 1_661706053|2_661706420|3_661706415|1000_1494151022|
|
||||
String synckey = sb.toString();
|
||||
|
||||
// 1_661706053|2_661706420|3_661706415|1000_1494151022
|
||||
core.getLoginInfo().put(StorageLoginInfoEnum.synckey.getKey(), synckey.substring(0, synckey.length() - 1));// 1_656161336|2_656161626|3_656161313|11_656159955|13_656120033|201_1492273724|1000_1492265953|1001_1492250432|1004_1491805192
|
||||
core.setUserName(user.getString("UserName"));
|
||||
core.setNickName(user.getString("NickName"));
|
||||
core.setUserSelf(obj.getJSONObject("User"));
|
||||
|
||||
String chatSet = obj.getString("ChatSet");
|
||||
String[] chatSetArray = chatSet.split(",");
|
||||
for (int i = 0; i < chatSetArray.length; i++) {
|
||||
if (chatSetArray[i].indexOf("@@") != -1) {
|
||||
// 更新GroupIdList
|
||||
core.getGroupIdList().add(chatSetArray[i]); //
|
||||
}
|
||||
}
|
||||
// JSONArray contactListArray = obj.getJSONArray("ContactList");
|
||||
// for (int i = 0; i < contactListArray.size(); i++) {
|
||||
// JSONObject o = contactListArray.getJSONObject(i);
|
||||
// if (o.getString("UserName").indexOf("@@") != -1) {
|
||||
// core.getGroupIdList().add(o.getString("UserName")); //
|
||||
// // 更新GroupIdList
|
||||
// core.getGroupList().add(o); // 更新GroupList
|
||||
// core.getGroupNickNameList().add(o.getString("NickName"));
|
||||
// }
|
||||
// }
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void wxStatusNotify() {
|
||||
// 组装请求URL和参数
|
||||
String url = String.format(URLEnum.STATUS_NOTIFY_URL.getUrl(),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey()));
|
||||
|
||||
Map<String, Object> paramMap = core.getParamMap();
|
||||
paramMap.put(StatusNotifyParaEnum.CODE.para(), StatusNotifyParaEnum.CODE.value());
|
||||
paramMap.put(StatusNotifyParaEnum.FROM_USERNAME.para(), core.getUserName());
|
||||
paramMap.put(StatusNotifyParaEnum.TO_USERNAME.para(), core.getUserName());
|
||||
paramMap.put(StatusNotifyParaEnum.CLIENT_MSG_ID.para(), System.currentTimeMillis());
|
||||
String paramStr = JSON.toJSONString(paramMap);
|
||||
|
||||
try {
|
||||
HttpEntity entity = httpClient.doPost(url, paramStr);
|
||||
EntityUtils.toString(entity, Consts.UTF_8);
|
||||
} catch (Exception e) {
|
||||
LOG.error("微信状态通知接口失败!", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startReceiving() {
|
||||
core.setAlive(true);
|
||||
new Thread(new Runnable() {
|
||||
int retryCount = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (core.isAlive()) {
|
||||
try {
|
||||
Map<String, String> resultMap = syncCheck();
|
||||
LOG.info(JSONObject.toJSONString(resultMap));
|
||||
String retcode = resultMap.get("retcode");
|
||||
String selector = resultMap.get("selector");
|
||||
if (retcode.equals(RetCodeEnum.UNKOWN.getCode())) {
|
||||
LOG.info(RetCodeEnum.UNKOWN.getType());
|
||||
continue;
|
||||
} else if (retcode.equals(RetCodeEnum.LOGIN_OUT.getCode())) { // 退出
|
||||
LOG.info(RetCodeEnum.LOGIN_OUT.getType());
|
||||
break;
|
||||
} else if (retcode.equals(RetCodeEnum.LOGIN_OTHERWHERE.getCode())) { // 其它地方登陆
|
||||
LOG.info(RetCodeEnum.LOGIN_OTHERWHERE.getType());
|
||||
break;
|
||||
} else if (retcode.equals(RetCodeEnum.MOBILE_LOGIN_OUT.getCode())) { // 移动端退出
|
||||
LOG.info(RetCodeEnum.MOBILE_LOGIN_OUT.getType());
|
||||
break;
|
||||
} else if (retcode.equals(RetCodeEnum.NORMAL.getCode())) {
|
||||
core.setLastNormalRetcodeTime(System.currentTimeMillis()); // 最后收到正常报文时间
|
||||
JSONObject msgObj = webWxSync();
|
||||
if (selector.equals("2")) {
|
||||
if (msgObj != null) {
|
||||
try {
|
||||
JSONArray msgList = new JSONArray();
|
||||
msgList = msgObj.getJSONArray("AddMsgList");
|
||||
msgList = MsgCenter.produceMsg(msgList);
|
||||
for (int j = 0; j < msgList.size(); j++) {
|
||||
BaseMsg baseMsg = JSON.toJavaObject(msgList.getJSONObject(j),
|
||||
BaseMsg.class);
|
||||
core.getMsgList().add(baseMsg);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
} else if (selector.equals("7")) {
|
||||
webWxSync();
|
||||
} else if (selector.equals("4")) {
|
||||
continue;
|
||||
} else if (selector.equals("3")) {
|
||||
continue;
|
||||
} else if (selector.equals("6")) {
|
||||
if (msgObj != null) {
|
||||
try {
|
||||
JSONArray msgList = new JSONArray();
|
||||
msgList = msgObj.getJSONArray("AddMsgList");
|
||||
JSONArray modContactList = msgObj.getJSONArray("ModContactList"); // 存在删除或者新增的好友信息
|
||||
msgList = MsgCenter.produceMsg(msgList);
|
||||
for (int j = 0; j < msgList.size(); j++) {
|
||||
JSONObject userInfo = modContactList.getJSONObject(j);
|
||||
// 存在主动加好友之后的同步联系人到本地
|
||||
core.getContactList().add(userInfo);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
JSONObject obj = webWxSync();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.info(e.getMessage());
|
||||
retryCount += 1;
|
||||
if (core.getReceivingRetryCount() < retryCount) {
|
||||
core.setAlive(false);
|
||||
} else {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException e1) {
|
||||
LOG.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void webWxGetContact() {
|
||||
String url = String.format(URLEnum.WEB_WX_GET_CONTACT.getUrl(),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()));
|
||||
Map<String, Object> paramMap = core.getParamMap();
|
||||
HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap));
|
||||
|
||||
try {
|
||||
String result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||
JSONObject fullFriendsJsonList = JSON.parseObject(result);
|
||||
// 查看seq是否为0,0表示好友列表已全部获取完毕,若大于0,则表示好友列表未获取完毕,当前的字节数(断点续传)
|
||||
long seq = 0;
|
||||
long currentTime = 0L;
|
||||
List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
|
||||
if (fullFriendsJsonList.get("Seq") != null) {
|
||||
seq = fullFriendsJsonList.getLong("Seq");
|
||||
currentTime = new Date().getTime();
|
||||
}
|
||||
core.setMemberCount(fullFriendsJsonList.getInteger(StorageLoginInfoEnum.MemberCount.getKey()));
|
||||
JSONArray member = fullFriendsJsonList.getJSONArray(StorageLoginInfoEnum.MemberList.getKey());
|
||||
// 循环获取seq直到为0,即获取全部好友列表 ==0:好友获取完毕 >0:好友未获取完毕,此时seq为已获取的字节数
|
||||
while (seq > 0) {
|
||||
// 设置seq传参
|
||||
params.add(new BasicNameValuePair("r", String.valueOf(currentTime)));
|
||||
params.add(new BasicNameValuePair("seq", String.valueOf(seq)));
|
||||
entity = httpClient.doGet(url, params, false, null);
|
||||
|
||||
params.remove(new BasicNameValuePair("r", String.valueOf(currentTime)));
|
||||
params.remove(new BasicNameValuePair("seq", String.valueOf(seq)));
|
||||
|
||||
result = EntityUtils.toString(entity, Consts.UTF_8);
|
||||
fullFriendsJsonList = JSON.parseObject(result);
|
||||
|
||||
if (fullFriendsJsonList.get("Seq") != null) {
|
||||
seq = fullFriendsJsonList.getLong("Seq");
|
||||
currentTime = new Date().getTime();
|
||||
}
|
||||
|
||||
// 累加好友列表
|
||||
member.addAll(fullFriendsJsonList.getJSONArray(StorageLoginInfoEnum.MemberList.getKey()));
|
||||
}
|
||||
core.setMemberCount(member.size());
|
||||
for (Iterator<?> iterator = member.iterator(); iterator.hasNext();) {
|
||||
JSONObject o = (JSONObject) iterator.next();
|
||||
if ((o.getInteger("VerifyFlag") & 8) != 0) { // 公众号/服务号
|
||||
core.getPublicUsersList().add(o);
|
||||
} else if (Config.API_SPECIAL_USER.contains(o.getString("UserName"))) { // 特殊账号
|
||||
core.getSpecialUsersList().add(o);
|
||||
} else if (o.getString("UserName").indexOf("@@") != -1) { // 群聊
|
||||
if (!core.getGroupIdList().contains(o.getString("UserName"))) {
|
||||
core.getGroupNickNameList().add(o.getString("NickName"));
|
||||
core.getGroupIdList().add(o.getString("UserName"));
|
||||
core.getGroupList().add(o);
|
||||
}
|
||||
} else if (o.getString("UserName").equals(core.getUserSelf().getString("UserName"))) { // 自己
|
||||
core.getContactList().remove(o);
|
||||
} else { // 普通联系人
|
||||
core.getContactList().add(o);
|
||||
}
|
||||
}
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void WebWxBatchGetContact() {
|
||||
String url = String.format(URLEnum.WEB_WX_BATCH_GET_CONTACT.getUrl(),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()), new Date().getTime(),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey()));
|
||||
Map<String, Object> paramMap = core.getParamMap();
|
||||
paramMap.put("Count", core.getGroupIdList().size());
|
||||
List<Map<String, String>> list = new ArrayList<Map<String, String>>();
|
||||
for (int i = 0; i < core.getGroupIdList().size(); i++) {
|
||||
HashMap<String, String> map = new HashMap<String, String>();
|
||||
map.put("UserName", core.getGroupIdList().get(i));
|
||||
map.put("EncryChatRoomId", "");
|
||||
list.add(map);
|
||||
}
|
||||
paramMap.put("List", list);
|
||||
HttpEntity entity = httpClient.doPost(url, JSON.toJSONString(paramMap));
|
||||
try {
|
||||
String text = EntityUtils.toString(entity, Consts.UTF_8);
|
||||
JSONObject obj = JSON.parseObject(text);
|
||||
JSONArray contactList = obj.getJSONArray("ContactList");
|
||||
for (int i = 0; i < contactList.size(); i++) { // 群好友
|
||||
if (contactList.getJSONObject(i).getString("UserName").indexOf("@@") > -1) { // 群
|
||||
core.getGroupNickNameList().add(contactList.getJSONObject(i).getString("NickName")); // 更新群昵称列表
|
||||
core.getGroupList().add(contactList.getJSONObject(i)); // 更新群信息(所有)列表
|
||||
core.getGroupMemeberMap().put(contactList.getJSONObject(i).getString("UserName"),
|
||||
contactList.getJSONObject(i).getJSONArray("MemberList")); // 更新群成员Map
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查登陆状态
|
||||
*
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
public String checklogin(String result) {
|
||||
String regEx = "window.code=(\\d+)";
|
||||
Matcher matcher = CommonTools.getMatcher(regEx, result);
|
||||
if (matcher.find()) {
|
||||
return matcher.group(1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理登陆信息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月9日 下午12:16:26
|
||||
* @param loginContent
|
||||
*/
|
||||
private void processLoginInfo(String loginContent) {
|
||||
String regEx = "window.redirect_uri=\"(\\S+)\";";
|
||||
Matcher matcher = CommonTools.getMatcher(regEx, loginContent);
|
||||
if (matcher.find()) {
|
||||
String originalUrl = matcher.group(1);
|
||||
String url = originalUrl.substring(0, originalUrl.lastIndexOf('/')); // https://wx2.qq.com/cgi-bin/mmwebwx-bin
|
||||
core.getLoginInfo().put("url", url);
|
||||
Map<String, List<String>> possibleUrlMap = this.getPossibleUrlMap();
|
||||
Iterator<Entry<String, List<String>>> iterator = possibleUrlMap.entrySet().iterator();
|
||||
Map.Entry<String, List<String>> entry;
|
||||
String fileUrl;
|
||||
String syncUrl;
|
||||
while (iterator.hasNext()) {
|
||||
entry = iterator.next();
|
||||
String indexUrl = entry.getKey();
|
||||
fileUrl = "https://" + entry.getValue().get(0) + "/cgi-bin/mmwebwx-bin";
|
||||
syncUrl = "https://" + entry.getValue().get(1) + "/cgi-bin/mmwebwx-bin";
|
||||
if (core.getLoginInfo().get("url").toString().contains(indexUrl)) {
|
||||
core.setIndexUrl(indexUrl);
|
||||
core.getLoginInfo().put("fileUrl", fileUrl);
|
||||
core.getLoginInfo().put("syncUrl", syncUrl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (core.getLoginInfo().get("fileUrl") == null && core.getLoginInfo().get("syncUrl") == null) {
|
||||
core.getLoginInfo().put("fileUrl", url);
|
||||
core.getLoginInfo().put("syncUrl", url);
|
||||
}
|
||||
core.getLoginInfo().put("deviceid", "e" + String.valueOf(new Random().nextLong()).substring(1, 16)); // 生成15位随机数
|
||||
core.getLoginInfo().put("BaseRequest", new ArrayList<String>());
|
||||
String text = "";
|
||||
|
||||
try {
|
||||
HttpEntity entity = myHttpClient.doGet(originalUrl, null, false, null);
|
||||
text = EntityUtils.toString(entity);
|
||||
} catch (Exception e) {
|
||||
LOG.info(e.getMessage());
|
||||
return;
|
||||
}
|
||||
//add by 默非默 2017-08-01 22:28:09
|
||||
//如果登录被禁止时,则登录返回的message内容不为空,下面代码则判断登录内容是否为空,不为空则退出程序
|
||||
String msg = getLoginMessage(text);
|
||||
if (!"".equals(msg)){
|
||||
LOG.info(msg);
|
||||
System.exit(0);
|
||||
}
|
||||
Document doc = CommonTools.xmlParser(text);
|
||||
if (doc != null) {
|
||||
core.getLoginInfo().put(StorageLoginInfoEnum.skey.getKey(),
|
||||
doc.getElementsByTagName(StorageLoginInfoEnum.skey.getKey()).item(0).getFirstChild()
|
||||
.getNodeValue());
|
||||
core.getLoginInfo().put(StorageLoginInfoEnum.wxsid.getKey(),
|
||||
doc.getElementsByTagName(StorageLoginInfoEnum.wxsid.getKey()).item(0).getFirstChild()
|
||||
.getNodeValue());
|
||||
core.getLoginInfo().put(StorageLoginInfoEnum.wxuin.getKey(),
|
||||
doc.getElementsByTagName(StorageLoginInfoEnum.wxuin.getKey()).item(0).getFirstChild()
|
||||
.getNodeValue());
|
||||
core.getLoginInfo().put(StorageLoginInfoEnum.pass_ticket.getKey(),
|
||||
doc.getElementsByTagName(StorageLoginInfoEnum.pass_ticket.getKey()).item(0).getFirstChild()
|
||||
.getNodeValue());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, List<String>> getPossibleUrlMap() {
|
||||
Map<String, List<String>> possibleUrlMap = new HashMap<String, List<String>>();
|
||||
possibleUrlMap.put("wx.qq.com", new ArrayList<String>() {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
{
|
||||
add("file.wx.qq.com");
|
||||
add("webpush.wx.qq.com");
|
||||
}
|
||||
});
|
||||
|
||||
possibleUrlMap.put("wx2.qq.com", new ArrayList<String>() {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
{
|
||||
add("file.wx2.qq.com");
|
||||
add("webpush.wx2.qq.com");
|
||||
}
|
||||
});
|
||||
possibleUrlMap.put("wx8.qq.com", new ArrayList<String>() {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
{
|
||||
add("file.wx8.qq.com");
|
||||
add("webpush.wx8.qq.com");
|
||||
}
|
||||
});
|
||||
|
||||
possibleUrlMap.put("web2.wechat.com", new ArrayList<String>() {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
{
|
||||
add("file.web2.wechat.com");
|
||||
add("webpush.web2.wechat.com");
|
||||
}
|
||||
});
|
||||
possibleUrlMap.put("wechat.com", new ArrayList<String>() {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
{
|
||||
add("file.web.wechat.com");
|
||||
add("webpush.web.wechat.com");
|
||||
}
|
||||
});
|
||||
return possibleUrlMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步消息 sync the messages
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月12日 上午12:24:55
|
||||
* @return
|
||||
*/
|
||||
private JSONObject webWxSync() {
|
||||
JSONObject result = null;
|
||||
String url = String.format(URLEnum.WEB_WX_SYNC_URL.getUrl(),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.url.getKey()),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.wxsid.getKey()),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.skey.getKey()),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.pass_ticket.getKey()));
|
||||
Map<String, Object> paramMap = core.getParamMap();
|
||||
paramMap.put(StorageLoginInfoEnum.SyncKey.getKey(),
|
||||
core.getLoginInfo().get(StorageLoginInfoEnum.SyncKey.getKey()));
|
||||
paramMap.put("rr", -new Date().getTime() / 1000);
|
||||
String paramStr = JSON.toJSONString(paramMap);
|
||||
try {
|
||||
HttpEntity entity = myHttpClient.doPost(url, paramStr);
|
||||
String text = EntityUtils.toString(entity, Consts.UTF_8);
|
||||
JSONObject obj = JSON.parseObject(text);
|
||||
if (obj.getJSONObject("BaseResponse").getInteger("Ret") != 0) {
|
||||
result = null;
|
||||
} else {
|
||||
result = obj;
|
||||
core.getLoginInfo().put(StorageLoginInfoEnum.SyncKey.getKey(), obj.getJSONObject("SyncCheckKey"));
|
||||
JSONArray syncArray = obj.getJSONObject(StorageLoginInfoEnum.SyncKey.getKey()).getJSONArray("List");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < syncArray.size(); i++) {
|
||||
sb.append(syncArray.getJSONObject(i).getString("Key") + "_"
|
||||
+ syncArray.getJSONObject(i).getString("Val") + "|");
|
||||
}
|
||||
String synckey = sb.toString();
|
||||
core.getLoginInfo().put(StorageLoginInfoEnum.synckey.getKey(),
|
||||
synckey.substring(0, synckey.length() - 1));// 1_656161336|2_656161626|3_656161313|11_656159955|13_656120033|201_1492273724|1000_1492265953|1001_1492250432|1004_1491805192
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.info(e.getMessage());
|
||||
}
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否有新消息 check whether there's a message
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月16日 上午11:11:34
|
||||
* @return
|
||||
*
|
||||
*/
|
||||
private Map<String, String> syncCheck() {
|
||||
Map<String, String> resultMap = new HashMap<String, String>();
|
||||
// 组装请求URL和参数
|
||||
String url = core.getLoginInfo().get(StorageLoginInfoEnum.syncUrl.getKey()) + URLEnum.SYNC_CHECK_URL.getUrl();
|
||||
List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
|
||||
for (BaseParaEnum baseRequest : BaseParaEnum.values()) {
|
||||
params.add(new BasicNameValuePair(baseRequest.para().toLowerCase(),
|
||||
core.getLoginInfo().get(baseRequest.value()).toString()));
|
||||
}
|
||||
params.add(new BasicNameValuePair("r", String.valueOf(new Date().getTime())));
|
||||
params.add(new BasicNameValuePair("synckey", (String) core.getLoginInfo().get("synckey")));
|
||||
params.add(new BasicNameValuePair("_", String.valueOf(new Date().getTime())));
|
||||
SleepUtils.sleep(7);
|
||||
try {
|
||||
HttpEntity entity = myHttpClient.doGet(url, params, true, null);
|
||||
if (entity == null) {
|
||||
resultMap.put("retcode", "9999");
|
||||
resultMap.put("selector", "9999");
|
||||
return resultMap;
|
||||
}
|
||||
String text = EntityUtils.toString(entity);
|
||||
String regEx = "window.synccheck=\\{retcode:\"(\\d+)\",selector:\"(\\d+)\"\\}";
|
||||
Matcher matcher = CommonTools.getMatcher(regEx, text);
|
||||
if (!matcher.find() || matcher.group(1).equals("2")) {
|
||||
LOG.info(String.format("Unexpected sync check result: %s", text));
|
||||
} else {
|
||||
resultMap.put("retcode", matcher.group(1));
|
||||
resultMap.put("selector", matcher.group(2));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析登录返回的消息,如果成功登录,则message为空
|
||||
* @param result
|
||||
* @return
|
||||
*/
|
||||
public String getLoginMessage(String result){
|
||||
String[] strArr = result.split("<message>");
|
||||
String[] rs = strArr[1].split("</message>");
|
||||
if (rs!=null && rs.length>1) {
|
||||
return rs[0];
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.xmzs.common.wechat.thread;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.xmzs.common.wechat.core.Core;
|
||||
import com.xmzs.common.wechat.utils.SleepUtils;
|
||||
|
||||
/**
|
||||
* 检查微信在线状态
|
||||
* <p>
|
||||
* 如何来感知微信状态?
|
||||
* 微信会有心跳包,LoginServiceImpl.syncCheck()正常在线情况下返回的消息中retcode报文应该为"0",心跳间隔一般在25秒,
|
||||
* 那么可以通过最后收到正常报文的时间来作为判断是否在线的依据。若报文间隔大于60秒,则认为已掉线。
|
||||
* </p>
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年5月17日 下午10:53:15
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class CheckLoginStatusThread implements Runnable {
|
||||
private static Logger LOG = LoggerFactory.getLogger(CheckLoginStatusThread.class);
|
||||
private Core core = Core.getInstance();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (core.isAlive()) {
|
||||
long t1 = System.currentTimeMillis(); // 秒为单位
|
||||
if (t1 - core.getLastNormalRetcodeTime() > 60 * 1000) { // 超过60秒,判为离线
|
||||
core.setAlive(false);
|
||||
LOG.info("微信已离线");
|
||||
}
|
||||
SleepUtils.sleep(10 * 1000); // 休眠10秒
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.xmzs.common.wechat.utils;
|
||||
|
||||
import com.xmzs.common.wechat.utils.enums.OsNameEnum;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
|
||||
/**
|
||||
* 配置信息
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年4月23日 下午2:26:21
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class Config {
|
||||
|
||||
public static final String API_WXAPPID = "API_WXAPPID";
|
||||
|
||||
public static final String picDir = "D://itchat4j";
|
||||
public static final String VERSION = "1.4.1";
|
||||
public static final String BASE_URL = "https://login.weixin.qq.com";
|
||||
public static final String REFERER = "https://wx.qq.com/?&lang=zh_CN&target=t";
|
||||
public static final String OS = "";
|
||||
public static final String DIR = "";
|
||||
public static final String DEFAULT_QR = "QR.jpg";
|
||||
public static final String USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36";
|
||||
public static final String UOS_PATCH_CLIENT_VERSION = "2.0.0";
|
||||
|
||||
public static final String UOS_PATCH_EXTSPAM =
|
||||
"Go8FCIkFEokFCggwMDAwMDAwMRAGGvAESySibk50w5Wb3uTl2c2h64jVVrV7gNs06GFlWplHQbY/5FfiO++1yH4ykCyNPWKXmco+wfQzK5R98D3so7rJ5LmGFvBLjGceleySrc3SOf2Pc1gVehzJgODeS0lDL3/I/0S2SSE98YgKleq6Uqx6ndTy9yaL9qFxJL7eiA/R3SEfTaW1SBoSITIu+EEkXff+Pv8NHOk7N57rcGk1w0ZzRrQDkXTOXFN2iHYIzAAZPIOY45Lsh+A4slpgnDiaOvRtlQYCt97nmPLuTipOJ8Qc5pM7ZsOsAPPrCQL7nK0I7aPrFDF0q4ziUUKettzW8MrAaiVfmbD1/VkmLNVqqZVvBCtRblXb5FHmtS8FxnqCzYP4WFvz3T0TcrOqwLX1M/DQvcHaGGw0B0y4bZMs7lVScGBFxMj3vbFi2SRKbKhaitxHfYHAOAa0X7/MSS0RNAjdwoyGHeOepXOKY+h3iHeqCvgOH6LOifdHf/1aaZNwSkGotYnYScW8Yx63LnSwba7+hESrtPa/huRmB9KWvMCKbDThL/nne14hnL277EDCSocPu3rOSYjuB9gKSOdVmWsj9Dxb/iZIe+S6AiG29Esm+/eUacSba0k8wn5HhHg9d4tIcixrxveflc8vi2/wNQGVFNsGO6tB5WF0xf/plngOvQ1/ivGV/C1Qpdhzznh0ExAVJ6dwzNg7qIEBaw+BzTJTUuRcPk92Sn6QDn2Pu3mpONaEumacjW4w6ipPnPw+g2TfywJjeEcpSZaP4Q3YV5HG8D6UjWA4GSkBKculWpdCMadx0usMomsSS/74QgpYqcPkmamB4nVv1JxczYITIqItIKjD35IGKAUwAA==";
|
||||
|
||||
|
||||
public static final ArrayList<String> API_SPECIAL_USER = new ArrayList<String>(Arrays.asList("filehelper", "weibo",
|
||||
"qqmail", "fmessage", "tmessage", "qmessage", "qqsync", "floatbottle", "lbsapp", "shakeapp", "medianote",
|
||||
"qqfriend", "readerapp", "blogapp", "facebookapp", "masssendapp", "meishiapp", "feedsapp", "voip",
|
||||
"blogappweixin", "brandsessionholder", "weixin", "weixinreminder", "officialaccounts", "wxitil",
|
||||
"notification_messages", "wxid_novlwrv3lqwv11", "gh_22b87fa7cb3c", "userexperience_alarm"));
|
||||
|
||||
/**
|
||||
* 获取文件目录
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月8日 下午10:27:42
|
||||
* @return
|
||||
*/
|
||||
public static String getLocalPath() {
|
||||
String localPath = null;
|
||||
try {
|
||||
localPath = new File("").getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
return localPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取系统平台
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月8日 下午10:27:53
|
||||
*/
|
||||
public static OsNameEnum getOsNameEnum() {
|
||||
String os = System.getProperty("os.name").toUpperCase();
|
||||
if (os.indexOf(OsNameEnum.DARWIN.toString()) >= 0) {
|
||||
return OsNameEnum.DARWIN;
|
||||
} else if (os.indexOf(OsNameEnum.WINDOWS.toString()) >= 0) {
|
||||
return OsNameEnum.WINDOWS;
|
||||
} else if (os.indexOf(OsNameEnum.LINUX.toString()) >= 0) {
|
||||
return OsNameEnum.LINUX;
|
||||
} else if (os.indexOf(OsNameEnum.MAC.toString()) >= 0) {
|
||||
return OsNameEnum.MAC;
|
||||
}
|
||||
return OsNameEnum.OTHER;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.xmzs.common.wechat.utils;
|
||||
|
||||
/**
|
||||
* 常量
|
||||
*
|
||||
* @author https=//github.com/yaphone
|
||||
* @date 创建时间:2017年5月5日 下午11=29=04
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class ConstantConfigEnum {
|
||||
public static final int APPMSGTYPE_TEXT = 1;
|
||||
public static final int APPMSGTYPE_IMG = 2;
|
||||
public static final int APPMSGTYPE_AUDIO = 3;
|
||||
public static final int APPMSGTYPE_VIDEO = 4;
|
||||
public static final int APPMSGTYPE_URL = 5;
|
||||
public static final int APPMSGTYPE_ATTACH = 6;
|
||||
public static final int APPMSGTYPE_OPEN = 7;
|
||||
public static final int APPMSGTYPE_EMOJI = 8;
|
||||
public static final int APPMSGTYPE_VOICE_REMIND = 9;
|
||||
public static final int APPMSGTYPE_SCAN_GOOD = 10;
|
||||
public static final int APPMSGTYPE_GOOD = 13;
|
||||
public static final int APPMSGTYPE_EMOTION = 15;
|
||||
public static final int APPMSGTYPE_CARD_TICKET = 16;
|
||||
public static final int APPMSGTYPE_REALTIME_SHARE_LOCATION = 17;
|
||||
// public static final int APPMSGTYPE_TRANSFERS = 2e3;
|
||||
public static final int APPMSGTYPE_RED_ENVELOPES = 2001;
|
||||
public static final int APPMSGTYPE_READER_TYPE = 100001;
|
||||
public static final int UPLOAD_MEDIA_TYPE_IMAGE = 1;
|
||||
public static final int UPLOAD_MEDIA_TYPE_VIDEO = 2;
|
||||
public static final int UPLOAD_MEDIA_TYPE_AUDIO = 3;
|
||||
public static final int UPLOAD_MEDIA_TYPE_ATTACHMENT = 4;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.xmzs.common.wechat.utils;
|
||||
|
||||
public class MsgKeywords {
|
||||
public static String newFriendStr = "我通过了你的朋友验证请求";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package com.xmzs.common.wechat.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.http.Consts;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.apache.http.client.CookieStore;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.client.entity.UrlEncodedFormEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.cookie.Cookie;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
/**
|
||||
* HTTP访问类,对Apache HttpClient进行简单封装,适配器模式
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年4月9日 下午7:05:04
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class MyHttpClient {
|
||||
private Logger logger = Logger.getLogger("MyHttpClient");
|
||||
|
||||
private static CloseableHttpClient httpClient = HttpClients.createDefault();
|
||||
|
||||
private static MyHttpClient instance = null;
|
||||
|
||||
private static CookieStore cookieStore;
|
||||
|
||||
static {
|
||||
cookieStore = new BasicCookieStore();
|
||||
|
||||
// 将CookieStore设置到httpClient中
|
||||
httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore).build();
|
||||
}
|
||||
|
||||
public static String getCookie(String name) {
|
||||
List<Cookie> cookies = cookieStore.getCookies();
|
||||
for (Cookie cookie : cookies) {
|
||||
if (cookie.getName().equalsIgnoreCase(name)) {
|
||||
return cookie.getValue();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
private MyHttpClient() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取cookies
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月7日 下午8:37:17
|
||||
* @return
|
||||
*/
|
||||
public static MyHttpClient getInstance() {
|
||||
if (instance == null) {
|
||||
synchronized (MyHttpClient.class) {
|
||||
if (instance == null) {
|
||||
instance = new MyHttpClient();
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理GET请求
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月9日 下午7:06:19
|
||||
* @param url
|
||||
* @param params
|
||||
* @return
|
||||
*/
|
||||
public HttpEntity doGet(String url, List<BasicNameValuePair> params, boolean redirect,
|
||||
Map<String, String> headerMap) {
|
||||
HttpEntity entity = null;
|
||||
HttpGet httpGet = new HttpGet();
|
||||
|
||||
try {
|
||||
if (params != null) {
|
||||
String paramStr = EntityUtils.toString(new UrlEncodedFormEntity(params, Consts.UTF_8));
|
||||
httpGet = new HttpGet(url + "?" + paramStr);
|
||||
} else {
|
||||
httpGet = new HttpGet(url);
|
||||
}
|
||||
if (!redirect) {
|
||||
httpGet.setConfig(RequestConfig.custom().setRedirectsEnabled(false).build()); // 禁止重定向
|
||||
}
|
||||
httpGet.setHeader("User-Agent", Config.USER_AGENT);
|
||||
httpGet.setHeader("client-version", Config.UOS_PATCH_CLIENT_VERSION);
|
||||
httpGet.setHeader("extspam", Config.UOS_PATCH_EXTSPAM);
|
||||
httpGet.setHeader("referer", Config.REFERER);
|
||||
if (headerMap != null) {
|
||||
Set<Entry<String, String>> entries = headerMap.entrySet();
|
||||
for (Entry<String, String> entry : entries) {
|
||||
httpGet.setHeader(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
CloseableHttpResponse response = httpClient.execute(httpGet);
|
||||
entity = response.getEntity();
|
||||
} catch (ClientProtocolException e) {
|
||||
logger.info(e.getMessage());
|
||||
} catch (IOException e) {
|
||||
logger.info(e.getMessage());
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理POST请求
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月9日 下午7:06:35
|
||||
* @param url
|
||||
* @param params
|
||||
* @return
|
||||
*/
|
||||
public HttpEntity doPost(String url, String paramsStr) {
|
||||
HttpEntity entity = null;
|
||||
HttpPost httpPost = new HttpPost();
|
||||
try {
|
||||
StringEntity params = new StringEntity(paramsStr, Consts.UTF_8);
|
||||
httpPost = new HttpPost(url);
|
||||
httpPost.setEntity(params);
|
||||
httpPost.setHeader("Content-type", "application/json; charset=utf-8");
|
||||
httpPost.setHeader("User-Agent", Config.USER_AGENT);
|
||||
httpPost.setHeader("client-version", Config.UOS_PATCH_CLIENT_VERSION);
|
||||
httpPost.setHeader("extspam", Config.UOS_PATCH_EXTSPAM);
|
||||
httpPost.setHeader("referer", Config.REFERER);
|
||||
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
entity = response.getEntity();
|
||||
} catch (ClientProtocolException e) {
|
||||
logger.info(e.getMessage());
|
||||
} catch (IOException e) {
|
||||
logger.info(e.getMessage());
|
||||
}
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件到服务器
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年5月7日 下午9:19:23
|
||||
* @param url
|
||||
* @param reqEntity
|
||||
* @return
|
||||
*/
|
||||
public HttpEntity doPostFile(String url, HttpEntity reqEntity) {
|
||||
HttpEntity entity = null;
|
||||
HttpPost httpPost = new HttpPost(url);
|
||||
httpPost.setHeader("User-Agent", Config.USER_AGENT);
|
||||
httpPost.setHeader("client-version", Config.UOS_PATCH_CLIENT_VERSION);
|
||||
httpPost.setHeader("extspam", Config.UOS_PATCH_EXTSPAM);
|
||||
httpPost.setHeader("referer", Config.REFERER);
|
||||
|
||||
httpPost.setEntity(reqEntity);
|
||||
try {
|
||||
CloseableHttpResponse response = httpClient.execute(httpPost);
|
||||
entity = response.getEntity();
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.info(e.getMessage());
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
public static CloseableHttpClient getHttpClient() {
|
||||
return httpClient;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.xmzs.common.wechat.utils;
|
||||
|
||||
/**
|
||||
* Created by xiaoxiaomo on 2017/5/6.
|
||||
*/
|
||||
public class SleepUtils {
|
||||
|
||||
/**
|
||||
* 毫秒为单位
|
||||
* @param time
|
||||
*/
|
||||
public static void sleep( long time ){
|
||||
try {
|
||||
Thread.sleep( time );
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.xmzs.common.wechat.utils.enums;
|
||||
|
||||
/**
|
||||
* 消息类型
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年4月23日 下午12:15:00
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public enum MsgCodeEnum {
|
||||
|
||||
// public static final int MSGTYPE_TEXT = 1; // 文本消息类型
|
||||
// public static final int MSGTYPE_IMAGE = 3; // 图片消息
|
||||
// public static final int MSGTYPE_VOICE = 34; // 语音消息
|
||||
// public static final int MSGTYPE_VIDEO = 43; // 小视频消息
|
||||
// public static final int MSGTYPE_MICROVIDEO = 62; // 短视频消息
|
||||
// public static final int MSGTYPE_EMOTICON = 47; // 表情消息
|
||||
// public static final int MSGTYPE_APP = 49;
|
||||
// public static final int MSGTYPE_VOIPMSG = 50;
|
||||
// public static final int MSGTYPE_VOIPNOTIFY = 52;
|
||||
// public static final int MSGTYPE_VOIPINVITE = 53;
|
||||
// public static final int MSGTYPE_LOCATION = 48;
|
||||
// public static final int MSGTYPE_STATUSNOTIFY = 51;
|
||||
// public static final int MSGTYPE_SYSNOTICE = 9999;
|
||||
// public static final int MSGTYPE_POSSIBLEFRIEND_MSG = 40;
|
||||
// public static final int MSGTYPE_VERIFYMSG = 37;
|
||||
// public static final int MSGTYPE_SHARECARD = 42;
|
||||
// public static final int MSGTYPE_SYS = 10000;
|
||||
// public static final int MSGTYPE_RECALLED = 10002;
|
||||
MSGTYPE_TEXT(1, "文本消息类型"),
|
||||
MSGTYPE_IMAGE(3, "图片消息"),
|
||||
MSGTYPE_VOICE(34, "语音消息"),
|
||||
MSGTYPE_VIDEO(43, "小视频消息"),
|
||||
MSGTYPE_MICROVIDEO(62, "短视频消息"),
|
||||
MSGTYPE_EMOTICON(47, "表情消息"),
|
||||
MSGTYPE_MEDIA(49, "多媒体消息"),
|
||||
MSGTYPE_VOIPMSG(50, ""),
|
||||
MSGTYPE_VOIPNOTIFY(52, ""),
|
||||
MSGTYPE_VOIPINVITE(53, ""),
|
||||
MSGTYPE_LOCATION(48, ""),
|
||||
MSGTYPE_STATUSNOTIFY(51, ""),
|
||||
MSGTYPE_SYSNOTICE(9999, ""),
|
||||
MSGTYPE_POSSIBLEFRIEND_MSG(40, ""),
|
||||
MSGTYPE_VERIFYMSG(37, "好友请求"),
|
||||
MSGTYPE_SHARECARD(42, ""),
|
||||
MSGTYPE_SYS(10000, "系统消息"),
|
||||
MSGTYPE_RECALLED(10002, "")
|
||||
|
||||
;
|
||||
|
||||
private int code;
|
||||
private String type;
|
||||
|
||||
MsgCodeEnum(int code, String type) {
|
||||
this.code = code;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.xmzs.common.wechat.utils.enums;
|
||||
|
||||
|
||||
/**
|
||||
* 消息类型枚举类
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年5月13日 下午11:53:00
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public enum MsgTypeEnum {
|
||||
TEXT("Text", "文本消息"),
|
||||
PIC("Pic", "图片消息"),
|
||||
VOICE("Voice", "语音消息"),
|
||||
VIEDO("Viedo", "小视频消息"),
|
||||
NAMECARD("NameCard", "名片消息"),
|
||||
SYS("Sys", "系统消息"),
|
||||
VERIFYMSG("VerifyMsg", "添加好友"),
|
||||
MEDIA("app", "文件消息");
|
||||
|
||||
private String type;
|
||||
private String code;
|
||||
|
||||
MsgTypeEnum(String type, String code) {
|
||||
this.type = type;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.xmzs.common.wechat.utils.enums;
|
||||
|
||||
/**
|
||||
* 系统平台
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年4月8日 下午10:36:28
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public enum OsNameEnum {
|
||||
WINDOWS, LINUX, DARWIN, MAC, OTHER
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.xmzs.common.wechat.utils.enums;
|
||||
|
||||
/**
|
||||
* 返回结构枚举类
|
||||
* <p>
|
||||
* Created by xiaoxiaomo on 2017/5/6.
|
||||
*/
|
||||
public enum ResultEnum {
|
||||
|
||||
SUCCESS("200", "成功"),
|
||||
WAIT_CONFIRM("201", "请在手机上点击确认"),
|
||||
WAIT_SCAN("400", "请扫描二维码");
|
||||
|
||||
private String code;
|
||||
private String msg;
|
||||
|
||||
ResultEnum(String code, String msg) {
|
||||
this.code = code;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
// public static MsgInfoEnum getCode(String code) {
|
||||
// switch (code) {
|
||||
// case "Text":
|
||||
// return MsgInfoEnum.TEXT;
|
||||
// default:
|
||||
// return MsgInfoEnum.VIDEO;
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.xmzs.common.wechat.utils.enums;
|
||||
|
||||
public enum RetCodeEnum {
|
||||
|
||||
NORMAL("0", "普通"),
|
||||
LOGIN_OUT("1102", "退出"),
|
||||
LOGIN_OTHERWHERE("1101", "其它地方登陆"),
|
||||
MOBILE_LOGIN_OUT("1102", "移动端退出"),
|
||||
UNKOWN("9999", "未知")
|
||||
|
||||
;
|
||||
|
||||
|
||||
private String code;
|
||||
private String type;
|
||||
|
||||
RetCodeEnum(String code, String type) {
|
||||
this.code = code;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.xmzs.common.wechat.utils.enums;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Created by xiaoxiaomo on 2017/5/7.
|
||||
*/
|
||||
public enum StorageLoginInfoEnum {
|
||||
|
||||
//URL
|
||||
url("url",new String()),
|
||||
fileUrl("fileUrl",new String()),
|
||||
syncUrl("syncUrl",new String()),
|
||||
|
||||
deviceid("deviceid",new String()), //生成15位随机数
|
||||
|
||||
//baseRequest
|
||||
skey("skey",new String()),
|
||||
wxsid("wxsid",new String()),
|
||||
wxuin("wxuin",new String()),
|
||||
pass_ticket("pass_ticket",new String()),
|
||||
|
||||
|
||||
InviteStartCount("InviteStartCount",0),
|
||||
User("User",new JSONObject()),
|
||||
SyncKey("SyncKey",new JSONObject()),
|
||||
synckey("synckey",new String()),
|
||||
|
||||
|
||||
|
||||
MemberCount("MemberCount",new String()),
|
||||
MemberList("MemberList",new JSONArray()),
|
||||
|
||||
|
||||
|
||||
;
|
||||
|
||||
private String key;
|
||||
private Object type;
|
||||
|
||||
StorageLoginInfoEnum(String key, Object type) {
|
||||
this.key = key;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
public Object getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.xmzs.common.wechat.utils.enums;
|
||||
|
||||
/**
|
||||
* URL
|
||||
* Created by xiaoxiaomo on 2017/5/6.
|
||||
*/
|
||||
public enum URLEnum {
|
||||
|
||||
|
||||
|
||||
BASE_URL("https://login.weixin.qq.com","基本的URL"),
|
||||
UUID_URL(BASE_URL.url+"/jslogin","UUIDLURL"),
|
||||
QRCODE_URL(BASE_URL.url+"/qrcode/","初始化URL"),
|
||||
STATUS_NOTIFY_URL(BASE_URL.url+"/webwxstatusnotify?lang=zh_CN&pass_ticket=%s","微信状态通知"),
|
||||
LOGIN_URL(BASE_URL.url+"/cgi-bin/mmwebwx-bin/login","登陆URL"),
|
||||
INIT_URL("%s/webwxinit?r=%s&pass_ticket=%s","初始化URL"),
|
||||
SYNC_CHECK_URL("/synccheck","检查心跳URL"),
|
||||
WEB_WX_SYNC_URL("%s/webwxsync?sid=%s&skey=%s&pass_ticket=%s","web微信消息同步URL"),
|
||||
WEB_WX_GET_CONTACT("%s/webwxgetcontact","web微信获取联系人信息URL"),
|
||||
WEB_WX_SEND_MSG("%s/webwxsendmsg","发送消息URL"),
|
||||
WEB_WX_UPLOAD_MEDIA("%s/webwxuploadmedia?f=json", "上传文件到服务器"),
|
||||
WEB_WX_GET_MSG_IMG("%s/webwxgetmsgimg", "下载图片消息"),
|
||||
WEB_WX_GET_VOICE("%s/webwxgetvoice", "下载语音消息"),
|
||||
WEB_WX_GET_VIEDO("%s/webwxgetvideo", "下载语音消息"),
|
||||
WEB_WX_PUSH_LOGIN("%s/webwxpushloginurl", "不扫码登陆"),
|
||||
WEB_WX_LOGOUT("%s/webwxlogout", "退出微信"),
|
||||
WEB_WX_BATCH_GET_CONTACT("%s/webwxbatchgetcontact?type=ex&r=%s&lang=zh_CN&pass_ticket=%s", "查询群信息"),
|
||||
WEB_WX_REMARKNAME("%s/webwxoplog?lang=zh_CN&pass_ticket=%s", "修改好友备注"),
|
||||
WEB_WX_VERIFYUSER("%s/webwxverifyuser?r=%s&lang=zh_CN&pass_ticket=%s", "被动添加好友"),
|
||||
WEB_WX_GET_MEDIA("%s/webwxgetmedia", "下载文件")
|
||||
|
||||
|
||||
|
||||
|
||||
;
|
||||
|
||||
private String url;
|
||||
private String msg;
|
||||
|
||||
URLEnum(String url, String msg) {
|
||||
this.url = url;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.xmzs.common.wechat.utils.enums;
|
||||
|
||||
/**
|
||||
* 确认添加好友Enum
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年6月29日 下午9:47:14
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public enum VerifyFriendEnum {
|
||||
|
||||
ADD(2, "添加"),
|
||||
ACCEPT(3, "接受");
|
||||
|
||||
private int code;
|
||||
private String desc;
|
||||
|
||||
private VerifyFriendEnum(int code, String desc) {
|
||||
this.code = code;
|
||||
this.desc = desc;
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.xmzs.common.wechat.utils.enums.parameters;
|
||||
|
||||
/**
|
||||
*
|
||||
* 基本请求参数
|
||||
* 1. webWxInit 初始化
|
||||
* 2. wxStatusNotify 微信状态通知
|
||||
*
|
||||
* <p>
|
||||
* Created by xiaoxiaomo on 2017/5/7.
|
||||
*/
|
||||
public enum BaseParaEnum {
|
||||
|
||||
Uin("Uin", "wxuin"),
|
||||
Sid("Sid", "wxsid"),
|
||||
Skey("Skey", "skey"),
|
||||
DeviceID("DeviceID", "pass_ticket");
|
||||
|
||||
private String para;
|
||||
private String value;
|
||||
|
||||
BaseParaEnum(String para, String value) {
|
||||
this.para = para;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String para() {
|
||||
return para;
|
||||
}
|
||||
|
||||
|
||||
public Object value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.xmzs.common.wechat.utils.enums.parameters;
|
||||
|
||||
/**
|
||||
* 登陆
|
||||
* <p>
|
||||
* Created by xiaoxiaomo on 2017/5/7.
|
||||
*/
|
||||
public enum LoginParaEnum {
|
||||
|
||||
LOGIN_ICON("loginicon", "true"),
|
||||
UUID("uuid", ""),
|
||||
TIP("tip", "0"),
|
||||
R("r", ""),
|
||||
_1("_", "");
|
||||
|
||||
private String para;
|
||||
private String value;
|
||||
|
||||
LoginParaEnum(String para, String value) {
|
||||
this.para = para;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String para() {
|
||||
return para;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.xmzs.common.wechat.utils.enums.parameters;
|
||||
|
||||
/**
|
||||
* 状态通知
|
||||
* <p>
|
||||
* Created by xiaoxiaomo on 2017/5/7.
|
||||
*/
|
||||
public enum StatusNotifyParaEnum {
|
||||
|
||||
CODE("Code", "3"),
|
||||
FROM_USERNAME("FromUserName", ""),
|
||||
TO_USERNAME("ToUserName", ""),
|
||||
CLIENT_MSG_ID("ClientMsgId", ""); //时间戳
|
||||
|
||||
private String para;
|
||||
private String value;
|
||||
|
||||
StatusNotifyParaEnum(String para, String value) {
|
||||
this.para = para;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String para() {
|
||||
return para;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.xmzs.common.wechat.utils.enums.parameters;
|
||||
|
||||
/**
|
||||
* UUID
|
||||
* <p>
|
||||
* Created by xiaoxiaomo on 2017/5/7.
|
||||
*/
|
||||
public enum UUIDParaEnum {
|
||||
|
||||
APP_ID("appid", "wx782c26e4c19acffb"),
|
||||
FUN("fun", "new"),
|
||||
LANG("lang", "zh_CN"),
|
||||
REDIRECT_URL("redirect_uri",
|
||||
"https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?mod=desktop"),
|
||||
_1("_", "时间戳");
|
||||
|
||||
|
||||
private String para;
|
||||
private String value;
|
||||
|
||||
UUIDParaEnum(String para, String value) {
|
||||
this.para = para;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String para() {
|
||||
return para;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
package com.xmzs.common.wechat.utils.tools;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.vdurmont.emoji.EmojiParser;
|
||||
|
||||
import com.xmzs.common.wechat.utils.Config;
|
||||
import com.xmzs.common.wechat.utils.enums.OsNameEnum;
|
||||
|
||||
/**
|
||||
* 常用工具类
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年4月8日 下午10:59:55
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class CommonTools {
|
||||
|
||||
public static boolean printQr(String qrPath) {
|
||||
|
||||
switch (Config.getOsNameEnum()) {
|
||||
case WINDOWS:
|
||||
if (Config.getOsNameEnum().equals(OsNameEnum.WINDOWS)) {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
try {
|
||||
runtime.exec("cmd /c start " + qrPath);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MAC:
|
||||
if (Config.getOsNameEnum().equals(OsNameEnum.MAC)) {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
try {
|
||||
runtime.exec("open " + qrPath);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean clearScreen() {
|
||||
switch (Config.getOsNameEnum()) {
|
||||
case WINDOWS:
|
||||
if (Config.getOsNameEnum().equals(OsNameEnum.WINDOWS)) {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
try {
|
||||
runtime.exec("cmd /c " + "cls");
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 正则表达式处理工具
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月9日 上午12:27:10
|
||||
* @return
|
||||
*/
|
||||
public static Matcher getMatcher(String regEx, String text) {
|
||||
Pattern pattern = Pattern.compile(regEx);
|
||||
Matcher matcher = pattern.matcher(text);
|
||||
return matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* xml解析器
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月9日 下午6:24:25
|
||||
* @param text
|
||||
* @return
|
||||
*/
|
||||
public static Document xmlParser(String text) {
|
||||
Document doc = null;
|
||||
StringReader sr = new StringReader(text);
|
||||
InputSource is = new InputSource(sr);
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
try {
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
doc = builder.parse(is);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
public static JSONObject structFriendInfo(JSONObject userObj) {
|
||||
Map<String, Object> friendInfoTemplate = new HashMap<String, Object>();
|
||||
friendInfoTemplate.put("UserName", "");
|
||||
friendInfoTemplate.put("City", "");
|
||||
friendInfoTemplate.put("DisplayName", "");
|
||||
friendInfoTemplate.put("PYQuanPin", "");
|
||||
friendInfoTemplate.put("RemarkPYInitial", "");
|
||||
friendInfoTemplate.put("Province", "");
|
||||
friendInfoTemplate.put("KeyWord", "");
|
||||
friendInfoTemplate.put("RemarkName", "");
|
||||
friendInfoTemplate.put("PYInitial", "");
|
||||
friendInfoTemplate.put("EncryChatRoomId", "");
|
||||
friendInfoTemplate.put("Alias", "");
|
||||
friendInfoTemplate.put("Signature", "");
|
||||
friendInfoTemplate.put("NickName", "");
|
||||
friendInfoTemplate.put("RemarkPYQuanPin", "");
|
||||
friendInfoTemplate.put("HeadImgUrl", "");
|
||||
|
||||
friendInfoTemplate.put("UniFriend", 0);
|
||||
friendInfoTemplate.put("Sex", 0);
|
||||
friendInfoTemplate.put("AppAccountFlag", 0);
|
||||
friendInfoTemplate.put("VerifyFlag", 0);
|
||||
friendInfoTemplate.put("ChatRoomId", 0);
|
||||
friendInfoTemplate.put("HideInputBarFlag", 0);
|
||||
friendInfoTemplate.put("AttrStatus", 0);
|
||||
friendInfoTemplate.put("SnsFlag", 0);
|
||||
friendInfoTemplate.put("MemberCount", 0);
|
||||
friendInfoTemplate.put("OwnerUin", 0);
|
||||
friendInfoTemplate.put("ContactFlag", 0);
|
||||
friendInfoTemplate.put("Uin", 0);
|
||||
friendInfoTemplate.put("StarFriend", 0);
|
||||
friendInfoTemplate.put("Statues", 0);
|
||||
|
||||
friendInfoTemplate.put("MemberList", new ArrayList<Object>());
|
||||
|
||||
JSONObject r = new JSONObject();
|
||||
Set<String> keySet = friendInfoTemplate.keySet();
|
||||
for (String key : keySet) {
|
||||
if (userObj.containsKey(key)) {
|
||||
r.put(key, userObj.get(key));
|
||||
} else {
|
||||
r.put(key, friendInfoTemplate.get(key));
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
public static String getSynckey(JSONObject obj) {
|
||||
JSONArray obj2 = obj.getJSONArray("List");
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < obj2.size(); i++) {
|
||||
JSONObject obj3 = (JSONObject) JSON.toJSON(obj2.get(i));
|
||||
sb.append(obj3.get("Val") + "|");
|
||||
}
|
||||
return sb.substring(0, sb.length() - 1); // 656159784|656159911|656159873|1491905341
|
||||
|
||||
}
|
||||
|
||||
public static JSONObject searchDictList(List<JSONObject> list, String key, String value) {
|
||||
JSONObject r = null;
|
||||
for (JSONObject i : list) {
|
||||
if (i.getString(key).equals(value)) {
|
||||
r = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理emoji表情
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月23日 下午2:39:04
|
||||
* @param d
|
||||
* @param k
|
||||
*/
|
||||
public static void emojiFormatter(JSONObject d, String k) {
|
||||
Matcher matcher = getMatcher("<span class=\"emoji emoji(.{1,10})\"></span>", d.getString(k));
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String content = d.getString(k);
|
||||
int lastStart = 0;
|
||||
while (matcher.find()) {
|
||||
String str = matcher.group(1);
|
||||
if (str.length() == 6) {
|
||||
|
||||
} else if (str.length() == 10) {
|
||||
|
||||
} else {
|
||||
str = "&#x" + str + ";";
|
||||
String tmp = content.substring(lastStart, matcher.start());
|
||||
sb.append(tmp + str);
|
||||
lastStart = matcher.end();
|
||||
}
|
||||
}
|
||||
if (lastStart < content.length()) {
|
||||
sb.append(content.substring(lastStart));
|
||||
}
|
||||
if (sb.length() != 0) {
|
||||
d.put(k, EmojiParser.parseToUnicode(sb.toString()));
|
||||
} else {
|
||||
d.put(k, content);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息格式化
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月23日 下午4:19:08
|
||||
* @param d
|
||||
* @param k
|
||||
*/
|
||||
public static void msgFormatter(JSONObject d, String k) {
|
||||
d.put(k, d.getString(k).replace("<br/>", "\n"));
|
||||
emojiFormatter(d, k);
|
||||
// TODO 与emoji表情有部分兼容问题,目前暂未处理解码处理 d.put(k,
|
||||
// StringEscapeUtils.unescapeHtml4(d.getString(k)));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.xmzs.common.wechat.utils.tools;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.message.BasicNameValuePair;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
import com.xmzs.common.wechat.beans.BaseMsg;
|
||||
import com.xmzs.common.wechat.core.Core;
|
||||
import com.xmzs.common.wechat.utils.MyHttpClient;
|
||||
import com.xmzs.common.wechat.utils.enums.MsgTypeEnum;
|
||||
import com.xmzs.common.wechat.utils.enums.URLEnum;
|
||||
|
||||
/**
|
||||
* 下载工具类
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 创建时间:2017年4月21日 下午11:18:46
|
||||
* @version 1.0
|
||||
*
|
||||
*/
|
||||
public class DownloadTools {
|
||||
private static Logger logger = Logger.getLogger("DownloadTools");
|
||||
private static Core core = Core.getInstance();
|
||||
private static MyHttpClient myHttpClient = core.getMyHttpClient();
|
||||
|
||||
/**
|
||||
* 处理下载任务
|
||||
*
|
||||
* @author https://github.com/yaphone
|
||||
* @date 2017年4月21日 下午11:00:25
|
||||
* @param url
|
||||
* @param msgId
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
public static Object getDownloadFn(BaseMsg msg, String type, String path) {
|
||||
Map<String, String> headerMap = new HashMap<String, String>();
|
||||
List<BasicNameValuePair> params = new ArrayList<BasicNameValuePair>();
|
||||
String url = "";
|
||||
if (type.equals(MsgTypeEnum.PIC.getType())) {
|
||||
url = String.format(URLEnum.WEB_WX_GET_MSG_IMG.getUrl(), (String) core.getLoginInfo().get("url"));
|
||||
} else if (type.equals(MsgTypeEnum.VOICE.getType())) {
|
||||
url = String.format(URLEnum.WEB_WX_GET_VOICE.getUrl(), (String) core.getLoginInfo().get("url"));
|
||||
} else if (type.equals(MsgTypeEnum.VIEDO.getType())) {
|
||||
headerMap.put("Range", "bytes=0-");
|
||||
url = String.format(URLEnum.WEB_WX_GET_VIEDO.getUrl(), (String) core.getLoginInfo().get("url"));
|
||||
} else if (type.equals(MsgTypeEnum.MEDIA.getType())) {
|
||||
headerMap.put("Range", "bytes=0-");
|
||||
url = String.format(URLEnum.WEB_WX_GET_MEDIA.getUrl(), (String) core.getLoginInfo().get("fileUrl"));
|
||||
params.add(new BasicNameValuePair("sender", msg.getFromUserName()));
|
||||
params.add(new BasicNameValuePair("mediaid", msg.getMediaId()));
|
||||
params.add(new BasicNameValuePair("filename", msg.getFileName()));
|
||||
}
|
||||
params.add(new BasicNameValuePair("msgid", msg.getNewMsgId()));
|
||||
params.add(new BasicNameValuePair("skey", (String) core.getLoginInfo().get("skey")));
|
||||
HttpEntity entity = myHttpClient.doGet(url, params, true, headerMap);
|
||||
try {
|
||||
OutputStream out = new FileOutputStream(path);
|
||||
byte[] bytes = EntityUtils.toByteArray(entity);
|
||||
out.write(bytes);
|
||||
out.flush();
|
||||
out.close();
|
||||
// Tools.printQr(path);
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.info(e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -14,7 +14,9 @@
|
||||
<module>ruoyi-demo</module>
|
||||
<module>ruoyi-generator</module>
|
||||
<module>ruoyi-job</module>
|
||||
<module>ruoyi-midjourney</module>
|
||||
<module>ruoyi-system</module>
|
||||
<module>ruoyi-live</module>
|
||||
</modules>
|
||||
|
||||
<artifactId>ruoyi-modules</artifactId>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<!--
|
||||
~ MIT License
|
||||
~
|
||||
~ Copyright (c) 2023 OrdinaryRoad
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>live-chat-client-commons</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>live-chat-client-commons-base</artifactId>
|
||||
<name>ordinaryroad-live-chat-client-commons-base</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.constant;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
public class Constants {
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.exception;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/9/5
|
||||
*/
|
||||
public class BaseException extends RuntimeException {
|
||||
|
||||
public BaseException() {
|
||||
}
|
||||
|
||||
public BaseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public BaseException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public BaseException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public BaseException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.listener;
|
||||
|
||||
|
||||
/**
|
||||
* 连接回调
|
||||
*
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
public interface IBaseConnectionListener<T> {
|
||||
|
||||
/**
|
||||
* 连接建立成功
|
||||
*/
|
||||
default void onConnected(T t) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接建立失败
|
||||
*
|
||||
* @param t
|
||||
*/
|
||||
default void onConnectFailed(T t) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接断开
|
||||
*
|
||||
* @param t
|
||||
*/
|
||||
default void onDisconnected(T t) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.listener;
|
||||
|
||||
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.msg.BaseCmdMsg;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.msg.BaseMsg;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.msg.ICmdMsg;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.msg.IMsg;
|
||||
|
||||
/**
|
||||
* Base消息回调
|
||||
*
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
public interface IBaseMsgListener<T, CmdEnum extends Enum<CmdEnum>> {
|
||||
|
||||
/**
|
||||
* 收到消息(所有消息)
|
||||
*
|
||||
* @param msg IMsg
|
||||
*/
|
||||
default void onMsg(T t, IMsg msg) {
|
||||
this.onMsg(msg);
|
||||
}
|
||||
|
||||
default void onMsg(IMsg msg) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到cmd消息(所有cmd)
|
||||
*
|
||||
* @param cmd CmdEnum
|
||||
* @param cmdMsg BaseCmdMsg
|
||||
*/
|
||||
default void onCmdMsg(T t, CmdEnum cmd, ICmdMsg<CmdEnum> cmdMsg) {
|
||||
this.onCmdMsg(cmd, cmdMsg);
|
||||
}
|
||||
|
||||
default void onCmdMsg(CmdEnum cmd, ICmdMsg<CmdEnum> cmdMsg) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到其他cmd消息(存在Enum,但Listener没有对应的回调)
|
||||
*
|
||||
* @param cmd CmdEnum
|
||||
* @param cmdMsg BaseCmdMsg
|
||||
*/
|
||||
default void onOtherCmdMsg(T t, CmdEnum cmd, ICmdMsg<CmdEnum> cmdMsg) {
|
||||
this.onOtherCmdMsg(cmd, cmdMsg);
|
||||
}
|
||||
|
||||
default void onOtherCmdMsg(CmdEnum cmd, ICmdMsg<CmdEnum> cmdMsg) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到未知cmd消息
|
||||
*
|
||||
* @param cmdString 实际收到的cmd字符串
|
||||
* @param msg BaseMsg
|
||||
*/
|
||||
default void onUnknownCmd(T t, String cmdString, IMsg msg) {
|
||||
this.onUnknownCmd(cmdString, msg);
|
||||
}
|
||||
|
||||
default void onUnknownCmd(String cmdString, IMsg msg) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到cmd消息(所有cmd)
|
||||
*
|
||||
* @param cmd CmdEnum
|
||||
* @param cmdMsg BaseCmdMsg
|
||||
* @deprecated use {@link #onCmdMsg(T, Enum, ICmdMsg)}
|
||||
*/
|
||||
default void onCmdMsg(T t, CmdEnum cmd, BaseCmdMsg<CmdEnum> cmdMsg) {
|
||||
this.onCmdMsg(cmd, cmdMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #onCmdMsg(Enum, ICmdMsg)}
|
||||
*/
|
||||
default void onCmdMsg(CmdEnum cmd, BaseCmdMsg<CmdEnum> cmdMsg) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到其他cmd消息(存在Enum,但Listener没有对应的回调)
|
||||
*
|
||||
* @param cmd CmdEnum
|
||||
* @param cmdMsg BaseCmdMsg
|
||||
* @deprecated use {@link #onOtherCmdMsg(T, Enum, ICmdMsg)}
|
||||
*/
|
||||
default void onOtherCmdMsg(T t, CmdEnum cmd, BaseCmdMsg<CmdEnum> cmdMsg) {
|
||||
this.onOtherCmdMsg(cmd, cmdMsg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #onOtherCmdMsg(Enum, ICmdMsg)}
|
||||
*/
|
||||
default void onOtherCmdMsg(CmdEnum cmd, BaseCmdMsg<CmdEnum> cmdMsg) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
/**
|
||||
* 收到未知cmd消息
|
||||
*
|
||||
* @param cmdString 实际收到的cmd字符串
|
||||
* @param msg BaseMsg
|
||||
* @deprecated use {@link #onUnknownCmd(T, String, IMsg)}
|
||||
*/
|
||||
default void onUnknownCmd(T t, String cmdString, BaseMsg msg) {
|
||||
this.onUnknownCmd(cmdString, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #onUnknownCmd(String, IMsg)}
|
||||
*/
|
||||
default void onUnknownCmd(String cmdString, BaseMsg msg) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.listener;
|
||||
|
||||
|
||||
/**
|
||||
* 弹幕消息回调
|
||||
*
|
||||
* @author mjz
|
||||
* @since 0.0.6
|
||||
*/
|
||||
public interface IDanmuMsgListener<T, DanmuMsg> {
|
||||
|
||||
/**
|
||||
* 收到弹幕
|
||||
*/
|
||||
default void onDanmuMsg(T t, DanmuMsg msg) {
|
||||
this.onDanmuMsg(msg);
|
||||
}
|
||||
|
||||
default void onDanmuMsg(DanmuMsg msg) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.listener;
|
||||
|
||||
|
||||
/**
|
||||
* 进入房间消息回调
|
||||
*
|
||||
* @author mjz
|
||||
* @date 2023/12/14
|
||||
* @since 0.0.16
|
||||
*/
|
||||
public interface IEnterRoomMsgListener<T, EnterRoomMsg> {
|
||||
|
||||
/**
|
||||
* 用户进入房间
|
||||
*/
|
||||
default void onEnterRoomMsg(T t, EnterRoomMsg msg) {
|
||||
this.onEnterRoomMsg(msg);
|
||||
}
|
||||
|
||||
default void onEnterRoomMsg(EnterRoomMsg msg) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.listener;
|
||||
|
||||
|
||||
/**
|
||||
* 礼物消息回调
|
||||
*
|
||||
* @author mjz
|
||||
* @since 0.0.8
|
||||
*/
|
||||
public interface IGiftMsgListener<T, GiftMsg> {
|
||||
|
||||
|
||||
/**
|
||||
* 收到礼物
|
||||
*/
|
||||
default void onGiftMsg(T t, GiftMsg msg) {
|
||||
this.onGiftMsg(msg);
|
||||
}
|
||||
|
||||
default void onGiftMsg(GiftMsg msg) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.listener;
|
||||
|
||||
|
||||
/**
|
||||
* 点赞消息回调
|
||||
*
|
||||
* @author mjz
|
||||
* @since 0.2.0
|
||||
*/
|
||||
public interface ILikeMsgListener<T, LikeMsg> {
|
||||
|
||||
/**
|
||||
* 收到点赞
|
||||
*/
|
||||
default void onLikeMsg(T t, LikeMsg msg) {
|
||||
this.onLikeMsg(msg);
|
||||
}
|
||||
|
||||
default void onLikeMsg(LikeMsg msg) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.listener;
|
||||
|
||||
|
||||
/**
|
||||
* 醒目留言消息回调
|
||||
*
|
||||
* @author mjz
|
||||
* @date 2023/9/24
|
||||
* @since 0.0.11
|
||||
*/
|
||||
public interface ISuperChatMsgListener<T, SuperChatMsg> {
|
||||
|
||||
/**
|
||||
* 收到醒目留言
|
||||
*/
|
||||
default void onSuperChatMsg(T t, SuperChatMsg msg) {
|
||||
this.onSuperChatMsg(msg);
|
||||
}
|
||||
|
||||
default void onSuperChatMsg(SuperChatMsg msg) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.msg;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
public abstract class BaseCmdMsg<CmdEnum extends Enum<CmdEnum>> extends BaseMsg
|
||||
implements ICmdMsg<CmdEnum> {
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.msg;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.exception.BaseException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public abstract class BaseMsg implements IMsg {
|
||||
|
||||
public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()
|
||||
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
|
||||
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
|
||||
|
||||
/**
|
||||
* 未知属性都放在这
|
||||
*/
|
||||
private final Map<String, JsonNode> unknownProperties = new HashMap<>();
|
||||
|
||||
@JsonAnyGetter
|
||||
public Map<String, JsonNode> getUnknownProperties() {
|
||||
return unknownProperties;
|
||||
}
|
||||
|
||||
@JsonAnySetter
|
||||
public void setOther(String key, JsonNode value) {
|
||||
this.unknownProperties.put(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
try {
|
||||
return OBJECT_MAPPER.writeValueAsString(this);
|
||||
} catch (JsonProcessingException e) {
|
||||
throw new BaseException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.msg;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/10/2
|
||||
*/
|
||||
public interface ICmdMsg<CmdEnum extends Enum<CmdEnum>> extends IMsg {
|
||||
|
||||
String getCmd();
|
||||
|
||||
void setCmd(String cmd);
|
||||
|
||||
CmdEnum getCmdEnum();
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.msg;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/9/8
|
||||
*/
|
||||
public interface IDanmuMsg extends IMsg {
|
||||
|
||||
/**
|
||||
* 粉丝牌名称
|
||||
*/
|
||||
String getBadgeName();
|
||||
|
||||
/**
|
||||
* 粉丝牌等级
|
||||
*/
|
||||
byte getBadgeLevel();
|
||||
|
||||
/**
|
||||
* 弹幕发送者id
|
||||
*/
|
||||
String getUid();
|
||||
|
||||
/**
|
||||
* 弹幕发送者用户名
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* 弹幕发送者头像地址
|
||||
*
|
||||
* @since 0.0.11
|
||||
*/
|
||||
default String getUserAvatar() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹幕内容
|
||||
*/
|
||||
String getContent();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.msg;
|
||||
|
||||
/**
|
||||
* 入房消息
|
||||
*
|
||||
* @author mjz
|
||||
* @date 2023/12/26
|
||||
* @since 0.0.16
|
||||
*/
|
||||
public interface IEnterRoomMsg extends IMsg {
|
||||
|
||||
/**
|
||||
* 粉丝牌名称
|
||||
*/
|
||||
String getBadgeName();
|
||||
|
||||
/**
|
||||
* 粉丝牌等级
|
||||
*/
|
||||
byte getBadgeLevel();
|
||||
|
||||
/**
|
||||
* 用户id
|
||||
*/
|
||||
String getUid();
|
||||
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* 头像地址
|
||||
*/
|
||||
default String getUserAvatar() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.msg;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/9/8
|
||||
*/
|
||||
public interface IGiftMsg extends IMsg {
|
||||
|
||||
/**
|
||||
* 粉丝牌名称
|
||||
*/
|
||||
default String getBadgeName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 粉丝牌等级
|
||||
*/
|
||||
default byte getBadgeLevel() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送方id
|
||||
*/
|
||||
String getUid();
|
||||
|
||||
/**
|
||||
* 发送方用户名
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* 发送方头像地址
|
||||
*
|
||||
* @since 0.0.11
|
||||
*/
|
||||
default String getUserAvatar() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 礼物名称
|
||||
*/
|
||||
String getGiftName();
|
||||
|
||||
/**
|
||||
* 礼物图像地址
|
||||
*/
|
||||
String getGiftImg();
|
||||
|
||||
/**
|
||||
* 礼物id
|
||||
*/
|
||||
String getGiftId();
|
||||
|
||||
/**
|
||||
* 礼物数量
|
||||
*/
|
||||
int getGiftCount();
|
||||
|
||||
/**
|
||||
* 单个礼物价格
|
||||
*/
|
||||
int getGiftPrice();
|
||||
|
||||
/**
|
||||
* 接收方id
|
||||
*/
|
||||
String getReceiveUid();
|
||||
|
||||
/**
|
||||
* 接收方用户名
|
||||
*/
|
||||
String getReceiveUsername();
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.msg;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2024/1/31
|
||||
* @since 0.2.0
|
||||
*/
|
||||
public interface ILikeMsg extends IMsg {
|
||||
|
||||
/**
|
||||
* 粉丝牌名称
|
||||
*/
|
||||
default String getBadgeName(){
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 粉丝牌等级
|
||||
*/
|
||||
default byte getBadgeLevel(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 点赞者id
|
||||
*/
|
||||
String getUid();
|
||||
|
||||
/**
|
||||
* 点赞者用户名
|
||||
*/
|
||||
String getUsername();
|
||||
|
||||
/**
|
||||
* 点赞者头像地址
|
||||
*/
|
||||
default String getUserAvatar() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 点赞数
|
||||
*/
|
||||
default int getClickCount() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.msg;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
public interface IMsg extends Serializable {
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.base.msg;
|
||||
|
||||
/**
|
||||
* 醒目留言
|
||||
*
|
||||
* @author mjz
|
||||
* @date 2023/9/22
|
||||
*/
|
||||
public interface ISuperChatMsg extends IDanmuMsg {
|
||||
|
||||
/**
|
||||
* 醒目留言持续时间,单位秒
|
||||
*/
|
||||
int getDuration();
|
||||
|
||||
@Override
|
||||
default String getBadgeName() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
default byte getBadgeLevel() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<!--
|
||||
~ MIT License
|
||||
~
|
||||
~ Copyright (c) 2023 OrdinaryRoad
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>live-chat-client-commons</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>live-chat-client-commons-client</artifactId>
|
||||
<name>live-chat-client-commons-client</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>live-chat-client-commons-base</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>live-chat-client-commons-util</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.client;
|
||||
|
||||
import lombok.Getter;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseMsgListener;
|
||||
import tech.ordinaryroad.live.chat.client.commons.client.config.BaseLiveChatClientConfig;
|
||||
import tech.ordinaryroad.live.chat.client.commons.client.enums.ClientStatusEnums;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
public abstract class BaseLiveChatClient<
|
||||
Config extends BaseLiveChatClientConfig,
|
||||
MsgListener extends IBaseMsgListener<?, ?>
|
||||
> implements IBaseLiveChatClient<MsgListener> {
|
||||
|
||||
private final Config config;
|
||||
@Getter
|
||||
private volatile ClientStatusEnums status = ClientStatusEnums.NEW;
|
||||
protected PropertyChangeSupport statusChangeSupport = new PropertyChangeSupport(status);
|
||||
protected volatile boolean cancelReconnect = false;
|
||||
protected final List<MsgListener> msgListeners = Collections.synchronizedList(new ArrayList<>());
|
||||
|
||||
protected BaseLiveChatClient(Config config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
public Config getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(Runnable success) {
|
||||
this.connect(success, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect() {
|
||||
this.connect(null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect(boolean cancelReconnect) {
|
||||
this.cancelReconnect = cancelReconnect;
|
||||
this.disconnect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Object msg) {
|
||||
this.send(msg, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Object msg, Runnable success) {
|
||||
this.send(msg, success, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Object msg, Consumer<Throwable> failed) {
|
||||
this.send(msg, null, failed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDanmu(Object danmu) {
|
||||
this.sendDanmu(danmu, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDanmu(Object danmu, Runnable success) {
|
||||
this.sendDanmu(danmu, success, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDanmu(Object danmu, Consumer<Throwable> failed) {
|
||||
this.sendDanmu(danmu, null, failed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clickLike(int count) {
|
||||
this.clickLike(count, null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clickLike(int count, Runnable success) {
|
||||
this.clickLike(count, success, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clickLike(int count, Consumer<Throwable> failed) {
|
||||
this.clickLike(count, null, failed);
|
||||
}
|
||||
|
||||
protected abstract void tryReconnect();
|
||||
|
||||
protected abstract String getWebSocketUriString();
|
||||
|
||||
/**
|
||||
* 判断是否处于某个状态,或者处于后续状态
|
||||
*
|
||||
* @param status {@link ClientStatusEnums}
|
||||
* @return false: 还没有到达该状态
|
||||
*/
|
||||
protected boolean checkStatus(ClientStatusEnums status) {
|
||||
return this.status.getCode() >= Objects.requireNonNull(status).getCode();
|
||||
}
|
||||
|
||||
protected void setStatus(ClientStatusEnums status) {
|
||||
ClientStatusEnums oldStatus = this.status;
|
||||
if (oldStatus != status) {
|
||||
this.status = status;
|
||||
this.statusChangeSupport.firePropertyChange("status", oldStatus, status);
|
||||
}
|
||||
}
|
||||
|
||||
public void addStatusChangeListener(PropertyChangeListener listener) {
|
||||
this.statusChangeSupport.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
public void removeStatusChangeListener(PropertyChangeListener listener) {
|
||||
this.statusChangeSupport.removePropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (PropertyChangeListener propertyChangeListener : this.statusChangeSupport.getPropertyChangeListeners()) {
|
||||
this.statusChangeSupport.removePropertyChangeListener(propertyChangeListener);
|
||||
}
|
||||
this.msgListeners.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addMsgListener(MsgListener msgListener) {
|
||||
if (msgListener == null) {
|
||||
return false;
|
||||
}
|
||||
return this.msgListeners.add(msgListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addMsgListeners(List<MsgListener> msgListeners) {
|
||||
if (msgListeners == null || msgListeners.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return this.msgListeners.addAll(msgListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeMsgListener(MsgListener msgListener) {
|
||||
if (msgListener == null) {
|
||||
return false;
|
||||
}
|
||||
return this.msgListeners.remove(msgListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeMsgListeners(List<MsgListener> msgListeners) {
|
||||
if (msgListeners == null || msgListeners.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
return this.msgListeners.removeAll(msgListeners);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllMsgListeners() {
|
||||
this.msgListeners.clear();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.client;
|
||||
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseMsgListener;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/9/5
|
||||
*/
|
||||
public interface IBaseLiveChatClient<MsgListener extends IBaseMsgListener<?, ?>> {
|
||||
|
||||
void init();
|
||||
|
||||
boolean addMsgListener(MsgListener msgListener);
|
||||
|
||||
boolean addMsgListeners(List<MsgListener> msgListeners);
|
||||
|
||||
boolean removeMsgListener(MsgListener msgListener);
|
||||
|
||||
boolean removeMsgListeners(List<MsgListener> msgListeners);
|
||||
|
||||
void removeAllMsgListeners();
|
||||
|
||||
void connect(Runnable success, Consumer<Throwable> failed);
|
||||
|
||||
void connect(Runnable success);
|
||||
|
||||
void connect();
|
||||
|
||||
/**
|
||||
* 手动断开连接
|
||||
*
|
||||
* @param cancelReconnect 取消本次的自动重连(如果启用自动重连)
|
||||
*/
|
||||
void disconnect(boolean cancelReconnect);
|
||||
|
||||
void disconnect();
|
||||
|
||||
void destroy();
|
||||
|
||||
void send(Object msg);
|
||||
|
||||
void send(Object msg, Runnable success, Consumer<Throwable> failed);
|
||||
|
||||
void send(Object msg, Runnable success);
|
||||
|
||||
void send(Object msg, Consumer<Throwable> failed);
|
||||
|
||||
/**
|
||||
* 发送弹幕
|
||||
*
|
||||
* @param danmu 弹幕内容
|
||||
* @since 0.0.6
|
||||
*/
|
||||
void sendDanmu(Object danmu);
|
||||
|
||||
/**
|
||||
* 发送弹幕
|
||||
*
|
||||
* @param danmu 弹幕内容
|
||||
* @since 0.0.6
|
||||
*/
|
||||
void sendDanmu(Object danmu, Runnable success, Consumer<Throwable> failed);
|
||||
|
||||
/**
|
||||
* 发送弹幕
|
||||
*
|
||||
* @param danmu 弹幕内容
|
||||
* @since 0.0.6
|
||||
*/
|
||||
void sendDanmu(Object danmu, Runnable success);
|
||||
|
||||
/**
|
||||
* 发送弹幕
|
||||
*
|
||||
* @param danmu 弹幕内容
|
||||
* @since 0.0.6
|
||||
*/
|
||||
void sendDanmu(Object danmu, Consumer<Throwable> failed);
|
||||
|
||||
/**
|
||||
* 为直播间点赞
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
void clickLike(int count);
|
||||
|
||||
/**
|
||||
* 为直播间点赞
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
void clickLike(int count, Runnable success, Consumer<Throwable> failed);
|
||||
|
||||
/**
|
||||
* 为直播间点赞
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
void clickLike(int count, Runnable success);
|
||||
|
||||
/**
|
||||
* 为直播间点赞
|
||||
*
|
||||
* @since 0.2.0
|
||||
*/
|
||||
void clickLike(int count, Consumer<Throwable> failed);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.client.config;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.exception.BaseException;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
|
||||
/**
|
||||
* 直播间弹幕客户端配置
|
||||
*
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuperBuilder(toBuilder = true)
|
||||
public abstract class BaseLiveChatClientConfig {
|
||||
|
||||
protected PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
|
||||
public static final long DEFAULT_HEARTBEAT_INITIAL_DELAY = 15;
|
||||
public static final long DEFAULT_HEARTBEAT_PERIOD = 25;
|
||||
public static final long DEFAULT_MIN_SEND_DANMU_PERIOD = 3000L;
|
||||
|
||||
private String websocketUri;
|
||||
|
||||
/**
|
||||
* 浏览器中的Cookie
|
||||
*/
|
||||
private String cookie;
|
||||
|
||||
/**
|
||||
* 直播间id
|
||||
*/
|
||||
private Object roomId;
|
||||
|
||||
/**
|
||||
* 是否启用自动重连
|
||||
*/
|
||||
@Builder.Default
|
||||
private boolean autoReconnect = Boolean.TRUE;
|
||||
|
||||
/**
|
||||
* 重试延迟时间(秒),默认5s后重试
|
||||
*/
|
||||
@Builder.Default
|
||||
private int reconnectDelay = 5;
|
||||
|
||||
/**
|
||||
* 首次发送心跳包的延迟时间(秒)
|
||||
*/
|
||||
@Builder.Default
|
||||
private long heartbeatInitialDelay = DEFAULT_HEARTBEAT_INITIAL_DELAY;
|
||||
|
||||
/**
|
||||
* 心跳包发送周期(秒)
|
||||
*/
|
||||
@Builder.Default
|
||||
private long heartbeatPeriod = DEFAULT_HEARTBEAT_PERIOD;
|
||||
|
||||
/**
|
||||
* 最小发送弹幕时间间隔(毫秒)
|
||||
*/
|
||||
@Builder.Default
|
||||
private long minSendDanmuPeriod = DEFAULT_MIN_SEND_DANMU_PERIOD;
|
||||
|
||||
public void setCookie(String cookie) {
|
||||
String oldValue = this.cookie;
|
||||
this.cookie = cookie;
|
||||
this.propertyChangeSupport.firePropertyChange("cookie", oldValue, cookie);
|
||||
}
|
||||
|
||||
public void setRoomId(Object roomId) {
|
||||
if (!(roomId instanceof Number || roomId instanceof String)) {
|
||||
throw new BaseException("房间ID仅支持数字或字符串,所传参数类型:" + roomId.getClass() + "值:" + roomId);
|
||||
}
|
||||
Object oldValue = this.roomId;
|
||||
this.roomId = roomId;
|
||||
this.propertyChangeSupport.firePropertyChange("roomId", oldValue, roomId);
|
||||
}
|
||||
|
||||
public void setWebsocketUri(String websocketUri) {
|
||||
String oldValue = this.websocketUri;
|
||||
this.websocketUri = websocketUri;
|
||||
this.propertyChangeSupport.firePropertyChange("websocketUri", oldValue, websocketUri);
|
||||
}
|
||||
|
||||
public void setMinSendDanmuPeriod(long minSendDanmuPeriod) {
|
||||
long oldValue = this.minSendDanmuPeriod;
|
||||
this.minSendDanmuPeriod = minSendDanmuPeriod;
|
||||
this.propertyChangeSupport.firePropertyChange("minSendDanmuPeriod", oldValue, minSendDanmuPeriod);
|
||||
}
|
||||
|
||||
public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
|
||||
this.propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
|
||||
}
|
||||
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||
this.propertyChangeSupport.addPropertyChangeListener(listener);
|
||||
}
|
||||
|
||||
public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
|
||||
this.propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
|
||||
}
|
||||
|
||||
public void removePropertyChangeListener(PropertyChangeListener listener) {
|
||||
this.propertyChangeSupport.removePropertyChangeListener(listener);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.client.enums;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
public enum ClientStatusEnums {
|
||||
/**
|
||||
* 新创建
|
||||
*/
|
||||
NEW(0),
|
||||
|
||||
/**
|
||||
* 已初始化
|
||||
*/
|
||||
INITIALIZED(1),
|
||||
|
||||
/**
|
||||
* 连接中
|
||||
*/
|
||||
CONNECTING(100),
|
||||
|
||||
/**
|
||||
* 重新连接中
|
||||
*/
|
||||
RECONNECTING(101),
|
||||
|
||||
/**
|
||||
* 已连接
|
||||
*/
|
||||
CONNECTED(200),
|
||||
|
||||
/**
|
||||
* 连接失败
|
||||
*/
|
||||
CONNECT_FAILED(401),
|
||||
|
||||
/**
|
||||
* 已断开连接
|
||||
*/
|
||||
DISCONNECTED(400),
|
||||
|
||||
/**
|
||||
* 已销毁
|
||||
*/
|
||||
DESTROYED(-1),
|
||||
;
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
ClientStatusEnums(int order) {
|
||||
this.code = order;
|
||||
}
|
||||
|
||||
private final int code;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>live-chat-client-commons</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>live-chat-client-commons-util</artifactId>
|
||||
<name>live-chat-client-commons-util</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.util;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/8/27
|
||||
*/
|
||||
public class OrLiveChatCookieUtil {
|
||||
|
||||
public static String toString(List<HttpCookie> cookies) {
|
||||
if (CollUtil.isEmpty(cookies)) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
return cookies.stream().map(httpCookie -> {
|
||||
httpCookie.setVersion(0);
|
||||
return httpCookie.toString();
|
||||
}).collect(Collectors.joining("; "));
|
||||
}
|
||||
|
||||
public static Map<String, String> parseCookieString(String cookies) {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
if (StrUtil.isNotBlank(cookies) && !StrUtil.isNullOrUndefined(cookies)) {
|
||||
try {
|
||||
String[] split = cookies.split("; ");
|
||||
for (String s : split) {
|
||||
String[] split1 = s.split("=");
|
||||
map.put(split1[0], split1[1]);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("cookie解析失败 " + cookies, e);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public static String getCookieByName(Map<String, String> cookieMap, String name, Supplier<String> supplier) {
|
||||
String str = MapUtil.getStr(cookieMap, name);
|
||||
return str == null ? supplier.get() : str;
|
||||
}
|
||||
|
||||
public static String getCookieByName(String cookie, String name, Supplier<String> supplier) {
|
||||
String str = MapUtil.getStr(parseCookieString(cookie), name);
|
||||
return str == null ? supplier.get() : str;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.util;
|
||||
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/12/2
|
||||
*/
|
||||
public class OrLiveChatNumberUtil extends NumberUtil {
|
||||
|
||||
public static long parseLong(Object object){
|
||||
return NumberUtil.parseLong(StrUtil.toStringOrNull(object));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.util;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/8/28
|
||||
*/
|
||||
public class OrLiveChatReflectUtil extends ReflectUtil {
|
||||
|
||||
public static Method getGetterMethod(Class<?> objectClass, String key) {
|
||||
Method method;
|
||||
if (key.startsWith("is")) {
|
||||
method = ReflectUtil.getMethodByNameIgnoreCase(objectClass, key);
|
||||
if (method == null) {
|
||||
ReflectUtil.getMethodByNameIgnoreCase(objectClass, "get" + key);
|
||||
}
|
||||
} else {
|
||||
method = ReflectUtil.getMethodByNameIgnoreCase(objectClass, "get" + key);
|
||||
}
|
||||
return method;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.commons.util;
|
||||
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/9/7
|
||||
*/
|
||||
public class OrLocalDateTimeUtil extends LocalDateTimeUtil {
|
||||
|
||||
public static ZoneId ZONE_ID_CTT = ZoneId.of(ZoneId.SHORT_IDS.get("CTT"));
|
||||
|
||||
/**
|
||||
* 获取中国标准时间的当前时间戳(毫秒)
|
||||
*/
|
||||
public static long zonedCurrentTimeMillis() {
|
||||
ZonedDateTime now = ZonedDateTime.now(ZONE_ID_CTT);
|
||||
return now.toEpochSecond() * 1000 + now.getNano() / 1_000_000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取中国标准时间的当前时间戳(秒)
|
||||
*/
|
||||
public static long zonedCurrentTimeSecs() {
|
||||
return ZonedDateTime.now(ZONE_ID_CTT).toEpochSecond();
|
||||
}
|
||||
}
|
||||
43
ruoyi-modules/ruoyi-live/live-chat-client-commons/pom.xml
Normal file
@@ -0,0 +1,43 @@
|
||||
<!--
|
||||
~ MIT License
|
||||
~
|
||||
~ Copyright (c) 2023 OrdinaryRoad
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>ruoyi-live</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<artifactId>live-chat-client-commons</artifactId>
|
||||
|
||||
<modules>
|
||||
<module>live-chat-client-commons-base</module>
|
||||
<module>live-chat-client-commons-util</module>
|
||||
<module>live-chat-client-commons-client</module>
|
||||
</modules>
|
||||
</project>
|
||||
@@ -0,0 +1,54 @@
|
||||
<!--
|
||||
~ MIT License
|
||||
~
|
||||
~ Copyright (c) 2023 OrdinaryRoad
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>live-chat-client-servers</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>live-chat-client-servers-netty-client</artifactId>
|
||||
<name>live-chat-client-servers-netty</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>live-chat-client-commons-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>live-chat-client-servers-netty</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.servers.netty.client.base;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.channel.*;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.handler.codec.http.HttpClientCodec;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.exception.BaseException;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseConnectionListener;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseMsgListener;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.msg.IMsg;
|
||||
import tech.ordinaryroad.live.chat.client.commons.client.BaseLiveChatClient;
|
||||
import tech.ordinaryroad.live.chat.client.commons.client.enums.ClientStatusEnums;
|
||||
import tech.ordinaryroad.live.chat.client.servers.netty.client.config.BaseNettyClientConfig;
|
||||
import tech.ordinaryroad.live.chat.client.servers.netty.handler.base.BaseBinaryFrameHandler;
|
||||
import tech.ordinaryroad.live.chat.client.servers.netty.handler.base.BaseConnectionHandler;
|
||||
|
||||
import javax.net.ssl.SSLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BaseNettyClient
|
||||
<Config extends BaseNettyClientConfig,
|
||||
CmdEnum extends Enum<CmdEnum>,
|
||||
Msg extends IMsg,
|
||||
MsgListener extends IBaseMsgListener<BinaryFrameHandler, CmdEnum>,
|
||||
ConnectionHandler extends BaseConnectionHandler<ConnectionHandler>,
|
||||
BinaryFrameHandler extends BaseBinaryFrameHandler<BinaryFrameHandler, CmdEnum, Msg, MsgListener>
|
||||
>
|
||||
extends BaseLiveChatClient<Config, MsgListener> {
|
||||
|
||||
@Getter
|
||||
private final EventLoopGroup workerGroup;
|
||||
@Getter
|
||||
private final Bootstrap bootstrap = new Bootstrap();
|
||||
private BinaryFrameHandler binaryFrameHandler;
|
||||
private ConnectionHandler connectionHandler;
|
||||
private IBaseConnectionListener<ConnectionHandler> connectionListener;
|
||||
private Channel channel;
|
||||
@Getter
|
||||
private URI websocketUri;
|
||||
protected IBaseConnectionListener<ConnectionHandler> clientConnectionListener;
|
||||
/**
|
||||
* 控制弹幕发送频率
|
||||
*/
|
||||
private volatile long lastSendDanmuTimeInMillis;
|
||||
|
||||
public abstract ConnectionHandler initConnectionHandler(IBaseConnectionListener<ConnectionHandler> clientConnectionListener);
|
||||
|
||||
public abstract BinaryFrameHandler initBinaryFrameHandler();
|
||||
|
||||
protected BaseNettyClient(Config config, EventLoopGroup workerGroup, IBaseConnectionListener<ConnectionHandler> connectionListener) {
|
||||
super(config);
|
||||
this.workerGroup = workerGroup;
|
||||
this.connectionListener = connectionListener;
|
||||
}
|
||||
|
||||
public void onConnected(ConnectionHandler connectionHandler) {
|
||||
this.setStatus(ClientStatusEnums.CONNECTED);
|
||||
if (this.connectionListener != null) {
|
||||
this.connectionListener.onConnected(connectionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public void onConnectFailed(ConnectionHandler connectionHandler) {
|
||||
this.setStatus(ClientStatusEnums.CONNECT_FAILED);
|
||||
tryReconnect();
|
||||
if (this.connectionListener != null) {
|
||||
this.connectionListener.onConnectFailed(connectionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
public void onDisconnected(ConnectionHandler connectionHandler) {
|
||||
this.setStatus(ClientStatusEnums.DISCONNECTED);
|
||||
tryReconnect();
|
||||
if (this.connectionListener != null) {
|
||||
this.connectionListener.onDisconnected(connectionHandler);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
if (checkStatus(ClientStatusEnums.INITIALIZED)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
this.websocketUri = new URI(getWebSocketUriString());
|
||||
SslContext sslCtx = SslContextBuilder.forClient().build();
|
||||
|
||||
this.clientConnectionListener = new IBaseConnectionListener<ConnectionHandler>() {
|
||||
@Override
|
||||
public void onConnected(ConnectionHandler connectionHandler) {
|
||||
BaseNettyClient.this.onConnected(connectionHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectFailed(ConnectionHandler connectionHandler) {
|
||||
BaseNettyClient.this.onConnectFailed(connectionHandler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(ConnectionHandler connectionHandler) {
|
||||
BaseNettyClient.this.onDisconnected(connectionHandler);
|
||||
}
|
||||
};
|
||||
this.binaryFrameHandler = this.initBinaryFrameHandler();
|
||||
this.connectionHandler = this.initConnectionHandler(this.clientConnectionListener);
|
||||
|
||||
this.bootstrap.group(this.workerGroup)
|
||||
// 创建Channel
|
||||
.channel(NioSocketChannel.class)
|
||||
.remoteAddress(this.websocketUri.getHost(), getInetPort())
|
||||
.option(ChannelOption.TCP_NODELAY, true)
|
||||
.option(ChannelOption.SO_KEEPALIVE, true)
|
||||
// Channel配置
|
||||
.handler(new ChannelInitializer<SocketChannel>() {
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) {
|
||||
// 责任链
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
// 放到第一位 addFirst 支持wss链接服务端
|
||||
pipeline.addFirst(sslCtx.newHandler(ch.alloc(), BaseNettyClient.this.websocketUri.getHost(), getInetPort()));
|
||||
|
||||
// 添加一个http的编解码器
|
||||
pipeline.addLast(new HttpClientCodec());
|
||||
// 添加一个用于支持大数据流的支持
|
||||
pipeline.addLast(new ChunkedWriteHandler());
|
||||
// 添加一个聚合器,这个聚合器主要是将HttpMessage聚合成FullHttpRequest/Response
|
||||
pipeline.addLast(new HttpObjectAggregator(BaseNettyClient.this.getConfig().getAggregatorMaxContentLength()));
|
||||
|
||||
// 连接处理器
|
||||
pipeline.addLast(BaseNettyClient.this.connectionHandler);
|
||||
// 弹幕处理器
|
||||
pipeline.addLast(BaseNettyClient.this.binaryFrameHandler);
|
||||
}
|
||||
});
|
||||
this.setStatus(ClientStatusEnums.INITIALIZED);
|
||||
} catch (URISyntaxException e) {
|
||||
throw new BaseException(e);
|
||||
} catch (SSLException e) {
|
||||
throw new BaseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private int getInetPort() {
|
||||
int port = this.websocketUri.getPort();
|
||||
return port == -1 ? "wss".equalsIgnoreCase(websocketUri.getScheme()) ? 443 : 80 : port;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect(Runnable success, Consumer<Throwable> failed) {
|
||||
if (this.cancelReconnect) {
|
||||
this.cancelReconnect = false;
|
||||
}
|
||||
if (!checkStatus(ClientStatusEnums.INITIALIZED)) {
|
||||
return;
|
||||
}
|
||||
if (getStatus() == ClientStatusEnums.CONNECTED) {
|
||||
return;
|
||||
}
|
||||
if (getStatus() != ClientStatusEnums.RECONNECTING) {
|
||||
this.setStatus(ClientStatusEnums.CONNECTING);
|
||||
}
|
||||
this.bootstrap.connect().addListener((ChannelFutureListener) connectFuture -> {
|
||||
if (connectFuture.isSuccess()) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("连接建立成功!");
|
||||
}
|
||||
this.channel = connectFuture.channel();
|
||||
// 监听是否握手成功
|
||||
this.connectionHandler.getHandshakeFuture().addListener((ChannelFutureListener) handshakeFuture -> {
|
||||
try {
|
||||
connectionHandler.sendAuthRequest(channel);
|
||||
if (success != null) {
|
||||
success.run();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("认证包发送失败,断开连接", e);
|
||||
this.disconnect();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
log.error("连接建立失败", connectFuture.cause());
|
||||
this.onConnectFailed(this.connectionHandler);
|
||||
if (failed != null) {
|
||||
failed.accept(connectFuture.cause());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnect() {
|
||||
if (this.channel == null) {
|
||||
return;
|
||||
}
|
||||
this.channel.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tryReconnect() {
|
||||
if (this.cancelReconnect) {
|
||||
this.cancelReconnect = false;
|
||||
return;
|
||||
}
|
||||
if (!getConfig().isAutoReconnect()) {
|
||||
return;
|
||||
}
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("{}s后将重新连接 {}", getConfig().getReconnectDelay(), getConfig().getRoomId());
|
||||
}
|
||||
workerGroup.schedule(() -> {
|
||||
this.setStatus(ClientStatusEnums.RECONNECTING);
|
||||
this.connect();
|
||||
}, getConfig().getReconnectDelay(), TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Object msg, Runnable success, Consumer<Throwable> failed) {
|
||||
ChannelFuture future = this.channel.writeAndFlush(msg);
|
||||
if (success != null || failed != null) {
|
||||
future.addListener((ChannelFutureListener) channelFuture -> {
|
||||
if (channelFuture.isSuccess()) {
|
||||
if (success != null) {
|
||||
success.run();
|
||||
}
|
||||
} else {
|
||||
if (failed != null) {
|
||||
failed.accept(channelFuture.cause());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
super.destroy();
|
||||
|
||||
// 销毁时不需要重连
|
||||
this.cancelReconnect = true;
|
||||
workerGroup.shutdownGracefully().addListener(future -> {
|
||||
if (future.isSuccess()) {
|
||||
this.setStatus(ClientStatusEnums.DESTROYED);
|
||||
} else {
|
||||
throw new BaseException("client销毁失败", future.cause());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getWebSocketUriString() {
|
||||
return getConfig().getWebsocketUri();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setStatus(ClientStatusEnums status) {
|
||||
if (log.isDebugEnabled()) {
|
||||
if (getStatus() != status) {
|
||||
log.debug("{} 状态变化 {} => {}\n", getClass().getSimpleName(), getStatus(), status);
|
||||
}
|
||||
}
|
||||
super.setStatus(status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDanmu(Object danmu, Runnable success, Consumer<Throwable> failed) {
|
||||
throw new BaseException("暂未支持该功能");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clickLike(int count, Runnable success, Consumer<Throwable> failed) {
|
||||
throw new BaseException("暂未支持该功能");
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送弹幕前判断是否可以发送
|
||||
*
|
||||
* @param checkConnected 是否检查Client连接状态
|
||||
*/
|
||||
protected boolean checkCanSendDanmu(boolean checkConnected) {
|
||||
if (checkConnected && getStatus() != ClientStatusEnums.CONNECTED) {
|
||||
throw new BaseException("连接未建立,无法发送弹幕");
|
||||
}
|
||||
if (System.currentTimeMillis() - this.lastSendDanmuTimeInMillis <= getConfig().getMinSendDanmuPeriod()) {
|
||||
if (log.isWarnEnabled()) {
|
||||
log.warn("发送弹幕频率过快,忽略该次发送");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean checkCanSendDanmu() {
|
||||
return checkCanSendDanmu(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送弹幕后调用该方法
|
||||
*/
|
||||
protected void finishSendDanmu() {
|
||||
this.lastSendDanmuTimeInMillis = System.currentTimeMillis();
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("弹幕发送完成");
|
||||
}
|
||||
}
|
||||
|
||||
public void iteratorMsgListeners(Consumer<MsgListener> consumer) {
|
||||
binaryFrameHandler.iteratorMsgListeners(consumer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.servers.netty.client.config;
|
||||
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketVersion;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import tech.ordinaryroad.live.chat.client.commons.client.config.BaseLiveChatClientConfig;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author mjz
|
||||
* @date 2023/8/26
|
||||
*/
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@SuperBuilder(toBuilder = true)
|
||||
public abstract class BaseNettyClientConfig extends BaseLiveChatClientConfig {
|
||||
|
||||
/**
|
||||
* 聚合器允许的最大消息体长度,默认 64*1024 byte
|
||||
*
|
||||
* @see HttpObjectAggregator#HttpObjectAggregator(int)
|
||||
*/
|
||||
@Builder.Default
|
||||
private int aggregatorMaxContentLength = 64 * 1024;
|
||||
|
||||
/**
|
||||
* WebSocketClientHandshaker最大消息体长度,默认 64*1024 byte
|
||||
*
|
||||
* @see WebSocketClientHandshakerFactory#newHandshaker(URI, WebSocketVersion, String, boolean, HttpHeaders, int)
|
||||
*/
|
||||
@Builder.Default
|
||||
private int maxFramePayloadLength = 64 * 1024;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.servers.netty.client.handler;
|
||||
|
||||
import lombok.Getter;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseMsgListener;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.msg.IMsg;
|
||||
import tech.ordinaryroad.live.chat.client.servers.netty.client.base.BaseNettyClient;
|
||||
import tech.ordinaryroad.live.chat.client.servers.netty.handler.base.BaseBinaryFrameHandler;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* BaseClientBinaryFrameHandler
|
||||
*
|
||||
* @author mjz
|
||||
* @date 2023/8/30
|
||||
*/
|
||||
public abstract class BaseNettyClientBinaryFrameHandler<
|
||||
Client extends BaseNettyClient<?, ?, ?, ?, ?, ?>,
|
||||
BinaryFrameHandler extends BaseBinaryFrameHandler<BinaryFrameHandler, CmdEnum, Msg, MsgListener>,
|
||||
CmdEnum extends Enum<CmdEnum>,
|
||||
Msg extends IMsg,
|
||||
MsgListener extends IBaseMsgListener<BinaryFrameHandler, CmdEnum>>
|
||||
extends BaseBinaryFrameHandler<BinaryFrameHandler, CmdEnum, Msg, MsgListener> {
|
||||
|
||||
@Getter
|
||||
protected final Client client;
|
||||
|
||||
public BaseNettyClientBinaryFrameHandler(List<MsgListener> msgListeners, Client client, long roomId) {
|
||||
super(msgListeners, roomId);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public BaseNettyClientBinaryFrameHandler(List<MsgListener> msgListeners, Client client) {
|
||||
super(msgListeners, client.getConfig().getRoomId());
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public BaseNettyClientBinaryFrameHandler(List<MsgListener> msgListeners, long roomId) {
|
||||
super(msgListeners, roomId);
|
||||
this.client = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2023 OrdinaryRoad
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package tech.ordinaryroad.live.chat.client.servers.netty.client.handler;
|
||||
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketClientHandshaker;
|
||||
import lombok.Getter;
|
||||
import tech.ordinaryroad.live.chat.client.commons.base.listener.IBaseConnectionListener;
|
||||
import tech.ordinaryroad.live.chat.client.servers.netty.client.base.BaseNettyClient;
|
||||
import tech.ordinaryroad.live.chat.client.servers.netty.handler.base.BaseConnectionHandler;
|
||||
|
||||
/**
|
||||
* BaseClientConnectionHandler
|
||||
*
|
||||
* @author mjz
|
||||
* @date 2023/8/27
|
||||
*/
|
||||
public abstract class BaseNettyClientConnectionHandler<
|
||||
Client extends BaseNettyClient<?, ?, ?, ?, ?, ?>,
|
||||
ConnectionHandler extends BaseConnectionHandler<ConnectionHandler>>
|
||||
extends BaseConnectionHandler<ConnectionHandler> {
|
||||
|
||||
@Getter
|
||||
protected final Client client;
|
||||
|
||||
public BaseNettyClientConnectionHandler(WebSocketClientHandshaker handshaker, Client client, IBaseConnectionListener<ConnectionHandler> listener) {
|
||||
super(handshaker, listener);
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
public BaseNettyClientConnectionHandler(WebSocketClientHandshaker handshaker, Client client) {
|
||||
this(handshaker, client, null);
|
||||
}
|
||||
|
||||
public BaseNettyClientConnectionHandler(WebSocketClientHandshaker handshaker, IBaseConnectionListener<ConnectionHandler> listener) {
|
||||
super(handshaker, listener);
|
||||
this.client = null;
|
||||
}
|
||||
|
||||
public BaseNettyClientConnectionHandler(WebSocketClientHandshaker handshaker, long roomId) {
|
||||
super(handshaker, null);
|
||||
this.client = null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
<!--
|
||||
~ MIT License
|
||||
~
|
||||
~ Copyright (c) 2023 OrdinaryRoad
|
||||
~
|
||||
~ Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
~ of this software and associated documentation files (the "Software"), to deal
|
||||
~ in the Software without restriction, including without limitation the rights
|
||||
~ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
~ copies of the Software, and to permit persons to whom the Software is
|
||||
~ furnished to do so, subject to the following conditions:
|
||||
~
|
||||
~ The above copyright notice and this permission notice shall be included in all
|
||||
~ copies or substantial portions of the Software.
|
||||
~
|
||||
~ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
~ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
~ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
~ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
~ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
~ SOFTWARE.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>live-chat-client-servers</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<artifactId>live-chat-client-servers-netty</artifactId>
|
||||
<name>live-chat-client-servers-netty</name>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.xmzs</groupId>
|
||||
<artifactId>live-chat-client-commons-base</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||