Skip to content

Commit

Permalink
add zoom, add inpaint, add region
Browse files Browse the repository at this point in the history
  • Loading branch information
trueai-org committed Jul 10, 2024
1 parent 81cf627 commit c613bcd
Show file tree
Hide file tree
Showing 14 changed files with 240 additions and 39 deletions.
16 changes: 10 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,27 +8,31 @@ Midjourney api 的 C# 版本。

## 主要功能

#### 绘画功能

- [x] 支持 Imagine 指令和相关动作 [V1/V2.../U1/U2.../R]
- [x] Imagine 时支持添加图片 base64,作为垫图
- [x] 支持 Blend (图片混合)、Describe (图生文) 指令
- [x] 支持任务实时进度
- [x] 支持中文 prompt 翻译,需配置百度翻译或 gpt
- [x] prompt 敏感词预检测,支持覆盖调整
- [x] user-token 连接 wss,可以获取错误信息和完整功能
- [x] 支持多账号配置,每个账号可设置对应的任务队列
- [x] 支持 Shorten(prompt分析) 指令
- [x] 支持焦点移动: Pan ⬅️ ➡️ ⬆️ ⬇️
- [x] 支持焦点移动: Pan ⬅️➡⬆️⬇️
- [x] 支持局部重绘: Vary (Region) 🖌
- [x] 支持几乎所有的关联按钮动作和 🎛️ Remix 模式
- [x] 账号池持久化,动态维护
- [x] 支持获取账号 /info、/settings信 息
- [x] 支持图片变焦,自定义变焦 Zoom 🔍
- [ ] 支持获取图片的 seed 值

#### 账号管理

- [ ] 支持多账号配置,每个账号可设置对应的任务队列
- [ ] 支持获取账号 /info、/settings信 息
- [ ] 账号 settings 设置
- [ ] 支持 niji bot 机器人
- [ ] 支持获取图片的 seed 值
- [ ] 支持 InsightFace 人脸替换机器人
- [ ] 内嵌管理后台页面
- [ ] 支持图片变焦: Zoom 🔍
- [ ] 支持局部重绘

