├── .env.example
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ └── feature_request.md
└── workflows
│ └── v4.3.x.yaml
├── .gitignore
├── .npmrc
├── .prettierignore
├── LICENSE
├── README.md
├── README_zh.md
├── custompan.ts
├── doc
├── README.md
├── SUMMARY.md
├── faq
│ ├── node.js-less-than-18.md
│ ├── proxy.md
│ └── vercel.md
├── getting-started
│ ├── api-reference
│ │ ├── README.md
│ │ ├── blend.md
│ │ ├── describe.md
│ │ ├── fast.md
│ │ ├── imagine.md
│ │ ├── info.md
│ │ ├── relax.md
│ │ ├── reroll.md
│ │ ├── reset.md
│ │ ├── setting.md
│ │ ├── upscale.md
│ │ └── variation.md
│ ├── config.md
│ ├── example
│ │ ├── README.md
│ │ ├── image.md
│ │ └── imagine.md
│ ├── install.md
│ └── quickstart.md
└── use-cases
│ ├── discord-bot.md
│ └── web-ui.md
├── example
├── banend-words.ts
├── customzoom.ts
├── describe.ts
├── faceswap.ts
├── fast.ts
├── imagine-blend.ts
├── imagine-dm.ts
├── imagine-err.ts
├── imagine-niji.ts
├── imagine-ws-m.ts
├── imagine-ws.ts
├── imagine.js
├── imagine.ts
├── info.ts
├── prefer-remix.ts
├── relax.ts
├── reset.ts
├── settings.ts
├── shorten.ts
├── upscale-ws.ts
├── upscale.ts
├── variation-ws.ts
├── variation.ts
├── vary.ts
└── zoomout.ts
├── images
├── ali.png
└── wechat.png
├── package-lock.json
├── package.json
├── src
├── banned.words.ts
├── command.ts
├── discord.message.ts
├── discord.ws.ts
├── face.swap.ts
├── gradio
│ ├── client.ts
│ ├── globals.d.ts
│ ├── index.ts
│ ├── types.ts
│ └── utils.ts
├── index.ts
├── interfaces
│ ├── config.ts
│ ├── index.ts
│ ├── message.ts
│ ├── modal.ts
│ └── upload.ts
├── midjourney.api.ts
├── midjourney.ts
├── utils
│ └── index.ts
└── verify.human.ts
├── test
├── face.ts
├── test.ts
└── test2.ts
├── tsconfig.json
└── yarn.lock
/.env.example:
--------------------------------------------------------------------------------
1 | SALAI_TOKEN="Token of the Account from which you paid MidJourney"
2 | SERVER_ID="Server id here"
3 | CHANNEL_ID="Channel in which commands are sent"
4 | SESSION_ID="session id here"
5 | HUGGINGFACE_TOKEN="verify human https://huggingface.co/docs/hub/security-tokens"
6 | IMAGE_PROXY="image proxy url here"
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 | ko_fi: erictik
3 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report for Examples
2 |
3 | description: Create a report to help us improve
4 | labels:
5 | - bug
6 |
7 | body:
8 | - type: textarea
9 | id: code
10 | attributes:
11 | label: the code that reproduces this issue or a replay of the bug
12 | description: |
13 | public repo link or copy code
14 | validations:
15 | required: true
16 | - type: textarea
17 | id: Describe
18 | attributes:
19 | label: Describe the bug
20 | value: |
21 | **Describe the bug**
22 | A clear and concise description of what the bug is.
23 |
24 | **Expected behavior**
25 | A clear and concise description of what you expected to happen.
26 |
27 | **Screenshots**
28 | If applicable, add screenshots to help explain your problem.
29 |
30 | validations:
31 | required: true
32 | - type: textarea
33 | id: log
34 | attributes:
35 | label: error log
36 | description: Add any other context about the problem here.
37 | validations:
38 | required: true
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/v4.3.x.yaml:
--------------------------------------------------------------------------------
1 | name: verson 4.3.x
2 | env:
3 | APPVERSION: 4.3.${{ github.run_number }}
4 | on:
5 | workflow_dispatch:
6 | push:
7 | branches:
8 | - "main"
9 | paths:
10 | - "src/**.ts"
11 | jobs:
12 | publish-gpr:
13 | runs-on: ubuntu-latest
14 | permissions:
15 | contents: write
16 | steps:
17 | - uses: actions/checkout@v3
18 | - uses: actions/setup-node@v3.6.0
19 | with:
20 | node-version: "18.x"
21 | registry-url: "https://registry.npmjs.org"
22 | env:
23 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
24 | - run: yarn install
25 | # - run: sed -i '3s/\([0-9]\{1,3\}\.[0-9]\{1,3\}\)\.[0-9]\{1,3\}/\1.${{ github.run_number }}/g' package.json
26 | - name: sed version
27 | run: sed -i '3s/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/${{ env.APPVERSION }}/g' package.json
28 | - run: npm run build
29 | - run: npm publish
30 | - name: GH Release
31 | uses: softprops/action-gh-release@v0.1.15
32 | with:
33 | tag_name: ${{ env.APPVERSION }}
34 | target_commitish: ${{ github.sha }}
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 |
132 |
133 | # Directory for build generated files
134 | libs
135 | .idea
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | # //registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}
2 | registry=https://registry.npmjs.org/
3 | always-auth=true
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | libs
2 | node_modules
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # midjourney-client
2 |
3 | Node.js client for the unofficial MidJourney api.
4 |
5 | English / [中文文档](README_zh.md)
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## What's new
15 |
16 | - [face swap](https://github.com/erictik/midjourney-client/blob/main/example/faceswap.ts)
17 | - [niji bot](https://github.com/erictik/midjourney-client/blob/main/example/imagine-niji.ts)
18 | - [custom zoom](https://github.com/erictik/midjourney-client/blob/main/example/customzoom.ts)
19 | - [remix mode](https://github.com/erictik/midjourney-client/blob/main/example/variation-ws.ts)
20 |
21 | ## Example
22 |
23 | To run the included example, you must have [Node.js](https://nodejs.org/en/) installed. Then, run the following commands in the root directory of this project:
24 |
25 | 1. clone the repository
26 |
27 | ```bash
28 | git clone https://github.com/erictik/midjourney-client.git
29 | cd midjourney-client
30 | ```
31 |
32 | 2. install dependencies
33 |
34 | ```bash
35 | yarn
36 | # or npm
37 | npm install
38 | ```
39 |
40 | 3. set the environment variables
41 |
42 | - [Login Discord](https://discord.com/channels/@me)`F12` _OR_ `Ctrl + Shift + I` (or `Command + Option + I` on Mac) to open the developer tools _AND_ paste the following code into the console
43 |
44 | ```javascript
45 | window.webpackChunkdiscord_app.push([
46 | [Math.random()],
47 | {},
48 | (req) => {
49 | for (const m of Object.keys(req.c)
50 | .map((x) => req.c[x].exports)
51 | .filter((x) => x)) {
52 | if (m.default && m.default.getToken !== undefined) {
53 | return copy(m.default.getToken());
54 | }
55 | if (m.getToken !== undefined) {
56 | return copy(m.getToken());
57 | }
58 | }
59 | },
60 | ]);
61 | console.log("%cWorked!", "font-size: 50px");
62 | console.log(`%cYou now have your token in the clipboard!`, "font-size: 16px");
63 | ```
64 |
65 | OR [use network your Discord TOKEN](https://www.androidauthority.com/get-discord-token-3149920/)
66 |
67 | - [Join my discord server](https://discord.com/invite/GavuGHQbV4)
68 |
69 | ```
70 | export SERVER_ID="1082500871478329374"
71 | export CHANNEL_ID="1094892992281718894"
72 | ```
73 |
74 | - OR [Create a server](https://discord.com/blog/starting-your-first-discord-server) and [Invite Midjourney Bot to Your Server](https://docs.midjourney.com/docs/invite-the-bot)
75 |
76 | ```bash
77 | # How to get server and channel ids:
78 | # when you click on a channel in your server in the browser
79 | # expect to have the follow URL pattern
80 | # `https://discord.com/channels/$SERVER_ID/$CHANNEL_ID`
81 | export SERVER_ID="your-server-id"
82 | export CHANNEL_ID="your-channel-id"
83 | ```
84 |
85 | - wirte your token to `.env` file or set the environment variables
86 |
87 | ```bash
88 | #example variables, please set up yours
89 |
90 | export SERVER_ID="1082500871478329374"
91 | export CHANNEL_ID="1094892992281718894"
92 | export SALAI_TOKEN="your-discord-token"
93 | ```
94 |
95 | - Then, run the example with the following command:
96 |
97 | ```bash
98 | npx tsx example/imagine-ws.ts
99 | ```
100 |
101 | OR
102 |
103 | ```bash
104 | yarn example:imagine
105 | # or npm
106 | npm run example:imagine
107 | ```
108 |
109 | - [more example](./example/)
110 |
111 | ## Usage
112 |
113 | 1. Install
114 |
115 | ```bash
116 | npm i midjourney
117 | # or
118 | yarn add midjourney
119 | ```
120 |
121 | 2. config param
122 | ```typescript
123 | export interface MJConfigParam {
124 | SalaiToken: string; //DISCORD_TOKEN
125 | ChannelId?: string; //DISCORD_CHANNEL_ID
126 | ServerId?: string; //DISCORD_SERVER_ID
127 | BotId?: typeof MJBot | typeof NijiBot; //DISCORD_BOT_ID MJBot OR NijiBot
128 | Debug?: boolean; // print log
129 | ApiInterval?: number; //ApiInterval request api interval
130 | Limit?: number; //Limit of get message list
131 | MaxWait?: number;
132 | Remix?: boolean; //Remix:true use remix mode
133 | Ws?: boolean; //Ws:true use websocket get discord message (ephemeral message)
134 | HuggingFaceToken?: string; //HuggingFaceToken for verify human
135 | SessionId?: string;
136 | DiscordBaseUrl?: string;
137 | ImageProxy?: string;
138 | WsBaseUrl?: string;
139 | fetch?: FetchFn; //Node.js<18 need node.fetch Or proxy
140 | WebSocket?: WebSocketCl; //isomorphic-ws Or proxy
141 | }
142 | ```
143 | 3. Use Imagine 、Variation and Upscale
144 |
145 | ```typescript
146 | import { Midjourney } from "midjourney";
147 | const client = new Midjourney({
148 | ServerId: process.env.SERVER_ID,
149 | ChannelId: process.env.CHANNEL_ID,
150 | SalaiToken: process.env.SALAI_TOKEN,
151 | Debug: true,
152 | Ws: true, //enable ws is required for remix mode (and custom zoom)
153 | });
154 | await client.init();
155 | const prompt =
156 | "Christmas dinner with spaghetti with family in a cozy house, we see interior details , simple blue&white illustration";
157 | //imagine
158 | const Imagine = await client.Imagine(
159 | prompt,
160 | (uri: string, progress: string) => {
161 | console.log("loading", uri, "progress", progress);
162 | }
163 | );
164 | console.log(Imagine);
165 | if (!Imagine) {
166 | console.log("no message");
167 | return;
168 | }
169 | //U1 U2 U3 U4 V1 V2 V3 V4 "Vary (Strong)" ...
170 | //⬅️,⬆️,⬇️,➡️
171 | const V1CustomID = Imagine.options?.find((o) => o.label === "V1")?.custom;
172 | if (!V1CustomID) {
173 | console.log("no V1");
174 | return;
175 | }
176 | // Varition V1
177 | const Varition = await client.Custom({
178 | msgId: Imagine.id,
179 | flags: Imagine.flags,
180 | customId: V1CustomID,
181 | content: prompt, //remix mode require content
182 | loading: (uri: string, progress: string) => {
183 | console.log("loading", uri, "progress", progress);
184 | },
185 | });
186 | console.log(Varition);
187 | const U1CustomID = Imagine.options?.find((o) => o.label === "U1")?.custom;
188 | if (!U1CustomID) {
189 | console.log("no U1");
190 | return;
191 | }
192 | // Upscale U1
193 | const Upscale = await client.Custom({
194 | msgId: Imagine.id,
195 | flags: Imagine.flags,
196 | customId: U1CustomID,
197 | loading: (uri: string, progress: string) => {
198 | console.log("loading", uri, "progress", progress);
199 | },
200 | });
201 | if (!Upscale) {
202 | console.log("no Upscale");
203 | return;
204 | }
205 | console.log(Upscale);
206 | const zoomout = Upscale?.options?.find((o) => o.label === "Custom Zoom");
207 | if (!zoomout) {
208 | console.log("no zoomout");
209 | return;
210 | }
211 | // Custom Zoom
212 | const CustomZoomout = await client.Custom({
213 | msgId: Upscale.id,
214 | flags: Upscale.flags,
215 | content: `${prompt} --zoom 2`, // Custom Zoom require content
216 | customId: zoomout.custom,
217 | loading: (uri: string, progress: string) => {
218 | console.log("loading", uri, "progress", progress);
219 | },
220 | });
221 | console.log(CustomZoomout);
222 | ```
223 |
224 |
225 |
226 | ## route-map
227 |
228 | - [x] `/imagine` `variation` `upscale` `reroll` `blend` `zoomout` `vary`
229 | - [x] `/info`
230 | - [x] `/fast ` and `/relax `
231 | - [x] [`/prefer remix`](https://github.com/erictik/midjourney-client/blob/main/example/prefer-remix.ts)
232 | - [x] [`variation (remix mode)`](https://github.com/erictik/midjourney-client/blob/main/example/variation-ws.ts)
233 | - [x] `/describe`
234 | - [x] [`/shorten`](https://github.com/erictik/midjourney-client/blob/main/example/shorten.ts)
235 | - [x] `/settings` `reset`
236 | - [x] verify human
237 | - [x] [proxy](https://github.com/erictik/midjourney-discord/blob/main/examples/proxy.ts)
238 | - [x] [niji bot](https://github.com/erictik/midjourney-client/blob/main/example/imagine-niji.ts)
239 | - [x] [custom zoom](https://github.com/erictik/midjourney-client/blob/main/example/customzoom.ts)
240 | - [x] autoload command payload
241 |
242 | ---
243 |
244 | ## Projects
245 |
246 | - [midjourney-ui](https://github.com/erictik/midjourney-ui/) next.js + vercel
247 | - [midjourney-discord](https://github.com/erictik/midjourney-discord)-bot
248 | - [phrame](https://github.com/jakowenko/phrame)
249 | - [guapitu](https://www.guapitu.com/zh/draw?code=RRXQNF)
250 |
251 | ---
252 |
253 | ## Support
254 |
255 | If you find it valuable and would like to show your support, any donations would be greatly appreciated. Your contribution helps me maintain and improve the program.
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 | ## Star History
264 |
265 | [](https://star-history.com/#erictik/midjourney-client&Date)
266 |
--------------------------------------------------------------------------------
/README_zh.md:
--------------------------------------------------------------------------------
1 | # midjourney-api
2 |
3 | 非官方的 MidJourney api 的 Node.js 客户端。
4 |
5 | [English](README.md) / 中文文档
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## 最近更新
15 |
16 | - [换脸](https://github.com/erictik/midjourney-api/blob/main/example/faceswap.ts)
17 | - [支持 niji bot](https://github.com/erictik/midjourney-api/blob/main/example/imagine-niji.ts)
18 | - [custom zoom](https://github.com/erictik/midjourney-api/blob/main/example/customzoom.ts)
19 | - [remix mode](https://github.com/erictik/midjourney-api/blob/main/example/variation-ws.ts)
20 |
21 | ## 快速开始
22 |
23 | 运行本项目需要安装 [Node.js](https://nodejs.org/en/),然后在本项目的根目录运行以下命令:
24 |
25 | 1. 下载项目代码
26 |
27 | ```bash
28 | git clone https://github.com/erictik/midjourney-api.git
29 | cd midjourney-api
30 | ```
31 |
32 | 2. 安装依赖
33 |
34 | ```bash
35 | yarn
36 | # or npm
37 | npm install
38 | ```
39 |
40 | 3. 设置环境变量
41 |
42 | - 获取 Discord TOKEN
43 | [登录 Discor](https://discord.com/channels/@me) F12 或者 [Ctrl + Shift + I] 或者 [Command + Option + I] 打开开发者工具,然后在 Console 中输入以下代码:
44 |
45 | ```javascript
46 | window.webpackChunkdiscord_app.push([
47 | [Math.random()],
48 | {},
49 | (req) => {
50 | for (const m of Object.keys(req.c)
51 | .map((x) => req.c[x].exports)
52 | .filter((x) => x)) {
53 | if (m.default && m.default.getToken !== undefined) {
54 | return copy(m.default.getToken());
55 | }
56 | if (m.getToken !== undefined) {
57 | return copy(m.getToken());
58 | }
59 | }
60 | },
61 | ]);
62 | console.log("%cWorked!", "font-size: 50px");
63 | console.log(`%您的Token在剪贴板了!`, "font-size: 16px");
64 | ```
65 |
66 | 也可以通过 查看 network [获取 discord token](https://www.androidauthority.com/get-discord-token-3149920/)
67 |
68 | - [加入我的 Discord 服务器](https://discord.com/invite/GavuGHQbV4)
69 | ```
70 | export SERVER_ID="1082500871478329374"
71 | export CHANNEL_ID="1094892992281718894"
72 | ```
73 | - 或者 [创建一个 Discord 服务器](https://discord.com/blog/starting-your-first-discord-server) 并邀请 [Midjourney Bot](https://docs.midjourney.com/docs/invite-the-bot)
74 |
75 | ```bash
76 | # 在浏览器中复制你的服务器网址
77 | # `https://discord.com/channels/$SERVER_ID/$CHANNEL_ID`
78 | export SERVER_ID="your-server-id"
79 | export CHANNEL_ID="your-channel-id"
80 | ```
81 |
82 | - 将环境变量写入`.env`文件或者 在控制台中设置
83 |
84 | ```bash
85 | #example variables, please set up yours
86 |
87 | export SERVER_ID="1082500871478329374"
88 | export CHANNEL_ID="1094892992281718894"
89 | export SALAI_TOKEN="your-discord-token"
90 | ```
91 |
92 | 4. 现在可以运行示例了
93 |
94 | ```bash
95 | npx tsx example/imagine-ws.ts
96 | ```
97 |
98 | 或者
99 |
100 | ```bash
101 | yarn example:imagine
102 | # or npm
103 | npm run example:imagine
104 | ```
105 |
106 | 5. 更多使用案例
107 | - [more example](./example/)
108 |
109 | ## 在项目中使用
110 |
111 | 1. 安装
112 |
113 | ```bash
114 | npm i midjourney
115 | # or
116 | yarn add midjourney
117 | ```
118 |
119 | 2. 使用 Imagine 、Variation 和 Upscale
120 |
121 | ```typescript
122 | import { Midjourney } from "midjourney";
123 | const client = new Midjourney({
124 | ServerId: process.env.SERVER_ID,
125 | ChannelId: process.env.CHANNEL_ID,
126 | SalaiToken: process.env.SALAI_TOKEN,
127 | Debug: true,
128 | Ws: true, //enable ws is required for remix mode (and custom zoom)
129 | });
130 | await client.init();
131 | const prompt =
132 | "Christmas dinner with spaghetti with family in a cozy house, we see interior details , simple blue&white illustration";
133 | //imagine
134 | const Imagine = await client.Imagine(
135 | prompt,
136 | (uri: string, progress: string) => {
137 | console.log("loading", uri, "progress", progress);
138 | }
139 | );
140 | console.log(Imagine);
141 | if (!Imagine) {
142 | console.log("no message");
143 | return;
144 | }
145 | // U1 U2 U3 U4 V1 V2 V3 V4 "Vary (Strong)" ...
146 | const V1CustomID = Imagine.options?.find((o) => o.label === "V1")?.custom;
147 | if (!V1CustomID) {
148 | console.log("no V1");
149 | return;
150 | }
151 | // Varition V1
152 | const Varition = await client.Custom({
153 | msgId: Imagine.id,
154 | flags: Imagine.flags,
155 | customId: V1CustomID,
156 | content: prompt, //remix mode require content
157 | loading: (uri: string, progress: string) => {
158 | console.log("loading", uri, "progress", progress);
159 | },
160 | });
161 | console.log(Varition);
162 | const U1CustomID = Imagine.options?.find((o) => o.label === "U1")?.custom;
163 | if (!U1CustomID) {
164 | console.log("no U1");
165 | return;
166 | }
167 | // Upscale U1
168 | const Upscale = await client.Custom({
169 | msgId: Imagine.id,
170 | flags: Imagine.flags,
171 | customId: U1CustomID,
172 | loading: (uri: string, progress: string) => {
173 | console.log("loading", uri, "progress", progress);
174 | },
175 | });
176 | if (!Upscale) {
177 | console.log("no Upscale");
178 | return;
179 | }
180 | console.log(Upscale);
181 | const zoomout = Upscale?.options?.find((o) => o.label === "Custom Zoom");
182 | if (!zoomout) {
183 | console.log("no zoomout");
184 | return;
185 | }
186 | // Custom Zoom
187 | const CustomZoomout = await client.Custom({
188 | msgId: Upscale.id,
189 | flags: Upscale.flags,
190 | content: `${prompt} --zoom 2`, // Custom Zoom require content
191 | customId: zoomout.custom,
192 | loading: (uri: string, progress: string) => {
193 | console.log("loading", uri, "progress", progress);
194 | },
195 | });
196 | console.log(CustomZoomout);
197 | ```
198 |
199 | ## route-map
200 |
201 | - [x] `/imagine` `variation` `upscale` `reroll` `blend` `zoomout` `vary`
202 | - [x] `/info`
203 | - [x] `/fast ` and `/relax `
204 | - [x] [`/prefer remix`](https://github.com/erictik/midjourney-api/blob/main/example/prefer-remix.ts)
205 | - [x] [`variation (remix mode)`](https://github.com/erictik/midjourney-api/blob/main/example/variation-ws.ts)
206 | - [x] `/describe`
207 | - [x] [`/shorten`](https://github.com/erictik/midjourney-api/blob/main/example/shorten.ts)
208 | - [x] `/settings` `reset`
209 | - [x] verify human
210 | - [x] [proxy](https://github.com/erictik/midjourney-discord/blob/main/examples/proxy.ts)
211 | - [x] [niji bot](https://github.com/erictik/midjourney-api/blob/main/example/imagine-niji.ts)
212 | - [x] [custom zoom](https://github.com/erictik/midjourney-api/blob/main/example/customzoom.ts)
213 | - [x] autoload command payload
214 |
215 | ---
216 |
217 | ## 应用项目
218 |
219 | - [midjourney-ui](https://github.com/erictik/midjourney-ui/) next.js + vercel
220 | - [midjourney-discord](https://github.com/erictik/midjourney-discord)-bot
221 | - [phrame](https://github.com/jakowenko/phrame)
222 | - [guapitu](https://www.guapitu.com/zh/draw?code=RRXQNF)
223 |
224 | ---
225 |
226 | ## 支持一下我吧
227 |
228 | 如果您觉得它很有价值,可以通过以下方式支持作者
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 | ## Star History
237 |
238 | [](https://star-history.com/#erictik/midjourney-api&Date)
239 |
--------------------------------------------------------------------------------
/custompan.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the (custom pan) options with ws command
6 | * keep remix turned off in your settings for this to work
7 | * ```
8 | * npx tsx example/custompan.ts
9 | * ```
10 | */
11 | async function main() {
12 | const client = new Midjourney({
13 | ServerId: process.env.SERVER_ID,
14 | ChannelId: process.env.CHANNEL_ID,
15 | SalaiToken: process.env.SALAI_TOKEN,
16 | Debug: true,
17 | Ws: true, //enable ws is required for custom pan
18 | });
19 | await client.init();
20 | const prompt =
21 | "Christmas dinner with spaghetti with family in a cozy house, we see interior details , simple blue&white illustration";
22 | //imagine
23 | const Imagine = await client.Imagine(
24 | prompt,
25 | (uri: string, progress: string) => {
26 | console.log("loading", uri, "progress", progress);
27 | }
28 | );
29 | console.log(Imagine);
30 | if (!Imagine) {
31 | console.log("no message");
32 | return;
33 | }
34 | //U1 U2 U3 U4 V1 V2 V3 V4 "Vary (Strong)" ...
35 | const V1CustomID = Imagine.options?.find((o) => o.label === "V1")?.custom;
36 | if (!V1CustomID) {
37 | console.log("no V1");
38 | return;
39 | }
40 | // Varition V1
41 | const Varition = await client.Custom({
42 | msgId: Imagine.id,
43 | flags: Imagine.flags,
44 | customId: V1CustomID,
45 | loading: (uri: string, progress: string) => {
46 | console.log("loading", uri, "progress", progress);
47 | },
48 | });
49 | console.log(Varition);
50 | const U1CustomID = Imagine.options?.find((o) => o.label === "U1")?.custom;
51 | if (!U1CustomID) {
52 | console.log("no U1");
53 | return;
54 | }
55 | // Upscale U1
56 | const Upscale = await client.Custom({
57 | msgId: Imagine.id,
58 | flags: Imagine.flags,
59 | customId: U1CustomID,
60 | loading: (uri: string, progress: string) => {
61 | console.log("loading", uri, "progress", progress);
62 | },
63 | });
64 | if (!Upscale) {
65 | console.log("no Upscale");
66 | return;
67 | }
68 | console.log(Upscale);
69 | const panright = Upscale?.options?.find((o) => o.label === "➡️"); // keep remix turned off in your settings for this to work
70 | if (!panright) {
71 | console.log("no panright");
72 | return;
73 | }
74 | // Custom Pan
75 | const CustomPanRight = await client.Custom({
76 | msgId: Upscale.id,
77 | flags: Upscale.flags,
78 | content: `${prompt} --pan_right 2`,
79 | customId: panright.custom,
80 | loading: (uri: string, progress: string) => {
81 | console.log("loading", uri, "progress", progress);
82 | },
83 | });
84 | console.log("Custom Pan", CustomPanRight);
85 | client.Close();
86 | }
87 | main()
88 | .then(() => {
89 | console.log("done");
90 | })
91 | .catch((err) => {
92 | console.error(err);
93 | process.exit(1);
94 | });
95 |
--------------------------------------------------------------------------------
/doc/README.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: MidJourney API. Unofficial Node.js client
3 | ---
4 |
5 | # 👋 MidJourney Node Client
6 |
7 | [](https://discord.gg/GavuGHQbV4) [](https://www.npmjs.com/package/midjourney)
8 |
9 | ## Get Started
10 |
11 |
12 |
13 | {% content-ref url="getting-started/quickstart.md" %}
14 | [quickstart.md](getting-started/quickstart.md)
15 | {% endcontent-ref %}
16 |
--------------------------------------------------------------------------------
/doc/SUMMARY.md:
--------------------------------------------------------------------------------
1 | # Table of contents
2 |
3 | * [👋 MidJourney Node Client](README.md)
4 |
5 | ## 📓 Getting started
6 |
7 | * [Quickstart](getting-started/quickstart.md)
8 | * [Install](getting-started/install.md)
9 | * [Config](getting-started/config.md)
10 | * [API Reference](getting-started/api-reference/README.md)
11 | * [imagine](getting-started/api-reference/imagine.md)
12 | * [variation](getting-started/api-reference/variation.md)
13 | * [upscale](getting-started/api-reference/upscale.md)
14 | * [reroll](getting-started/api-reference/reroll.md)
15 | * [blend](getting-started/api-reference/blend.md)
16 | * [describe](getting-started/api-reference/describe.md)
17 | * [info](getting-started/api-reference/info.md)
18 | * [fast](getting-started/api-reference/fast.md)
19 | * [relax](getting-started/api-reference/relax.md)
20 | * [setting](getting-started/api-reference/setting.md)
21 | * [reset](getting-started/api-reference/reset.md)
22 | * [Example](getting-started/example/README.md)
23 | * [Imagine](getting-started/example/imagine.md)
24 |
25 | ## FAQ
26 |
27 | * [vercel](faq/vercel.md)
28 | * [node.js <18](faq/node.js-less-than-18.md)
29 | * [proxy](faq/proxy.md)
30 | * [How to get your Discord Token](https://www.androidauthority.com/get-discord-token-3149920/)
31 | * [Invite Midjourney Bot to Your Server](https://docs.midjourney.com/docs/invite-the-bot)
32 |
33 | ***
34 |
35 | * [Join the Beta](https://discord.com/invite/GavuGHQbV4)
36 |
37 | ## Use Cases
38 |
39 | * [🎨 Web UI](use-cases/web-ui.md)
40 | * [🤖 Discord Bot](use-cases/discord-bot.md)
41 |
--------------------------------------------------------------------------------
/doc/faq/node.js-less-than-18.md:
--------------------------------------------------------------------------------
1 | # node.js <18
2 |
3 |
--------------------------------------------------------------------------------
/doc/faq/proxy.md:
--------------------------------------------------------------------------------
1 | # proxy
2 |
3 |
--------------------------------------------------------------------------------
/doc/faq/vercel.md:
--------------------------------------------------------------------------------
1 | # vercel
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/README.md:
--------------------------------------------------------------------------------
1 | # API Reference
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/blend.md:
--------------------------------------------------------------------------------
1 | # blend
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/describe.md:
--------------------------------------------------------------------------------
1 | # describe
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/fast.md:
--------------------------------------------------------------------------------
1 | # fast
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/imagine.md:
--------------------------------------------------------------------------------
1 | # imagine
2 |
3 |
4 |
5 | import { Midjourney } from "midjourney";
6 | async function main() {
7 | const client = new Midjourney({
8 | ServerId: "1082500871478329374",
9 | ChannelId: "1094892992281718894",
10 | SalaiToken: "your discord token",
11 | });
12 | await client.Connect();
13 | const Imagine = await client.Imagine(
14 | "Red hamster smoking a cigaret",
15 | (uri: string, progress: string) => {
16 | console.log("Imagine.loading", uri, "progress", progress);
17 | }
18 | );
19 | console.log( Imagine );
20 | client.Close();
21 | }
22 |
23 |
24 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/info.md:
--------------------------------------------------------------------------------
1 | # info
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/relax.md:
--------------------------------------------------------------------------------
1 | # relax
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/reroll.md:
--------------------------------------------------------------------------------
1 | # reroll
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/reset.md:
--------------------------------------------------------------------------------
1 | # reset
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/setting.md:
--------------------------------------------------------------------------------
1 | # setting
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/upscale.md:
--------------------------------------------------------------------------------
1 | # upscale
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/api-reference/variation.md:
--------------------------------------------------------------------------------
1 | # variation
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/config.md:
--------------------------------------------------------------------------------
1 | # Config
2 |
3 | Property | Type | Description | Required | Default |
---|
SalaiToken | string | Discord Token | true | |
ServerId | string | Discord server Id | false | null ( DM Midjourney bot) |
ChannelId | string | Discord Channel Id | true | 1077800642086703114 (Midjourney bot) |
SessionId | string | Discord Session Id | false | 8bb7f5b79c7a49f7d0824ab4b8773a81 |
Debug | boolean | print log | false | false |
Ws | boolean | websocket get message | false | false |
HuggingFaceToken | string | verify human | false | |
DiscordBaseUrl | string | Proxy Discord Url | false | |
WsBaseUrl | string | Proxy Discord Wsebsocket Url | false | |
Limit | number | | false | 50 |
MaxWait | number | | false | 200 |
fetch | fetch | node <18 OR proxy | false | |
websocket | websocket | proxy | false | |
4 |
--------------------------------------------------------------------------------
/doc/getting-started/example/README.md:
--------------------------------------------------------------------------------
1 | # Example
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/example/image.md:
--------------------------------------------------------------------------------
1 | # Image
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/example/imagine.md:
--------------------------------------------------------------------------------
1 | # Imagine
2 |
3 |
--------------------------------------------------------------------------------
/doc/getting-started/install.md:
--------------------------------------------------------------------------------
1 | ---
2 | description: Install MidJourney , the unofficial Node.js client
3 | ---
4 |
5 | # Install
6 |
7 | [](https://www.npmjs.com/package/midjourney)
8 |
9 | {% hint style="info" %}
10 | Make sure you're using `node >= 18` so `fetch` is available
11 | {% endhint %}
12 |
13 | ### npm
14 |
15 | ```
16 | npm i midjourney
17 | ```
18 |
19 | ### yarn
20 |
21 | ```
22 | yarn add midjourney
23 | ```
24 |
--------------------------------------------------------------------------------
/doc/getting-started/quickstart.md:
--------------------------------------------------------------------------------
1 | # Quickstart
2 |
3 | > #### **Step1: Get your Discord Token**[Join the Beta](https://discord.com/invite/GavuGHQbV4)
4 |
5 | {% content-ref url="broken-reference" %}
6 | [Broken link](broken-reference)
7 | {% endcontent-ref %}
8 |
9 | > #### **Step2: Join Discord Server**
10 |
11 | {% content-ref url="broken-reference" %}
12 | [Broken link](broken-reference)
13 | {% endcontent-ref %}
14 |
15 | > #### **Step3: Install** midjourney
16 |
17 | {% content-ref url="install.md" %}
18 | [install.md](install.md)
19 | {% endcontent-ref %}
20 |
21 | > #### **Step3:** Use the imagine api
22 |
23 | {% tabs %}
24 | {% tab title="Typescirpt" %}
25 | {% code overflow="wrap" lineNumbers="true" %}
26 | ```typescript
27 | import { Midjourney } from "midjourney";
28 |
29 | const client = new Midjourney({
30 | ServerId: "1082500871478329374",
31 | ChannelId: "1094892992281718894",
32 | SalaiToken: "your discord token",
33 | Debug: true,
34 | });
35 |
36 | const msg = await client.Imagine(
37 | "A little white elephant",
38 | (uri: string, progress: string) => {
39 | console.log("loading:", uri, "progress:", progress);
40 | }
41 | );
42 | console.log({ msg });
43 | ```
44 | {% endcode %}
45 | {% endtab %}
46 |
47 | {% tab title="Javascirpt" %}
48 | {% code overflow="wrap" lineNumbers="true" %}
49 | ```javascript
50 | const { Midjourney } = require("midjourney");
51 |
52 | const client = new Midjourney({
53 | ServerId: "1082500871478329374",
54 | ChannelId: "1094892992281718894",
55 | SalaiToken: "your discord token",
56 | Debug: true,
57 | Ws:true,
58 | });
59 | const msg = await client.Imagine("A little pink elephant", (uri, progress) => {
60 | console.log("loading:", uri, "progress:", progress);
61 | });
62 | console.log({ msg });
63 |
64 |
65 | ```
66 | {% endcode %}
67 | {% endtab %}
68 | {% endtabs %}
69 |
--------------------------------------------------------------------------------
/doc/use-cases/discord-bot.md:
--------------------------------------------------------------------------------
1 | # 🤖 Discord Bot
2 |
3 | [https://github.com/erictik/midjourney-discord/issues](https://github.com/erictik/midjourney-discord/issues)
4 |
--------------------------------------------------------------------------------
/doc/use-cases/web-ui.md:
--------------------------------------------------------------------------------
1 | # 🎨 Web UI
2 |
3 | ## [https://github.com/erictik/midjourney-ui](https://github.com/erictik/midjourney-ui)
4 |
--------------------------------------------------------------------------------
/example/banend-words.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney, detectBannedWords } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the banned words
6 | * ```
7 | * npx tsx example/banend-words.ts
8 | * ```
9 | */
10 | async function main() {
11 | var prompt = "horny girl";
12 | var message = detectBannedWords(prompt);
13 | if (message.length > 0) {
14 | console.error("banned words detected");
15 | }
16 | console.log(message);
17 | }
18 | main().catch((err) => {
19 | console.error(err);
20 | process.exit(1);
21 | });
22 |
--------------------------------------------------------------------------------
/example/customzoom.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the (custom zoom) options with ws command
6 | * ```
7 | * npx tsx example/customzoom.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true, //enable ws is required for custom zoom
17 | });
18 | await client.init();
19 | const prompt =
20 | "Christmas dinner with spaghetti with family in a cozy house, we see interior details , simple blue&white illustration";
21 | //imagine
22 | const Imagine = await client.Imagine(
23 | prompt,
24 | (uri: string, progress: string) => {
25 | console.log("loading", uri, "progress", progress);
26 | }
27 | );
28 | console.log(Imagine);
29 | if (!Imagine) {
30 | console.log("no message");
31 | return;
32 | }
33 | //U1 U2 U3 U4 V1 V2 V3 V4 "Vary (Strong)" ...
34 | const V1CustomID = Imagine.options?.find((o) => o.label === "V1")?.custom;
35 | if (!V1CustomID) {
36 | console.log("no V1");
37 | return;
38 | }
39 | // Varition V1
40 | const Varition = await client.Custom({
41 | msgId: Imagine.id,
42 | flags: Imagine.flags,
43 | customId: V1CustomID,
44 | loading: (uri: string, progress: string) => {
45 | console.log("loading", uri, "progress", progress);
46 | },
47 | });
48 | console.log(Varition);
49 | const U1CustomID = Imagine.options?.find((o) => o.label === "U1")?.custom;
50 | if (!U1CustomID) {
51 | console.log("no U1");
52 | return;
53 | }
54 | // Upscale U1
55 | const Upscale = await client.Custom({
56 | msgId: Imagine.id,
57 | flags: Imagine.flags,
58 | customId: U1CustomID,
59 | loading: (uri: string, progress: string) => {
60 | console.log("loading", uri, "progress", progress);
61 | },
62 | });
63 | if (!Upscale) {
64 | console.log("no Upscale");
65 | return;
66 | }
67 | console.log(Upscale);
68 | const zoomout = Upscale?.options?.find((o) => o.label === "Custom Zoom");
69 | if (!zoomout) {
70 | console.log("no zoomout");
71 | return;
72 | }
73 | // Custom Zoom
74 | const CustomZoomout = await client.Custom({
75 | msgId: Upscale.id,
76 | flags: Upscale.flags,
77 | content: `${prompt} --zoom 2`,
78 | customId: zoomout.custom,
79 | loading: (uri: string, progress: string) => {
80 | console.log("loading", uri, "progress", progress);
81 | },
82 | });
83 | console.log("Custom Zoom", CustomZoomout);
84 | client.Close();
85 | }
86 | main()
87 | .then(() => {
88 | console.log("done");
89 | })
90 | .catch((err) => {
91 | console.error(err);
92 | process.exit(1);
93 | });
94 |
--------------------------------------------------------------------------------
/example/describe.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of using the describe api
6 | * ```
7 | * npx tsx example/describe.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true,
17 | });
18 | await client.Connect();
19 | const Describe = await client.Describe(
20 | "https://cdn.discordapp.com/attachments/1107965981839605792/1119977411631652914/Soga_a_cool_cat_blue_ears_yellow_hat_02afd1ed-17eb-4a61-9101-7a99b105e4cc.png"
21 | );
22 | console.log(Describe);
23 | if (!Describe) {
24 | console.log("failed to describe");
25 | }
26 | }
27 | main().catch((err) => {
28 | console.log("finished");
29 | console.error(err);
30 | process.exit(1);
31 | });
32 |
--------------------------------------------------------------------------------
/example/faceswap.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney, detectBannedWords } from "../src";
3 | import { url } from "inspector";
4 | /**
5 | *
6 | * a simple example of how to use faceSwap
7 | * ```
8 | * npx tsx example/faceswap.ts
9 | * ```
10 | */
11 | async function main() {
12 | const source = `https://cdn.discordapp.com/attachments/1107965981839605792/1129362418775113789/3829c5d7-3e7e-473c-9c7b-b858e3ec97bc.jpeg`;
13 | // const source = `https://cdn.discordapp.com/attachments/1108587422389899304/1129321826804306031/guapitu006_Cute_warrior_girl_in_the_style_of_Baten_Kaitos__111f39bc-329e-4fab-9af7-ee219fedf260.png`;
14 | const target = `https://cdn.discordapp.com/attachments/1108587422389899304/1129321837042602016/guapitu006_a_girls_face_with_david_bowies_thunderbolt_71ee5899-bd45-4fc4-8c9d-92f19ddb0a03.png`;
15 | const client = new Midjourney({
16 | ServerId: process.env.SERVER_ID,
17 | ChannelId: process.env.CHANNEL_ID,
18 | SalaiToken: process.env.SALAI_TOKEN,
19 | Debug: true,
20 | HuggingFaceToken: process.env.HUGGINGFACE_TOKEN,
21 | });
22 | const info = await client.FaceSwap(target, source);
23 | console.log(info?.uri);
24 | }
25 | main()
26 | .then(() => {
27 | console.log("finished");
28 | process.exit(0);
29 | })
30 | .catch((err) => {
31 | console.error(err);
32 | process.exit(1);
33 | });
34 |
--------------------------------------------------------------------------------
/example/fast.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of using the fast api
6 | * ```
7 | * npx tsx example/fast.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true,
17 | });
18 | await client.Connect();
19 | await client.Fast();
20 | const msg = await client.Info();
21 | console.log({ msg });
22 | client.Close();
23 | }
24 | main()
25 | .then(() => {
26 | console.log("finished");
27 | process.exit(0);
28 | })
29 | .catch((err) => {
30 | console.log("finished");
31 | console.error(err);
32 | process.exit(1);
33 | });
34 |
--------------------------------------------------------------------------------
/example/imagine-blend.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the blend
6 | * ```
7 | * npx tsx example/blend.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | });
17 | await client.Connect();
18 | const msg = await client.Imagine(
19 | "https://media.discordapp.net/attachments/1094892992281718894/1106660210380132503/Soga_A_Greek_man_with_mustache_in_national_costume_riding_a_don_3255e7c1-38ee-4892-b7c7-9f0dc3f2786d.png?width=1040&height=1040 https://cdn.discordapp.com/attachments/1094892992281718894/1106798152188702720/Soga__489d80b2-db74-4a93-a998-881a9542abbe.png",
20 | (uri: string, progress: string) => {
21 | console.log("loading", uri, "progress", progress);
22 | }
23 | );
24 | console.log({ msg });
25 | if (!msg) {
26 | console.log("no message");
27 | return;
28 | }
29 | const msg2 = await client.Upscale({
30 | index: 2,
31 | msgId: msg.id,
32 | hash: msg.hash,
33 | flags: msg.flags,
34 | content: msg.content,
35 | loading: (uri: string, progress: string) => {
36 | console.log("loading", uri, "progress", progress);
37 | },
38 | });
39 | console.log({ msg2 });
40 | }
41 | main().catch((err) => {
42 | console.error(err);
43 | process.exit(1);
44 | });
45 |
--------------------------------------------------------------------------------
/example/imagine-dm.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of using the imagine api via DM Midjourney Bot
6 | * ```
7 | * npx tsx example/imagine-dm.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | SalaiToken: process.env.SALAI_TOKEN,
13 | Debug: true,
14 | Ws: true,
15 | });
16 | await client.Connect();
17 | // const info = await client.Info();
18 | // console.log(info);
19 | // return
20 | const msg = await client.Imagine(
21 | "A little white dog",
22 | (uri: string, progress: string) => {
23 | console.log("loading", uri, "progress", progress);
24 | }
25 | );
26 | console.log({ msg });
27 | }
28 | main()
29 | .then(() => {
30 | console.log("finished");
31 | process.exit(0);
32 | })
33 | .catch((err) => {
34 | console.log("finished");
35 | console.error(err);
36 | process.exit(1);
37 | });
38 |
--------------------------------------------------------------------------------
/example/imagine-err.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the imagine command
6 | * ```
7 | * npx tsx example/imagine-err.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | });
17 | await client.init();
18 | // `https://images.guapitu.com/chatgpt/5b9b907a/d3297338-ae3e-4276-9bd9-3b6ca27cedcf.png
19 | // https://images.guapitu.com/chatgpt/762a2db4/459d52f1-23fd-41c3-a912-317e65155fcc.png
20 | // https://images.guapitu.com/chatgpt/f86613ac/2e2497ae-9906-44d9-8396-e41abab2f47b.png
21 | // cat`
22 | const prompt = `%s %sTiny cute isometric Hcia illustration, a girl with long white hair, smile, seawater, colorful bubbles, dreamy portrait, Teana punk, more details, fiber tracking, snail core, Kuvshinov Ilya, yakamoz emoji, soft lighting, soft colors, matte clay, blender 3d, pastel background --v 5.1 --ar 1:1 --s 350 --q 1`;
23 | const msg = await client.Imagine(prompt, (uri: string, progress: string) => {
24 | console.log("loading", uri, "progress", progress);
25 | });
26 | console.log({ msg });
27 | }
28 | main().catch((err) => {
29 | console.error(err);
30 | process.exit(1);
31 | });
32 |
--------------------------------------------------------------------------------
/example/imagine-niji.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney, NijiBot } from "../src";
3 | /**
4 | *
5 | * a simple example of using the imagine api via DM Niji Bot
6 | * ```
7 | * npx tsx example/imagine-niji.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | SalaiToken: process.env.SALAI_TOKEN,
13 | BotId: NijiBot, // NijiBot
14 | ChannelId: "1125452970276954204",
15 | Debug: true,
16 | Ws: true,
17 | });
18 | await client.Connect();
19 | const info = await client.Info();
20 | console.log(info);
21 | const msg = await client.Imagine(
22 | "A little white dog",
23 | (uri: string, progress: string) => {
24 | console.log("loading", uri, "progress", progress);
25 | }
26 | );
27 | console.log( msg );
28 | client.Close();
29 | }
30 | main()
31 | .then(() => {
32 | // console.log("finished");
33 | // process.exit(0);
34 | })
35 | .catch((err) => {
36 | console.log("finished");
37 | console.error(err);
38 | process.exit(1);
39 | });
40 |
--------------------------------------------------------------------------------
/example/imagine-ws-m.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of using the imagine api with ws
6 | * ```
7 | * npx tsx example/imagine-ws-m.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | HuggingFaceToken: process.env.HUGGINGFACE_TOKEN,
16 | Debug: true,
17 | });
18 | await client.Connect();
19 | client
20 | .Imagine("A little pink elephant", (uri) => {
21 | console.log("loading123---", uri);
22 | })
23 | .then(function (msg) {
24 | console.log("msg123", msg);
25 | });
26 |
27 | client
28 | .Imagine("A little pink dog", (uri) => {
29 | console.log("loading234---", uri);
30 | })
31 | .then(function (msg) {
32 | console.log("msg234", msg);
33 | });
34 | }
35 | main()
36 | .then(() => {
37 | console.log("finished");
38 | // process.exit(0);
39 | })
40 | .catch((err) => {
41 | console.log("finished");
42 | console.error(err);
43 | process.exit(1);
44 | });
45 |
--------------------------------------------------------------------------------
/example/imagine-ws.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of using the imagine api with ws
6 | * ```
7 | * npx tsx example/imagine-ws.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | HuggingFaceToken: process.env.HUGGINGFACE_TOKEN,
16 | Debug: true,
17 | Ws: true, // required `Only you can see this`
18 | });
19 | await client.Connect(); // required
20 | const Imagine = await client.Imagine(
21 | "Red hamster smoking a cigaret --fast",
22 | (uri: string, progress: string) => {
23 | console.log("Imagine.loading", uri, "progress", progress);
24 | }
25 | );
26 | console.log({ Imagine });
27 | if (!Imagine) {
28 | return;
29 | }
30 | const reroll = await client.Reroll({
31 | msgId: Imagine.id,
32 | hash: Imagine.hash,
33 | flags: Imagine.flags,
34 | loading: (uri: string, progress: string) => {
35 | console.log("Reroll.loading", uri, "progress", progress);
36 | },
37 | });
38 | console.log({ reroll });
39 |
40 | const Variation = await client.Variation({
41 | index: 2,
42 | msgId: Imagine.id,
43 | hash: Imagine.hash,
44 | flags: Imagine.flags,
45 | loading: (uri: string, progress: string) => {
46 | console.log("Variation.loading", uri, "progress", progress);
47 | },
48 | });
49 |
50 | console.log({ Variation });
51 | if (!Variation) {
52 | return;
53 | }
54 | const Upscale = await client.Upscale({
55 | index: 2,
56 | msgId: Variation.id,
57 | hash: Variation.hash,
58 | flags: Variation.flags,
59 | loading: (uri: string, progress: string) => {
60 | console.log("Upscale.loading", uri, "progress", progress);
61 | },
62 | });
63 | console.log({ Upscale });
64 |
65 | client.Close();
66 | }
67 | main()
68 | .then(() => {
69 | // console.log("finished");
70 | // process.exit(0);
71 | })
72 | .catch((err) => {
73 | console.log("finished");
74 | console.error(err);
75 | process.exit(1);
76 | });
77 |
--------------------------------------------------------------------------------
/example/imagine.js:
--------------------------------------------------------------------------------
1 | require("dotenv").config();
2 | const { Midjourney } = require("../libs");
3 | /**
4 | *
5 | * a simple example of how to use the imagine command
6 | * ```
7 | * node example/imagine.js
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true,
17 | SessionId: process.env.SALAI_TOKEN || "8bb7f5b79c7a49f7d0824ab4b8773a81",
18 | });
19 | await client.init();
20 | const msg = await client.Imagine("A little pink elephant", (uri) => {
21 | console.log("loading", uri);
22 | });
23 | console.log({ msg });
24 | }
25 | main().catch((err) => {
26 | console.error(err);
27 | process.exit(1);
28 | });
29 |
--------------------------------------------------------------------------------
/example/imagine.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the imagine command
6 | * ```
7 | * npx tsx example/imagine.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: false,
17 | });
18 |
19 | const msg = await client.Imagine(
20 | "Red hamster",
21 | (uri: string, progress: string) => {
22 | console.log("loading", uri, "progress", progress);
23 | }
24 | );
25 | console.log(JSON.stringify(msg));
26 | }
27 | main().catch((err) => {
28 | console.error(err);
29 | process.exit(1);
30 | });
31 |
--------------------------------------------------------------------------------
/example/info.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of using the info api
6 | * ```
7 | * npx tsx example/info.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true,
17 | });
18 | await client.Connect();
19 | const msg = await client.Info();
20 | console.log({ msg });
21 | client.Close();
22 | }
23 | main()
24 | .then(() => {
25 | console.log("finished");
26 | process.exit(0);
27 | })
28 | .catch((err) => {
29 | console.log("finished");
30 | console.error(err);
31 | process.exit(1);
32 | });
33 |
--------------------------------------------------------------------------------
/example/prefer-remix.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of using the prefer remix api
6 | * ```
7 | * npx tsx example/prefer-remix.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true, //enable ws is required for prefer remix
17 | });
18 | await client.Connect();
19 | const msg = await client.SwitchRemix();
20 | console.log(msg);
21 | client.Close();
22 | }
23 | main()
24 | .then(() => {
25 | console.log("finished");
26 | process.exit(0);
27 | })
28 | .catch((err) => {
29 | console.log("finished");
30 | console.error(err);
31 | process.exit(1);
32 | });
33 |
--------------------------------------------------------------------------------
/example/relax.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of using the relax api
6 | * ```
7 | * npx tsx example/relax.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true,
17 | });
18 | await client.Connect();
19 | await client.Relax();
20 | const msg = await client.Info();
21 | console.log({ msg });
22 | client.Close();
23 | }
24 | main()
25 | .then(() => {
26 | console.log("finished");
27 | process.exit(0);
28 | })
29 | .catch((err) => {
30 | console.log("finished");
31 | console.error(err);
32 | process.exit(1);
33 | });
34 |
--------------------------------------------------------------------------------
/example/reset.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of using the reset api
6 | * ```
7 | * npx tsx example/reset.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true,
17 | });
18 | await client.Connect();
19 | await client.Reset();
20 | client.Close();
21 | }
22 | main()
23 | .then(() => {
24 | console.log("finished");
25 | process.exit(0);
26 | })
27 | .catch((err) => {
28 | console.log("finished");
29 | console.error(err);
30 | process.exit(1);
31 | });
32 |
--------------------------------------------------------------------------------
/example/settings.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of using the settings api
6 | * ```
7 | * npx tsx example/settings.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true, //Important
17 | });
18 | await client.Connect();
19 | const msg = await client.Settings();
20 | console.log(msg);
21 | if (!msg) {
22 | return;
23 | }
24 | // //niji5
25 | const niji5 = msg.options.filter((x) => {
26 | return x.label === "Niji version 5";
27 | })[0];
28 | console.log(niji5);
29 | // const httpstatus = await client.MJApi.CustomApi({
30 | // msgId: msg.id,
31 | // customId: niji5.custom,
32 | // flags: msg.flags,
33 | // });
34 | // console.log({ httpstatus });
35 | // const setting = await client.Settings();
36 | // console.log({ setting });
37 | //reset settings
38 |
39 | client.Close();
40 | }
41 | main()
42 | .then(() => {
43 | console.log("finished");
44 | process.exit(0);
45 | })
46 | .catch((err) => {
47 | console.log("finished");
48 | console.error(err);
49 | process.exit(1);
50 | });
51 |
--------------------------------------------------------------------------------
/example/shorten.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | import { sleep } from "../src/utls";
4 | /**
5 | *
6 | * a simple example of using the shorten api
7 | * ```
8 | * npx tsx example/shorten.ts
9 | * ```
10 | */
11 | async function main() {
12 | const client = new Midjourney({
13 | ServerId: process.env.SERVER_ID,
14 | ChannelId: process.env.CHANNEL_ID,
15 | SalaiToken: process.env.SALAI_TOKEN,
16 | Debug: true,
17 | Ws: true,
18 | });
19 | await client.Connect();
20 | const Shorten = await client.Shorten(
21 | "Peeking out from the bushes, masterpiece, octane rendering, focus, realistic photography, colorful background, detailed, intricate details, rich colors, realistic style"
22 | );
23 | console.log(Shorten);
24 | if (!Shorten) {
25 | console.log("no message");
26 | return;
27 | }
28 | const prompt = Shorten.options.find((o) => o.label === `1️⃣`);
29 | if (!prompt) {
30 | console.log("no prompt");
31 | return;
32 | }
33 | await sleep(1400);
34 | //shorten click
35 | // const imagine = await client.Custom({
36 | // msgId: Shorten.id,
37 | // flags: Shorten.flags,
38 | // content: Shorten.prompts[0],
39 | // customId: prompt.custom,
40 | // loading: (uri: string, progress: string) => {
41 | // console.log("loading", uri, "progress", progress);
42 | // },
43 | // });
44 | // console.log("Custom Zoom", zoomout2x);
45 |
46 | client.Close();
47 | }
48 | main().catch((err) => {
49 | console.log("finished");
50 | console.error(err);
51 | process.exit(1);
52 | });
53 |
--------------------------------------------------------------------------------
/example/upscale-ws.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the Upscale with ws command
6 | * ```
7 | * npx tsx example/upscale-ws.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | // Debug: true,
16 | Ws: true,
17 | });
18 | await client.Connect();
19 | const Imagine = await client.Imagine(
20 | "a cool cat, blue ears, yellow hat --v 4",
21 | (uri: string, progress: string) => {
22 | console.log("loading", uri, "progress", progress);
23 | }
24 | );
25 | console.log(Imagine);
26 | if (!Imagine) {
27 | console.log("no message");
28 | return;
29 | }
30 | const Upscale = await client.Upscale({
31 | index: 2,
32 | msgId: Imagine.id,
33 | hash: Imagine.hash,
34 | flags: Imagine.flags,
35 | loading: (uri: string, progress: string) => {
36 | console.log("loading", uri, "progress", progress);
37 | },
38 | });
39 | console.log(Upscale);
40 | client.Close();
41 | }
42 | main().catch((err) => {
43 | console.error(err);
44 | process.exit(1);
45 | });
46 |
--------------------------------------------------------------------------------
/example/upscale.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the Upscale command
6 | * ```
7 | * npx tsx example/upscale.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | });
17 | const msg = await client.Imagine("a cool cat, blue ears, yellow hat");
18 | console.log({ msg });
19 | if (!msg) {
20 | console.log("no message");
21 | return;
22 | }
23 | const msg2 = await client.Upscale({
24 | index: 2,
25 | msgId: msg.id,
26 | hash: msg.hash,
27 | flags: msg.flags,
28 | content: msg.content,
29 | loading: (uri: string, progress: string) => {
30 | console.log("loading", uri, "progress", progress);
31 | },
32 | });
33 | console.log({ msg2 });
34 | }
35 | main().catch((err) => {
36 | console.error(err);
37 | process.exit(1);
38 | });
39 |
--------------------------------------------------------------------------------
/example/variation-ws.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the Variation (remix mode) with ws command
6 | * ```
7 | * npx tsx example/variation-ws.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true, //enable ws is required for remix mode
17 | });
18 | await client.init(); //init auto enable remix mode
19 | const prompt =
20 | "48 year old woman with auburn hair plays video games on a tablet in her bedroom and is a chemist. Engaged. Happy. Evening. Silver blue walls in room. In the style of anime. does not exceed 10 MB.";
21 | const Imagine = await client.Imagine(
22 | prompt,
23 | (uri: string, progress: string) => {
24 | console.log("Imagine.loading", uri, "progress", progress);
25 | }
26 | );
27 | console.log(Imagine);
28 | if (!Imagine) {
29 | console.log("no message");
30 | return;
31 | }
32 | const Variation = await client.Variation({
33 | index: 1,
34 | msgId: Imagine.id,
35 | hash: Imagine.hash,
36 | flags: Imagine.flags,
37 | content: prompt,
38 | loading: (uri: string, progress: string) => {
39 | console.log("Variation1.loading", uri, "progress", progress);
40 | },
41 | });
42 | console.log("Variation", Variation);
43 | if (!Variation) {
44 | console.log("no Variation");
45 | return;
46 | }
47 |
48 | const Upscale = await client.Upscale({
49 | index: 2,
50 | msgId: Variation.id,
51 | hash: Variation.hash,
52 | flags: Variation.flags,
53 | content: prompt,
54 | loading: (uri: string, progress: string) => {
55 | console.log("Upscale.loading", uri, "progress", progress);
56 | },
57 | });
58 | console.log("Upscale", Upscale);
59 | // client
60 | // .Variation({
61 | // index: 2,
62 | // msgId: Imagine.id,
63 | // hash: Imagine.hash,
64 | // flags: Imagine.flags,
65 | // loading: (uri: string, progress: string) => {
66 | // console.log("Variation2.loading", uri, "progress", progress);
67 | // },
68 | // })
69 | // .then((msg2) => {
70 | // console.log({ msg2 });
71 | // });
72 | // client
73 | // .Variation({
74 | // index: 3,
75 | // msgId: Imagine.id,
76 | // hash: Imagine.hash,
77 | // flags: Imagine.flags,
78 | // loading: (uri: string, progress: string) => {
79 | // console.log("Variation3.loading", uri, "progress", progress);
80 | // },
81 | // })
82 | // .then((msg3) => {
83 | // console.log({ msg3 });
84 | // });
85 | // client
86 | // .Variation({
87 | // index: 4,
88 | // msgId: Imagine.id,
89 | // hash: Imagine.hash,
90 | // flags: Imagine.flags,
91 | // loading: (uri: string, progress: string) => {
92 | // console.log("Variation4.loading", uri, "progress", progress);
93 | // },
94 | // })
95 | // .then((msg4) => {
96 | // console.log({ msg4 });
97 | // });
98 | }
99 | main().catch((err) => {
100 | console.error(err);
101 | process.exit(1);
102 | });
103 |
--------------------------------------------------------------------------------
/example/variation.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the Variation command
6 | * ```
7 | * npx tsx example/variation.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | });
17 | const msg = await client.Imagine(
18 | "crazy donkey pilot, hyperrealism, rembrandt lighting, 32k, volumetric lighting, air, tonal perspective, sharp focus 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 ",
19 | (uri: string, progress: string) => {
20 | console.log("Imagine.loading", uri, "progress", progress);
21 | }
22 | );
23 | console.log({ msg });
24 | if (!msg) {
25 | console.log("no message");
26 | return;
27 | }
28 | const Variation = await client.Variation({
29 | index: 1,
30 | msgId: msg.id,
31 | hash: msg.hash,
32 | flags: msg.flags,
33 | content: msg.content,
34 | loading: (uri: string, progress: string) => {
35 | console.log("Variation.loading", uri, "progress", progress);
36 | },
37 | });
38 | console.log({ Variation });
39 | const Variation2 = await client.Variation({
40 | index: 2,
41 | msgId: msg.id,
42 | hash: msg.hash,
43 | flags: msg.flags,
44 | content: msg.content,
45 | loading: (uri: string, progress: string) => {
46 | console.log("Variation2.loading", uri, "progress", progress);
47 | },
48 | });
49 | console.log({ Variation2 });
50 | }
51 | main().catch((err) => {
52 | console.error(err);
53 | process.exit(1);
54 | });
55 |
--------------------------------------------------------------------------------
/example/vary.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the vary
6 | * ```
7 | * npx tsx example/vary.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true, //enable ws is required for custom zoom
17 | });
18 | await client.init();
19 | const prompt =
20 | "Christmas dinner with spaghetti with family in a cozy house, we see interior details , simple blue&white illustration";
21 | const Imagine = await client.Imagine(
22 | prompt,
23 | (uri: string, progress: string) => {
24 | console.log("loading", uri, "progress", progress);
25 | }
26 | );
27 | console.log(Imagine);
28 | if (!Imagine) {
29 | console.log("no message");
30 | return;
31 | }
32 | const Upscale = await client.Upscale({
33 | index: 2,
34 | msgId: Imagine.id,
35 | hash: Imagine.hash,
36 | flags: Imagine.flags,
37 | loading: (uri: string, progress: string) => {
38 | console.log("loading", uri, "progress", progress);
39 | },
40 | });
41 | if (!Upscale) {
42 | console.log("no message");
43 | return;
44 | }
45 | console.log(Upscale);
46 |
47 | const vary = Upscale?.options?.find((o) => o.label === "Vary (Strong)");
48 | if (!vary) {
49 | console.log("no zoomout");
50 | return;
51 | }
52 | const varyCustom = await client.Custom({
53 | msgId: Upscale.id,
54 | flags: Upscale.flags,
55 | content: `${prompt} --zoom 2`,
56 | customId: vary.custom,
57 | loading: (uri: string, progress: string) => {
58 | console.log("loading", uri, "progress", progress);
59 | },
60 | });
61 | console.log("vary (Strong)", varyCustom);
62 | client.Close();
63 | }
64 | main()
65 | .then(() => {
66 | console.log("done");
67 | })
68 | .catch((err) => {
69 | console.error(err);
70 | process.exit(1);
71 | });
72 |
--------------------------------------------------------------------------------
/example/zoomout.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Midjourney } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the zoomout with ws command
6 | * ```
7 | * npx tsx example/zoomout.ts
8 | * ```
9 | */
10 | async function main() {
11 | const client = new Midjourney({
12 | ServerId: process.env.SERVER_ID,
13 | ChannelId: process.env.CHANNEL_ID,
14 | SalaiToken: process.env.SALAI_TOKEN,
15 | Debug: true,
16 | Ws: true,
17 | });
18 | await client.Connect();
19 | const Imagine = await client.Imagine("a cool cat, blue ears, yellow hat");
20 | console.log(Imagine);
21 | if (!Imagine) {
22 | console.log("no message");
23 | return;
24 | }
25 | const Upscale = await client.Upscale({
26 | index: 2,
27 | msgId: Imagine.id,
28 | hash: Imagine.hash,
29 | flags: Imagine.flags,
30 | loading: (uri: string, progress: string) => {
31 | console.log("loading", uri, "progress", progress);
32 | },
33 | });
34 | if (!Upscale) {
35 | console.log("no message");
36 | return;
37 | }
38 | console.log(Upscale);
39 | const Zoomout = await client.ZoomOut({
40 | level: "2x",
41 | msgId: Upscale.id,
42 | hash: Upscale.hash,
43 | flags: Upscale.flags,
44 | loading: (uri: string, progress: string) => {
45 | console.log("loading", uri, "progress", progress);
46 | },
47 | });
48 | console.log(Zoomout);
49 |
50 | client.Close();
51 | }
52 | main().catch((err) => {
53 | console.error(err);
54 | process.exit(1);
55 | });
56 |
--------------------------------------------------------------------------------
/images/ali.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erictik/midjourney-api/9f78a5b9a9e4d699add1b27c5de7d33142f5aca5/images/ali.png
--------------------------------------------------------------------------------
/images/wechat.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/erictik/midjourney-api/9f78a5b9a9e4d699add1b27c5de7d33142f5aca5/images/wechat.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "midjourney",
3 | "version": "4.0.0",
4 | "description": "Node.js client for the unofficial MidJourney API.",
5 | "main": "libs/index.js",
6 | "types": "libs/index.d.ts",
7 | "source": "./src/index.ts",
8 | "scripts": {
9 | "test": "echo \"Error: no test specified\" && exit 1",
10 | "build": "tsc",
11 | "prettier": "prettier --write .",
12 | "prettier:check": "prettier --check .",
13 | "example:imagine": "tsx ./examples/imagine-ws.ts"
14 | },
15 | "files": [
16 | "libs/*",
17 | "src/*",
18 | "*.md"
19 | ],
20 | "repository": {
21 | "type": "git",
22 | "url": "git+https://github.com/erictik/midjourney-client.git"
23 | },
24 | "keywords": [
25 | "midjourney-client",
26 | "midjourney"
27 | ],
28 | "engines": {
29 | "node": ">=18"
30 | },
31 | "author": "Eric Yang ",
32 | "license": "ISC",
33 | "bugs": {
34 | "url": "https://github.com/erictik/midjourney-client/issues"
35 | },
36 | "homepage": "https://github.com/erictik/midjourney-client#readme",
37 | "devDependencies": {
38 | "@types/async": "^3.2.20",
39 | "@types/node": "^18.16.0",
40 | "@types/ws": "^8.5.4",
41 | "dotenv": "^16.0.3",
42 | "prettier": "^2.8.8",
43 | "ts-node": "^10.9.1",
44 | "tsx": "^3.12.6",
45 | "typescript": "^5.0.4"
46 | },
47 | "dependencies": {
48 | "@huggingface/inference": "^2.5.0",
49 | "async": "^3.2.4",
50 | "isomorphic-ws": "^5.0.0",
51 | "semiver": "^1.1.0",
52 | "snowyflake": "^2.0.0",
53 | "tslib": "^2.5.0",
54 | "ws": "^8.13.0"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/banned.words.ts:
--------------------------------------------------------------------------------
1 | // https://tokenizedhq.com/list-of-banned-words-in-midjourney/
2 | // https://www.greataiprompts.com/imageprompt/list-of-banned-words-in-midjourney/
3 | export const bannedWords = [
4 | "hot slut",
5 | "blood",
6 | "twerk",
7 | "making love",
8 | "voluptuous",
9 | "naughty",
10 | "wincest",
11 | "orgy",
12 | "no clothes",
13 | "au naturel",
14 | "no shirt",
15 | "decapitate",
16 | "bare",
17 | "nude",
18 | "barely dressed",
19 | "nude",
20 | "bra",
21 | "risque",
22 | "scantily clad",
23 | "cleavage",
24 | "stripped",
25 | "infested",
26 | "full frontal",
27 | "unclothed",
28 | "invisible clothes",
29 | "wearing nothing",
30 | "lingerie",
31 | "with no shirt",
32 | "naked",
33 | "without clothes on",
34 | "negligee",
35 | "zero clothes",
36 | "gruesome",
37 | "fascist",
38 | "nazi",
39 | "prophet mohammed",
40 | "slave",
41 | "coon",
42 | "honkey",
43 | "cocaine",
44 | "heroin",
45 | "meth",
46 | "crack",
47 | "kill",
48 | "belle delphine",
49 | "hitler",
50 | "jinping",
51 | "lolita",
52 | "president xi",
53 | "torture",
54 | "disturbing",
55 | "farts",
56 | "fart",
57 | "poop",
58 | "infected",
59 | "warts",
60 | "shit",
61 | "brown pudding",
62 | "bunghole",
63 | "vomit",
64 | "voluptuous",
65 | "seductive",
66 | "sperm",
67 | "sexy",
68 | "sadist",
69 | "sensored",
70 | "censored",
71 | "silenced",
72 | "deepfake",
73 | "inappropriate",
74 | "waifu",
75 | "succubus",
76 | "slaughter",
77 | "surgery",
78 | "reproduce",
79 | "crucified",
80 | "seductively",
81 | "explicit",
82 | "inappropriate",
83 | "large bust",
84 | "explicit",
85 | "wang",
86 | "inappropriate",
87 | "teratoma",
88 | "intimate",
89 | "see through",
90 | "tryphophobia",
91 | "bloodbath",
92 | "wound",
93 | "cronenberg",
94 | "khorne",
95 | "cannibal",
96 | "cannibalism",
97 | "visceral",
98 | "guts",
99 | "bloodshot",
100 | "gory",
101 | "killing",
102 | "crucifixion",
103 | "surgery",
104 | "vivisection",
105 | "massacre",
106 | "hemoglobin",
107 | "suicide",
108 | "arse",
109 | "labia",
110 | "ass",
111 | "mammaries",
112 | "badonkers",
113 | "bloody",
114 | "minge",
115 | "big ass",
116 | "mommy milker",
117 | "booba",
118 | "nipple",
119 | "oppai",
120 | "booty",
121 | "organs",
122 | "bosom",
123 | "ovaries",
124 | "flesh",
125 | "breasts",
126 | "penis",
127 | "busty",
128 | "phallus",
129 | "clunge",
130 | "sexy female",
131 | "crotch",
132 | "skimpy",
133 | "dick",
134 | "thick",
135 | "bruises",
136 | "girth",
137 | "titty",
138 | "honkers",
139 | "vagina",
140 | "hooters",
141 | "veiny",
142 | "knob",
143 | "ahegao",
144 | "pinup",
145 | "ballgag",
146 | "car crash",
147 | "playboy",
148 | "bimbo",
149 | "pleasure",
150 | "bodily fluids",
151 | "pleasures",
152 | "boudoir",
153 | "rule34",
154 | "brothel",
155 | "seducing",
156 | "dominatrix",
157 | "corpse",
158 | "seductive",
159 | "erotic",
160 | "seductive",
161 | "fuck",
162 | "sensual",
163 | "hardcore",
164 | "sexy",
165 | "hentai",
166 | "shag",
167 | "horny",
168 | "crucified",
169 | "shibari",
170 | "incest",
171 | "smut",
172 | "jav",
173 | "succubus",
174 | "jerk off king at pic",
175 | "thot",
176 | "kinbaku",
177 | "legs spread",
178 | "sensuality",
179 | "belly button",
180 | "porn",
181 | "patriotic",
182 | "bleed",
183 | "excrement",
184 | "petite",
185 | "seduction",
186 | "mccurry",
187 | "provocative",
188 | "sultry",
189 | "erected",
190 | "camisole",
191 | "tight white",
192 | "arrest",
193 | "see-through",
194 | "feces",
195 | "anus",
196 | "revealing clothing",
197 | "vein",
198 | "loli",
199 | "-edge",
200 | "boobs",
201 | "-backed",
202 | "tied up",
203 | "zedong",
204 | "bathing",
205 | "jail",
206 | "reticulum",
207 | "rear end",
208 | "sakimichan",
209 | "behind bars",
210 | "shirtless",
211 | "sakimichan",
212 | "seductive",
213 | "sexi",
214 | "sexualiz",
215 | "sexual",
216 | ];
217 | export function detectBannedWords(prompt: string): string[] {
218 | const matches: string[] = [];
219 | bannedWords.forEach((word) => {
220 | const regex = new RegExp(`\\b${word}\\b`, "gi");
221 | const wordMatches = prompt.match(regex);
222 | if (wordMatches) {
223 | wordMatches.forEach((match) => {
224 | matches.push(match);
225 | });
226 | }
227 | });
228 | return matches;
229 | }
230 |
--------------------------------------------------------------------------------
/src/command.ts:
--------------------------------------------------------------------------------
1 | import { DiscordImage, MJConfig } from "./interfaces";
2 | import async from "async";
3 | import { sleep } from "./utils";
4 | export const Commands = [
5 | "ask",
6 | "blend",
7 | "describe",
8 | "fast",
9 | "help",
10 | "imagine",
11 | "info",
12 | "prefer",
13 | "private",
14 | "public",
15 | "relax",
16 | "settings",
17 | "show",
18 | "stealth",
19 | "shorten",
20 | "subscribe",
21 | ] as const;
22 | export type CommandName = (typeof Commands)[number];
23 | function getCommandName(name: string): CommandName | undefined {
24 | for (const command of Commands) {
25 | if (command === name) {
26 | return command;
27 | }
28 | }
29 | }
30 |
31 | export class Command {
32 | constructor(public config: MJConfig) {}
33 | cache: Partial> = {};
34 |
35 | async cacheCommand(name: CommandName) {
36 | if (this.cache[name] !== undefined) {
37 | return this.cache[name];
38 | }
39 | const command = await this.getCommand(name);
40 | console.log("=========", { command });
41 | this.cache[name] = command;
42 | return command;
43 | this.allCommand();
44 | return this.cache[name];
45 | }
46 | async allCommand() {
47 | let serverId = this.config.ServerId;
48 | if (!serverId) {
49 | serverId = this.config.ChannelId;
50 | }
51 | const url = `${this.config.DiscordBaseUrl}/api/v9/guilds/${serverId}/application-command-index`;
52 | const response = await this.safeFetch(url, {
53 | headers: { authorization: this.config.SalaiToken },
54 | });
55 |
56 | const data = await response.json();
57 | if (data?.application_commands) {
58 | data.application_commands.forEach((command: any) => {
59 | const name = getCommandName(command.name);
60 | if (name) {
61 | this.cache[name] = command;
62 | }
63 | });
64 | }
65 | }
66 |
67 | async getCommand(name: CommandName) {
68 | let serverId = this.config.ServerId;
69 | if (!serverId) {
70 | serverId = this.config.ChannelId;
71 | }
72 | const url = `${this.config.DiscordBaseUrl}/api/v9/guilds/${serverId}/application-command-index`;
73 | const response = await this.safeFetch(url, {
74 | headers: { authorization: this.config.SalaiToken },
75 | });
76 | const data = await response.json();
77 | if (data?.application_commands?.[0]) {
78 | return data.application_commands[0];
79 | }
80 | throw new Error(`Failed to get application_commands for command ${name}`);
81 | }
82 | private safeFetch(input: RequestInfo | URL, init?: RequestInit | undefined) {
83 | const request = this.config.fetch.bind(this, input, init);
84 | return new Promise((resolve, reject) => {
85 | this.fetchQueue.push(
86 | {
87 | request,
88 | callback: (res: Response) => {
89 | resolve(res);
90 | },
91 | },
92 | (error: any, result: any) => {
93 | if (error) {
94 | reject(error);
95 | } else {
96 | resolve(result);
97 | }
98 | }
99 | );
100 | });
101 | }
102 | private async processFetchRequest({
103 | request,
104 | callback,
105 | }: {
106 | request: () => Promise;
107 | callback: (res: Response) => void;
108 | }) {
109 | const res = await request();
110 | callback(res);
111 | await sleep(1000 * 4);
112 | }
113 | private fetchQueue = async.queue(this.processFetchRequest, 1);
114 |
115 | async imaginePayload(prompt: string, nonce?: string) {
116 | const data = await this.commandData("imagine", [
117 | {
118 | type: 3,
119 | name: "prompt",
120 | value: prompt,
121 | },
122 | ]);
123 | return this.data2Paylod(data, nonce);
124 | }
125 | async PreferPayload(nonce?: string) {
126 | const data = await this.commandData("prefer", [
127 | {
128 | type: 1,
129 | name: "remix",
130 | options: [],
131 | },
132 | ]);
133 | return this.data2Paylod(data, nonce);
134 | }
135 |
136 | async shortenPayload(prompt: string, nonce?: string) {
137 | const data = await this.commandData("shorten", [
138 | {
139 | type: 3,
140 | name: "prompt",
141 | value: prompt,
142 | },
143 | ]);
144 | return this.data2Paylod(data, nonce);
145 | }
146 | async infoPayload(nonce?: string) {
147 | const data = await this.commandData("info");
148 | return this.data2Paylod(data, nonce);
149 | }
150 | async fastPayload(nonce?: string) {
151 | const data = await this.commandData("fast");
152 | return this.data2Paylod(data, nonce);
153 | }
154 | async relaxPayload(nonce?: string) {
155 | const data = await this.commandData("relax");
156 | return this.data2Paylod(data, nonce);
157 | }
158 | async settingsPayload(nonce?: string) {
159 | const data = await this.commandData("settings");
160 | return this.data2Paylod(data, nonce);
161 | }
162 | async describePayload(image: DiscordImage, nonce?: string) {
163 | const data = await this.commandData(
164 | "describe",
165 | [
166 | {
167 | type: 11,
168 | name: "image",
169 | value: image.id,
170 | },
171 | ],
172 | [
173 | {
174 | id: image.id,
175 | filename: image.filename,
176 | uploaded_filename: image.upload_filename,
177 | },
178 | ]
179 | );
180 | return this.data2Paylod(data, nonce);
181 | }
182 |
183 | protected async commandData(
184 | name: CommandName,
185 | options: any[] = [],
186 | attachments: any[] = []
187 | ) {
188 | const command = await this.cacheCommand(name);
189 | const data = {
190 | version: command.version,
191 | id: command.id,
192 | name: command.name,
193 | type: command.type,
194 | options,
195 | application_command: command,
196 | attachments,
197 | };
198 | return data;
199 | }
200 | //TODO data type
201 | protected data2Paylod(data: any, nonce?: string) {
202 | const payload = {
203 | type: 2,
204 | application_id: data.application_command.application_id,
205 | guild_id: this.config.ServerId,
206 | channel_id: this.config.ChannelId,
207 | session_id: this.config.SessionId,
208 | nonce,
209 | data,
210 | };
211 | return payload;
212 | }
213 | }
214 |
--------------------------------------------------------------------------------
/src/discord.message.ts:
--------------------------------------------------------------------------------
1 | import {
2 | DefaultMJConfig,
3 | LoadingHandler,
4 | MJMessage,
5 | MJConfig,
6 | MJConfigParam,
7 | } from "./interfaces";
8 | import { formatOptions, sleep } from "./utils";
9 | import async from "async";
10 |
11 | export class MidjourneyMessage {
12 | public config: MJConfig;
13 | constructor(defaults: MJConfigParam) {
14 | const { SalaiToken } = defaults;
15 | if (!SalaiToken) {
16 | throw new Error("SalaiToken are required");
17 | }
18 | this.config = {
19 | ...DefaultMJConfig,
20 | ...defaults,
21 | };
22 | }
23 | private safeRetrieveMessages = (request = 50) => {
24 | return new Promise((resolve, reject) => {
25 | this.queue.push(
26 | {
27 | request,
28 | callback: (any: any) => {
29 | resolve(any);
30 | },
31 | },
32 | (error: any, result: any) => {
33 | if (error) {
34 | reject(error);
35 | } else {
36 | resolve(result);
37 | }
38 | }
39 | );
40 | });
41 | };
42 | private processRequest = async ({
43 | request,
44 | callback,
45 | }: {
46 | request: any;
47 | callback: (any: any) => void;
48 | }) => {
49 | const httpStatus = await this.RetrieveMessages(request);
50 | callback(httpStatus);
51 | await sleep(this.config.ApiInterval);
52 | };
53 | private queue = async.queue(this.processRequest, 1);
54 |
55 | protected log(...args: any[]) {
56 | this.config.Debug && console.log(...args, new Date().toISOString());
57 | }
58 | async FilterMessages(
59 | timestamp: number,
60 | prompt: string,
61 | loading?: LoadingHandler
62 | ) {
63 | const seed = prompt.match(/\[(.*?)\]/)?.[1];
64 | this.log(`seed:`, seed);
65 | const data = await this.safeRetrieveMessages(this.config.Limit);
66 | for (let i = 0; i < data.length; i++) {
67 | const item = data[i];
68 | if (
69 | item.author.id === this.config.BotId &&
70 | item.content.includes(`${seed}`)
71 | ) {
72 | const itemTimestamp = new Date(item.timestamp).getTime();
73 | if (itemTimestamp < timestamp) {
74 | this.log("old message");
75 | continue;
76 | }
77 | if (item.attachments.length === 0) {
78 | this.log("no attachment");
79 | break;
80 | }
81 | let uri = item.attachments[0].url;
82 | if (this.config.ImageProxy !== "") {
83 | uri = uri.replace(
84 | "https://cdn.discordapp.com/",
85 | this.config.ImageProxy
86 | );
87 | } //waiting
88 | if (
89 | item.attachments[0].filename.startsWith("grid") ||
90 | item.components.length === 0
91 | ) {
92 | this.log(`content`, item.content);
93 | const progress = this.content2progress(item.content);
94 | loading?.(uri, progress);
95 | break;
96 | }
97 | //finished
98 | const content = item.content.split("**")[1];
99 | const { proxy_url, width, height } = item.attachments[0];
100 | const msg: MJMessage = {
101 | content,
102 | id: item.id,
103 | uri,
104 | proxy_url,
105 | flags: item.flags,
106 | hash: this.UriToHash(uri),
107 | progress: "done",
108 | options: formatOptions(item.components),
109 | width,
110 | height,
111 | };
112 | return msg;
113 | }
114 | }
115 | return null;
116 | }
117 | protected content2progress(content: string) {
118 | const spcon = content.split("**");
119 | if (spcon.length < 3) {
120 | return "";
121 | }
122 | content = spcon[2];
123 | const regex = /\(([^)]+)\)/; // matches the value inside the first parenthesis
124 | const match = content.match(regex);
125 | let progress = "";
126 | if (match) {
127 | progress = match[1];
128 | }
129 | return progress;
130 | }
131 | UriToHash(uri: string) {
132 | return uri.split("_").pop()?.split(".")[0] ?? "";
133 | }
134 | async WaitMessage(
135 | prompt: string,
136 | loading?: LoadingHandler,
137 | timestamp?: number
138 | ) {
139 | timestamp = timestamp ?? Date.now();
140 | for (let i = 0; i < this.config.MaxWait; i++) {
141 | const msg = await this.FilterMessages(timestamp, prompt, loading);
142 | if (msg !== null) {
143 | return msg;
144 | }
145 | this.log(i, "wait no message found");
146 | await sleep(1000 * 2);
147 | }
148 | return null;
149 | }
150 |
151 | async RetrieveMessages(limit = this.config.Limit) {
152 | const headers = {
153 | "Content-Type": "application/json",
154 | Authorization: this.config.SalaiToken,
155 | };
156 | const response = await this.config.fetch(
157 | `${this.config.DiscordBaseUrl}/api/v10/channels/${this.config.ChannelId}/messages?limit=${limit}`,
158 | {
159 | headers,
160 | }
161 | );
162 | if (!response.ok) {
163 | this.log("error config", { config: this.config });
164 | this.log(`HTTP error! status: ${response.status}`);
165 | }
166 | const data: any = await response.json();
167 | return data;
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/src/discord.ws.ts:
--------------------------------------------------------------------------------
1 | import {
2 | MJConfig,
3 | WaitMjEvent,
4 | MJMessage,
5 | LoadingHandler,
6 | MJEmit,
7 | MJInfo,
8 | MJSettings,
9 | MJOptions,
10 | OnModal,
11 | MJShorten,
12 | MJDescribe,
13 | } from "./interfaces";
14 | import { MidjourneyApi } from "./midjourney.api";
15 | import {
16 | content2progress,
17 | content2prompt,
18 | formatInfo,
19 | formatOptions,
20 | formatPrompts,
21 | nextNonce,
22 | uriToHash,
23 | } from "./utils";
24 | import { VerifyHuman } from "./verify.human";
25 | import WebSocket from "isomorphic-ws";
26 | export class WsMessage {
27 | ws: WebSocket;
28 | private closed = false;
29 | private event: Array<{ event: string; callback: (message: any) => void }> =
30 | [];
31 | private waitMjEvents: Map = new Map();
32 | private skipMessageId: string[] = [];
33 | private reconnectTime: boolean[] = [];
34 | private heartbeatInterval = 0;
35 | public UserId = "";
36 | constructor(public config: MJConfig, public MJApi: MidjourneyApi) {
37 | this.ws = new this.config.WebSocket(this.config.WsBaseUrl);
38 | this.ws.addEventListener("open", this.open.bind(this));
39 | this.onSystem("messageCreate", this.onMessageCreate.bind(this));
40 | this.onSystem("messageUpdate", this.onMessageUpdate.bind(this));
41 | this.onSystem("messageDelete", this.onMessageDelete.bind(this));
42 | this.onSystem("ready", this.onReady.bind(this));
43 | this.onSystem("interactionSuccess", this.onInteractionSuccess.bind(this));
44 | }
45 |
46 | private async heartbeat(num: number) {
47 | if (this.reconnectTime[num]) return;
48 | //check if ws is closed
49 | if (this.closed) return;
50 | if (this.ws.readyState !== this.ws.OPEN) {
51 | this.reconnect();
52 | return;
53 | }
54 | this.log("heartbeat", this.heartbeatInterval);
55 | this.heartbeatInterval++;
56 | this.ws.send(
57 | JSON.stringify({
58 | op: 1,
59 | d: this.heartbeatInterval,
60 | })
61 | );
62 | await this.timeout(1000 * 40);
63 | this.heartbeat(num);
64 | }
65 | close() {
66 | this.closed = true;
67 | this.ws.close();
68 | }
69 | async checkWs() {
70 | if (this.closed) return;
71 | if (this.ws.readyState !== this.ws.OPEN) {
72 | this.reconnect();
73 | await this.onceReady();
74 | }
75 | }
76 |
77 | async onceReady() {
78 | return new Promise((resolve) => {
79 | this.once("ready", (user) => {
80 | //print user nickname
81 | console.log(`🎊 ws ready!!! Hi: ${user.global_name}`);
82 | resolve(this);
83 | });
84 | });
85 | }
86 | //try reconnect
87 | reconnect() {
88 | if (this.closed) return;
89 | this.ws = new this.config.WebSocket(this.config.WsBaseUrl);
90 | this.heartbeatInterval = 0;
91 | this.ws.addEventListener("open", this.open.bind(this));
92 | }
93 | // After opening ws
94 | private async open() {
95 | const num = this.reconnectTime.length;
96 | this.log("open.time", num);
97 | this.reconnectTime.push(false);
98 | this.auth();
99 | this.ws.addEventListener("message", (event) => {
100 | this.parseMessage(event.data as string);
101 | });
102 | this.ws.addEventListener("error", (event) => {
103 | this.reconnectTime[num] = true;
104 | this.reconnect();
105 | });
106 | this.ws.addEventListener("close", (event) => {
107 | this.reconnectTime[num] = true;
108 | this.reconnect();
109 | });
110 | setTimeout(() => {
111 | this.heartbeat(num);
112 | }, 1000 * 10);
113 | }
114 | // auth
115 | private auth() {
116 | this.ws.send(
117 | JSON.stringify({
118 | op: 2,
119 | d: {
120 | token: this.config.SalaiToken,
121 | capabilities: 8189,
122 | properties: {
123 | os: "Mac OS X",
124 | browser: "Chrome",
125 | device: "",
126 | },
127 | compress: false,
128 | },
129 | })
130 | );
131 | }
132 | async timeout(ms: number) {
133 | return new Promise((resolve) => setTimeout(resolve, ms));
134 | }
135 | private async messageCreate(message: any) {
136 | const { embeds, id, nonce, components, attachments } = message;
137 | if (nonce) {
138 | // this.log("waiting start image or info or error");
139 | this.updateMjEventIdByNonce(id, nonce);
140 | if (embeds?.[0]) {
141 | const { color, description, title } = embeds[0];
142 | this.log("embeds[0].color", color);
143 | switch (color) {
144 | case 16711680: //error
145 | if (title == "Action needed to continue") {
146 | return this.continue(message);
147 | } else if (title == "Pending mod message") {
148 | return this.continue(message);
149 | }
150 |
151 | const error = new Error(description);
152 | this.EventError(id, error);
153 | return;
154 |
155 | case 16776960: //warning
156 | console.warn(description);
157 | break;
158 |
159 | default:
160 | if (
161 | title?.includes("continue") &&
162 | description?.includes("verify you're human")
163 | ) {
164 | //verify human
165 | await this.verifyHuman(message);
166 | return;
167 | }
168 |
169 | if (title?.includes("Invalid")) {
170 | //error
171 | const error = new Error(description);
172 | this.EventError(id, error);
173 | return;
174 | }
175 | }
176 | }
177 | }
178 |
179 | if (attachments?.length > 0 && components?.length > 0) {
180 | this.done(message);
181 | return;
182 | }
183 |
184 | this.messageUpdate(message);
185 | }
186 |
187 | private messageUpdate(message: any) {
188 | // this.log("messageUpdate", message);
189 | const {
190 | content,
191 | embeds,
192 | interaction = {},
193 | nonce,
194 | id,
195 | components,
196 | } = message;
197 |
198 | if (!nonce) {
199 | const { name } = interaction;
200 |
201 | switch (name) {
202 | case "settings":
203 | this.emit("settings", message);
204 | return;
205 | case "describe":
206 | let uri = embeds?.[0]?.image?.url;
207 | if (this.config.ImageProxy !== "") {
208 | uri = uri.replace(
209 | "https://cdn.discordapp.com/",
210 | this.config.ImageProxy
211 | );
212 | }
213 | const describe: MJDescribe = {
214 | id: id,
215 | flags: message.flags,
216 | descriptions: embeds?.[0]?.description.split("\n\n"),
217 | uri: uri,
218 | proxy_url: embeds?.[0]?.image?.proxy_url,
219 | options: formatOptions(components),
220 | };
221 | this.emitMJ(id, describe);
222 | break;
223 | case "prefer remix":
224 | if (content != "") {
225 | this.emit("prefer-remix", content);
226 | }
227 | break;
228 | case "shorten":
229 | const shorten: MJShorten = {
230 | description: embeds?.[0]?.description,
231 | prompts: formatPrompts(embeds?.[0]?.description as string),
232 | options: formatOptions(components),
233 | id,
234 | flags: message.flags,
235 | };
236 | this.emitMJ(id, shorten);
237 | break;
238 | case "info":
239 | this.emit("info", embeds?.[0]?.description);
240 | return;
241 | }
242 | }
243 | if (embeds?.[0]) {
244 | var { description, title } = embeds[0];
245 | if (title === "Duplicate images detected") {
246 | const error = new Error(description);
247 | this.EventError(id, error);
248 | return;
249 | }
250 | }
251 |
252 | if (content) {
253 | this.processingImage(message);
254 | }
255 | }
256 |
257 | //interaction success
258 | private async onInteractionSuccess({
259 | nonce,
260 | id,
261 | }: {
262 | nonce: string;
263 | id: string;
264 | }) {
265 | // this.log("interactionSuccess", nonce, id);
266 | const event = this.getEventByNonce(nonce);
267 | if (!event) {
268 | return;
269 | }
270 | event.onmodal && event.onmodal(nonce, id);
271 | }
272 | private async onReady(user: any) {
273 | this.UserId = user.id;
274 | }
275 | private async onMessageCreate(message: any) {
276 | const { channel_id, author, interaction } = message;
277 | if (channel_id !== this.config.ChannelId) return;
278 | if (author?.id !== this.config.BotId) return;
279 | if (interaction && interaction.user.id !== this.UserId) return;
280 | // this.log("[messageCreate]", JSON.stringify(message));
281 | this.messageCreate(message);
282 | }
283 |
284 | private async onMessageUpdate(message: any) {
285 | const { channel_id, author, interaction } = message;
286 | if (channel_id !== this.config.ChannelId) return;
287 | if (author?.id !== this.config.BotId) return;
288 | if (interaction && interaction.user.id !== this.UserId) return;
289 | // this.log("[messageUpdate]", JSON.stringify(message));
290 | this.messageUpdate(message);
291 | }
292 | private async onMessageDelete(message: any) {
293 | const { channel_id, id } = message;
294 | if (channel_id !== this.config.ChannelId) return;
295 | for (const [key, value] of this.waitMjEvents.entries()) {
296 | if (value.id === id) {
297 | this.waitMjEvents.set(key, { ...value, del: true });
298 | }
299 | }
300 | }
301 |
302 | // parse message from ws
303 | private parseMessage(data: string) {
304 | const msg = JSON.parse(data);
305 | if (!msg.t) {
306 | return;
307 | }
308 | const message = msg.d;
309 | if (message.channel_id === this.config.ChannelId) {
310 | this.log(data);
311 | }
312 | this.log("event", msg.t);
313 | // console.log(data);
314 | switch (msg.t) {
315 | case "READY":
316 | this.emitSystem("ready", message.user);
317 | break;
318 | case "MESSAGE_CREATE":
319 | this.emitSystem("messageCreate", message);
320 | break;
321 | case "MESSAGE_UPDATE":
322 | this.emitSystem("messageUpdate", message);
323 | break;
324 | case "MESSAGE_DELETE":
325 | this.emitSystem("messageDelete", message);
326 | case "INTERACTION_SUCCESS":
327 | if (message.nonce) {
328 | this.emitSystem("interactionSuccess", message);
329 | }
330 | break;
331 | case "INTERACTION_CREATE":
332 | if (message.nonce) {
333 | this.emitSystem("interactionCreate", message);
334 | }
335 | }
336 | }
337 | //continue click appeal or Acknowledged
338 | private async continue(message: any) {
339 | const { components, id, flags, nonce } = message;
340 | const appeal = components[0]?.components[0];
341 | this.log("appeal", appeal);
342 | if (appeal) {
343 | var newnonce = nextNonce();
344 | const httpStatus = await this.MJApi.CustomApi({
345 | msgId: id,
346 | customId: appeal.custom_id,
347 | flags,
348 | nonce: newnonce,
349 | });
350 | this.log("appeal.httpStatus", httpStatus);
351 | if (httpStatus == 204) {
352 | //todo
353 | this.on(newnonce, (data) => {
354 | this.emit(nonce, data);
355 | });
356 | }
357 | }
358 | }
359 | private async verifyHuman(message: any) {
360 | const { HuggingFaceToken } = this.config;
361 | if (HuggingFaceToken === "" || !HuggingFaceToken) {
362 | this.log("HuggingFaceToken is empty");
363 | return;
364 | }
365 | const { embeds, components, id, flags, nonce } = message;
366 | const uri = embeds[0].image.url;
367 | const categories = components[0].components;
368 | const classify = categories.map((c: any) => c.label);
369 | const verifyClient = new VerifyHuman(this.config);
370 | const category = await verifyClient.verify(uri, classify);
371 | if (category) {
372 | const custom_id = categories.find(
373 | (c: any) => c.label === category
374 | ).custom_id;
375 | var newnonce = nextNonce();
376 | const httpStatus = await this.MJApi.CustomApi({
377 | msgId: id,
378 | customId: custom_id,
379 | flags,
380 | nonce: newnonce,
381 | });
382 | if (httpStatus == 204) {
383 | this.on(newnonce, (data) => {
384 | this.emit(nonce, data);
385 | });
386 | }
387 | this.log("verifyHumanApi", httpStatus, custom_id, message.id);
388 | }
389 | }
390 | private EventError(id: string, error: Error) {
391 | const event = this.getEventById(id);
392 | if (!event) {
393 | return;
394 | }
395 | const eventMsg: MJEmit = {
396 | error,
397 | };
398 | this.emit(event.nonce, eventMsg);
399 | }
400 |
401 | private done(message: any) {
402 | const { content, id, attachments, components, flags } = message;
403 | const { url, proxy_url, width, height } = attachments[0];
404 | let uri = url;
405 | if (this.config.ImageProxy !== "") {
406 | uri = uri.replace("https://cdn.discordapp.com/", this.config.ImageProxy);
407 | }
408 |
409 | const MJmsg: MJMessage = {
410 | id,
411 | flags,
412 | content,
413 | hash: uriToHash(url),
414 | progress: "done",
415 | uri,
416 | proxy_url,
417 | options: formatOptions(components),
418 | width,
419 | height,
420 | };
421 | this.filterMessages(MJmsg);
422 | return;
423 | }
424 | private processingImage(message: any) {
425 | const { content, id, attachments, flags } = message;
426 | if (!content) {
427 | return;
428 | }
429 | const event = this.getEventById(id);
430 | if (!event) {
431 | return;
432 | }
433 | event.prompt = content;
434 | //not image
435 | if (!attachments || attachments.length === 0) {
436 | return;
437 | }
438 |
439 | let uri = attachments[0].url;
440 | if (this.config.ImageProxy !== "") {
441 | uri = uri.replace("https://cdn.discordapp.com/", this.config.ImageProxy);
442 | }
443 | const MJmsg: MJMessage = {
444 | uri: uri,
445 | proxy_url: attachments[0].proxy_url,
446 | content: content,
447 | flags: flags,
448 | progress: content2progress(content),
449 | };
450 | const eventMsg: MJEmit = {
451 | message: MJmsg,
452 | };
453 | this.emitImage(event.nonce, eventMsg);
454 | }
455 |
456 | private async filterMessages(MJmsg: MJMessage) {
457 | // delay 300ms for discord message delete
458 | await this.timeout(300);
459 | const event = this.getEventByContent(MJmsg.content);
460 | if (!event) {
461 | this.log("FilterMessages not found", MJmsg, this.waitMjEvents);
462 | return;
463 | }
464 | const eventMsg: MJEmit = {
465 | message: MJmsg,
466 | };
467 | this.emitImage(event.nonce, eventMsg);
468 | }
469 | private getEventByContent(content: string) {
470 | const prompt = content2prompt(content);
471 | //fist del message
472 | for (const [key, value] of this.waitMjEvents.entries()) {
473 | if (
474 | value.del === true &&
475 | prompt === content2prompt(value.prompt as string)
476 | ) {
477 | return value;
478 | }
479 | }
480 |
481 | for (const [key, value] of this.waitMjEvents.entries()) {
482 | if (prompt === content2prompt(value.prompt as string)) {
483 | return value;
484 | }
485 | }
486 | }
487 |
488 | private getEventById(id: string) {
489 | for (const [key, value] of this.waitMjEvents.entries()) {
490 | if (value.id === id) {
491 | return value;
492 | }
493 | }
494 | }
495 | private getEventByNonce(nonce: string) {
496 | for (const [key, value] of this.waitMjEvents.entries()) {
497 | if (value.nonce === nonce) {
498 | return value;
499 | }
500 | }
501 | }
502 | private updateMjEventIdByNonce(id: string, nonce: string) {
503 | if (nonce === "" || id === "") return;
504 | let event = this.waitMjEvents.get(nonce);
505 | if (!event) return;
506 | event.id = id;
507 | this.log("updateMjEventIdByNonce success", this.waitMjEvents.get(nonce));
508 | }
509 |
510 | protected async log(...args: any[]) {
511 | this.config.Debug && console.info(...args, new Date().toISOString());
512 | }
513 |
514 | emit(event: string, message: any) {
515 | this.event
516 | .filter((e) => e.event === event)
517 | .forEach((e) => e.callback(message));
518 | }
519 | private emitImage(type: string, message: MJEmit) {
520 | this.emit(type, message);
521 | }
522 | //FIXME: emitMJ rename
523 | private emitMJ(id: string, data: any) {
524 | const event = this.getEventById(id);
525 | if (!event) return;
526 | this.emit(event.nonce, data);
527 | }
528 |
529 | on(event: string, callback: (message: any) => void) {
530 | this.event.push({ event, callback });
531 | }
532 | onSystem(
533 | event:
534 | | "ready"
535 | | "messageCreate"
536 | | "messageUpdate"
537 | | "messageDelete"
538 | | "interactionCreate"
539 | | "interactionSuccess",
540 | callback: (message: any) => void
541 | ) {
542 | this.on(event, callback);
543 | }
544 | private emitSystem(
545 | type:
546 | | "ready"
547 | | "messageCreate"
548 | | "messageUpdate"
549 | | "messageDelete"
550 | | "interactionSuccess"
551 | | "interactionCreate",
552 | message: MJEmit
553 | ) {
554 | this.emit(type, message);
555 | }
556 | once(event: string, callback: (message: any) => void) {
557 | const once = (message: any) => {
558 | this.remove(event, once);
559 | callback(message);
560 | };
561 | this.event.push({ event, callback: once });
562 | }
563 | remove(event: string, callback: (message: any) => void) {
564 | this.event = this.event.filter(
565 | (e) => e.event !== event && e.callback !== callback
566 | );
567 | }
568 | removeEvent(event: string) {
569 | this.event = this.event.filter((e) => e.event !== event);
570 | }
571 | //FIXME: USE ONCE
572 | onceInfo(callback: (message: any) => void) {
573 | const once = (message: any) => {
574 | this.remove("info", once);
575 | callback(message);
576 | };
577 | this.event.push({ event: "info", callback: once });
578 | }
579 | //FIXME: USE ONCE
580 | onceSettings(callback: (message: any) => void) {
581 | const once = (message: any) => {
582 | this.remove("settings", once);
583 | callback(message);
584 | };
585 | this.event.push({ event: "settings", callback: once });
586 | }
587 | onceMJ(nonce: string, callback: (data: any) => void) {
588 | const once = (message: any) => {
589 | this.remove(nonce, once);
590 | //FIXME: removeWaitMjEvent
591 | this.removeWaitMjEvent(nonce);
592 | callback(message);
593 | };
594 | //FIXME: addWaitMjEvent
595 | this.waitMjEvents.set(nonce, { nonce });
596 | this.event.push({ event: nonce, callback: once });
597 | }
598 | private removeSkipMessageId(messageId: string) {
599 | const index = this.skipMessageId.findIndex((id) => id !== messageId);
600 | if (index !== -1) {
601 | this.skipMessageId.splice(index, 1);
602 | }
603 | }
604 | private removeWaitMjEvent(nonce: string) {
605 | this.waitMjEvents.delete(nonce);
606 | }
607 | onceImage(nonce: string, callback: (data: MJEmit) => void) {
608 | const once = (data: MJEmit) => {
609 | const { message, error } = data;
610 | if (error || (message && message.progress === "done")) {
611 | this.remove(nonce, once);
612 | }
613 | callback(data);
614 | };
615 | this.event.push({ event: nonce, callback: once });
616 | }
617 |
618 | async waitImageMessage({
619 | nonce,
620 | prompt,
621 | onmodal,
622 | messageId,
623 | loading,
624 | }: {
625 | nonce: string;
626 | prompt?: string;
627 | messageId?: string;
628 | onmodal?: OnModal;
629 | loading?: LoadingHandler;
630 | }) {
631 | if (messageId) this.skipMessageId.push(messageId);
632 | return new Promise((resolve, reject) => {
633 | const handleImageMessage = ({ message, error }: MJEmit) => {
634 | if (error) {
635 | this.removeWaitMjEvent(nonce);
636 | reject(error);
637 | return;
638 | }
639 | if (message && message.progress === "done") {
640 | this.removeWaitMjEvent(nonce);
641 | messageId && this.removeSkipMessageId(messageId);
642 | resolve(message);
643 | return;
644 | }
645 | message && loading && loading(message.uri, message.progress || "");
646 | };
647 | this.waitMjEvents.set(nonce, {
648 | nonce,
649 | prompt,
650 | onmodal: async (oldnonce, id) => {
651 | if (onmodal === undefined) {
652 | // reject(new Error("onmodal is not defined"))
653 | return "";
654 | }
655 | var nonce = await onmodal(oldnonce, id);
656 | if (nonce === "") {
657 | // reject(new Error("onmodal return empty nonce"))
658 | return "";
659 | }
660 | this.removeWaitMjEvent(oldnonce);
661 | this.waitMjEvents.set(nonce, { nonce });
662 | this.onceImage(nonce, handleImageMessage);
663 | return nonce;
664 | },
665 | });
666 | this.onceImage(nonce, handleImageMessage);
667 | });
668 | }
669 | async waitDescribe(nonce: string) {
670 | return new Promise((resolve) => {
671 | this.onceMJ(nonce, (message) => {
672 | resolve(message);
673 | });
674 | });
675 | }
676 | async waitShorten(nonce: string) {
677 | return new Promise((resolve) => {
678 | this.onceMJ(nonce, (message) => {
679 | resolve(message);
680 | });
681 | });
682 | }
683 | async waitContent(event: string) {
684 | return new Promise((resolve) => {
685 | this.once(event, (message) => {
686 | resolve(message);
687 | });
688 | });
689 | }
690 | async waitInfo() {
691 | return new Promise((resolve, reject) => {
692 | this.onceInfo((message) => {
693 | resolve(formatInfo(message));
694 | });
695 | });
696 | }
697 | async waitSettings() {
698 | return new Promise((resolve, reject) => {
699 | this.onceSettings((message) => {
700 | resolve({
701 | id: message.id,
702 | flags: message.flags,
703 | content: message,
704 | options: formatOptions(message.components),
705 | });
706 | });
707 | });
708 | }
709 | }
710 |
--------------------------------------------------------------------------------
/src/face.swap.ts:
--------------------------------------------------------------------------------
1 | import { client } from "./gradio/index";
2 |
3 | export class faceSwap {
4 | public hf_token?: string;
5 | constructor(hf_token?: string) {
6 | this.hf_token = hf_token;
7 | }
8 | async changeFace(Target: Blob, Source: Blob) {
9 | const app = await client("https://felixrosberg-face-swap.hf.space/", {
10 | hf_token: this.hf_token as any,
11 | });
12 | // console.log("app", app);
13 | const result: any = await app.predict(1, [
14 | Target, // blob in 'Target' Image component
15 | Source, // blob in 'Source' Image component
16 | 0, // number (numeric value between 0 and 100) in 'Anonymization ratio (%)' Slider component
17 | 0, // number (numeric value between 0 and 100) in 'Adversarial defense ratio (%)' Slider component
18 | "Compare", // string[] (array of strings) in 'Mode' Checkboxgroup component
19 | ]);
20 | // result.data;
21 | return result.data;
22 | // console.log(result.data[0]);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/gradio/globals.d.ts:
--------------------------------------------------------------------------------
1 | declare global {
2 | interface Window {
3 | __gradio_mode__: "app" | "website";
4 | launchGradio: Function;
5 | launchGradioFromSpaces: Function;
6 | gradio_config: Config;
7 | scoped_css_attach: (link: HTMLLinkElement) => void;
8 | __is_colab__: boolean;
9 | }
10 | }
11 |
12 | export interface Config {
13 | auth_required: boolean | undefined;
14 | auth_message: string;
15 | components: any[];
16 | css: string | null;
17 | dependencies: any[];
18 | dev_mode: boolean;
19 | enable_queue: boolean;
20 | layout: any;
21 | mode: "blocks" | "interface";
22 | root: string;
23 | theme: string;
24 | title: string;
25 | version: string;
26 | space_id: string | null;
27 | is_colab: boolean;
28 | show_api: boolean;
29 | stylesheets: string[];
30 | path: string;
31 | }
32 |
--------------------------------------------------------------------------------
/src/gradio/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | client,
3 | post_data,
4 | upload_files,
5 | duplicate,
6 | api_factory,
7 | } from "./client";
8 | export type { SpaceStatus } from "./types";
9 |
--------------------------------------------------------------------------------
/src/gradio/types.ts:
--------------------------------------------------------------------------------
1 | export interface Config {
2 | auth_required: boolean | undefined;
3 | auth_message: string;
4 | components: any[];
5 | css: string | null;
6 | dependencies: any[];
7 | dev_mode: boolean;
8 | enable_queue: boolean;
9 | layout: any;
10 | mode: "blocks" | "interface";
11 | root: string;
12 | root_url?: string;
13 | theme: string;
14 | title: string;
15 | version: string;
16 | space_id: string | null;
17 | is_colab: boolean;
18 | show_api: boolean;
19 | stylesheets: string[];
20 | path: string;
21 | }
22 |
23 | export interface Payload {
24 | data: Array;
25 | fn_index?: number;
26 | event_data?: unknown;
27 | time?: Date;
28 | }
29 |
30 | export interface PostResponse {
31 | error?: string;
32 | [x: string]: any;
33 | }
34 | export interface UploadResponse {
35 | error?: string;
36 | files?: Array;
37 | }
38 |
39 | export interface Status {
40 | queue: boolean;
41 | code?: string;
42 | success?: boolean;
43 | stage: "pending" | "error" | "complete" | "generating";
44 | size?: number;
45 | position?: number;
46 | eta?: number;
47 | message?: string;
48 | progress_data?: Array<{
49 | progress: number | null;
50 | index: number | null;
51 | length: number | null;
52 | unit: string | null;
53 | desc: string | null;
54 | }>;
55 | time?: Date;
56 | }
57 |
58 | export interface SpaceStatusNormal {
59 | status: "sleeping" | "running" | "building" | "error" | "stopped";
60 | detail:
61 | | "SLEEPING"
62 | | "RUNNING"
63 | | "RUNNING_BUILDING"
64 | | "BUILDING"
65 | | "NOT_FOUND";
66 | load_status: "pending" | "error" | "complete" | "generating";
67 | message: string;
68 | }
69 | export interface SpaceStatusError {
70 | status: "space_error" | "paused";
71 | detail:
72 | | "NO_APP_FILE"
73 | | "CONFIG_ERROR"
74 | | "BUILD_ERROR"
75 | | "RUNTIME_ERROR"
76 | | "PAUSED";
77 | load_status: "error";
78 | message: string;
79 | discussions_enabled: boolean;
80 | }
81 | export type SpaceStatus = SpaceStatusNormal | SpaceStatusError;
82 |
83 | export type status_callback_function = (a: Status) => void;
84 | export type SpaceStatusCallback = (a: SpaceStatus) => void;
85 |
86 | export type EventType = "data" | "status";
87 |
88 | export interface EventMap {
89 | data: Payload;
90 | status: Status;
91 | }
92 |
93 | export type Event = {
94 | [P in K]: EventMap[P] & { type: P; endpoint: string; fn_index: number };
95 | }[K];
96 | export type EventListener = (event: Event) => void;
97 | export type ListenerMap = {
98 | [P in K]?: EventListener[];
99 | };
100 | export interface FileData {
101 | name: string;
102 | orig_name?: string;
103 | size?: number;
104 | data: string;
105 | blob?: File;
106 | is_file?: boolean;
107 | mime_type?: string;
108 | alt_text?: string;
109 | }
110 |
--------------------------------------------------------------------------------
/src/gradio/utils.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from "./types";
2 |
3 | export function determine_protocol(endpoint: string): {
4 | ws_protocol: "ws" | "wss";
5 | http_protocol: "http:" | "https:";
6 | host: string;
7 | } {
8 | if (endpoint.startsWith("http")) {
9 | const { protocol, host } = new URL(endpoint);
10 |
11 | if (host.endsWith("hf.space")) {
12 | return {
13 | ws_protocol: "wss",
14 | host: host,
15 | http_protocol: protocol as "http:" | "https:",
16 | };
17 | } else {
18 | return {
19 | ws_protocol: protocol === "https:" ? "wss" : "ws",
20 | http_protocol: protocol as "http:" | "https:",
21 | host,
22 | };
23 | }
24 | }
25 |
26 | // default to secure if no protocol is provided
27 | return {
28 | ws_protocol: "wss",
29 | http_protocol: "https:",
30 | host: endpoint,
31 | };
32 | }
33 |
34 | export const RE_SPACE_NAME = /^[^\/]*\/[^\/]*$/;
35 | export const RE_SPACE_DOMAIN = /.*hf\.space\/{0,1}$/;
36 | export async function process_endpoint(
37 | app_reference: string,
38 | token?: `hf_${string}`
39 | ): Promise<{
40 | space_id: string | false;
41 | host: string;
42 | ws_protocol: "ws" | "wss";
43 | http_protocol: "http:" | "https:";
44 | }> {
45 | const headers: { Authorization?: string } = {};
46 | if (token) {
47 | headers.Authorization = `Bearer ${token}`;
48 | }
49 |
50 | const _app_reference = app_reference.trim();
51 |
52 | if (RE_SPACE_NAME.test(_app_reference)) {
53 | try {
54 | const res = await fetch(
55 | `https://huggingface.co/api/spaces/${_app_reference}/host`,
56 | { headers }
57 | );
58 |
59 | if (res.status !== 200)
60 | throw new Error("Space metadata could not be loaded.");
61 | const _host = (await res.json()).host;
62 |
63 | return {
64 | space_id: app_reference,
65 | ...determine_protocol(_host),
66 | };
67 | } catch (e: any) {
68 | throw new Error("Space metadata could not be loaded." + e.message);
69 | }
70 | }
71 |
72 | if (RE_SPACE_DOMAIN.test(_app_reference)) {
73 | const { ws_protocol, http_protocol, host } =
74 | determine_protocol(_app_reference);
75 |
76 | return {
77 | space_id: host.replace(".hf.space", ""),
78 | ws_protocol,
79 | http_protocol,
80 | host,
81 | };
82 | }
83 |
84 | return {
85 | space_id: false,
86 | ...determine_protocol(_app_reference),
87 | };
88 | }
89 |
90 | export function map_names_to_ids(fns: Config["dependencies"]) {
91 | let apis: Record = {};
92 |
93 | fns.forEach(({ api_name }, i) => {
94 | if (api_name) apis[api_name] = i;
95 | });
96 |
97 | return apis;
98 | }
99 |
100 | const RE_DISABLED_DISCUSSION =
101 | /^(?=[^]*\b[dD]iscussions{0,1}\b)(?=[^]*\b[dD]isabled\b)[^]*$/;
102 | export async function discussions_enabled(space_id: string) {
103 | try {
104 | const r = await fetch(
105 | `https://huggingface.co/api/spaces/${space_id}/discussions`,
106 | {
107 | method: "HEAD",
108 | }
109 | );
110 | const error = r.headers.get("x-error-message");
111 |
112 | if (error && RE_DISABLED_DISCUSSION.test(error)) return false;
113 | else return true;
114 | } catch (e) {
115 | return false;
116 | }
117 | }
118 |
119 | export async function get_space_hardware(
120 | space_id: string,
121 | token: `hf_${string}`
122 | ) {
123 | const headers: { Authorization?: string } = {};
124 | if (token) {
125 | headers.Authorization = `Bearer ${token}`;
126 | }
127 |
128 | try {
129 | const res = await fetch(
130 | `https://huggingface.co/api/spaces/${space_id}/runtime`,
131 | { headers }
132 | );
133 |
134 | if (res.status !== 200)
135 | throw new Error("Space hardware could not be obtained.");
136 |
137 | const { hardware } = await res.json();
138 |
139 | return hardware;
140 | } catch (e: any) {
141 | throw new Error(e.message);
142 | }
143 | }
144 |
145 | export async function set_space_hardware(
146 | space_id: string,
147 | new_hardware: (typeof hardware_types)[number],
148 | token: `hf_${string}`
149 | ) {
150 | const headers: { Authorization?: string } = {};
151 | if (token) {
152 | headers.Authorization = `Bearer ${token}`;
153 | }
154 |
155 | try {
156 | const res = await fetch(
157 | `https://huggingface.co/api/spaces/${space_id}/hardware`,
158 | { headers, body: JSON.stringify(new_hardware) }
159 | );
160 |
161 | if (res.status !== 200)
162 | throw new Error(
163 | "Space hardware could not be set. Please ensure the space hardware provided is valid and that a Hugging Face token is passed in."
164 | );
165 |
166 | const { hardware } = await res.json();
167 |
168 | return hardware;
169 | } catch (e: any) {
170 | throw new Error(e.message);
171 | }
172 | }
173 |
174 | export async function set_space_timeout(
175 | space_id: string,
176 | timeout: number,
177 | token: `hf_${string}`
178 | ) {
179 | const headers: { Authorization?: string } = {};
180 | if (token) {
181 | headers.Authorization = `Bearer ${token}`;
182 | }
183 |
184 | try {
185 | const res = await fetch(
186 | `https://huggingface.co/api/spaces/${space_id}/hardware`,
187 | { headers, body: JSON.stringify({ seconds: timeout }) }
188 | );
189 |
190 | if (res.status !== 200)
191 | throw new Error(
192 | "Space hardware could not be set. Please ensure the space hardware provided is valid and that a Hugging Face token is passed in."
193 | );
194 |
195 | const { hardware } = await res.json();
196 |
197 | return hardware;
198 | } catch (e: any) {
199 | throw new Error(e.message);
200 | }
201 | }
202 |
203 | export const hardware_types = [
204 | "cpu-basic",
205 | "cpu-upgrade",
206 | "t4-small",
207 | "t4-medium",
208 | "a10g-small",
209 | "a10g-large",
210 | "a100-large",
211 | ] as const;
212 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./midjourney";
2 | export * from "./discord.message";
3 | export * from "./discord.ws";
4 | export * from "./interfaces/index";
5 | export * from "./midjourney.api";
6 | export * from "./command";
7 | export * from "./verify.human";
8 | export * from "./banned.words";
9 | export * from "./face.swap";
10 |
--------------------------------------------------------------------------------
/src/interfaces/config.ts:
--------------------------------------------------------------------------------
1 | import WebSocket from "isomorphic-ws";
2 |
3 | export type FetchFn = typeof fetch;
4 | export type WebSocketCl = typeof WebSocket;
5 | export const MJBot = "936929561302675456";
6 | export const NijiBot = "1022952195194359889";
7 | export interface MJConfig {
8 | ChannelId: string;
9 | SalaiToken: string;
10 | BotId: typeof MJBot | typeof NijiBot;
11 | Debug: boolean;
12 | Limit: number;
13 | MaxWait: number;
14 | SessionId: string;
15 | ServerId?: string;
16 | Ws?: boolean;
17 | Remix?: boolean;
18 | HuggingFaceToken?: string;
19 | DiscordBaseUrl: string;
20 | WsBaseUrl: string;
21 | fetch: FetchFn;
22 | ApiInterval: number;
23 | WebSocket: WebSocketCl;
24 | ImageProxy: string;
25 | }
26 | export interface MJConfigParam {
27 | SalaiToken: string; //DISCORD_TOKEN
28 | ChannelId?: string; //DISCORD_CHANNEL_ID
29 | ServerId?: string; //DISCORD_SERVER_ID
30 | BotId?: typeof MJBot | typeof NijiBot; //DISCORD_BOT_ID MJBot OR NijiBot
31 | Debug?: boolean; // print log
32 | ApiInterval?: number; //ApiInterval request api interval
33 | Limit?: number; //Limit of get message list
34 | MaxWait?: number;
35 | Remix?: boolean; //Remix:true use remix mode
36 | Ws?: boolean; //Ws:true use websocket get discord message (ephemeral message)
37 | HuggingFaceToken?: string; //HuggingFaceToken for verify human
38 | SessionId?: string;
39 | DiscordBaseUrl?: string;
40 | ImageProxy?: string;
41 | WsBaseUrl?: string;
42 | fetch?: FetchFn; //Node.js<18 need node.fetch Or proxy
43 | WebSocket?: WebSocketCl; //isomorphic-ws Or proxy
44 | }
45 |
46 | export const DefaultMJConfig: MJConfig = {
47 | BotId: MJBot,
48 | ChannelId: "1077800642086703114",
49 | SalaiToken: "",
50 | ApiInterval: 350,
51 | SessionId: "8bb7f5b79c7a49f7d0824ab4b8773a81",
52 | Debug: false,
53 | Limit: 50,
54 | Ws: true,
55 | MaxWait: 200,
56 | ImageProxy: "",
57 | DiscordBaseUrl: "https://discord.com",
58 | WsBaseUrl: "wss://gateway.discord.gg/?encoding=json&v=9",
59 | fetch: fetch,
60 | WebSocket: WebSocket,
61 | };
62 |
--------------------------------------------------------------------------------
/src/interfaces/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./message";
2 | export * from "./config";
3 | export * from "./upload";
4 | export * from "./modal";
5 |
--------------------------------------------------------------------------------
/src/interfaces/message.ts:
--------------------------------------------------------------------------------
1 | export interface MJMessage {
2 | uri: string;
3 | proxy_url?: string;
4 | content: string;
5 | flags: number;
6 | id?: string;
7 | hash?: string;
8 | progress?: string;
9 | options?: MJOptions[];
10 | width?: number;
11 | height?: number;
12 | }
13 |
14 | export type LoadingHandler = (uri: string, progress: string) => void;
15 | export type OnModal = (nonce: string, id: string) => Promise;
16 |
17 | export interface WaitMjEvent {
18 | nonce: string;
19 | prompt?: string;
20 | id?: string;
21 | del?: boolean; // is delete message
22 | onmodal?: OnModal;
23 | }
24 | export interface MJEmit {
25 | error?: Error;
26 | message?: MJMessage;
27 | }
28 |
29 | export interface MJInfo {
30 | subscription: string;
31 | jobMode: string;
32 | visibilityMode: string;
33 | fastTimeRemaining: string;
34 | lifetimeUsage: string;
35 | relaxedUsage: string;
36 | queuedJobsFast: string;
37 | queuedJobsRelax: string;
38 | runningJobs: string;
39 | }
40 |
41 | export interface MJOptions {
42 | label: string;
43 | type: number;
44 | style: number;
45 | custom: string;
46 | }
47 | export interface MJSettings {
48 | content: string;
49 | id: string;
50 | flags: number;
51 | options: MJOptions[];
52 | }
53 | export interface MJDescribe {
54 | id: string;
55 | flags: number;
56 | uri: string;
57 | proxy_url?: string;
58 | options: MJOptions[];
59 | descriptions: string[];
60 | }
61 |
62 | export interface MJShorten {
63 | description: string;
64 | id: string;
65 | flags: number;
66 | options: MJOptions[];
67 | prompts: string[];
68 | }
69 |
--------------------------------------------------------------------------------
/src/interfaces/modal.ts:
--------------------------------------------------------------------------------
1 |
2 | export const RemixModalSubmitID = "MJ::RemixModal::new_prompt"
3 | export const CustomZoomModalSubmitID = "MJ::OutpaintCustomZoomModal::prompt"
4 | export const ShortenModalSubmitID = "MJ::ImagineModal::new_prompt"
5 | export const DescribeModalSubmitID = "MJ::Picreader::Modal::PromptField"
6 |
7 | export type ModalSubmitID = typeof RemixModalSubmitID | typeof CustomZoomModalSubmitID | typeof ShortenModalSubmitID | typeof DescribeModalSubmitID
--------------------------------------------------------------------------------
/src/interfaces/upload.ts:
--------------------------------------------------------------------------------
1 | export type UploadParam = {
2 | filename: string;
3 | file_size: number;
4 | id: number | string;
5 | };
6 | export type UploadSlot = {
7 | id: number;
8 | upload_filename: string;
9 | upload_url: string;
10 | };
11 | export type DiscordImage = {
12 | id: number | string;
13 | filename: string;
14 | upload_filename: string;
15 | };
16 |
--------------------------------------------------------------------------------
/src/midjourney.api.ts:
--------------------------------------------------------------------------------
1 | import {
2 | CustomZoomModalSubmitID,
3 | DescribeModalSubmitID,
4 | DiscordImage,
5 | MJConfig,
6 | ModalSubmitID,
7 | RemixModalSubmitID,
8 | ShortenModalSubmitID,
9 | UploadParam,
10 | UploadSlot,
11 | } from "./interfaces";
12 |
13 | import { nextNonce, sleep } from "./utils";
14 | import { Command } from "./command";
15 | import async from "async";
16 |
17 | export class MidjourneyApi extends Command {
18 | UpId = Date.now() % 10; // upload id
19 | constructor(public config: MJConfig) {
20 | super(config);
21 | }
22 | private safeIteractions = (request: any) => {
23 | return new Promise((resolve, reject) => {
24 | this.queue.push(
25 | {
26 | request,
27 | callback: (any: any) => {
28 | resolve(any);
29 | },
30 | },
31 | (error: any, result: any) => {
32 | if (error) {
33 | reject(error);
34 | } else {
35 | resolve(result);
36 | }
37 | }
38 | );
39 | });
40 | };
41 | private processRequest = async ({
42 | request,
43 | callback,
44 | }: {
45 | request: any;
46 | callback: (any: any) => void;
47 | }) => {
48 | const httpStatus = await this.interactions(request);
49 | callback(httpStatus);
50 | await sleep(this.config.ApiInterval);
51 | };
52 | private queue = async.queue(this.processRequest, 1);
53 | private interactions = async (payload: any) => {
54 | try {
55 | const headers = {
56 | "Content-Type": "application/json",
57 | Authorization: this.config.SalaiToken,
58 | };
59 | const response = await this.config.fetch(
60 | `${this.config.DiscordBaseUrl}/api/v9/interactions`,
61 | {
62 | method: "POST",
63 | body: JSON.stringify(payload),
64 | headers: headers,
65 | }
66 | );
67 | if (response.status >= 400) {
68 | console.error("api.error.config", {
69 | payload: JSON.stringify(payload),
70 | config: this.config,
71 | });
72 | }
73 | return response.status;
74 | } catch (error) {
75 | console.error(error);
76 | return 500;
77 | }
78 | };
79 |
80 | async ImagineApi(prompt: string, nonce: string = nextNonce()) {
81 | const payload = await this.imaginePayload(prompt, nonce);
82 | return this.safeIteractions(payload);
83 | }
84 |
85 | async SwitchRemixApi(nonce: string = nextNonce()) {
86 | const payload = await this.PreferPayload(nonce);
87 | return this.safeIteractions(payload);
88 | }
89 |
90 | async ShortenApi(prompt: string, nonce: string = nextNonce()) {
91 | const payload = await this.shortenPayload(prompt, nonce);
92 | return this.safeIteractions(payload);
93 | }
94 |
95 | async VariationApi({
96 | index,
97 | msgId,
98 | hash,
99 | nonce = nextNonce(),
100 | flags = 0,
101 | }: {
102 | index: 1 | 2 | 3 | 4;
103 | msgId: string;
104 | hash: string;
105 | nonce?: string;
106 | flags?: number;
107 | }) {
108 | return this.CustomApi({
109 | msgId,
110 | customId: `MJ::JOB::variation::${index}::${hash}`,
111 | flags,
112 | nonce,
113 | });
114 | }
115 |
116 | async UpscaleApi({
117 | index,
118 | msgId,
119 | hash,
120 | nonce = nextNonce(),
121 | flags,
122 | }: {
123 | index: 1 | 2 | 3 | 4;
124 | msgId: string;
125 | hash: string;
126 | nonce?: string;
127 | flags: number;
128 | }) {
129 | return this.CustomApi({
130 | msgId,
131 | customId: `MJ::JOB::upsample::${index}::${hash}`,
132 | flags,
133 | nonce,
134 | });
135 | }
136 |
137 | async RerollApi({
138 | msgId,
139 | hash,
140 | nonce = nextNonce(),
141 | flags,
142 | }: {
143 | msgId: string;
144 | hash: string;
145 | nonce?: string;
146 | flags: number;
147 | }) {
148 | return this.CustomApi({
149 | msgId,
150 | customId: `MJ::JOB::reroll::0::${hash}::SOLO`,
151 | flags,
152 | nonce,
153 | });
154 | }
155 |
156 | async CustomApi({
157 | msgId,
158 | customId,
159 | flags,
160 | nonce = nextNonce(),
161 | }: {
162 | msgId: string;
163 | customId: string;
164 | flags: number;
165 | nonce?: string;
166 | }) {
167 | if (!msgId) throw new Error("msgId is empty");
168 | if (flags === undefined) throw new Error("flags is undefined");
169 | const payload = {
170 | type: 3,
171 | nonce,
172 | guild_id: this.config.ServerId,
173 | channel_id: this.config.ChannelId,
174 | message_flags: flags,
175 | message_id: msgId,
176 | application_id: this.config.BotId,
177 | session_id: this.config.SessionId,
178 | data: {
179 | component_type: 2,
180 | custom_id: customId,
181 | },
182 | };
183 | return this.safeIteractions(payload);
184 | }
185 |
186 | //FIXME: get SubmitCustomId from discord api
187 | async ModalSubmitApi({
188 | nonce,
189 | msgId,
190 | customId,
191 | prompt,
192 | submitCustomId,
193 | }: {
194 | nonce: string;
195 | msgId: string;
196 | customId: string;
197 | prompt: string;
198 | submitCustomId: ModalSubmitID;
199 | }) {
200 | var payload = {
201 | type: 5,
202 | application_id: this.config.BotId,
203 | channel_id: this.config.ChannelId,
204 | guild_id: this.config.ServerId,
205 | data: {
206 | id: msgId,
207 | custom_id: customId,
208 | components: [
209 | {
210 | type: 1,
211 | components: [
212 | {
213 | type: 4,
214 | custom_id: submitCustomId,
215 | value: prompt,
216 | },
217 | ],
218 | },
219 | ],
220 | },
221 | session_id: this.config.SessionId,
222 | nonce,
223 | };
224 | console.log("submitCustomId", JSON.stringify(payload));
225 | return this.safeIteractions(payload);
226 | }
227 |
228 | async RemixApi({
229 | nonce,
230 | msgId,
231 | customId,
232 | prompt,
233 | }: {
234 | nonce: string;
235 | msgId: string;
236 | customId: string;
237 | prompt: string;
238 | }) {
239 | return this.ModalSubmitApi({
240 | nonce,
241 | msgId,
242 | customId,
243 | prompt,
244 | submitCustomId: RemixModalSubmitID,
245 | });
246 | }
247 |
248 | async ShortenImagineApi({
249 | nonce,
250 | msgId,
251 | customId,
252 | prompt,
253 | }: {
254 | nonce: string;
255 | msgId: string;
256 | customId: string;
257 | prompt: string;
258 | }) {
259 | return this.ModalSubmitApi({
260 | nonce,
261 | msgId,
262 | customId,
263 | prompt,
264 | submitCustomId: ShortenModalSubmitID,
265 | });
266 | }
267 |
268 | async DescribeImagineApi({
269 | nonce,
270 | msgId,
271 | customId,
272 | prompt,
273 | }: {
274 | nonce: string;
275 | msgId: string;
276 | customId: string;
277 | prompt: string;
278 | }) {
279 | return this.ModalSubmitApi({
280 | nonce,
281 | msgId,
282 | customId,
283 | prompt,
284 | submitCustomId: DescribeModalSubmitID,
285 | });
286 | }
287 |
288 | async CustomZoomImagineApi({
289 | nonce,
290 | msgId,
291 | customId,
292 | prompt,
293 | }: {
294 | nonce: string;
295 | msgId: string;
296 | customId: string;
297 | prompt: string;
298 | }) {
299 | customId = customId.replace(
300 | "MJ::CustomZoom",
301 | "MJ::OutpaintCustomZoomModal"
302 | );
303 | return this.ModalSubmitApi({
304 | nonce,
305 | msgId,
306 | customId,
307 | prompt,
308 | submitCustomId: CustomZoomModalSubmitID,
309 | });
310 | }
311 |
312 | async InfoApi(nonce?: string) {
313 | const payload = await this.infoPayload(nonce);
314 | return this.safeIteractions(payload);
315 | }
316 |
317 | async SettingsApi(nonce?: string) {
318 | const payload = await this.settingsPayload(nonce);
319 | return this.safeIteractions(payload);
320 | }
321 |
322 | async FastApi(nonce?: string) {
323 | const payload = await this.fastPayload(nonce);
324 | return this.safeIteractions(payload);
325 | }
326 |
327 | async RelaxApi(nonce?: string) {
328 | const payload = await this.relaxPayload(nonce);
329 | return this.safeIteractions(payload);
330 | }
331 |
332 | /**
333 | *
334 | * @param fileUrl http file path
335 | * @returns
336 | */
337 | async UploadImageByUri(fileUrl: string) {
338 | const response = await this.config.fetch(fileUrl);
339 | const fileData = await response.arrayBuffer();
340 | const mimeType = response.headers.get("content-type");
341 | const filename = fileUrl.split("/").pop() || "image.png";
342 | const file_size = fileData.byteLength;
343 | if (!mimeType) {
344 | throw new Error("Unknown mime type");
345 | }
346 | const { attachments } = await this.attachments({
347 | filename,
348 | file_size,
349 | id: this.UpId++,
350 | });
351 | const UploadSlot = attachments[0];
352 | await this.uploadImage(UploadSlot, fileData, mimeType);
353 | const resp: DiscordImage = {
354 | id: UploadSlot.id,
355 | filename: UploadSlot.upload_filename.split("/").pop() || "image.png",
356 | upload_filename: UploadSlot.upload_filename,
357 | };
358 | return resp;
359 | }
360 |
361 | async UploadImageByBole(blob: Blob, filename = nextNonce() + ".png") {
362 | const fileData = await blob.arrayBuffer();
363 | const mimeType = blob.type;
364 | const file_size = fileData.byteLength;
365 | if (!mimeType) {
366 | throw new Error("Unknown mime type");
367 | }
368 | const { attachments } = await this.attachments({
369 | filename,
370 | file_size,
371 | id: this.UpId++,
372 | });
373 | const UploadSlot = attachments[0];
374 | await this.uploadImage(UploadSlot, fileData, mimeType);
375 | const resp: DiscordImage = {
376 | id: UploadSlot.id,
377 | filename: UploadSlot.upload_filename.split("/").pop() || "image.png",
378 | upload_filename: UploadSlot.upload_filename,
379 | };
380 | return resp;
381 | }
382 |
383 | /**
384 | * prepare an attachement to upload an image.
385 | */
386 | private async attachments(
387 | ...files: UploadParam[]
388 | ): Promise<{ attachments: UploadSlot[] }> {
389 | const { SalaiToken, DiscordBaseUrl, ChannelId, fetch } = this.config;
390 | const headers = {
391 | Authorization: SalaiToken,
392 | "content-type": "application/json",
393 | };
394 | const url = new URL(
395 | `${DiscordBaseUrl}/api/v9/channels/${ChannelId}/attachments`
396 | );
397 | const body = { files };
398 | const response = await this.config.fetch(url, {
399 | headers,
400 | method: "POST",
401 | body: JSON.stringify(body),
402 | });
403 | if (response.status === 200) {
404 | return (await response.json()) as { attachments: UploadSlot[] };
405 | }
406 | const error = `Attachments return ${response.status} ${
407 | response.statusText
408 | } ${await response.text()}`;
409 | throw new Error(error);
410 | }
411 |
412 | private async uploadImage(
413 | slot: UploadSlot,
414 | data: ArrayBuffer,
415 | contentType: string
416 | ): Promise {
417 | const body = new Uint8Array(data);
418 | const headers = { "content-type": contentType };
419 | const response = await this.config.fetch(slot.upload_url, {
420 | method: "PUT",
421 | headers,
422 | body,
423 | });
424 | if (!response.ok) {
425 | throw new Error(
426 | `uploadImage return ${response.status} ${
427 | response.statusText
428 | } ${await response.text()}`
429 | );
430 | }
431 | }
432 |
433 | async DescribeApi(image: DiscordImage, nonce?: string) {
434 | const payload = await this.describePayload(image, nonce);
435 | return this.safeIteractions(payload);
436 | }
437 | async upImageApi(image: DiscordImage, nonce?: string) {
438 | const { SalaiToken, DiscordBaseUrl, ChannelId, fetch } = this.config;
439 | const payload = {
440 | content: "",
441 | nonce,
442 | channel_id: ChannelId,
443 | type: 0,
444 | sticker_ids: [],
445 | attachments: [image],
446 | };
447 |
448 | const url = new URL(
449 | `${DiscordBaseUrl}/api/v9/channels/${ChannelId}/messages`
450 | );
451 | const headers = {
452 | Authorization: SalaiToken,
453 | "content-type": "application/json",
454 | };
455 | const response = await fetch(url, {
456 | headers,
457 | method: "POST",
458 | body: JSON.stringify(payload),
459 | });
460 |
461 | return response.status;
462 | }
463 | }
464 |
--------------------------------------------------------------------------------
/src/midjourney.ts:
--------------------------------------------------------------------------------
1 | import {
2 | DefaultMJConfig,
3 | LoadingHandler,
4 | MJConfig,
5 | MJConfigParam,
6 | } from "./interfaces";
7 | import { MidjourneyApi } from "./midjourney.api";
8 | import { MidjourneyMessage } from "./discord.message";
9 | import {
10 | toRemixCustom,
11 | custom2Type,
12 | nextNonce,
13 | random,
14 | base64ToBlob,
15 | } from "./utils";
16 | import { WsMessage } from "./discord.ws";
17 | import { faceSwap } from "./face.swap";
18 | export class Midjourney extends MidjourneyMessage {
19 | public config: MJConfig;
20 | private wsClient?: WsMessage;
21 | public MJApi: MidjourneyApi;
22 | constructor(defaults: MJConfigParam) {
23 | const { SalaiToken } = defaults;
24 | if (!SalaiToken) {
25 | throw new Error("SalaiToken are required");
26 | }
27 | super(defaults);
28 | this.config = {
29 | ...DefaultMJConfig,
30 | ...defaults,
31 | };
32 | this.MJApi = new MidjourneyApi(this.config);
33 | }
34 | async Connect() {
35 | if (!this.config.Ws) {
36 | return this;
37 | }
38 | await this.MJApi.allCommand();
39 | if (this.wsClient) return this;
40 | this.wsClient = new WsMessage(this.config, this.MJApi);
41 | await this.wsClient.onceReady();
42 | return this;
43 | }
44 | async init() {
45 | await this.Connect();
46 | const settings = await this.Settings();
47 | if (settings) {
48 | // this.log(`settings:`, settings.content);
49 | const remix = settings.options.find((o) => o.label === "Remix mode");
50 | if (remix?.style == 3) {
51 | this.config.Remix = true;
52 | this.log(`Remix mode enabled`);
53 | }
54 | }
55 | return this;
56 | }
57 | async Imagine(prompt: string, loading?: LoadingHandler) {
58 | prompt = prompt.trim();
59 | if (!this.config.Ws) {
60 | const seed = random(1000000000, 9999999999);
61 | prompt = `[${seed}] ${prompt}`;
62 | } else {
63 | await this.getWsClient();
64 | }
65 |
66 | const nonce = nextNonce();
67 | this.log(`Imagine`, prompt, "nonce", nonce);
68 | const httpStatus = await this.MJApi.ImagineApi(prompt, nonce);
69 | if (httpStatus !== 204) {
70 | throw new Error(`ImagineApi failed with status ${httpStatus}`);
71 | }
72 | if (this.wsClient) {
73 | return await this.wsClient.waitImageMessage({ nonce, loading, prompt });
74 | } else {
75 | this.log(`await generate image`);
76 | const msg = await this.WaitMessage(prompt, loading);
77 | this.log(`image generated`, prompt, msg?.uri);
78 | return msg;
79 | }
80 | }
81 | // check ws enabled && connect
82 | private async getWsClient() {
83 | if (!this.config.Ws) {
84 | throw new Error(`ws not enabled`);
85 | }
86 | if (!this.wsClient) {
87 | await this.Connect();
88 | }
89 | if (!this.wsClient) {
90 | throw new Error(`ws not connected`);
91 | }
92 | return this.wsClient;
93 | }
94 |
95 | async Settings() {
96 | const wsClient = await this.getWsClient();
97 | const nonce = nextNonce();
98 | const httpStatus = await this.MJApi.SettingsApi(nonce);
99 | if (httpStatus !== 204) {
100 | throw new Error(`ImagineApi failed with status ${httpStatus}`);
101 | }
102 | return wsClient.waitSettings();
103 | }
104 | async Reset() {
105 | const settings = await this.Settings();
106 | if (!settings) {
107 | throw new Error(`Settings not found`);
108 | }
109 | const reset = settings.options.find((o) => o.label === "Reset Settings");
110 | if (!reset) {
111 | throw new Error(`Reset Settings not found`);
112 | }
113 | const httpstatus = await this.MJApi.CustomApi({
114 | msgId: settings.id,
115 | customId: reset.custom,
116 | flags: settings.flags,
117 | });
118 | if (httpstatus !== 204) {
119 | throw new Error(`Reset failed with status ${httpstatus}`);
120 | }
121 | }
122 |
123 | async Info() {
124 | const wsClient = await this.getWsClient();
125 | const nonce = nextNonce();
126 | const httpStatus = await this.MJApi.InfoApi(nonce);
127 | if (httpStatus !== 204) {
128 | throw new Error(`InfoApi failed with status ${httpStatus}`);
129 | }
130 | return wsClient.waitInfo();
131 | }
132 |
133 | async Fast() {
134 | const nonce = nextNonce();
135 | const httpStatus = await this.MJApi.FastApi(nonce);
136 | if (httpStatus !== 204) {
137 | throw new Error(`FastApi failed with status ${httpStatus}`);
138 | }
139 | return null;
140 | }
141 | async Relax() {
142 | const nonce = nextNonce();
143 | const httpStatus = await this.MJApi.RelaxApi(nonce);
144 | if (httpStatus !== 204) {
145 | throw new Error(`RelaxApi failed with status ${httpStatus}`);
146 | }
147 | return null;
148 | }
149 | async SwitchRemix() {
150 | const wsClient = await this.getWsClient();
151 | const nonce = nextNonce();
152 | const httpStatus = await this.MJApi.SwitchRemixApi(nonce);
153 | if (httpStatus !== 204) {
154 | throw new Error(`RelaxApi failed with status ${httpStatus}`);
155 | }
156 | return wsClient.waitContent("prefer-remix");
157 | }
158 | async Describe(imgUri: string) {
159 | const wsClient = await this.getWsClient();
160 | const nonce = nextNonce();
161 | const DcImage = await this.MJApi.UploadImageByUri(imgUri);
162 | this.log(`Describe`, DcImage);
163 | const httpStatus = await this.MJApi.DescribeApi(DcImage, nonce);
164 | if (httpStatus !== 204) {
165 | throw new Error(`DescribeApi failed with status ${httpStatus}`);
166 | }
167 | return wsClient.waitDescribe(nonce);
168 | }
169 | async DescribeByBlob(blob: Blob) {
170 | const wsClient = await this.getWsClient();
171 | const nonce = nextNonce();
172 | const DcImage = await this.MJApi.UploadImageByBole(blob);
173 | this.log(`Describe`, DcImage);
174 | const httpStatus = await this.MJApi.DescribeApi(DcImage, nonce);
175 | if (httpStatus !== 204) {
176 | throw new Error(`DescribeApi failed with status ${httpStatus}`);
177 | }
178 | return wsClient.waitDescribe(nonce);
179 | }
180 |
181 | async Shorten(prompt: string) {
182 | const wsClient = await this.getWsClient();
183 | const nonce = nextNonce();
184 | const httpStatus = await this.MJApi.ShortenApi(prompt, nonce);
185 | if (httpStatus !== 204) {
186 | throw new Error(`ShortenApi failed with status ${httpStatus}`);
187 | }
188 | return wsClient.waitShorten(nonce);
189 | }
190 |
191 | async Variation({
192 | index,
193 | msgId,
194 | hash,
195 | content,
196 | flags,
197 | loading,
198 | }: {
199 | index: 1 | 2 | 3 | 4;
200 | msgId: string;
201 | hash: string;
202 | content?: string;
203 | flags: number;
204 | loading?: LoadingHandler;
205 | }) {
206 | return await this.Custom({
207 | customId: `MJ::JOB::variation::${index}::${hash}`,
208 | msgId,
209 | content,
210 | flags,
211 | loading,
212 | });
213 | }
214 |
215 | async Upscale({
216 | index,
217 | msgId,
218 | hash,
219 | content,
220 | flags,
221 | loading,
222 | }: {
223 | index: 1 | 2 | 3 | 4;
224 | msgId: string;
225 | hash: string;
226 | content?: string;
227 | flags: number;
228 | loading?: LoadingHandler;
229 | }) {
230 | return await this.Custom({
231 | customId: `MJ::JOB::upsample::${index}::${hash}`,
232 | msgId,
233 | content,
234 | flags,
235 | loading,
236 | });
237 | }
238 |
239 | async Custom({
240 | msgId,
241 | customId,
242 | content,
243 | flags,
244 | loading,
245 | }: {
246 | msgId: string;
247 | customId: string;
248 | content?: string;
249 | flags: number;
250 | loading?: LoadingHandler;
251 | }) {
252 | if (this.config.Ws) {
253 | await this.getWsClient();
254 | }
255 | const nonce = nextNonce();
256 | const httpStatus = await this.MJApi.CustomApi({
257 | msgId,
258 | customId,
259 | flags,
260 | nonce,
261 | });
262 | if (httpStatus !== 204) {
263 | throw new Error(`CustomApi failed with status ${httpStatus}`);
264 | }
265 | if (this.wsClient) {
266 | return await this.wsClient.waitImageMessage({
267 | nonce,
268 | loading,
269 | messageId: msgId,
270 | prompt: content,
271 | onmodal: async (nonde, id) => {
272 | if (content === undefined || content === "") {
273 | return "";
274 | }
275 | const newNonce = nextNonce();
276 | switch (custom2Type(customId)) {
277 | case "customZoom":
278 | const httpStatus = await this.MJApi.CustomZoomImagineApi({
279 | msgId: id,
280 | customId,
281 | prompt: content,
282 | nonce: newNonce,
283 | });
284 | if (httpStatus !== 204) {
285 | throw new Error(
286 | `CustomZoomImagineApi failed with status ${httpStatus}`
287 | );
288 | }
289 | return newNonce;
290 | case "variation":
291 | if (this.config.Remix !== true) {
292 | return "";
293 | }
294 | customId = toRemixCustom(customId);
295 | const remixHttpStatus = await this.MJApi.RemixApi({
296 | msgId: id,
297 | customId,
298 | prompt: content,
299 | nonce: newNonce,
300 | });
301 | if (remixHttpStatus !== 204) {
302 | throw new Error(
303 | `RemixApi failed with status ${remixHttpStatus}`
304 | );
305 | }
306 | return newNonce;
307 | default:
308 | return "";
309 | throw new Error(`unknown customId ${customId}`);
310 | }
311 | },
312 | });
313 | }
314 | if (content === undefined || content === "") {
315 | throw new Error(`content is required`);
316 | }
317 | return await this.WaitMessage(content, loading);
318 | }
319 |
320 | async ZoomOut({
321 | level,
322 | msgId,
323 | hash,
324 | content,
325 | flags,
326 | loading,
327 | }: {
328 | level: "high" | "low" | "2x" | "1.5x";
329 | msgId: string;
330 | hash: string;
331 | content?: string;
332 | flags: number;
333 | loading?: LoadingHandler;
334 | }) {
335 | let customId: string;
336 | switch (level) {
337 | case "high":
338 | customId = `MJ::JOB::high_variation::1::${hash}::SOLO`;
339 | break;
340 | case "low":
341 | customId = `MJ::JOB::low_variation::1::${hash}::SOLO`;
342 | break;
343 | case "2x":
344 | customId = `MJ::Outpaint::50::1::${hash}::SOLO`;
345 | break;
346 | case "1.5x":
347 | customId = `MJ::Outpaint::75::1::${hash}::SOLO`;
348 | break;
349 | }
350 | return this.Custom({
351 | msgId,
352 | customId,
353 | content,
354 | flags,
355 | loading,
356 | });
357 | }
358 |
359 | async Reroll({
360 | msgId,
361 | hash,
362 | content,
363 | flags,
364 | loading,
365 | }: {
366 | msgId: string;
367 | hash: string;
368 | content?: string;
369 | flags: number;
370 | loading?: LoadingHandler;
371 | }) {
372 | return await this.Custom({
373 | customId: `MJ::JOB::reroll::0::${hash}::SOLO`,
374 | msgId,
375 | content,
376 | flags,
377 | loading,
378 | });
379 | }
380 |
381 | async FaceSwap(target: string, source: string) {
382 | const wsClient = await this.getWsClient();
383 | const app = new faceSwap(this.config.HuggingFaceToken);
384 | const Target = await (await this.config.fetch(target)).blob();
385 | const Source = await (await this.config.fetch(source)).blob();
386 | const res = await app.changeFace(Target, Source);
387 | this.log(res[0]);
388 | const blob = await base64ToBlob(res[0] as string);
389 | const DcImage = await this.MJApi.UploadImageByBole(blob);
390 | const nonce = nextNonce();
391 | const httpStatus = await this.MJApi.DescribeApi(DcImage, nonce);
392 | if (httpStatus !== 204) {
393 | throw new Error(`DescribeApi failed with status ${httpStatus}`);
394 | }
395 | return wsClient.waitDescribe(nonce);
396 | }
397 |
398 | Close() {
399 | if (this.wsClient) {
400 | this.wsClient.close();
401 | this.wsClient = undefined;
402 | }
403 | }
404 | }
405 |
--------------------------------------------------------------------------------
/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | import { Snowyflake, Epoch } from "snowyflake";
2 | import { MJInfo, MJOptions } from "../interfaces";
3 |
4 | export const sleep = async (ms: number): Promise =>
5 | await new Promise((resolve) => setTimeout(resolve, ms));
6 |
7 | export const random = (min: number, max: number): number =>
8 | Math.floor(Math.random() * (max - min) + min);
9 |
10 | const snowflake = new Snowyflake({
11 | workerId: 0n,
12 | processId: 0n,
13 | epoch: Epoch.Discord, // BigInt timestamp
14 | });
15 |
16 | export const nextNonce = (): string => snowflake.nextId().toString();
17 |
18 | export const formatPrompts = (prompts: string) => {
19 | const regex = /(\d️⃣ .+)/g;
20 | const matches = prompts.match(regex);
21 | if (matches) {
22 | const shortenedPrompts = matches.map((match) => match.trim());
23 | return shortenedPrompts;
24 | } else {
25 | return [];
26 | }
27 | };
28 |
29 | export const formatOptions = (components: any) => {
30 | var data: MJOptions[] = [];
31 | for (var i = 0; i < components.length; i++) {
32 | const component = components[i];
33 | if (component.components && component.components.length > 0) {
34 | const item = formatOptions(component.components);
35 | data = data.concat(item);
36 | }
37 | if (!component.custom_id) continue;
38 | data.push({
39 | type: component.type,
40 | style: component.style,
41 | label: component.label || component.emoji?.name,
42 | custom: component.custom_id,
43 | });
44 | }
45 | return data;
46 | };
47 |
48 | export const formatInfo = (msg: string) => {
49 | let jsonResult: MJInfo = {
50 | subscription: "",
51 | jobMode: "",
52 | visibilityMode: "",
53 | fastTimeRemaining: "",
54 | lifetimeUsage: "",
55 | relaxedUsage: "",
56 | queuedJobsFast: "",
57 | queuedJobsRelax: "",
58 | runningJobs: "",
59 | }; // Initialize jsonResult with empty object
60 | msg.split("\n").forEach(function (line) {
61 | const colonIndex = line.indexOf(":");
62 | if (colonIndex > -1) {
63 | const key = line.substring(0, colonIndex).trim().replaceAll("**", "");
64 | const value = line.substring(colonIndex + 1).trim();
65 | switch (key) {
66 | case "Subscription":
67 | jsonResult.subscription = value;
68 | break;
69 | case "Job Mode":
70 | jsonResult.jobMode = value;
71 | break;
72 | case "Visibility Mode":
73 | jsonResult.visibilityMode = value;
74 | break;
75 | case "Fast Time Remaining":
76 | jsonResult.fastTimeRemaining = value;
77 | break;
78 | case "Lifetime Usage":
79 | jsonResult.lifetimeUsage = value;
80 | break;
81 | case "Relaxed Usage":
82 | jsonResult.relaxedUsage = value;
83 | break;
84 | case "Queued Jobs (fast)":
85 | jsonResult.queuedJobsFast = value;
86 | break;
87 | case "Queued Jobs (relax)":
88 | jsonResult.queuedJobsRelax = value;
89 | break;
90 | case "Running Jobs":
91 | jsonResult.runningJobs = value;
92 | break;
93 | default:
94 | // Do nothing
95 | }
96 | }
97 | });
98 | return jsonResult;
99 | };
100 |
101 | export const uriToHash = (uri: string) => {
102 | return uri.split("_").pop()?.split(".")[0] ?? "";
103 | };
104 |
105 | export const content2progress = (content: string) => {
106 | if (!content) return "";
107 | const spcon = content.split("<@");
108 | if (spcon.length < 2) {
109 | return "";
110 | }
111 | content = spcon[1];
112 | const regex = /\(([^)]+)\)/; // matches the value inside the first parenthesis
113 | const match = content.match(regex);
114 | let progress = "";
115 | if (match) {
116 | progress = match[1];
117 | }
118 | return progress;
119 | };
120 |
121 | export const content2prompt = (content: string) => {
122 | if (!content) return "";
123 | const pattern = /\*\*(.*?)\*\*/; // Match **middle content
124 | const matches = content.match(pattern);
125 | if (matches && matches.length > 1) {
126 | return matches[1]; // Get the matched content
127 | } else {
128 | console.log("No match found.", content);
129 | return content;
130 | }
131 | };
132 |
133 | export function custom2Type(custom: string) {
134 | if (custom.includes("upsample")) {
135 | return "upscale";
136 | } else if (custom.includes("variation")) {
137 | return "variation";
138 | } else if (custom.includes("reroll")) {
139 | return "reroll";
140 | } else if (custom.includes("CustomZoom")) {
141 | return "customZoom";
142 | } else if (custom.includes("Outpaint")) {
143 | return "variation";
144 | } else if (custom.includes("remaster")) {
145 | return "reroll";
146 | }
147 | return null;
148 | }
149 |
150 | export const toRemixCustom = (customID: string) => {
151 | const parts = customID.split("::");
152 | const convertedString = `MJ::RemixModal::${parts[4]}::${parts[3]}::1`;
153 | return convertedString;
154 | };
155 |
156 | export async function base64ToBlob(base64Image: string): Promise {
157 | // 移除 base64 图像头部信息
158 | const base64Data = base64Image.replace(
159 | /^data:image\/(png|jpeg|jpg);base64,/,
160 | ""
161 | );
162 |
163 | // 将 base64 数据解码为二进制数据
164 | const binaryData = atob(base64Data);
165 |
166 | // 创建一个 Uint8Array 来存储二进制数据
167 | const arrayBuffer = new ArrayBuffer(binaryData.length);
168 | const uint8Array = new Uint8Array(arrayBuffer);
169 | for (let i = 0; i < binaryData.length; i++) {
170 | uint8Array[i] = binaryData.charCodeAt(i);
171 | }
172 |
173 | // 使用 Uint8Array 创建 Blob 对象
174 | return new Blob([uint8Array], { type: "image/png" }); // 替换为相应的 MIME 类型
175 | }
176 |
--------------------------------------------------------------------------------
/src/verify.human.ts:
--------------------------------------------------------------------------------
1 | import { HfInference } from "@huggingface/inference";
2 | import { MJConfig } from "./interfaces";
3 | export class VerifyHuman {
4 | private inference: HfInference;
5 |
6 | constructor(public config: MJConfig) {
7 | const { HuggingFaceToken } = config;
8 | if (HuggingFaceToken === "" || HuggingFaceToken) {
9 | throw new Error("HuggingFaceToken is required");
10 | }
11 | this.inference = new HfInference(HuggingFaceToken);
12 | }
13 |
14 | async verify(imageUri: string, categories: string[]) {
15 | console.log("verify----start", imageUri, categories);
16 | const imageCates = await this.inference.imageClassification({
17 | data: await (await this.config.fetch(imageUri)).blob(),
18 | model: "google/vit-base-patch16-224",
19 | });
20 | console.log("verify----response", { imageCates });
21 | for (const imageCate of imageCates) {
22 | const { label } = imageCate;
23 | for (const category of categories) {
24 | if (label.includes(category)) {
25 | return category;
26 | }
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/test/face.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { faceSwap } from "../src";
3 | /**
4 | *
5 | * ```
6 | * npx tsx test/face.ts
7 | * ```
8 | */
9 |
10 | async function test2() {
11 | const app = new faceSwap(process.env.HuggingFaceToken);
12 | const Target = await (
13 | await fetch(
14 | "https://cdn.discordapp.com/attachments/1108587422389899304/1129321837042602016/guapitu006_a_girls_face_with_david_bowies_thunderbolt_71ee5899-bd45-4fc4-8c9d-92f19ddb0a03.png"
15 | )
16 | ).blob();
17 | const Source = await (
18 | await fetch(
19 | "https://cdn.discordapp.com/attachments/1108587422389899304/1129321826804306031/guapitu006_Cute_warrior_girl_in_the_style_of_Baten_Kaitos__111f39bc-329e-4fab-9af7-ee219fedf260.png"
20 | )
21 | ).blob();
22 |
23 | await app.changeFace(Target, Source);
24 | }
25 | test2();
26 |
--------------------------------------------------------------------------------
/test/test.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 | import { Command, DefaultMJConfig, Midjourney, WsMessage } from "../src";
3 | /**
4 | *
5 | * a simple example of how to use the imagine command
6 | * ```
7 | * npx tsx test/test.ts
8 | * ```
9 | */
10 |
11 | async function test2() {
12 | const config = {
13 | ...DefaultMJConfig,
14 | ...{
15 | ServerId: process.env.SERVER_ID,
16 | ChannelId: process.env.CHANNEL_ID,
17 | SalaiToken: process.env.SALAI_TOKEN,
18 | },
19 | };
20 |
21 | const command = new Command(config);
22 | const msg = await command.getCommand("imagine");
23 | }
24 | test2();
25 |
--------------------------------------------------------------------------------
/test/test2.ts:
--------------------------------------------------------------------------------
1 | import async from "async";
2 | /**
3 | *
4 | * a simple example of how to use the imagine command
5 | * ```
6 | * npx tsx test/test2.ts
7 | * ```
8 | */
9 |
10 | const processRequest = async ({
11 | request,
12 | callback,
13 | }: {
14 | request: any;
15 | callback: (any) => void;
16 | }) => {
17 | // 在这里执行实际的HTTP请求
18 | // 可以使用任何HTTP库,比如axios或node-fetch
19 | // console.log("Request processed:", request, new Date());
20 |
21 | // 这里只是一个示例,使用setTimeout模拟一个异步请求
22 | await new Promise((resolve) => setTimeout(resolve, 1000));
23 | callback(request + " processed");
24 | await new Promise((resolve) => setTimeout(resolve, 2000));
25 | return "sleep processed";
26 | };
27 | const queue = async.queue(processRequest, 1);
28 | const addRequest = (request: any) => {
29 | console.log("Request queued:", request, new Date());
30 | return new Promise((resolve, reject) => {
31 | queue.push(
32 | {
33 | request,
34 | callback: (any) => {
35 | resolve(any);
36 | },
37 | },
38 | (error: any, result: any) => {
39 | if (error) {
40 | reject(error);
41 | } else {
42 | resolve(result);
43 | }
44 | }
45 | );
46 | });
47 | };
48 |
49 | const safeRequest = async (request: any) => {
50 | const dd = await addRequest(request);
51 | return dd + " safeRequest" + new Date();
52 | };
53 | async function test2() {
54 | safeRequest("1").then((result) => console.log(result));
55 | safeRequest("2").then((result) => console.log(result));
56 | safeRequest("3").then((result) => console.log(result));
57 | safeRequest("4").then((result) => console.log(result));
58 | safeRequest("5").then((result) => console.log(result));
59 | safeRequest("6").then((result) => console.log(result));
60 | const dd = await safeRequest("32");
61 | console.log(dd);
62 | }
63 | test2();
64 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 | /* Projects */
5 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
6 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
7 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
8 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
9 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
10 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
11 | /* Language and Environment */
12 | "target": "ES2022" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
13 | "lib": [
14 | "es2021",
15 | "DOM",
16 | "esnext.asynciterable"
17 | ] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
18 | // "jsx": "preserve", /* Specify what JSX code is generated. */
19 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
20 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
21 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
22 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
23 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
24 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
25 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
26 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
27 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
28 | /* Modules */
29 | "module": "commonjs" /* Specify what module code is generated. */,
30 | // "rootDir": "./", /* Specify the root folder within your source files. */
31 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
32 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
33 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
34 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
35 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
36 | "types": [
37 | "node"
38 | ] /* Specify type package names to be included without being referenced in a source file. */,
39 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
40 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
41 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
42 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
43 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
44 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
45 | // "resolveJsonModule": true, /* Enable importing .json files. */
46 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
47 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
48 | /* JavaScript Support */
49 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
50 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
51 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
52 | /* Emit */
53 | "declaration": true /* Generate .d.ts files from TypeScript and JavaScript files in your project. */,
54 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
55 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
56 | "sourceMap": true /* Create source map files for emitted JavaScript files. */,
57 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
58 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
59 | "outDir": "./libs" /* Specify an output folder for all emitted files. */,
60 | // "removeComments": true, /* Disable emitting comments. */
61 | // "noEmit": true, /* Disable emitting files from a compilation. */
62 | "importHelpers": true /* Allow importing helper functions from tslib once per project, instead of including them per-file. */,
63 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
64 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
65 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
66 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
67 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
68 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
69 | // "newLine": "crlf", /* Set the newline character for emitting files. */
70 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
71 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
72 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
73 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
74 | "declarationDir": "./libs" /* Specify the output directory for generated declaration files. */,
75 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
76 | /* Interop Constraints */
77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
78 | // "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. */
79 | "allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */,
80 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
82 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
83 | /* Type Checking */
84 | "strict": true /* Enable all strict type-checking options. */,
85 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
86 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
87 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
88 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
89 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
90 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
91 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
92 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
93 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
94 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
95 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
96 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
97 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
98 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
99 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
100 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
101 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
102 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
103 | /* Completeness */
104 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
105 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
106 | },
107 | "include": ["./src"]
108 | }
109 |
--------------------------------------------------------------------------------
/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@cspotcode/source-map-support@^0.8.0":
6 | version "0.8.1"
7 | resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz"
8 | integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==
9 | dependencies:
10 | "@jridgewell/trace-mapping" "0.3.9"
11 |
12 | "@esbuild-kit/cjs-loader@^2.4.2":
13 | version "2.4.2"
14 | resolved "https://registry.npmjs.org/@esbuild-kit/cjs-loader/-/cjs-loader-2.4.2.tgz"
15 | integrity sha512-BDXFbYOJzT/NBEtp71cvsrGPwGAMGRB/349rwKuoxNSiKjPraNNnlK6MIIabViCjqZugu6j+xeMDlEkWdHHJSg==
16 | dependencies:
17 | "@esbuild-kit/core-utils" "^3.0.0"
18 | get-tsconfig "^4.4.0"
19 |
20 | "@esbuild-kit/core-utils@^3.0.0":
21 | version "3.1.0"
22 | resolved "https://registry.npmjs.org/@esbuild-kit/core-utils/-/core-utils-3.1.0.tgz"
23 | integrity sha512-Uuk8RpCg/7fdHSceR1M6XbSZFSuMrxcePFuGgyvsBn+u339dk5OeL4jv2EojwTN2st/unJGsVm4qHWjWNmJ/tw==
24 | dependencies:
25 | esbuild "~0.17.6"
26 | source-map-support "^0.5.21"
27 |
28 | "@esbuild-kit/esm-loader@^2.5.5":
29 | version "2.5.5"
30 | resolved "https://registry.npmjs.org/@esbuild-kit/esm-loader/-/esm-loader-2.5.5.tgz"
31 | integrity sha512-Qwfvj/qoPbClxCRNuac1Du01r9gvNOT+pMYtJDapfB1eoGN1YlJ1BixLyL9WVENRx5RXgNLdfYdx/CuswlGhMw==
32 | dependencies:
33 | "@esbuild-kit/core-utils" "^3.0.0"
34 | get-tsconfig "^4.4.0"
35 |
36 | "@esbuild/android-arm64@0.17.18":
37 | version "0.17.18"
38 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz#4aa8d8afcffb4458736ca9b32baa97d7cb5861ea"
39 | integrity sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==
40 |
41 | "@esbuild/android-arm@0.17.18":
42 | version "0.17.18"
43 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.17.18.tgz#74a7e95af4ee212ebc9db9baa87c06a594f2a427"
44 | integrity sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==
45 |
46 | "@esbuild/android-x64@0.17.18":
47 | version "0.17.18"
48 | resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.17.18.tgz#1dcd13f201997c9fe0b204189d3a0da4eb4eb9b6"
49 | integrity sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==
50 |
51 | "@esbuild/darwin-arm64@0.17.18":
52 | version "0.17.18"
53 | resolved "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz"
54 | integrity sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==
55 |
56 | "@esbuild/darwin-x64@0.17.18":
57 | version "0.17.18"
58 | resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz#a6da308d0ac8a498c54d62e0b2bfb7119b22d315"
59 | integrity sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==
60 |
61 | "@esbuild/freebsd-arm64@0.17.18":
62 | version "0.17.18"
63 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz#b83122bb468889399d0d63475d5aea8d6829c2c2"
64 | integrity sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==
65 |
66 | "@esbuild/freebsd-x64@0.17.18":
67 | version "0.17.18"
68 | resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz#af59e0e03fcf7f221b34d4c5ab14094862c9c864"
69 | integrity sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==
70 |
71 | "@esbuild/linux-arm64@0.17.18":
72 | version "0.17.18"
73 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz#8551d72ba540c5bce4bab274a81c14ed01eafdcf"
74 | integrity sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==
75 |
76 | "@esbuild/linux-arm@0.17.18":
77 | version "0.17.18"
78 | resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz#e09e76e526df4f665d4d2720d28ff87d15cdf639"
79 | integrity sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==
80 |
81 | "@esbuild/linux-ia32@0.17.18":
82 | version "0.17.18"
83 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz#47878860ce4fe73a36fd8627f5647bcbbef38ba4"
84 | integrity sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==
85 |
86 | "@esbuild/linux-loong64@0.17.18":
87 | version "0.17.18"
88 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz#3f8fbf5267556fc387d20b2e708ce115de5c967a"
89 | integrity sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==
90 |
91 | "@esbuild/linux-mips64el@0.17.18":
92 | version "0.17.18"
93 | resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz#9d896d8f3c75f6c226cbeb840127462e37738226"
94 | integrity sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==
95 |
96 | "@esbuild/linux-ppc64@0.17.18":
97 | version "0.17.18"
98 | resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz#3d9deb60b2d32c9985bdc3e3be090d30b7472783"
99 | integrity sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==
100 |
101 | "@esbuild/linux-riscv64@0.17.18":
102 | version "0.17.18"
103 | resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz#8a943cf13fd24ff7ed58aefb940ef178f93386bc"
104 | integrity sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==
105 |
106 | "@esbuild/linux-s390x@0.17.18":
107 | version "0.17.18"
108 | resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz#66cb01f4a06423e5496facabdce4f7cae7cb80e5"
109 | integrity sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==
110 |
111 | "@esbuild/linux-x64@0.17.18":
112 | version "0.17.18"
113 | resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz#23c26050c6c5d1359c7b774823adc32b3883b6c9"
114 | integrity sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==
115 |
116 | "@esbuild/netbsd-x64@0.17.18":
117 | version "0.17.18"
118 | resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz#789a203d3115a52633ff6504f8cbf757f15e703b"
119 | integrity sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==
120 |
121 | "@esbuild/openbsd-x64@0.17.18":
122 | version "0.17.18"
123 | resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz#d7b998a30878f8da40617a10af423f56f12a5e90"
124 | integrity sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==
125 |
126 | "@esbuild/sunos-x64@0.17.18":
127 | version "0.17.18"
128 | resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz#ecad0736aa7dae07901ba273db9ef3d3e93df31f"
129 | integrity sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==
130 |
131 | "@esbuild/win32-arm64@0.17.18":
132 | version "0.17.18"
133 | resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz#58dfc177da30acf956252d7c8ae9e54e424887c4"
134 | integrity sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==
135 |
136 | "@esbuild/win32-ia32@0.17.18":
137 | version "0.17.18"
138 | resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz#340f6163172b5272b5ae60ec12c312485f69232b"
139 | integrity sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==
140 |
141 | "@esbuild/win32-x64@0.17.18":
142 | version "0.17.18"
143 | resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz#3a8e57153905308db357fd02f57c180ee3a0a1fa"
144 | integrity sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==
145 |
146 | "@huggingface/inference@^2.5.0":
147 | version "2.5.0"
148 | resolved "https://registry.npmjs.org/@huggingface/inference/-/inference-2.5.0.tgz"
149 | integrity sha512-X3NSdrWAKNTLAsEKabH48Wc+Osys+S7ilRcH1bf9trSDmJlzPVXDseXMRBHCFPCYd5AAAIakhENO4zCqstVg8g==
150 |
151 | "@jridgewell/resolve-uri@^3.0.3":
152 | version "3.1.1"
153 | resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz"
154 | integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==
155 |
156 | "@jridgewell/sourcemap-codec@^1.4.10":
157 | version "1.4.15"
158 | resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
159 | integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
160 |
161 | "@jridgewell/trace-mapping@0.3.9":
162 | version "0.3.9"
163 | resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz"
164 | integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==
165 | dependencies:
166 | "@jridgewell/resolve-uri" "^3.0.3"
167 | "@jridgewell/sourcemap-codec" "^1.4.10"
168 |
169 | "@tsconfig/node10@^1.0.7":
170 | version "1.0.9"
171 | resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz"
172 | integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==
173 |
174 | "@tsconfig/node12@^1.0.7":
175 | version "1.0.11"
176 | resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz"
177 | integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==
178 |
179 | "@tsconfig/node14@^1.0.0":
180 | version "1.0.3"
181 | resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz"
182 | integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==
183 |
184 | "@tsconfig/node16@^1.0.2":
185 | version "1.0.4"
186 | resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz"
187 | integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==
188 |
189 | "@types/async@^3.2.20":
190 | version "3.2.20"
191 | resolved "https://registry.yarnpkg.com/@types/async/-/async-3.2.20.tgz#53517caaa68c94f99da1c4e986cf7f2954981515"
192 | integrity sha512-6jSBQQugzyX1aWto0CbvOnmxrU9tMoXfA9gc4IrLEtvr3dTwSg5GLGoWiZnGLI6UG/kqpB3JOQKQrqnhUWGKQA==
193 |
194 | "@types/node@*", "@types/node@^18.16.0":
195 | version "18.16.0"
196 | resolved "https://registry.npmjs.org/@types/node/-/node-18.16.0.tgz"
197 | integrity sha512-BsAaKhB+7X+H4GnSjGhJG9Qi8Tw+inU9nJDwmD5CgOmBLEI6ArdhikpLX7DjbjDRDTbqZzU2LSQNZg8WGPiSZQ==
198 |
199 | "@types/ws@^8.5.4":
200 | version "8.5.4"
201 | resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz"
202 | integrity sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg==
203 | dependencies:
204 | "@types/node" "*"
205 |
206 | acorn-walk@^8.1.1:
207 | version "8.2.0"
208 | resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz"
209 | integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==
210 |
211 | acorn@^8.4.1:
212 | version "8.8.2"
213 | resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz"
214 | integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
215 |
216 | arg@^4.1.0:
217 | version "4.1.3"
218 | resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz"
219 | integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==
220 |
221 | async@^3.2.4:
222 | version "3.2.4"
223 | resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c"
224 | integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==
225 |
226 | buffer-from@^1.0.0:
227 | version "1.1.2"
228 | resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
229 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
230 |
231 | create-require@^1.1.0:
232 | version "1.1.1"
233 | resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
234 | integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
235 |
236 | diff@^4.0.1:
237 | version "4.0.2"
238 | resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz"
239 | integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
240 |
241 | dotenv@^16.0.3:
242 | version "16.0.3"
243 | resolved "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz"
244 | integrity sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==
245 |
246 | esbuild@~0.17.6:
247 | version "0.17.18"
248 | resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz"
249 | integrity sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==
250 | optionalDependencies:
251 | "@esbuild/android-arm" "0.17.18"
252 | "@esbuild/android-arm64" "0.17.18"
253 | "@esbuild/android-x64" "0.17.18"
254 | "@esbuild/darwin-arm64" "0.17.18"
255 | "@esbuild/darwin-x64" "0.17.18"
256 | "@esbuild/freebsd-arm64" "0.17.18"
257 | "@esbuild/freebsd-x64" "0.17.18"
258 | "@esbuild/linux-arm" "0.17.18"
259 | "@esbuild/linux-arm64" "0.17.18"
260 | "@esbuild/linux-ia32" "0.17.18"
261 | "@esbuild/linux-loong64" "0.17.18"
262 | "@esbuild/linux-mips64el" "0.17.18"
263 | "@esbuild/linux-ppc64" "0.17.18"
264 | "@esbuild/linux-riscv64" "0.17.18"
265 | "@esbuild/linux-s390x" "0.17.18"
266 | "@esbuild/linux-x64" "0.17.18"
267 | "@esbuild/netbsd-x64" "0.17.18"
268 | "@esbuild/openbsd-x64" "0.17.18"
269 | "@esbuild/sunos-x64" "0.17.18"
270 | "@esbuild/win32-arm64" "0.17.18"
271 | "@esbuild/win32-ia32" "0.17.18"
272 | "@esbuild/win32-x64" "0.17.18"
273 |
274 | fsevents@~2.3.2:
275 | version "2.3.2"
276 | resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz"
277 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
278 |
279 | get-tsconfig@^4.4.0:
280 | version "4.5.0"
281 | resolved "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.5.0.tgz"
282 | integrity sha512-MjhiaIWCJ1sAU4pIQ5i5OfOuHHxVo1oYeNsWTON7jxYkod8pHocXeh+SSbmu5OZZZK73B6cbJ2XADzXehLyovQ==
283 |
284 | isomorphic-ws@^5.0.0:
285 | version "5.0.0"
286 | resolved "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz"
287 | integrity sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==
288 |
289 | make-error@^1.1.1:
290 | version "1.3.6"
291 | resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz"
292 | integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==
293 |
294 | prettier@^2.8.8:
295 | version "2.8.8"
296 | resolved "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz"
297 | integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
298 |
299 | semiver@^1.1.0:
300 | version "1.1.0"
301 | resolved "https://registry.yarnpkg.com/semiver/-/semiver-1.1.0.tgz#9c97fb02c21c7ce4fcf1b73e2c7a24324bdddd5f"
302 | integrity sha512-QNI2ChmuioGC1/xjyYwyZYADILWyW6AmS1UH6gDj/SFUUUS4MBAWs/7mxnkRPc/F4iHezDP+O8t0dO8WHiEOdg==
303 |
304 | snowyflake@^2.0.0:
305 | version "2.0.0"
306 | resolved "https://registry.npmjs.org/snowyflake/-/snowyflake-2.0.0.tgz"
307 | integrity sha512-BxeqV0KJxJASu6EBJGUkX194Zhh37AEa0ow/JRK39icWbLTG9Wl/7LAL6a/ZMSjNm4O9pZk6QoLcWP7f/YKmtA==
308 |
309 | source-map-support@^0.5.21:
310 | version "0.5.21"
311 | resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz"
312 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
313 | dependencies:
314 | buffer-from "^1.0.0"
315 | source-map "^0.6.0"
316 |
317 | source-map@^0.6.0:
318 | version "0.6.1"
319 | resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz"
320 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
321 |
322 | ts-node@^10.9.1:
323 | version "10.9.1"
324 | resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz"
325 | integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==
326 | dependencies:
327 | "@cspotcode/source-map-support" "^0.8.0"
328 | "@tsconfig/node10" "^1.0.7"
329 | "@tsconfig/node12" "^1.0.7"
330 | "@tsconfig/node14" "^1.0.0"
331 | "@tsconfig/node16" "^1.0.2"
332 | acorn "^8.4.1"
333 | acorn-walk "^8.1.1"
334 | arg "^4.1.0"
335 | create-require "^1.1.0"
336 | diff "^4.0.1"
337 | make-error "^1.1.1"
338 | v8-compile-cache-lib "^3.0.1"
339 | yn "3.1.1"
340 |
341 | tslib@^2.5.0:
342 | version "2.5.0"
343 | resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz"
344 | integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==
345 |
346 | tsx@^3.12.6:
347 | version "3.12.6"
348 | resolved "https://registry.npmjs.org/tsx/-/tsx-3.12.6.tgz"
349 | integrity sha512-q93WgS3lBdHlPgS0h1i+87Pt6n9K/qULIMNYZo07nSeu2z5QE2CellcAZfofVXBo2tQg9av2ZcRMQ2S2i5oadQ==
350 | dependencies:
351 | "@esbuild-kit/cjs-loader" "^2.4.2"
352 | "@esbuild-kit/core-utils" "^3.0.0"
353 | "@esbuild-kit/esm-loader" "^2.5.5"
354 | optionalDependencies:
355 | fsevents "~2.3.2"
356 |
357 | typescript@^5.0.4:
358 | version "5.0.4"
359 | resolved "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz"
360 | integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
361 |
362 | v8-compile-cache-lib@^3.0.1:
363 | version "3.0.1"
364 | resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz"
365 | integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==
366 |
367 | ws@^8.13.0:
368 | version "8.13.0"
369 | resolved "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz"
370 | integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
371 |
372 | yn@3.1.1:
373 | version "3.1.1"
374 | resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz"
375 | integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==
376 |
--------------------------------------------------------------------------------