Skip to content

Commit

Permalink
work time
Browse files Browse the repository at this point in the history
  • Loading branch information
trueai-org committed Aug 1, 2024
1 parent 6c51514 commit 961636f
Show file tree
Hide file tree
Showing 11 changed files with 235 additions and 97 deletions.
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
- [x] 支持客户端路径指定模式,默认地址例子 https://{BASE_URL}/mj/submit/imagine, /mj-turbo/mj 是 turbo mode, /mj-relax/mj 是 relax mode, /mj-fast/mj 是 fast mode, /mj 不指定模式
- [x] CloudFlare 手动真人验证,触发后自动锁定账号,通过 GUI 直接验证或通过邮件通知验证
- [x] [⚠⚠暂不稳定] CloudFlare 自动真人验证,配置验证服务器地址(自动验证器仅支持 Windows 部署)
- [x] 支持工作时间段配置,24 小时不间断绘图可能会触发警告,建议休息 6~8 小时

## 在线预览

Expand Down Expand Up @@ -371,7 +372,6 @@ https://discord.com/oauth2/authorize?client_id=xxx&permissions=8&scope=bot

## 路线图

- [ ] 工作时间段配置,非工作时间段释放连接,到了非工作时间自动释放,24 小时不间断作业将会触发警告
- [ ] 优化指令和状态进度显示
- [ ] 优化任务和队列满时的提醒
- [ ] 优化共享账号的并发队列可能出现的问题
Expand All @@ -388,7 +388,6 @@ https://discord.com/oauth2/authorize?client_id=xxx&permissions=8&scope=bot
- [ ] 支付接入支持、支持微信、支付宝,支持绘图定价策略等
- [ ] 增加公告功能
- [ ] 账号增加咸鱼模式/放松模式,避免高频作业(此模式下不可创建新的绘图,仍可以执行其他命令,可以配置为多个时间段或定时等策略)
- [ ] 增加强制休眠模式,或定时休眠模式
- [ ] 图生文 seed 值处理
- [ ] 自动读私信消息
- [ ] 增加允许速度模式配置
Expand Down
249 changes: 161 additions & 88 deletions src/Midjourney.API/DiscordAccountInitializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using Midjourney.Infrastructure.Domain;
using Midjourney.Infrastructure.LoadBalancer;
using Midjourney.Infrastructure.Services;
using Midjourney.Infrastructure.Util;
using MimeKit;
using Serilog;

using ILogger = Serilog.ILogger;
Expand All @@ -19,6 +21,8 @@ public class DiscordAccountInitializer : IHostedService
private readonly ProxyProperties _properties;
private readonly ILogger _logger;

private readonly SemaphoreSlim _semaphoreSlim = new(1, 1);
private Timer _timer;

public DiscordAccountInitializer(
DiscordLoadBalancer discordLoadBalancer,
Expand All @@ -39,20 +43,6 @@ public DiscordAccountInitializer(
/// </summary>
/// <param name="cancellationToken">取消令牌。</param>
public async Task StartAsync(CancellationToken cancellationToken)
{
_ = Task.Run(async () =>
{
await Initialize();
});

await Task.CompletedTask;
}

/// <summary>
/// 初始化所有账号
/// </summary>
/// <returns></returns>
public async Task Initialize(params DiscordAccountConfig[] appends)
{
var proxy = _properties.Proxy;
if (!string.IsNullOrEmpty(proxy.Host))
Expand All @@ -63,103 +53,169 @@ public async Task Initialize(params DiscordAccountConfig[] appends)
Environment.SetEnvironmentVariable("https_proxyPort", proxy.Port.ToString());
}

var db = DbHelper.AccountStore;
var accounts = db.GetAll().OrderBy(c => c.Sort).ToList();
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMinutes(1));

await Task.CompletedTask;
}

// 将启动配置中的 account 添加到数据库
var configAccounts = _properties.Accounts.ToList();
if (!string.IsNullOrEmpty(_properties.Discord.ChannelId))
private async void DoWork(object state)
{
if (_semaphoreSlim.CurrentCount == 0)
{
configAccounts.Add(_properties.Discord);
return;
}

if (appends?.Length > 0)
await _semaphoreSlim.WaitAsync();

try
{
_logger.Information("开始例行检查");

await Initialize();

_logger.Information("例行检查完成");
}
catch (Exception ex)
{
configAccounts.AddRange(appends);
_logger.Error(ex, "执行例行检查时发生异常");
}
finally
{
_semaphoreSlim.Release();
}
}