## 配置项
- mj.accounts: 参考 [账号池配置](./docs/config.md#%E8%B4%A6%E5%8F%B7%E6%B1%A0%E9%85%8D%E7%BD%AE%E5%8F%82%E8%80%83)
Expand Down
72 changes: 72 additions & 0 deletions docs/discord-params.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,75 @@

频道的url里取出 服务器ID、频道ID,后续设置到配置项
![Guild Channel ID](img_9.png)


### 如何捕获事件:INTERACTION_IFRAME_MODAL_CREATE

> 参考
https://github.com/dolfies/discord.py-self/discussions/573

> 注意修改 command.ts 为:https://discord.com/api/v9/users/@me/application-command-index
https://github.com/bao-io/midjourney-sdk
https://www.npmjs.com/package/midjourney-sdk

在处理 WebSocket 连接和会话管理时,捕获特定的前端事件,如 `INTERACTION_IFRAME_MODAL_CREATE`,可以是一个挑战。以下教程基于实际对话,解释了如何正确捕获这一事件。


#### 环境设定
- 技术栈:TypeScript
- 应用场景:WebSocket 连接管理

#### 步骤概述

1. **创建 WebSocket 连接**
确保在创建 WebSocket 连接时捕获并保存 `session_id`。这个 `session_id` 是后续所有交互的关键。

2. **捕获 `session_id`**
在建立 WebSocket 连接时,通常会从服务器接收到一个类型为 `READY` 的消息,该消息包含了 `session_id`

3. **使用 `session_id` 发送请求**
在发送请求以创建交互式 iframe 模态框时,必须使用从 WebSocket 连接中获得的 `session_id`

4. **处理和监听事件**
使用该 `session_id` 发送数据后,系统应能够正确触发 `INTERACTION_IFRAME_MODAL_CREATE` 事件。

#### 代码示例

```typescript
// 假设 websocket 已经连接并且是可用的状态
websocket.onmessage = function(event) {
let data = JSON.parse(event.data);
let type = data.type;

// 当服务器发送 READY 类型的消息时,保存 session_id
if (type === 'READY') {
this.opts.session_id = data.session_id;
}
};

// 使用保存的 session_id 发送请求
function sendInteractionRequest() {
let request = {
type: 'INTERACTION_IFRAME_MODAL_CREATE',
session_id: this.opts.session_id
};
websocket.send(JSON.stringify(request));
}

// 监听事件
websocket.onmessage = function(event) {
let data = JSON.parse(event.data);
if (data.type === 'INTERACTION_IFRAME_MODAL_CREATE') {
console.log('Modal create interaction triggered successfully.');
}
};
```

#### 常见问题解决

- **问题**: `INTERACTION_IFRAME_MODAL_CREATE` 事件不触发。
- **解决方案**: 确保发送的 `session_id` 是在建立 WebSocket 连接时接收的那个。如果 `session_id` 错误,该事件不会被正确触发。

通过以上步骤和示例代码,您应该能够有效地捕获并处理 `INTERACTION_IFRAME_MODAL_CREATE` 事件。这需要确保您正确管理和使用 `session_id`,以保证数据的一致性和事件的正确触发。
32 changes: 19 additions & 13 deletions src/Midjourney.API/Controllers/SubmitController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Midjourney.Infrastructure.Dto;
using Midjourney.Infrastructure.Services;
using Midjourney.Infrastructure.Util;
using System.Buffers.Text;
using System.Text.RegularExpressions;

using TaskStatus = Midjourney.Infrastructure.TaskStatus;
Expand Down Expand Up @@ -329,6 +330,12 @@ public ActionResult<SubmitResultVO> Action([FromBody] SubmitActionDTO actionDTO)
task.Action = TaskAction.ACTION;
task.Description = "Waiting for window confirm";
}
// 局部绘制
// MJ::Inpaint::1::da2b1fda-0455-4952-9f0e-d4cb891f8b1e::SOLO
else if (actionDTO.CustomId.StartsWith("MJ::Inpaint::"))
{
task.Action = TaskAction.ACTION;
}
else
{
task.Action = TaskAction.ACTION;
Expand Down Expand Up @@ -377,22 +384,21 @@ public ActionResult<SubmitResultVO> Modal([FromBody] SubmitModalDTO actionDTO)
.SetProperty("bannedWord", e.Message));
}

//List<string> base64Array = imagineDTO.Base64Array ?? new List<string>();

//List<DataUrl> dataUrls = new List<DataUrl>();
//try
//{
// dataUrls = ConvertUtils.ConvertBase64Array(base64Array);
//}
//catch (Exception e)
//{
// _logger.LogError(e, "base64格式转换异常");
// return BadRequest(SubmitResultVO.Fail(ReturnCode.VALIDATION_ERROR, "base64格式错误"));
//}
// 不检查
DataUrl dataUrl = null;
try
{
//dataUrl = DataUrl.Parse(actionDTO.MaskBase64);
}
catch (Exception e)
{
_logger.LogError(e, "base64格式转换异常");
return BadRequest(SubmitResultVO.Fail(ReturnCode.VALIDATION_ERROR, "base64格式错误"));
}

task.PromptEn = promptEn;

return Ok(_taskService.SubmitModal(task, actionDTO));
return Ok(_taskService.SubmitModal(task, actionDTO, dataUrl));
}


Expand Down
16 changes: 12 additions & 4 deletions src/Midjourney.Infrastructure/BotMessageListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,6 @@ public async Task StartAsync()
// Subscribe a handler to see if a message invokes a command.
_client.MessageReceived += HandleCommandAsync;
_client.MessageUpdated += MessageUpdatedAsync;

//_client.InteractionCreated += HandleInteractionAsync;
}

