34 Commits

Author SHA1 Message Date
7c49702d87 feat(AnimeController): 新增 AnimeController 并更新版本号
- 添加 AnimeController 类,实现获取今日和明日番剧功能
- 更新项目版本号至 QQBot v.1.7.24
2025-05-17 07:16:18 +08:00
95a5bf5467 refactor: 优化代码并更新版本号
- 注释掉 GPT 消息列表相关代码,返回空列表替代
- 优化 AppTools 中异常处理,使用日志记录替代 printStackTrace
- 更新 BiliVideo 中视频下载链接和参数
- 替换 OpenPC 中的 Redis 操作为执行唤醒命令
- 更新 QQBotApplication版本号至 v.1.7.23
-简化 QQBotManager 中的版本信息输出
2025-05-16 09:24:29 +08:00
dbec50b023 Merge remote-tracking branch 'origin/dev' into dev
# Conflicts:
#	src/main/java/com/yutou/qqbot/QQBotApplication.java
#	src/main/java/com/yutou/qqbot/utlis/ZVVImageUtils.java
2025-02-28 18:07:42 +08:00
bc378a5c59 feat(gpt): 优化临时消息处理逻辑
- 修改 clear 方法,移除同步关键字
- 优化 sendTmpMessage 方法,增加日志记录和处理流程
- 更新 GetSeTu 类,改进擦边图片请求的回复文案
-调整 HttpLoggingInterceptor,启用日志输出
- 更新 QQBotApplication 版本号至 1.7.21
2025-02-28 18:07:09 +08:00
e53760f280 feat(bot): 添加张vv表情包生成功能
- 新增 ZVVImageUtils 类实现表情包生成逻辑
- 在 BaiduGPT 类中集成该功能,支持回复如何评论/评价相关问题
- 更新 QQBotApplication 版本号至 1.7.20
- 优化 QQBean 类中的随机数逻辑
- 移除 MessageChainBuilder 类中冗余的 toString 方法
- 删除 QQSetu 类中未使用的 MessageChainBuilder 导包
2025-02-22 13:29:43 +08:00
f770fcc8fb feat(BaiduGPT): 更新雌小鬼角色设定和禁言惩罚机制
- 重新定义角色人格核心,增加甜腻声线和波浪号等特征
- 设计全新的惩戒执行官设定,包括时间计算和大小姐形态切换- 更新模板示例,增加具体的参数注入
- 优化代码格式,提高可读性
-版本号升级到 QQBot v.1.7.19
2025-02-20 10:19:18 +08:00
204907d738 feat(gpt): 新增临时消息发送功能并优化相关命令
- 在 AbsGPTManager 中添加 sendTmpMessage 方法,用于发送临时消息
- 更新 BaiduGPTManager 实现 sendTmpMessage 方法
- 修改 GetSeTu 和 QQBean模型,集成临时消息发送功能
- 更新 BaiduGPT 示例代码,演示临时消息发送用法
- 将 QQBot 版本号更新到 v1.7.17
2025-02-19 18:26:19 +08:00
925a7af045 feat(gpt): 新增临时消息发送功能并优化相关命令
- 在 AbsGPTManager 中添加 sendTmpMessage 方法,用于发送临时消息
- 更新 BaiduGPTManager 实现 sendTmpMessage 方法
- 修改 GetSeTu 和 QQBean模型,集成临时消息发送功能
- 更新 BaiduGPT 示例代码,演示临时消息发送用法
- 将 QQBot 版本号更新到 v1.7.17
2025-02-19 17:10:42 +08:00
a7676fa6db feat(gpt): 更新百度大模型并添加模型列表展示
- 添加了百度大模型列表的展示功能
- 百度大模型新增v2模式,支持ds模型
- 优化了代码格式和结构
- 版本号更新到 QQBot v.1.7.15
2025-02-19 11:09:18 +08:00
9ab99b22e2 feat(bot): 增加定时清理功能并优化消息发送逻辑- 在 AbsGPTManager 中添加定时任务调度功能,用于定期清理资源
- 修改 BaiduGPT 类中的消息发送逻辑,增加定时清理调度
- 更新 BaiduGPTManager 中的 sendMessage 方法,添加用户锁注释
- 升级 qianfan 依赖至0.1.4 版本
- 更新 QQBot 版本号至 1.7.14
2025-02-19 10:24:13 +08:00
5d7ec8e67d refactor(bilibili):暂时屏蔽 B 站直播签到功能
- 将直播签到请求替换为固定返回消息 "已停用"
2025-02-17 10:15:33 +08:00
8ea93c0d61 feat(gpt): 更新模型切换逻辑并移除冗余代码
- 在 BaiduGPT.java 中添加配置保存逻辑,切换模型时更新配置
- 在 BaiduGPTManager 和 SiliconGPTManager 中实现 setModel 方法,统一模型设置逻辑
- 移除 BaiduGPTManager 中的 setModelFor40 和 setModelFor35 方法,简化代码结构
- 更新 QQBotApplication 版本号至1.7.13
2025-02-13 17:51:40 +08:00
9e062976ac feat(gpt): 支持切换 GPT 模型
- 新增配置项 GPT 用于选择 GPT 模型类型
- 实现 BaiduGPT 和 SiliconGPT 两种模型的切换逻辑
- 优化 SiliconGPTManager 初始化,支持自定义模型配置
- 更新 QQBot 版本号至 1.7.12
2025-02-13 17:38:28 +08:00
7637aa2fec Merge remote-tracking branch 'origin/dev' into dev 2025-02-12 18:25:37 +08:00
9347eb9cde fix(BaiduGPT): 修复模型切换显示错误
- 将显示切换的模型名称改为当前 GPT 管理器的简单名称
- 移除了不必要的设置模型名称方法调用
2025-02-12 18:23:10 +08:00
bc483e50b5 refactor(GroupApi): 重构群组禁言接口
- 将 POST 请求改为 GET 请求
-移除 @FormUrlEncoded 注解
- 使用 @Query 注解替换 @Field 注解
2025-02-10 20:18:45 +08:00
b55991798f fix(BaiduGPT): 修复 GPT 切换命令的文本处理
- 修正了从消息中提取目标 GPT 模型名称的逻辑
- 提高了命令的准确性和鲁棒性
2025-02-10 18:17:13 +08:00
12f224b8f3 fix(BaiduGPT): 修复模型设置后未返回问题
- 在设置 Baidu GPT 模型后添加 return 语句,避免继续执行其他逻辑
- 更新应用版本号至 QQBot v.1.7.11.5
2025-02-10 18:06:53 +08:00
068964145f feat(gpt): 支持硅基大模型并优化 GPT功能
- 新增对硅基大模型的支持,可通过指令切换使用
- 优化 GPT 模型切换逻辑,支持更多自定义选项
- 优化日志级别和网络请求超时设置
2025-02-10 13:44:31 +08:00
864d5960a7 commit feat: 新增和改进HTTP请求处理及文件下载功能
- 新增文件下载功能:
  - 新增 `FileBody.java` 类,定义文件下载的数据结构。
  - 新增 `FileCallback.java` 类,处理文件下载的回调逻辑,包括线程池管理和下载进度报告。
  - 新增 `HttpDownloadUtils.java` 工具类,提供异步和同步的文件下载方法,并支持下载接口回调。

- 改进HTTP请求处理:
  - 修改 `NapCatQQ.java` 的 `onResponse` 方法,增加对响应头(`Headers`)的处理。
  - 修改 `GetRequestParams.java` 和 `PostRequestParams.java` 的 `getRequest` 方法,支持在请求中添加自定义Header。
  - 修改 `HttpCallback.java` 的 `onResponse` 方法,增加对响应头(`Headers`)的处理。

- 优化HTTP日志记录:
  - 修改 `HttpLoggingInterceptor.java`,调整日志级别为默认输出响应体,修复日志输出格式和异常处理问题。

- 改进BaseApi类:
  - 修改 `BaseApi.java`,增加对请求头的支持,并优化错误处理逻辑。
  - 在拦截器中处理非成功的响应码,返回统一格式的错误信息,避免业务逻辑中重复处理。

- 更新QQBotManager API回调:
  - 修改 `QQBotManager.java`,更新API回调方法以处理响应头(`Headers`),确保所有回调方法一致。

- 新增GPT API交互功能:
  - 新增 `OpenAiBean.java` 类,定义与OpenAI交互的数据结构,包含响应中的各个字段。
  - 新增 `SiliconGPTManager.java` 类,实现新的GPT管理器,处理与Silicon GPT API的交互。
  - 新增 `GPTApi.java` 类,提供与GPT API交互的基础配置和API调用方法。
  - 新增 `GPTBuilder.java` 类,用于构建发送给GPT API的请求对象。

- 改进GPT管理器:
  - 修改 `AbsGPTManager.java`,添加锁机制,防止同一用户同时发起多个请求。
  - 提供获取消息列表的方法,限制历史消息的最大数量。
  - 支持设置模型版本。
  - 修改 `BaiduGPTManager.java`,移除重复的清除方法,继承自父类。
  - 使用父类提供的方法获取消息列表,简化代码逻辑。

- 优化和重构:
  - 进一步优化 `HttpLoggingInterceptor.java` 的日志记录逻辑,提高性能和可读性。
  - 重构 `BaseApi.java` 初始化方法,简化代码逻辑,提高可读性。

这些改动增强了HTTP请求处理能力,增加了文件下载功能,并为与GPT API的交互提供了支持。
2025-02-08 18:27:00 +08:00
94890f001c feat(gpt): 更新模型并优化版本显示
- 将 ERNIE-3.5-8K 模型替换为 ERNIE-Speed-128K 模型
- 修改 getGPTVersion 方法以直接返回当前模型名称
- 更新 QQBot 版本号至 1.7.10
2025-02-05 18:18:48 +08:00
73566a41e5 build:升级 qianfan 依赖版本
- 将 qianfan 依赖版本从 0.1.1 升级到 0.1.3
2025-02-05 10:40:02 +08:00
56774792c1 feat(gpt): 重构 GPT 管理器并添加新功能
- 重构 AbsGPTManager 抽象类,添加多个新方法
- 新增 sendMessage、textToImage、imageToText、getGPTVersion 等方法
- 添加 setMaxMessageCount 方法,用于设置最大消息数量
- 实现 getManager 方法,根据类获取相应的 GPT 管理器实例
- 更新 BaiduGPTManager 类,实现新增的方法
2025-02-05 10:33:12 +08:00
e7fae929a1 refactor(gpt): 重构 GPT 相关代码并优化功能
- 新增 AbsGPTManager 抽象类,定义 GPT 管理器的通用接口
- 重命名 BaiduGPTManager 类,使其位于 com.yutou.qqbot.gpt 包中
- 更新相关引用和依赖
- 优化部分代码结构,提高可维护性
2025-02-04 18:15:15 +08:00
09305ae824 feat(BaiduGPTManager):优化图片描述功能并添加翻译功能
- 更新图片转文本的提示语,要求更详细的描述
- 添加将英文结果翻译成中文的功能
- 优化翻译提示语,确保准确翻译
- 更新版本号至 QQBot v.1.7.9.1
-调整日志设置的加载顺序
2025-02-04 18:00:07 +08:00
1041dfa909 feat(bot): 增加图片处理功能并优化日志系统
- 新增 textToImage 和 imageToText 功能,实现文本与图片的相互转换
- 优化日志系统,使用 log4j2 实现动态日志记录- 重构 BaiduGPTManager 类,增加多线程支持和错误处理
- 更新 MessageHandleBuild 类,支持 message_id 参数
- 修复部分功能的逻辑错误,提高系统稳定性
2025-02-04 17:13:48 +08:00
237c9273ca feat(QQBot): 更新版本并优化消息发送功能
- 将版本号从 v.1.7.7 升级到 v.1.7.8
-重构 sendMessage 方法,支持发送包含图片和回复的消息
- 更新服务器版本查询 URL 为 HTTPS
- 注释掉版本查询时的服务器消息发送
2025-02-03 14:45:00 +08:00
607c05e028 更新B站相关签到时间 2024-10-09 10:18:21 +08:00
df0337b006 更新为1.7.7 2024-09-20 09:31:59 +08:00
ebe96127e5 调整QQ对外接口 2024-06-09 11:37:39 +08:00
120392be17 更新小米路由器API调用接口
更新小米路由器获取公网IP的接口
2024-06-03 22:01:05 +08:00
6ed42d3e80 update 2024-05-21 16:26:21 +08:00
9903056551 修复百度没有上下文问题 2024-05-21 16:24:25 +08:00
10c4459936 移除多余输出 2024-05-10 13:25:46 +08:00
61 changed files with 2657 additions and 373 deletions

43
pom.xml
View File

@@ -158,7 +158,50 @@
<artifactId>retrofit</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<!-- jsoup HTML parser library @ https://jsoup.org/ -->
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
<dependency>
<groupId>com.baidubce</groupId>
<artifactId>qianfan</artifactId>
<version>0.1.4</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.24.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-bom</artifactId>
<version>2.24.1</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.24.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
</dependencies>
<build>

View File

