From 7f536d2034049f55bdc9c867a0bdb5ae0201e834 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 15 May 2023 22:29:46 +0800 Subject: [PATCH 01/11] ws demo --- package.json | 4 ++- src/ws.ts | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 17 +++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 src/ws.ts diff --git a/package.json b/package.json index ae85ed8..cc0edcb 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "homepage": "https://github.com/erictik/midjourney-api#readme", "devDependencies": { "@types/node": "^18.16.0", + "@types/ws": "^8.5.4", "dotenv": "^16.0.3", "prettier": "^2.8.8", "ts-node": "^10.9.1", @@ -43,6 +44,7 @@ "dependencies": { "p-queue": "^6.6.2", "throat": "^6.0.2", - "tslib": "^2.5.0" + "tslib": "^2.5.0", + "ws": "^8.13.0" } } diff --git a/src/ws.ts b/src/ws.ts new file mode 100644 index 0000000..11e7398 --- /dev/null +++ b/src/ws.ts @@ -0,0 +1,81 @@ +import WebSocket from "ws"; +import "dotenv/config"; +export const DISCORD_GATEWAY = + "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream"; +// import type { Inflate } from "zlib"; +import { createInflate, constants as ZlibConstants } from "zlib"; +async function timeout(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +async function main() { + const sss = new WebSocket(DISCORD_GATEWAY); + var heartbeatInterval = 0; + function heartbeat() { + console.log("heartbeat"); + if (sss.readyState !== WebSocket.OPEN) return; + sss.send( + JSON.stringify({ + op: 1, + d: heartbeatInterval++, + }) + ); + setTimeout(heartbeat, 1000 * 40); + } + + sss.on("open", async function open() { + console.log("open"); + sss.send( + JSON.stringify({ + op: 2, + d: { + token: process.env.SALAI_TOKEN, + capabilities: 8189, + properties: { + os: "Mac OS X", + browser: "Chrome", + device: "", + system_locale: "zh-CN", + browser_user_agent: + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36", + browser_version: "112.0.0.0", + os_version: "10.15.7", + referrer: "", + referring_domain: "", + referrer_current: "", + referring_domain_current: "", + release_channel: "stable", + client_build_number: 198192, + client_event_source: null, + }, + compress: false, + }, + }) + ); + heartbeat(); + }); + console.log("sss"); + let zlibChunks: Buffer[] = []; + const inflate = createInflate({ flush: ZlibConstants.Z_SYNC_FLUSH }); + inflate.on("data", (data) => zlibChunks.push(data)); + function handleFlushComplete() { + const data = + zlibChunks.length > 1 ? Buffer.concat(zlibChunks) : zlibChunks[0]; + + zlibChunks = []; + var jsonString = data.toString(); + console.log("jsonString", new Date().toJSON(), jsonString); + } + // inflate.reset(); + sss.on("message", function incoming(data: Buffer) { + inflate.write(data); + // this.#inflate.write(data) + console.log("data"); + if (data.length >= 4 && data.readUInt32BE(data.length - 4) === 0x0ffff) { + console.log("解析 data"); + inflate.flush(ZlibConstants.Z_SYNC_FLUSH, () => handleFlushComplete()); + } + }); +} + +main().catch(console.error); diff --git a/yarn.lock b/yarn.lock index f7b2560..f15d0e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -181,11 +181,23 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.3.tgz#472eaab5f15c1ffdd7f8628bd4c4f753995ec79e" integrity sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ== +"@types/node@*": + version "20.1.4" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.1.4.tgz#83f148d2d1f5fe6add4c53358ba00d97fc4cdb71" + integrity sha512-At4pvmIOki8yuwLtd7BNHl3CiWNbtclUbNtScGx4OHfBd4/oWoJC8KRCIxXwkdndzhxOsPXihrsOoydxBjlE9Q== + "@types/node@^18.16.0": version "18.16.3" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.3.tgz#6bda7819aae6ea0b386ebc5b24bdf602f1b42b01" integrity sha512-OPs5WnnT1xkCBiuQrZA4+YAV4HEJejmHneyraIaxsbev5yCEr6KMwINNFP9wQeFIw8FWcoTqF3vQsa5CDaI+8Q== +"@types/ws@^8.5.4": + version "8.5.4" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5" + integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== + dependencies: + "@types/node" "*" + acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" @@ -357,6 +369,11 @@ v8-compile-cache-lib@^3.0.1: resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== +ws@^8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" From c1089ea536b87c8126bdd8b7edd4a46164b10321 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 16 May 2023 11:34:22 +0800 Subject: [PATCH 02/11] add ws.message --- src/ws.message.ts | 89 +++++++++++++++++++++++++++++++++++++++++++++++ src/ws.ts | 81 ------------------------------------------ 2 files changed, 89 insertions(+), 81 deletions(-) create mode 100644 src/ws.message.ts delete mode 100644 src/ws.ts diff --git a/src/ws.message.ts b/src/ws.message.ts new file mode 100644 index 0000000..56c36a4 --- /dev/null +++ b/src/ws.message.ts @@ -0,0 +1,89 @@ +import WebSocket from "ws"; +import { createInflate, Inflate, constants as ZlibConstants } from "zlib"; +import { + MessageConfig, + MessageConfigParam, + DefaultMessageConfig, +} from "./interfaces"; + +export class WsMessage { + DISCORD_GATEWAY = + "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream"; + ws: WebSocket; + private zlibChunks: Buffer[] = []; + public config: MessageConfig; + private inflate: Inflate; + + constructor(defaults: MessageConfigParam) { + const { ChannelId, SalaiToken } = defaults; + if (!ChannelId || !SalaiToken) { + throw new Error("ChannelId and SalaiToken are required"); + } + + this.config = { + ...DefaultMessageConfig, + ...defaults, + }; + this.ws = new WebSocket(this.DISCORD_GATEWAY); + this.ws.on("open", this.open); + this.ws.on("message", this.incomingMessage); + this.inflate = createInflate({ flush: ZlibConstants.Z_SYNC_FLUSH }); + this.inflate.on("data", (data) => this.zlibChunks.push(data)); + } + heartbeatInterval = 0; + private heartbeat() { + console.log("heartbeat"); + if (this.ws.readyState !== WebSocket.OPEN) return; + this.heartbeatInterval++; + this.ws.send( + JSON.stringify({ + op: 1, + d: null, + }) + ); + setTimeout(this.heartbeat, 1000 * 40); + } + private async open() { + this.ws.send( + JSON.stringify({ + op: 2, + d: { + token: this.config.SalaiToken, + capabilities: 8189, + properties: { + os: "Mac OS X", + browser: "Chrome", + device: "", + }, + compress: false, + }, + }) + ); + this.heartbeat(); + } + async timeout(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); + } + private incomingMessage(data: Buffer) { + this.inflate.write(data); + if (data.length >= 4 && data.readUInt32BE(data.length - 4) === 0x0ffff) { + this.inflate.flush(ZlibConstants.Z_SYNC_FLUSH, this.handleFlushComplete); + } + } + private handleFlushComplete() { + const data = + this.zlibChunks.length > 1 + ? Buffer.concat(this.zlibChunks) + : this.zlibChunks[0]; + + this.zlibChunks = []; + this.onMessage(data); + } + private onMessage(data: Buffer) { + var jsonString = data.toString(); + console.log("jsonString", new Date().toJSON(), jsonString); + } + protected async log(...args: any[]) { + this.config.Debug && console.debug(...args, new Date().toISOString()); + } +} diff --git a/src/ws.ts b/src/ws.ts deleted file mode 100644 index 11e7398..0000000 --- a/src/ws.ts +++ /dev/null @@ -1,81 +0,0 @@ -import WebSocket from "ws"; -import "dotenv/config"; -export const DISCORD_GATEWAY = - "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream"; -// import type { Inflate } from "zlib"; -import { createInflate, constants as ZlibConstants } from "zlib"; -async function timeout(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -async function main() { - const sss = new WebSocket(DISCORD_GATEWAY); - var heartbeatInterval = 0; - function heartbeat() { - console.log("heartbeat"); - if (sss.readyState !== WebSocket.OPEN) return; - sss.send( - JSON.stringify({ - op: 1, - d: heartbeatInterval++, - }) - ); - setTimeout(heartbeat, 1000 * 40); - } - - sss.on("open", async function open() { - console.log("open"); - sss.send( - JSON.stringify({ - op: 2, - d: { - token: process.env.SALAI_TOKEN, - capabilities: 8189, - properties: { - os: "Mac OS X", - browser: "Chrome", - device: "", - system_locale: "zh-CN", - browser_user_agent: - "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36", - browser_version: "112.0.0.0", - os_version: "10.15.7", - referrer: "", - referring_domain: "", - referrer_current: "", - referring_domain_current: "", - release_channel: "stable", - client_build_number: 198192, - client_event_source: null, - }, - compress: false, - }, - }) - ); - heartbeat(); - }); - console.log("sss"); - let zlibChunks: Buffer[] = []; - const inflate = createInflate({ flush: ZlibConstants.Z_SYNC_FLUSH }); - inflate.on("data", (data) => zlibChunks.push(data)); - function handleFlushComplete() { - const data = - zlibChunks.length > 1 ? Buffer.concat(zlibChunks) : zlibChunks[0]; - - zlibChunks = []; - var jsonString = data.toString(); - console.log("jsonString", new Date().toJSON(), jsonString); - } - // inflate.reset(); - sss.on("message", function incoming(data: Buffer) { - inflate.write(data); - // this.#inflate.write(data) - console.log("data"); - if (data.length >= 4 && data.readUInt32BE(data.length - 4) === 0x0ffff) { - console.log("解析 data"); - inflate.flush(ZlibConstants.Z_SYNC_FLUSH, () => handleFlushComplete()); - } - }); -} - -main().catch(console.error); From c5264bc19c0721894a8ba278bb776c4ebe0419ea Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 16 May 2023 15:53:48 +0800 Subject: [PATCH 03/11] add on event --- src/ws.message copy.ts | 68 +++++++++++++++++++++++++ src/ws.message.ts | 111 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 166 insertions(+), 13 deletions(-) create mode 100644 src/ws.message copy.ts diff --git a/src/ws.message copy.ts b/src/ws.message copy.ts new file mode 100644 index 0000000..a32bcbe --- /dev/null +++ b/src/ws.message copy.ts @@ -0,0 +1,68 @@ +import WebSocket from "ws"; +import "dotenv/config"; +export const DISCORD_GATEWAY = + "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream"; +import { createInflate, constants as ZlibConstants } from "zlib"; +async function timeout(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +async function main() { + const sss = new WebSocket(DISCORD_GATEWAY); + var heartbeatInterval = 0; + function heartbeat() { + console.log("heartbeat"); + if (sss.readyState !== WebSocket.OPEN) return; + sss.send( + JSON.stringify({ + op: 1, + d: heartbeatInterval++, + }) + ); + setTimeout(heartbeat, 1000 * 40); + } + + sss.on("open", async function open() { + console.log("open"); + sss.send( + JSON.stringify({ + op: 2, + d: { + token: process.env.SALAI_TOKEN, + capabilities: 8189, + properties: { + os: "Mac OS X", + browser: "Chrome", + device: "", + }, + compress: false, + }, + }) + ); + heartbeat(); + }); + console.log("sss"); + let zlibChunks: Buffer[] = []; + const inflate = createInflate({ flush: ZlibConstants.Z_SYNC_FLUSH }); + inflate.on("data", (data) => zlibChunks.push(data)); + function handleFlushComplete() { + const data = + zlibChunks.length > 1 ? Buffer.concat(zlibChunks) : zlibChunks[0]; + + zlibChunks = []; + var jsonString = data.toString(); + console.log("jsonString", new Date().toJSON(), jsonString); + } + // inflate.reset(); + sss.on("message", function incoming(data: Buffer) { + inflate.write(data); + // this.#inflate.write(data) + console.log("data"); + if (data.length >= 4 && data.readUInt32BE(data.length - 4) === 0x0ffff) { + console.log("解析 data"); + inflate.flush(ZlibConstants.Z_SYNC_FLUSH, () => handleFlushComplete()); + } + }); +} + +main().catch(console.error); diff --git a/src/ws.message.ts b/src/ws.message.ts index 56c36a4..5ed64ff 100644 --- a/src/ws.message.ts +++ b/src/ws.message.ts @@ -10,9 +10,14 @@ export class WsMessage { DISCORD_GATEWAY = "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream"; ws: WebSocket; + MJId = "936929561302675456"; private zlibChunks: Buffer[] = []; public config: MessageConfig; private inflate: Inflate; + private event: Array<{ event: string; callback: (message: any) => void }> = + []; + private reconnectTime: boolean[] = []; + private heartbeatInterval = 0; constructor(defaults: MessageConfigParam) { const { ChannelId, SalaiToken } = defaults; @@ -25,25 +30,47 @@ export class WsMessage { ...defaults, }; this.ws = new WebSocket(this.DISCORD_GATEWAY); - this.ws.on("open", this.open); - this.ws.on("message", this.incomingMessage); + this.ws.on("open", this.open.bind(this)); + + this.ws.on("message", this.incomingMessage.bind(this)); + this.inflate = createInflate({ flush: ZlibConstants.Z_SYNC_FLUSH }); this.inflate.on("data", (data) => this.zlibChunks.push(data)); } - heartbeatInterval = 0; - private heartbeat() { - console.log("heartbeat"); + + private reconnect() { + //reconnect + this.ws = new WebSocket(this.DISCORD_GATEWAY); + this.ws.on("open", this.open.bind(this)); + } + + private async heartbeat(num: number) { + if (this.reconnectTime[num]) return; if (this.ws.readyState !== WebSocket.OPEN) return; this.heartbeatInterval++; this.ws.send( JSON.stringify({ op: 1, - d: null, + d: this.heartbeatInterval, }) ); - setTimeout(this.heartbeat, 1000 * 40); + await this.timeout(1000 * 40); + this.heartbeat(num); } private async open() { + const num = this.reconnectTime.length; + this.log("open", num); + this.reconnectTime.push(false); + this.auth(); + this.ws.onclose = (event: WebSocket.CloseEvent) => { + this.log("close", event); + this.reconnectTime[num] = true; + this.reconnect(); + }; + await this.timeout(1000 * 10); + this.heartbeat(num); + } + private auth() { this.ws.send( JSON.stringify({ op: 2, @@ -59,7 +86,6 @@ export class WsMessage { }, }) ); - this.heartbeat(); } async timeout(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); @@ -67,7 +93,10 @@ export class WsMessage { private incomingMessage(data: Buffer) { this.inflate.write(data); if (data.length >= 4 && data.readUInt32BE(data.length - 4) === 0x0ffff) { - this.inflate.flush(ZlibConstants.Z_SYNC_FLUSH, this.handleFlushComplete); + this.inflate.flush( + ZlibConstants.Z_SYNC_FLUSH, + this.handleFlushComplete.bind(this) + ); } } private handleFlushComplete() { @@ -77,13 +106,69 @@ export class WsMessage { : this.zlibChunks[0]; this.zlibChunks = []; - this.onMessage(data); + this.parseMessage(data); } - private onMessage(data: Buffer) { + private parseMessage(data: Buffer) { var jsonString = data.toString(); - console.log("jsonString", new Date().toJSON(), jsonString); + const messageInfo = JSON.parse(jsonString); + this.log("has message"); + if ( + !( + messageInfo.t === "MESSAGE_CREATE" || messageInfo.t === "MESSAGE_UPDATE" + ) + ) + return; + const message = messageInfo.d; + const { author, content, channel_id, embeds } = message; + if (author.id === this.MJId) return; + if (channel_id !== this.config.ChannelId) return; } protected async log(...args: any[]) { - this.config.Debug && console.debug(...args, new Date().toISOString()); + this.config.Debug && console.info(...args, new Date().toISOString()); + } + + on(event: string, callback: (message: any) => void) { + this.event.push({ event, callback }); + } + once(event: string, callback: (message: any) => void) { + const once = (message: any) => { + this.remove(event, once); + callback(message); + }; + this.event.push({ event, callback: once }); + } + remove(event: string, callback: (message: any) => void) { + this.event = this.event.filter( + (e) => e.event !== event && e.callback !== callback + ); + } + removeEvent(event: string) { + this.event = this.event.filter((e) => e.event !== event); + } + onMessage(callback: (message: any) => void) { + this.event.push({ event: "message", callback }); + } + onInfo(callback: (message: any) => void) { + this.event.push({ event: "info", callback }); + } + onceInfo(callback: (message: any) => void) { + const once = (message: any) => { + this.remove("info", once); + callback(message); + }; + this.event.push({ event: "info", callback: once }); + } + removeInfo(callback: (message: any) => void) { + this.remove("info", callback); + } + onImagine(prompt: string, callback: (message: any) => void) { + this.event.push({ event: "imagine", callback }); + } + onceImagine(prompt: string, callback: (message: any) => void) { + const once = (message: any) => { + this.remove("imagine", once); + callback(message); + }; + this.event.push({ event: "imagine", callback: once }); } } From b635a4434adb37fc850b388cdca819904f3f9938 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 16 May 2023 15:54:12 +0800 Subject: [PATCH 04/11] rename --- src/interfaces/{message.config.ts => config.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/interfaces/{message.config.ts => config.ts} (100%) diff --git a/src/interfaces/message.config.ts b/src/interfaces/config.ts similarity index 100% rename from src/interfaces/message.config.ts rename to src/interfaces/config.ts From f738411c30340a50f208b7c61ad4924336736ffa Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 16 May 2023 15:54:29 +0800 Subject: [PATCH 05/11] del copy --- src/ws.message copy.ts | 68 ------------------------------------------ 1 file changed, 68 deletions(-) delete mode 100644 src/ws.message copy.ts diff --git a/src/ws.message copy.ts b/src/ws.message copy.ts deleted file mode 100644 index a32bcbe..0000000 --- a/src/ws.message copy.ts +++ /dev/null @@ -1,68 +0,0 @@ -import WebSocket from "ws"; -import "dotenv/config"; -export const DISCORD_GATEWAY = - "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream"; -import { createInflate, constants as ZlibConstants } from "zlib"; -async function timeout(ms: number) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -async function main() { - const sss = new WebSocket(DISCORD_GATEWAY); - var heartbeatInterval = 0; - function heartbeat() { - console.log("heartbeat"); - if (sss.readyState !== WebSocket.OPEN) return; - sss.send( - JSON.stringify({ - op: 1, - d: heartbeatInterval++, - }) - ); - setTimeout(heartbeat, 1000 * 40); - } - - sss.on("open", async function open() { - console.log("open"); - sss.send( - JSON.stringify({ - op: 2, - d: { - token: process.env.SALAI_TOKEN, - capabilities: 8189, - properties: { - os: "Mac OS X", - browser: "Chrome", - device: "", - }, - compress: false, - }, - }) - ); - heartbeat(); - }); - console.log("sss"); - let zlibChunks: Buffer[] = []; - const inflate = createInflate({ flush: ZlibConstants.Z_SYNC_FLUSH }); - inflate.on("data", (data) => zlibChunks.push(data)); - function handleFlushComplete() { - const data = - zlibChunks.length > 1 ? Buffer.concat(zlibChunks) : zlibChunks[0]; - - zlibChunks = []; - var jsonString = data.toString(); - console.log("jsonString", new Date().toJSON(), jsonString); - } - // inflate.reset(); - sss.on("message", function incoming(data: Buffer) { - inflate.write(data); - // this.#inflate.write(data) - console.log("data"); - if (data.length >= 4 && data.readUInt32BE(data.length - 4) === 0x0ffff) { - console.log("解析 data"); - inflate.flush(ZlibConstants.Z_SYNC_FLUSH, () => handleFlushComplete()); - } - }); -} - -main().catch(console.error); From e272a792cc96c97cdf41d0581fa5ee717265c11d Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 16 May 2023 16:38:33 +0800 Subject: [PATCH 06/11] add Star History --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 45e232e..4720251 100644 --- a/README.md +++ b/README.md @@ -85,3 +85,8 @@ npx tsx example/upscale.ts ```bash npx tsx example/variation.ts ``` + + + +## Star History +[![Star History Chart](https://api.star-history.com/svg?repos=erictik/midjourney-api&type=Date)](https://star-history.com/#erictik/midjourney-api&Date) From aed2d30886fa4eeb57a1bab909fb3d6b80ef774f Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 16 May 2023 16:38:43 +0800 Subject: [PATCH 07/11] ES2022 --- tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsconfig.json b/tsconfig.json index f55a657..6e6e23a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,7 @@ // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, + "target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, "lib": [ "es2021", "DOM", From 35bc7db97ac49be29766872e9610323aa5e43b6a Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 17 May 2023 14:29:27 +0800 Subject: [PATCH 08/11] ws waitMessage --- example/imagine-err.ts | 30 +++++ example/imagine-ws.ts | 36 ++++++ example/message.ts | 8 +- package.json | 2 + src/index.ts | 1 + src/interfaces/config.ts | 2 + src/interfaces/index.ts | 2 +- src/interfaces/message.ts | 22 ++-- src/midjourney.ts | 38 +++++-- src/ws.message.ts | 204 +++++++++++++++++++++++++++++----- yarn.lock | 224 ++++++++++++++++++++++++++++++++++++++ 11 files changed, 523 insertions(+), 46 deletions(-) create mode 100644 example/imagine-err.ts create mode 100644 example/imagine-ws.ts diff --git a/example/imagine-err.ts b/example/imagine-err.ts new file mode 100644 index 0000000..9223019 --- /dev/null +++ b/example/imagine-err.ts @@ -0,0 +1,30 @@ +import "dotenv/config"; +import { Midjourney } from "../src"; +/** + * + * a simple example of how to use the imagine command + * ``` + * npx tsx example/imagine-err.ts + * ``` + */ +async function main() { + const client = new Midjourney({ + ServerId: process.env.SERVER_ID, + ChannelId: process.env.CHANNEL_ID, + SalaiToken: process.env.SALAI_TOKEN, + Debug: true, + SessionId: process.env.SALAI_TOKEN || "8bb7f5b79c7a49f7d0824ab4b8773a81", + }); + + const msg = await client.Imagine( + "A little white elephant --ar1:1", + (uri: string, progress: string) => { + console.log("loading", uri, "progress", progress); + } + ); + console.log({ msg }); +} +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/example/imagine-ws.ts b/example/imagine-ws.ts new file mode 100644 index 0000000..f92783c --- /dev/null +++ b/example/imagine-ws.ts @@ -0,0 +1,36 @@ +import "dotenv/config"; +import { Midjourney } from "../src"; +/** + * + * a simple example of using the imagine api with ws + * ``` + * npx tsx example/imagine-ws.ts + * ``` + */ +async function main() { + const client = new Midjourney({ + ServerId: process.env.SERVER_ID, + ChannelId: process.env.CHANNEL_ID, + SalaiToken: process.env.SALAI_TOKEN, + Debug: true, + Ws: true, + }); + await client.init(); + const msg = await client.Imagine( + "A little white dog", + (uri: string, progress: string) => { + console.log("loading", uri, "progress", progress); + } + ); + console.log({ msg }); +} +main() + .then(() => { + console.log("finished"); + process.exit(0); + }) + .catch((err) => { + console.log("finished"); + console.error(err); + process.exit(1); + }); diff --git a/example/message.ts b/example/message.ts index 827b445..69975c6 100644 --- a/example/message.ts +++ b/example/message.ts @@ -1,5 +1,5 @@ import "dotenv/config"; -import { Midjourney, MidjourneyMessage } from "../src"; +import { WsMessage } from "../src"; /** * * a simple example of how to use the imagine command @@ -8,12 +8,12 @@ import { Midjourney, MidjourneyMessage } from "../src"; * ``` */ async function main() { - const client = new MidjourneyMessage({ + const client = new WsMessage({ ChannelId: process.env.CHANNEL_ID, SalaiToken: process.env.SALAI_TOKEN, + Debug: true, }); - const msg = await client.RetrieveMessages(); - console.log({ msg }); + console.log("client"); } main().catch((err) => { console.error(err); diff --git a/package.json b/package.json index cc0edcb..3febefb 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,8 @@ "typescript": "^5.0.4" }, "dependencies": { + "discord-api-types": "^0.37.42", + "discord.js": "^14.11.0", "p-queue": "^6.6.2", "throat": "^6.0.2", "tslib": "^2.5.0", diff --git a/src/index.ts b/src/index.ts index fa07743..804ab72 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ export * from "./midjourney"; export * from "./midjourney.message"; +export * from "./ws.message"; export * from "./interfaces/index"; diff --git a/src/interfaces/config.ts b/src/interfaces/config.ts index 1950ff2..8943526 100644 --- a/src/interfaces/config.ts +++ b/src/interfaces/config.ts @@ -4,6 +4,7 @@ export interface MessageConfig { Debug: boolean; Limit: number; MaxWait: number; + Ws?: boolean; } export interface MessageConfigParam { ChannelId: string; @@ -11,6 +12,7 @@ export interface MessageConfigParam { Debug?: boolean; Limit?: number; MaxWait?: number; + Ws?: boolean; } export interface MidjourneyConfig extends MessageConfig { ServerId: string; diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts index 42cd3bd..e028fc6 100644 --- a/src/interfaces/index.ts +++ b/src/interfaces/index.ts @@ -1,2 +1,2 @@ export * from "./message"; -export * from "./message.config"; +export * from "./config"; diff --git a/src/interfaces/message.ts b/src/interfaces/message.ts index 0edbd0a..15818e6 100644 --- a/src/interfaces/message.ts +++ b/src/interfaces/message.ts @@ -1,14 +1,22 @@ -export interface Message { - text: string; - img: string; -} - export interface MJMessage { - id: string; uri: string; - hash: string; content: string; + id?: string; + hash?: string; progress?: string; } export type LoadingHandler = (uri: string, progress: string) => void; + +export interface WaitMjEvent { + type: "imagine" | "upscale" | "variation" | "info"; + nonce: string; + prompt?: string; + id?: string; + index?: number; +} +export interface WsEventMsg { + error?: Error; + message?: MJMessage; +} +export type ImageEventType = "imagine" | "upscale" | "variation"; diff --git a/src/midjourney.ts b/src/midjourney.ts index a0408d1..32852e9 100644 --- a/src/midjourney.ts +++ b/src/midjourney.ts @@ -1,3 +1,4 @@ +import { Snowflake, SnowflakeUtil } from "discord.js"; import { DefaultMidjourneyConfig, LoadingHandler, @@ -7,11 +8,13 @@ import { import { MidjourneyMessage } from "./midjourney.message"; import { CreateQueue } from "./queue"; import { random, sleep } from "./utls"; +import { WsMessage } from "./ws.message"; export class Midjourney extends MidjourneyMessage { private ApiQueue = CreateQueue(1); public config: MidjourneyConfig; + private wsClient?: WsMessage; constructor(defaults: MidjourneyConfigParam) { - const { ServerId, SalaiToken, ChannelId } = defaults; + const { ServerId, SalaiToken, ChannelId, Ws } = defaults; if (!ServerId || !SalaiToken || !ChannelId) { throw new Error("ServerId, ChannelId and SalaiToken are required"); } @@ -21,22 +24,37 @@ export class Midjourney extends MidjourneyMessage { ...defaults, }; } + async init() { + if (this.wsClient) return this; + return new Promise((resolve) => { + this.wsClient = new WsMessage(this.config); + this.wsClient.once("ready", () => { + resolve(this); + }); + }); + } async Imagine(prompt: string, loading?: LoadingHandler) { if (!prompt.includes("--seed")) { const speed = random(1000, 9999); prompt = `${prompt} --seed ${speed}`; } - this.log(`Imagine`, prompt); - const httpStatus = await this.ImagineApi(prompt); + const nonce = SnowflakeUtil.generate().toString(); + this.log(`Imagine`, prompt, "nonce", nonce); + const httpStatus = await this.ImagineApi(prompt, nonce); if (httpStatus !== 204) { throw new Error(`ImagineApi failed with status ${httpStatus}`); } - this.log(`await generate image`); - const msg = await this.WaitMessage(prompt, loading); - this.log(`image generated`, prompt, msg?.uri); - return msg; + if (this.wsClient) { + return await this.wsClient.waitMessage(nonce, loading); + } else { + this.log(`await generate image`); + const msg = await this.WaitMessage(prompt, loading); + this.log(`image generated`, prompt, msg?.uri); + return msg; + } } + // limit the number of concurrent interactions protected async safeIteractions(payload: any) { return this.ApiQueue.addTask( @@ -76,7 +94,10 @@ export class Midjourney extends MidjourneyMessage { } } - async ImagineApi(prompt: string) { + async ImagineApi( + prompt: string, + nonce: Snowflake = SnowflakeUtil.generate().toString() + ) { const payload = { type: 2, application_id: "936929561302675456", @@ -117,6 +138,7 @@ export class Midjourney extends MidjourneyMessage { }, attachments: [], }, + nonce, }; return this.safeIteractions(payload); } diff --git a/src/ws.message.ts b/src/ws.message.ts index 5ed64ff..a6a280f 100644 --- a/src/ws.message.ts +++ b/src/ws.message.ts @@ -4,18 +4,25 @@ import { MessageConfig, MessageConfigParam, DefaultMessageConfig, + WaitMjEvent, + MJMessage, + LoadingHandler, + WsEventMsg, + ImageEventType, } from "./interfaces"; +import { error } from "console"; export class WsMessage { DISCORD_GATEWAY = "wss://gateway.discord.gg/?v=9&encoding=json&compress=zlib-stream"; ws: WebSocket; - MJId = "936929561302675456"; + MJBotId = "936929561302675456"; private zlibChunks: Buffer[] = []; public config: MessageConfig; private inflate: Inflate; private event: Array<{ event: string; callback: (message: any) => void }> = []; + private waitMjEvent: Array = []; private reconnectTime: boolean[] = []; private heartbeatInterval = 0; @@ -32,8 +39,6 @@ export class WsMessage { this.ws = new WebSocket(this.DISCORD_GATEWAY); this.ws.on("open", this.open.bind(this)); - this.ws.on("message", this.incomingMessage.bind(this)); - this.inflate = createInflate({ flush: ZlibConstants.Z_SYNC_FLUSH }); this.inflate.on("data", (data) => this.zlibChunks.push(data)); } @@ -57,11 +62,13 @@ export class WsMessage { await this.timeout(1000 * 40); this.heartbeat(num); } + // After opening ws private async open() { const num = this.reconnectTime.length; this.log("open", num); this.reconnectTime.push(false); this.auth(); + this.ws.on("message", this.incomingMessage.bind(this)); this.ws.onclose = (event: WebSocket.CloseEvent) => { this.log("close", event); this.reconnectTime[num] = true; @@ -70,6 +77,7 @@ export class WsMessage { await this.timeout(1000 * 10); this.heartbeat(num); } + // auth private auth() { this.ws.send( JSON.stringify({ @@ -108,25 +116,141 @@ export class WsMessage { this.zlibChunks = []; this.parseMessage(data); } + // parse message from ws private parseMessage(data: Buffer) { var jsonString = data.toString(); - const messageInfo = JSON.parse(jsonString); - this.log("has message"); - if ( - !( - messageInfo.t === "MESSAGE_CREATE" || messageInfo.t === "MESSAGE_UPDATE" - ) - ) + const msg = JSON.parse(jsonString); + if (msg.t === null || msg.t === "READY_SUPPLEMENTAL") return; + if (msg.t === "READY") { + this.emit("ready", null); return; - const message = messageInfo.d; - const { author, content, channel_id, embeds } = message; - if (author.id === this.MJId) return; + } + if (!(msg.t === "MESSAGE_CREATE" || msg.t === "MESSAGE_UPDATE")) return; + const message = msg.d; + const { + channel_id, + content, + application_id, + embeds, + id, + nonce, + author, + attachments, + } = message; + if (!(author && author.id === this.MJBotId)) return; if (channel_id !== this.config.ChannelId) return; + this.log("has message", content, nonce, id); + //done image + if (msg.t === "MESSAGE_CREATE" && !nonce && !application_id) { + this.log("done image"); + //match done image + const MJmsg: MJMessage = { + id, + hash: this.uriToHash(attachments[0].url), + progress: "done", + uri: attachments[0].url, + content, + }; + this.filterMessages(MJmsg); + return; + } + + //waiting start image or info or error + if (nonce && msg.t === "MESSAGE_CREATE") { + this.log("waiting start image or info or error"); + this.updateMjEventIdByNonce(id, nonce); + } + + //processing image + { + this.log("processing image"); + const index = this.waitMjEvent.findIndex((e) => e.id === id); + if (index < 0 || !this.waitMjEvent[index]) { + return; + } + const event = this.waitMjEvent[index]; + this.waitMjEvent[index].prompt = content; + if (!attachments || attachments.length === 0) { + this.log("wait", { + id, + nonce, + content, + event, + }); + return; + } + const MJmsg: MJMessage = { + uri: attachments[0].url, + content, + progress: this.content2progress(content), + }; + const eventMsg: WsEventMsg = { + message: MJmsg, + }; + this.emitImage(event.type, eventMsg); + } + // this.log(message); + // this.log("message", { + // id, + // nonce, + // attachments, + // content, + // embeds, + // }); + } + + protected content2progress(content: string) { + const regex = /\(([^)]+)\)/; // matches the value inside the first parenthesis + const match = content.match(regex); + let progress = ""; + if (match) { + progress = match[1]; + } + return progress; + } + + private filterMessages(MJmsg: MJMessage) { + // str.replace(/<@(.*?)>.*$/, "") + const index = this.waitMjEvent.findIndex( + (e) => + e.prompt?.replace(/<@(.*?)>.*$/, "") === + MJmsg.content.replace(/<@(.*?)>.*$/, "") + ); + if (index < 0) { + this.log("FilterMessages not found", MJmsg, this.waitMjEvent); + return; + } + const event = this.waitMjEvent[index]; + if (!event) { + this.log("FilterMessages not found", MJmsg, this.waitMjEvent); + return; + } + const eventMsg: WsEventMsg = { + message: MJmsg, + }; + this.emitImage(event.type, eventMsg); + } + + private updateMjEventIdByNonce(id: string, nonce: string) { + const index = this.waitMjEvent.findIndex((e) => e.nonce === nonce); + if (index < 0) return; + this.waitMjEvent[index].id = id; + this.log("updateMjEventIdByNonce success", this.waitMjEvent[index]); } + uriToHash(uri: string) { + return uri.split("_").pop()?.split(".")[0] ?? ""; + } + protected async log(...args: any[]) { this.config.Debug && console.info(...args, new Date().toISOString()); } + emit(event: string, message: any) { + this.event + .filter((e) => e.event === event) + .forEach((e) => e.callback(message)); + } + on(event: string, callback: (message: any) => void) { this.event.push({ event, callback }); } @@ -145,12 +269,6 @@ export class WsMessage { removeEvent(event: string) { this.event = this.event.filter((e) => e.event !== event); } - onMessage(callback: (message: any) => void) { - this.event.push({ event: "message", callback }); - } - onInfo(callback: (message: any) => void) { - this.event.push({ event: "info", callback }); - } onceInfo(callback: (message: any) => void) { const once = (message: any) => { this.remove("info", once); @@ -161,14 +279,48 @@ export class WsMessage { removeInfo(callback: (message: any) => void) { this.remove("info", callback); } - onImagine(prompt: string, callback: (message: any) => void) { - this.event.push({ event: "imagine", callback }); + private removeWaitMjEvent(nonce: string) { + this.waitMjEvent = this.waitMjEvent.filter((e) => e.nonce !== nonce); } - onceImagine(prompt: string, callback: (message: any) => void) { - const once = (message: any) => { - this.remove("imagine", once); - callback(message); + + private emitImage(type: ImageEventType, message: WsEventMsg) { + this.emit(type, message); + } + onceImage( + type: ImageEventType, + nonce: string, + callback: (data: WsEventMsg) => void + ) { + const once = (data: WsEventMsg) => { + const { message, error } = data; + if (error || (message && message.progress === "done")) { + this.log("onceImage", type, "done", data, error); + this.remove(type, once); + this.removeWaitMjEvent(nonce); + } + callback(data); }; - this.event.push({ event: "imagine", callback: once }); + this.waitMjEvent.push({ type, nonce }); + this.event.push({ event: type, callback: once }); + } + onceImagine(nonce: string, callback: (data: WsEventMsg) => void) { + this.onceImage("imagine", nonce, callback); + } + + async waitMessage(nonce: string, loading?: LoadingHandler) { + return new Promise((resolve, reject) => { + this.onceImagine(nonce, ({ message, error }) => { + if (error) { + reject(error); + return; + } + if (message && message.progress === "done") { + resolve(message); + return; + } + this.log("loading"); + message && loading && loading(message.uri, message.progress || ""); + }); + }); } } diff --git a/yarn.lock b/yarn.lock index f15d0e1..6f322a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,6 +9,65 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" +"@discordjs/builders@^1.6.3": + version "1.6.3" + resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.6.3.tgz#994b4fe57e77b47096f74bb5a1f664870a930a43" + integrity sha512-CTCh8NqED3iecTNuiz49mwSsrc2iQb4d0MjMdmS/8pb69Y4IlzJ/DIy/p5GFlgOrFbNO2WzMHkWKQSiJ3VNXaw== + dependencies: + "@discordjs/formatters" "^0.3.1" + "@discordjs/util" "^0.3.1" + "@sapphire/shapeshift" "^3.8.2" + discord-api-types "^0.37.41" + fast-deep-equal "^3.1.3" + ts-mixer "^6.0.3" + tslib "^2.5.0" + +"@discordjs/collection@^1.5.1": + version "1.5.1" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.1.tgz#bc7ca557838dc29247bf19860426637f103bc383" + integrity sha512-aWEc9DCf3TMDe9iaJoOnO2+JVAjeRNuRxPZQA6GVvBf+Z3gqUuWYBy2NWh4+5CLYq5uoc3MOvUQ5H5m8CJBqOA== + +"@discordjs/formatters@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.3.1.tgz#81393cf25e6e3223361061629752ea727475e842" + integrity sha512-M7X4IGiSeh4znwcRGcs+49B5tBkNDn4k5bmhxJDAUhRxRHTiFAOTVUNQ6yAKySu5jZTnCbSvTYHW3w0rAzV1MA== + dependencies: + discord-api-types "^0.37.41" + +"@discordjs/rest@^1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.7.1.tgz#eeef0e71a37c95fa27962129729b2aa9de8e3752" + integrity sha512-Ofa9UqT0U45G/eX86cURQnX7gzOJLG2oC28VhIk/G6IliYgQF7jFByBJEykPSHE4MxPhqCleYvmsrtfKh1nYmQ== + dependencies: + "@discordjs/collection" "^1.5.1" + "@discordjs/util" "^0.3.0" + "@sapphire/async-queue" "^1.5.0" + "@sapphire/snowflake" "^3.4.2" + discord-api-types "^0.37.41" + file-type "^18.3.0" + tslib "^2.5.0" + undici "^5.22.0" + +"@discordjs/util@^0.3.0", "@discordjs/util@^0.3.1": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-0.3.1.tgz#4e8737e1dcff7e9f5eccc3116fb44755b65b1e97" + integrity sha512-HxXKYKg7vohx2/OupUN/4Sd02Ev3PBJ5q0gtjdcvXb0ErCva8jNHWfe/v5sU3UKjIB/uxOhc+TDOnhqffj9pRA== + +"@discordjs/ws@^0.8.3": + version "0.8.3" + resolved "https://registry.yarnpkg.com/@discordjs/ws/-/ws-0.8.3.tgz#77db8d563b731a2198c1b40f63b1ef8d230504f7" + integrity sha512-hcYtppanjHecbdNyCKQNH2I4RP9UrphDgmRgLYrATEQF1oo4sYSve7ZmGsBEXSzH72MO2tBPdWSThunbxUVk0g== + dependencies: + "@discordjs/collection" "^1.5.1" + "@discordjs/rest" "^1.7.1" + "@discordjs/util" "^0.3.1" + "@sapphire/async-queue" "^1.5.0" + "@types/ws" "^8.5.4" + "@vladfrangu/async_event_emitter" "^2.2.1" + discord-api-types "^0.37.41" + tslib "^2.5.0" + ws "^8.13.0" + "@esbuild-kit/cjs-loader@^2.4.2": version "2.4.2" resolved "https://registry.npmjs.org/@esbuild-kit/cjs-loader/-/cjs-loader-2.4.2.tgz" @@ -161,6 +220,29 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" +"@sapphire/async-queue@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.0.tgz#2f255a3f186635c4fb5a2381e375d3dfbc5312d8" + integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA== + +"@sapphire/shapeshift@^3.8.2": + version "3.9.0" + resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.9.0.tgz#5425d90b5f394b44b13928006f6b78c5294c0ce9" + integrity sha512-iJpHmjAdwX9aSL6MvFpVyo+tkokDtInmSjoJHbz/k4VJfnim3DjvG0hgGEKWtWZgCu45RaLgcoNgR1fCPdIz3w== + dependencies: + fast-deep-equal "^3.1.3" + lodash "^4.17.21" + +"@sapphire/snowflake@^3.4.2": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.5.1.tgz#254521c188b49e8b2d4cc048b475fb2b38737fec" + integrity sha512-BxcYGzgEsdlG0dKAyOm0ehLGm2CafIrfQTZGWgkfKYbj+pNNsorZ7EotuZukc2MT70E0UbppVbtpBrqpzVzjNA== + +"@tokenizer/token@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276" + integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A== + "@tsconfig/node10@^1.0.7": version "1.0.9" resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.9.tgz#df4907fc07a886922637b15e02d4cebc4c0021b2" @@ -198,6 +280,11 @@ dependencies: "@types/node" "*" +"@vladfrangu/async_event_emitter@^2.2.1": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@vladfrangu/async_event_emitter/-/async_event_emitter-2.2.2.tgz#84c5a3f8d648842cec5cc649b88df599af32ed88" + integrity sha512-HIzRG7sy88UZjBJamssEczH5q7t5+axva19UbZLO6u0ySbYPrwzWiXBcC0WuHyhKKoeCyneH+FvYzKQq/zTtkQ== + acorn-walk@^8.1.1: version "8.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" @@ -218,6 +305,13 @@ buffer-from@^1.0.0: resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +busboy@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/busboy/-/busboy-1.6.0.tgz#966ea36a9502e43cdb9146962523b92f531f6893" + integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA== + dependencies: + streamsearch "^1.1.0" + create-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" @@ -228,6 +322,31 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +discord-api-types@^0.37.41, discord-api-types@^0.37.42: + version "0.37.42" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.42.tgz#3c196267ed31e9ea249e6880c15e2af1c6428629" + integrity sha512-1Huaj9cQ1W7/uryS8MZs/tZemnoKB94thM1cE40lep3rpU3q7WHqkdjN/veX0prTkYlPhcyLd/DeF/pBO8X8oQ== + +discord.js@^14.11.0: + version "14.11.0" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.11.0.tgz#6529d49f30d10fc5a9ff8e6796661aa998769afe" + integrity sha512-CkueWYFQ28U38YPR8HgsBR/QT35oPpMbEsTNM30Fs8loBIhnA4s70AwQEoy6JvLcpWWJO7GY0y2BUzZmuBMepQ== + dependencies: + "@discordjs/builders" "^1.6.3" + "@discordjs/collection" "^1.5.1" + "@discordjs/formatters" "^0.3.1" + "@discordjs/rest" "^1.7.1" + "@discordjs/util" "^0.3.1" + "@discordjs/ws" "^0.8.3" + "@sapphire/snowflake" "^3.4.2" + "@types/ws" "^8.5.4" + discord-api-types "^0.37.41" + fast-deep-equal "^3.1.3" + lodash.snakecase "^4.1.1" + tslib "^2.5.0" + undici "^5.22.0" + ws "^8.13.0" + dotenv@^16.0.3: version "16.0.3" resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz" @@ -266,6 +385,20 @@ eventemitter3@^4.0.4: resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +file-type@^18.3.0: + version "18.4.0" + resolved "https://registry.yarnpkg.com/file-type/-/file-type-18.4.0.tgz#1d6c2c9351ad0dfe1f03165e04d60797b159578f" + integrity sha512-o6MQrZKTAK6WpvmQk3jqTVUmqxYBxW5bloUfrdH1ZnRFDvvAPNr+l+rgOxM3nkqWT+3khaj3FRMDydWe0xhu+w== + dependencies: + readable-web-to-node-stream "^3.0.2" + strtok3 "^7.0.0" + token-types "^5.0.1" + fsevents@~2.3.2: version "2.3.2" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" @@ -276,6 +409,26 @@ get-tsconfig@^4.4.0: resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.5.0.tgz" integrity sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ== +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + make-error@^1.1.1: version "1.3.6" resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" @@ -301,11 +454,37 @@ p-timeout@^3.2.0: dependencies: p-finally "^1.0.0" +peek-readable@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec" + integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A== + prettier@^2.8.8: version "2.8.8" resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +readable-stream@^3.6.0: + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +readable-web-to-node-stream@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb" + integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw== + dependencies: + readable-stream "^3.6.0" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + source-map-support@^0.5.21: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" @@ -319,11 +498,44 @@ source-map@^0.6.0: resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +streamsearch@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" + integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strtok3@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5" + integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ== + dependencies: + "@tokenizer/token" "^0.3.0" + peek-readable "^5.0.0" + throat@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/throat/-/throat-6.0.2.tgz" integrity sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ== +token-types@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4" + integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg== + dependencies: + "@tokenizer/token" "^0.3.0" + ieee754 "^1.2.1" + +ts-mixer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.3.tgz#69bd50f406ff39daa369885b16c77a6194c7cae6" + integrity sha512-k43M7uCG1AkTyxgnmI5MPwKoUvS/bRvLvUb7+Pgpdlmok8AoqmUaZxUUw8zKM5B1lqZrt41GjYgnvAi0fppqgQ== + ts-node@^10.9.1: version "10.9.1" resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.1.tgz#e73de9102958af9e1f0b168a6ff320e25adcff4b" @@ -364,6 +576,18 @@ typescript@^5.0.4: resolved "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz" integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== +undici@^5.22.0: + version "5.22.1" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.22.1.tgz#877d512effef2ac8be65e695f3586922e1a57d7b" + integrity sha512-Ji2IJhFXZY0x/0tVBXeQwgPlLWw13GVzpsWPQ3rV50IFMMof2I55PZZxtm4P6iNq+L5znYN9nSTAq0ZyE6lSJw== + dependencies: + busboy "^1.6.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + v8-compile-cache-lib@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" From 10522d28e8f1d747061001371bfcfecf7c663a39 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 17 May 2023 22:28:55 +0800 Subject: [PATCH 09/11] websocket get message --- .github/workflows/releases.yaml | 2 +- README.md | 16 +++++++++----- example/upscale-ws.ts | 39 +++++++++++++++++++++++++++++++++ example/upscale.ts | 4 ++-- example/variation-ws.ts | 39 +++++++++++++++++++++++++++++++++ example/variation.ts | 4 ++-- package.json | 2 +- src/midjourney.ts | 34 ++++++++++++++++++++++------ src/ws.message.ts | 27 +++++++++++++++++------ 9 files changed, 142 insertions(+), 25 deletions(-) create mode 100644 example/upscale-ws.ts create mode 100644 example/variation-ws.ts diff --git a/.github/workflows/releases.yaml b/.github/workflows/releases.yaml index e0db529..e5c0b75 100644 --- a/.github/workflows/releases.yaml +++ b/.github/workflows/releases.yaml @@ -1,6 +1,6 @@ name: Node.js Package env: - APPVERSION: v2.1.${{ github.run_number }} + APPVERSION: v2.2.${{ github.run_number }} on: workflow_dispatch: push: diff --git a/README.md b/README.md index 4720251..ebbb160 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,7 @@ Node.js client for the unofficial MidJourney API. [discord bot example](https://github.com/erictik/midjourney-discord-wrapper/) -[web ui example](https://github.com/erictik/midjourney-ui/) - +[web ui example](https://github.com/erictik/midjourney-ui/) ## Install @@ -37,6 +36,7 @@ import { Midjourney } from "midjourney"; ChannelId: process.env.CHANNEL_ID, SalaiToken: process.env.SALAI_TOKEN, Debug: true, + Ws:true, }); const msg = await client.Imagine("A little pink elephant", (uri: string) => { console.log("loading", uri); @@ -75,18 +75,24 @@ export SALAI_TOKEN="your-salai-token" Then, run the example with the following command: ```bash -npx tsx example/imagine.ts +npx tsx example/imagine-ws.ts ``` ```bash -npx tsx example/upscale.ts +npx tsx example/upscale-w.ts ``` ```bash -npx tsx example/variation.ts +npx tsx example/variation-ws.ts ``` +## route-map +- [x] websocket get message +- [ ] call back error +- [ ] add `/info` `/fast` and `/relax` + + ## Star History [![Star History Chart](https://api.star-history.com/svg?repos=erictik/midjourney-api&type=Date)](https://star-history.com/#erictik/midjourney-api&Date) diff --git a/example/upscale-ws.ts b/example/upscale-ws.ts new file mode 100644 index 0000000..2d39440 --- /dev/null +++ b/example/upscale-ws.ts @@ -0,0 +1,39 @@ +import "dotenv/config"; +import { Midjourney } from "../src"; +/** + * + * a simple example of how to use the Upscale with ws command + * ``` + * npx tsx example/upscale-ws.ts + * ``` + */ +async function main() { + const client = new Midjourney({ + ServerId: process.env.SERVER_ID, + ChannelId: process.env.CHANNEL_ID, + SalaiToken: process.env.SALAI_TOKEN, + Debug: true, + Ws: true, + }); + await client.init(); + const msg = await client.Imagine("a cool cat, blue ears, yellow hat"); + console.log({ msg }); + if (!msg) { + console.log("no message"); + return; + } + const msg2 = await client.Upscale( + msg.content, + 2, + msg.id, + msg.hash, + (uri: string, progress: string) => { + console.log("loading", uri, "progress", progress); + } + ); + console.log({ msg2 }); +} +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/example/upscale.ts b/example/upscale.ts index 900c2d0..80e6142 100644 --- a/example/upscale.ts +++ b/example/upscale.ts @@ -23,8 +23,8 @@ async function main() { const msg2 = await client.Upscale( msg.content, 2, - msg.id, - msg.hash, + msg.id, + msg.hash, (uri: string, progress: string) => { console.log("loading", uri, "progress", progress); } diff --git a/example/variation-ws.ts b/example/variation-ws.ts new file mode 100644 index 0000000..729acba --- /dev/null +++ b/example/variation-ws.ts @@ -0,0 +1,39 @@ +import "dotenv/config"; +import { Midjourney } from "../src"; +/** + * + * a simple example of how to use the Variation with ws command + * ``` + * npx tsx example/variation-ws.ts + * ``` + */ +async function main() { + const client = new Midjourney({ + ServerId: process.env.SERVER_ID, + ChannelId: process.env.CHANNEL_ID, + SalaiToken: process.env.SALAI_TOKEN, + Debug: true, + Ws: true, + }); + await client.init(); + const msg = await client.Imagine("a dog, blue ears, and a red nose"); + console.log({ msg }); + if (!msg) { + console.log("no message"); + return; + } + const msg2 = await client.Variation( + msg.content, + 2, + msg.id, + msg.hash, + (uri: string) => { + console.log("loading", uri); + } + ); + console.log({ msg2 }); +} +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/example/variation.ts b/example/variation.ts index ce59aef..4bd9d78 100644 --- a/example/variation.ts +++ b/example/variation.ts @@ -23,8 +23,8 @@ async function main() { const msg2 = await client.Variation( msg.content, 2, - msg.id, - msg.hash, + msg.id, + msg.hash, (uri: string) => { console.log("loading", uri); } diff --git a/package.json b/package.json index 3febefb..c76e325 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "midjourney", - "version": "2.1.0", + "version": "2.2.0", "description": "Node.js client for the unofficial MidJourney API.", "main": "libs/index.js", "types": "libs/index.d.ts", diff --git a/src/midjourney.ts b/src/midjourney.ts index 32852e9..09684ba 100644 --- a/src/midjourney.ts +++ b/src/midjourney.ts @@ -46,7 +46,7 @@ export class Midjourney extends MidjourneyMessage { throw new Error(`ImagineApi failed with status ${httpStatus}`); } if (this.wsClient) { - return await this.wsClient.waitMessage(nonce, loading); + return await this.wsClient.waitMessage("imagine", nonce, loading); } else { this.log(`await generate image`); const msg = await this.WaitMessage(prompt, loading); @@ -154,14 +154,23 @@ export class Midjourney extends MidjourneyMessage { if (index < 1 || index > 4) { throw new Error(`Variation index must be between 1 and 4, got ${index}`); } - const httpStatus = await this.VariationApi(index, msgId, msgHash); + const nonce = SnowflakeUtil.generate().toString(); + const httpStatus = await this.VariationApi(index, msgId, msgHash, nonce); if (httpStatus !== 204) { throw new Error(`VariationApi failed with status ${httpStatus}`); } - this.log(`await generate image`); - return await this.WaitOptionMessage(content, `Variations`, loading); + if (this.wsClient) { + return await this.wsClient.waitMessage("variation", nonce, loading); + } else { + return await this.WaitOptionMessage(content, `Variations`, loading); + } } - async VariationApi(index: number, messageId: string, messageHash: string) { + async VariationApi( + index: number, + messageId: string, + messageHash: string, + nonce?: Snowflake + ) { const payload = { type: 3, guild_id: this.config.ServerId, @@ -174,6 +183,7 @@ export class Midjourney extends MidjourneyMessage { component_type: 2, custom_id: `MJ::JOB::variation::${index}::${messageHash}`, }, + nonce, }; return this.safeIteractions(payload); } @@ -189,15 +199,24 @@ export class Midjourney extends MidjourneyMessage { if (index < 1 || index > 4) { throw new Error(`Variation index must be between 1 and 4, got ${index}`); } - const httpStatus = await this.UpscaleApi(index, msgId, msgHash); + const nonce = SnowflakeUtil.generate().toString(); + const httpStatus = await this.UpscaleApi(index, msgId, msgHash, nonce); if (httpStatus !== 204) { throw new Error(`VariationApi failed with status ${httpStatus}`); } this.log(`await generate image`); + if (this.wsClient) { + return await this.wsClient.waitMessage("upscale", nonce, loading); + } return await this.WaitUpscaledMessage(content, index, loading); } - async UpscaleApi(index: number, messageId: string, messageHash: string) { + async UpscaleApi( + index: number, + messageId: string, + messageHash: string, + nonce?: Snowflake + ) { const payload = { type: 3, guild_id: this.config.ServerId, @@ -210,6 +229,7 @@ export class Midjourney extends MidjourneyMessage { component_type: 2, custom_id: `MJ::JOB::upsample::${index}::${messageHash}`, }, + nonce, }; return this.safeIteractions(payload); } diff --git a/src/ws.message.ts b/src/ws.message.ts index a6a280f..ed74d9a 100644 --- a/src/ws.message.ts +++ b/src/ws.message.ts @@ -209,12 +209,22 @@ export class WsMessage { return progress; } + matchContent(content: string | undefined) { + if (!content) return ""; + const pattern = /\*\*(.*?)\*\*/; // Match **middle content + const matches = content.match(pattern); + if (matches && matches.length > 1) { + return matches[1]; // Get the matched content + } else { + this.log("No match found.", content); + return ""; + } + } + private filterMessages(MJmsg: MJMessage) { - // str.replace(/<@(.*?)>.*$/, "") + // this.log("filterMessages", MJmsg, this.waitMjEvent); const index = this.waitMjEvent.findIndex( - (e) => - e.prompt?.replace(/<@(.*?)>.*$/, "") === - MJmsg.content.replace(/<@(.*?)>.*$/, "") + (e) => this.matchContent(e.prompt) === this.matchContent(MJmsg.content) ); if (index < 0) { this.log("FilterMessages not found", MJmsg, this.waitMjEvent); @@ -307,9 +317,13 @@ export class WsMessage { this.onceImage("imagine", nonce, callback); } - async waitMessage(nonce: string, loading?: LoadingHandler) { + async waitMessage( + type: ImageEventType, + nonce: string, + loading?: LoadingHandler + ) { return new Promise((resolve, reject) => { - this.onceImagine(nonce, ({ message, error }) => { + this.onceImage(type, nonce, ({ message, error }) => { if (error) { reject(error); return; @@ -318,7 +332,6 @@ export class WsMessage { resolve(message); return; } - this.log("loading"); message && loading && loading(message.uri, message.progress || ""); }); }); From 2c31d17c20646557a1dba9419b49fcbde0bce4cb Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 18 May 2023 14:53:42 +0800 Subject: [PATCH 10/11] add call back error --- example/imagine-err.ts | 1 + src/midjourney.message.ts | 3 ++ src/ws.message.ts | 62 +++++++++++++++++++++++++-------------- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/example/imagine-err.ts b/example/imagine-err.ts index 9223019..71814c6 100644 --- a/example/imagine-err.ts +++ b/example/imagine-err.ts @@ -16,6 +16,7 @@ async function main() { SessionId: process.env.SALAI_TOKEN || "8bb7f5b79c7a49f7d0824ab4b8773a81", }); + await client.init(); const msg = await client.Imagine( "A little white elephant --ar1:1", (uri: string, progress: string) => { diff --git a/src/midjourney.message.ts b/src/midjourney.message.ts index 98e840a..03ccce7 100644 --- a/src/midjourney.message.ts +++ b/src/midjourney.message.ts @@ -105,6 +105,7 @@ export class MidjourneyMessage { this.log(i, "wait no message found"); await sleep(1000 * 2); } + return null; } async WaitOptionMessage( @@ -120,6 +121,7 @@ export class MidjourneyMessage { this.log(i, content, "wait no message found"); await sleep(1000 * 2); } + return null; } async WaitUpscaledMessage( content: string, @@ -139,6 +141,7 @@ export class MidjourneyMessage { this.log(i, content, "wait no message found"); await sleep(1000 * 2); } + return null; } // limit the number of concurrent interactions diff --git a/src/ws.message.ts b/src/ws.message.ts index ed74d9a..3ca0769 100644 --- a/src/ws.message.ts +++ b/src/ws.message.ts @@ -140,25 +140,26 @@ export class WsMessage { if (!(author && author.id === this.MJBotId)) return; if (channel_id !== this.config.ChannelId) return; this.log("has message", content, nonce, id); - //done image - if (msg.t === "MESSAGE_CREATE" && !nonce && !application_id) { - this.log("done image"); - //match done image - const MJmsg: MJMessage = { - id, - hash: this.uriToHash(attachments[0].url), - progress: "done", - uri: attachments[0].url, - content, - }; - this.filterMessages(MJmsg); - return; - } //waiting start image or info or error if (nonce && msg.t === "MESSAGE_CREATE") { this.log("waiting start image or info or error"); this.updateMjEventIdByNonce(id, nonce); + if ( + embeds && + embeds.length > 0 && + embeds[0].title === "Invalid parameter" + ) { + //error + const error = new Error(embeds[0].description); + this.EventError(id, error); + } + } + //done image + if (msg.t === "MESSAGE_CREATE" && !nonce && !application_id) { + this.log("done image"); + this.done(message); + return; } //processing image @@ -189,14 +190,31 @@ export class WsMessage { }; this.emitImage(event.type, eventMsg); } - // this.log(message); - // this.log("message", { - // id, - // nonce, - // attachments, - // content, - // embeds, - // }); + } + private EventError(id: string, error: Error) { + this.log("EventError", id, error); + const index = this.waitMjEvent.findIndex((e) => e.id === id); + if (index < 0 || !this.waitMjEvent[index]) { + return; + } + const event = this.waitMjEvent[index]; + const eventMsg: WsEventMsg = { + error, + }; + this.emit(event.type, eventMsg); + } + + private done(message: any) { + const { content, id, attachments } = message; + const MJmsg: MJMessage = { + id, + hash: this.uriToHash(attachments[0].url), + progress: "done", + uri: attachments[0].url, + content, + }; + this.filterMessages(MJmsg); + return; } protected content2progress(content: string) { From abcedfaa981f47e2ed7bed2f500775f589a89a1c Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 18 May 2023 14:54:19 +0800 Subject: [PATCH 11/11] fix readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ebbb160..3dcf2c7 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ npx tsx example/variation-ws.ts ## route-map - [x] websocket get message -- [ ] call back error +- [x] call back error - [ ] add `/info` `/fast` and `/relax`