当前位置:AIGC资讯 > AIGC > 正文

Midjourney如何集成到自己(个人/企业)的平台(三-完结)

前两篇文章写如何注册和配置

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;
    }

更新时间 2024-06-27