foreach (var configAccount in configAccounts)
/// <summary>
/// 初始化所有账号
/// </summary>
/// <returns></returns>
public async Task Initialize(params DiscordAccountConfig[] appends)
{
var isLock = LocalLock.TryLock("Initialize", TimeSpan.FromSeconds(10), async () =>
{
var account = accounts.FirstOrDefault(c => c.ChannelId == configAccount.ChannelId);
if (account == null)
var proxy = _properties.Proxy;
var db = DbHelper.AccountStore;
var accounts = db.GetAll().OrderBy(c => c.Sort).ToList();

// 将启动配置中的 account 添加到数据库
var configAccounts = _properties.Accounts.ToList();
if (!string.IsNullOrEmpty(_properties.Discord.ChannelId))
{
if (configAccount.Interval < 1.2m)
{
configAccount.Interval = 1.2m;
}
configAccounts.Add(_properties.Discord);
}

account = new DiscordAccount
{
Id = configAccount.ChannelId,
ChannelId = configAccount.ChannelId,

GuildId = configAccount.GuildId,
UserToken = configAccount.UserToken,
UserAgent = string.IsNullOrEmpty(configAccount.UserAgent) ? Constants.DEFAULT_DISCORD_USER_AGENT : configAccount.UserAgent,
Enable = configAccount.Enable,
CoreSize = configAccount.CoreSize,
QueueSize = configAccount.QueueSize,
BotToken = configAccount.BotToken,
TimeoutMinutes = configAccount.TimeoutMinutes,
PrivateChannelId = configAccount.PrivateChannelId,
NijiBotChannelId = configAccount.NijiBotChannelId,
MaxQueueSize = configAccount.MaxQueueSize,
Mode = configAccount.Mode,
Weight = configAccount.Weight,
Remark = configAccount.Remark,
RemixAutoSubmit = configAccount.RemixAutoSubmit,
Sponsor = configAccount.Sponsor,
Sort = configAccount.Sort,
Interval = configAccount.Interval
};

db.Add(account);
accounts.Add(account);
if (appends?.Length > 0)
{
configAccounts.AddRange(appends);
}
}

var instances = _discordLoadBalancer.GetAllInstances();
foreach (var account in accounts)
{
if (!account.Enable)
foreach (var configAccount in configAccounts)
{
continue;
var account = accounts.FirstOrDefault(c => c.ChannelId == configAccount.ChannelId);
if (account == null)
{
if (configAccount.Interval < 1.2m)
{
configAccount.Interval = 1.2m;
}

account = new DiscordAccount
{
Id = configAccount.ChannelId,
ChannelId = configAccount.ChannelId,

GuildId = configAccount.GuildId,
UserToken = configAccount.UserToken,
UserAgent = string.IsNullOrEmpty(configAccount.UserAgent) ? Constants.DEFAULT_DISCORD_USER_AGENT : configAccount.UserAgent,
Enable = configAccount.Enable,
CoreSize = configAccount.CoreSize,
QueueSize = configAccount.QueueSize,
BotToken = configAccount.BotToken,
TimeoutMinutes = configAccount.TimeoutMinutes,
PrivateChannelId = configAccount.PrivateChannelId,
NijiBotChannelId = configAccount.NijiBotChannelId,
MaxQueueSize = configAccount.MaxQueueSize,
Mode = configAccount.Mode,
Weight = configAccount.Weight,
Remark = configAccount.Remark,
RemixAutoSubmit = configAccount.RemixAutoSubmit,
Sponsor = configAccount.Sponsor,
Sort = configAccount.Sort,
Interval = configAccount.Interval,
WorkTime = configAccount.WorkTime
};

db.Add(account);
accounts.Add(account);
}
}

IDiscordInstance disInstance = null;
try
var instances = _discordLoadBalancer.GetAllInstances();
foreach (var account in accounts)
{
disInstance = _discordLoadBalancer.GetDiscordInstance(account.ChannelId);
if (disInstance == null)
if (!account.Enable)
{
disInstance = await _discordAccountHelper.CreateDiscordInstance(account);
instances.Add(disInstance);
_discordLoadBalancer.AddInstance(disInstance);
continue;
}

// TODO 这里应该等待初始化完成,并获取用户信息验证,获取用户成功后设置为可用状态
// 多账号启动时,等待一段时间再启动下一个账号
await Task.Delay(1000 * 5);
IDiscordInstance disInstance = null;
try
{
disInstance = _discordLoadBalancer.GetDiscordInstance(account.ChannelId);

// 判断是否在工作时间内
if (DateTime.Now.IsInWorkTime(account.WorkTime))
{
if (disInstance == null)
{
disInstance = await _discordAccountHelper.CreateDiscordInstance(account);
instances.Add(disInstance);
_discordLoadBalancer.AddInstance(disInstance);

// 这里应该等待初始化完成,并获取用户信息验证,获取用户成功后设置为可用状态
// 多账号启动时,等待一段时间再启动下一个账号
await Task.Delay(1000 * 5);

// 启动后执行 info setting 操作
await _taskService.InfoSetting(account.ChannelId);
}
}
else
{
// 非工作时间内,如果存在实例则释放
if (disInstance != null)
{
_discordLoadBalancer.RemoveInstance(disInstance);

disInstance.Dispose();
}
}
}
catch (Exception ex)
{
_logger.Error("Account({@0}) init fail, disabled: {@1}", account.GetDisplay(), ex.Message);

// 启动后执行 info setting 操作
await _taskService.InfoSetting(account.ChannelId);
account.Enable = false;
account.DisabledReason = "初始化失败";

db.Update(account);

disInstance?.ClearAccountCache(account.Id);
}
}
catch (Exception ex)
{
_logger.Error("Account({@0}) init fail, disabled: {@1}", account.GetDisplay(), ex.Message);

account.Enable = false;
var enableInstanceIds = instances.Where(instance => instance.IsAlive)
.Select(instance => instance.GetInstanceId)
.ToHashSet();

db.Update(account);
disInstance?.ClearAccountCache(account.Id);
}
_logger.Information("当前可用账号数 [{@0}] - {@1}", enableInstanceIds.Count, string.Join(", ", enableInstanceIds));
});
if (!isLock)
{
throw new LogicException("初始化中,请稍后重拾");
}

var enableInstanceIds = instances.Where(instance => instance.IsAlive)
.Select(instance => instance.GetInstanceId)
.ToHashSet();

_logger.Information("当前可用账号数 [{@0}] - {@1}", enableInstanceIds.Count, string.Join(", ", enableInstanceIds));
await Task.CompletedTask;
}

