前两篇文章写如何注册和配置
Midjourney如何集成到自己(个人/企业)的平台(一)
Midjourney如何集成到自己(个人/企业)的平台(二)
这篇文章是完结篇,也是代码篇,本文章内容描述开发语言为Java,使用框架为SpringBoot,废话不多说,开始上代码。
一、准备maven依赖:
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>5.0.0-alpha.12</version>
</dependency>
这个Jar包是某位大神写的,用于监听、配置discord平台中Midjourney的信息的,主要是监听discord平台的websocket消息,这个WebSocket主要是图片制作进度、绘画结果(包含图片路径、图片Hash等),Java代码中的log.info(),是在类上使用了@Slf4j,这个@Slf4j是在Maven
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
。
二、发送请求常量参数,这些是通过Discord平台的请求获得的请求参数,把这个几个常量复制到一个Java类就行,后继有大用,不用管参数中的参数值,后面会动态替换,(个人建议开发的人自己去Discord平台操作获取参数,使用谷歌浏览器按F12查看Network中的https://discord.com/api/v9/interactions请求,就可以获取了),这些常量参数都有注解说明请仔细阅读。
/**生成图片的参数*/ public static final String GEN_PARAMS ="{\n" + "\t\"type\": 2,\n" + "\t\"guild_id\": \"1123095988219944971\",\n" + "\t\"channel_id\": \"1123095988219944974\",\n" + "\t\"application_id\": \"936929561302675456\",\n" + "\t\"session_id\": \"27f4c1c64e8b08bce77af151f794ad21\",\n" + "\t\"data\": {\n" + "\t\t\"version\": \"1118961510123847772\",\n" + "\t\t\"id\": \"938956540159881230\",\n" + "\t\t\"name\": \"imagine\",\n" + "\t\t\"type\": 1,\n" + "\t\t\"options\": [{\n" + "\t\t\t\"type\": 3,\n" + "\t\t\t\"name\": \"prompt\",\n" + "\t\t\t\"value\": \"Autumn solitude, solitary figure, fallen leaves, melancholic colors, misty atmosphere, lonely path, vintage lens, shooting in the morning, black and white film, 1080p HD.\"\n" + "\t\t}],\n" + "\t\t\"application_command\": {\n" + "\t\t\t\"id\": \"938956540159881230\",\n" + "\t\t\t\"application_id\": \"936929561302675456\",\n" + "\t\t\t\"version\": \"1118961510123847772\",\n" + "\t\t\t\"default_member_permissions\": null,\n" + "\t\t\t\"type\": 1,\n" + "\t\t\t\"nsfw\": false,\n" + "\t\t\t\"name\": \"imagine\",\n" + "\t\t\t\"description\": \"Create images with Midjourney\",\n" + "\t\t\t\"dm_permission\": true,\n" + "\t\t\t\"contexts\": [0, 1, 2],\n" + "\t\t\t\"options\": [{\n" + "\t\t\t\t\"type\": 3,\n" + "\t\t\t\t\"name\": \"prompt\",\n" + "\t\t\t\t\"description\": \"The prompt to imagine\",\n" + "\t\t\t\t\"required\": true\n" + "\t\t\t}]\n" + "\t\t},\n" + "\t\t\"attachments\": []\n" + "\t}\n" + "}"; /**重新生成*/ public static final String RE_ROLL_PARAMS = "{\n" + "\t\"type\": 3,\n" + "\t\"guild_id\": \"1123095988219944971\",\n" + "\t\"channel_id\": \"1123095988219944974\",\n" + "\t\"message_flags\": 0,\n" + "\t\"message_id\": \"1123096851374153798\",\n" + "\t\"application_id\": \"936929561302675456\",\n" + "\t\"session_id\": \"27f4c1c64e8b08bce77af151f794ad21\",\n" + "\t\"data\": {\n" + "\t\t\"component_type\": 2,\n" + "\t\t\"custom_id\": \"MJ::JOB::reroll::0::%s::SOLO\"\n" + "\t}\n" + "}"; /**采样参数,U1-U4*/ public static final String UP_SAMPLE_PARAMS = "{\n" + "\t\"type\": 3,\n" + "\t\"guild_id\": \"1123095988219944971\",\n" + "\t\"channel_id\": \"1123095988219944974\",\n" + "\t\"message_flags\": 0,\n" + "\t\"message_id\": \"1123096851374153798\",\n" + "\t\"application_id\": \"936929561302675456\",\n" + "\t\"session_id\": \"27f4c1c64e8b08bce77af151f794ad21\",\n" + "\t\"data\": {\n" + "\t\t\"component_type\": 2,\n" + "\t\t\"custom_id\": \"MJ::JOB::upsample::%d::%s\"\n" + "\t}\n" + "}"; /**V1-V4参数*/ public static final String VARIATION_PARAMS = "{\n" + "\t\"type\": 3,\n" + "\t\"guild_id\": \"1123095988219944971\",\n" + "\t\"channel_id\": \"1123095988219944974\",\n" + "\t\"message_flags\": 0,\n" + "\t\"message_id\": \"1123096851374153798\",\n" + "\t\"application_id\": \"936929561302675456\",\n" + "\t\"session_id\": \"27f4c1c64e8b08bce77af151f794ad21\",\n" + "\t\"data\": {\n" + "\t\t\"component_type\": 2,\n" + "\t\t\"custom_id\": \"MJ::JOB::variation::%d::%s\"\n" + "\t}\n" + "}"; /**缩小1.5x*/ public static final String ZOOM_1_5 = "{\n" + "\t\"type\": 3,\n" + "\t\"guild_id\": \"1123095988219944971\",\n" + "\t\"channel_id\": \"1123107397800562688\",\n" + "\t\"message_flags\": 0,\n" + "\t\"message_id\": \"1125299549653704795\",\n" + "\t\"application_id\": \"936929561302675456\",\n" + "\t\"session_id\": \"343b05ab59fc7e77543793a4077792f1\",\n" + "\t\"data\": {\n" + "\t\t\"component_type\": 2,\n" + "\t\t\"custom_id\": \"MJ::Outpaint::75::1::%s::SOLO\"\n" + "\t}\n" + "}"; /**缩小2.0x*/ public static final String ZOOM_2_0 = "{\n" + "\t\"type\": 3,\n" + "\t\"guild_id\": \"1123095988219944971\",\n" + "\t\"channel_id\": \"1123107397800562688\",\n" + "\t\"message_flags\": 0,\n" + "\t\"message_id\": \"1125299549653704795\",\n" + "\t\"application_id\": \"936929561302675456\",\n" + "\t\"session_id\": \"343b05ab59fc7e77543793a4077792f1\",\n" + "\t\"data\": {\n" + "\t\t\"component_type\": 2,\n" + "\t\t\"custom_id\": \"MJ::Outpaint::50::1::%s::SOLO\"\n" + "\t}\n" + "}"; /**高度变异(Vary(Strong))*/ public static final String VARY_STRONG="{\n" + "\t\"type\": 3,\n" + "\t\"guild_id\": \"1123095988219944971\",\n" + "\t\"channel_id\": \"1123107397800562688\",\n" + "\t\"message_flags\": 0,\n" + "\t\"message_id\": \"1124291785129218179\",\n" + "\t\"application_id\": \"936929561302675456\",\n" + "\t\"session_id\": \"ad88929a9b1303a2515112e4fe0e452d\",\n" + "\t\"data\": {\n" + "\t\t\"component_type\": 2,\n" + "\t\t\"custom_id\": \"MJ::JOB::high_variation::1::%s::SOLO\"\n" + "\t}\n" + "}"; /**低度变异(Vary(Subtle))*/ public static final String VARY_SUBTLE = "{\n" + "\t\"type\": 3,\n" + "\t\"guild_id\": \"1123095988219944971\",\n" + "\t\"channel_id\": \"1123107397800562688\",\n" + "\t\"message_flags\": 0,\n" + "\t\"message_id\": \"1124291785129218179\",\n" + "\t\"application_id\": \"936929561302675456\",\n" + "\t\"session_id\": \"ad88929a9b1303a2515112e4fe0e452d\",\n" + "\t\"data\": {\n" + "\t\t\"component_type\": 2,\n" + "\t\t\"custom_id\": \"MJ::JOB::low_variation::1::%s::SOLO\"\n" + "\t}\n" + "}"; /**平移图片(左=left、右=right、上=up、下=down 平移)*/ public static final String PAN_IMG = "{\n" + "\t\"type\": 3,\n" + "\t\"nonce\": \"1125688926233690112\",\n" + "\t\"guild_id\": \"1123095988219944971\",\n" + "\t\"channel_id\": \"1123107397800562688\",\n" + "\t\"message_flags\": 0,\n" + "\t\"message_id\": \"1125686738073952256\",\n" + "\t\"application_id\": \"936929561302675456\",\n" + "\t\"session_id\": \"1ec611d049b922bcb71ccece58946d89\",\n" + "\t\"data\": {\n" + "\t\t\"component_type\": 2,\n" + "\t\t\"custom_id\": \"MJ::JOB::%s::1::%s::SOLO\"\n" + "\t}\n" + "}";
三、枚举类型(这个类是一个整类,直接复制走就OK了)
/** * @author s.li * @describe 绘画类型 * @date 2023/7/3 14:24 */ @Getter public enum DrawType { /**生成图片*/ GENERATE("generate",false,false,false,false), /**重画*/ RE_ROLL("reRoll",false,false,false,false), /**采样1*/ UP_SAMPLE_1("upSample1",true,false,false,false){ @Override public JSONObject getActions() { return upSampleActions(); } }, /**采样2*/ UP_SAMPLE_2("upSample2",true,false,false,false){ @Override public JSONObject getActions() { return upSampleActions(); } }, /**采样3*/ UP_SAMPLE_3("upSample3",true,false,false,false){ @Override public JSONObject getActions() { return upSampleActions(); } }, /**采样4*/ UP_SAMPLE_4("upSample4",true,false,false,false){ @Override public JSONObject getActions() { return upSampleActions(); } }, /**变化1*/ VARIATION_1("variation1",false,true,false,false), /**变化2*/ VARIATION_2("variation2",false,true,false,false), /**变化3*/ VARIATION_3("variation3",false,true,false,false), /**变化4*/ VARIATION_4("variation4",false,true,false,false), /**缩小1.5x*/ ZOOM1_5_X("zoom1.5x",false,false,false,true), /**缩小2.0x*/ ZOOM2_0_X("zoom2.0x",false,false,false,true), /**高度变化*/ VARY_STRONG("vary_h2",false,false,false,false), /**低度变化*/ VARY_SUBTLE("vary_subtle",false,false,false,false), /**左平移*/ PAN_LEFT("pan_left",false,false,true,false){ @Override public JSONObject getActions() { return panImgActions(); } }, /**右平移*/ PAN_RIGHT("pan_right",false,false,true,false){ @Override public JSONObject getActions() { return panImgActions(); } }, /**上平移*/ PAN_UP("pan_up",false,false,true,false){ @Override public JSONObject getActions() { return panImgActions(); } }, /**下平移*/ PAN_DOWN("pan_down",false,false,true,false){ @Override public JSONObject getActions() { return panImgActions(); } }, ; private String key; private boolean upSample; private boolean variation; private boolean pan; private boolean zoom; DrawType(String key,boolean upSample,boolean variation,boolean pan,boolean zoom){ this.key = key; this.upSample = upSample; this.variation = variation; this.pan = pan; this.zoom = zoom; } public static DrawType get(String key){ for(DrawType drawType : values()){ if(Objects.equals(key,drawType.getKey())){ return drawType; } } return null; } /**默认可以操作的类型*/ public JSONObject getActions() { JSONObject jsonObject = new JSONObject(); jsonObject.put("reRoll",true); jsonObject.put("upSample", Arrays.asList(1,2,3,4)); jsonObject.put("variation", Arrays.asList(1,2,3,4)); jsonObject.put("zoom", Collections.emptyList()); jsonObject.put("vary",Collections.emptyList()); jsonObject.put("pan",Collections.emptyList()); return jsonObject; } /**普通采样后可以操作的类型*/ private static JSONObject upSampleActions(){ JSONObject jsonObject = new JSONObject(); jsonObject.put("reRoll",false); jsonObject.put("upSample",Collections.emptyList()); jsonObject.put("variation",Collections.emptyList()); jsonObject.put("zoom", Arrays.asList(1.5,2.0)); jsonObject.put("vary", Arrays.asList("vary_h2","vary_subtle")); jsonObject.put("pan", Arrays.asList("pan_left","pan_right","pan_up","pan_down")); return jsonObject; } /**左右偏移后,再采样后,图片可以操作的类型*/ public static JSONObject panLeftOrRightUpSampleActions(){ JSONObject jsonObject = new JSONObject(); jsonObject.put("reRoll",false); jsonObject.put("upSample",Collections.emptyList()); jsonObject.put("variation",Collections.emptyList()); jsonObject.put("zoom", Arrays.asList(1.5,2.0)); jsonObject.put("vary", Collections.emptyList()); jsonObject.put("pan", Arrays.asList("pan_left","pan_right")); return jsonObject; } /**上下偏移后,再采样后,图片可以操作的类型*/ public static JSONObject panUpOrDownUpSampleActions(){ JSONObject jsonObject = new JSONObject(); jsonObject.put("variation",Collections.emptyList()); jsonObject.put("upSample",Collections.emptyList()); jsonObject.put("reRoll",false); jsonObject.put("zoom", Arrays.asList(1.5,2.0)); jsonObject.put("vary", Collections.emptyList()); jsonObject.put("pan", Arrays.asList("pan_up","pan_down")); return jsonObject; } /**偏移后,图片可操作的类型*/ public static JSONObject panImgActions(){ JSONObject jsonObject = new JSONObject(); jsonObject.put("reRoll",true); jsonObject.put("upSample",Arrays.asList(1,2,3,4)); jsonObject.put("variation",Collections.emptyList()); jsonObject.put("zoom", Collections.emptyList()); jsonObject.put("vary",Collections.emptyList()); jsonObject.put("pan", Collections.emptyList()); return jsonObject; } }
三、发送请求类,下面只是有方法,开发人员自己创建一个类,把方法复制进去
1、统一请求方法
/** * 发送请求 * @param reqParams 请求端发送的参数 * @param requestBody 请求参数 * @param drawType 绘画类型 */ private void sendRequest(JSONObject reqParams, JSONObject requestBody, DrawType drawType) { if(Objects.isNull(drawType)){ throw new RuntimeException("sendRequest() to drawType is null"); } String serverId = reqParams.getString("serverId"); String channelId = reqParams.getString("channelId"); String userToken = reqParams.getString("userToken"); String sessionId = reqParams.getString("sessionId"); requestBody.put("guild_id", serverId); requestBody.put("channel_id",channelId); requestBody.put("session_id",sessionId); try { MediaType mediaType = MediaType.parse("application/json"); String bodyStr = requestBody.toJSONString(); log.info("------>>>> bodyStr = {}",bodyStr); RequestBody body = RequestBody.create(mediaType, bodyStr); Request request = new Request.Builder().url(DISCORD_API_URL) .method("POST", body) .addHeader("Content-Type", "application/json") .addHeader("Authorization", userToken) .build(); Response response = HTTP_CLIENT.newCall(request).execute(); String resultStr = response.body().string(); log.info("--------->>>> resultStr = {}",resultStr); //这里可以写自己的业务,比如向数据库中生成一个记录 }catch (Exception e){ log.error(e.getMessage(),e); } }
2、sessionId:请求的会话ID,同一张图片中的sessionId最好是一致的,这里我是通过UUID.randomUUID().toString()生成,把其中的“-”字符批量替换成空。
3、serverId、channelId:
这两个ID登录discord获取
4、userToken:这个方法中的请求头中的Authorization 是从Discord获取,获得的办法还是老办法,使用谷歌浏览器按F12,在Network中随便找到一条请求路径,查看其中的请求头,看看有没有Authorization,如果没有Authorization,就换一条链接继续找,找到了Authorization就复制它的值出来。这个值就是这里用到的userToken了。
5、生成图片方法
/** * 生成初始图片 * @param jsonObject 请求参数 */ public void generateImage(JSONObject jsonObject) throws Exception { String prompt = jsonObject.getString("prompt"); JSONObject requestMjBody = JSON.parseObject(ParamsConstant.GEN_PARAMS); JSONObject data = requestMjBody.getJSONObject("data"); JSONArray options = data.getJSONArray("options"); JSONObject option = options.getJSONObject(0); option.put("value",prompt); options.set(0,option); data.put("options",options); requestMjBody.put("data",data); this.sendRequest(jsonObject,requestMjBody,DrawType.GENERATE); }
这里使用到了之前说的那个常量类的一个常量参数ParamsConstant.GEN_PARAMS,这里我的定义的类名是“ParamsConstant”,你们可以根据自己的类名来调用,刚才说那些常量值中的会动态替换就在这里进行的,把常量值能过JSON.parseObject(ParamsConstant.GEN_PARAMS)转成JSONObject对象
6、重新生成图片
/** * 重新生成 * @param jsonObject 请求参数 */ public void reRoll(JSONObject jsonObject){ String messageId = jsonObject.getString("messageId"); String messageHash = jsonObject.getString("messageHash"); JSONObject requestMjBody = JSON.parseObject(ParamsConstant.RE_ROLL_PARAMS); requestMjBody.put("message_id",messageId); this.updateCustomId(requestMjBody,messageHash); this.sendRequest(jsonObject,requestMjBody,DrawType.RE_ROLL); }
其中的messageId和messageHash这个值,是通过监听第5步的生成图片的WebSocket获得的(一会再说什么监听),再进行动态替换。
7、选中采样(也就是U1-U4的选中)
/** * 选中采样 * @param jsonObject 请求参数 */ public void upSample(JSONObject jsonObject){ JSONObject requestMjBody = JSON.parseObject(ParamsConstant.UP_SAMPLE_PARAMS); Integer index = jsonObject.getInteger("index"); String key = "upSample"+index; DrawType drawType = DrawType.get(key); this.upSampleOrVariation(requestMjBody,jsonObject,drawType); }
相同的道理,这里需要动态替换参数,这个index就是1-4,是固定值,拼接好后是upSample1-upSample4
8、V1-V4的操作
/** * V1-V4 * @param jsonObject 请求参数 */ public void variation(JSONObject jsonObject){ JSONObject requestMjBody = JSON.parseObject(ParamsConstant.VARIATION_PARAMS); Integer index = jsonObject.getInteger("index"); String key = "variation"+index; DrawType drawType = DrawType.get(key); this.upSampleOrVariation(requestMjBody,jsonObject,drawType); }
相同的道理,这里需要动态替换参数,这个index就是1-4,是固定值,拼接好后是variation1-variation4。
9、图片缩放
/** * 缩放,只支持1.5x 和 2.0x * @param jsonObject 消息ID */ public void zoom(JSONObject jsonObject){ String zoom = jsonObject.getString("zoom"); DrawType drawType = DrawType.get(zoom); String messageId = jsonObject.getString("messageId"); String messageHash = jsonObject.getString("messageHash"); JSONObject requestMjBody; if(Objects.equals(drawType,DrawType.ZOOM1_5_X)){ requestMjBody = JSON.parseObject(ParamsConstant.ZOOM_1_5); }else{ requestMjBody = JSON.parseObject(ParamsConstant.ZOOM_2_0); } requestMjBody.put("message_id",messageId); this.updateCustomId(requestMjBody,messageHash); this.sendRequest(jsonObject,requestMjBody,drawType); }
这里的zoom是两个固定值:zoom1.5x和zoom2.0x ,其中的messageId和messageHash这个值,是通过监听第5步的生成图片的WebSocket获得的(一会再说什么监听),再进行动态替换。
10、变化图片
/** * 高/低度变化 * @param jsonObject 参数对象 */ public void vary(JSONObject jsonObject){ String varyVal = jsonObject.getString("varyType"); DrawType drawType = DrawType.get(varyVal); String messageId = jsonObject.getString("messageId"); String messageHash = jsonObject.getString("messageHash"); JSONObject requestMjBody; if(Objects.equals(drawType,DrawType.VARY_STRONG)){ requestMjBody = JSON.parseObject(ParamsConstant.VARY_STRONG); }else{ requestMjBody = JSON.parseObject(ParamsConstant.VARY_SUBTLE); } requestMjBody.put("message_id",messageId); this.updateCustomId(requestMjBody,messageHash); this.sendRequest(jsonObject,requestMjBody,drawType); }
varyType有两个固定值 :vary_h2和vary_subtle,messageId和messageHash同上描述。
11、平移图片
/** * 平移图片 * @param jsonObject 参数对象 */ public void panImg(JSONObject jsonObject){ String position = jsonObject.getString("position"); String messageId = jsonObject.getString("messageId"); String messageHash = jsonObject.getString("messageHash"); JSONObject requestMjBody = JSON.parseObject(ParamsConstant.PAN_IMG); JSONObject data = requestMjBody.getJSONObject("data"); String customId = data.getString("custom_id"); customId = String.format(customId,position, messageHash); data.put("custom_id",customId); requestMjBody.put("data",data); requestMjBody.put("message_id",messageId); DrawType drawType = DrawType.get(position); this.sendRequest(jsonObject,requestMjBody,drawType); }
position参数有四个值 :pan_left、pan_right、pan_up、pan_down,表示四个方向,messageId和messageHash同上描述。
12、工具方法
/** * 更新自定义ID * @param requestMjBody 发送的参数对象 * @param messageHash 图片Key */ private void updateCustomId(JSONObject requestMjBody,String messageHash){ JSONObject data = requestMjBody.getJSONObject("data"); String customId = data.getString("custom_id"); customId = String.format(customId, messageHash); data.put("custom_id",customId); requestMjBody.put("data",data); } /** * U1-U4,V1-V4 * @param requestMjBody 请求内容 * @param reqObject 请求的参数 * @param drawType 操作类型 */ private void upSampleOrVariation(JSONObject requestMjBody,JSONObject reqObject,DrawType drawType){ String messageId = reqObject.getString("messageId"); String messageHash = reqObject.getString("messageHash"); Integer index = reqObject.getInteger("index"); requestMjBody.put("message_id",messageId); JSONObject data = requestMjBody.getJSONObject("data"); String customId = data.getString("custom_id"); customId = String.format(customId,index, messageHash); data.put("custom_id",customId); requestMjBody.put("data",data); this.sendRequest(reqObject,requestMjBody,drawType); }
把这1-12步中的代码,复制到一个类就OK了。这样请求的方法就介绍完成了。
四、监听Discord中的WebSocket进行获取图片结果,上面的各个发送请求方法操作成功后是不会返回任何结果的,所有的请求结果都会通过WebSocket接收。
1、监听类
/** * Discord中机器人消息的监听器 * @date 2023-06-30 15:46:15 */ @Component @Slf4j public class DiscordMsgEventHandler extends ListenerAdapter{ static final OkHttpClient HTTP_CLIENT= new OkHttpClient.Builder().build(); private String WAITING_TO_START = "(Waiting to start)"; private String RE_ROLLING = "Rerolling **"; private String STOPPED = "(Stopped)"; @Autowired public DiscordMsgEventHandler(MjBotConfiguration mjBotConfiguration) { log.info("-------->>> new DiscordEventHandler complete"); } /** * 监听接收的消息 * @param event 消息事件 */ @Override public void onMessageReceived(MessageReceivedEvent event) { Message message = event.getMessage(); String msgId = message.getId(); String messageContent = message.getContentRaw(); JSONObject keyObj = this.getSessionIdOrChannelId(messageContent); String channelId = keyObj.getString("channelId"); log.info("------------->>> onMessageReceived msgId = {}",msgId); MessageChannel channel = event.getChannel(); User author = event.getAuthor(); log.info("------->>> channel.getId() = {}",channel.getId()); log.info("------->>> mjBotConfiguration.getChannelId() = {}",channelId); log.info("------->>> author.getId() = {}",author.getId()); log.info("------->>> event.getJDA().getSelfUser().getId() = {}",event.getJDA().getSelfUser().getId()); if (!channel.getId().equals(channelId) ||author.getId().equals(event.getJDA().getSelfUser().getId())) { return; } log.info("---------->>> onMessageReceived messageContent = {}",messageContent); if (messageContent.contains(WAITING_TO_START) && !messageContent.contains(RE_ROLLING)) { trigger(msgId,messageContent, Scene.FIRST_TRIGGER); return; } for (Message.Attachment attachment : message.getAttachments()) { if (attachment.isImage()) { replay(msgId,messageContent,attachment); return; } } } /** * 监听消息更新 * @param event 事件对象 */ @Override public void onMessageUpdate(MessageUpdateEvent event) { Message message = event.getMessage(); String msgId = message.getId(); log.info("------------->>> onMessageReceived msgId = {}",msgId); String messageContent = message.getContentRaw(); JSONObject keyObj = this.getSessionIdOrChannelId(messageContent); String channelId = keyObj.getString("channelId"); MessageChannel channel = event.getChannel(); User author = event.getAuthor(); log.info("-------------->>> onMessageUpdate msg = {}",message.getContentRaw()); if (!channel.getId().equals(channelId) || author.getId().equals(event.getJDA().getSelfUser().getId())) { return; } if (message.getContentRaw().contains(STOPPED)) { trigger(msgId,message.getContentRaw(), Scene.GENERATE_EDIT_ERROR); } } /** * 处理获取得到结果 * @param msgId 消息ID * @param messageContent 消息内容 * @param attachment 图片属性 */ private void replay(String msgId,String messageContent,Message.Attachment attachment ) { String fileName = attachment.getFileName(); String[] keys = fileName.split("_", -1); JSONObject jsonObject =new JSONObject(); jsonObject.put("id", attachment.getId()); jsonObject.put("url", attachment.getUrl()); jsonObject.put("proxyUrl", attachment.getProxyUrl()); jsonObject.put("fileName", attachment.getFileName()); jsonObject.put("contentType", attachment.getContentType()); jsonObject.put("description", attachment.getDescription()); jsonObject.put("size", attachment.getSize()); jsonObject.put("height", attachment.getHeight()); jsonObject.put("width", attachment.getWidth()); String images = keys[keys.length - 1]; String msgHash = images.split("\\.")[0]; jsonObject.put("msgHash",msgHash); DateTimeFormatter df = DateTimeFormatter.ofPattern(CommonConstant.YYYY_MM_DD_HH_MM_SS); LocalDateTime time = LocalDateTime.now(); String localTime = df.format(time); jsonObject.put("date",localTime); //拼接路径,数据库中可以直接保存该路径,返回前端,前端即可访问 JSONObject body = new JSONObject(); body.put("discord", jsonObject); body.put("type", Scene.GENERATE_END); body.put("messageId", msgId); body.put("prompt", messageContent); handle(body); } /** * 接收到消息后触发 * @param messageId 消息ID * @param content 消息内容 * @param scene 事件 */ private void trigger(String messageId ,String content, Scene scene) { JSONObject body = new JSONObject(); body.put("messageId", messageId); body.put("content", content); body.put("type", scene); handle(body); } /** * 接收到消息后触发 * @param messageId 消息ID * @param content 消息内容 * @param scene 事件 */ private void handle(JSONObject body ){ //自己处理结果 } }
这个类继承了ListenerAdapter,对Discord的WebSocket进行监听。
2、配置类
/** * @describe MJ机器人配置 * @date 2023/6/13 18:24 */ @Configuration @ConfigurationProperties(prefix="mj.config") @Data public class MjBotConfiguration { /**用户令牌*/ private String userToken; /**机器人令牌*/ private String botToken; /**服务ID*/ private String serverId; /**通道ID*/ private String channelId; }
在spring boot的配置文件中添加:
mj.config.userToken=用户令牌
mj.config.botToken=Discord的机器人令牌,去看第二篇就知道什么得到了
mj.config.serverId=Discord的服务ID
mj.config.channelId= Discord的通道ID
3、绘画结果枚举
/** * 绘画返回结果类型 * @date 2023-06-27 */ public enum Scene { /**第一触发器*/ FIRST_TRIGGER, /**生成结束*/ GENERATE_END, /**生成错误*/ GENERATE_EDIT_ERROR }
4、让监听器生效
在SpringBoot的配置类中(有使用@Configuration这个注解的类中)
@Getter private JDA jda; @Resource private MjBotConfiguration mjBotConfiguration; @Resource private DiscordMsgEventHandler discordMsgEventHandler;
/** * 监听系统启动完成 */ @EventListener(classes = {ApplicationStartedEvent.class}) public void startFinish(){ this.initJDA(); }
/*** * 初始化MJ机器人的监听器 */ private void initJDA() { try{ log.info("------>>>>开始初始化监听"); jda = JDABuilder .createDefault(mjBotConfiguration.getBotToken()) .addEventListeners(discordMsgEventHandler) .build(); log.info("------>>>>机器人监听初始化完成,没有任务异常"); }catch (Exception e){ log.error(e.getMessage(),e); } }
到这里基本就能用了,再开发Controller对外开放请求接口就行了。
四、Controller的代码,下面是我息的代码,只能作为参考了
/** * 生成图片 * @param jsonObject 参数对象 * @return ResultEntity<DrawRecord> * @throws Exception */ @RequestMapping("/generateImage") public ResultEntity<DrawRecord> generateImage(@RequestBody JSONObject jsonObject) throws Exception{ String sessionId = UUID.randomUUID().toString().replaceAll("-",""); jsonObject.put("sessionId",sessionId); jsonObject.put("serverId",mjBotConfiguration.getServerId()); jsonObject.put("channelId",mjBotConfiguration.getChannelId()); jsonObject.put("userToken",mjBotConfiguration.getUserToken()); DrawRecord drawRecord = discordMjRequest.generateImage(jsonObject); return ResultEntity.success().entity(drawRecord).build(); } /** * 重新生成 * @param jsonObject 参数对象 * @return ResultEntity<DrawRecord> */ @RequestMapping("/reRoll") public ResultEntity<DrawRecord> reRoll(@RequestBody JSONObject jsonObject){ //验证 ResultEntity<DrawRecord> resultEntity = this.verify(DrawType.RE_ROLL,jsonObject); if(!Objects.isNull(resultEntity)){ return resultEntity; } DrawRecord drawRecord = discordMjRequest.reRoll(jsonObject); return ResultEntity.success().entity(drawRecord).build(); } /** * 采样 * @param jsonObject 请求参数 * @return ResultEntity<DrawRecord> */ @RequestMapping("/upSample") public ResultEntity<DrawRecord> upSample(@RequestBody JSONObject jsonObject){ int index = jsonObject.getInteger("index"); int min = 1,max = 4; if(index > max || index < min){ return ResultEntity.fail().message("采样下标必须是1-4的数值").build(); } //验证 ResultEntity<DrawRecord> resultEntity = this.verifyUV(jsonObject,true); if(!Objects.isNull(resultEntity)){ return resultEntity; } DrawRecord drawRecord = discordMjRequest.upSample(jsonObject); return ResultEntity.success().entity(drawRecord).build(); } /** * 变化 * @param jsonObject 请求参数 * @return ResultEntity<DrawRecord> */ @RequestMapping("/variation") public ResultEntity<DrawRecord> variation(@RequestBody JSONObject jsonObject){ int index = jsonObject.getInteger("index"); int min = 1,max = 4; if(index > max || index < min){ return ResultEntity.fail().message("变化下标必须是1-4的数值").build(); } //验证 ResultEntity<DrawRecord> resultEntity = this.verifyUV(jsonObject,false); if(!Objects.isNull(resultEntity)){ return resultEntity; } DrawRecord drawRecord = discordMjRequest.variation(jsonObject); return ResultEntity.success().entity(drawRecord).build(); } /** * 缩小 * @param jsonObject 请求参数 * @return ResultEntity<DrawRecord> */ @RequestMapping("/zoom") public ResultEntity<DrawRecord> zoom(@RequestBody JSONObject jsonObject){ String zoom = jsonObject.getString("zoom"); DrawType drawType = DrawType.get(zoom); if(Objects.isNull(drawType)){ return ResultEntity.fail().message("缩放类型错误").build(); } //验证 ResultEntity<DrawRecord> resultEntity = this.verify(drawType,jsonObject); if(!Objects.isNull(resultEntity)){ return resultEntity; } DrawRecord drawRecord = discordMjRequest.zoom(jsonObject); return ResultEntity.success().entity(drawRecord).build(); } /** * 高/低度变化 * @param jsonObject 请求参数 * @return ResultEntity<DrawRecord> */ @RequestMapping("/vary") public ResultEntity<DrawRecord> varyStrong(@RequestBody JSONObject jsonObject){ String varyType = jsonObject.getString("varyType"); DrawType drawType = DrawType.get(varyType); if(Objects.isNull(drawType)){ return ResultEntity.fail().message("变化类型错误").build(); } //验证 ResultEntity<DrawRecord> resultEntity = this.verify(drawType,jsonObject); if(!Objects.isNull(resultEntity)){ return resultEntity; } DrawRecord drawRecord = discordMjRequest.vary(jsonObject); return ResultEntity.success().entity(drawRecord).build(); } /** * 平移图片 * @param jsonObject 参数对象 * @return ResultEntity<DrawRecord> */ @RequestMapping("/panImg") public ResultEntity<DrawRecord> panImg(@RequestBody JSONObject jsonObject){ String position = jsonObject.getString("position"); DrawType drawType = DrawType.get(position); if(Objects.isNull(drawType)){ return ResultEntity.fail().message("图片平移方向错误").build(); } //验证 ResultEntity<DrawRecord> resultEntity = this.verify(drawType,jsonObject); if(!Objects.isNull(resultEntity)){ return resultEntity; } DrawRecord drawRecord = discordMjRequest.panImg(jsonObject); return ResultEntity.success().entity(drawRecord).build(); } /** * 查询记录 * @param condition 条件对象 * @return ResultEntity<DrawRecord> */ @RequestMapping("/getRecord") public ResultEntity<DrawRecord> getRecord(@RequestBody DrawRecord condition){ DrawRecord drawRecord = drawRecordBiz.findDrawRecord(condition); return ResultEntity.success().entity(drawRecord).build(); } //=========================================== /** * 验证upSample或variation * @param jsonObject 请求参数 * @param isUpSample 是否是 upSample * @return ResultEntity<DrawRecord> */ private ResultEntity<DrawRecord> verifyUV(JSONObject jsonObject, boolean isUpSample){ int index = jsonObject.getInteger("index"); int min = 1,max = 4; if(index > max || index < min){ if(isUpSample){ return ResultEntity.fail().message("采样下标必须是1-4的数值").build(); }else{ return ResultEntity.fail().message("变化下标必须是1-4的数值").build(); } } String type; if(isUpSample){ type = "upSample"+index; }else{ type = "variation"+index; } DrawType drawType = DrawType.get(type); ResultEntity<DrawRecord> resultEntity = this.verify(drawType,jsonObject); if(!Objects.isNull(resultEntity)){ return resultEntity; } String sessionId = jsonObject.getString("sessionId"); String messageHash = jsonObject.getString("messageHash"); DrawRecord condition = new DrawRecord(); condition.setSessionId(sessionId); condition.setType(type); condition.setOldMsgHash(messageHash); condition.setReqState(DrawRecord.REQ_STATE_Y); DrawRecord old = drawRecordBiz.findDrawRecordOne(condition); if(!old.isDefObj()){ if(isUpSample){ return ResultEntity.fail().message("当前图片的U"+index+"已变化过,不可重复操作").build(); }else{ return ResultEntity.fail().message("当前图片的V"+index+"已变化过,不可重复操作").build(); } } return null; } /** * 验证 * @param drawType 操作类型 * @param reqObj 请求参数 * @return ResultEntity<DrawRecord> */ private ResultEntity<DrawRecord> verify(DrawType drawType,JSONObject reqObj){ //验证记录是否存在 ResultEntity<DrawRecord> resultEntity = this.verifyExist(drawType,reqObj); if(!Objects.isNull(resultEntity)){ return resultEntity; } return null; } /** * 验证是否可操作 * @param drawType 要操作的类型 * @param drawRecord 记录对象 * @return ResultEntity<DrawRecord> */ private ResultEntity<DrawRecord> verifyType(DrawType drawType,DrawRecord drawRecord){ if(Objects.equals(drawType,DrawType.UP_SAMPLE_1) || Objects.equals(drawType,DrawType.UP_SAMPLE_2) || Objects.equals(drawType,DrawType.UP_SAMPLE_3) || Objects.equals(drawType,DrawType.UP_SAMPLE_4)){ JSONArray jsonArray = drawRecord.getActionsObj().getJSONArray("upSample"); if(jsonArray.isEmpty()){ return ResultEntity.fail().message("当前记录不可采样操作").build(); } } if(Objects.equals(drawType,DrawType.VARIATION_1) || Objects.equals(drawType,DrawType.VARIATION_2) || Objects.equals(drawType,DrawType.VARIATION_3) || Objects.equals(drawType,DrawType.VARIATION_4)){ JSONArray jsonArray = drawRecord.getActionsObj().getJSONArray("variation"); if(jsonArray.isEmpty()){ return ResultEntity.fail().message("当前记录不可变化操作").build(); } } if(Objects.equals(drawType,DrawType.ZOOM1_5_X) || Objects.equals(drawType,DrawType.ZOOM2_0_X)){ JSONArray jsonArray = drawRecord.getActionsObj().getJSONArray("zoom"); if(jsonArray.isEmpty()){ return ResultEntity.fail().message("当前记录不可缩放操作").build(); } } if(Objects.equals(drawType,DrawType.VARY_STRONG) || Objects.equals(drawType,DrawType.VARY_SUBTLE)){ JSONArray jsonArray = drawRecord.getActionsObj().getJSONArray("vary"); if(jsonArray.isEmpty()){ return ResultEntity.fail().message("当前记录不可高/低变化操作").build(); } } if(Objects.equals(drawType,DrawType.PAN_LEFT) || Objects.equals(drawType,DrawType.PAN_RIGHT) || Objects.equals(drawType,DrawType.PAN_UP) || Objects.equals(drawType,DrawType.PAN_DOWN)){ JSONArray jsonArray = drawRecord.getActionsObj().getJSONArray("pan"); List<String> list = jsonArray.toJavaList(String.class); if(jsonArray.isEmpty() || !list.contains(drawType.getKey())){ return ResultEntity.fail().message("当前记录不可此平移操作").build(); } } return null; }
jsonpsazoomdiscordactionivaappbottokenparseideseoguiaslwebsocprompturl机器人traw