目前Midjourney
没有对外开放API接口,所以通过MJ自动化生图的主要方式是,集成Discord
应用机器人,通过机器人与MJ机器人进行交互,并监听频道内的生图结果,最终拿到图片地址。
简单介绍下步骤
一、购买MJ账号
二、获取账号Authorization
在网页中向Midjourney Bot
发送/imagine
进行生图
我们在检查元素
中能够获取调用的地址和Authorization
三、模拟Http请求调用
public String generateImage(String promptText) {
Map<String, String> headerMap = Maps.newHashMap();
// 添加付费账号对应的 authorization
headerMap.put("authorization", getAuthorization());
headerMap.put("Content-Type", "application/json");
// 构建请求(详见下方payload_json)
InteractionsRequest req = buildInteractionsRequest(promptText);
String reqJson = JSON.toJSONString(req);
log.info("===reqJson is {}", reqJson);
try {
// POST请求需要代理
String res = HttpUtils.httpPostProxy(MidjourneyContants.API_URL, reqJson, headerMap);
return res;
} catch (Exception e) {
log.error("VolcanoTtsService generateVoice error,request={}", reqJson, e);
}
return StringUtils.EMPTY;
}
{
"type": 2, // 注意:type为2不能缺少
"application_id": "",
"guild_id": "",
"channel_id": "",
"session_id": "",
"data": {
"version": "",
"id": "",
"name": "imagine",
"type": 1,
"options": [
{
"type": 3,
"name": "prompt",
"value": "a beautiful lady"
}
],
"application_command": {
"id": "",
"application_id": "",
"version": "",
"default_member_permissions": null,
"type": 1,
"nsfw": false,
"name": "imagine",
"description": "Create images with Midjourney",
"dm_permission": true,
"contexts": null,
"options": [
{
"type": 3,
"name": "prompt",
"description": "The prompt to imagine",
"required": true
}
]
},
"attachments": [ ]
},
"nonce": ""
}
四、Discord机器人监听消息
之前写过监听消息的方法,这里就不再赘述了
Discord 机器人Java Api使用
1、生图完成结果
生图结果是四张图的拼图
我们收到的响应通过debug
可以看到
需要关注:
- 我们的
prompt
在响应中是在event.message.content
中可以获得,前后有**
标识
private static final String PROMPT_MARKER = "**";
private String getPromptBetweenMarkers(String messageContent, String marker) {
return str.substring(str.indexOf(marker) + marker.length(), str.lastIndexOf(marker));
}
五、调用获取单张图片
获取单图的所调用的Http
地址与生图相同,不通的是Body
中的payload_json
。所需要的参数如下:
{
"type": 3, // 注意:type为3不能错
"nonce": "",
"guild_id": "",
"channel_id": "",
"message_flags": 0,
"message_id": "生图响应中的messageId",
"application_id": "",
"session_id": "",
"data": {
"component_type": 2,
"custom_id": "生图响应中的customId"
}
}
六、外网图片转换
获取的MJ单图图片是外网图片,转换成内网的方式有很多,比如说先下载本地File再上传到自己的图床等
七、Discord监听器完整代码
@Slf4j
@Component
public class ImageGenerateComplete implements MessageCreateListener {
@Autowired
private IMidjourneyService midjourneyService;
@Override
public void onMessageCreate(MessageCreateEvent event) {
// 反转为Spring的Bean管理
ImageGenerateComplete self = SpringUtil.getBean(this);
BizExecutor.getInstance().getThreadPool().execute(() -> self.eventSolution(event));
}
public void eventSolution(MessageCreateEvent event) {
try{
MessageAuthor messageAuthor = event.getMessageAuthor();
// 消息发送这是否为机器人
if(messageAuthor.isBotUser()){
// 发送人id,可用来校验是否为需要监听的应用
long messageAuthorId = messageAuthor.getId();
Message message = event.getMessage();
long messageId = message.getId();
// 生图返回的消息类型为NORMAL。点击U1生成单图的消息类型为REPLY
if(MessageType.NORMAL.equals(message.getType())){
// 示例默认取U1,非空校验略
String customId = message.getComponents().get(0).asActionRow().orElse(null)
.getComponents().stream().filter(c -> c.asButton().orElse(null).getLabel().orElse("").equals("U1")).findFirst().orElse(null)
.asButton().orElse(null).getCustomId().orElse(null);
// 获取单张图片服务
midjourneyService.getSingleImage(messageId, customId);
}
}
}catch (Exception e) {
log.info("!==[ImageGenerateComplete#eventSolution] ex is " + e.getMessage());
// do sth.
}
}
}
@Slf4j
@Component
public class SingleImageComplete implements MessageCreateListener {
private static final String PROMPT_MARKER = "**";
@Resource
private IMidjourneryBiz midjourneryBiz;
@Override
public void onMessageCreate(MessageCreateEvent event) {
// 反转为Spring的Bean管理
SingleImageComplete self = SpringUtil.getBean(this);
BizExecutor.getInstance().getThreadPool().execute(() -> self.eventSolution(event));
}
public void eventSolution(MessageCreateEvent event) {
try{
MessageAuthor messageAuthor = event.getMessageAuthor();
if(messageAuthor.isBotUser()){
Message message = event.getMessage();
long messageId = message.getId();
if(MessageType.REPLY.equals(message.getType())){
String messageContent = message.getContent();
// 获取prompt
String prompt = getStringBetweenMarkers(messageContent, PROMPT_MARKER);
// 获取图片地址
String discordImageUrl = event.getMessage().getAttachments().get(0).getUrl().toString();
// 转内网图片地址
}
}
}catch (Exception e) {
log.info("!==[SingleImageComplete#eventSolution] ex is " + e.getMessage());
}
}
private String getStringBetweenMarkers(String str, String marker) {
return str.substring(str.indexOf(marker) + marker.length(), str.lastIndexOf(marker));
}
}
可参考我的GitHub:
all-in-one/springboot/aigc at master · Meidanlong/all-in-one