Commit f2b9c14819c750ac43dcb190b0a56b6edcc5a341

Authored by cherishsince
1 parent b09fc557

增加mj 图片生成消息转换

yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/MjMessage.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.midjourney;
  2 +
  3 +import lombok.Data;
  4 +import lombok.experimental.Accessors;
  5 +
  6 +import java.util.List;
  7 +
  8 +@Data
  9 +@Accessors(chain = true)
  10 +public class MjMessage {
  11 +
  12 + /**
  13 + * id是一个重要的字段,在同时生成多个的时候,可以区分生成信息
  14 + */
  15 + private String id;
  16 + /**
  17 + * 现在已知:
  18 + * 0:我们发送的消息,和指令
  19 + * 20: mj生成图片发送过程中
  20 + */
  21 + private Integer type;
  22 + /**
  23 + * content
  24 + */
  25 + private Content content;
  26 + /**
  27 + * 图片生成完成才有
  28 + */
  29 + private List<ComponentType> components;
  30 + /**
  31 + * 生成过程中如果有,预展示图片,这里会有
  32 + */
  33 + private List<Attachment> attachments;
  34 + /**
  35 + * 原始数据(discard 返回的原始数据)
  36 + */
  37 + private String rawData;
  38 + /**
  39 + * 生成状态(用于区分生成状态)
  40 + * 1、等待
  41 + * 2、进行中
  42 + * 3、完成
  43 + * {@link cn.iocoder.yudao.framework.ai.midjourney.constants.MjGennerateStatusEnum}
  44 + */
  45 + private String generateStatus;
  46 +
  47 + @Data
  48 + @Accessors(chain = true)
  49 + public static class ComponentType {
  50 +
  51 + private int type;
  52 +
  53 + private List<Component> components;
  54 + }
  55 +
  56 + @Data
  57 + @Accessors(chain = true)
  58 + public static class Component {
  59 + /**
  60 + * 自定义ID,用于唯一标识特定交互动作及其上下文信息。
  61 + */
  62 + private String customId;
  63 +
  64 + /**
  65 + * 样式编号,用于确定按钮的样式外观。
  66 + * 在某些应用中,例如Discord,2可能表示一种特定的颜色或形状的按钮。
  67 + */
  68 + private int style;
  69 +
  70 + /**
  71 + * 按钮的标签文本,用户可见的内容。
  72 + */
  73 + private String label;
  74 +
  75 + /**
  76 + * 组件类型,此处为2可能表示这是一种特定类型的交互组件,
  77 + * 如在Discord API中,类型2对应的是一个可点击的按钮组件。
  78 + */
  79 + private int type;
  80 +
  81 + }
  82 +
  83 + @Data
  84 + @Accessors(chain = true)
  85 + public static class Attachment {
  86 + // 文件名
  87 + private String filename;
  88 +
  89 + // 附件大小(字节)
  90 + private int size;
  91 +
  92 + // 内容类型(例如:image/webp)
  93 + private String contentType;
  94 +
  95 + // 图像宽度(像素)
  96 + private int width;
  97 +
  98 + // 占位符版本号
  99 + private int placeholderVersion;
  100 +
  101 + // 代理URL,用于访问附件资源
  102 + private String proxyUrl;
  103 +
  104 + // 占位符标识符
  105 + private String placeholder;
  106 +
  107 + // 附件ID
  108 + private String id;
  109 +
  110 + // 直接访问附件资源的URL
  111 + private String url;
  112 +
  113 + // 图像高度(像素)
  114 + private int height;
  115 + }
  116 +
  117 + @Data
  118 + @Accessors(chain = true)
  119 + public static class Content {
  120 + private String prompt;
  121 + private String progress;
  122 + private String status;
  123 + }
  124 +}
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/constants/MjGennerateStatusEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.ai.midjourney.constants;
  2 +
  3 +import lombok.Getter;
  4 +
  5 +/**
  6 + * mj 生成状态
  7 + *
  8 + * author: fansili
  9 + * time: 2024/4/6 21:07
  10 + */
  11 +@Getter
  12 +public enum MjGennerateStatusEnum {
  13 +
  14 +
  15 + WAITING("waiting", "等待..."),
  16 + IN_PROGRESS("in_progress", "进行中"),
  17 + COMPLETED("completed", "完成"),
  18 +
  19 + ;
  20 +
  21 + MjGennerateStatusEnum(String value, String message) {
  22 + this.value = value;
  23 + this.message = message;
  24 + }
  25 +
  26 + private String value;
  27 +
  28 + private String message;
  29 +}
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/midjourney/webSocket/listener/MjMessageListener.java
1 package cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener; 1 package cn.iocoder.yudao.framework.ai.midjourney.webSocket.listener;
2 2
3 3
  4 +import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.text.CharSequenceUtil; 5 import cn.hutool.core.text.CharSequenceUtil;
  6 +import cn.hutool.core.util.StrUtil;
  7 +import cn.hutool.json.JSONUtil;
5 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig; 8 import cn.iocoder.yudao.framework.ai.midjourney.MidjourneyConfig;
  9 +import cn.iocoder.yudao.framework.ai.midjourney.MjMessage;
6 import cn.iocoder.yudao.framework.ai.midjourney.constants.MjConstants; 10 import cn.iocoder.yudao.framework.ai.midjourney.constants.MjConstants;
  11 +import cn.iocoder.yudao.framework.ai.midjourney.constants.MjGennerateStatusEnum;
