Skip to content

Commit

Permalink
add seed & discord api rate limit
Browse files Browse the repository at this point in the history
Eric committed Apr 28, 2023
1 parent 75329de commit 7c260ac
Showing 11 changed files with 516 additions and 75 deletions.
350 changes: 350 additions & 0 deletions package-lock.json

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

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -30,13 +30,15 @@
},
"homepage": "https://github.com/erictik/midjourney-api#readme",
"devDependencies": {
"@types/async": "^3.2.20",
"@types/node": "^18.16.0",
"dotenv": "^16.0.3",
"prettier": "^2.8.8",
"tsx": "^3.12.6",
"typescript": "^5.0.4"
},
"dependencies": {
"async": "^3.2.4",
"axios": "^1.3.6"
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./midjourney";
export * from "./midjourney.message";
export * from "./interfaces/index";
1 change: 1 addition & 0 deletions src/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./message";
9 changes: 2 additions & 7 deletions src/midjourney.message.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import axios from "axios";
import { MJMessage } from "./interfaces";

export type MJMessage = {
id: string;
uri: string;
hash: string;
content: string;
};
export class MidjourneyMessage {
constructor(
public ChannelId: string,
@@ -81,7 +76,7 @@ export class MidjourneyMessage {
if (msg !== null) {
return msg;
}
this.log(i, "wait no message found");
this.log(i, content, "wait no message found");
await this.Wait(1000 * 2);
}
}
111 changes: 57 additions & 54 deletions src/midjourney.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import axios from "axios";
import { MidjourneyMessage } from "./midjourney.message";
import { CreateQueue, QueueTask } from "./queue";
import { random, sleep } from "./utls";
import { QueueObject, tryEach } from "async";

export class Midjourney extends MidjourneyMessage {
ApiQueue: QueueObject<QueueTask<any>>;
constructor(
public ServerId: string,
public ChannelId: string,
@@ -10,27 +14,66 @@ export class Midjourney extends MidjourneyMessage {
) {
super(ChannelId, SalaiToken, debug);
this.log("Midjourney constructor");
this.ApiQueue = CreateQueue(1);
}

async Imagine(prompt: string, loading?: (uri: string) => void) {
//if prompt not include --seed, use it
if (!prompt.includes("--seed")) {
const speed = random(1000, 9999);
prompt = `${prompt} --seed ${speed}`;
}

const httpStatus = await this.ImagineApi(prompt);
if (httpStatus !== 204) {
throw new Error(`ImagineApi failed with status ${httpStatus}`);
}
this.log(`await generate image`);
return await this.WaitMessage(prompt, loading);
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) {
const httpStatus: number = await new Promise((resolve, reject) => {
this.ApiQueue.push(
{
task: this.interactions.bind(this, payload, (res) => {
resolve(res);
}),
},
(err) => {
reject(err);
}
);
});
return httpStatus;
}

protected async interactions(payload: any) {
protected async interactions(
payload: any,
callback: (result: number) => void
) {
const headers = { authorization: this.SalaiToken };
const response = await axios.post(
"https://discord.com/api/v9/interactions",
payload,
{
headers,
}
);
return response.status;
const t0 = performance.now();
try {
const response = await axios.post(
"https://discord.com/api/v9/interactions",
payload,
{
headers,
}
);
const t1 = performance.now();
this.log(`Execution time: ${t1 - t0} milliseconds.`);
callback && callback(response.status);
//discord api rate limit
await sleep(950);
return response.status;
} catch (error) {
console.log(error);
callback && callback(500);
}
}

async ImagineApi(prompt: string) {
@@ -75,7 +118,7 @@ export class Midjourney extends MidjourneyMessage {
attachments: [],
},
};
return this.interactions(payload);
return this.safeIteractions(payload);
}

async Variation(
@@ -110,47 +153,7 @@ export class Midjourney extends MidjourneyMessage {
custom_id: `MJ::JOB::variation::${index}::${messageHash}`,
},
};
return this.interactions(payload);
}
//FIXME this is not working
async RemixModelApi(
content: string,
index: number,
messageId: string,
messageHash: string
) {
const payload = {
type: 5,
application_id: "936929561302675456",
channel_id: this.ChannelId,
guild_id: this.ServerId,
data: {
id: "1100105238322618488",
custom_id: `MJ::RemixModal::${messageHash}::${index}`,
components: [
{
type: 1,
components: [
{
type: 4,
custom_id: "MJ::RemixModal::new_prompt",
value: content,
},
],
},
],
},
session_id: "ec6524c8d2926e285a8232f7ed1ced98",
};
const headers = { authorization: this.SalaiToken };
const response = await axios.post(
"https://discord.com/api/v9/interactions",
payload,
{
headers,
}
);
return response.status;
return this.safeIteractions(payload);
}

async Upscale(
@@ -186,7 +189,7 @@ export class Midjourney extends MidjourneyMessage {
custom_id: `MJ::JOB::upsample::${index}::${messageHash}`,
},
};
return this.interactions(payload);
return this.safeIteractions(payload);
}
async UpscaleByCustomID(messageId: string, customId: string) {
const payload = {
@@ -202,6 +205,6 @@ export class Midjourney extends MidjourneyMessage {
custom_id: customId,
},
};
return this.interactions(payload);
return this.safeIteractions(payload);
}
}
27 changes: 27 additions & 0 deletions src/queue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import async from "async";
type TaskFunction<T> = () => Promise<T>;
export interface QueueTask<T> {
task: TaskFunction<T>;
callback?: (result: T) => void;
}
// const queue = async.queue(async ({ task, callback }: QueueTask<any>) => {
// const result = await task();
// callback(result);
// }, 10);

// const task1: TaskFunction<number> = async () => {
// // do some async work
// return 42;
// };
// queue.push({
// task: task1,
// callback: (result: number) => {
// console.log(result); // output: 42
// },
// });
export function CreateQueue<T>(concurrency: number) {
return async.queue(async ({ task, callback }: QueueTask<any>) => {
const result = await task();
callback && callback(result);
}, concurrency);
}
5 changes: 5 additions & 0 deletions src/utls/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const sleep = async (ms: number): Promise<void> =>
await new Promise((resolve) => setTimeout(resolve, ms));

export const random = (min: number, max: number): number =>
Math.floor(Math.random() * (max - min) + min);
44 changes: 44 additions & 0 deletions test/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
type TaskFunction<T> = () => Promise<T>;

interface QueueTask<T> {
task: TaskFunction<T>;
callback: (result: T) => void;
}
import async from "async";

const queue = async.queue(async ({ task, callback }: QueueTask<any>) => {
const result = await task();
callback(result);
}, 10);

const task1: TaskFunction<number> = async () => {
// do some async work
return 42;
};

queue.push({
task: task1,
callback: (result: number) => {
console.log(result); // output: 42
},
});
queue.push({
task: task1,
callback: (result: number) => {
console.log(result); // output: 42
},
});

queue.push({
task: task1,
callback: (result: number) => {
console.log(result); // output: 42
},
});

queue.push({
task: task1,
callback: (result: number) => {
console.log(result); // output: 42
},
});
9 changes: 6 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -12,7 +12,10 @@

/* Language and Environment */
"target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
"lib": [
"es2021",
"esnext.asynciterable"
], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
@@ -60,7 +63,7 @@
"outDir": "./libs" /* Specify an output folder for all emitted files. */,
// "removeComments": true, /* Disable emitting comments. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
"importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
@@ -78,7 +81,7 @@
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
32 changes: 21 additions & 11 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -4,23 +4,23 @@

"@esbuild-kit/cjs-loader@^2.4.2":
version "2.4.2"
resolved "https://registry.yarnpkg.com/@esbuild-kit/cjs-loader/-/cjs-loader-2.4.2.tgz#cb4dde00fbf744a68c4f20162ea15a8242d0fa54"
resolved "https://registry.npmjs.org/@esbuild-kit/cjs-loader/-/cjs-loader-2.4.2.tgz"
integrity sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==
dependencies:
"@esbuild-kit/core-utils" "^3.0.0"
get-tsconfig "^4.4.0"

"@esbuild-kit/core-utils@^3.0.0":
version "3.1.0"
resolved "https://registry.yarnpkg.com/@esbuild-kit/core-utils/-/core-utils-3.1.0.tgz#49945d533dbd5e1b7620aa0fc522c15e6ec089c5"
resolved "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.1.0.tgz"
integrity sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==
dependencies:
esbuild "~0.17.6"
source-map-support "^0.5.21"

"@esbuild-kit/esm-loader@^2.5.5":
version "2.5.5"
resolved "https://registry.yarnpkg.com/@esbuild-kit/esm-loader/-/esm-loader-2.5.5.tgz#b82da14fcee3fc1d219869756c06f43f67d1ca71"
resolved "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.5.5.tgz"
integrity sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==
dependencies:
"@esbuild-kit/core-utils" "^3.0.0"
@@ -136,11 +136,21 @@
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz#3a8e57153905308db357fd02f57c180ee3a0a1fa"
integrity sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==

"@types/async@^3.2.20":
version "3.2.20"
resolved "https://registry.npmjs.org/@types/async/-/async-3.2.20.tgz"
integrity sha512-6jSBQQugzyX1aWto0CbvOnmxrU9tMoXfA9gc4IrLEtvr3dTwSg5GLGoWiZnGLI6UG/kqpB3JOQKQrqnhUWGKQA==

"@types/node@^18.16.0":
version "18.16.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.16.0.tgz#4668bc392bb6938637b47e98b1f2ed5426f33316"
resolved "https://registry.npmjs.org/@types/node/-/node-18.16.0.tgz"
integrity sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==

async@^3.2.4:
version "3.2.4"
resolved "https://registry.npmjs.org/async/-/async-3.2.4.tgz"
integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==

asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
@@ -157,7 +167,7 @@ axios@^1.3.6:

buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==

combined-stream@^1.0.8:
@@ -174,7 +184,7 @@ delayed-stream@~1.0.0:

dotenv@^16.0.3:
version "16.0.3"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.3.tgz#115aec42bac5053db3c456db30cc243a5a836a07"
resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz"
integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==

esbuild@~0.17.6:
@@ -226,7 +236,7 @@ fsevents@~2.3.2:

get-tsconfig@^4.4.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.5.0.tgz#6d52d1c7b299bd3ee9cd7638561653399ac77b0f"
resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.5.0.tgz"
integrity sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==

mime-db@1.52.0:
@@ -243,7 +253,7 @@ mime-types@^2.1.12:

prettier@^2.8.8:
version "2.8.8"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz"
integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==

proxy-from-env@^1.1.0:
@@ -253,20 +263,20 @@ proxy-from-env@^1.1.0:

source-map-support@^0.5.21:
version "0.5.21"
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz"
integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"

source-map@^0.6.0:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==

tsx@^3.12.6:
version "3.12.6"
resolved "https://registry.yarnpkg.com/tsx/-/tsx-3.12.6.tgz#36b3693e48b8392da374487190972c7b80e433b4"
resolved "https://registry.npmjs.org/tsx/-/tsx-3.12.6.tgz"
integrity sha512-q93WgS3lBdHlPgS0h1i+87Pt6n9K/qULIMNYZo07nSeu2z5QE2CellcAZfofVXBo2tQg9av2ZcRMQ2S2i5oadQ==
dependencies:
"@esbuild-kit/cjs-loader" "^2.4.2"

0 comments on commit 7c260ac

Please sign in to comment.