From 5044a58118408c98367c7d3eaed440abac4accd5 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Fri, 5 Apr 2024 09:27:09 +0800 Subject: [PATCH] 增加mj web socket --- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/MidjourneyInteractionsEnum.java | 28 ---------------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjConstants.java | 8 ++++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjInteractionsEnum.java | 28 ++++++++++++++++++++++++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjMessageTypeEnum.java | 26 ++++++++++++++++++++++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjNotifyCode.java | 42 ++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/DiscordJadMain.java | 10 ---------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/DiscordWebSocketClient.java | 141 --------------------------------------------------------------------------------------------------------------------------------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MidjourneyApi.java | 19 ------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MjExecute.java | 19 ------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MjHttpExecute.java | 50 -------------------------------------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/interactions_type2.json | 48 ------------------------------------------------ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/interactions_type3.json | 14 -------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/Constants.java | 22 ---------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/DiscordAccount.java | 35 ----------------------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/DomainObject.java | 70 ---------------------------------------------------------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/AsyncLockUtils.java | 61 ------------------------------------------------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/DiscordHelper.java | 57 --------------------------------------------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/MessageType.java | 26 -------------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/ReturnCode.java | 42 ------------------------------------------ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/WebSocketStarter.java | 8 -------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/FailureCallback.java | 6 ------ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SpringUserWebSocketStarter.java | 189 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SpringWebSocketHandler.java | 240 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SuccessCallback.java | 7 ------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/UserMessageListener.java | 27 --------------------------- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjImagineInteractions.java | 4 ++-- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjInteractions.java | 4 ++-- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/package-info.java | 5 ----- yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/FailureCallback.java | 6 ++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/MjWebSocketStarter.java | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/SuccessCallback.java | 7 +++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/WebSocketStarter.java | 8 ++++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/handler/MjWebSocketHandler.java | 246 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/listener/MjMessageListener.java | 44 ++++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/interactions_type2.json | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/interactions_type3.json | 14 ++++++++++++++ 36 files changed, 647 insertions(+), 1128 deletions(-) delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/MidjourneyInteractionsEnum.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjConstants.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjInteractionsEnum.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjMessageTypeEnum.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjNotifyCode.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/DiscordJadMain.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/DiscordWebSocketClient.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MidjourneyApi.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MjExecute.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MjHttpExecute.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/interactions_type2.json delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/interactions_type3.json delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/Constants.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/DiscordAccount.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/DomainObject.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/AsyncLockUtils.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/DiscordHelper.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/MessageType.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/ReturnCode.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/WebSocketStarter.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/FailureCallback.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SpringUserWebSocketStarter.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SpringWebSocketHandler.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SuccessCallback.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/UserMessageListener.java delete mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/package-info.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/FailureCallback.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/MjWebSocketStarter.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/SuccessCallback.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/WebSocketStarter.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/handler/MjWebSocketHandler.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/listener/MjMessageListener.java create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/interactions_type2.json create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/interactions_type3.json diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/MidjourneyInteractionsEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/MidjourneyInteractionsEnum.java deleted file mode 100644 index 20b70b5..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/MidjourneyInteractionsEnum.java +++ /dev/null @@ -1,28 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney; - -import lombok.Getter; - -/** - * MJ 命令 - */ -@Getter -public enum MidjourneyInteractionsEnum { - - IMAGINE("imagine", "生成图片"), - DESCRIBE("describe", "生成描述"), - FAST("fast", "快速生成"), - SETTINGS("settings", "设置"), - ASK("ask", "提问"), - BLEND("blend", "融合"), - - ; - - MidjourneyInteractionsEnum(String value, String message) { - this.value =value; - this.message =message; - } - - private String value; - private String message; - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjConstants.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjConstants.java new file mode 100644 index 0000000..79b5b1b --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjConstants.java @@ -0,0 +1,8 @@ +package cn.iocoder.yudao.framework.ai.midjourney.constants; + +public final class MjConstants { + + public static final String CHANNEL_ID = "channel_id"; + + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjInteractionsEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjInteractionsEnum.java new file mode 100644 index 0000000..124bc83 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjInteractionsEnum.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.framework.ai.midjourney.constants; + +import lombok.Getter; + +/** + * MJ 命令 + */ +@Getter +public enum MjInteractionsEnum { + + IMAGINE("imagine", "生成图片"), + DESCRIBE("describe", "生成描述"), + FAST("fast", "快速生成"), + SETTINGS("settings", "设置"), + ASK("ask", "提问"), + BLEND("blend", "融合"), + + ; + + MjInteractionsEnum(String value, String message) { + this.value =value; + this.message =message; + } + + private String value; + private String message; + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjMessageTypeEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjMessageTypeEnum.java new file mode 100644 index 0000000..b47199e --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjMessageTypeEnum.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.framework.ai.midjourney.constants; + + +public enum MjMessageTypeEnum { + /** + * 创建. + */ + CREATE, + /** + * 修改. + */ + UPDATE, + /** + * 删除. + */ + DELETE; + + public static MjMessageTypeEnum of(String type) { + return switch (type) { + case "MESSAGE_CREATE" -> CREATE; + case "MESSAGE_UPDATE" -> UPDATE; + case "MESSAGE_DELETE" -> DELETE; + default -> null; + }; + } +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjNotifyCode.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjNotifyCode.java new file mode 100644 index 0000000..823d13f --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjNotifyCode.java @@ -0,0 +1,42 @@ +package cn.iocoder.yudao.framework.ai.midjourney.constants; + +import lombok.experimental.UtilityClass; + +@UtilityClass +public final class MjNotifyCode { + /** + * 成功. + */ + public static final int SUCCESS = 1; + /** + * 数据未找到. + */ + public static final int NOT_FOUND = 3; + /** + * 校验错误. + */ + public static final int VALIDATION_ERROR = 4; + /** + * 系统异常. + */ + public static final int FAILURE = 9; + + /** + * 已存在. + */ + public static final int EXISTED = 21; + /** + * 排队中. + */ + public static final int IN_QUEUE = 22; + /** + * 队列已满. + */ + public static final int QUEUE_REJECTED = 23; + /** + * prompt包含敏感词. + */ + public static final int BANNED_PROMPT = 24; + + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/DiscordJadMain.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/DiscordJadMain.java deleted file mode 100644 index d9c9dba..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/DiscordJadMain.java +++ /dev/null @@ -1,10 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo; - -/** - * author: fansili - * time: 2024/4/3 15:54 - */ -public class DiscordJadMain { - - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/DiscordWebSocketClient.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/DiscordWebSocketClient.java deleted file mode 100644 index df97050..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/DiscordWebSocketClient.java +++ /dev/null @@ -1,141 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo; -import cn.hutool.http.useragent.UserAgent; -import cn.hutool.http.useragent.UserAgentUtil; -import cn.hutool.json.JSONObject; -import com.alibaba.fastjson.JSON; -import org.springframework.web.socket.*; -import org.springframework.web.socket.client.standard.StandardWebSocketClient; -import org.springframework.web.socket.handler.TextWebSocketHandler; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -/** - * https://blog.csdn.net/qq_38490457/article/details/125250135 - */ -public class DiscordWebSocketClient { - - private static final String DISCORD_GATEWAY_URL = "wss://gateway.discord.gg/?v=9&encoding=json"; - - public static void main(String[] args) throws InterruptedException, ExecutionException, IOException, URISyntaxException { - StandardWebSocketClient client = new StandardWebSocketClient(); - DiscordWebSocketHandler handler = new DiscordWebSocketHandler(); - - - WebSocketHttpHeaders headers = new WebSocketHttpHeaders(); - headers.add("Accept-Encoding", "gzip, deflate, br"); - headers.add("Accept-Language", "zh-CN,zh;q=0.9"); - headers.add("Cache-Control", "no-cache"); - headers.add("Pragma", "no-cache"); - headers.add("Sec-Websocket-Extensions", "permessage-deflate; client_max_window_bits"); - headers.add("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"); - - Future futureSession = client.doHandshake(handler, headers, new URI(DISCORD_GATEWAY_URL)); - WebSocketSession session = futureSession.get(); // 这会阻塞直到连接建立 - - // 登录过程(你需要替换 TOKEN 为你的 Discord Bot Token) -// String token = "YOUR_DISCORD_BOT_TOKEN"; // 请替换为你的 Bot Token -// String identifyPayload = "{\"op\":2,\"d\":{\"token\":\"" + token + "\",\"properties\":{\"$os\":\"java\",\"$browser\":\"spring-websocket\",\"$device\":\"spring-websocket\"},\"compress\":false,\"large_threshold\":256,\"shard\":[0,1]}}"; -// session.sendMessage(new TextMessage(identifyPayload)); - - // 发送心跳以保持连接活跃 - Thread heartbeatThread = new Thread(() -> { - int interval = 0; // 初始心跳间隔,后续从 Discord 服务器获取 - while (!Thread.currentThread().isInterrupted()) { - try { - Thread.sleep(interval * 1000); // 等待指定的心跳间隔 - session.sendMessage(new TextMessage("{\"op\":1,\"d\":null}")); // 发送心跳包 - } catch (Exception e) { - e.printStackTrace(); - break; - } - } - }); - heartbeatThread.start(); - - // 等待用户输入来保持程序运行(仅用于示例) - System.in.read(); - - // 关闭连接和线程 - session.close(); - heartbeatThread.interrupt(); - } - - private static class DiscordWebSocketHandler implements WebSocketHandler { - - @Override - public void afterConnectionEstablished(WebSocketSession session) throws Exception { - - } - - @Override - public void handleMessage(WebSocketSession session, WebSocketMessage message) throws Exception { - Object payload = message.getPayload(); - - session.sendMessage(new TextMessage(JSON.toJSONString(createAuthData()))); - String a= ""; - } - - @Override - public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { - - } - - @Override - public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { - - } - - @Override - public boolean supportsPartialMessages() { - return false; - } - - - private JSONObject createAuthData() { - String userAgentStr = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"; - UserAgent userAgent = UserAgentUtil.parse(userAgentStr); - String token = "NTY5MDY4NDAxNzEyOTU1Mzky.G4-Fu0.MzD-7ll-ElbXTTgDPHF-WS_UyhMAfbKN3WyyBc"; - - JSONObject connectionProperties = new JSONObject() - .put("browser", userAgent.getBrowser().getName()) - .put("browser_user_agent", userAgentStr) - .put("browser_version", userAgent.getVersion()) - .put("client_build_number", 222963) - .put("client_event_source", null) - .put("device", "") - .put("os", userAgent.getOs().getName()) - .put("referer", "https://www.midjourney.com") - .put("referrer_current", "") - .put("referring_domain", "www.midjourney.com") - .put("referring_domain_current", "") - .put("release_channel", "stable") - .put("system_locale", "zh-CN"); - JSONObject presence = new JSONObject() - .put("activities", "") - .put("afk", false) - .put("since", 0) - .put("status", "online"); - JSONObject clientState = new JSONObject() - .put("api_code_version", 0) - .put("guild_versions", "") - .put("highest_last_message_id", "0") - .put("private_channels_version", "0") - .put("read_state_version", 0) - .put("user_guild_settings_version", -1) - .put("user_settings_version", -1); - return new JSONObject() - .put("capabilities", 16381) - .put("client_state", clientState) - .put("compress", false) - .put("presence", presence) - .put("properties", connectionProperties) - .put("token", token); - } - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MidjourneyApi.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MidjourneyApi.java deleted file mode 100644 index 1c0cbd4..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MidjourneyApi.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo; - -/** - * 文档: https://www.xiubbs.com/t-401-1-1.html - * - * https://github.com/novicezk/midjourney-proxy/blob/main/README_CN.md - * - * discord4j、https://github.com/discord-jda/JDA - * - */ -public class MidjourneyApi { - - -// https://discord.com/api/v9/interactions -// -// payload_json: {"type":2,"application_id":"936929561302675456","guild_id":"1224337694918971392","channel_id":"1224337694918971396","session_id":"696318caed5180a2210e358e44801449","data":{"version":"1166847114203123795","id":"938956540159881230","name":"imagine","type":1,"options":[{"type":3,"name":"prompt","value":"中国的是什么样子"}],"application_command":{"id":"938956540159881230","type":1,"application_id":"936929561302675456","version":"1166847114203123795","name":"imagine","description":"Create images with Midjourney","options":[{"type":3,"name":"prompt","description":"The prompt to imagine","required":true,"description_localized":"The prompt to imagine","name_localized":"prompt"}],"dm_permission":true,"integration_types":[0],"global_popularity_rank":1,"description_localized":"Create images with Midjourney","name_localized":"imagine"},"attachments":[]},"nonce":"1224342266261274624","analytics_location":"slash_ui"} -// - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MjExecute.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MjExecute.java deleted file mode 100644 index 299266c..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MjExecute.java +++ /dev/null @@ -1,19 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo; - -import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyInteractionsEnum; - -/** - * mj 命令 - */ -public interface MjExecute { - - /** - * 执行命令 - * - * @param mjCommand - * @param prompt - * @return - */ - boolean execute(MidjourneyInteractionsEnum mjCommand, String prompt); - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MjHttpExecute.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MjHttpExecute.java deleted file mode 100644 index 34cf4fd..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/MjHttpExecute.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo; - -import cn.hutool.core.io.FileUtil; -import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyInteractionsEnum; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; -import org.springframework.web.client.RestTemplate; - -import java.nio.charset.Charset; - -public class MjHttpExecute implements MjExecute { - - private static final String URL = "https://discord.com/"; - - @Override - public boolean execute(MidjourneyInteractionsEnum mjCommand, String prompt) { - // 发送的 uri - String uri = "api/v9/interactions"; - // restTemplate 发送post请求 -// String result = restTemplate.postForObject(URL + uri, prompt, String.class); - // 加载当前目录下文件 - return false; - } - - public static void main(String[] args) { - RestTemplate restTemplate = new RestTemplate(); - - String token = "NTY5MDY4NDAxNzEyOTU1Mzky.G4-Fu0.MzD-7ll-ElbXTTgDPHF-WS_UyhMAfbKN3WyyBc"; - String body = FileUtil.readString("/Users/fansili/projects/github/ruoyi-vue-pro/yudao-module-ai" + - "/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney" + - "/interactions_type2.json", Charset.forName("utf-8")); - // 创建HTTP头部 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); // 设置内容类型为JSON - headers.set("Authorization", token); // 如果需要,设置认证信息(例如JWT令牌) - headers.set("Referer", "https://discord.com/channels/1221445697157468200/1221445862962630706"); // 如果需要,设置认证信息(例如JWT令牌) - headers.set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"); // 如果需要,设置认证信息(例如JWT令牌) - headers.set("Cookie", "__dcfduid=6ca536c0e3fa11eeb7cbe34c31b49caf; __sdcfduid=6ca536c1e3fa11eeb7cbe34c31b49caf52cce5ffd8983d2a052cf6aba75fe5fe566f2c265902e283ce30dbf98b8c9c93; _gcl_au=1.1.245923998.1710853617; _ga=GA1.1.111061823.1710853617; __cfruid=6385bb3f48345a006b25992db7dcf984e395736d-1712124666; _cfuvid=O09la5ms0ypNptiG0iD8A6BKWlTxz1LG0WR7qRStD7o-1712124666575-0.0.1.1-604800000; locale=zh-CN; cf_clearance=l_YGod1_SUtYxpDVeZXiX7DLLPl1DYrquZe8WVltvYs-1712124668-1.0.1.1-Hl2.fToel23EpF2HCu9J20rB4D7OhhCzoajPSdo.9Up.wPxhvq22DP9RHzEBKuIUlKyH.kJLxXJfAt2N.LD5WQ; OptanonConsent=isIABGlobal=false&datestamp=Wed+Apr+03+2024+14%3A11%3A15+GMT%2B0800+(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4)&version=6.33.0&hosts=&landingPath=https%3A%2F%2Fdiscord.com%2F&groups=C0001%3A1%2CC0002%3A1%2CC0003%3A1; _ga_Q149DFWHT7=GS1.1.1712124668.4.1.1712124679.0.0.0"); // 如果需要,设置认证信息(例如JWT令牌) - // 封装请求体和头部信息 - HttpEntity requestEntity = new HttpEntity<>(body, headers); - - // 定义请求URL和返回类型 - String uri = "api/v9/interactions"; - String res = restTemplate.postForObject(URL + uri, requestEntity, String.class); - System.err.println("11"); - - - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/interactions_type2.json b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/interactions_type2.json deleted file mode 100644 index b8a2afe..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/interactions_type2.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "type": 2, - "application_id": "936929561302675456", - "guild_id": "1221445697157468200", - "channel_id": "1221445862962630706", - "session_id": "696318caed5180a2210e358e44801449", - "data": { - "version": "1166847114203123795", - "id": "938956540159881230", - "name": "imagine", - "type": 1, - "options": [ - { - "type": 3, - "name": "prompt", - "value": "童话世界应该是什么样?" - } - ], - "application_command": { - "id": "938956540159881230", - "type": 1, - "application_id": "936929561302675456", - "version": "1166847114203123795", - "name": "imagine", - "description": "Create images with Midjourney", - "options": [ - { - "type": 3, - "name": "prompt", - "description": "The prompt to imagine", - "required": true, - "description_localized": "The prompt to imagine", - "name_localized": "prompt" - } - ], - "dm_permission": true, - "integration_types": [ - 0 - ], - "global_popularity_rank": 1, - "description_localized": "Create images with Midjourney", - "name_localized": "imagine" - }, - "attachments": [] - }, - "nonce": "1224342266261274624", - "analytics_location": "slash_ui" -} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/interactions_type3.json b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/interactions_type3.json deleted file mode 100644 index 4d85c1c..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/interactions_type3.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "type": 3, - "nonce": "1224397626158350336", - "guild_id": "1224337694918971392", - "channel_id": "1224337694918971396", - "message_flags": 64, - "message_id": "1224397599197368330", - "application_id": "936929561302675456", - "session_id": "0d9ae3c799f2fb55ff435df5e6deb121", - "data": { - "component_type": 2, - "custom_id": "MJ::ModMsg::Ack::2744093999977583880" - } -} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/Constants.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/Constants.java deleted file mode 100644 index 42e1b2f..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/Constants.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.jad; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public final class Constants { - // 任务扩展属性 start - public static final String TASK_PROPERTY_NOTIFY_HOOK = "notifyHook"; - public static final String TASK_PROPERTY_FINAL_PROMPT = "finalPrompt"; - public static final String TASK_PROPERTY_MESSAGE_ID = "messageId"; - public static final String TASK_PROPERTY_MESSAGE_HASH = "messageHash"; - public static final String TASK_PROPERTY_PROGRESS_MESSAGE_ID = "progressMessageId"; - public static final String TASK_PROPERTY_FLAGS = "flags"; - public static final String TASK_PROPERTY_NONCE = "nonce"; - public static final String TASK_PROPERTY_DISCORD_INSTANCE_ID = "discordInstanceId"; - // 任务扩展属性 end - - public static final String API_SECRET_HEADER_NAME = "mj-api-secret"; - public static final String DEFAULT_DISCORD_USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36"; - - public static final String MJ_MESSAGE_HANDLED = "mj_proxy_handled"; -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/DiscordAccount.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/DiscordAccount.java deleted file mode 100644 index 8fafa9f..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/DiscordAccount.java +++ /dev/null @@ -1,35 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.jad; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.Data; -import lombok.EqualsAndHashCode; - -@Data -@EqualsAndHashCode(callSuper = true) - -public class DiscordAccount extends DomainObject { - - - private String guildId; - - private String channelId; - - private String userToken; - - private String userAgent = Constants.DEFAULT_DISCORD_USER_AGENT; - - - private boolean enable = true; - - - private int coreSize = 3; - - private int queueSize = 10; - - private int timeoutMinutes = 5; - - @JsonIgnore - public String getDisplay() { - return this.channelId; - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/DomainObject.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/DomainObject.java deleted file mode 100644 index e12339e..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/jad/DomainObject.java +++ /dev/null @@ -1,70 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.jad; - - -import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.Getter; -import lombok.Setter; - -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - - -public class DomainObject implements Serializable { - @Getter - @Setter - protected String id; - - @Setter - protected Map properties; // 扩展属性,仅支持基本类型 - - @JsonIgnore - private final transient Object lock = new Object(); - - public void sleep() throws InterruptedException { - synchronized (this.lock) { - this.lock.wait(); - } - } - - public void awake() { - synchronized (this.lock) { - this.lock.notifyAll(); - } - } - - public DomainObject setProperty(String name, Object value) { - getProperties().put(name, value); - return this; - } - - public DomainObject removeProperty(String name) { - getProperties().remove(name); - return this; - } - - public Object getProperty(String name) { - return getProperties().get(name); - } - - @SuppressWarnings("unchecked") - public T getPropertyGeneric(String name) { - return (T) getProperty(name); - } - - public T getProperty(String name, Class clz) { - return getProperty(name, clz, null); - } - - public T getProperty(String name, Class clz, T defaultValue) { - Object value = getProperty(name); - return value == null ? defaultValue : clz.cast(value); - } - - public Map getProperties() { - if (this.properties == null) { - this.properties = new HashMap<>(); - } - return this.properties; - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/AsyncLockUtils.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/AsyncLockUtils.java deleted file mode 100644 index 365935a..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/AsyncLockUtils.java +++ /dev/null @@ -1,61 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.wss; - -import cn.hutool.cache.CacheUtil; -import cn.hutool.cache.impl.TimedCache; -import cn.hutool.core.thread.ThreadUtil; -import cn.iocoder.yudao.framework.ai.midjourney.demo.jad.DomainObject; -import lombok.experimental.UtilityClass; - -import java.time.Duration; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -@UtilityClass -public class AsyncLockUtils { - private static final TimedCache LOCK_MAP = CacheUtil.newTimedCache(Duration.ofDays(1).toMillis()); - - public static synchronized LockObject getLock(String key) { - return LOCK_MAP.get(key); - } - - public static LockObject waitForLock(String key, Duration duration) throws TimeoutException { - LockObject lockObject; - synchronized (LOCK_MAP) { - if (LOCK_MAP.containsKey(key)) { - lockObject = LOCK_MAP.get(key); - } else { - lockObject = new LockObject(key); - LOCK_MAP.put(key, lockObject); - } - } - Future future = ThreadUtil.execAsync(() -> { - try { - lockObject.sleep(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - }); - try { - future.get(duration.toMillis(), TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } catch (ExecutionException e) { - // do nothing - } catch (TimeoutException e) { - future.cancel(true); - throw new TimeoutException("Wait Timeout"); - } finally { - LOCK_MAP.remove(lockObject.getId()); - } - return lockObject; - } - - public static class LockObject extends DomainObject { - - public LockObject(String id) { - this.id = id; - } - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/DiscordHelper.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/DiscordHelper.java deleted file mode 100644 index 7f57fea..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/DiscordHelper.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.wss; - -import cn.hutool.core.text.CharSequenceUtil; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - -@Component -@RequiredArgsConstructor -public class DiscordHelper { - /** - * DISCORD_SERVER_URL. - */ - public static final String DISCORD_SERVER_URL = "https://discord.com"; - /** - * DISCORD_CDN_URL. - */ - public static final String DISCORD_CDN_URL = "https://cdn.discordapp.com"; - /** - * DISCORD_WSS_URL. - */ - public static final String DISCORD_WSS_URL = "wss://gateway.discord.gg"; - /** - * DISCORD_UPLOAD_URL. - */ - public static final String DISCORD_UPLOAD_URL = "https://discord-attachments-uploads-prd.storage.googleapis.com"; - - public String getServer() { - return DISCORD_SERVER_URL; - } - - public String getCdn() { - return DISCORD_CDN_URL; - } - - public String getWss() { - return DISCORD_WSS_URL; - } - - public String getMessageHash(String imageUrl) { - if (CharSequenceUtil.isBlank(imageUrl)) { - return null; - } - if (CharSequenceUtil.endWith(imageUrl, "_grid_0.webp")) { - int hashStartIndex = imageUrl.lastIndexOf("/"); - if (hashStartIndex < 0) { - return null; - } - return CharSequenceUtil.sub(imageUrl, hashStartIndex + 1, imageUrl.length() - "_grid_0.webp".length()); - } - int hashStartIndex = imageUrl.lastIndexOf("_"); - if (hashStartIndex < 0) { - return null; - } - return CharSequenceUtil.subBefore(imageUrl.substring(hashStartIndex + 1), ".", true); - } - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/MessageType.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/MessageType.java deleted file mode 100644 index cc48fe9..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/MessageType.java +++ /dev/null @@ -1,26 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.wss; - - -public enum MessageType { - /** - * 创建. - */ - CREATE, - /** - * 修改. - */ - UPDATE, - /** - * 删除. - */ - DELETE; - - public static MessageType of(String type) { - return switch (type) { - case "MESSAGE_CREATE" -> CREATE; - case "MESSAGE_UPDATE" -> UPDATE; - case "MESSAGE_DELETE" -> DELETE; - default -> null; - }; - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/ReturnCode.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/ReturnCode.java deleted file mode 100644 index 0de5390..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/ReturnCode.java +++ /dev/null @@ -1,42 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.wss; - -import lombok.experimental.UtilityClass; - -@UtilityClass -public final class ReturnCode { - /** - * 成功. - */ - public static final int SUCCESS = 1; - /** - * 数据未找到. - */ - public static final int NOT_FOUND = 3; - /** - * 校验错误. - */ - public static final int VALIDATION_ERROR = 4; - /** - * 系统异常. - */ - public static final int FAILURE = 9; - - /** - * 已存在. - */ - public static final int EXISTED = 21; - /** - * 排队中. - */ - public static final int IN_QUEUE = 22; - /** - * 队列已满. - */ - public static final int QUEUE_REJECTED = 23; - /** - * prompt包含敏感词. - */ - public static final int BANNED_PROMPT = 24; - - -} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/WebSocketStarter.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/WebSocketStarter.java deleted file mode 100644 index 82874f2..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/WebSocketStarter.java +++ /dev/null @@ -1,8 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.wss; - - -public interface WebSocketStarter { - - void start() throws Exception; - -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/FailureCallback.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/FailureCallback.java deleted file mode 100644 index 9600dda..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/FailureCallback.java +++ /dev/null @@ -1,6 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.wss.user; - - -public interface FailureCallback { - void onFailure(int code, String reason); -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SpringUserWebSocketStarter.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SpringUserWebSocketStarter.java deleted file mode 100644 index 644a368..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SpringUserWebSocketStarter.java +++ /dev/null @@ -1,189 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.wss.user; - - -import cn.hutool.core.exceptions.ValidateException; -import cn.hutool.core.text.CharSequenceUtil; -import cn.hutool.core.thread.ThreadUtil; -import cn.iocoder.yudao.framework.ai.midjourney.demo.jad.DiscordAccount; -import cn.iocoder.yudao.framework.ai.midjourney.demo.wss.AsyncLockUtils; -import cn.iocoder.yudao.framework.ai.midjourney.demo.wss.ReturnCode; -import cn.iocoder.yudao.framework.ai.midjourney.demo.wss.WebSocketStarter; -import lombok.extern.slf4j.Slf4j; -import org.apache.tomcat.websocket.Constants; -import org.jetbrains.annotations.NotNull; -import org.springframework.util.concurrent.ListenableFutureCallback; -import org.springframework.web.socket.CloseStatus; -import org.springframework.web.socket.WebSocketHttpHeaders; -import org.springframework.web.socket.WebSocketSession; -import org.springframework.web.socket.client.standard.StandardWebSocketClient; - -import java.io.IOException; -import java.net.URI; -import java.time.Duration; -import java.util.concurrent.TimeoutException; - -@Slf4j -public class SpringUserWebSocketStarter implements WebSocketStarter { - private static final int CONNECT_RETRY_LIMIT = 5; - - private final DiscordAccount account; - private final UserMessageListener userMessageListener; - private final String wssServer; - private final String resumeWss; - - private boolean running = false; - - private WebSocketSession webSocketSession = null; - private ResumeData resumeData = null; - - public SpringUserWebSocketStarter(String wssServer, String resumeWss, DiscordAccount account, UserMessageListener userMessageListener) { - this.wssServer = wssServer; - this.resumeWss = resumeWss; - this.account = account; - this.userMessageListener = userMessageListener; - } - - @Override - public void start() throws Exception { - start(false); - } - - private void start(boolean reconnect) { - WebSocketHttpHeaders headers = new WebSocketHttpHeaders(); - headers.add("Accept-Encoding", "gzip, deflate, br"); - headers.add("Accept-Language", "zh-CN,zh;q=0.9"); - headers.add("Cache-Control", "no-cache"); - headers.add("Pragma", "no-cache"); - headers.add("Sec-Websocket-Extensions", "permessage-deflate; client_max_window_bits"); - headers.add("User-Agent", this.account.getUserAgent()); - var handler = new SpringWebSocketHandler(this.account, this.userMessageListener, this::onSocketSuccess, this::onSocketFailure); - String gatewayUrl; - if (reconnect) { - gatewayUrl = getGatewayServer(this.resumeData.resumeGatewayUrl()) + "/?encoding=json&v=9&compress=zlib-stream"; - handler.setSessionId(this.resumeData.sessionId()); - handler.setSequence(this.resumeData.sequence()); - handler.setResumeGatewayUrl(this.resumeData.resumeGatewayUrl()); - } else { - gatewayUrl = getGatewayServer(null) + "/?encoding=json&v=9&compress=zlib-stream"; - } - var webSocketClient = new StandardWebSocketClient(); - webSocketClient.getUserProperties().put(Constants.IO_TIMEOUT_MS_PROPERTY, "10000"); - var socketSessionFuture = webSocketClient.doHandshake(handler, headers, URI.create(gatewayUrl)); - socketSessionFuture.addCallback(new ListenableFutureCallback<>() { - @Override - public void onFailure(@NotNull Throwable e) { - onSocketFailure(SpringWebSocketHandler.CLOSE_CODE_EXCEPTION, e.getMessage()); - } - - @Override - public void onSuccess(WebSocketSession session) { - SpringUserWebSocketStarter.this.webSocketSession = session; - } - }); - } - - private void onSocketSuccess(String sessionId, Object sequence, String resumeGatewayUrl) { - this.resumeData = new ResumeData(sessionId, sequence, resumeGatewayUrl); - this.running = true; - notifyWssLock(ReturnCode.SUCCESS, ""); - } - - private void onSocketFailure(int code, String reason) { - if (code == 1001) { - return; - } - closeSocketSessionWhenIsOpen(); - if (!this.running) { - notifyWssLock(code, reason); - return; - } - this.running = false; - if (code >= 4000) { - log.warn("[wss-{}] Can't reconnect! Account disabled. Closed by {}({}).", this.account.getDisplay(), code, reason); - disableAccount(); - } else if (code == 2001) { - log.warn("[wss-{}] Closed by {}({}). Try reconnect...", this.account.getDisplay(), code, reason); - tryReconnect(); - } else { - log.warn("[wss-{}] Closed by {}({}). Try new connection...", this.account.getDisplay(), code, reason); - tryNewConnect(); - } - } - - private void tryReconnect() { - try { - tryStart(true); - } catch (Exception e) { - if (e instanceof TimeoutException) { - closeSocketSessionWhenIsOpen(); - } - log.warn("[wss-{}] Reconnect fail: {}, Try new connection...", this.account.getDisplay(), e.getMessage()); - ThreadUtil.sleep(1000); - tryNewConnect(); - } - } - - private void tryNewConnect() { - for (int i = 1; i <= CONNECT_RETRY_LIMIT; i++) { - try { - tryStart(false); - return; - } catch (Exception e) { - if (e instanceof TimeoutException) { - closeSocketSessionWhenIsOpen(); - } - log.warn("[wss-{}] New connect fail ({}): {}", this.account.getDisplay(), i, e.getMessage()); - ThreadUtil.sleep(5000); - } - } - log.error("[wss-{}] Account disabled", this.account.getDisplay()); - disableAccount(); - } - - public void tryStart(boolean reconnect) throws Exception { - start(reconnect); - AsyncLockUtils.LockObject lock = AsyncLockUtils.waitForLock("wss:" + this.account.getId(), Duration.ofSeconds(20)); - int code = lock.getProperty("code", Integer.class, 0); - if (code == ReturnCode.SUCCESS) { - log.debug("[wss-{}] {} success.", this.account.getDisplay(), reconnect ? "Reconnect" : "New connect"); - return; - } - throw new ValidateException(lock.getProperty("description", String.class)); - } - - private void notifyWssLock(int code, String reason) { - AsyncLockUtils.LockObject lock = AsyncLockUtils.getLock("wss:" + this.account.getId()); - if (lock != null) { - lock.setProperty("code", code); - lock.setProperty("description", reason); - lock.awake(); - } - } - - private void disableAccount() { - if (Boolean.FALSE.equals(this.account.isEnable())) { - return; - } - this.account.setEnable(false); - } - - private void closeSocketSessionWhenIsOpen() { - try { - if (this.webSocketSession != null && this.webSocketSession.isOpen()) { - this.webSocketSession.close(CloseStatus.GOING_AWAY); - } - } catch (IOException e) { - // do nothing - } - } - - private String getGatewayServer(String resumeGatewayUrl) { - if (CharSequenceUtil.isNotBlank(resumeGatewayUrl)) { - return CharSequenceUtil.isBlank(this.resumeWss) ? resumeGatewayUrl : this.resumeWss; - } - return this.wssServer; - } - - public record ResumeData(String sessionId, Object sequence, String resumeGatewayUrl) { - } -} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SpringWebSocketHandler.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SpringWebSocketHandler.java deleted file mode 100644 index b9d7778..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SpringWebSocketHandler.java +++ /dev/null @@ -1,240 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.wss.user; - -import cn.hutool.core.text.CharSequenceUtil; -import cn.hutool.core.thread.ThreadUtil; -import cn.hutool.core.util.RandomUtil; -import cn.hutool.http.useragent.UserAgent; -import cn.hutool.http.useragent.UserAgentUtil; -import cn.iocoder.yudao.framework.ai.midjourney.demo.jad.DiscordAccount; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import net.dv8tion.jda.api.utils.data.DataArray; -import net.dv8tion.jda.api.utils.data.DataObject; -import net.dv8tion.jda.api.utils.data.DataType; -import net.dv8tion.jda.internal.requests.WebSocketCode; -import net.dv8tion.jda.internal.utils.compress.Decompressor; -import net.dv8tion.jda.internal.utils.compress.ZlibDecompressor; -import org.jetbrains.annotations.NotNull; -import org.springframework.web.socket.*; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -@Slf4j -public class SpringWebSocketHandler implements WebSocketHandler { - public static final int CLOSE_CODE_RECONNECT = 2001; - public static final int CLOSE_CODE_INVALIDATE = 1009; - public static final int CLOSE_CODE_EXCEPTION = 1011; - - private final DiscordAccount account; - private final UserMessageListener userMessageListener; - private final SuccessCallback successCallback; - private final FailureCallback failureCallback; - - private final ScheduledExecutorService heartExecutor; - private final DataObject authData; - - @Setter - private String sessionId = null; - @Setter - private Object sequence = null; - @Setter - private String resumeGatewayUrl = null; - - private long interval = 41250; - private boolean heartbeatAck = false; - - private Future heartbeatInterval; - private Future heartbeatTimeout; - - private final Decompressor decompressor = new ZlibDecompressor(2048); - - public SpringWebSocketHandler(DiscordAccount account, UserMessageListener userMessageListener, SuccessCallback successCallback, FailureCallback failureCallback) { - this.account = account; - this.userMessageListener = userMessageListener; - this.successCallback = successCallback; - this.failureCallback = failureCallback; - this.heartExecutor = Executors.newSingleThreadScheduledExecutor(); - this.authData = createAuthData(); - } - - @Override - public void afterConnectionEstablished(@NotNull WebSocketSession session) throws Exception { - // do nothing - } - - @Override - public void handleTransportError(@NotNull WebSocketSession session, @NotNull Throwable e) throws Exception { - log.error("[wss-{}] Transport error", this.account.getDisplay(), e); - onFailure(CLOSE_CODE_EXCEPTION, "transport error"); - } - - @Override - public void afterConnectionClosed(@NotNull WebSocketSession session, @NotNull CloseStatus closeStatus) throws Exception { - onFailure(closeStatus.getCode(), closeStatus.getReason()); - } - - @Override - public boolean supportsPartialMessages() { - return true; - } - - @Override - public void handleMessage(@NotNull WebSocketSession session, WebSocketMessage message) throws Exception { - ByteBuffer buffer = (ByteBuffer) message.getPayload(); - byte[] decompressed = decompressor.decompress(buffer.array()); - if (decompressed == null) { - return; - } - String json = new String(decompressed, StandardCharsets.UTF_8); - DataObject data = DataObject.fromJson(json); - int opCode = data.getInt("op"); - switch (opCode) { - case WebSocketCode.HEARTBEAT -> handleHeartbeat(session); - case WebSocketCode.HEARTBEAT_ACK -> { - this.heartbeatAck = true; - clearHeartbeatTimeout(); - } - case WebSocketCode.HELLO -> { - handleHello(session, data); - doResumeOrIdentify(session); - } - case WebSocketCode.RESUME -> onSuccess(); - case WebSocketCode.RECONNECT -> onFailure(CLOSE_CODE_RECONNECT, "receive server reconnect"); - case WebSocketCode.INVALIDATE_SESSION -> onFailure(CLOSE_CODE_INVALIDATE, "receive session invalid"); - case WebSocketCode.DISPATCH -> handleDispatch(data); - default -> log.debug("[wss-{}] Receive unknown code: {}.", account.getDisplay(), data); - } - } - - private void handleDispatch(DataObject raw) { - this.sequence = raw.opt("s").orElse(null); - if (!raw.isType("d", DataType.OBJECT)) { - return; - } - DataObject content = raw.getObject("d"); - String t = raw.getString("t", null); - if ("READY".equals(t)) { - this.sessionId = content.getString("session_id"); - this.resumeGatewayUrl = content.getString("resume_gateway_url"); - onSuccess(); - } else if ("RESUMED".equals(t)) { - onSuccess(); - } else { - try { - this.userMessageListener.onMessage(raw); - } catch (Exception e) { - log.error("[wss-{}] Handle message error", this.account.getDisplay(), e); - } - } - } - - private void handleHeartbeat(WebSocketSession session) { - sendMessage(session, WebSocketCode.HEARTBEAT, this.sequence); - this.heartbeatTimeout = ThreadUtil.execAsync(() -> { - ThreadUtil.sleep(this.interval); - onFailure(CLOSE_CODE_RECONNECT, "heartbeat has not ack"); - }); - } - - private void handleHello(WebSocketSession session, DataObject data) { - clearHeartbeatInterval(); - this.interval = data.getObject("d").getLong("heartbeat_interval"); - this.heartbeatAck = true; - this.heartbeatInterval = this.heartExecutor.scheduleAtFixedRate(() -> { - if (this.heartbeatAck) { - this.heartbeatAck = false; - sendMessage(session, WebSocketCode.HEARTBEAT, this.sequence); - } else { - onFailure(CLOSE_CODE_RECONNECT, "heartbeat has not ack interval"); - } - }, (long) Math.floor(RandomUtil.randomDouble(0, 1) * this.interval), this.interval, TimeUnit.MILLISECONDS); - } - - private void doResumeOrIdentify(WebSocketSession session) { - if (CharSequenceUtil.isBlank(this.sessionId)) { - sendMessage(session, WebSocketCode.IDENTIFY, this.authData); - } else { - var data = DataObject.empty().put("token", this.account.getUserToken()) - .put("session_id", this.sessionId).put("seq", this.sequence); - sendMessage(session, WebSocketCode.RESUME, data); - } - } - - private void sendMessage(WebSocketSession session, int op, Object d) { - var data = DataObject.empty().put("op", op).put("d", d); - try { - session.sendMessage(new TextMessage(data.toString())); - } catch (IOException e) { - log.error("[wss-{}] Send message error", this.account.getDisplay(), e); - onFailure(CLOSE_CODE_EXCEPTION, "send message error"); - } - } - - private void onSuccess() { - ThreadUtil.execute(() -> this.successCallback.onSuccess(this.sessionId, this.sequence, this.resumeGatewayUrl)); - } - - private void onFailure(int code, String reason) { - clearHeartbeatTimeout(); - clearHeartbeatInterval(); - ThreadUtil.execute(() -> this.failureCallback.onFailure(code, reason)); - } - - private void clearHeartbeatTimeout() { - if (this.heartbeatTimeout != null) { - this.heartbeatTimeout.cancel(true); - this.heartbeatTimeout = null; - } - } - - private void clearHeartbeatInterval() { - if (this.heartbeatInterval != null) { - this.heartbeatInterval.cancel(true); - this.heartbeatInterval = null; - } - } - - private DataObject createAuthData() { - UserAgent userAgent = UserAgentUtil.parse(this.account.getUserAgent()); - DataObject connectionProperties = DataObject.empty() - .put("browser", userAgent.getBrowser().getName()) - .put("browser_user_agent", this.account.getUserAgent()) - .put("browser_version", userAgent.getVersion()) - .put("client_build_number", 222963) - .put("client_event_source", null) - .put("device", "") - .put("os", userAgent.getOs().getName()) - .put("referer", "https://www.midjourney.com") - .put("referrer_current", "") - .put("referring_domain", "www.midjourney.com") - .put("referring_domain_current", "") - .put("release_channel", "stable") - .put("system_locale", "zh-CN"); - DataObject presence = DataObject.empty() - .put("activities", DataArray.empty()) - .put("afk", false) - .put("since", 0) - .put("status", "online"); - DataObject clientState = DataObject.empty() - .put("api_code_version", 0) - .put("guild_versions", DataObject.empty()) - .put("highest_last_message_id", "0") - .put("private_channels_version", "0") - .put("read_state_version", 0) - .put("user_guild_settings_version", -1) - .put("user_settings_version", -1); - return DataObject.empty() - .put("capabilities", 16381) - .put("client_state", clientState) - .put("compress", false) - .put("presence", presence) - .put("properties", connectionProperties) - .put("token", this.account.getUserToken()); - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SuccessCallback.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SuccessCallback.java deleted file mode 100644 index 7ff2bd3..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/SuccessCallback.java +++ /dev/null @@ -1,7 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.wss.user; - - -public interface SuccessCallback { - - void onSuccess(String sessionId, Object sequence, String resumeGatewayUrl); -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/UserMessageListener.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/UserMessageListener.java deleted file mode 100644 index ca97b6c..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/demo/wss/user/UserMessageListener.java +++ /dev/null @@ -1,27 +0,0 @@ -package cn.iocoder.yudao.framework.ai.midjourney.demo.wss.user; - - -import cn.hutool.core.thread.ThreadUtil; -import cn.iocoder.yudao.framework.ai.midjourney.demo.wss.MessageType; -import lombok.extern.slf4j.Slf4j; -import net.dv8tion.jda.api.utils.data.DataObject; - -@Slf4j -public class UserMessageListener { - - public void onMessage(DataObject raw) { - MessageType messageType = MessageType.of(raw.getString("t")); - if (messageType == null || MessageType.DELETE == messageType) { - return; - } - DataObject data = raw.getObject("d"); - System.err.println(data); - ThreadUtil.sleep(50); -// for (MessageHandler messageHandler : this.messageHandlers) { -// if (data.getBoolean(Constants.MJ_MESSAGE_HANDLED, false)) { -// return; -// } -// messageHandler.handle(this.instance, messageType, data); -// } - } -} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjImagineInteractions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjImagineInteractions.java index 5768cbe..5e49d67 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjImagineInteractions.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjImagineInteractions.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.ai.midjourney.interactions; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; -import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyInteractionsEnum; +import cn.iocoder.yudao.framework.ai.midjourney.constants.MjInteractionsEnum; import com.google.common.collect.Maps; import lombok.extern.slf4j.Slf4j; @@ -27,7 +27,7 @@ public class MjImagineInteractions implements MjInteractions { } @Override - public List supperInteractions() { + public List supperInteractions() { return null; } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjInteractions.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjInteractions.java index 9b94abc..38f58f7 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjInteractions.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/interactions/MjInteractions.java @@ -1,6 +1,6 @@ package cn.iocoder.yudao.framework.ai.midjourney.interactions; -import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyInteractionsEnum; +import cn.iocoder.yudao.framework.ai.midjourney.constants.MjInteractionsEnum; import java.util.List; @@ -12,7 +12,7 @@ import java.util.List; */ public interface MjInteractions { - List supperInteractions(); + List supperInteractions(); Boolean execute(String prompt); } diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/package-info.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/package-info.java deleted file mode 100644 index 2ab1129..0000000 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * author: fansili - * time: 2024/4/3 17:08 - */ -package cn.iocoder.yudao.framework.ai.midjourney; \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/FailureCallback.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/FailureCallback.java new file mode 100644 index 0000000..29bdb06 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/FailureCallback.java @@ -0,0 +1,6 @@ +package cn.iocoder.yudao.framework.ai.midjourney.webSocket; + + +public interface FailureCallback { + void onFailure(int code, String reason); +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/MjWebSocketStarter.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/MjWebSocketStarter.java new file mode 100644 index 0000000..e6dcafc --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/MjWebSocketStarter.java @@ -0,0 +1,166 @@ +package cn.iocoder.yudao.framework.ai.midjourney.webSocket; + + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.thread.ThreadUtil; +import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; +import cn.iocoder.yudao.framework.ai.midjourney.constants.MjNotifyCode; +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.handler.MjWebSocketHandler; +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener.MjMessageListener; +import lombok.extern.slf4j.Slf4j; +import org.apache.tomcat.websocket.Constants; +import org.jetbrains.annotations.NotNull; +import org.springframework.util.concurrent.ListenableFutureCallback; +import org.springframework.web.socket.CloseStatus; +import org.springframework.web.socket.WebSocketHttpHeaders; +import org.springframework.web.socket.WebSocketSession; +import org.springframework.web.socket.client.standard.StandardWebSocketClient; + +import java.io.IOException; +import java.net.URI; +import java.util.concurrent.TimeoutException; + +@Slf4j +public class MjWebSocketStarter implements WebSocketStarter { + private static final int CONNECT_RETRY_LIMIT = 5; + + private final MidjourneyConfig midjourneyConfig; + private final MjMessageListener userMessageListener; + private final String wssServer; + private final String resumeWss; + + private boolean running = false; + + private WebSocketSession webSocketSession = null; + private ResumeData resumeData = null; + + public MjWebSocketStarter(String wssServer, String resumeWss, MidjourneyConfig midjourneyConfig, MjMessageListener userMessageListener) { + this.wssServer = wssServer; + this.resumeWss = resumeWss; + this.midjourneyConfig = midjourneyConfig; + this.userMessageListener = userMessageListener; + } + + @Override + public void start() throws Exception { + start(false); + } + + private void start(boolean reconnect) { + WebSocketHttpHeaders headers = new WebSocketHttpHeaders(); + headers.add("Accept-Encoding", "gzip, deflate, br"); + headers.add("Accept-Language", "zh-CN,zh;q=0.9"); + headers.add("Cache-Control", "no-cache"); + headers.add("Pragma", "no-cache"); + headers.add("Sec-Websocket-Extensions", "permessage-deflate; client_max_window_bits"); + headers.add("User-Agent", this.midjourneyConfig.getUserAage()); + var handler = new MjWebSocketHandler(this.midjourneyConfig, this.userMessageListener, this::onSocketSuccess, this::onSocketFailure); + String gatewayUrl; + if (reconnect) { + gatewayUrl = getGatewayServer(this.resumeData.resumeGatewayUrl()) + "/?encoding=json&v=9&compress=zlib-stream"; + handler.setSessionId(this.resumeData.sessionId()); + handler.setSequence(this.resumeData.sequence()); + handler.setResumeGatewayUrl(this.resumeData.resumeGatewayUrl()); + } else { + gatewayUrl = getGatewayServer(null) + "/?encoding=json&v=9&compress=zlib-stream"; + } + var webSocketClient = new StandardWebSocketClient(); + webSocketClient.getUserProperties().put(Constants.IO_TIMEOUT_MS_PROPERTY, "10000"); + var socketSessionFuture = webSocketClient.doHandshake(handler, headers, URI.create(gatewayUrl)); + socketSessionFuture.addCallback(new ListenableFutureCallback<>() { + @Override + public void onFailure(@NotNull Throwable e) { + onSocketFailure(MjWebSocketHandler.CLOSE_CODE_EXCEPTION, e.getMessage()); + } + + @Override + public void onSuccess(WebSocketSession session) { + MjWebSocketStarter.this.webSocketSession = session; + } + }); + } + + private void onSocketSuccess(String sessionId, Object sequence, String resumeGatewayUrl) { + this.resumeData = new ResumeData(sessionId, sequence, resumeGatewayUrl); + this.running = true; + notifyWssLock(MjNotifyCode.SUCCESS, ""); + } + + private void onSocketFailure(int code, String reason) { + if (code == 1001) { + return; + } + closeSocketSessionWhenIsOpen(); + if (!this.running) { + notifyWssLock(code, reason); + return; + } + this.running = false; + if (code >= 4000) { + log.warn("[wss-{}] Can't reconnect! Account disabled. Closed by {}({}).", this.midjourneyConfig.getChannelId(), code, reason); + } else if (code == 2001) { + log.warn("[wss-{}] Closed by {}({}). Try reconnect...", this.midjourneyConfig.getChannelId(), code, reason); + tryReconnect(); + } else { + log.warn("[wss-{}] Closed by {}({}). Try new connection...", this.midjourneyConfig.getChannelId(), code, reason); + tryNewConnect(); + } + } + + private void tryReconnect() { + try { + tryStart(true); + } catch (Exception e) { + if (e instanceof TimeoutException) { + closeSocketSessionWhenIsOpen(); + } + log.warn("[wss-{}] Reconnect fail: {}, Try new connection...", this.midjourneyConfig.getChannelId(), e.getMessage()); + ThreadUtil.sleep(1000); + tryNewConnect(); + } + } + + private void tryNewConnect() { + for (int i = 1; i <= CONNECT_RETRY_LIMIT; i++) { + try { + tryStart(false); + return; + } catch (Exception e) { + if (e instanceof TimeoutException) { + closeSocketSessionWhenIsOpen(); + } + log.warn("[wss-{}] New connect fail ({}): {}", this.midjourneyConfig.getChannelId(), i, e.getMessage()); + ThreadUtil.sleep(5000); + } + } + log.error("[wss-{}] Account disabled", this.midjourneyConfig.getChannelId()); + } + + public void tryStart(boolean reconnect) throws Exception { + start(reconnect); + } + + private void notifyWssLock(int code, String reason) { + System.err.println("notifyWssLock: " + code + " - " + reason); + } + + private void closeSocketSessionWhenIsOpen() { + try { + if (this.webSocketSession != null && this.webSocketSession.isOpen()) { + this.webSocketSession.close(CloseStatus.GOING_AWAY); + } + } catch (IOException e) { + // do nothing + } + } + + private String getGatewayServer(String resumeGatewayUrl) { + if (CharSequenceUtil.isNotBlank(resumeGatewayUrl)) { + return CharSequenceUtil.isBlank(this.resumeWss) ? resumeGatewayUrl : this.resumeWss; + } + return this.wssServer; + } + + public record ResumeData(String sessionId, Object sequence, String resumeGatewayUrl) { + } +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/SuccessCallback.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/SuccessCallback.java new file mode 100644 index 0000000..729498f --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/SuccessCallback.java @@ -0,0 +1,7 @@ +package cn.iocoder.yudao.framework.ai.midjourney.webSocket; + + +public interface SuccessCallback { + + void onSuccess(String sessionId, Object sequence, String resumeGatewayUrl); +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/WebSocketStarter.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/WebSocketStarter.java new file mode 100644 index 0000000..88e2832 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/WebSocketStarter.java @@ -0,0 +1,8 @@ +package cn.iocoder.yudao.framework.ai.midjourney.webSocket; + + +public interface WebSocketStarter { + + void start() throws Exception; + +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/handler/MjWebSocketHandler.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/handler/MjWebSocketHandler.java new file mode 100644 index 0000000..481fa1b --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/handler/MjWebSocketHandler.java @@ -0,0 +1,246 @@ +package cn.iocoder.yudao.framework.ai.midjourney.webSocket.handler; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.http.useragent.UserAgent; +import cn.hutool.http.useragent.UserAgentUtil; +import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.FailureCallback; +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.SuccessCallback; +import cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener.MjMessageListener; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.utils.data.DataArray; +import net.dv8tion.jda.api.utils.data.DataObject; +import net.dv8tion.jda.api.utils.data.DataType; +import net.dv8tion.jda.internal.requests.WebSocketCode; +import net.dv8tion.jda.internal.utils.compress.Decompressor; +import net.dv8tion.jda.internal.utils.compress.ZlibDecompressor; +import org.jetbrains.annotations.NotNull; +import org.springframework.web.socket.*; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +@Slf4j +public class MjWebSocketHandler implements WebSocketHandler { + public static final int CLOSE_CODE_RECONNECT = 2001; + public static final int CLOSE_CODE_INVALIDATE = 1009; + public static final int CLOSE_CODE_EXCEPTION = 1011; + + private final MidjourneyConfig midjourneyConfig; + private final MjMessageListener userMessageListener; + private final SuccessCallback successCallback; + private final FailureCallback failureCallback; + + private final ScheduledExecutorService heartExecutor; + private final DataObject authData; + + @Setter + private String sessionId = null; + @Setter + private Object sequence = null; + @Setter + private String resumeGatewayUrl = null; + + private long interval = 41250; + private boolean heartbeatAck = false; + + private Future heartbeatInterval; + private Future heartbeatTimeout; + + private final Decompressor decompressor = new ZlibDecompressor(2048); + + public MjWebSocketHandler(MidjourneyConfig account, + MjMessageListener userMessageListener, + SuccessCallback successCallback, + FailureCallback failureCallback) { + this.midjourneyConfig = account; + this.userMessageListener = userMessageListener; + this.successCallback = successCallback; + this.failureCallback = failureCallback; + this.heartExecutor = Executors.newSingleThreadScheduledExecutor(); + this.authData = createAuthData(); + } + + @Override + public void afterConnectionEstablished(@NotNull WebSocketSession session) throws Exception { + // do nothing + } + + @Override + public void handleTransportError(@NotNull WebSocketSession session, @NotNull Throwable e) throws Exception { + log.error("[wss-{}] Transport error", this.midjourneyConfig.getChannelId(), e); + onFailure(CLOSE_CODE_EXCEPTION, "transport error"); + } + + @Override + public void afterConnectionClosed(@NotNull WebSocketSession session, @NotNull CloseStatus closeStatus) throws Exception { + onFailure(closeStatus.getCode(), closeStatus.getReason()); + } + + @Override + public boolean supportsPartialMessages() { + return true; + } + + @Override + public void handleMessage(@NotNull WebSocketSession session, WebSocketMessage message) throws Exception { + ByteBuffer buffer = (ByteBuffer) message.getPayload(); + byte[] decompressed = decompressor.decompress(buffer.array()); + if (decompressed == null) { + return; + } + String json = new String(decompressed, StandardCharsets.UTF_8); + DataObject data = DataObject.fromJson(json); + int opCode = data.getInt("op"); + switch (opCode) { + case WebSocketCode.HEARTBEAT -> handleHeartbeat(session); + case WebSocketCode.HEARTBEAT_ACK -> { + this.heartbeatAck = true; + clearHeartbeatTimeout(); + } + case WebSocketCode.HELLO -> { + handleHello(session, data); + doResumeOrIdentify(session); + } + case WebSocketCode.RESUME -> onSuccess(); + case WebSocketCode.RECONNECT -> onFailure(CLOSE_CODE_RECONNECT, "receive server reconnect"); + case WebSocketCode.INVALIDATE_SESSION -> onFailure(CLOSE_CODE_INVALIDATE, "receive session invalid"); + case WebSocketCode.DISPATCH -> handleDispatch(data); + default -> log.debug("[wss-{}] Receive unknown code: {}.", midjourneyConfig.getChannelId(), data); + } + } + + private void handleDispatch(DataObject raw) { + this.sequence = raw.opt("s").orElse(null); + if (!raw.isType("d", DataType.OBJECT)) { + return; + } + DataObject content = raw.getObject("d"); + String t = raw.getString("t", null); + if ("READY".equals(t)) { + this.sessionId = content.getString("session_id"); + this.resumeGatewayUrl = content.getString("resume_gateway_url"); + onSuccess(); + } else if ("RESUMED".equals(t)) { + onSuccess(); + } else { + try { + this.userMessageListener.onMessage(raw); + } catch (Exception e) { + log.error("[wss-{}] Handle message error", this.midjourneyConfig.getChannelId(), e); + } + } + } + + private void handleHeartbeat(WebSocketSession session) { + sendMessage(session, WebSocketCode.HEARTBEAT, this.sequence); + this.heartbeatTimeout = ThreadUtil.execAsync(() -> { + ThreadUtil.sleep(this.interval); + onFailure(CLOSE_CODE_RECONNECT, "heartbeat has not ack"); + }); + } + + private void handleHello(WebSocketSession session, DataObject data) { + clearHeartbeatInterval(); + this.interval = data.getObject("d").getLong("heartbeat_interval"); + this.heartbeatAck = true; + this.heartbeatInterval = this.heartExecutor.scheduleAtFixedRate(() -> { + if (this.heartbeatAck) { + this.heartbeatAck = false; + sendMessage(session, WebSocketCode.HEARTBEAT, this.sequence); + } else { + onFailure(CLOSE_CODE_RECONNECT, "heartbeat has not ack interval"); + } + }, (long) Math.floor(RandomUtil.randomDouble(0, 1) * this.interval), this.interval, TimeUnit.MILLISECONDS); + } + + private void doResumeOrIdentify(WebSocketSession session) { + if (CharSequenceUtil.isBlank(this.sessionId)) { + sendMessage(session, WebSocketCode.IDENTIFY, this.authData); + } else { + var data = DataObject.empty().put("token", this.midjourneyConfig.getToken()) + .put("session_id", this.sessionId).put("seq", this.sequence); + sendMessage(session, WebSocketCode.RESUME, data); + } + } + + private void sendMessage(WebSocketSession session, int op, Object d) { + var data = DataObject.empty().put("op", op).put("d", d); + try { + session.sendMessage(new TextMessage(data.toString())); + } catch (IOException e) { + log.error("[wss-{}] Send message error", this.midjourneyConfig.getChannelId(), e); + onFailure(CLOSE_CODE_EXCEPTION, "send message error"); + } + } + + private void onSuccess() { + ThreadUtil.execute(() -> this.successCallback.onSuccess(this.sessionId, this.sequence, this.resumeGatewayUrl)); + } + + private void onFailure(int code, String reason) { + clearHeartbeatTimeout(); + clearHeartbeatInterval(); + ThreadUtil.execute(() -> this.failureCallback.onFailure(code, reason)); + } + + private void clearHeartbeatTimeout() { + if (this.heartbeatTimeout != null) { + this.heartbeatTimeout.cancel(true); + this.heartbeatTimeout = null; + } + } + + private void clearHeartbeatInterval() { + if (this.heartbeatInterval != null) { + this.heartbeatInterval.cancel(true); + this.heartbeatInterval = null; + } + } + + private DataObject createAuthData() { + UserAgent userAgent = UserAgentUtil.parse(this.midjourneyConfig.getUserAage()); + DataObject connectionProperties = DataObject.empty() + .put("browser", userAgent.getBrowser().getName()) + .put("browser_user_agent", this.midjourneyConfig.getUserAage()) + .put("browser_version", userAgent.getVersion()) + .put("client_build_number", 222963) + .put("client_event_source", null) + .put("device", "") + .put("os", userAgent.getOs().getName()) + .put("referer", "https://www.midjourney.com") + .put("referrer_current", "") + .put("referring_domain", "www.midjourney.com") + .put("referring_domain_current", "") + .put("release_channel", "stable") + .put("system_locale", "zh-CN"); + DataObject presence = DataObject.empty() + .put("activities", DataArray.empty()) + .put("afk", false) + .put("since", 0) + .put("status", "online"); + DataObject clientState = DataObject.empty() + .put("api_code_version", 0) + .put("guild_versions", DataObject.empty()) + .put("highest_last_message_id", "0") + .put("private_channels_version", "0") + .put("read_state_version", 0) + .put("user_guild_settings_version", -1) + .put("user_settings_version", -1); + return DataObject.empty() + .put("capabilities", 16381) + .put("client_state", clientState) + .put("compress", false) + .put("presence", presence) + .put("properties", connectionProperties) + .put("token", this.midjourneyConfig.getToken()); + } +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/listener/MjMessageListener.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/listener/MjMessageListener.java new file mode 100644 index 0000000..8e57a46 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/listener/MjMessageListener.java @@ -0,0 +1,44 @@ +package cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener; + + +import cn.hutool.core.text.CharSequenceUtil; +import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; +import cn.iocoder.yudao.framework.ai.midjourney.constants.MjConstants; +import cn.iocoder.yudao.framework.ai.midjourney.constants.MjMessageTypeEnum; +import lombok.extern.slf4j.Slf4j; +import net.dv8tion.jda.api.utils.data.DataObject; + +@Slf4j +public class MjMessageListener { + + private MidjourneyConfig midjourneyConfig; + + public MjMessageListener(MidjourneyConfig midjourneyConfig) { + this.midjourneyConfig = midjourneyConfig; + } + + public void onMessage(DataObject raw) { + MjMessageTypeEnum messageType = MjMessageTypeEnum.of(raw.getString("t")); + if (messageType == null || MjMessageTypeEnum.DELETE == messageType) { + return; + } + DataObject data = raw.getObject("d"); + if (ignoreAndLogMessage(data, messageType)) { + return; + } + System.err.println(data); +// if (data.getBoolean(Constants.MJ_MESSAGE_HANDLED, false)) { +// return; +// } + } + + private boolean ignoreAndLogMessage(DataObject data, MjMessageTypeEnum messageType) { + String channelId = data.getString(MjConstants.CHANNEL_ID); + if (!CharSequenceUtil.equals(channelId, midjourneyConfig.getChannelId())) { + return true; + } + String authorName = data.optObject("author").map(a -> a.getString("username")).orElse("System"); + log.debug("{} - {} - {}: {}", midjourneyConfig.getChannelId(), messageType.name(), authorName, data.opt("content").orElse("")); + return false; + } +} diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/interactions_type2.json b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/interactions_type2.json new file mode 100644 index 0000000..b8a2afe --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/interactions_type2.json @@ -0,0 +1,48 @@ +{ + "type": 2, + "application_id": "936929561302675456", + "guild_id": "1221445697157468200", + "channel_id": "1221445862962630706", + "session_id": "696318caed5180a2210e358e44801449", + "data": { + "version": "1166847114203123795", + "id": "938956540159881230", + "name": "imagine", + "type": 1, + "options": [ + { + "type": 3, + "name": "prompt", + "value": "童话世界应该是什么样?" + } + ], + "application_command": { + "id": "938956540159881230", + "type": 1, + "application_id": "936929561302675456", + "version": "1166847114203123795", + "name": "imagine", + "description": "Create images with Midjourney", + "options": [ + { + "type": 3, + "name": "prompt", + "description": "The prompt to imagine", + "required": true, + "description_localized": "The prompt to imagine", + "name_localized": "prompt" + } + ], + "dm_permission": true, + "integration_types": [ + 0 + ], + "global_popularity_rank": 1, + "description_localized": "Create images with Midjourney", + "name_localized": "imagine" + }, + "attachments": [] + }, + "nonce": "1224342266261274624", + "analytics_location": "slash_ui" +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/interactions_type3.json b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/interactions_type3.json new file mode 100644 index 0000000..4d85c1c --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/interactions_type3.json @@ -0,0 +1,14 @@ +{ + "type": 3, + "nonce": "1224397626158350336", + "guild_id": "1224337694918971392", + "channel_id": "1224337694918971396", + "message_flags": 64, + "message_id": "1224397599197368330", + "application_id": "936929561302675456", + "session_id": "0d9ae3c799f2fb55ff435df5e6deb121", + "data": { + "component_type": 2, + "custom_id": "MJ::ModMsg::Ack::2744093999977583880" + } +} \ No newline at end of file -- libgit2 0.21.4