diff --git a/docs/images/mock.png b/docs/images/mock.png new file mode 100644 index 0000000000000000000000000000000000000000..a73bb3307ec3dee9ca8c7a95a97765de3e77ba0e Binary files /dev/null and b/docs/images/mock.png differ diff --git a/pom.xml b/pom.xml index 36975c5d9fb292bdc201ad41e3e0f8377cfc0cd9..920214a7c5e8417a548b54b4ff8d916cdaebb8eb 100644 --- a/pom.xml +++ b/pom.xml @@ -10,6 +10,9 @@ pom xfg-dev-tech-app + xfg-dev-tech-infrastructure + xfg-dev-tech-domain + xfg-dev-tech-trigger @@ -45,6 +48,27 @@ guava 32.1.2-jre + + com.squareup.retrofit2 + converter-gson + 2.9.0 + + + cn.bugstack + chatglm-sdk-java + 1.0-SNAPSHOT + + + + org.apache.httpcomponents + httpclient + 4.5.14 + + + org.apache.httpcomponents + httpmime + 4.5.10 + diff --git a/xfg-dev-tech-app/pom.xml b/xfg-dev-tech-app/pom.xml index c04b35542da7b61cbaed06a0765a72bdf87e3c25..588a2962d356ada169aa70f86af8b3446b152f59 100644 --- a/xfg-dev-tech-app/pom.xml +++ b/xfg-dev-tech-app/pom.xml @@ -54,6 +54,21 @@ com.google.guava guava + + cn.bugstack + chatglm-sdk-java + + + + cn.bugstack + xfg-dev-tech-infrastructure + 1.0-SNAPSHOT + + + cn.bugstack + xfg-dev-tech-trigger + 1.0-SNAPSHOT + diff --git a/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfig.java b/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..4c93d43f986ff4c2f60c07cca2a890736ae35054 --- /dev/null +++ b/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfig.java @@ -0,0 +1,35 @@ +package cn.bugstack.xfg.dev.tech.config; + +import cn.bugstack.chatglm.session.OpenAiSession; +import cn.bugstack.chatglm.session.OpenAiSessionFactory; +import cn.bugstack.chatglm.session.defaults.DefaultOpenAiSessionFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description ChatGLMSDK + * @create 2023-10-22 10:00 + */ +@Configuration +@EnableConfigurationProperties(ChatGLMSDKConfigProperties.class) +public class ChatGLMSDKConfig { + + @Bean + @ConditionalOnProperty(value = "chatglm.sdk.config.enabled", havingValue = "true", matchIfMissing = false) + public OpenAiSession openAiSession(ChatGLMSDKConfigProperties properties) { + // 1. 配置文件 + cn.bugstack.chatglm.session.Configuration configuration = new cn.bugstack.chatglm.session.Configuration(); + configuration.setApiHost(properties.getApiHost()); + configuration.setApiSecretKey(properties.getApiSecretKey()); + + // 2. 会话工厂 + OpenAiSessionFactory factory = new DefaultOpenAiSessionFactory(configuration); + + // 3. 开启会话 + return factory.openSession(); + } + +} diff --git a/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfigProperties.java b/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfigProperties.java new file mode 100644 index 0000000000000000000000000000000000000000..ae57a3d9d53a197b3b9f39531e2c73fda0eff694 --- /dev/null +++ b/xfg-dev-tech-app/src/main/java/cn/bugstack/xfg/dev/tech/config/ChatGLMSDKConfigProperties.java @@ -0,0 +1,22 @@ +package cn.bugstack.xfg.dev.tech.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description ChatGLMSDK Config + * @create 2023-10-22 10:00 + */ +@Data +@ConfigurationProperties(prefix = "chatglm.sdk.config", ignoreInvalidFields = true) +public class ChatGLMSDKConfigProperties { + + /** 状态;open = 开启、close 关闭 */ + private boolean enable; + /** 转发地址 */ + private String apiHost; + /** 可以申请 sk-*** */ + private String apiSecretKey; + +} diff --git a/xfg-dev-tech-app/src/main/resources/application-dev.yml b/xfg-dev-tech-app/src/main/resources/application-dev.yml index a86d5e85f36103b28b364d0d2b4c237ce53c8bb8..3728d3ac8eb974f43809a9f824ae5c1abbc36514 100644 --- a/xfg-dev-tech-app/src/main/resources/application-dev.yml +++ b/xfg-dev-tech-app/src/main/resources/application-dev.yml @@ -2,6 +2,25 @@ server: port: 8091 application: name: xfg-dev-tech-mock + +# ChatGLM SDK Config +chatglm: + sdk: + config: + # 状态;true = 开启、false 关闭 + enabled: true + # 官网地址 + api-host: https://open.bigmodel.cn/ + # 官网申请 https://open.bigmodel.cn/usercenter/apikeys + api-secret-key: d570f7c5d289cdac2abdfdc562e39f3f.trqz1dH8ZK6ED7Pg + +# 知识星球配置信息;id -> 星球ID、user-id -> 用户ID、cookie -> 登录cookie +zsxq: + config: + id: 28885518425541 + user-id: 241858242255511 + cookie: zsxq_access_token=E0538FF2-B440-69E9-57D1-59EA37B9C0C6_9D76421394C6F474 + # 日志 logging: level: diff --git a/xfg-dev-tech-app/src/test/java/cn/bugstack/xfg/dev/tech/test/ApiTest.java b/xfg-dev-tech-app/src/test/java/cn/bugstack/xfg/dev/tech/test/ApiTest.java new file mode 100644 index 0000000000000000000000000000000000000000..20a429ea3821019a08733d7c667545f7e2655e61 --- /dev/null +++ b/xfg-dev-tech-app/src/test/java/cn/bugstack/xfg/dev/tech/test/ApiTest.java @@ -0,0 +1,32 @@ +package cn.bugstack.xfg.dev.tech.test; + +import cn.bugstack.xfg.dev.tech.domain.zsxq.service.IAiReply; +import lombok.extern.slf4j.Slf4j; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import javax.annotation.Resource; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 单元测试 + * @create 2023-10-22 09:08 + */ +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest +public class ApiTest { + + @Resource + private IAiReply aiReply; + + @Test + public void test_IAiReply() { + aiReply.doAiReply(); + + + } + +} diff --git a/xfg-dev-tech-domain/pom.xml b/xfg-dev-tech-domain/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..87d91881fa7c1ddac3bcd9e014df16ce5e6e0db3 --- /dev/null +++ b/xfg-dev-tech-domain/pom.xml @@ -0,0 +1,56 @@ + + + 4.0.0 + + cn.bugstack + xfg-dev-tech-mock + 1.0-SNAPSHOT + + + xfg-dev-tech-domain + + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + + + com.alibaba + fastjson + + + org.apache.commons + commons-lang3 + + + com.google.guava + guava + + + cn.bugstack + chatglm-sdk-java + + + + + xfg-dev-tech-domain + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + ${java.version} + + + + + + \ No newline at end of file diff --git a/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/adapter/IZSXQAdapter.java b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/adapter/IZSXQAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..34b4f2fef4b2ec9857e847039a9bb6d8baaef05a --- /dev/null +++ b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/adapter/IZSXQAdapter.java @@ -0,0 +1,18 @@ +package cn.bugstack.xfg.dev.tech.domain.zsxq.adapter; + +import cn.bugstack.xfg.dev.tech.domain.zsxq.model.vo.TopicsItemVO; + +import java.util.List; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 知识星球接口适配 + * @create 2023-10-22 10:11 + */ +public interface IZSXQAdapter { + + List queryTopics(); + + void comment(long topicId, String content); + +} diff --git a/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/model/vo/TopicsItemVO.java b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/model/vo/TopicsItemVO.java new file mode 100644 index 0000000000000000000000000000000000000000..de3a3b9ad809993ee7c8b3adb5612a55e234bad8 --- /dev/null +++ b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/model/vo/TopicsItemVO.java @@ -0,0 +1,35 @@ +package cn.bugstack.xfg.dev.tech.domain.zsxq.model.vo; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 话题 + * @create 2023-10-22 10:13 + */ +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class TopicsItemVO { + + private long topicId; + + private String talk; + + private List showCommentsItems; + + @Data + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class ShowCommentsItem { + private long userId; + } + +} diff --git a/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/AiReply.java b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/AiReply.java new file mode 100644 index 0000000000000000000000000000000000000000..74579b390c8890fcebeae5a663e898eed3707314 --- /dev/null +++ b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/AiReply.java @@ -0,0 +1,159 @@ +package cn.bugstack.xfg.dev.tech.domain.zsxq.service; + +import cn.bugstack.chatglm.model.*; +import cn.bugstack.chatglm.session.OpenAiSession; +import cn.bugstack.xfg.dev.tech.domain.zsxq.adapter.IZSXQAdapter; +import cn.bugstack.xfg.dev.tech.domain.zsxq.model.vo.TopicsItemVO; +import com.alibaba.fastjson.JSON; +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.extern.slf4j.Slf4j; +import okhttp3.sse.EventSource; +import okhttp3.sse.EventSourceListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.Nullable; +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 只能回帖 + * @create 2023-10-22 10:10 + */ +@Slf4j +@Service +public class AiReply implements IAiReply { + + @Autowired(required = false) + private OpenAiSession openAiSession; + @Resource + private IZSXQAdapter zsxqAdapter; + @Value("${zsxq.config.user-id}") + private Long userId; + + private final String regex = " (.*)"; + private volatile Set topicIds = new HashSet<>(); + + @Override + public void doAiReply() { + List topicsItemVOS = zsxqAdapter.queryTopics(); + + for (TopicsItemVO topicsItem : topicsItemVOS) { + // 是否回答过判断 + if (!isCommentDone(topicsItem)) continue; + // 找到圈我我帖子 + long topicId = topicsItem.getTopicId(); + String text = topicsItem.getTalk(); + + // " 提问 java 冒泡排序" + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(text); + + if (!matcher.find()) continue; + String uid = matcher.group(1); + String remainingText = matcher.group(3); + + if (this.userId.equals(Long.valueOf(uid))) { + + if (null == openAiSession) { + log.info("你没有开启 ChatGLM 参考yml配置文件来开启"); + // 你可以使用 ChatGLM SDK 进行回答,回复问题; + zsxqAdapter.comment(topicId, "【测试,只回答圈我的帖子】对接 ChatGLM SDK https://bugstack.cn/md/project/chatgpt/sdk/chatglm-sdk-java.html 回答:" + remainingText); + } else { + log.info("ChatGLM 进入回答 {} {}", topicId, remainingText); + if (topicIds.contains(topicId)) { + continue; + } else { + topicIds.add(topicId); + } + new Thread(() -> { + // 入参;模型、请求信息 + ChatCompletionRequest request = new ChatCompletionRequest(); + request.setModel(Model.CHATGLM_LITE); // chatGLM_6b_SSE、chatglm_lite、chatglm_lite_32k、chatglm_std、chatglm_pro + request.setPrompt(new ArrayList() { + private static final long serialVersionUID = -7988151926241837899L; + + { + add(ChatCompletionRequest.Prompt.builder() + .role(Role.user.getCode()) + .content(remainingText) + .build()); + } + }); + + // 请求 + try { + StringBuilder content = new StringBuilder(); + openAiSession.completions(request, new EventSourceListener() { + @Override + public void onEvent(EventSource eventSource, @Nullable String id, @Nullable String type, String data) { + ChatCompletionResponse chatCompletionResponse = com.alibaba.fastjson.JSON.parseObject(data, ChatCompletionResponse.class); + log.info("测试结果 onEvent:{}", chatCompletionResponse.getData()); + // type 消息类型,add 增量,finish 结束,error 错误,interrupted 中断 + if (EventType.finish.getCode().equals(type)) { + ChatCompletionResponse.Meta meta = com.alibaba.fastjson.JSON.parseObject(chatCompletionResponse.getMeta(), ChatCompletionResponse.Meta.class); + log.info("[输出结束] Tokens {}", com.alibaba.fastjson.JSON.toJSONString(meta)); + } + content.append(chatCompletionResponse.getData()); + } + + @Override + public void onClosed(EventSource eventSource) { + log.info("对话完成"); + + // 你可以使用 ChatGLM SDK 进行回答,回复问题; + String contents = "ChatGLM 回答:" + content; + int maxLength = 5000; + int contentLength = contents.length(); + int startIndex = 0; + int endIndex = maxLength; + + while (startIndex < contentLength) { + if (endIndex > contentLength) { + endIndex = contentLength; + } + + String subContent = contents.substring(startIndex, endIndex); + zsxqAdapter.comment(topicId, subContent); + + startIndex = endIndex; + endIndex += maxLength; + } + + topicIds.remove(topicId); + } + + }); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }).start(); + } + + } + + } + + log.info("AI回复:{}", JSON.toJSONString(topicsItemVOS)); + } + + private boolean isCommentDone(TopicsItemVO topicsItem) { + List showComments = topicsItem.getShowCommentsItems(); + if (null == showComments || showComments.isEmpty()) return true; + for (TopicsItemVO.ShowCommentsItem item : topicsItem.getShowCommentsItems()) { + long userId = item.getUserId(); + if (this.userId == userId) { + return false; + } + } + return true; + } + +} diff --git a/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/IAiReply.java b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/IAiReply.java new file mode 100644 index 0000000000000000000000000000000000000000..8f16237398d77dc04ce1aaa89ab52688cdcdf13a --- /dev/null +++ b/xfg-dev-tech-domain/src/main/java/cn/bugstack/xfg/dev/tech/domain/zsxq/service/IAiReply.java @@ -0,0 +1,12 @@ +package cn.bugstack.xfg.dev.tech.domain.zsxq.service; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 智能AI回帖 + * @create 2023-10-22 10:08 + */ +public interface IAiReply { + + void doAiReply(); + +} diff --git a/xfg-dev-tech-infrastructure/pom.xml b/xfg-dev-tech-infrastructure/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..bf0b4da224546c7869cca0d4effe694bb84501d5 --- /dev/null +++ b/xfg-dev-tech-infrastructure/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + cn.bugstack + xfg-dev-tech-mock + 1.0-SNAPSHOT + + + xfg-dev-tech-infrastructure + + + + org.springframework.boot + spring-boot-starter-web + + + org.projectlombok + lombok + + + com.squareup.retrofit2 + converter-gson + + + org.apache.httpcomponents + httpclient + + + org.apache.httpcomponents + httpmime + + + cn.bugstack + xfg-dev-tech-domain + 1.0-SNAPSHOT + + + + + xfg-dev-tech-infrastructure + + + \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/adapter/ZSXQAdapter.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/adapter/ZSXQAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..2a5e0ebc5723640fd6fe908715b673e19bcd3a2b --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/adapter/ZSXQAdapter.java @@ -0,0 +1,65 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.adapter; + +import cn.bugstack.xfg.dev.tech.domain.zsxq.adapter.IZSXQAdapter; +import cn.bugstack.xfg.dev.tech.domain.zsxq.model.vo.TopicsItemVO; +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.api.IZSXQApi; +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto.RespData; +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto.ResponseDTO; +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto.TopicsItem; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 知识星球适配器接口 + * @create 2023-10-22 10:18 + */ +@Slf4j +@Service +public class ZSXQAdapter implements IZSXQAdapter { + + @Resource + private IZSXQApi zsxqApi; + + @Override + public List queryTopics() { + try { + ResponseDTO responseDTO = zsxqApi.topics(); + RespData respData = responseDTO.getRespData(); + List topics = respData.getTopics(); + List topicsItemVOList = new ArrayList<>(); + + for (TopicsItem topicsItem : topics) { + TopicsItemVO topicsItemVO = TopicsItemVO.builder() + .topicId(topicsItem.getTopicId()) + .talk(topicsItem.getTalk().getText()) + .showCommentsItems(topicsItem.getShowComments() != null ? topicsItem.getShowComments().stream() + .map(showCommentsItem -> { + TopicsItemVO.ShowCommentsItem item = new TopicsItemVO.ShowCommentsItem(); + item.setUserId(showCommentsItem.getOwner().getUserId()); + return item; + }) + .collect(Collectors.toList()) : new ArrayList<>()) + .build(); + + topicsItemVOList.add(topicsItemVO); + } + + return topicsItemVOList; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void comment(long topicId, String content) { + zsxqApi.comment(topicId, content); + } + +} diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/IZSXQApi.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/IZSXQApi.java new file mode 100644 index 0000000000000000000000000000000000000000..663f1cabb936768aaf974597949c89c048cb01dc --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/IZSXQApi.java @@ -0,0 +1,30 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.api; + +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto.ResponseDTO; + +import java.io.IOException; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 知识星球API接口 + * @create 2023-10-22 09:47 + */ +public interface IZSXQApi { + + /** + * 查询知识星球帖子内容 + * + * @return 帖子数据 + * @throws IOException 异常 + */ + ResponseDTO topics() throws IOException; + + /** + * 回复帖子 + * + * @param topicId 帖子ID + * @param content 回复内容 + */ + void comment(long topicId, String content); + +} diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/impl/ZSXQApi.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/impl/ZSXQApi.java new file mode 100644 index 0000000000000000000000000000000000000000..26c1fd1213f96943feedfad93f3c5089349885f1 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/api/impl/ZSXQApi.java @@ -0,0 +1,112 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.api.impl; + +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.api.IZSXQApi; +import cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto.ResponseDTO; +import com.alibaba.fastjson2.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.HttpStatus; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.util.EntityUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.IOException; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 知识星球Api实现 + * @create 2023-10-22 09:53 + */ +@Slf4j +@Component +public class ZSXQApi implements IZSXQApi { + + @Value("${zsxq.config.id}") + private String id; + @Value("${zsxq.config.cookie}") + private String cookie; + + @Override + public ResponseDTO topics() throws IOException { + CloseableHttpClient httpClient = HttpClientBuilder.create().build(); + + HttpGet get = new HttpGet("https://api.zsxq.com/v2/groups/" + id + "/topics?scope=all&count=20"); + + get.addHeader("Accept-Encoding", "deflate, gzip"); + get.addHeader("accept", "application/json, text/plain, */*"); + get.addHeader("accept-language", "zh-CN,zh;q=0.9,en;q=0.8"); + get.addHeader("authority", "api.zsxq.com"); + get.addHeader("cookie", cookie); + get.addHeader("dnt", "1"); + get.addHeader("origin", "https://wx.zsxq.com"); + get.addHeader("referer", "https://wx.zsxq.com/"); + get.addHeader("sec-ch-ua", "\"Chromium\";v=\"118\", \"Google Chrome\";v=\"118\", \"Not=A?Brand\";v=\"99\""); + get.addHeader("sec-ch-ua-mobile", "?0"); + get.addHeader("sec-ch-ua-platform", "\"macOS\""); + get.addHeader("sec-fetch-dest", "empty"); + get.addHeader("sec-fetch-mode", "cors"); + get.addHeader("sec-fetch-site", "same-site"); + get.addHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"); + get.addHeader("x-request-id", "372177b46-4e7d-9373-d891-98a22adaeb7"); + get.addHeader("x-signature", "32b39b5d1af5995e3b5022e58a8d8f23cd427434"); + get.addHeader("x-timestamp", "1697249698"); + get.addHeader("x-version", "2.45.0"); + + CloseableHttpResponse response = httpClient.execute(get); + if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) { + String jsonStr = EntityUtils.toString(response.getEntity()); + return JSON.parseObject(jsonStr, ResponseDTO.class); + } else { + throw new RuntimeException("Err Code is " + response.getStatusLine().getStatusCode()); + } + } + + @Override + public void comment(long topicId, String content) { + CloseableHttpClient httpClient = HttpClientBuilder.create().build(); + + HttpPost httpPost = new HttpPost("https://api.zsxq.com/v2/topics/" + topicId + "/comments"); + + httpPost.setHeader("Accept-Encoding", "deflate, gzip"); + httpPost.setHeader("accept", "application/json, text/plain, */*"); + httpPost.setHeader("accept-language", "zh-CN,zh;q=0.9,en;q=0.8"); + httpPost.setHeader("authority", "api.zsxq.com"); + httpPost.setHeader("content-type", "application/json"); + httpPost.setHeader("cookie", cookie); + httpPost.setHeader("dnt", "1"); + httpPost.setHeader("origin", "https://wx.zsxq.com"); + httpPost.setHeader("referer", "https://wx.zsxq.com/"); + httpPost.setHeader("sec-ch-ua", "\"Chromium\";v=\"118\", \"Google Chrome\";v=\"118\", \"Not=A?Brand\";v=\"99\""); + httpPost.setHeader("sec-ch-ua-mobile", "?0"); + httpPost.setHeader("sec-ch-ua-platform", "\"macOS\""); + httpPost.setHeader("sec-fetch-dest", "empty"); + httpPost.setHeader("sec-fetch-mode", "cors"); + httpPost.setHeader("sec-fetch-site", "same-site"); + httpPost.setHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36"); + httpPost.setHeader("x-request-id", "162ae5f17-2123-4ae3-67df-8b9775414e0"); + httpPost.setHeader("x-signature", "698895e3ec4e651128b3d16755546bd2bc659687"); + httpPost.setHeader("x-timestamp", "1697257286"); + httpPost.setHeader("x-version", "2.45.0"); + + String requestBody = "{\"req_data\":{\"text\":\"" + StringEscapeUtils.escapeJava(content) + "\",\"image_ids\":[],\"mentioned_user_ids\":[]}}"; + try { + httpPost.setEntity(new StringEntity(requestBody)); + HttpResponse response = httpClient.execute(httpPost); + HttpEntity entity = response.getEntity(); + String responseString = EntityUtils.toString(entity); + log.info("回贴结果 {}", responseString); + httpClient.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Group.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Group.java new file mode 100644 index 0000000000000000000000000000000000000000..042bcf725fddb783c0624f34dd9e4add5ab174d0 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Group.java @@ -0,0 +1,20 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class Group{ + + @SerializedName("group_id") + private long groupId; + + @SerializedName("background_url") + private String backgroundUrl; + + @SerializedName("name") + private String name; + + @SerializedName("type") + private String type; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Owner.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Owner.java new file mode 100644 index 0000000000000000000000000000000000000000..8994d123b75722a5fcc339588712499cfc45c037 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Owner.java @@ -0,0 +1,20 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class Owner{ + + @SerializedName("avatar_url") + private String avatarUrl; + + @SerializedName("user_id") + private long userId; + + @SerializedName("name") + private String name; + + @SerializedName("location") + private String location; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/RespData.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/RespData.java new file mode 100644 index 0000000000000000000000000000000000000000..5420255c4800d64e362c17054c3f56dda27a23bd --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/RespData.java @@ -0,0 +1,13 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.List; + +@Data +public class RespData{ + + @SerializedName("topics") + private List topics; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ResponseDTO.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ResponseDTO.java new file mode 100644 index 0000000000000000000000000000000000000000..eea7a31413c0d1fdb950ffd3a151429e610e3932 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ResponseDTO.java @@ -0,0 +1,14 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class ResponseDTO { + + @SerializedName("resp_data") + private RespData respData; + + @SerializedName("succeeded") + private boolean succeeded; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ShowCommentsItem.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ShowCommentsItem.java new file mode 100644 index 0000000000000000000000000000000000000000..3fb8ad0ba1fb85d05a8948dd9cbfd5205532a18b --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/ShowCommentsItem.java @@ -0,0 +1,29 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class ShowCommentsItem{ + + @SerializedName("owner") + private Owner owner; + + @SerializedName("likes_count") + private int likesCount; + + @SerializedName("create_time") + private String createTime; + + @SerializedName("rewards_count") + private int rewardsCount; + + @SerializedName("sticky") + private boolean sticky; + + @SerializedName("text") + private String text; + + @SerializedName("comment_id") + private long commentId; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Talk.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Talk.java new file mode 100644 index 0000000000000000000000000000000000000000..c79c6dd407cbc03a30f874ce3d1332d9ad498e25 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/Talk.java @@ -0,0 +1,14 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class Talk{ + + @SerializedName("owner") + private Owner owner; + + @SerializedName("text") + private String text; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/TopicsItem.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/TopicsItem.java new file mode 100644 index 0000000000000000000000000000000000000000..9a2e41e3c4d6c4752009c8b3d21a71a196e4c73c --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/TopicsItem.java @@ -0,0 +1,52 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +import java.util.List; + +@Data +public class TopicsItem{ + + @SerializedName("reading_count") + private int readingCount; + + @SerializedName("create_time") + private String createTime; + + @SerializedName("user_specific") + private UserSpecific userSpecific; + + @SerializedName("rewards_count") + private int rewardsCount; + + @SerializedName("show_comments") + private List showComments; + + @SerializedName("type") + private String type; + + @SerializedName("digested") + private boolean digested; + + @SerializedName("likes_count") + private int likesCount; + + @SerializedName("comments_count") + private int commentsCount; + + @SerializedName("sticky") + private boolean sticky; + + @SerializedName("talk") + private Talk talk; + + @SerializedName("topic_id") + private long topicId; + + @SerializedName("readers_count") + private int readersCount; + + @SerializedName("group") + private Group group; +} \ No newline at end of file diff --git a/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/UserSpecific.java b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/UserSpecific.java new file mode 100644 index 0000000000000000000000000000000000000000..d5d92749fd3434d5b8101f816f4084b945a87bb3 --- /dev/null +++ b/xfg-dev-tech-infrastructure/src/main/java/cn/bugstack/xfg/dev/tech/infrastructure/gateway/dto/UserSpecific.java @@ -0,0 +1,14 @@ +package cn.bugstack.xfg.dev.tech.infrastructure.gateway.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.Data; + +@Data +public class UserSpecific{ + + @SerializedName("subscribed") + private boolean subscribed; + + @SerializedName("liked") + private boolean liked; +} \ No newline at end of file diff --git a/xfg-dev-tech-trigger/pom.xml b/xfg-dev-tech-trigger/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..838aefcc786eca9ae2d1e6f54d7014cccab7d7e6 --- /dev/null +++ b/xfg-dev-tech-trigger/pom.xml @@ -0,0 +1,63 @@ + + + 4.0.0 + + cn.bugstack + xfg-dev-tech-mock + 1.0-SNAPSHOT + + + xfg-dev-tech-trigger + + + + org.springframework.boot + spring-boot-starter-web + + + com.alibaba + fastjson + + + org.springframework + spring-tx + + + org.apache.commons + commons-lang3 + + + org.projectlombok + lombok + 1.18.26 + + + org.springframework + spring-context + + + cn.bugstack + xfg-dev-tech-domain + 1.0-SNAPSHOT + compile + + + + + xfg-dev-tech-trigger + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.version} + ${java.version} + ${java.version} + + + + + + \ No newline at end of file diff --git a/xfg-dev-tech-trigger/src/main/java/cn/bugstack/xfg/dev/tech/job/ReplyJob.java b/xfg-dev-tech-trigger/src/main/java/cn/bugstack/xfg/dev/tech/job/ReplyJob.java new file mode 100644 index 0000000000000000000000000000000000000000..1336057526f1660122da57f763566c20af80e1a5 --- /dev/null +++ b/xfg-dev-tech-trigger/src/main/java/cn/bugstack/xfg/dev/tech/job/ReplyJob.java @@ -0,0 +1,27 @@ +package cn.bugstack.xfg.dev.tech.job; + +import cn.bugstack.xfg.dev.tech.domain.zsxq.service.IAiReply; +import lombok.extern.slf4j.Slf4j; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * @author Fuzhengwei bugstack.cn @小傅哥 + * @description 回帖JOB + * @create 2023-10-22 11:02 + */ +@Slf4j +@Component() +public class ReplyJob { + + @Resource + private IAiReply aiReply; + + @Scheduled(cron = "0/10 * * * * ?") + public void exec() throws Exception { + aiReply.doAiReply(); + } + +}