7 import cn.iocoder.yudao.framework.ai.midjourney.constants.MjMessageTypeEnum; 12 import cn.iocoder.yudao.framework.ai.midjourney.constants.MjMessageTypeEnum;
  13 +import cn.iocoder.yudao.framework.ai.midjourney.util.MjUtil;
  14 +import com.alibaba.fastjson.JSON;
  15 +import com.google.common.collect.Lists;
8 import lombok.extern.slf4j.Slf4j; 16 import lombok.extern.slf4j.Slf4j;
9 import net.dv8tion.jda.api.utils.data.DataObject; 17 import net.dv8tion.jda.api.utils.data.DataObject;
10 18
  19 +import java.io.UnsupportedEncodingException;
  20 +import java.util.Arrays;
  21 +import java.util.List;
  22 +
11 @Slf4j 23 @Slf4j
12 public class MjMessageListener { 24 public class MjMessageListener {
13 25
14 - private MidjourneyConfig midjourneyConfig;  
15 -  
16 - public MjMessageListener(MidjourneyConfig midjourneyConfig) {  
17 - this.midjourneyConfig = midjourneyConfig;  
18 - }  
19 -  
20 - public void onMessage(DataObject raw) {  
21 - MjMessageTypeEnum messageType = MjMessageTypeEnum.of(raw.getString("t"));  
22 - if (messageType == null || MjMessageTypeEnum.DELETE == messageType) {  
23 - return;  
24 - }  
25 - DataObject data = raw.getObject("d");  
26 - if (ignoreAndLogMessage(data, messageType)) {  
27 - return;  
28 - }  
29 - System.err.println(data);  
30 -// if (data.getBoolean(Constants.MJ_MESSAGE_HANDLED, false)) {  
31 -// return;  
32 -// }  
33 - }  
34 -  
35 - private boolean ignoreAndLogMessage(DataObject data, MjMessageTypeEnum messageType) {  
36 - String channelId = data.getString(MjConstants.CHANNEL_ID);  
37 - if (!CharSequenceUtil.equals(channelId, midjourneyConfig.getChannelId())) {  
38 - return true;  
39 - }  
40 - String authorName = data.optObject("author").map(a -> a.getString("username")).orElse("System");  
41 - log.debug("{} - {} - {}: {}", midjourneyConfig.getChannelId(), messageType.name(), authorName, data.opt("content").orElse(""));  
42 - return false;  
43 - } 26 + private MidjourneyConfig midjourneyConfig;
  27 +
  28 + public MjMessageListener(MidjourneyConfig midjourneyConfig) {
  29 + this.midjourneyConfig = midjourneyConfig;
  30 + }
  31 +
  32 + public void onMessage(DataObject raw) {
  33 + MjMessageTypeEnum messageType = MjMessageTypeEnum.of(raw.getString("t"));
  34 + if (messageType == null || MjMessageTypeEnum.DELETE == messageType) {
  35 + return;
  36 + }
  37 + DataObject data = raw.getObject("d");
  38 + if (ignoreAndLogMessage(data, messageType)) {
  39 + return;
  40 + }
  41 +
  42 + MjMessage mjMessage = new MjMessage();
  43 + mjMessage.setId(data.getString("id"));
  44 + mjMessage.setType(data.getInt("type"));
  45 + mjMessage.setRawData(StrUtil.str(raw.toJson(), "UTF-8"));
  46 + mjMessage.setContent(MjUtil.parseContent(data.getString("content")));
  47 +
  48 + if (!data.getArray("components").isEmpty()) {
  49 + String componentsJson = StrUtil.str(data.getArray("components").toJson(), "UTF-8");
  50 + List<MjMessage.ComponentType> components = JSON.parseArray(componentsJson, MjMessage.ComponentType.class);
  51 + mjMessage.setComponents(components);
  52 + }
  53 + if (!data.getArray("attachments").isEmpty()) {
  54 + String attachmentsJson = StrUtil.str(data.getArray("attachments").toJson(), "UTF-8");
  55 + List<MjMessage.Attachment> attachments = JSON.parseArray(attachmentsJson, MjMessage.Attachment.class);
  56 + mjMessage.setAttachments(attachments);
  57 + }
  58 +
  59 + // 转换状态
  60 + convertGenerateStatus(mjMessage);
  61 + System.err.println(JSONUtil.toJsonPrettyStr(mjMessage));
  62 + }
  63 +
  64 + private void convertGenerateStatus(MjMessage mjMessage) {
  65 + if (mjMessage.getType() == 20 && mjMessage.getContent().getStatus().contains("Waiting")) {
  66 + mjMessage.setGenerateStatus(MjGennerateStatusEnum.WAITING.getValue());
  67 + } else if (mjMessage.getType() == 20 && !StrUtil.isBlank(mjMessage.getContent().getProgress())) {
  68 + mjMessage.setGenerateStatus(MjGennerateStatusEnum.IN_PROGRESS.getValue());
  69 + } else if (mjMessage.getType() == 0 && !CollUtil.isEmpty(mjMessage.getComponents())) {
  70 + mjMessage.setGenerateStatus(MjGennerateStatusEnum.COMPLETED.getValue());
  71 + }
  72 + }
  73 +
  74 + private boolean ignoreAndLogMessage(DataObject data, MjMessageTypeEnum messageType) {
  75 + String channelId = data.getString(MjConstants.CHANNEL_ID);
  76 + if (!CharSequenceUtil.equals(channelId, midjourneyConfig.getChannelId())) {
  77 + return true;
  78 + }
  79 + String authorName = data.optObject("author").map(a -> a.getString("username")).orElse("System");
  80 + log.debug("{} - {} - {}: {}", midjourneyConfig.getChannelId(), messageType.name(), authorName, data.opt("content").orElse(""));
  81 + return false;
  82 + }
44 } 83 }