private DiscordSocketClient _client;
Expand Down Expand Up @@ -236,7 +234,6 @@ public void OnMessage(JsonElement raw)
return;
}


// 如果有渠道 id,但不是当前渠道 id,则忽略
if (data.TryGetProperty("channel_id", out JsonElement channelIdElement) && channelIdElement.GetString() != _discordAccount.ChannelId)
{
Expand Down Expand Up @@ -271,7 +268,6 @@ public void OnMessage(JsonElement raw)
var id = idElement.GetString();
_logger.Debug($"用户消息, {messageType}, {_discordAccount.GetDisplay()} - {authorName}: {contentStr}, id: {id}, mid: {metaId}");


// 判断账号是否用量已经用完
if (messageType == MessageType.CREATE && data.TryGetProperty("embeds", out var em))
{
Expand Down Expand Up @@ -320,6 +316,18 @@ public void OnMessage(JsonElement raw)
{
task.InteractionMetadataId = id;
}
// MJ 局部重绘完成后
else if (messageType == MessageType.INTERACTION_IFRAME_MODAL_CREATE
&& data.TryGetProperty("custom_id", out var custom_id))
{
task.SetProperty(Constants.TASK_PROPERTY_IFRAME_MODAL_CREATE_CUSTOM_ID, custom_id.GetString());
task.MessageId = id;

if (!task.MessageIds.Contains(id))
{
task.MessageIds.Add(id);
}
}
else
{
task.MessageId = id;
Expand Down
5 changes: 5 additions & 0 deletions src/Midjourney.Infrastructure/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ public static class Constants
/// </summary>
public const string TASK_PROPERTY_REFERENCED_MESSAGE_ID = "referencedMessageId";

/// <summary>
/// 局部重绘弹窗 custom_id
/// </summary>
public const string TASK_PROPERTY_IFRAME_MODAL_CREATE_CUSTOM_ID = "iframe_modal_custom_id";

// 任务扩展属性 end

/// <summary>
Expand Down
4 changes: 3 additions & 1 deletion src/Midjourney.Infrastructure/DiscordAccountHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@ public async Task<IDiscordInstance> CreateDiscordInstance(DiscordAccount account
_messageListener = new BotMessageListener(account.BotToken, account, webProxy);

// 用户 WebSocket 连接
var webSocket = new WebSocketStarter(account, _discordHelper, _messageListener, webProxy);
var webSocket = new WebSocketStarter(account, _discordHelper, _messageListener,
webProxy, discordService);

await webSocket.StartAsync();

_messageListener.Init(discordInstance, _messageHandlers);
Expand Down
12 changes: 12 additions & 0 deletions src/Midjourney.Infrastructure/Enums.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ public enum MessageType
///
/// </summary>
INTERACTION_SUCCESS,

/// <summary>
///
/// </summary>
INTERACTION_IFRAME_MODAL_CREATE,

/// <summary>
///
/// </summary>
INTERACTION_MODAL_CREATE
}

public static class MessageTypeExtensions
Expand All @@ -171,6 +181,8 @@ public static class MessageTypeExtensions
"MESSAGE_DELETE" => MessageType.DELETE,
"INTERACTION_CREATE" => MessageType.INTERACTION_CREATE,
"INTERACTION_SUCCESS" => MessageType.INTERACTION_SUCCESS,
"INTERACTION_IFRAME_MODAL_CREATE" => MessageType.INTERACTION_IFRAME_MODAL_CREATE,
"INTERACTION_MODAL_CREATE" => MessageType.INTERACTION_MODAL_CREATE,
_ => null
};
}
Expand Down
11 changes: 11 additions & 0 deletions src/Midjourney.Infrastructure/LoadBalancer/DiscordInstanceImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,17 @@ public Task<Message> ActionAsync(string messageId, string customId, int messageF
public Task<Message> ZoomAsync(string messageId, string customId, string prompt, string nonce) =>
_service.ZoomAsync(messageId, customId, prompt, nonce);


/// <summary>
/// 局部重绘
/// </summary>
/// <param name="customId"></param>
/// <param name="prompt"></param>
/// <param name="maskBase64"></param>
/// <returns></returns>
public Task<Message> InpaintAsync(string customId, string prompt, string maskBase64) =>
_service.InpaintAsync(customId, prompt, maskBase64);

/// <summary>
/// 异步执行描述任务。
/// </summary>
Expand Down
48 changes: 47 additions & 1 deletion src/Midjourney.Infrastructure/Services/DiscordServiceImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ namespace Midjourney.Infrastructure.Services
/// </summary>
public class DiscordServiceImpl : IDiscordService
{
private static readonly string DefaultSessionId = "f1a313a09ce079ce252459dc70231f30";
private readonly DiscordAccount _account;
private readonly HttpClient _httpClient;
private readonly DiscordHelper _discordHelper;
Expand Down Expand Up @@ -42,6 +41,11 @@ public DiscordServiceImpl(DiscordAccount account,
_discordMessageUrl = $"{discordServer}/api/v9/channels/{account.ChannelId}/messages";
}

/// <summary>
/// 默认会话ID。
/// </summary>
public string DefaultSessionId { get; set; } = "f1a313a09ce079ce252459dc70231f30";

public async Task<Message> ImagineAsync(string prompt, string nonce)
{
string paramsStr = ReplaceInteractionParams(_paramsMap["imagine"], nonce);
Expand Down Expand Up @@ -146,6 +150,48 @@ public async Task<Message> ZoomAsync(string messageId, string customId, string p
return await PostJsonAndCheckStatusAsync(paramsStr);
}

/// <summary>
/// 局部重绘
/// </summary>
/// <param name="customId"></param>
/// <param name="prompt"></param>
/// <param name="maskBase64"></param>
/// <returns></returns>
public async Task<Message> InpaintAsync(string customId, string prompt, string maskBase64)
{
try
{
customId = customId.Replace("MJ::iframe::", "");

// mask.replace(/^data:.+?;base64,/, ''),
maskBase64 = maskBase64.Replace("data:image/png;base64,", "");

var obj = new
{
customId = customId,
//full_prompt = null,
mask = maskBase64,
prompt = prompt,
userId = "0",
username = "0",
};
var paramsStr = Newtonsoft.Json.JsonConvert.SerializeObject(obj);
var response = await PostJsonAsync("https://936929561302675456.discordsays.com/inpaint/api/submit-job",
paramsStr);

if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
return Message.Success();
}

return Message.Of((int)response.StatusCode, "提交失败");
}
catch (HttpRequestException e)
{
return ConvertHttpRequestException(e);
}
}

public async Task<Message> RerollAsync(string messageId, string messageHash, int messageFlags, string nonce)
{
string paramsStr = ReplaceInteractionParams(_paramsMap["reroll"], nonce)
Expand Down
10 changes: 10 additions & 0 deletions src/Midjourney.Infrastructure/Services/IDiscordService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,16 @@ public interface IDiscordService
/// <returns></returns>
Task<Message> ZoomAsync(string messageId, string customId, string prompt, string nonce);


/// <summary>
/// 局部重绘
/// </summary>
/// <param name="customId"></param>
/// <param name="prompt"></param>
/// <param name="maskBase64"></param>
/// <returns></returns>
Task<Message> InpaintAsync(string customId, string prompt, string maskBase64);

/// <summary>
/// 提交描述任务。
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion src/Midjourney.Infrastructure/Services/ITaskService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,6 @@ public interface ITaskService
/// <param name="task"></param>
/// <param name="submitAction"></param>
/// <returns></returns>
SubmitResultVO SubmitModal(TaskInfo task, SubmitModalDTO submitAction);
SubmitResultVO SubmitModal(TaskInfo task, SubmitModalDTO submitAction, DataUrl dataUrl = null);
}
}
Loading

0 comments on commit c613bcd

Please sign in to comment.