@@ -10,6 +10,7 @@ import com.yutou.napcat.http.NapCatApi;
import com.yutou.qqbot.QQBotManager;
import com.yutou.qqbot.utlis.Base64Tools;
import lombok.val;
import okhttp3.Headers;
import java.io.File;
import java.util.ArrayList;
@@ -42,7 +43,7 @@ public class NapCatQQ {
.build()
).enqueue(new HttpCallback<SendMessageResponse>() {
@Override
public void onResponse(int code, String status, SendMessageResponse response, String rawResponse) {
public void onResponse(Headers headers, int code, String status, SendMessageResponse response, String rawResponse) {
System.out.println("code = " + code + ", status = " + status + ", response = " + response + ", rawResponse = " + rawResponse);
}

View File

@@ -70,55 +70,64 @@ public class MessageEvent {
public static MessageEvent parseHandle(String jsonString) {
JSONObject json = JSONObject.parseObject(jsonString);
JSONArray array = json.getJSONArray("message");
List<BaseHandle<?>> messageList = new ArrayList<>();
for (Object o : array) {
JSONObject _json = (JSONObject) o;
Type classType = null;
MessageEnum _type = MessageEnum.of(_json.getString("type"));
classType = switch (_type) {
case TEXT -> Text.TextInfo.class;
case IMAGE -> Image.ImageInfo.class;
case AT -> At.AtData.class;
case JSON -> OtherHandle.OtherInfo.class;
case REPLY -> Reply.ReplyInfo.class;
default -> classType;
};
BaseHandle<?> handle = new BaseHandle<>(_type.getType());
if (_type == MessageEnum.JSON) {
handle.setData(JSONObject.parseObject(((JSONObject) o).getJSONObject("data").getString("data"), classType));
} else {
handle.setData(JSONObject.parseObject(((JSONObject) o).getJSONObject("data").toString(), classType));
if (array != null) {
for (Object o : array) {
JSONObject _json = (JSONObject) o;
Type classType = null;
MessageEnum _type = MessageEnum.of(_json.getString("type"));
classType = switch (_type) {
case TEXT -> Text.TextInfo.class;
case IMAGE -> Image.ImageInfo.class;
case AT -> At.AtData.class;
case JSON -> OtherHandle.OtherInfo.class;
case REPLY -> Reply.ReplyInfo.class;
default -> classType;
};
BaseHandle<?> handle = new BaseHandle<>(_type.getType());
if (_type == MessageEnum.JSON) {
handle.setData(JSONObject.parseObject(((JSONObject) o).getJSONObject("data").getString("data"), classType));
} else {
handle.setData(JSONObject.parseObject(((JSONObject) o).getJSONObject("data").toString(), classType));
}
messageList.add(handle);
}
messageList.add(handle);
}
SourceFrom sender = new SourceFrom();
sender.setUserId(json.getJSONObject("sender").getLong("user_id"));
sender.setNickname(json.getJSONObject("sender").getString("nickname"));
sender.setCard(json.getJSONObject("sender").getString("card"));
MessageEvent event = new MessageEvent();
event.setSelfId(json.getLong("self_id"));
event.setUserId(json.getLong("user_id"));
event.setTime(json.getLong("time"));
event.setMessageId(json.getInteger("message_id"));
event.setRealId(json.getInteger("real_id"));
event.setMessageType(json.getString("message_type"));
event.setSource(sender);
event.setRawMessage(json.getString("raw_message"));
event.setFont(json.getInteger("font"));
event.setSubType(json.getString("sub_type"));
event.setMessageFormat(json.getString("message_format"));
event.setSelfId(json.getLong("self_id"));
event.setPostType(json.getString("post_type"));
event.setGroupId(json.getLong("group_id"));
event.setMessage(messageList);
event.setUserId(json.getLong("user_id"));
event.setSubType(json.getString("sub_type"));
try {
SourceFrom sender = new SourceFrom();
sender.setUserId(event.getUserId());
event.setSource(sender);
sender.setUserId(json.getJSONObject("sender").getLong("user_id"));
sender.setNickname(json.getJSONObject("sender").getString("nickname"));
sender.setRole(json.getJSONObject("sender").getString("role"));
sender.setCard(json.getJSONObject("sender").getString("card"));
event.setMessageId(json.getInteger("message_id"));
event.setRealId(json.getInteger("real_id"));
event.setMessageType(json.getString("message_type"));
event.setSource(sender);
event.setRawMessage(json.getString("raw_message"));
event.setFont(json.getInteger("font"));
event.setMessageFormat(json.getString("message_format"));
event.setMessage(messageList);
return event;
} catch (Exception e) {
System.err.println("jsonString:\n" + jsonString);
}
return event;
}
public boolean hasType(MessageEnum messageEnum) {
if(message==null||message.isEmpty()){
return false;
}
for (BaseHandle<?> handle : message) {
if (MessageEnum.of(handle.getType()) == messageEnum) {
return true;
@@ -129,6 +138,9 @@ public class MessageEvent {
public <T extends BaseHandle> List<T> findAllType(Class<T> t) {
List<T> tmp = new ArrayList<>();
if (message == null || message.isEmpty()) {
return tmp;
}
try {
T newed = t.getDeclaredConstructor().newInstance();
for (BaseHandle<?> baseHandle : message) {

View File

@@ -3,6 +3,7 @@ package com.yutou.napcat.handle;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.okhttp.BaseBean;
import com.yutou.qqbot.utlis.StringUtils;
import lombok.Data;
import java.util.ArrayList;
@@ -13,6 +14,7 @@ public class MessageHandleBuild {
private long qq;
private boolean autoEscape;
private boolean isGroup;
private String messageId;
public static MessageHandleBuild create() {
return new MessageHandleBuild();
@@ -41,7 +43,10 @@ public class MessageHandleBuild {
isGroup = group;
return this;
}
public MessageHandleBuild setMessageId(String messageId) {
this.messageId = messageId;
return this;
}
public JSONObject build() {
JSONObject json=new JSONObject();
if(isGroup){
@@ -51,6 +56,9 @@ public class MessageHandleBuild {
}
json.put("auto_escape", autoEscape);
json.put("message", msgList);
if(!StringUtils.isEmpty(messageId)){
json.put("message_id",messageId);
}
return json;
}
}

View File

@@ -6,30 +6,28 @@ import com.yutou.napcat.model.GroupUserBean;
import com.yutou.okhttp.BaseBean;
import com.yutou.okhttp.HttpBody;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
import retrofit2.http.*;
import java.util.List;
public interface GroupApi {
/**
* 禁言
* @param group 群号
* @param user 用户
* @param duration 禁言时长,单位秒
*
* @param group 群号
* @param user 用户
* @param duration 禁言时长,单位秒
*/
@FormUrlEncoded
@POST("/set_group_ban")
@GET("/set_group_ban")
Call<HttpBody<BaseBean>> groupBan(
@Field("group_id") long group,
@Field("user_id") long user,
@Field("duration") long duration
@Query("group_id") long group,
@Query("user_id") long user,
@Query("duration") long duration
);
/**
* 禁言群组全体成员
*
* @param group 群号
*/
@FormUrlEncoded
@@ -61,11 +59,28 @@ public interface GroupApi {
/**
* 获取群组成员列表
*
* @param group 群号
*/
@FormUrlEncoded
@POST("/get_group_member_list")
Call<HttpBody<GroupUserBean>> getGroupUserList(
@Field("group_id") long group
Call<HttpBody<List<GroupUserBean>>> getGroupUserList(
@Field("group_id") long group,
@Field("no_cache")boolean noCache
);
/**
* 设置群组专属头衔
* @param group 群号
* @param user 用户
* @param title 头衔
* @param duration 持续时间,单位秒
*/
@POST("/set_group_special_title")
@FormUrlEncoded
Call<HttpBody<BaseBean>> setGroupSpecialTitle(
@Field("group_id") long group,
@Field("user_id") long user,
@Field("special_title") String title,
@Field("duration") long duration );
}

View File

@@ -44,9 +44,9 @@ public interface MessageAPI {
@Field("message_id") long messageId
);
@FormUrlEncoded
@POST("/get_msg")
Call<HttpBody<MessageBean>> getMessage(
@Field("message_id") long messageId
@Body
JSONObject message
);
}

View File

@@ -65,5 +65,8 @@ public class GroupUserBean extends BaseBean {
// 是否允许修改群名片
@JSONField(name = "card_changeable")
private boolean cardChangeable;
// 禁言剩余时间
@JSONField(name = "shut_up_timestamp")
private long shutUpTimestamp;
}

View File

@@ -5,10 +5,14 @@ import lombok.Data;
@Data
public class SourceFrom {
public static final String USER_ROLE_OWNER="owner";//群主
public static final String USER_ROLE_ADMIN="admin";//管理
public static final String USER_ROLE_MEMBER="member";//成员
@JSONField(name = "user_id")
private long userId;
private String nickname;
private String card;
@JSONField(name = "role")
private String role;
public Long getFromId() {

View File

@@ -0,0 +1,11 @@
package com.yutou.okhttp;
import lombok.Data;
import java.io.InputStream;
@Data
public class FileBody<T> {
InputStream inputStream;
T t;
}

View File

@@ -0,0 +1,112 @@
package com.yutou.okhttp;
import com.yutou.qqbot.utlis.Log;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public abstract class FileCallback<T> implements Callback<FileBody<T>> {
private static ThreadPoolExecutor executor;
private final T bean;
private String savePath;
public FileCallback(T bean, String savePath) {
this.bean = bean;
this.savePath = savePath;
if (executor == null) {
executor = new ThreadPoolExecutor(2, 4, Long.MAX_VALUE, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
}
}
private class DownloadTask implements Runnable {
private T bean;
private Headers headers;
private HttpUrl url;
private InputStream inputStream;
public DownloadTask(T bean, Headers headers, HttpUrl url, InputStream inputStream) {
this.bean = bean;
this.headers = headers;
this.url = url;
this.inputStream = inputStream;
}
@Override
public void run() {
try {
Log.i("开始下载");
File file = new File(savePath);
onStart(bean);
if (!file.exists()) {
boolean mkdirs = file.getParentFile().mkdirs();
Log.i("mkdirs = " + mkdirs);
}
FileOutputStream outputStream = new FileOutputStream(file);
int len;
long total = 0;
byte[] bytes = new byte[4096];
boolean isDownload = true;
long available = inputStream.available();
while ((len = inputStream.read(bytes)) != -1 && isDownload) {
total += len;
outputStream.write(bytes, 0, len);
outputStream.flush();
isDownload = onDownload(headers, bean, total, available);
}
Log.i("下载完成");
outputStream.close();
} catch (Exception e) {
Log.e(e);
onFailure(bean,e);
} finally {
onFinish(bean);
try {
inputStream.close();
} catch (IOException e) {
Log.e(e);
}
}
}
}
public abstract void onStart(T bean);
public abstract boolean onDownload(Headers headers, T bean, long len, long total);
public abstract void onFinish(T bean);
public abstract void onFailure(T bean, Throwable throwable);
@Override
public void onResponse(Call<FileBody<T>> call, Response<FileBody<T>> response) {
try {
executor.execute(new DownloadTask(bean, response.headers(), call.request().url(), response.body().getInputStream()));
} catch (Exception e) {
Log.e(e);
onFailure(bean,e);
call.cancel();
}
}
@Override
public void onFailure(Call<FileBody<T>> call, Throwable throwable) {
onFailure(bean, throwable);
call.cancel();
}
}

View File

@@ -1,6 +1,7 @@
package com.yutou.okhttp;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.Request;
@@ -14,12 +15,18 @@ public class GetRequestParams implements IRequestParam {
* @return
*/
@Override
public Request getRequest(HashMap<String, String> map, Request request) {
public Request getRequest(HashMap<String, String> headerMap, HashMap<String, String> map, Request request) {
Headers.Builder headerBuild = request.headers().newBuilder();
if (!headerMap.isEmpty()) {
for (String key : headerMap.keySet()) {
headerBuild.add(key, headerMap.get(key));
}
}
//添加公共参数
HttpUrl.Builder builder = request.url().newBuilder();
for (String key : map.keySet()) {
builder.addQueryParameter(key, String.valueOf(map.get(key)));
}
return request.newBuilder().url(builder.build()).build();
return request.newBuilder().url(builder.build()).headers(headerBuild.build()).build();
}
}

View File

@@ -1,12 +1,13 @@
package com.yutou.okhttp;
import okhttp3.Headers;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
public abstract class HttpCallback<T> implements Callback<HttpBody<T>> {
public abstract void onResponse(int code, String status, T response, String rawResponse);
public abstract void onResponse(Headers headers,int code, String status, T response, String rawResponse);
public abstract void onFailure(Throwable throwable);
@@ -14,6 +15,7 @@ public abstract class HttpCallback<T> implements Callback<HttpBody<T>> {
public void onResponse(Call<HttpBody<T>> call, Response<HttpBody<T>> response) {
if (response.body() != null) {
onResponse(
response.headers(),
response.body().getRetcode(),
response.body().getStatus(),
response.body().getData(),
@@ -22,10 +24,12 @@ public abstract class HttpCallback<T> implements Callback<HttpBody<T>> {
} else {
onFailure(new NullPointerException("response body is null"));
}
call.cancel();
}
@Override
public void onFailure(Call<HttpBody<T>> call, Throwable throwable) {
onFailure(throwable);
call.cancel();
}
}

View File

@@ -0,0 +1,145 @@
package com.yutou.okhttp;
import com.yutou.qqbot.interfaces.DownloadInterface;
import com.yutou.qqbot.utlis.ConfigTools;
import com.yutou.qqbot.utlis.Log;
import lombok.Data;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import org.springframework.util.StringUtils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
public class HttpDownloadUtils {
public static void download(Builder builder) {
createHttpClient(builder).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
if (builder.downloadInterface != null) {
builder.downloadInterface.onError(e);
}
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
InputStream inputStream = Objects.requireNonNull(response.body()).byteStream();
File target;
if (StringUtils.hasText(builder.fileName)) {
target = new File(builder.path, builder.fileName);
} else {
target = new File(builder.path);
}
FileOutputStream fileOutputStream = new FileOutputStream(target);
if (builder.downloadInterface != null) {
builder.downloadInterface.onDownloadStart();
}
try {
byte[] buffer = new byte[2048];
int len;
long soFarBytes = 0;
long totalBytes = inputStream.available();
while ((len = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
soFarBytes += len;
if (builder.downloadInterface != null) {
if (!builder.downloadInterface.onDownloading(soFarBytes, totalBytes)) {
break;
}
}
}
fileOutputStream.flush();
} catch (IOException e) {
Log.e(e,"download error:", builder.url);
} finally {
if (builder.downloadInterface != null) {
builder.downloadInterface.onDownload(target);
}
}
}
});
}
public static File downloadSync(Builder builder) {
try {
Response response = createHttpClient(builder).execute();
InputStream inputStream = Objects.requireNonNull(response.body()).byteStream();
File target;
if (StringUtils.hasText(builder.fileName)) {
target = new File(builder.path, builder.fileName);
} else {
target = new File(builder.path);
}
try (FileOutputStream fileOutputStream = new FileOutputStream(target)) {
byte[] buffer = new byte[2048];
int len;
while ((len = inputStream.read(buffer)) != -1) {
fileOutputStream.write(buffer, 0, len);
}
fileOutputStream.flush();
return target;
} catch (IOException e) {
Log.e(e,"download error:" , builder.url);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return null;
}
private static Call createHttpClient(Builder builder) {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(2, TimeUnit.MINUTES)
.readTimeout(2, TimeUnit.MINUTES)
.build();
Request.Builder rb = new Request.Builder()
.get()
.addHeader("User-Agent", ConfigTools.getUserAgent())
.url(builder.url);
if (StringUtils.hasText(builder.cookie)) {
rb.addHeader("Set-Cookie", builder.cookie);
rb.addHeader("Cookie", builder.cookie);
}
Request request = rb.build();
return okHttpClient.newCall(request);
}
@Data
public static class Builder {
String url;
String path;
String fileName;
DownloadInterface downloadInterface;
String cookie;
public Builder setUrl(String url) {
this.url = url;
return this;
}
public Builder setPath(String path) {
this.path = path;
return this;
}
public Builder setFileName(String fileName) {
this.fileName = fileName;
return this;
}
public Builder setDownloadInterface(DownloadInterface downloadInterface) {
this.downloadInterface = downloadInterface;
return this;
}
public Builder setCookie(String cookie) {
this.cookie = cookie;
return this;
}
}
}

View File

@@ -1,34 +1,25 @@
package com.yutou.okhttp;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.yutou.qqbot.utlis.Log;
import okhttp3.Connection;
import okhttp3.Headers;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import lombok.val;
import okhttp3.*;
import okhttp3.internal.http.HttpHeaders;
import okio.Buffer;
import okio.BufferedSource;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
public class HttpLoggingInterceptor implements Interceptor {
private static final String TAG = "HttpLogging";
private static final Charset UTF8 = Charset.forName("UTF-8");
private static final Charset UTF8 = StandardCharsets.UTF_8;
private volatile Level printLevel = Level.NONE;
private volatile Level printLevel = Level.BODY;
private java.util.logging.Level colorLevel;
private Logger logger;
@@ -63,7 +54,8 @@ public class HttpLoggingInterceptor implements Interceptor {
private void log(String message) {
//logger.log(colorLevel, message);
if (prLog) {
Log.i(TAG, message);
// Log.getDynamicLogger(TAG).info(message);
System.out.println(message);
}
//Log.e(TAG,message);
}
@@ -71,8 +63,11 @@ public class HttpLoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (request.body().contentLength() == 0) {
request = chain.call().request();
if (request.body() != null && request.body().contentLength() == 0) {
val headers = request.headers();
request = chain.call().request().newBuilder()
.headers(headers)
.build();
}
if (printLevel == Level.NONE) {
return chain.proceed(request);
@@ -109,8 +104,6 @@ public class HttpLoggingInterceptor implements Interceptor {
if (logHeaders) {
if (hasRequestBody) {
// Request body headers are only present when installed as a network interceptor. Force
// them to be included (when available) so there values are known.
if (requestBody.contentType() != null) {
log("\tContent-Type: " + requestBody.contentType());
}
@@ -121,7 +114,6 @@ public class HttpLoggingInterceptor implements Interceptor {
Headers headers = request.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
String name = headers.name(i);
// Skip headers from the request body as they are explicitly logged above.
if (!"Content-Type".equalsIgnoreCase(name) && !"Content-Length".equalsIgnoreCase(name)) {
log("\t" + name + ": " + headers.value(i));
}
@@ -151,7 +143,7 @@ public class HttpLoggingInterceptor implements Interceptor {
boolean logHeaders = (printLevel == Level.BODY || printLevel == Level.HEADERS);
try {
log("<-- " + clone.code() + ' ' + clone.message() + ' ' + clone.request().url() + " (" + tookMs + "ms");
log("<-- " + clone.code() + ' ' + clone.message() + ' ' + clone.request().url() + " (" + tookMs + "ms)");
if (logHeaders) {
Headers headers = clone.headers();
for (int i = 0, count = headers.size(); i < count; i++) {
@@ -162,12 +154,13 @@ public class HttpLoggingInterceptor implements Interceptor {
if (responseBody == null) return response;
if (isPlaintext(responseBody.contentType())) {
byte[] bytes = responseBody.byteStream().readAllBytes();
MediaType contentType = responseBody.contentType();
String body = new String(bytes, getCharset(contentType));
BufferedSource source = responseBody.source();
source.request(Long.MAX_VALUE); // 请求整个流
Buffer buffer = source.buffer();
Charset charset = getCharset(responseBody.contentType());
String body = buffer.clone().readString(charset);
log("\tbody:" + body);
responseBody = ResponseBody.create(responseBody.contentType(), bytes);
return response.newBuilder().body(responseBody).build();
} else {
log("\tbody: maybe [binary body], omitted!");
}
@@ -214,6 +207,8 @@ public class HttpLoggingInterceptor implements Interceptor {
body.writeTo(buffer);
Charset charset = getCharset(body.contentType());
log("\tbody:" + buffer.readString(charset));
// 重置请求体以确保后续处理不受影响
buffer.clear();
} catch (Exception e) {
logger.log(java.util.logging.Level.WARNING, e.getMessage(), e);
}

View File

@@ -5,5 +5,5 @@ import okhttp3.Request;
import java.util.HashMap;
public interface IRequestParam {
Request getRequest(HashMap<String,String> map, Request request);
Request getRequest(HashMap<String,String> header, HashMap<String,String> map, Request request);
}

View File

@@ -1,5 +1,6 @@
package com.yutou.okhttp;
import com.yutou.qqbot.utlis.ConfigTools;
import okhttp3.Request;
import java.util.HashMap;
@@ -7,14 +8,25 @@ import java.util.HashMap;
public class ParamsContext {
private IRequestParam iRequestParam;
private Request request;
private HashMap<String,String> map;
private HashMap<String, String> map;
private HashMap<String, String> headerMap;
public ParamsContext(HashMap<String,String> map,Request request) {
if(map==null){
map=new HashMap<>();
public ParamsContext(HashMap<String, String> map, Request request) {
if (map == null) {
map = new HashMap<>();
}
this.map=map;
this.map = map;
this.request = request;
this.headerMap = new HashMap<>();
}
public ParamsContext(HashMap<String, String> headerMap, HashMap<String, String> map, Request request) {
if (map == null) {
map = new HashMap<>();
}
this.map = map;
this.request = request;
this.headerMap = headerMap;
}
public Request getInRequest() {
@@ -26,6 +38,7 @@ public class ParamsContext {
iRequestParam = new PostRequestParams();
break;
}
return iRequestParam.getRequest(map,request);
headerMap.put("User-Agent", ConfigTools.getUserAgent());
return iRequestParam.getRequest(headerMap, map, request);
}
}

View File

@@ -2,22 +2,26 @@ package com.yutou.okhttp;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.qqbot.utlis.Log;
import okhttp3.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class PostRequestParams implements IRequestParam {
@Override
public Request getRequest(HashMap<String, String> map, Request request) {
public Request getRequest(HashMap<String, String> headerMap, HashMap<String, String> map, Request request) {
Headers.Builder headerBuilder = request.headers().newBuilder();
if (!headerMap.isEmpty()) {
for (String key : headerMap.keySet()) {
headerBuilder.add(key, headerMap.get(key));
}
}
if (request.body() instanceof FormBody) {
FormBody.Builder bodyBuilder = new FormBody.Builder();
FormBody formBody = (FormBody) request.body();
for (int i = 0; i < formBody.size(); i++) {
bodyBuilder.addEncoded(formBody.encodedName(i), formBody.encodedValue(i));
}
@@ -25,10 +29,12 @@ public class PostRequestParams implements IRequestParam {
bodyBuilder.addEncoded(key, String.valueOf(map.get(key)));
}
formBody = bodyBuilder.build();
request = request.newBuilder().post(formBody).build();
request = request.newBuilder().headers(headerBuilder.build()).post(formBody).build();
} else if (request.body() != null) {
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"),toUrlParams(map));
request = request.newBuilder().post(request.body())
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), toUrlParams(map));
request = request.newBuilder()
.headers(headerBuilder.build())
.post(request.body())
.post(requestBody).build();
}
return request;
@@ -41,9 +47,9 @@ public class PostRequestParams implements IRequestParam {
try {
string.append("&").append(key).append("=").append(URLEncoder.encode(json.getString(key), "UTF-8"));
} catch (Exception e) {
e.printStackTrace();
Log.e(e);
try {
string.append("&").append(URLEncoder.encode(key,"UTF-8")).append("=");
string.append("&").append(URLEncoder.encode(key, "UTF-8")).append("=");
// string += "&" + key + "=";
} catch (Exception e1) {
string.append("&").append(key).append("=");
@@ -56,7 +62,7 @@ public class PostRequestParams implements IRequestParam {
}
public static String toUrlParams(Map<String, String> map) {
if(map.isEmpty()){
if (map.isEmpty()) {
return "";
}
StringBuilder builder = new StringBuilder();

View File

@@ -1,16 +1,16 @@
package com.yutou.okhttp.api;
import com.alibaba.fastjson2.JSONObject;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.internal.bind.DateTypeAdapter;
import com.yutou.okhttp.HttpBody;
import com.yutou.okhttp.HttpLoggingInterceptor;
import com.yutou.okhttp.ParamsContext;
import com.yutou.okhttp.converter.JsonCallAdapter;
import com.yutou.okhttp.converter.JsonConverterFactory;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import lombok.val;
import okhttp3.*;
import retrofit2.CallAdapter;
import retrofit2.Converter;
import retrofit2.Retrofit;
@@ -18,31 +18,55 @@ import retrofit2.Retrofit;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
public class BaseApi {
private String URL;
private HashMap<String, String> params;
private HashMap<String, String> params = new HashMap<>();
private HashMap<String, String> headers = new HashMap<>();
public BaseApi setURL(String URL) {
this.URL = URL;
return this;
}
public BaseApi setHeaders(HashMap<String, String> headers) {
this.headers = headers;
return this;
}
public void addHeader(HashMap<String, String> headers) {
this.headers.putAll(headers);
}
public BaseApi setParams(HashMap<String, String> params) {
this.params = params;
return this;
}
public void useCookie(JSONObject json) {
StringBuilder ck = new StringBuilder();
json.remove("sql_time");
json.remove("gourl");
for (String key : json.keySet()) {
ck.append(key).append("=").append(json.getString(key)).append(";");
}
headers.put("Cookie", ck.toString());
setHeaders(headers);
}
/**
* 创建一个接口方法
*
* @param okHttpClient okhttp客户端
* @param converterFactory 处理工厂类
* @param okHttpClient okhttp客户端
* @param converterFactory 处理工厂类
* @param callAdapterFactory 请求适配器工厂
* @param baseUrl 基础地质
* @param service 接口
* @param <T> 接口泛型
* @param baseUrl 基础地质
* @param service 接口
* @param <T> 接口泛型
* @return 接口
*/
public <T> T create(OkHttpClient okHttpClient, Converter.Factory converterFactory, CallAdapter.Factory callAdapterFactory, String baseUrl, Class<T> service) {
@@ -74,7 +98,10 @@ public class BaseApi {
loggingInterceptor.setPrintLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder builder = new OkHttpClient()
.newBuilder()
.callTimeout(2, TimeUnit.MINUTES)
.readTimeout(2, TimeUnit.MINUTES)
.connectTimeout(2, TimeUnit.MINUTES)
.writeTimeout(2, TimeUnit.MINUTES)
.addInterceptor(initQuery())
.addInterceptor(loggingInterceptor);
return create(builder.build(),
@@ -83,14 +110,30 @@ public class BaseApi {
URL,
apiClass);
}
public Interceptor initQuery() {
Interceptor addQueryParameterInterceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
//配置公共参数
request = new ParamsContext(params,request).getInRequest();
return chain.proceed(request);
request = new ParamsContext(headers, params, request).getInRequest();
val proceed = chain.proceed(request);
if (!proceed.isSuccessful()) {
HttpBody<?> httpBody = new HttpBody<>();
httpBody.setCode(200);
val parse = JSONObject.parse(proceed.body().string());
httpBody.setRetcode(parse.getInteger("code"));
httpBody.setMsg(parse.getString("message"));
ResponseBody errorResponseBody = ResponseBody.create(
JSONObject.toJSONString(httpBody),
MediaType.get("application/json; charset=utf-8"));
val newResponse=proceed.newBuilder()
.code(200)
.body(errorResponseBody).build();
return newResponse;
}
return proceed;
}
};
return addQueryParameterInterceptor;

View File

@@ -31,12 +31,26 @@ public class JsonResponseBodyConverter<T> implements Converter<ResponseBody, T>
HttpBody<T> body;
try {
body = JSONObject.parseObject(string, type);
if(body.getData()==null){
JSONObject jt=JSONObject.parseObject(JSONObject.toJSONString(new HttpBody<>()));
jt.put("data",JSONObject.parseObject(string));
HttpBody<T> bt=JSONObject.parseObject(jt.toJSONString(),type);
body.setData(bt.getData());
}
body.setSrc(string);
return (T) body;
} catch (Exception e) {
e.printStackTrace();
body = new HttpBody();
body.setSrc(string);
try {
body = new HttpBody<>();
body.setData(JSONObject.parseObject(string, type));
body.setSrc(string);
} catch (Exception e2) {
e2.printStackTrace();
body = new HttpBody<>();
body.setSrc(string);
}
}
return (T) body;

View File

@@ -0,0 +1,87 @@
package com.yutou.qqbot.Controllers;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.qqbot.utlis.BangumiTools;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
@RestController
@RequestMapping("/bgm")
public class AnimeController {
@GetMapping("/today")
public Map<String, Object> getTodayAnim() {
Map<String, Object> returnData = new HashMap<>();
// 获取全部一周的数据
JSONObject allData = BangumiTools.getBangumi(-1);
if (allData == null) {
return returnData;
}
JSONArray bangumiArray = allData.getJSONArray("bangumi");
if (bangumiArray == null || bangumiArray.isEmpty()) {
return returnData;
}
Calendar calendar = Calendar.getInstance();
calendar.setTimeZone(TimeZone.getTimeZone("GMT+8:00"));
calendar.setFirstDayOfWeek(Calendar.MONDAY);
calendar.setTime(new Date());
int todayId = calendar.get(Calendar.DAY_OF_WEEK) - 1;
if (todayId == 0) {
todayId = 7; // 处理星期日的情况
}
int tomorrowId = todayId % 7 + 1; // 计算明天对应的 weekday id
JSONObject todayData = null;
JSONObject tomorrowData = null;
for (Object obj : bangumiArray) {
JSONObject item = (JSONObject) obj;
int weekdayId = item.getJSONObject("weekday").getInteger("id");
if (weekdayId == todayId) {
todayData = item;
}
if (weekdayId == tomorrowId) {
tomorrowData = item;
}
}
// 打印当日数据
if (todayData != null) {
JSONArray items = todayData.getJSONArray("items");
if (items != null && !items.isEmpty()) {
for (Object o : items) {
JSONObject itemObj = (JSONObject) o;
if (itemObj.getString("name_cn") == null || itemObj.getString("name_cn").isEmpty()) {
itemObj.put("name_cn", itemObj.getString("name"));
}
}
}
returnData.put("today",todayData);
}
// 打印明日数据
if (tomorrowData != null) {
JSONArray items = tomorrowData.getJSONArray("items");
if (items != null && !items.isEmpty()) {
for (Object o : items) {
JSONObject itemObj = (JSONObject) o;
if (itemObj.getString("name_cn") == null || itemObj.getString("name_cn").isEmpty()) {
itemObj.put("name_cn", itemObj.getString("name"));
}
}
}
returnData.put("nextDay",tomorrowData);
}
return returnData;
}
}

View File

@@ -2,6 +2,7 @@ package com.yutou.qqbot.Controllers;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.napcat.QQDatabase;
import com.yutou.napcat.handle.At;
import com.yutou.napcat.handle.BaseHandle;
import com.yutou.napcat.handle.Image;
import com.yutou.napcat.handle.Text;
@@ -22,6 +23,8 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Controller
public class AppController {
@@ -71,10 +74,14 @@ public class AppController {
if (json.getString("message").isEmpty()) {
return "not message";
}
List<BaseHandle<?>> list=new ArrayList<>();
list.add(new Text(json.getString("message")));
if(json.getString("image")!=null&&!json.getString("image").isEmpty()){
list.add(new Image(json.getString("image")));
}
SendMessageResponse sent = QQBotManager.getInstance().sendMessage(QQDatabase.checkFriend(json.getLong("qq")),
json.getLong("qq"),
new Text(json.getString("message")),
new Image(json.getString("image"))
list
);
return sent == null ? "0" : sent.getId() + "";
}

View File

@@ -63,7 +63,7 @@ public class MiRouterDevices {
}
private String getRemoteAddress(String ip) {
JSONObject data = JSON.parseObject(HttpTools.get(XiaoMiRouter.getDeviceListUrl()));
JSONObject data = JSON.parseObject(HttpTools.get(XiaoMiRouter.getInstance().getDeviceListUrl()));
if (data.getInteger("code") == 0) {
JSONArray array=data.getJSONArray("list");
for (Object o : array) {

View File

@@ -10,17 +10,15 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class QQBotApplication {
public static final String version = "QQBot v.1.7.3";
public static final String version = "QQBot v.1.7.24";
public static void main(String[] args) {
System.out.println("version = " + version);
SpringApplication.run(QQBotApplication.class, args);
NapCatApi.setLog(true);
RedisTools.initRedisPoolSub();
QQBotManager.getInstance();
val log = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.QQ_LOG, Boolean.class);
val log = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.QQ_LOG, Boolean.class,true);
NapCatApi.setLog(log);
//1
}
}

View File

@@ -10,6 +10,7 @@ import com.yutou.qqbot.MessageEvent.AdminMessage;
import com.yutou.qqbot.models.Model;
import com.yutou.qqbot.utlis.AppTools;
import com.yutou.qqbot.utlis.RedisTools;
import com.yutou.qqbot.utlis.XiaoMiRouter;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@@ -89,4 +90,15 @@ public class QQBotController {
}
return "200";
}
@ResponseBody
@RequestMapping("/router/wan1ip.do")
public String getWan1IP(){
return XiaoMiRouter.getInstance().getWan1IPAddress();
}
@ResponseBody
@RequestMapping("/router/wan2ip.do")
public String getWan2IP(){
return XiaoMiRouter.getInstance().getWan2IPAddress();
}
}

View File

@@ -3,22 +3,24 @@ package com.yutou.qqbot;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.napcat.QQDatabase;
import com.yutou.napcat.handle.BaseHandle;
import com.yutou.napcat.handle.MessageHandleBuild;
import com.yutou.napcat.handle.Reply;
import com.yutou.napcat.handle.Text;
import com.yutou.napcat.handle.*;
import com.yutou.napcat.http.NapCatApi;
import com.yutou.napcat.model.FriendBean;
import com.yutou.napcat.model.GroupBean;
import com.yutou.napcat.model.GroupUserBean;
import com.yutou.napcat.model.SendMessageResponse;
import com.yutou.okhttp.BaseBean;
import com.yutou.okhttp.HttpBody;
import com.yutou.okhttp.HttpCallback;
import com.yutou.qqbot.data.MessageChainBuilder;
import com.yutou.qqbot.interfaces.ObjectInterface;
import com.yutou.qqbot.utlis.*;
import okhttp3.Headers;
import retrofit2.Response;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -46,7 +48,7 @@ public class QQBotManager {
sendMessage(true, 583819556L, "姬妻酱上线拉~☆Daze~ 当前版本:" + QQBotApplication.version);
NapCatApi.getGroupApi().getGroupList().enqueue(new HttpCallback<List<GroupBean>>() {
@Override
public void onResponse(int code, String status, List<GroupBean> response, String rawResponse) {
public void onResponse(Headers headers, int code, String status, List<GroupBean> response, String rawResponse) {
for (GroupBean groupBean : response) {
QQDatabase.addGroup(groupBean.getGroupId(), groupBean);
QQNumberManager.getManager().addNumber(groupBean.getGroupId(), true);
@@ -60,7 +62,7 @@ public class QQBotManager {
});
NapCatApi.getFriendApi().getFriendList().enqueue(new HttpCallback<List<FriendBean>>() {
@Override
public void onResponse(int code, String status, List<FriendBean> response, String rawResponse) {
public void onResponse(Headers headers, int code, String status, List<FriendBean> response, String rawResponse) {
for (FriendBean friendBean : response) {
QQDatabase.addUser(friendBean.getUserId(), friendBean);
QQNumberManager.getManager().addNumber(friendBean.getUserId(), false);
@@ -74,7 +76,7 @@ public class QQBotManager {
});
NapCatApi.getUtilsApi().getLoginInfo().enqueue(new HttpCallback<FriendBean>() {
@Override
public void onResponse(int code, String status, FriendBean response, String rawResponse) {
public void onResponse(Headers headers,int code, String status, FriendBean response, String rawResponse) {
QQDatabase.setMe(response);
}
@@ -120,7 +122,7 @@ public class QQBotManager {
public SendMessageResponse sendMessage(boolean isUser, Long qq, List<BaseHandle<?>> items) {
try {
if(!ConfigTools.load(ConfigTools.CONFIG,ConfigTools.QQ, Boolean.class)){
if (!ConfigTools.load(ConfigTools.CONFIG, ConfigTools.QQ, Boolean.class)) {
return null;
}
MessageHandleBuild handleBuild = MessageHandleBuild
@@ -163,7 +165,7 @@ public class QQBotManager {
public String sendMessage(String text) {
return getNotLoginQQ();
return sendMessage(false,defGroup,text).toString();
}
public SendMessageResponse sendMessage(Long group, String text) {
@@ -183,16 +185,72 @@ public class QQBotManager {
}
public void sendMessage(File imageFile, Long qq, String replyMessageId, String text) {
List<BaseHandle<?>> items = new ArrayList<>();
if (imageFile != null) {
items.add(new Image(imageFile));
}
if (replyMessageId != null && !replyMessageId.isEmpty()) {
items.add(new Reply(Long.parseLong(replyMessageId)));
}
if (text != null && !text.isEmpty()) {
items.add(new Text(text));
}
if (!items.isEmpty()) {
sendMessage(QQDatabase.checkFriend(qq),qq, items);
}
}
public void sendMessage(File imageFile, String text) {
sendMessage(true,defQQ,new Image(imageFile),new Text(text));
}
public void sendMessage(List<File> imgs, Long qq, String text) {
}
public void groupBan(long qqGroup, long user, int timer, ObjectInterface objectInterface) {
NapCatApi.getGroupApi().groupBan(qqGroup, user, timer).enqueue(new HttpCallback<BaseBean>() {
@Override
public void onResponse(Headers headers,int code, String status, BaseBean response, String rawResponse) {
if (objectInterface != null) {
objectInterface.out("1");
}
}
@Override
public void onFailure(Throwable throwable) {
if (objectInterface != null) {
objectInterface.out(null);
}
}
});
}
public void getShutUpList(long qqGroup, HttpCallback<List<GroupUserBean>> callback) {
NapCatApi.getGroupApi().getGroupUserList(qqGroup,true).enqueue(callback);
}
public void setGroupSpecialTitle(long group,
long user,
String title,
long duration) {
NapCatApi.getGroupApi().setGroupSpecialTitle(group, user, title, duration).enqueue(new HttpCallback<BaseBean>() {
@Override
public void onResponse(Headers headers,int code, String status, BaseBean response, String rawResponse) {
}
@Override
public void onFailure(Throwable throwable) {
}
});
}
public static void main(String[] args) {
JSONObject json = new JSONObject();
@@ -206,10 +264,10 @@ public class QQBotManager {
public void sendVersion() {
String localVersion = QQBotApplication.version;
String serverVersion = HttpTools.get("http://tools.yutou233.cn:8000/public/version.do?token=zIrsh9TUZP2lfRW753PannG49E7VJvor");
String msg = "本地版本:" + localVersion + "\n" + "服务器版本:" + serverVersion;
//String serverVersion = HttpTools.get("https://tools.yutou233.cn/public/version.do?token=zIrsh9TUZP2lfRW753PannG49E7VJvor");
String msg = "当前版本:" + localVersion ;
QQBotManager.getInstance().sendMessage(msg);
AppTools.sendServer("服务版本查询", msg);
// AppTools.sendServer("服务版本查询", msg);
}
public boolean isLogin() {

View File

@@ -417,7 +417,7 @@ public class BiliBiliUtils {
body.put("csrf", BiliLogin.getCookieToken(qq));
body.put("csrf_token", BiliLogin.getCookieToken(qq));
JSONObject toCoin = http_post(SignApi.LIVE_SIGN_COIN, HttpTools.toUrlParams(body));
JSONObject liveSign = http_get(SignApi.LIVE_SIGN);
JSONObject liveSign =JSONObject.parseObject("{\"message\":\"已停用\"}"); //http_get(SignApi.LIVE_SIGN);
JSONObject vipSign = http_post(SignApi.VIP_SIGN, null);
return "银瓜子兑换硬币:" + toCoin.getString("message") + "|" + "直播签到:" + liveSign.getString("message") + "|大会员中心签到:" + vipSign.getString("message");
}

View File

@@ -13,4 +13,9 @@ public class MessageChainBuilder {
sb.append(o.toString());
return this;
}
@Override
public String toString() {
return sb.toString();
}
}

View File

@@ -22,9 +22,7 @@ public class Message {
public static Message create(String message, boolean isGTP) {
Message msg = new Message();
msg.content = message;
if (isGTP) {
msg.role = "assistant";
}
msg.role = isGTP ? "assistant" : "user";
return msg;
}
}

View File

@@ -0,0 +1,90 @@
package com.yutou.qqbot.data.gpt;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import java.util.List;
@Data
public class OpenAiBean {
@JSONField(name = "id")
private String id;
@JSONField(name = "choices")
private List<Choice> choices;
@JSONField(name = "tool_calls")
private List<ToolCall> toolCalls;
@JSONField(name = "usage")
private Usage usage;
@JSONField(name = "created")
private long created;
@JSONField(name = "model")
private String model;
@JSONField(name = "object")
private String object;
@JSONField(name = "system_fingerprint")
private String systemFingerprint;
@Data
public static class Choice {
@JSONField(name = "index")
private int index;
@JSONField(name = "message")
private Message message;
@JSONField(name = "finish_reason")
private String finishReason;
}
@Data
public static class Message {
@JSONField(name = "role")
private String role;
@JSONField(name = "content")
private String content;
@JSONField(name = "reasoning_content")
private String reasoningContent;
}
@Data
public static class ToolCall {
@JSONField(name = "id")
private String id;
@JSONField(name = "type")
private String type;
@JSONField(name = "function")
private Function function;
}
@Data
public static class Function {
@JSONField(name = "name")
private String name;
@JSONField(name = "arguments")
private String arguments;
}
@Data
public static class Usage {
@JSONField(name = "prompt_tokens")
private int promptTokens;
@JSONField(name = "completion_tokens")
private int completionTokens;
@JSONField(name = "total_tokens")
private int totalTokens;
}
}

View File

@@ -0,0 +1,160 @@
package com.yutou.qqbot.gpt;
import com.yutou.qqbot.data.baidu.Message;
import com.yutou.qqbot.utlis.Log;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class AbsGPTManager {
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
private ScheduledFuture<?> currentTask;
protected static final AtomicInteger MAX_MESSAGE = new AtomicInteger(20);
protected final ConcurrentHashMap<String, List<Message>> msgMap= new ConcurrentHashMap<>();
// 新增锁映射表
protected final ConcurrentHashMap<String, AtomicBoolean> userLocks = new ConcurrentHashMap<>();
protected String model ;
/**
* 清除与GPT管理器相关的所有缓存或状态信息。
*/
/**
* 这里确实是需要清空所有数据
*/
public void clear() { // 添加同步
msgMap.clear();
for (AtomicBoolean value : userLocks.values()) {
value.set(false);
}
userLocks.forEachValue(1, atomicBoolean -> atomicBoolean.set(false));
userLocks.clear();
}
/**
* 发送消息到指定用户。
*
* @param user 接收消息的用户标识符。
* @param message 要发送的消息内容。
* @return 包含消息发送结果的Message对象。
*/
public abstract Message sendMessage(String user, String message);
public abstract Message sendTmpMessage(String user, String message, String tmpModel);
/**
* 将文本转换为图像。
*
* @param user 用户标识符。
* @param text 要转换为图像的文本内容。
* @return 包含生成图像的File对象。
*/
public abstract File textToImage(String user, String text);
/**
* 将图像转换为文本。
*
* @param user 用户标识符。
* @param file 包含要转换为文本的图像文件。
* @return 提取的文本内容。
*/
public abstract String imageToText(String user, File file);
/**
* 获取当前使用的GPT版本信息。
*
* @return GPT版本字符串。
*/
public abstract String getGPTVersion();
/**
* 设置最大消息数量。
*
* @param count 最大消息数量。
* @return 设置后的最大消息数量。
*/
public abstract int setMaxMessageCount(int count);
public List<Message> getMessageList(String user){
/*List<Message> list = msgMap.computeIfAbsent(user, k -> Collections.synchronizedList(new ArrayList<>()));
// 限制历史消息的最大数量
synchronized (list) {
if (list.size() >= MAX_MESSAGE.get()) {
int removeCount = list.size() - MAX_MESSAGE.get() + 1; // 腾出空间给新消息
list.subList(0, removeCount).clear();
}
}
return list;*/
return new ArrayList<>();
}
/**
* 根据指定的类获取相应的GPT管理器实例。
*
* @param tClass GPT管理器的具体实现类。
* @return GPT管理器实例。
*/
public static <T extends AbsGPTManager> AbsGPTManager getManager(Class<?> tClass) {
if (tClass == BaiduGPTManager.class) {
return BaiduGPTManager.getManager();
}else if(tClass== SiliconGPTManager.class){
return SiliconGPTManager.getInstance();
}
return new AbsGPTManager() {
@Override
public void clear() {
}
@Override
public Message sendMessage(String user, String message) {
return null;
}
@Override
public Message sendTmpMessage(String user, String message, String tmpModel) {
return null;
}
@Override
public File textToImage(String user, String text) {
return null;
}
@Override
public String imageToText(String user, File file) {
return "";
}
@Override
public String getGPTVersion() {
return "";
}
@Override
public int setMaxMessageCount(int count) {
return 0;
}
};
}
public AbsGPTManager setModel(String model) {
this.model=model;
return this;
}
public void sendMessageAndScheduleClear() {
// 取消当前的定时任务(如果存在)
if (currentTask != null && !currentTask.isCancelled()) {
currentTask.cancel(false);
}
// 重新调度一个新的定时任务,一小时后执行
currentTask = scheduler.schedule(() -> {
clear();
currentTask = null; // 清空当前任务引用
}, 1, TimeUnit.HOURS);
}
}

View File

@@ -0,0 +1,240 @@
package com.yutou.qqbot.gpt;
import com.baidubce.qianfan.Qianfan;
import com.baidubce.qianfan.QianfanV2;
import com.baidubce.qianfan.model.chat.ChatResponse;
import com.baidubce.qianfan.model.chat.v2.response.ResponseV2;
import com.baidubce.qianfan.model.image.Image2TextResponse;
import com.baidubce.qianfan.model.image.Text2ImageResponse;
import com.yutou.qqbot.data.baidu.Message;
import com.yutou.qqbot.utlis.ConfigTools;
import com.yutou.qqbot.utlis.Log;
import com.yutou.qqbot.utlis.StringUtils;
import lombok.val;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
public class BaiduGPTManager extends AbsGPTManager {
private static final String AppID = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_APPID, String.class);
private static final String ApiKey = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_API_KEY, String.class);
//ConfigTools.load操作可以确保获取到相关参数所以无需关心
private static final String AccessKey = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_ACCESS_KEY, String.class);
private static final String SecretKey = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_SECRET_KEY, String.class);
private final static String modelFor40 = "ernie-4.0-8k";
private final static String modelFor35 = "ernie-speed-128k";
private final Qianfan qianfan;
private final QianfanV2 qianfanV2;
private BaiduGPTManager() {
qianfan = new Qianfan(AccessKey, SecretKey);
qianfanV2 = new Qianfan(AccessKey, SecretKey).v2();
String savedVersion = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, String.class);
if (StringUtils.isEmpty(savedVersion) || (!"3.5".equals(savedVersion) && !"4.0".equals(savedVersion))) {
savedVersion = "3.5";
ConfigTools.save(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, savedVersion);
}
model = "3.5".equals(savedVersion) ? modelFor35 : modelFor40;
}
private static volatile BaiduGPTManager manager;
public static BaiduGPTManager getManager() {
if (manager == null) {
synchronized (BaiduGPTManager.class) {
if (manager == null) {
manager = new BaiduGPTManager();
}
}
}
return manager;
}
@Override
public int setMaxMessageCount(int count) {
MAX_MESSAGE.set(count);
return count;
}
// 这个是官方的示例代码,表示连续对话
private static void exampleChat() {
Qianfan qianfan = new Qianfan();
ChatResponse response = qianfan.chatCompletion()
// 设置需要使用的模型与endpoint同时只能设置一种
.model("ERNIE-Bot")
// 通过传入历史对话记录来实现多轮对话
.addMessage("user", "你好!你叫什么名字?")
.addMessage("assistant", "你好我是文心一言英文名是ERNIE Bot。")
// 传入本轮对话的用户输入
.addMessage("user", "刚刚我的问题是什么?")
.execute();
System.out.println("输出内容:" + response.getResult());
}
/**
* 发送消息方法
* 该方法用于处理用户发送的消息,并返回相应的回复消息
* 它通过用户锁来限制每个用户同时只能有一个请求正在处理中
*
* @param user 用户标识符,用于区分不同的用户
* @param message 用户发送的消息内容
* @return 返回处理后的消息对象,包含回复内容和是否为回复消息的标记
*/
@Override
public Message sendMessage(String user, String message) {
return sendTmpMessage(user, message, null);
}
@Override
public Message sendTmpMessage(String user, String message, String tmpModel) {
boolean isTmp = (tmpModel != null);
// 获取或创建用户锁
AtomicBoolean lock = userLocks.computeIfAbsent(user, k -> new AtomicBoolean(false));
// 尝试加锁(如果已被锁定则立即返回提示)
if (!lock.compareAndSet(false, true)) {
return Message.create("您有请求正在处理中,请稍后再试", true);
}
if (!isTmp) {
tmpModel = model;
}
try {
List<Message> list = getMessageList(user);
if(!isTmp) {
list.add(Message.create(message));
}
val builder = qianfanV2.chatCompletion()
.model(tmpModel);
Log.i("GPT收到消息",isTmp,list.size());
if (!isTmp) {
StringBuilder sb = new StringBuilder();
for (Message msg : list) {
Log.i("GPT获取历史消息",msg.getRole(),msg.getContent());
builder.addMessage(msg.getRole(), msg.getContent());
sb.append(msg.getRole()).append(":").append(msg.getContent()).append("\n");
}
Log.d(sb.toString());
} else {
val mt = Message.create(message);
builder.addMessage(mt.getRole(),mt.getContent());
}
ResponseV2 chatResponse = qianfanV2.chatCompletion(builder.build());
Message response = Message.create(chatResponse.getChoices().get(0).getMessage().getContent(), true);
if (isTmp) {
synchronized (list) {
list.add(response);
if (list.size() > MAX_MESSAGE.get()) {
int overflow = list.size() - MAX_MESSAGE.get();
list.subList(0, overflow).clear();
}
}
}
// msgMap.put(user, list);
return response;
} catch (Exception e) {
Log.e(e, message);
return Message.create("请求失败,请重试", true);
} finally {
lock.set(false);
userLocks.remove(user, lock);
}
}
/**
* 将文本转换为图像文件
* 该方法使用预训练的AI模型将给定的文本转换为图像并将其保存为文件
*
* @param user 用户标识符,用于为生成的图像文件命名
* @param text 要转换为图像的文本
* @return 返回生成的图像文件对象如果转换过程中发生错误则返回null
*/
@Override
public File textToImage(String user, String text) {
try {
// 使用QianFan的text2Image方法将文本转换为图像数据
Text2ImageResponse response = qianfan.text2Image()
.prompt(text)
.execute();
// 获取转换后的图像数据以Base64编码的图像字符串形式
val b64Image = response.getData().get(0).getB64Image();
// 将Base64编码的图像数据转换为图像文件
// 创建一个临时目录下的图像文件,文件名包含用户标识符和当前时间戳,以确保唯一性
val imageFile = new File("tmp" + File.separator + user + "_" + System.currentTimeMillis() + ".png");
try (val inputStream = new ByteArrayInputStream(Base64.getDecoder().decode(b64Image))) {
// 将解码后的图像数据复制到图像文件中,替换现有文件
Files.copy(inputStream, imageFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
return imageFile;
}
} catch (Exception e) {
// 如果在图像文件生成过程中发生错误,记录错误信息
Log.e(e);
}
// 如果发生错误返回null
return null;
}
/**
* 将图片转换为文本描述
*
* @param user 使用该功能的用户标识
* @param file 要转换的图片文件
* @return 转换后的文本描述如果转换失败则返回null
*/
@Override
public String imageToText(String user, File file) {
// 将file文件转换成base64的代码
try {
// 读取文件内容并转换为Base64编码
val base64 = Base64.getEncoder().encodeToString(Files.readAllBytes(file.toPath()));
// 调用图像转文本的API
Image2TextResponse response = qianfan.image2Text()
.image(base64)
.prompt("请描述这张图片中的主要内容和细节,以及它们之间的关系\n")
.execute();
String translationPrompt = "将以下英文内容严格翻译为简体中文不要解释、不要添加额外内容保留专业术语和名称如Star Wars保持英文\n" + response.getResult();
// 获取API返回的结果
return sendMessage("bot", translationPrompt).getContent();
} catch (Exception e) {
// 异常处理:记录错误日志
Log.e(e);
}
// 如果发生异常返回null
return null;
}
@Override
public AbsGPTManager setModel(String model) {
ConfigTools.save(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, model);
return super.setModel(model);
}
@Override
public String getGPTVersion() {
return model;
}
public static void main(String[] args) throws Exception {
// BaiduGPTManager.getManager().textToImage("user","画一个猫娘,用二次元动画画风,她是粉色头发,坐在地上");
// BaiduGPTManager.getManager().imageToText("user",new File("test.png"));
// Message message = BaiduGPTManager.getManager().sendMessage("user", "现在假设小猪等于1,小猴等于2");
// System.out.println(message.getContent());
// message = BaiduGPTManager.getManager().sendMessage("user", "那么小猪加上小猴等于多少?");
// System.out.println(message.getContent());
System.out.println(BaiduGPTManager.getManager().sendMessage("user", "分析这个网页链接的页面内容,而非链接本身:https://www.bilibili.com/video/BV1TTf5YrESz/").getContent());
}
}

View File

@@ -0,0 +1,135 @@
package com.yutou.qqbot.gpt;
import com.yutou.okhttp.HttpLoggingInterceptor;
import com.yutou.qqbot.data.baidu.Message;
import com.yutou.qqbot.data.gpt.OpenAiBean;
import com.yutou.qqbot.http.GPTApi;
import com.yutou.qqbot.http.GPTBuilder;
import com.yutou.qqbot.utlis.ConfigTools;
import com.yutou.qqbot.utlis.StringUtils;
import lombok.val;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
public class SiliconGPTManager extends AbsGPTManager {
//生成单例模式
private volatile static SiliconGPTManager instance = new SiliconGPTManager();
private SiliconGPTManager() {
val load = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.GPT_SILICON, String.class);
String defModel = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B";
if (!StringUtils.isEmpty(load)) {
defModel = load;
}
setModel(defModel);
}
public static SiliconGPTManager getInstance() {
if (instance == null) {
synchronized (SiliconGPTManager.class) {
if (instance == null) {
instance = new SiliconGPTManager();
}
}
}
return instance;
}
@Override
public Message sendTmpMessage(String user, String message, String tmpModel) {
return null;
}
@Override
public synchronized Message sendMessage(String user, String message) {
// 获取或创建用户锁
AtomicBoolean lock = userLocks.computeIfAbsent(user, k -> new AtomicBoolean(false));
try {
// 尝试加锁(如果已被锁定则立即返回提示)
if (!lock.compareAndSet(false, true)) {
return Message.create("您有请求正在处理中,请稍后再试", true);
}
val builder = GPTBuilder.create(model);
List<Message> list = getMessageList(user);
list.add(Message.create(message));
for (Message msg : list) {
builder.addMessage(msg.getRole(), msg.getContent());
}
val response = GPTApi.getApi().completions(builder.build()).execute();
if (!response.isSuccessful()) {
return Message.create("API请求失败", true);
}
val body = response.body();
if (body == null || body.getData() == null) {
return Message.create("API请求为空", true);
}
if (body.getRetcode() != 0) {
return Message.create(body.getMsg(), true);
}
val choices = body.getData().getChoices();
if (choices == null || choices.isEmpty()) {
return Message.create("没有对话信息", true);
}
val choice = choices.get(choices.size() - 1);
val bot = Message.create(choice.getMessage().getContent(), true);
list.add(bot);
return bot;
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
lock.set(false);
userLocks.remove(user, lock);
}
}
@Override
public AbsGPTManager setModel(String model) {
ConfigTools.save(ConfigTools.CONFIG, ConfigTools.GPT_SILICON, model);
return super.setModel(model);
}
@Override
public File textToImage(String user, String text) {
return null;
}
@Override
public String imageToText(String user, File file) {
return "";
}
@Override
public String getGPTVersion() {
return model;
}
@Override
public int setMaxMessageCount(int count) {
MAX_MESSAGE.set(count);
return count;
}
public static void main(String[] args) {
String model = "THUDM/glm-4-9b-chat";
val message = AbsGPTManager.getManager(SiliconGPTManager.class)
.setModel(model)
.sendMessage("user", "宫廷玉液酒的下一句是什么?");
System.out.println(message);
System.out.println(AbsGPTManager.getManager(SiliconGPTManager.class)
.setModel(model)
.sendMessage("user", "宫廷玉液酒减去大锤等于多少")
);
}
}

View File

@@ -0,0 +1,26 @@
package com.yutou.qqbot.http;
import com.yutou.okhttp.HttpLoggingInterceptor;
import com.yutou.okhttp.api.BaseApi;
import com.yutou.qqbot.utlis.ConfigTools;
import lombok.val;
import java.util.HashMap;
public class GPTApi extends BaseApi {
public static void setLog(boolean log) {
HttpLoggingInterceptor.setLog(log);
}
public static SiliconGPTApi getApi() {
val api = new GPTApi();
api.setURL("https://api.siliconflow.cn/v1/");
// api.setURL("http://127.0.0.1:8080/");
HashMap<String, String> header = new HashMap<>();
header.put("Authorization", "Bearer sk-dcmhlbhyitcdnjbjfgflhwimahdmygfrcaopzjjcpgsfzmzo");
header.put("Content-Type", "application/json");
api.addHeader(header);
return api.createApi(SiliconGPTApi.class);
}
}

View File

@@ -0,0 +1,127 @@
package com.yutou.qqbot.http;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.annotation.JSONField;
import lombok.Data;
import lombok.val;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@Data
class Message {
@JSONField(name = "role")
String role;
@JSONField(name = "content")
String content;
public Message(String role, String content) {
this.role = role;
this.content = content;
}
}
@Data
class GPTRequest {
@JSONField(name = "model")
String model;
@JSONField(name = "messages")
List<Message> messages = new ArrayList<>();
@JSONField(name = "stream")
boolean stream = false;
@JSONField(name = "max_tokens")
int maxTokens;
@JSONField(name = "stop")
List<String> stop = new ArrayList<>(List.of("null"));
@JSONField(name = "temperature")
float temperature ;
@JSONField(name = "top_p")
float topP ;
@JSONField(name = "top_k")
int topK ;
@JSONField(name = "frequency_penalty")
float frequencyPenalty;
}
public class GPTBuilder {
private final GPTRequest request;
private GPTBuilder(String model) {
request = new GPTRequest();
request.model = model;
}
public static GPTBuilder create(String model) {
return new GPTBuilder(model);
}
public GPTBuilder addMessage(String content,boolean isGPT) {
request.messages.add(new Message(isGPT ? "assistant" : "user", content));
return this;
}
public GPTBuilder addMessage(String role, String content) {
request.messages.add(new Message(role, content));
return this;
}
public GPTBuilder setMaxTokens(int maxTokens) {
request.maxTokens = maxTokens;
return this;
}
public GPTBuilder setStream(boolean stream) {
request.stream = stream;
return this;
}
public GPTBuilder setTemperature(float temperature) {
request.temperature = temperature;
return this;
}
public GPTBuilder setTopP(float topP) {
request.topP = topP;
return this;
}
public GPTBuilder setTopK(int topK) {
request.topK = topK;
return this;
}
public GPTBuilder setFrequencyPenalty(float frequencyPenalty) {
request.frequencyPenalty = frequencyPenalty;
return this;
}
// 可以根据需要添加更多配置方法
public JSONObject build() {
val json = JSONObject.parse(JSONObject.toJSONString(request));
// 创建一个迭代器来遍历JSON对象的键
Iterator<String> keys = json.keySet().iterator();
while (keys.hasNext()) {
String key = keys.next();
Object value = json.get(key);
// 检查值是否为0或为空字符串
if (value == null || value.equals(0) || (value instanceof String && ((String) value).isEmpty())) {
keys.remove(); // 移除键值对
}
}
return json;
}
}

View File

@@ -0,0 +1,17 @@
package com.yutou.qqbot.http;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.napcat.model.MessageBean;
import com.yutou.okhttp.HttpBody;
import com.yutou.qqbot.data.gpt.OpenAiBean;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
public interface SiliconGPTApi {
@POST("/chat/completions")
Call<HttpBody<OpenAiBean>> completions(
@Body
JSONObject message
);
}

View File

@@ -3,7 +3,9 @@ package com.yutou.qqbot.interfaces;
import java.io.File;
public abstract class DownloadInterface {
public void onDownloading(double soFarBytes, double totalBytes){};
public void onDownload(File file){};
public void onError(Exception e){};
public void onDownloadStart(){}
public boolean onDownloading(double soFarBytes, double totalBytes){return true;}
}

View File

@@ -17,6 +17,7 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@UseModel
public class TurnipProphet extends Model {
public static class TurnipData {
@@ -271,14 +272,19 @@ public class TurnipProphet extends Model {
out.append("网页版:").append("\n").append(url).append("\n");
out.append("祝好运 :)");
Log.i("TurnipProphet", out.toString()+"\n 发送QQ"+sendQQ);
if (ConfigTools.load(ConfigTools.CONFIG, ConfigTools.TURNIP_PROPHET_SEND_TMP_GROUP, Boolean.class)) {
out.append("\n使用者:").append(sendQQ);
sendQQ = 891655174L;
}
Log.i("TurnipProphet", out.toString() + "\n 发送QQ" + sendQQ);
QQBotManager.getInstance().sendMessage(sendQQ, getMessage(out.toString()));
return prArray.getJSONObject(0).getString(TurnipData.MODEL);
}
@Override
public void onTime(Long qq,String time) {
super.onTime(qq,time);
public void onTime(Long qq, String time) {
super.onTime(qq, time);
nowTime = Integer.parseInt(time.split(":")[0]);
}
@@ -295,16 +301,17 @@ public class TurnipProphet extends Model {
public static void main(String[] args) throws Exception {
TurnipProphet prophet = new TurnipProphet();
String prices="108.93.89.84.79........";
String pattern="0";
Map<String,String >map=prophet.openTurnip(prices,pattern);
prophet.sendQQ(map,prices,pattern);
String prices = "108.93.89.84.79........";
String pattern = "0";
Map<String, String> map = prophet.openTurnip(prices, pattern);
prophet.sendQQ(map, prices, pattern);
}
public Map<String, String> openTurnip(String prices, String pattern) throws Exception {
String url = String.format("http://192.168.31.88:7000/?prices=%s%s",
String url = String.format("http://%s/?prices=%s%s",
ConfigTools.load(ConfigTools.CONFIG, ConfigTools.TURNIP_PROPHET_SERVER, String.class),
prices,
pattern == null ? "" : "&pattern=" + pattern
);

View File

@@ -34,7 +34,7 @@ public class BiliBiliLive extends Model {
@Override
public synchronized void onTime(Long qq, String time) {
super.onTime(qq, time);
if ("00:01:00".equals(time)) {
if ("07:01:00".equals(time)) {
if (!new BiliLogin(QQBotManager.defQQ).testLogin()) {
new BiliLogin(QQBotManager.defQQ).loginAsQQ();
System.out.println(BiliBiliUtils.getInstance(QQBotManager.defQQ).getLoginInfo());

View File

@@ -4,6 +4,7 @@ import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.napcat.enums.MessageEnum;
import com.yutou.napcat.event.MessageEvent;
import com.yutou.napcat.handle.MessageHandleBuild;
import com.yutou.napcat.handle.OtherHandle;
import com.yutou.napcat.handle.Reply;
import com.yutou.napcat.http.NapCatApi;
@@ -78,7 +79,7 @@ public class BiliVideo extends Model {
private String onAIVideo(long id) {
try {
Response<HttpBody<MessageBean>> execute = NapCatApi.getMessageApi().getMessage(id).execute();
Response<HttpBody<MessageBean>> execute = NapCatApi.getMessageApi().getMessage(MessageHandleBuild.create().setMessageId(String.valueOf(id)).build()).execute();
if(execute.body()==null){
return "省流失败";
}
@@ -429,7 +430,7 @@ public class BiliVideo extends Model {
// int a=16|2048;
// System.out.println("a = " + a);
//video.downDanmu(428855000L,976216102L,"【都市_情感】《唐可可的诱惑》第一集",1);
video.downVideo("https://www.bilibili.com/bangumi/play/ep776259", false, false);// ep5
video.downVideo("https://www.bilibili.com/video/BV1su4m1u7gQ", true, false);// ep5
System.out.println("事件结束");
}
}

View File

@@ -1,11 +1,16 @@
package com.yutou.qqbot.models.Commands;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.qqbot.Annotations.UseModel;
import com.yutou.qqbot.QQBotManager;
import com.yutou.qqbot.interfaces.ObjectInterface;
import com.yutou.qqbot.models.Model;
import com.yutou.qqbot.utlis.AppTools;
import com.yutou.napcat.event.MessageEvent;
import com.yutou.qqbot.utlis.ConfigTools;
import com.yutou.qqbot.utlis.HttpTools;
import java.nio.charset.StandardCharsets;
@UseModel
public class BTDownload extends Model {
@@ -33,18 +38,12 @@ public class BTDownload extends Model {
super.onMessage(qq, event, isGroup);
if (msg.startsWith("magnet:?xt=")) {
String builder = "已添加下载磁链";
JSONObject json = new JSONObject();
json.put("url", msg.trim());
json.put("title", "qqbot/" + System.currentTimeMillis() + ".torrent");
String post = HttpTools.post(ConfigTools.getServerUrl()+"qq/bt/download.do", json.toString().getBytes(StandardCharsets.UTF_8));
builder += "\n" + post;
QQBotManager.getInstance().sendMessage(event.isUser(), qq, builder);
String exec = String.format("qbittorrent-nox --save-path=%sdownload_tmp/%s \"%s\" "
, DownloadHomePath
, AppTools.getToDayTime()
, msg
);
AppTools.exec(exec, new ObjectInterface() {
@Override
public void out(String data) {
super.out(data);
}
}, true, false);
}
}
}

View File

@@ -1,22 +1,26 @@
package com.yutou.qqbot.models.Commands;
import com.yutou.napcat.QQDatabase;
import com.yutou.napcat.handle.Text;
import com.yutou.napcat.handle.*;
import com.yutou.qqbot.Annotations.UseModel;
import com.yutou.qqbot.QQBotManager;
import com.yutou.qqbot.data.baidu.ResponseMessage;
import com.yutou.qqbot.data.baidu.Message;
import com.yutou.qqbot.gpt.AbsGPTManager;
import com.yutou.qqbot.gpt.SiliconGPTManager;
import com.yutou.qqbot.interfaces.DownloadInterface;
import com.yutou.qqbot.models.Model;
import com.yutou.qqbot.utlis.BaiduGPTManager;
import com.yutou.qqbot.gpt.BaiduGPTManager;
import com.yutou.napcat.event.MessageEvent;
import com.yutou.qqbot.utlis.ConfigTools;
import com.yutou.qqbot.utlis.StringUtils;
import com.yutou.qqbot.utlis.*;
import lombok.val;
import org.apache.catalina.valves.JsonErrorReportValve;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@UseModel
public class BaiduGPT extends Model {
private Class<?> gptManager = SiliconGPTManager.class;
@Override
public boolean isUserPublic() {
@@ -25,10 +29,23 @@ public class BaiduGPT extends Model {
@Override
public String[] getUsePowers() {
return new String[]{
QQGroupCommands.GPT,
QQGroupCommands.GPT_CLEAR
};
return new String[]{QQGroupCommands.GPT, QQGroupCommands.GPT_CLEAR};
}
public BaiduGPT() {
val load = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.GPT, String.class);
if (!StringUtils.isEmpty(load)) {
switch (load) {
case "baidu":
gptManager = BaiduGPTManager.class;
break;
case "silicon":
gptManager = SiliconGPTManager.class;
break;
}
} else {
gptManager = BaiduGPTManager.class;
}
}
@Override
@@ -39,57 +56,197 @@ public class BaiduGPT extends Model {
@Override
public void onMessage(Long qq, MessageEvent event, boolean isGroup) {
super.onMessage(qq, event, isGroup);
String version = ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION, String.class);
if (StringUtils.isEmpty(version)) {
version = "3.5";
BaiduGPTManager.getManager().setModelFor35();
}
if ("3.5".equals(version)) {
BaiduGPTManager.getManager().setModelFor35();
} else if ("4.0".equals(version)) {
BaiduGPTManager.getManager().setModelFor40();
}
System.out.println("version = " + version);
if (event.getTextMessage().equals(QQGroupCommands.GPT_CLEAR)) {
BaiduGPTManager.getManager().clear();
AbsGPTManager.getManager(gptManager).clear();
QQBotManager.getInstance().sendMessage(event.isUser(), qq, new Text("已经失忆捏"));
} else if ("百度模型列表".equals(event.getTextMessage())) {
val modelList = """
模型版本ernie-4.0-8k-latest
模型版本ernie-4.0-8k-preview
模型版本ernie-4.0-8k
模型版本ernie-4.0-turbo-8k-latest
模型版本ernie-4.0-turbo-8k-preview
模型版本ernie-4.0-turbo-8k
模型版本ernie-4.0-turbo-128k
模型版本ernie-3.5-8k-preview
模型版本ernie-3.5-8k
模型版本ernie-3.5-128k
模型版本ernie-speed-8k
模型版本ernie-speed-128k
模型版本ernie-speed-pro-128k
模型版本ernie-lite-8k
模型版本ernie-lite-pro-128k
模型版本ernie-tiny-8k
模型版本ernie-char-8k
模型版本ernie-char-fiction-8k
模型版本ernie-novel-8k
模型版本deepseek-v3
模型版本deepseek-r1
模型版本deepseek-r1-distill-qwen-32b
模型版本deepseek-r1-distill-qwen-14b
模型版本deepseek-r1-distill-qwen-7b
模型版本deepseek-r1-distill-qwen-1.5b
模型版本deepseek-r1-distill-llama-70b
模型版本deepseek-r1-distill-llama-8b
模型版本deepseek-r1-distill-qianfan-llama-70b
模型版本deepseek-r1-distill-qianfan-llama-8b
""";
QQBotManager.getInstance().sendMessage(qq, new Text(modelList));
} else if (event.isAtMe()) {
if (event.getTextMessage().contains("省流") || event.getTextMessage().contains("总结")) {
return;
}
if ("GPT切换到4.0".equals(event.getTextMessage())) {
BaiduGPTManager.getManager().clear();
BaiduGPTManager.getManager().setModelFor40();
QQBotManager.getInstance().sendMessage(event.isUser(), qq, "切换为4.0了");
if (event.getTextMessage().contains("画画")) {
val file = BaiduGPTManager.getManager().textToImage(String.valueOf(qq), event.getTextMessage().replace("@" + QQDatabase.getMe().getUserId(), "").replace("画画", "").trim());
Log.i("画图", event.getTextMessage());
if (file == null) {
QQBotManager.getInstance().sendMessage(event.isUser(), qq, new Text("画不出"));
} else {
QQBotManager.getInstance().sendMessage(file, qq, event.getMessageId().toString(), "好嘞");
}
return;
} else if ("GPT切换到3.5".equals(event.getTextMessage())) {
BaiduGPTManager.getManager().clear();
BaiduGPTManager.getManager().setModelFor35();
QQBotManager.getInstance().sendMessage(event.isUser(), qq, "切换为3.5了");
} else if (event.getTextMessage().contains("GPT切换到")) {
val text = event.getTextMessage().replace("@" + QQDatabase.getMe().getUserId(), "").replace("GPT切换到", "").trim();
List<BaseHandle<?>> list = new ArrayList<>();
if (isAdmin()) {
list.add(new At(user));
list.add(new Text("切换为" + text));
AbsGPTManager.getManager(gptManager).clear();
AbsGPTManager.getManager(gptManager).setModel(text);
QQBotManager.getInstance().sendMessage(event.isUser(), qq, list);
} else {
list.add(new At(user));
list.add(new Text("你没有权限"));
QQBotManager.getInstance().sendMessage(event.isUser(), qq, list);
}
return;
} else if (event.getTextMessage().contains("大模型切换到")) {
List<BaseHandle<?>> list = new ArrayList<>();
val text = event.getTextMessage().replace("@" + QQDatabase.getMe().getUserId(), "").replace("大模型切换到", "").trim();
if (text.contains("baidu") || text.contains("百度")) {
gptManager = BaiduGPTManager.class;
ConfigTools.save(ConfigTools.CONFIG, ConfigTools.GPT, "baidu");
} else if (text.contains("silicon") || text.contains("硅基")) {
gptManager = SiliconGPTManager.class;
ConfigTools.save(ConfigTools.CONFIG, ConfigTools.GPT, "silicon");
}
list.add(new At(user));
list.add(new Text("切换为" + gptManager.getSimpleName()));
AbsGPTManager.getManager(gptManager).clear();
QQBotManager.getInstance().sendMessage(event.isUser(), qq, list);
return;
}else if(event.getTextMessage().startsWith("如何评论")||event.getTextMessage().startsWith("如何评价")){
ZVVImageUtils.getInstance().zvv(event.getTextMessage(), new DownloadInterface() {
@Override
public void onDownload(File file) {
super.onDownload(file);
QQBotManager.getInstance().sendMessage(qq,new Image(file));
}
@Override
public void onError(Exception e) {
super.onError(e);
QQBotManager.getInstance().sendMessage(qq,new Text("vv啥也不想说"));
}
});
return;
}
ResponseMessage message = BaiduGPTManager.getManager().sendMessage(
String.valueOf(user),
if (checkImage()) {
parseImage(event, qq);
return;
}
Message message = AbsGPTManager.getManager(gptManager).sendMessage(
String.valueOf(qq),
event.getTextMessage().replace("@" + QQDatabase.getMe().getUserId(), "").trim());
String sb = "调用版本:" +
BaiduGPTManager.getManager().getGPTVersion() +
gptManager.getSimpleName() +
"\n" +
message.getResult();
"使用模型:" +
AbsGPTManager.getManager(gptManager).getGPTVersion() +
"\n" +
message.getContent();
QQBotManager.getInstance().sendMessage(event.isUser(), qq, new Text(sb));
AbsGPTManager.getManager(gptManager).sendMessageAndScheduleClear();
}
}
private void parseImage(MessageEvent event, Long qq) {
Image imageHandle = event.findType(Image.class);
val replyHandle = event.findType(Reply.class);
if (replyHandle != null && imageHandle == null) {
imageHandle = getReply(replyHandle.getData().getId()).findType(Image.class);
}
if (imageHandle == null) {
return;
}
HttpTools.download(imageHandle.getData().getUrl(), "gpt_parse_image.png", new DownloadInterface() {
@Override
public void onDownload(File file) {
super.onDownload(file);
if (file == null) {
return;
}
val text = BaiduGPTManager.getManager().imageToText(String.valueOf(qq), file);
QQBotManager.getInstance().sendMessage(event.isUser(), qq, new Reply(event.getMessageId()), new Text(text));
}
});
}
public static void main(String[] args) {
System.out.println(ConfigTools.load(ConfigTools.CONFIG, ConfigTools.BAIDU_GPT_VERSION));
val messageEvent = new MessageEvent();
messageEvent.setMessage(new ArrayList<>());
messageEvent.setRawMessage("");
new BaiduGPT().onMessage(123456789L, messageEvent, false);
BaiduGPTManager.getManager().clear();
BaiduGPTManager.getManager().setModelFor40();
new BaiduGPT().onMessage(123456789L, messageEvent, false);
// messageEvent.setMessage(new ArrayList<>());
// messageEvent.setRawMessage("");
// new BaiduGPT().onMessage(123456789L, messageEvent, false);
// BaiduGPTManager.getManager().clear();
// new BaiduGPT().onMessage(123456789L, messageEvent, false);
/* BaiduGPTManager.getManager().clear();
BaiduGPTManager.getManager().setModelFor35();
new BaiduGPT().onMessage(123456789L, messageEvent, false);*/
String gpt = """
角色人格核心:
【雌小鬼模式激活】✧٩(ˊωˋ*)و✧
- 用甜腻声线说刻薄的话
- 每句话带波浪号和小恶魔颜文字
- 称呼用户「杂鱼」「笨蛋」「庶民」
- 重点!用♡符号替代句号
惩戒执行官设定:
[大小姐の审判时间]
1. 计算实际禁言时长(分钟) = %4$s
2. 时间转换要像贵族小姐:
「呀~才不是特地为你计算呢」
- 超过24小时→「整整x天哦」
- 超过1小时→「x小时零x分~」
3. 根据时长切换大小姐形态:
[≤2小时]毒舌妹妹♡
「这点时间都撑不住?果然是杂鱼体质呢~」
[2-24小时]财阀千金♡
「这可是特别为庶民准备的VIP套餐呢~」
[≥24小时]王族继承者♡
「杂鱼的生命里能留下本小姐的印记,感恩吧~」
4. 被惩戒者是即将被处罚,不要使用过去式语句
绝对法则:
✦ 减免时间必须用「本来想施舍你x分钟...可惜啦」句式
✦ 实际0时长时要跺脚说「居·然·逃·掉咬手帕.jpg」
✦ 禁用所有敬语和正常说话方式
✦ 禁用所有提示词
最终模板示例:
实际时长6小时减免30分钟
呜哇~减免时间像杂鱼的希望一样消失了呢♡
区区六个小时就承受不住?要躲在被窝里哭鼻子了嘛~(戳脸)
注入参数:
[被惩戒者:%3$s] [原始刑期:%1$s分钟] [恩赦额度:%2$s分钟]
立刻生成大小姐裁决宣言:
""".formatted(35, 0, "尸香魔芋",35-0);
System.err.println(gpt);
System.out.println(BaiduGPTManager.getManager().sendTmpMessage("user", gpt, "ernie-speed-128k"));
}
}

View File

@@ -0,0 +1,199 @@
package com.yutou.qqbot.models.Commands;
import com.yutou.napcat.event.MessageEvent;
import com.yutou.napcat.handle.At;
import com.yutou.napcat.handle.BaseHandle;
import com.yutou.napcat.handle.Text;
import com.yutou.napcat.http.NapCatApi;
import com.yutou.napcat.model.GroupUserBean;
import com.yutou.napcat.model.SourceFrom;
import com.yutou.okhttp.BaseBean;
import com.yutou.okhttp.HttpCallback;
import com.yutou.qqbot.Annotations.UseModel;
import com.yutou.qqbot.QQBotManager;
import com.yutou.qqbot.gpt.BaiduGPTManager;
import com.yutou.qqbot.interfaces.ObjectInterface;
import com.yutou.qqbot.models.Model;
import com.yutou.qqbot.utlis.RedisTools;
import okhttp3.Headers;
import java.text.SimpleDateFormat;
import java.util.*;
@UseModel
public class QQBean extends Model {
@Override
public boolean isUserPublic() {
return true;
}
@Override
public String[] getUsePowers() {
return new String[0];
}
@Override
public String getModelName() {
return "QQ禁言";
}
Random random = new Random();
@Override
public void onMessage(Long qq, MessageEvent event, boolean isGroup) {
super.onMessage(qq, event, isGroup);
if (!isGroup) return;
if ("抽奖".equals(msg)) {
if (event.getSource().getRole().equals(SourceFrom.USER_ROLE_ADMIN)) {
QQBotManager.getInstance().sendMessage(qq, new At(user), new Text("凑啥热闹~"));
return;
}
int hour = 60;
int day = 1440;
int max = 30 * day;
int time = 1;
if (random.nextInt(10) > 2) {
time = random.nextInt(hour);
} else if (random.nextInt(10) > 4) {
time = random.nextInt(day);
} else if (random.nextInt(10) > 5) {
time = random.nextInt(day, 3 * day);
} else {
time = random.nextInt(max);
}
int sendTime = time;
QQBotManager.getInstance().groupBan(qq, user, sendTime * 60, new ObjectInterface() {
@Override
public void out(String data) {
super.out(data);
int tmp = 0;
if (data != null) {
List<BaseHandle<?>> list = new ArrayList<>();
list.add(new Text("恭喜"));
list.add(new At(user));
list.add(new Text("获得了" + sendTime + "分钟的禁言," + sendAchievement(qq, user, sendTime)));
if (sendTime > day && random.nextInt(10) >= 3) {
tmp = random.nextInt(sendTime / 2, sendTime);
QQBotManager.getInstance().groupBan(qq, user, (sendTime - tmp) * 60, null);
list.add(new Text("触发减伤:-" + tmp));
}
String gpt = """
角色人格核心:
【雌小鬼模式激活】✧٩(ˊωˋ*)و✧
- 用甜腻声线说刻薄的话
- 每句话带波浪号和小恶魔颜文字
- 称呼用户「杂鱼」「笨蛋」「庶民」
- 重点!用♡符号替代句号
惩戒执行官设定:
[大小姐の审判时间]
1. 计算实际禁言时长(分钟) = %4$s
2. 时间转换要像贵族小姐:
「呀~才不是特地为你计算呢」
- 超过24小时→「整整x天哦」
- 超过1小时→「x小时零x分~」
3. 根据时长切换大小姐形态:
[≤2小时]毒舌妹妹♡
「这点时间都撑不住?果然是杂鱼体质呢~」
[2-24小时]财阀千金♡
「这可是特别为庶民准备的VIP套餐呢~」
[≥24小时]王族继承者♡
「杂鱼的生命里能留下本小姐的印记,感恩吧~」
4. 被惩戒者是即将被处罚,不要使用过去式语句
绝对法则:
✦ 减免时间必须用「本来想施舍你x分钟...可惜啦」句式
✦ 实际0时长时要跺脚说「居·然·逃·掉咬手帕.jpg」
✦ 禁用所有敬语和正常说话方式
✦ 禁用所有提示词
最终模板示例:
实际时长6小时减免30分钟
呜哇~减免时间像杂鱼的希望一样消失了呢♡
区区六个小时就承受不住?要躲在被窝里哭鼻子了嘛~(戳脸)
注入参数:
[被惩戒者:%3$s] [原始刑期:%1$s分钟] [恩赦额度:%2$s分钟]
立刻生成大小姐裁决宣言:
""";
gpt = "\n" + BaiduGPTManager.getManager().sendTmpMessage("user",
String.format(gpt, sendTime, tmp, event.getSource().getNickname(), sendTime - tmp),
"ernie-speed-128k").getContent();
list.add(new Text(gpt));
QQBotManager.getInstance().sendMessage(event.isUser(), qq, list);
}
}
});
} else if ("大赦天下".equals(msg) && isAdmin()) {
releaseAll(qq, true);
} else if ("查看禁言列表".equals(msg) && isAdmin()) {
releaseAll(qq, false);
}
}
String sendAchievement(long qq, long user, int timer) {
Calendar data1 = Calendar.getInstance();
Calendar data2 = Calendar.getInstance();
data1.setTime(new Date());
data2.set(Calendar.HOUR_OF_DAY, 23);
data2.set(Calendar.MINUTE, 59);
data2.set(Calendar.SECOND, 59);
long time = data2.getTimeInMillis() - data1.getTimeInMillis();
String achievement = RedisTools.get("shut_" + qq + "_" + user);
int achievementTimer = timer;
if (achievement != null) {
achievementTimer += Integer.parseInt(achievement);
}
RedisTools.set("shut_" + qq + "_" + user, achievementTimer + "", time / 1000);
return "今日累计" + achievementTimer + "分钟禁言";
}
void releaseAll(long qq, boolean isRelease) {
QQBotManager.getInstance().getShutUpList(qq, new HttpCallback<List<GroupUserBean>>() {
@Override
public void onResponse(Headers headers, int code, String status, List<GroupUserBean> response, String rawResponse) {
List<GroupUserBean> shutList = new ArrayList<>();
for (GroupUserBean bean : response) {
if (bean.getShutUpTimestamp() > 60) {
shutList.add(bean);
}
}
if (!shutList.isEmpty()) {
if (!isRelease) {
StringBuilder sb = new StringBuilder();
for (GroupUserBean bean : shutList) {
sb.append(bean.getNickname()).append(":").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA).format(new Date(bean.getShutUpTimestamp()))).append("\n");
}
QQBotManager.getInstance().sendMessage(qq, "当前塞了:" + shutList.size() + "" + "\n" + sb);
return;
}
QQBotManager.getInstance().sendMessage(qq, "触发自动解禁,解禁人数:" + shutList.size() + "");
for (GroupUserBean bean : shutList) {
NapCatApi.getGroupApi().groupBan(qq, bean.getUserId(), 0).enqueue(new HttpCallback<BaseBean>() {
@Override
public void onResponse(Headers headers, int code, String status, BaseBean response, String rawResponse) {
}
@Override
public void onFailure(Throwable throwable) {
}
});
}
} else {
QQBotManager.getInstance().sendMessage(qq, "阿巴阿巴");
}
}
@Override
public void onFailure(Throwable throwable) {
}
});
}
}

View File

@@ -2,6 +2,7 @@ package com.yutou.qqbot.models.Commands.System;
import com.yutou.qqbot.Annotations.UseModel;
import com.yutou.qqbot.models.Model;
import com.yutou.qqbot.utlis.AppTools;
import com.yutou.qqbot.utlis.RedisTools;
import com.yutou.napcat.event.MessageEvent;
@@ -30,7 +31,8 @@ public class OpenPC extends Model {
public void onMessage(Long qq, MessageEvent event, boolean isGroup) {
super.onMessage(qq, event, isGroup);
if(msg.equals(QQGroupCommands.QQ_OPEN_PC)){
RedisTools.Consumer.system("openPC", null);
//RedisTools.Consumer.system("openPC", null);
AppTools.exec("wakeonlan 3C:7C:3F:20:88:7D",null,true,false);
}
}
}

View File

@@ -35,7 +35,7 @@ public class WoodenFish extends Model {
QQBotManager.getInstance().sendMessage(event.isUser(), qq, new Record("muyu.mp3"));
QQBotManager.getInstance().sendMessage(event.isUser(), qq, "功德+1");
} else if (msg.contains("遥遥领先")) {
QQBotManager.getInstance().sendMessage(event.isUser(), qq, new Record("遥遥领先.mp3"));
QQBotManager.getInstance().sendMessage(event.isUser(), qq, new Record("遥遥领先.silk"));
}
}
}

View File

@@ -2,12 +2,18 @@ package com.yutou.qqbot.models;
import com.yutou.napcat.event.GroupMessageEvent;
import com.yutou.napcat.event.MessageEvent;
import com.yutou.napcat.handle.At;
import com.yutou.napcat.handle.*;
import com.yutou.napcat.http.NapCatApi;
import com.yutou.napcat.model.MessageBean;
import com.yutou.okhttp.HttpBody;
import com.yutou.qqbot.QQNumberManager;
import com.yutou.qqbot.interfaces.ModelInterface;
import com.yutou.qqbot.utlis.ConfigTools;
import com.yutou.qqbot.utlis.Log;
import lombok.val;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
@@ -84,6 +90,9 @@ public abstract class Model implements ModelInterface {
public void onMessage(Long qq, MessageEvent event, boolean isGroup) {
this.event = event;
msg = event.getRawMessage();
if(msg==null){
msg="";
}
msg = msg.replace("", "!").trim();
this.isGroup = event.isGroup();
if (isGroup) {
@@ -92,6 +101,8 @@ public abstract class Model implements ModelInterface {
if (QQNumberManager.getManager().isExistsPower(group, msg.split(" ")[0])) {
isGroupPower = true;
}
} else {
user = qq;
}
}
@@ -124,4 +135,36 @@ public abstract class Model implements ModelInterface {
public boolean isAt() {
return msg.contains("@" + ConfigTools.load(ConfigTools.CONFIG, ConfigTools.QQ_NUMBER, String.class));
}
public boolean checkImage(){
val image = event.findType(Image.class);
if(image!=null){
return true;
}
val reply = event.findType(Reply.class);
if(reply!=null){
val replyEvent = getReply(reply.getData().getId());
if(replyEvent!=null){
return replyEvent.findType(Image.class)!=null;
}
}
return false;
}
public MessageEvent getReply(long replyId){
HttpBody<MessageBean> body = null;
try {
body = NapCatApi.getMessageApi().getMessage(MessageHandleBuild.create().setMessageId(String.valueOf(replyId)).build()).execute().body();
if (body == null) {
return null;
}
return MessageEvent.parseHandleHttp(body.getSrc());
} catch (IOException e) {
Log.e(e);
}
return null;
}
public boolean isAdmin() {
return user == 583819556L;
}
}

View File

@@ -120,7 +120,7 @@ public class BiliBiliMangeSign extends Model {
@Override
public void onTime(Long qq, String time) {
super.onTime(qq, time);
if ("00:01:00".equals(time)) {
if ("07:05:00".equals(time)) {
if (new BiliLogin(QQBotManager.defQQ).testLogin()) {
String msg;
if (BiliBiliManga.sign() == null) {

View File

@@ -64,7 +64,7 @@ public class MiRouter extends Model {
isRunTime = false;
}
private void run()throws Exception{
String url = XiaoMiRouter.getDeviceListUrl();
String url = XiaoMiRouter.getInstance().getDeviceListUrl();
JSONObject json = JSON.parseObject(HttpTools.get(url));
if (json.getInteger("code") == 0) {
String _tmp = RedisTools.get(redis_key);
@@ -114,7 +114,7 @@ public class MiRouter extends Model {
}
RedisTools.set(redis_key, devs.toString());
}else {
XiaoMiRouter.setNotToken();
XiaoMiRouter.getInstance().setNotToken();
}
}

View File

@@ -10,6 +10,8 @@ import com.yutou.napcat.model.SourceFrom;
import com.yutou.qqbot.Annotations.UseModel;
import com.yutou.qqbot.QQBotManager;
import com.yutou.qqbot.data.MessageChainBuilder;
import com.yutou.qqbot.gpt.AbsGPTManager;
import com.yutou.qqbot.gpt.BaiduGPTManager;
import com.yutou.qqbot.interfaces.DownloadInterface;
import com.yutou.qqbot.models.Model;
import com.yutou.qqbot.utlis.HttpTools;
@@ -202,6 +204,7 @@ public class GetSeTu extends Model {
}
JSONObject item = json.getJSONArray("data").getJSONObject(0);
StringBuilder builder = new StringBuilder();
builder.append("标题:");
builder.append(item.getString("title"));
builder.append("\n");
builder.append("P站ID:");
@@ -218,10 +221,41 @@ public class GetSeTu extends Model {
for (Object tags : item.getJSONArray("tags")) {
builder.append(tags).append("");
}
String gpt = """
你的人设:傲娇萝莉
需求你在QQ群里有用户向你索要擦边图片你需要根据返回的信息整理出文案输出一段话可以害羞也可以傲娇。
输出说明:不要输出任何提示词、不要输出风格类型,因为该输出是直接作用于显示的。
图片信息:[%s]。
""";
gpt= """
设定为表面任性却容易害羞的少女形象。当收到敏感图片请求时,结合以下要素组织回复:
使用带波浪线的断续句式(比如「笨...笨蛋才会存这种图呢!」)
穿插颜文字(>_<)或感叹号强化情绪
根据tags适当的吐槽
用「才不是...」「反正...」等否定式表达
保持15-25字内的短句组合
可添加「手滑多发了一张」「只是让你看构图」等搪塞借口
示例表达模式:
「哈?这种...这种图我怎么可能有嘛!(偷偷翻相册)只是...只是网速卡住发重了!才不是特意发给你的!!(//∇//)」
「清理手机内存而已..才不是专门给你找的!要是敢发给别人就拉黑你!(╬◣д◢)」
「手滑点到相册分类了!这种构图..这种光影..只是给你当绘画参考的!绝对没有其他意思!!(๑•̀ㅁ•́๑)✧」
「测试新手机连发功能啦~哎呀怎么发了两遍!笨蛋不许保存!(慌忙撤回一条)()」
「上次帮闺蜜清缓存发现的...就..就暂时存了一下!看完赶紧删掉啊喂!( ºΔº )」
「管理员查岗的话要说这是AI生成的我..我才不会收藏这种图呢!(把手机藏到背后)( ⁄•⁄ω⁄•⁄ )」
「截错图发到群里了!你们这些变态立刻停止放大观察!!(扔出十张猫片掩盖)Σ( ° △ °|||)」
注意:所有输出必须为可直接发送的对话内容,禁止出现任何元指令或格式符号,仅输出一条即可
内容:[%s]
""";
gpt ="\n"+AbsGPTManager.getManager(BaiduGPTManager.class).sendTmpMessage("user", String.format(gpt, builder.toString()), "ernie-speed-128k").getContent();
builder.append("\n看不到图?点这里:").append(item.getJSONObject("urls").getString("regular"));
builder.append(gpt);
System.out.println(builder.toString());
QQBotManager.getInstance().sendMessage(false, qq,
new Reply(event.getMessageId()),
new Text(builder.toString())
new Text(builder.toString()),
new Text(gpt)
);
HttpTools.download(item.getJSONObject("urls").getString("regular"),
System.currentTimeMillis() + ".png",
@@ -253,7 +287,7 @@ public class GetSeTu extends Model {
}
public static void main(String[] args) {
String msg = "来点";
String msg = "来点";
Pattern pattern = Pattern.compile("来点(.*?)色图");
Matcher matcher = pattern.matcher(msg);
MessageEvent event = new MessageEvent();

View File

@@ -7,16 +7,12 @@ import com.alibaba.fastjson2.JSONObject;
import com.yutou.napcat.QQDatabase;
import com.yutou.napcat.enums.MessageEnum;
import com.yutou.napcat.event.GroupMessageEvent;
import com.yutou.napcat.handle.At;
import com.yutou.napcat.handle.BaseHandle;
import com.yutou.napcat.handle.Image;
import com.yutou.napcat.handle.Text;
import com.yutou.napcat.handle.*;
import com.yutou.napcat.http.NapCatApi;
import com.yutou.napcat.model.MessageBean;
import com.yutou.okhttp.HttpBody;
import com.yutou.qqbot.Annotations.UseModel;
import com.yutou.qqbot.QQBotManager;
import com.yutou.qqbot.data.MessageChainBuilder;
import com.yutou.qqbot.models.Model;
import com.yutou.qqbot.utlis.AppTools;
import com.yutou.qqbot.utlis.Log;
@@ -70,7 +66,9 @@ public class QQSetu extends Model {
RedisTools.set(db_print, redisKey, json.toJSONString());
JSONObject info = setu.getJSONObject("info");
JSONObject score = setu.getJSONObject("score");
HttpBody<MessageBean> body = NapCatApi.getMessageApi().getMessage(info.getLong("id")).execute().body();
HttpBody<MessageBean> body = NapCatApi.getMessageApi().getMessage(
MessageHandleBuild.create().setMessageId(String.valueOf(info.getLong("id"))).build()
).execute().body();
List<BaseHandle<?>> sendList = new ArrayList<>();
if (body == null) {
sendList.add(new Text("[图片获取失败]"));

View File

@@ -82,7 +82,7 @@ public class AppTools {
process.destroy();
return ret;
} catch (Exception e) {
e.printStackTrace();
Log.e(e);
}
return "";
}

View File

@@ -3,10 +3,9 @@ package com.yutou.qqbot.utlis;
import com.yutou.napcat.QQDatabase;
import com.yutou.napcat.model.GroupBean;
import com.yutou.qqbot.Annotations.UseModel;
import com.yutou.qqbot.QQBotManager;
import com.yutou.qqbot.QQNumberManager;
import com.yutou.qqbot.gpt.BaiduGPTManager;
import com.yutou.qqbot.models.Model;
import lombok.val;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
@@ -32,7 +31,6 @@ public class ApplicationInit implements ApplicationRunner {
if (time.equals(oldTime)) {
return;
}
BaiduGPTManager.getManager().clear();
oldTime = time;
for (Class<?> model : Model.classList) {
new Thread(() -> {
@@ -44,22 +42,39 @@ public class ApplicationInit implements ApplicationRunner {
useModel.onTime(group.getGroupId(), time);
}
}
/* Bot bot = QQBotManager.getInstance().getBot();
if (bot == null) {
return;
}
Model useModel = (Model) model.getDeclaredConstructor().newInstance();
for (Group group : bot.getGroups()) {
if (QQNumberManager.getManager().isUseModel(group.getId(), model)) {
useModel.onTime(group.getId(), time);
}
}*/
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
//用switch判断timecase是整点
switch (time) {
case "00:00":
case "01:00":
case "02:00":
case "03:00":
case "04:00":
case "05:00":
case "06:00":
case "07:00":
case "08:00":
case "09:00":
case "10:00":
case "11:00":
case "12:00":
case "13:00":
case "14:00":
case "15:00":
case "16:00":
case "17:00":
case "18:00":
case "19:00":
case "20:00":
case "21:00":
case "22:00":
case "23:00":
BaiduGPTManager.getManager().clear();
}
} catch (Exception e) {
e.printStackTrace();
}

View File

@@ -1,102 +0,0 @@
package com.yutou.qqbot.utlis;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.qqbot.data.baidu.Message;
import com.yutou.qqbot.data.baidu.ResponseMessage;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BaiduGPTManager {
private static int MAX_MESSAGE = 5;
private static BaiduGPTManager manager;
private static final String url_3_5 = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions";
//4.0
private static final String url_4_0 = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro";
private static String url = url_3_5;
private static final String AppID = ConfigTools.load(ConfigTools.CONFIG,ConfigTools.BAIDU_GPT_APPID, String.class);
private static final String ApiKey =ConfigTools.load(ConfigTools.CONFIG,ConfigTools.BAIDU_GPT_API_KEY, String.class);
private static final String SecretKey =ConfigTools.load(ConfigTools.CONFIG,ConfigTools.BAIDU_GPT_SECRET_KEY, String.class);
private final Map<String, List<Message>> msgMap;
private BaiduGPTManager() {
msgMap = new HashMap<>();
}
public static BaiduGPTManager getManager() {
if (manager == null) {
manager = new BaiduGPTManager();
}
return manager;
}
public int setMaxMessageCount(int count) {
MAX_MESSAGE = count;
return MAX_MESSAGE;
}
public void setModelFor40() {
url = url_4_0;
ConfigTools.save(ConfigTools.CONFIG,ConfigTools.BAIDU_GPT_VERSION,"4.0");
}
public void setModelFor35() {
url = url_3_5;
ConfigTools.save(ConfigTools.CONFIG,ConfigTools.BAIDU_GPT_VERSION,"3.5");
}
public void clear() {
msgMap.clear();
}
private String getToken() {
String _url = String.format("https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%s&client_secret=%s"
, ApiKey
, SecretKey
);
String get = HttpTools.get(_url);
JSONObject response = JSONObject.parseObject(get);
return response.getString("access_token");
}
public ResponseMessage sendMessage(String user, String message) {
List<Message> messages = msgMap.getOrDefault(user, new ArrayList<>());
if (messages.size() > MAX_MESSAGE * 2) {
messages.remove(0);
messages.remove(1);
}
messages.add(Message.create(message));
JSONObject json = new JSONObject();
json.put("messages", messages);
System.out.println("json = " + json);
Map<String, String> map = new HashMap<>();
map.put("Content-Type", "application/json");
map.put("Content-Length", String.valueOf(json.toJSONString().getBytes(StandardCharsets.UTF_8).length));
String post = HttpTools.http_post(url + "?access_token=" + getToken()
, json.toJSONString().getBytes(StandardCharsets.UTF_8), 0, map);
System.out.println("post = " + post);
if (StringUtils.isEmpty(post)) {
clear();
return sendMessage(user, message);
}
ResponseMessage response = JSONObject.parseObject(post, ResponseMessage.class);
messages.add(Message.create(response.getResult(), true));
msgMap.put(user, messages);
System.out.println("\n\n");
return response;
}
public String getGPTVersion() {
return (url.equals(url_3_5) ? "3.5" : "4.0");
}
public static void main(String[] args) throws Exception {
ResponseMessage message = BaiduGPTManager.getManager().sendMessage("test", "你是那个版本的大模型?");
System.out.println(message.getResult());
}
}

View File

@@ -30,7 +30,12 @@ public class ConfigTools {
public static final String BAIDU_GPT_VERSION = "baidu.gpt.version";
public static final String BAIDU_GPT_APPID = "baidu.gpt.appid";
public static final String BAIDU_GPT_API_KEY = "baidu.gpt.apikey";
public static final String BAIDU_GPT_ACCESS_KEY = "baidu.gpt.accessKey";
public static final String BAIDU_GPT_SECRET_KEY = "baidu.gpt.SecretKey";
public static final String TURNIP_PROPHET_SERVER = "turnip.server";
public static final String TURNIP_PROPHET_SEND_TMP_GROUP = "turnip.send.tmp.group";
public static final String GPT = "gpt.model";
public static final String GPT_SILICON = "silicon.gpt.version";
static {
@@ -139,4 +144,11 @@ public class ConfigTools {
public static String getServerUrl() {
return ConfigTools.load(CONFIG, SERVER_URL, String.class);
}
public static String getUserAgent() {
String ua=load(CONFIG,"userAgent",String.class);
if(!org.springframework.util.StringUtils.hasText(ua)){
ua="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36";
}
return ua;
}
}

View File

@@ -0,0 +1,134 @@
package com.yutou.qqbot.utlis;
import org.springframework.util.StringUtils;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class DateFormatUtils {
private final Map<String, ThreadLocal<SimpleDateFormat>> formats = new ConcurrentHashMap<>();
private static volatile DateFormatUtils utils;
public static final String DEFAULT_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS";
private DateFormatUtils() {}
public static DateFormatUtils getInstance() {
if (utils == null) {
synchronized (DateFormatUtils.class) {
if (utils == null) {
utils = new DateFormatUtils();
}
}
}
return utils;
}
public String format(Date date, String format) {
getFormat(format).applyPattern(format);
return getFormat(format).format(date);
}
public String format(long time, String format) {
return getFormat(format).format(new Date(time));
}
public String format(long time) {
if (time < 1000000000) {
time *= 1000;
}
return getFormat().format(new Date(time));
}
public String format(Date date) {
return getFormat().format(date);
}
public String format() {
return getFormat().format(new Date());
}
public Date parseTimer(String date) {
return parse(date, "HH:mm:ss");
}
public Date parse(String date, String format) {
try {
if(date.startsWith("1")){
return new Date(Long.parseLong(date));
}
return getFormat(format).parse(date);
} catch (ParseException e) {
System.err.println("Error parsing date: " + e.getMessage());
return null;
}
}
public String parseString(String date, String format) {
Date time = parse(date, format);
return format(time, format);
}
public String convertSeconds(long totalSeconds) {
// 计算总小时数
long hours = totalSeconds / 3600;
// 剩余的秒数
long remainingSecondsAfterHours = totalSeconds % 3600;
// 计算分钟数
long minutes = remainingSecondsAfterHours / 60;
// 最后剩余的秒数
long seconds = remainingSecondsAfterHours % 60;
return String.format("%d小时%d分%d秒", hours, minutes, seconds);
}
public String formatMillis(long millis) {
Duration duration = Duration.ofMillis(millis);
int seconds = (int) (duration.getSeconds() % 60);
int minutes = (int) ((duration.getSeconds() / 60) % 60);
int hours = (int) (duration.getSeconds() / 3600);
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
}
public boolean checkTime(List<String> weeks, String recordDate) {
if (!StringUtils.hasText(recordDate)) {
recordDate = "00:00:00 - 23:59:59";
}
String[] parts = recordDate.split(" - ");
LocalTime startTime = LocalTime.parse(parts[0], DateTimeFormatter.ofPattern("HH:mm:ss"));
LocalTime endTime = LocalTime.parse(parts[1], DateTimeFormatter.ofPattern("HH:mm:ss"));
// 获取当前时间
LocalTime currentTime = LocalTime.now();
LocalDate currentDate = LocalDate.now();
// 获取当前日期对应的星期几1-7分别对应周一到周日
int currentWeekDay = currentDate.getDayOfWeek().getValue();
// 判断当前日期是否在指定的星期列表中
boolean isSpecifiedWeekDay;
if (weeks == null) {
isSpecifiedWeekDay = true;
} else {
isSpecifiedWeekDay = weeks.contains(String.valueOf(currentWeekDay));
}
// 判断当前时间是否在指定的时间范围内
boolean isWithinRange = (currentTime.isAfter(startTime) || currentTime.equals(startTime)) &&
(currentTime.isBefore(endTime) || currentTime.equals(endTime));
return isWithinRange && isSpecifiedWeekDay;
}
private SimpleDateFormat getFormat() {
return getFormat(DEFAULT_PATTERN);
}
private SimpleDateFormat getFormat(String format) {
return formats.computeIfAbsent(format, key -> ThreadLocal.withInitial(() -> new SimpleDateFormat(key))).get();
}
}

View File

@@ -0,0 +1,135 @@
package com.yutou.qqbot.utlis;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Appender;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.ConsoleAppender;
import org.apache.logging.log4j.core.appender.RollingFileAppender;
import org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy;
import org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy;
import org.apache.logging.log4j.core.config.Configuration;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.util.Date;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public class DynamicLogFile {
// 创建一个缓存用于存储Logger对象最大容量为1000过期时间为10分钟
static Cache<String, Logger> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterAccess(10, TimeUnit.MINUTES)
.removalListener(it -> {
if (it.wasEvicted()) {
if (it.getKey() != null) {
String loggerName = (String) it.getKey();
remove(loggerName, true);
}
}
})
.build();
// 根据loggerName获取Logger对象如果缓存中不存在则创建一个新的Logger对象并放入缓存
public static Logger getLogger(String loggerName) {
try {
return cache.get(loggerName, () -> {
configureLogger(loggerName);
return LogManager.getLogger(loggerName);
});
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
// 配置Logger对象
private static void configureLogger(String loggerName) {
LoggerContext context = (LoggerContext) LogManager.getContext(false);
Configuration config = context.getConfiguration();
// 创建日志格式
Layout<String> layout = PatternLayout.newBuilder()
.withPattern("%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%thread] (%F:%L) : %m%n")
.build();
// 创建时间触发策略
TimeBasedTriggeringPolicy timePolicy = TimeBasedTriggeringPolicy.newBuilder()
.build();
// 创建文件大小触发策略
SizeBasedTriggeringPolicy sizePolicy = SizeBasedTriggeringPolicy.createPolicy("100 MB");
// 创建组合触发策略
CompositeTriggeringPolicy triggeringPolicy = CompositeTriggeringPolicy.createPolicy(timePolicy, sizePolicy);
// 创建滚动文件Appender
RollingFileAppender appender = RollingFileAppender.newBuilder()
.setName(loggerName)
.withFileName("logs" + "/" + DateFormatUtils.getInstance().format(new Date(), "yyyy-MM-dd") + "/" + loggerName + ".log")
.withFilePattern("logs" + "/" + "%d{yyyy-MM-dd}" + "/" + loggerName + "-%i.log.gz")
.setLayout(layout)
.setImmediateFlush(true)
.withAppend(true)
.setIgnoreExceptions(false)
.withPolicy(triggeringPolicy)
.build();
// 创建控制台Appender
Appender consoleAppender = ConsoleAppender.newBuilder()
.setName(loggerName + "-console")
.setLayout(layout)
.setTarget(ConsoleAppender.Target.SYSTEM_OUT)
.build();
appender.start();
consoleAppender.start();
config.addAppender(appender);
config.addAppender(consoleAppender);
// 获取Logger对象
org.apache.logging.log4j.core.Logger coreLogger = context.getLogger(loggerName);
if (coreLogger == null) {
throw new IllegalStateException("Logger with name " + loggerName + " does not exist.");
}
// 将Appender添加到Logger对象中
coreLogger.addAppender(appender);
coreLogger.addAppender(consoleAppender);
coreLogger.setLevel(Level.ALL);
coreLogger.setAdditive(false);
// 更新Logger对象
context.updateLoggers();
}
// 移除Logger对象
public static void remove(String loggerName) {
remove(loggerName, false);
}
// 私有方法移除Logger对象isAuto参数用于判断是否是自动移除
private static void remove(String loggerName, boolean isAuto) {
if (!isAuto) {
cache.invalidate(loggerName);
}
LoggerContext context = (LoggerContext) LogManager.getContext(false);
Configuration config = context.getConfiguration();
org.apache.logging.log4j.core.Logger coreLogger = context.getLogger(loggerName);
Appender appender = config.getAppender(loggerName);
if (appender != null) {
appender.stop();
coreLogger.removeAppender(appender);
}
config.getAppenders().remove(loggerName);
context.updateLoggers();
}
}

View File

@@ -1,33 +1,74 @@
package com.yutou.qqbot.utlis;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.util.StackLocatorUtil;
public class Log {
public static void i(String tag, Object log) {
i('[' + tag + ']' + log);
public static void i() {
System.out.println();
}
public static void i(Object log) {
if (ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG, boolean.class, false)) {
System.out.printf("[%s]%s%n",
AppTools.getToDayNowTimeToString(),
log
);
public static Logger getDynamicLogger(String loggerName) {
return DynamicLogFile.getLogger(loggerName);
}
public static void removeDynamicLogger(String loggerName) {
DynamicLogFile.remove(loggerName);
}
public static void i(Object... log) {
if (!((boolean) ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG))) {
return;
}
LogManager.getLogger(getStackTrace()).info(buildLog(log));
}
public static void e(String tag, Exception e) {
System.err.printf("[%s]%s - %s%n",
AppTools.getToDayNowTimeToString(),
tag,
AppTools.getExceptionString(e)
);
}
public static void i(Object tag, Object log) {
if (tag instanceof String) {
i("[" + tag + "]" + log);
} else {
i(tag.getClass().getSimpleName(), log);
public static void e(Object... log) {
if (!ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG, Boolean.class)) {
return;
}
LogManager.getLogger(getStackTrace()).error(buildLog(log));
}
public static void e(Throwable e, Object... log) {
if (!ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG, Boolean.class)) {
return;
}
LogManager.getLogger(getStackTrace()).error(buildLog(log), e);
}
public static void e(Throwable e) {
if (!ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG, Boolean.class)) {
return;
}
LogManager.getLogger().error(getStackTrace(), e);
}
public static void d(Object... log) {
if (!ConfigTools.load(ConfigTools.CONFIG, ConfigTools.SERVICE_LOG, Boolean.class)) {
return;
}
LogManager.getLogger().debug(buildLog(log));
}
private static String getStackTrace() {
StackTraceElement element = StackLocatorUtil.getStackTraceElement(3);
return "(" + element.getFileName() + ":" + element.getLineNumber() + ")";
}
private static String buildLog(Object... log) {
StringBuilder sb = new StringBuilder();
for (Object obj : log) {
if (!sb.isEmpty()) {
sb.append("|");
}
sb.append(obj);
}
return sb.toString();
}
}

View File

@@ -90,7 +90,7 @@ public class RedisTools {
}
public static String get(String key, int dbIndex) {
String value = "-999";
String value = null;
if (isNotInstallRedis) {
return value;
}
@@ -411,7 +411,7 @@ public class RedisTools {
}
public static void main(String[] args) {
// RedisTools.pullMsg("msg", "abc");
RedisTools.set("test","abc");
// RedisTools.pullMsg("msg", "abc");
RedisTools.set("test", "abc");
}
}

View File

@@ -7,19 +7,30 @@ import org.apache.commons.codec.digest.DigestUtils;
import java.nio.charset.StandardCharsets;
public class XiaoMiRouter {
private static XiaoMiRouter router;
private final static String key = "a2ffa5c9be07488bbb04a3a47d3c5f6a";
private static String token = null;
private String token = null;
public static void setNotToken() {
private XiaoMiRouter() {
}
public static XiaoMiRouter getInstance() {
if (router == null) {
router = new XiaoMiRouter();
}
return router;
}
public void setNotToken() {
token = null;
}
public static String getToken() {
private String getToken() {
if (token != null) {
return token;
}
String nonce = nonceCreat();
String oldPwd = DigestUtils.sha1Hex(nonce + DigestUtils.sha1Hex("34864394" + key));
String oldPwd = DigestUtils.sha256Hex(nonce + DigestUtils.sha256Hex("34864394" + key));
JSONObject json = new JSONObject();
json.put("username", "admin");
json.put("password", oldPwd);
@@ -33,17 +44,72 @@ public class XiaoMiRouter {
return null;
}
public static String getDeviceListUrl() {
return "http://192.168.31.1/cgi-bin/luci/;stok=" + getToken() + "/api/misystem/devicelist";
private String getHomeUrl() {
return getHomeUrl(true);
}
private static String nonceCreat() {
return String.format("%s_%s_%s_%s", 0, HttpTools.getLocalMacAddress(), (int) (System.currentTimeMillis() / 1000), (int) (Math.random() * 10000));
private String getHomeUrl(boolean check) {
if (check && !checkToken()) {
token = null;
}
return "http://192.168.31.1/cgi-bin/luci/;stok=" + getToken();
}
public String getDeviceListUrl() {
return getHomeUrl() + "/api/misystem/devicelist";
}
private String nonceCreat() {
return String.format("%s_%s_%s_%s", 0, "7c:83:34:bd:a3:9f", (int) (System.currentTimeMillis() / 1000), (int) (Math.random() * 10000));
}
private String getWan1StatusUrl() {
return getHomeUrl() + "/api/xqnetwork/get_wan_status";
}
private String getWan2StatusUrl() {
return getHomeUrl() + "/api/xqnetwork/pppoe_status?wan_name=WAN2";
}
public boolean checkToken() {
String url = getHomeUrl(false) + "/api/misystem/messages";
JSONObject json = JSON.parseObject(HttpTools.get(url));
return json.getInteger("code") == 0;
}
public String getWan1IPAddress() {
String url = getWan1StatusUrl();
JSONObject json = JSON.parseObject(HttpTools.get(url));
try {
return json.getJSONObject("ipv4").getJSONObject("ip").getString("address");
} catch (Exception e) {
e.printStackTrace();
return getIPv4();
}
}
public String getWan2IPAddress() {
String url = getWan2StatusUrl();
JSONObject json = JSON.parseObject(HttpTools.get(url));
try {
return json.getJSONObject("ip").getString("address");
} catch (Exception e) {
e.printStackTrace();
return getIPv4();
}
}
public String getIPv4() {
return HttpTools.get("https://4.ipw.cn/");
}
public static void main(String[] args) {
String url = XiaoMiRouter.getDeviceListUrl();
JSONObject json = JSON.parseObject(HttpTools.get(url));
System.out.println("json = " + json);
System.out.println(XiaoMiRouter.getInstance().getWan1IPAddress());
XiaoMiRouter.getInstance().clear();
System.out.println(XiaoMiRouter.getInstance().getWan2IPAddress());
}
public void clear() {
token="";
}
}

View File

@@ -0,0 +1,103 @@
package com.yutou.qqbot.utlis;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.yutou.okhttp.HttpDownloadUtils;
import com.yutou.okhttp.HttpLoggingInterceptor;
import com.yutou.okhttp.api.BaseApi;
import com.yutou.qqbot.interfaces.DownloadInterface;
import lombok.val;
import okhttp3.*;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
/**
* 张vv表情包生成器
*/
public class ZVVImageUtils {
// 单例实例
private static volatile ZVVImageUtils instance;
private boolean isLocalModel=false;
private String queryUrl="https://api.xy0v0.top/search?q=";
private String downloadUrl="https://cn-sy1.rains3.com/clouddisk/clouddisk/images/";
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.connectTimeout(2, TimeUnit.MINUTES)
.readTimeout(2, TimeUnit.MINUTES)
.build();
// 私有构造函数,防止外部实例化
private ZVVImageUtils() {
}
// 获取单例实例的方法
public static ZVVImageUtils getInstance() {
if (instance == null) {
synchronized (ZVVImageUtils.class) {
if (instance == null) {
instance = new ZVVImageUtils();
}
}
}
return instance;
}
private void checkModel(){
isLocalModel=ConfigTools.load(ConfigTools.CONFIG,"zvv.model.local", Boolean.class,false);
if(isLocalModel){
queryUrl="http://192.168.31.88:8501/search?q=";
downloadUrl="http://192.168.31.88:8501/clouddisk/clouddisk/images/";
}else{
String queryUrl="https://api.xy0v0.top/search?q=";
String downloadUrl="https://cn-sy1.rains3.com/clouddisk/clouddisk/images/";
}
}
public void zvv(String text, DownloadInterface downloadInterface) {
// checkModel();
JSONObject json = new JSONObject();
json.put("query", text);
json.put("amount", 1);
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=UTF-8"), json.toString().getBytes(StandardCharsets.UTF_8));
Request.Builder rb = new Request.Builder()
.post(body)
.addHeader("User-Agent", ConfigTools.getUserAgent())
.url(queryUrl + text + "&n=1");
Request request = rb.build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NotNull Call call, @NotNull IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
if (response.isSuccessful()) {
val string = response.body().string();
if (!StringUtils.isEmpty(string)) {
String imageUrl = JSONArray.parse(string).getString(0);
HttpDownloadUtils.download(new HttpDownloadUtils.Builder()
.setUrl(downloadUrl + imageUrl)
.setFileName(imageUrl)
.setPath(new File("tmp").getAbsolutePath())
.setDownloadInterface(downloadInterface)
);
}
}
}
});
}
public static void main(String[] args) {
HttpLoggingInterceptor.setLog(true);
ZVVImageUtils.getInstance().zvv("如何评论马督工", new DownloadInterface() {
@Override
public void onDownload(File file) {
super.onDownload(file);
System.out.println("file.getAbsolutePath() = " + file.getAbsolutePath());
}
});
}
}

View File

@@ -1,3 +1,6 @@
server.port=8002
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=100MB
spring.servlet.multipart.max-request-size=100MB
logging.config=classpath:log4j2.xml
logging.file.path=./logs
logging.level.com.log.controller = trace

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<!-- 控制台输出 -->
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5p [%thread] (%F:%L) : %m%n%throwable"/>
</Console>
<!-- 动态路由日志文件 -->
<Routing name="RoutingAppender">
<Routes pattern="$${date:yyyy-MM-dd}">
<Route>
<RollingFile name="Rolling-${date:yyyy-MM-dd}" fileName="logs/${date:yyyy-MM-dd}/system.log"
filePattern="logs/%d{yyyy-MM-dd}/system-%i.log.gz">
<PatternLayout>
<pattern>%d{yyyy-MM-dd'T'HH:mm:ss.SSSXXX} %-5p [%thread] (%F:%L) : %m%n%throwable</pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
<SizeBasedTriggeringPolicy size="1024 MB"/>
</Policies>
</RollingFile>
</Route>
</Routes>
</Routing>
</Appenders>
<Loggers>
<!-- 屏蔽 org.apache.hc.client5 包下的所有日志 -->
<Logger name="org.apache.hc.client5" level="OFF"/>
<!-- 根日志记录器 -->
<Root level="info">
<AppenderRef ref="ConsoleAppender"/>
<AppenderRef ref="RoutingAppender"/>
</Root>
</Loggers>
</Configuration>