relax-mj
Version:
Node.js client for the unofficial MidJourney API.
188 lines (182 loc) • 5.07 kB
text/typescript
import {
DefaultMJConfig,
LoadingHandler,
MJMessage,
MJConfig,
MJConfigParam,
} from "./interfaces";
import { CreateQueue } from "./queue";
import { sleep } from "./utls";
import fetch from "node-fetch";
import { HttpsProxyAgent } from "https-proxy-agent";
export class MidjourneyMessage {
private magApiQueue = CreateQueue(1);
public config: MJConfig;
agent?: HttpsProxyAgent<string>;
constructor(defaults: MJConfigParam) {
const { SalaiToken } = defaults;
if (!SalaiToken) {
throw new Error("SalaiToken are required");
}
this.config = {
...DefaultMJConfig,
...defaults,
};
if (this.config.ProxyUrl && this.config.ProxyUrl !== "") {
this.agent = new HttpsProxyAgent(this.config.ProxyUrl);
}
}
protected log(...args: any[]) {
this.config.Debug && console.log(...args, new Date().toISOString());
}
async FilterMessages(
timestamp: number,
prompt: string,
loading?: LoadingHandler,
options?: string,
index?: number
) {
const seed = prompt.match(/--seed (\d+)/)?.[1];
const data = await this.safeRetrieveMessages(this.config.Limit);
for (let i = 0; i < data.length; i++) {
const item = data[i];
if (
item.author.id === "936929561302675456" &&
item.content.includes(`${seed}`)
) {
this.log(JSON.stringify(item));
// Upscaled or Variation
if (item.timestamp < timestamp) {
this.log("old message");
continue;
}
if (
options &&
!(
item.content.includes(options) ||
(options === "Upscaled" && item.content.includes(`Image #${index}`))
)
) {
this.log("no options");
continue;
}
if (item.attachments.length === 0) {
this.log("no attachment");
break;
}
const imageUrl = item.attachments[0].url;
//waiting
this.log(`waiting.content`, item.content);
if (
item.attachments[0].filename.startsWith("grid") ||
item.components.length === 0
) {
this.log(`content`, item.content);
const regex = /\(([^)]+)\)/; // matches the value inside the first parenthesis
const match = item.content.match(regex);
let progress = "wait";
if (match) {
progress = match[1];
} else {
this.log("No match found");
}
loading?.(imageUrl, progress);
break;
}
//finished
const content = item.content.split("**")[1];
const msg: MJMessage = {
id: item.id,
uri: imageUrl,
hash: this.UriToHash(imageUrl),
content: content,
progress: "done",
};
return msg;
}
}
return null;
}
UriToHash(uri: string) {
return uri.split("_").pop()?.split(".")[0] ?? "";
}
async WaitMessage(prompt: string, loading?: LoadingHandler) {
var timestamp = Date.now();
for (let i = 0; i < this.config.MaxWait; i++) {
const msg = await this.FilterMessages(timestamp, prompt, loading);
if (msg !== null) {
return msg;
}
this.log(i, "wait no message found");
await sleep(1000 * 2);
}
return null;
}
async WaitOptionMessage(
content: string,
options: string,
loading?: LoadingHandler
) {
var timestamp = Date.now();
for (let i = 0; i < this.config.MaxWait; i++) {
const msg = await this.FilterMessages(
timestamp,
content,
loading,
options
);
if (msg !== null) {
return msg;
}
this.log(i, content, "wait no message found");
await sleep(1000 * 2);
}
return null;
}
async WaitUpscaledMessage(
content: string,
index: number,
loading?: LoadingHandler
) {
var timestamp = Date.now();
for (let i = 0; i < this.config.MaxWait; i++) {
const msg = await this.FilterMessages(
timestamp,
content,
loading,
"Upscaled",
index
);
if (msg !== null) {
return msg;
}
this.log(i, content, "wait no message found");
await sleep(1000 * 2);
}
return null;
}
// limit the number of concurrent interactions
protected async safeRetrieveMessages(limit = 50) {
return this.magApiQueue.addTask(() => this.RetrieveMessages(limit));
}
async RetrieveMessages(limit = this.config.Limit) {
const headers = {
"Content-Type": "application/json",
Authorization: this.config.SalaiToken,
};
const agent = this.agent;
const response = await fetch(
`${this.config.DiscordBaseUrl}/api/v10/channels/${this.config.ChannelId}/messages?limit=${limit}`,
{
headers,
agent,
}
);
if (!response.ok) {
this.log("error config", { config: this.config });
this.log(`HTTP error! status: ${response.status}`);
}
const data: any = await response.json();
return data;
}
}