前两篇文章写如何注册和配置
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_strong",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_strong","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_strong和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;
}