/// <summary>
Expand Down Expand Up @@ -196,11 +252,24 @@ public void UpdateAccount(DiscordAccount param)
model.CfHashUrl = null;
model.CfUrl = null;


// 验证 Interval
if (param.Interval < 1.2m)
{
param.Interval = 1.2m;
}

// 验证 WorkTime
if (!string.IsNullOrEmpty(param.WorkTime))
{
var ts = param.WorkTime.ToTimeSlots();
if (ts.Count == 0)
{
param.WorkTime = null;
}
}

model.WorkTime = param.WorkTime;
model.Interval = param.Interval;
model.Sort = param.Sort;
model.Enable = param.Enable;
Expand Down Expand Up @@ -282,9 +351,13 @@ public void DeleteAccount(string id)
/// </summary>
/// <param name="cancellationToken"></param>
/// <returns></returns>
public Task StopAsync(CancellationToken cancellationToken)
public async Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
_logger.Information("例行检查服务已停止");

_timer?.Change(Timeout.Infinite, 0);
await _semaphoreSlim.WaitAsync();
_semaphoreSlim.Release();
}
}
}
4 changes: 2 additions & 2 deletions src/Midjourney.API/wwwroot/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
<title>Midjourney Proxy Admin</title>
<link rel="stylesheet" href="/umi.56ce17d3.css">
<script async src="/scripts/loading.js"></script>
<script src="/preload_helper.cc182953.js"></script>
<script src="/preload_helper.092c005f.js"></script>
</head>
<body>
<div id="root"></div>
<script src="/umi.2f632aee.js"></script>
<script src="/umi.d8283903.js"></script>
</body>
</html>

This file was deleted.

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 961636f

Please sign in to comment.