diff --git a/example/customzoom.ts b/example/customzoom.ts index 060de7a..05429dc 100644 --- a/example/customzoom.ts +++ b/example/customzoom.ts @@ -17,8 +17,14 @@ async function main() { Ws: true, //enable ws is required for custom zoom }); await client.init(); - const prompt = "Christmas dinner with spaghetti with family in a cozy house, we see interior details , simple blue&white illustration" - const Imagine = await client.Imagine(prompt); + const prompt = + "Christmas dinner with spaghetti with family in a cozy house, we see interior details , simple blue&white illustration"; + const Imagine = await client.Imagine( + prompt, + (uri: string, progress: string) => { + console.log("loading", uri, "progress", progress); + } + ); console.log(Imagine); if (!Imagine) { console.log("no message"); diff --git a/example/imagine-ws.ts b/example/imagine-ws.ts index ac97b41..56e9c1c 100644 --- a/example/imagine-ws.ts +++ b/example/imagine-ws.ts @@ -14,11 +14,11 @@ async function main() { SalaiToken: process.env.SALAI_TOKEN, HuggingFaceToken: process.env.HUGGINGFACE_TOKEN, Debug: true, - Ws: true, // required `Only you can see this` + Ws: true, // required `Only you can see this` }); await client.Connect(); // required const Imagine = await client.Imagine( - "Red hamster smoking a cigaret, https://media.discordapp.net/attachments/1108515696385720410/1118385339732590682/DanielH_A_giant_hamster_monster._Friendly_in_a_business_suit_si_d4be1836-a4e1-41a8-b1d7-99eebc521220.png?width=1878&height=1878 ", + "Red hamster smoking a cigaret", (uri: string, progress: string) => { console.log("Imagine.loading", uri, "progress", progress); } diff --git a/src/discord.message.ts b/src/discord.message.ts index 14af8c2..0062b26 100644 --- a/src/discord.message.ts +++ b/src/discord.message.ts @@ -35,7 +35,7 @@ export class MidjourneyMessage { for (let i = 0; i < data.length; i++) { const item = data[i]; if ( - item.author.id === "936929561302675456" && + item.author.id === this.config.BotId && item.content.includes(`${seed}`) ) { const itemTimestamp = new Date(item.timestamp).getTime(); @@ -64,6 +64,7 @@ export class MidjourneyMessage { content, id: item.id, uri: imageUrl, + proxy_url: item.attachments[0].proxy_url, flags: item.flags, hash: this.UriToHash(imageUrl), progress: "done", diff --git a/src/discord.ws.ts b/src/discord.ws.ts index 5db26c0..6af5e0b 100644 --- a/src/discord.ws.ts +++ b/src/discord.ws.ts @@ -116,7 +116,6 @@ export class WsMessage { if (nonce) { // this.log("waiting start image or info or error"); this.updateMjEventIdByNonce(id, nonce); - if (embeds?.[0]) { const { color, description, title } = embeds[0]; this.log("embeds[0].color", color); @@ -229,7 +228,7 @@ export class WsMessage { if (channel_id !== this.config.ChannelId) return; if (author?.id !== this.config.BotId) return; if (interaction && interaction.user.id !== this.UserId) return; - this.log('[messageCreate]', JSON.stringify(message)); + this.log("[messageCreate]", JSON.stringify(message)); this.messageCreate(message); } @@ -238,7 +237,7 @@ export class WsMessage { if (channel_id !== this.config.ChannelId) return; if (author?.id !== this.config.BotId) return; if (interaction && interaction.user.id !== this.UserId) return; - this.log('[messageUpdate]', JSON.stringify(message)); + this.log("[messageUpdate]", JSON.stringify(message)); this.messageUpdate(message); } @@ -315,6 +314,7 @@ export class WsMessage { hash: uriToHash(attachments[0].url), progress: "done", uri: attachments[0].url, + proxy_url: attachments[0].proxy_url, options: formatOptions(components), }; this.filterMessages(MJmsg); @@ -336,6 +336,7 @@ export class WsMessage { } const MJmsg: MJMessage = { uri: attachments[0].url, + proxy_url: attachments[0].proxy_url, content: content, flags: flags, progress: content2progress(content), diff --git a/src/interfaces/message.ts b/src/interfaces/message.ts index e770efa..f4730de 100644 --- a/src/interfaces/message.ts +++ b/src/interfaces/message.ts @@ -1,5 +1,6 @@ export interface MJMessage { uri: string; + proxy_url?: string; content: string; flags: number; id?: string; diff --git a/src/midjourne.api.ts b/src/midjourne.api.ts index 1eaac0c..a19eee3 100644 --- a/src/midjourne.api.ts +++ b/src/midjourne.api.ts @@ -1,4 +1,14 @@ -import { CustomZoomModalSubmitID, DescribeModalSubmitID, DiscordImage, MJConfig, ModalSubmitID, RemixModalSubmitID, ShortenModalSubmitID, UploadParam, UploadSlot } from "./interfaces"; +import { + CustomZoomModalSubmitID, + DescribeModalSubmitID, + DiscordImage, + MJConfig, + ModalSubmitID, + RemixModalSubmitID, + ShortenModalSubmitID, + UploadParam, + UploadSlot, +} from "./interfaces"; import { CreateQueue } from "./queue"; import { nextNonce, sleep } from "./utls"; import * as fs from "fs"; @@ -40,7 +50,10 @@ export class MidjourneyApi extends Command { } ); if (response.status >= 400) { - console.error("api.error.config", { payload:JSON.stringify(payload), config: this.config }); + console.error("api.error.config", { + payload: JSON.stringify(payload), + config: this.config, + }); } callback && callback(response.status); //discord api rate limit @@ -55,7 +68,7 @@ export class MidjourneyApi extends Command { const payload = await this.imaginePayload(prompt, nonce); return this.safeIteractions(payload); } - async SwitchRemixApi( nonce: string = nextNonce()) { + async SwitchRemixApi(nonce: string = nextNonce()) { const payload = await this.PreferPayload(nonce); return this.safeIteractions(payload); } @@ -134,6 +147,8 @@ export class MidjourneyApi extends Command { flags: number; nonce?: string; }) { + if (!msgId) throw new Error("msgId is empty"); + if (flags === undefined) throw new Error("flags is undefined"); const payload = { type: 3, nonce, @@ -141,7 +156,7 @@ export class MidjourneyApi extends Command { channel_id: this.config.ChannelId, message_flags: flags, message_id: msgId, - application_id: "936929561302675456", + application_id: this.config.BotId, session_id: this.config.SessionId, data: { component_type: 2, @@ -152,25 +167,25 @@ export class MidjourneyApi extends Command { } //FIXME: get SubmitCustomId from discord api async ModalSubmitApi({ - nonce , + nonce, msgId, customId, prompt, - submitCustomId - }:{ + submitCustomId, + }: { nonce: string; msgId: string; customId: string; prompt: string; - submitCustomId:ModalSubmitID; + submitCustomId: ModalSubmitID; }) { var payload = { type: 5, - application_id: "936929561302675456", + application_id: this.config.BotId, channel_id: this.config.ChannelId, guild_id: this.config.ServerId, data: { - id:msgId, + id: msgId, custom_id: customId, components: [ { @@ -179,7 +194,7 @@ export class MidjourneyApi extends Command { { type: 4, custom_id: submitCustomId, - value:prompt, + value: prompt, }, ], }, @@ -187,16 +202,16 @@ export class MidjourneyApi extends Command { }, session_id: this.config.SessionId, nonce, - } - console.log("submitCustomId",JSON.stringify(payload)) + }; + console.log("submitCustomId", JSON.stringify(payload)); return this.safeIteractions(payload); } async RemixApi({ - nonce , + nonce, msgId, customId, prompt, - }:{ + }: { nonce: string; msgId: string; customId: string; @@ -207,73 +222,73 @@ export class MidjourneyApi extends Command { msgId, customId, prompt, - submitCustomId:RemixModalSubmitID - }) + submitCustomId: RemixModalSubmitID, + }); } async ShortenImagineApi({ - nonce , + nonce, msgId, customId, prompt, - }:{ + }: { nonce: string; msgId: string; customId: string; prompt: string; - }){ + }) { return this.ModalSubmitApi({ nonce, msgId, customId, prompt, - submitCustomId:ShortenModalSubmitID - }) + submitCustomId: ShortenModalSubmitID, + }); } - - async DescribeImagineApi({ - nonce , + nonce, msgId, customId, prompt, - }:{ + }: { nonce: string; msgId: string; customId: string; prompt: string; - }){ + }) { return this.ModalSubmitApi({ nonce, msgId, customId, prompt, - submitCustomId:DescribeModalSubmitID - }) + submitCustomId: DescribeModalSubmitID, + }); } async CustomZoomImagineApi({ - nonce , + nonce, msgId, customId, prompt, - }:{ + }: { nonce: string; msgId: string; customId: string; prompt: string; - }){ - customId = customId.replace("MJ::CustomZoom","MJ::OutpaintCustomZoomModal") + }) { + customId = customId.replace( + "MJ::CustomZoom", + "MJ::OutpaintCustomZoomModal" + ); return this.ModalSubmitApi({ nonce, msgId, customId, prompt, - submitCustomId:CustomZoomModalSubmitID - }) + submitCustomId: CustomZoomModalSubmitID, + }); } - async InfoApi(nonce?: string) { const payload = await this.infoPayload(nonce); return this.safeIteractions(payload); diff --git a/src/midjourney.ts b/src/midjourney.ts index 167b202..9abf1f1 100644 --- a/src/midjourney.ts +++ b/src/midjourney.ts @@ -25,16 +25,15 @@ export class Midjourney extends MidjourneyMessage { this.MJApi = new MidjourneyApi(this.config); } async Connect() { - //if auth failed, will throw error - if (this.config.ServerId) - { - await this.MJApi.getCommand('settings'); - } else{ - await this.MJApi.allCommand(); - } if (!this.config.Ws) { return this; } + //if auth failed, will throw error + if (this.config.ServerId) { + await this.MJApi.getCommand("settings"); + } else { + await this.MJApi.allCommand(); + } if (this.wsClient) return this; return new Promise((resolve) => { this.wsClient = new WsMessage(this.config, this.MJApi); @@ -48,19 +47,19 @@ export class Midjourney extends MidjourneyMessage { async init() { await this.Connect(); const settings = await this.Settings(); - if (settings){ - this.log(`settings:`, settings.content); - const remix = settings.options.find((o) => o.label === "Remix mode") + if (settings) { + // this.log(`settings:`, settings.content); + const remix = settings.options.find((o) => o.label === "Remix mode"); if (remix?.style == 3) { - this.config.Remix = true - this.log(`Remix mode enabled`) + this.config.Remix = true; + this.log(`Remix mode enabled`); } } return this; } async Imagine(prompt: string, loading?: LoadingHandler) { prompt = prompt.trim(); - if (!this.wsClient) { + if (!this.config.Ws) { const seed = random(1000000000, 9999999999); prompt = `[${seed}] ${prompt}`; } @@ -71,8 +70,9 @@ export class Midjourney extends MidjourneyMessage { if (httpStatus !== 204) { throw new Error(`ImagineApi failed with status ${httpStatus}`); } - if (this.wsClient) { - return await this.wsClient.waitImageMessage({ nonce, loading }); + if (this.config.Ws) { + const wsClient = await this.getWsClient(); + return await wsClient.waitImageMessage({ nonce, loading, prompt }); } else { this.log(`await generate image`); const msg = await this.WaitMessage(prompt, loading); @@ -80,16 +80,28 @@ export class Midjourney extends MidjourneyMessage { return msg; } } + // check ws enabled && connect + private async getWsClient() { + if (!this.config.Ws) { + throw new Error(`ws not enabled`); + } + if (!this.wsClient) { + await this.Connect(); + } + if (!this.wsClient) { + throw new Error(`ws not connected`); + } + return this.wsClient; + } + async Settings() { + const wsClient = await this.getWsClient(); const nonce = nextNonce(); const httpStatus = await this.MJApi.SettingsApi(nonce); if (httpStatus !== 204) { throw new Error(`ImagineApi failed with status ${httpStatus}`); } - if (this.wsClient) { - return this.wsClient.waitSettings(); - } - return null; + return wsClient.waitSettings(); } async Reset() { const settings = await this.Settings(); @@ -111,15 +123,13 @@ export class Midjourney extends MidjourneyMessage { } async Info() { + const wsClient = await this.getWsClient(); const nonce = nextNonce(); const httpStatus = await this.MJApi.InfoApi(nonce); if (httpStatus !== 204) { throw new Error(`InfoApi failed with status ${httpStatus}`); } - if (this.wsClient) { - return this.wsClient.waitInfo(); - } - return null; + return wsClient.waitInfo(); } async Fast() { @@ -139,40 +149,33 @@ export class Midjourney extends MidjourneyMessage { return null; } async SwitchRemix() { + const wsClient = await this.getWsClient(); const nonce = nextNonce(); const httpStatus = await this.MJApi.SwitchRemixApi(nonce); if (httpStatus !== 204) { throw new Error(`RelaxApi failed with status ${httpStatus}`); } - if (this.wsClient) { - return this.wsClient.waitContent("prefer-remix"); - } - return null; + return wsClient.waitContent("prefer-remix"); } async Describe(imgUri: string) { + const wsClient = await this.getWsClient(); const nonce = nextNonce(); const DcImage = await this.MJApi.UploadImage(imgUri); - this.log(`Describe`, DcImage, "nonce", nonce); const httpStatus = await this.MJApi.DescribeApi(DcImage, nonce); if (httpStatus !== 204) { throw new Error(`DescribeApi failed with status ${httpStatus}`); } - if (this.wsClient) { - return this.wsClient.waitDescribe(nonce); - } - return null; + return wsClient.waitDescribe(nonce); } async Shorten(prompt: string) { + const wsClient = await this.getWsClient(); const nonce = nextNonce(); const httpStatus = await this.MJApi.ShortenApi(prompt, nonce); if (httpStatus !== 204) { throw new Error(`ShortenApi failed with status ${httpStatus}`); } - if (this.wsClient) { - return this.wsClient.waitShorten(nonce); - } - return null; + return wsClient.waitShorten(nonce); } async Variation({ @@ -236,7 +239,6 @@ export class Midjourney extends MidjourneyMessage { flags: number; loading?: LoadingHandler; }) { - this.log("Custom", customId, "msgId", msgId, "content", content); const nonce = nextNonce(); const httpStatus = await this.MJApi.CustomApi({ msgId, @@ -247,10 +249,12 @@ export class Midjourney extends MidjourneyMessage { if (httpStatus !== 204) { throw new Error(`CustomApi failed with status ${httpStatus}`); } - if (this.wsClient) { - return await this.wsClient.waitImageMessage({ + if (this.config.Ws) { + const wsClient = await this.getWsClient(); + return await wsClient.waitImageMessage({ nonce, loading, + prompt: content, onmodal: async (nonde, id) => { if (content === undefined || content === "") { return ""; @@ -271,7 +275,7 @@ export class Midjourney extends MidjourneyMessage { } return newNonce; case "variation": - if(this.config.Remix !== true){ + if (this.config.Remix !== true) { return ""; } customId = toRemixCustom(customId); @@ -352,23 +356,13 @@ export class Midjourney extends MidjourneyMessage { flags: number; loading?: LoadingHandler; }) { - const nonce = nextNonce(); - const httpStatus = await this.MJApi.RerollApi({ + return await this.Custom({ + customId: `MJ::JOB::reroll::0::${hash}::SOLO`, msgId, - hash: hash, + content, flags, - nonce, + loading, }); - if (httpStatus !== 204) { - throw new Error(`RerollApi failed with status ${httpStatus}`); - } - if (this.wsClient) { - return await this.wsClient.waitImageMessage({ nonce, loading }); - } - if (content === undefined || content === "") { - throw new Error(`content is required`); - } - return await this.WaitMessage(content, loading); } Close() {