├── .gitignore
├── Readme.md
├── cli.js
├── config.json.dist
├── index.js
├── modules
├── auth.js
├── dialoges.js
└── messages.js
├── package-lock.json
├── package.json
├── scripts
├── download-channel.js
├── download-selected-message.js
└── listen-channel.js
├── templates
└── channels.ejs
└── utils
├── file-helper.js
├── helper.js
├── input-helper.js
└── logger.js
/.gitignore:
--------------------------------------------------------------------------------
1 | config.json
2 | export/
3 | .DS_Store
4 | node_modules/
5 |
--------------------------------------------------------------------------------
/Readme.md:
--------------------------------------------------------------------------------
1 | # Telegram Channel Downloader
2 |
3 | **Telegram Channel Downloader** is a Node.js application that allows users to download media files and messages in HTML and JSON formats from Telegram channels, groups, or users. This tool simplifies the process of archiving content from Telegram for offline viewing or storage.
4 |
5 | ## Sponsor the Project
6 |
7 |
Support the project by buying me a coffee! Every contribution helps keep the project running.
8 |
9 |
10 | ## Setup
11 |
12 | To use the Telegram Channel Downloader, follow these steps:
13 |
14 | 1. **Create a Telegram App**: Go to [https://my.telegram.org/apps](https://my.telegram.org/apps) and create a new application.
15 | 2. **Get API Credentials**: After creating the app, copy the API ID and API Hash provided by Telegram.
16 |
17 | ### Configure `config.json`
18 |
19 | 3. In the root directory of the application, create a file named `config.json` and paste the following code:
20 |
21 | ```json
22 | {
23 | "apiId": "YOUR_API_ID",
24 | "apiHash": "YOUR_API_HASH",
25 | "sessionId": ""
26 | }
27 | ```
28 |
29 | Replace `YOUR_API_ID` and `YOUR_API_HASH` with the values obtained in step 2. Keep the `sessionId` blank for now; it will be updated automatically after logging in for the first time.
30 |
31 | ## Usage
32 |
33 | Once the setup is complete, you can start using the Telegram Channel Downloader:
34 |
35 | 1. Install all dependencies using:
36 |
37 | ```bash
38 | npm install
39 | ```
40 |
41 | 2. **Run the Script**: Open your terminal or command prompt and navigate to the directory where the Telegram Channel Downloader is located. Run the following command to start the script:
42 |
43 | ```bash
44 | npm start
45 | ```
46 |
47 | 3. **Login**: The script will prompt you to enter your phone number and the code sent to your phone or Telegram account. This authentication is required only the first time you run the script.
48 |
49 | 4. **Select Chat/Channel/Group**: After logging in, choose the target chat, channel, or group you want to scrape. Use the arrow keys to move and select the target chat.
50 |
51 | 5. **Wait for Download**: The script will start downloading all available media files and messages from the specified channel, group, or user. Depending on the size of the content, this process may take some time.
52 |
53 | 6. **Access Downloaded Files**: Once the download is complete, you can find the downloaded media files and messages in the `export/` directory within the Telegram Channel Downloader directory.
54 |
55 | ## CLI
56 |
57 | Available Commands
58 |
59 | | Script Name | Description |
60 | |---------------------------|---------------------------------------------------------------|
61 | | `listen-channel` | Listen to a channel and download media from incoming messages |
62 | | `download-selected-message`| Download media from selected messages |
63 | | `download-channel` | Download all media from a channel |
64 |
65 | ***Using CLI Commands***
66 |
67 | ```bash
68 | node cli [script-name] --options
69 | ```
70 |
71 | example `node cli listen-channel --channelId=12345`
72 |
73 | ## Additional Notes
74 |
75 | * **Session Handling**: The `sessionId` field in the `config.json` file will be automatically updated after logging in for the first time. This session ID is used for subsequent logins to avoid re-entering your credentials.
76 | * **Media Types**: The Telegram Channel Downloader supports downloading various types of media files, including images, videos, audio files, documents, and other attachments shared within the specified channel, group, or user.
77 |
78 | ## Contributing
79 |
80 | Contributions are welcome! If you have any suggestions, bug reports, or feature requests, please open an issue or submit a pull request.
81 |
82 | Happy coding
83 |
--------------------------------------------------------------------------------
/cli.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | // example command script
3 | // - node cli script-name --option1=value1 --option2=value2
4 |
5 | const fs = require("fs");
6 | const path = require("path");
7 | const { glob } = require("glob");
8 | const logger = require("./utils/logger");
9 | const commandFile = path.join(__dirname, "./scripts");
10 |
11 | const commandFiles = glob.sync([`${commandFile}/**/*.js`]);
12 |
13 | process.on("uncaughtException", (error) => {
14 | logger.error(`Uncaught Exception: ${error.message}`);
15 | logger.error(error.stack);
16 | process.exit(1);
17 | });
18 |
19 | process.on("unhandledRejection", (reason, promise) => {
20 | logger.error(
21 | `Unhandled Rejection: ${reason instanceof Error ? reason.message : reason}`
22 | );
23 | logger.error(`Promise: ${promise}`);
24 | if (reason instanceof Error) {
25 | logger.error(reason.stack);
26 | }
27 | process.exit(1);
28 | });
29 |
30 | /**
31 | * Loads command classes from the specified file paths and returns an object
32 | * mapping command signatures to their respective file paths.
33 | *
34 | * @param {string[]} commandFiles - An array of file paths to the command classes.
35 | * @returns {Object} An object where the keys are command signatures and the values are file paths.
36 | */
37 | function loadCommands(commandFiles) {
38 | const commands = {};
39 | commandFiles.forEach((filePath) => {
40 | const CommandClass = require(filePath);
41 | const signature = path.basename(filePath, ".js");
42 | commands[signature] = {
43 | signature,
44 | path: filePath,
45 | description: CommandClass.description(),
46 | };
47 | });
48 |
49 | return commands;
50 | }
51 |
52 | /**
53 | * Logs the available commands to the console.
54 | * It iterates over the keys of the `availableCommands` object and logs each command signature.
55 | */
56 | function logAvailableCommands() {
57 | logger.success("Available commands:");
58 | const allCommands = Object.values(loadCommands(commandFiles)).map(
59 | (command) => ({
60 | signature: command.signature,
61 | description: command.description,
62 | })
63 | );
64 |
65 | logger.table(allCommands);
66 | }
67 |
68 | /**
69 | * Parses command-line arguments into a script signature and options object.
70 | *
71 | * @param {string[]} argv - The array of command-line arguments.
72 | * @returns {Object} An object containing the script signature and options.
73 | * @returns {string} return.scriptSignature - The script signature (usually the command to run).
74 | * @returns {Object} return.options - An object containing key-value pairs of options.
75 | */
76 | function parseArguments(argv) {
77 | const scriptSignature = argv[2];
78 | const args = argv.slice(3);
79 | const options = {};
80 |
81 | args.forEach((arg) => {
82 | if (arg.startsWith("--")) {
83 | const [key, value] = arg.slice(2).split("=", 2);
84 | options[key] = value === undefined ? true : value;
85 | }
86 | });
87 |
88 | return { scriptSignature, options };
89 | }
90 |
91 | /**
92 | * Executes a command script located at the given path with the specified options.
93 | *
94 | * @param {string} commandPath - The path to the command script to execute.
95 | * @param {Object} options - The options to pass to the command script.
96 | * @returns {Promise} - A promise that resolves when the command execution is complete.
97 | */
98 |
99 | async function runCommand(commandPath, options) {
100 | const Command = require(commandPath);
101 | logger.info(`${path.basename(commandFile, '.js')} - ${Command.description()}`);
102 |
103 | if (options.help) {
104 | if (Command.help) {
105 | logger.info(Command.help());
106 | } else {
107 | logger.info("No help available for this command");
108 | }
109 | } else {
110 | const commandInstance = new Command();
111 | await commandInstance.handle(options);
112 | }
113 | }
114 |
115 | /**
116 | * Parses the command line arguments and extracts the script signature and options.
117 | *
118 | * @param {string[]} process.argv - The array of command line arguments passed to the script.
119 | * @returns {{ scriptSignature: string, options: object }} An object containing the script signature and options.
120 | */
121 |
122 | (async () => {
123 | if (!fs.existsSync("./export")) {
124 | fs.mkdirSync("./export");
125 | }
126 |
127 | const availableCommands = loadCommands(commandFiles);
128 | const { scriptSignature, options } = parseArguments(process.argv);
129 |
130 | if (scriptSignature) {
131 | const commandDetail = availableCommands[scriptSignature];
132 | if (commandDetail) {
133 | await runCommand(commandDetail.path, options);
134 | }
135 | } else {
136 | logAvailableCommands();
137 | }
138 | })();
139 |
--------------------------------------------------------------------------------
/config.json.dist:
--------------------------------------------------------------------------------
1 | {
2 | "apiId": YOUR_API_ID,
3 | "apiHash": "YOUR_API_HASH",
4 | "sessionId": ""
5 | }
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const ChannelDownloader = require("./scripts/download-channel");
2 | const channelDownloader = new ChannelDownloader();
3 |
4 | const channelId = "";
5 | const downloadableFiles = {
6 | webpage: true,
7 | poll: true,
8 | geo: true,
9 | contact: true,
10 | venue: true,
11 | sticker: true,
12 | image: true,
13 | video: true,
14 | audio: true,
15 | pdf: true,
16 | };
17 |
18 | (async () => {
19 | try {
20 | await channelDownloader.handle({ channelId, downloadableFiles });
21 | } catch (err) {
22 | console.error(err);
23 | }
24 | })();
25 |
--------------------------------------------------------------------------------
/modules/auth.js:
--------------------------------------------------------------------------------
1 | const { TelegramClient } = require("telegram");
2 | const { updateCredentials, getCredentials } = require("../utils/file-helper");
3 | const { StringSession } = require("telegram/sessions");
4 | const { logMessage } = require("../utils/helper");
5 |
6 | const {
7 | textInput,
8 | mobileNumberInput,
9 | otpInput,
10 | selectInput,
11 | } = require("../utils/input-helper");
12 |
13 | const OTP_METHOD = {
14 | SMS: "sms",
15 | APP: "app",
16 | };
17 |
18 | let { apiHash, apiId, sessionId } = getCredentials();
19 | const stringSession = new StringSession(sessionId || "");
20 |
21 | /**
22 | * Initializes the authentication process for the Telegram client.
23 | * @param {string} [otpPreference=OTP_METHOD.APP] - The preferred method for receiving the OTP (either 'app' or 'sms').
24 | * @returns {Promise} - The authenticated Telegram client.
25 | */
26 | const initAuth = async (otpPreference = OTP_METHOD.APP) => {
27 | const client = new TelegramClient(stringSession, apiId, apiHash, {
28 | connectionRetries: 5,
29 | });
30 |
31 | try {
32 | if (!sessionId) {
33 | otpPreference = await selectInput("Where do you want the login OTP:", [
34 | OTP_METHOD.APP,
35 | OTP_METHOD.SMS,
36 | ]);
37 | }
38 |
39 | const forceSMS = otpPreference === OTP_METHOD.SMS;
40 |
41 | await client.start({
42 | phoneNumber: async () => await mobileNumberInput(),
43 | password: async () => await textInput("Enter your password"),
44 | phoneCode: async (isCodeViaApp) => {
45 | logMessage.info(`OTP sent over ${isCodeViaApp ? "APP" : "SMS"}`);
46 |
47 | return await otpInput();
48 | },
49 | forceSMS,
50 | onError: (err) => logMessage.error(err),
51 | });
52 |
53 | logMessage.success("You should now be connected.");
54 |
55 | if (!sessionId) {
56 | sessionId = client.session.save();
57 | updateCredentials({ sessionId });
58 | logMessage.info(
59 | "To avoid logging in again and again, the session ID has been saved to config.json. Please don't share it with anyone."
60 | );
61 | }
62 |
63 | return client;
64 | } catch (err) {
65 | logMessage.error(err);
66 |
67 | throw err;
68 | }
69 | };
70 |
71 | module.exports = {
72 | initAuth,
73 | };
74 |
--------------------------------------------------------------------------------
/modules/dialoges.js:
--------------------------------------------------------------------------------
1 | const ejs = require('ejs');
2 | const fs = require("fs");
3 | const path = require('path');
4 | const { updateLastSelection } = require("../utils/file-helper");
5 | const { logMessage, getDialogType, circularStringify } = require("../utils/helper");
6 | const { numberInput, textInput, booleanInput } = require('../utils/input-helper');
7 |
8 | /**
9 | * Fetches all dialogs from the client, sorts them by name, and exports them to JSON and HTML files.
10 | * @param {Object} client - The client object to fetch dialogs from.
11 | * @param {boolean} [sortByName=true] - Whether to sort the dialogs by name.
12 | * @returns {Promise} - A promise that resolves to the list of dialogs.
13 | */
14 | const getAllDialogs = async (client, sortByName = true) => {
15 | try {
16 | const dialogs = await client.getDialogs();
17 | const dialogList = dialogs.map(d => ({
18 | deletedAccount: d.entity?.deleted,
19 | isBot: d.entity?.bot,
20 | username: d.entity?.username?.trim(),
21 | lastMessage: d.message?.message?.trim(),
22 | lastMessageTimestamp: d.message?.date,
23 | phone: d.entity?.phone,
24 | firstName: d.entity?.firstName?.trim(),
25 | lastName: d.entity?.lastName?.trim(),
26 | name: d.title?.trim(),
27 | id: d.id,
28 | type: getDialogType(d)
29 | }));
30 |
31 | if (sortByName) {
32 | dialogList.sort((a, b) => a.name.localeCompare(b.name));
33 | }
34 |
35 | const channelTemplateFile = path.resolve(__dirname, '../templates/channels.ejs');
36 | const renderedHtml = await ejs.renderFile(channelTemplateFile, { channels: dialogList });
37 |
38 | fs.writeFileSync("./export/raw_dialog_list.json", circularStringify(dialogs, null, 2));
39 | fs.writeFileSync("./export/dialog_list.html", renderedHtml);
40 | fs.writeFileSync("./export/dialog_list.json", JSON.stringify(dialogList, null, 2));
41 |
42 | return dialogList;
43 | } catch (error) {
44 | logMessage.error(`Failed to get dialogs: ${error.message}`);
45 | throw error;
46 | }
47 | };
48 |
49 | /**
50 | * Prompts the user to select a dialog from the list.
51 | * @param {Array} dialogs - The list of dialogs.
52 | * @returns {Promise} - A promise that resolves to the selected dialog's ID.
53 | */
54 | const userDialogSelection = async (dialogs) => {
55 | try {
56 | const selectedChannelNumber = await numberInput(`Please select from above list (1-${dialogs.length}): `, 1, dialogs.length);
57 |
58 | if (selectedChannelNumber > dialogs.length) {
59 | logMessage.error("Invalid Input");
60 | process.exit(0);
61 | }
62 |
63 | const selectedChannel = dialogs[selectedChannelNumber - 1];
64 | const channelId = selectedChannel.id;
65 | logMessage.info(`Selected channel: ${selectedChannel.name}`);
66 |
67 | updateLastSelection({
68 | channelId: channelId,
69 | messageOffsetId: 0
70 | });
71 |
72 | return channelId;
73 | } catch (error) {
74 | logMessage.error(`Failed to select dialog: ${error.message}`);
75 | throw error;
76 | }
77 | };
78 |
79 | /**
80 | * Displays the list of dialogs and prompts the user to select one.
81 | * @param {Array} dialogs - The list of dialogs.
82 | * @returns {Promise} - A promise that resolves to the selected dialog's ID.
83 | */
84 | const selectDialog = async (dialogs) => {
85 | dialogs.forEach((d, index) => {
86 | console.log(`${index + 1} - ${d.name}`);
87 | });
88 |
89 | return await userDialogSelection(dialogs);
90 | };
91 |
92 | /**
93 | * Prompts the user to search for a dialog by name.
94 | * @param {Array} dialogs - The list of dialogs.
95 | * @returns {Promise} - A promise that resolves to the selected dialog's ID.
96 | */
97 | const searchDialog = async (dialogs) => {
98 | try {
99 | const searchString = await textInput('Please enter name of channel to search');
100 | searchThroughDialogsWithSearchString(dialogs, searchString);
101 |
102 | const foundWantedDialog = await booleanInput('Found channel? If answering with "no" you can search again');
103 | if (foundWantedDialog) {
104 | return await userDialogSelection(dialogs);
105 | } else {
106 | return await searchDialog(dialogs);
107 | }
108 | } catch (error) {
109 | logMessage.error(`Failed to search dialog: ${error.message}`);
110 | throw error;
111 | }
112 | };
113 |
114 | /**
115 | * Searches through the dialogs for a given search string and logs the results.
116 | * @param {Array} dialogs - The list of dialogs.
117 | * @param {string} searchString - The search string.
118 | */
119 | const searchThroughDialogsWithSearchString = (dialogs, searchString) => {
120 | dialogs.forEach((d, index) => {
121 | if (d.name.toUpperCase().includes(searchString.toUpperCase())) {
122 | console.log(`${index + 1} - ${d.name}`);
123 | }
124 | });
125 | };
126 |
127 | /**
128 | * Retrieves the name of a dialog by its ID.
129 | * @param {number} channelId - The ID of the channel.
130 | * @returns {string|null} - The name of the dialog, or null if not found.
131 | */
132 | const getDialogName = async (client, channelId) => {
133 | try {
134 | const diaLogPath = path.resolve(process.cwd(), "./export/dialog_list.json");
135 | if(!fs.existsSync(diaLogPath)) {
136 | await getAllDialogs(client);
137 | process.exit(0);
138 | }
139 |
140 | const dialogs = require(diaLogPath);
141 | const dialog = dialogs.find(d => d.id == channelId);
142 | return dialog ? dialog.name : null;
143 | } catch (error) {
144 | logMessage.error(`Failed to get dialog name: ${error.message}`);
145 | return null;
146 | }
147 | };
148 |
149 | module.exports = {
150 | getAllDialogs,
151 | selectDialog,
152 | searchDialog,
153 | getDialogName
154 | };
--------------------------------------------------------------------------------
/modules/messages.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 | const logger = require("../utils/logger");
4 | const { circularStringify } = require("../utils/helper");
5 |
6 | const getMessages = async (client, channelId, limit = 10, offsetId = 0) => {
7 | if (!client || !channelId) {
8 | throw new Error("Client and channelId are required");
9 | }
10 |
11 | try {
12 | const result = await client.getMessages(channelId, { limit, offsetId });
13 | return result;
14 | } catch (error) {
15 | throw new Error(`Failed to get messages: ${error.message}`);
16 | }
17 | };
18 |
19 | const getMessageDetail = async (client, channelId, messageIds) => {
20 | if (!client || !channelId || !messageIds) {
21 | throw new Error("Client, channelId, and messageIds are required");
22 | }
23 |
24 | try {
25 | const result = await client.getMessages(channelId, { ids: messageIds });
26 | return result;
27 | } catch (error) {
28 | throw new Error(`Failed to get message details: ${error.message}`);
29 | }
30 | };
31 |
32 | const downloadMessageMedia = async (client, message, mediaPath) => {
33 | try {
34 | if (!client || !message || !mediaPath) {
35 | logger.error("Client, message, and mediaPath are required");
36 | return false;
37 | }
38 |
39 | if (message.media) {
40 | if (message.media.webpage) {
41 | const url = message.media.webpage.url;
42 | if (url) {
43 | const urlPath = path.join(mediaPath, `../${message.id}_url.txt`);
44 | fs.writeFileSync(urlPath, url);
45 | }
46 |
47 | mediaPath = path.join(
48 | mediaPath,
49 | `../${message?.media?.webpage?.id}_image.jpeg`
50 | );
51 | }
52 |
53 | if (message.media.poll) {
54 | const pollPath = path.join(mediaPath, `../${message.id}_poll.json`);
55 | fs.writeFileSync(
56 | pollPath,
57 | circularStringify(message.media.poll, null, 2)
58 | );
59 | }
60 |
61 | await client.downloadMedia(message, {
62 | outputFile: mediaPath,
63 | progressCallback: (downloaded, total) => {
64 | const name = path.basename(mediaPath);
65 | if (total === downloaded) {
66 | logger.success(`File ${name} downloaded successfully`);
67 | }
68 | },
69 | });
70 |
71 | return true;
72 | } else {
73 | logger.error("No media found in the message");
74 | return false;
75 | }
76 |
77 | } catch (err) {
78 | logger.error("Error in downloadMessageMedia()");
79 | console.error(err);
80 | return false;
81 | }
82 | };
83 |
84 | module.exports = {
85 | getMessages,
86 | getMessageDetail,
87 | downloadMessageMedia,
88 | };
89 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "telegram-channel-downloader",
3 | "version": "1.0.0",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "telegram-channel-downloader",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "ejs": "^3.1.10",
13 | "glob": "^11.0.0",
14 | "inquirer": "^8.2.6",
15 | "mime-db": "^1.52.0",
16 | "telegram": "^2.21.2"
17 | }
18 | },
19 | "node_modules/@cryptography/aes": {
20 | "version": "0.1.1",
21 | "resolved": "https://registry.npmjs.org/@cryptography/aes/-/aes-0.1.1.tgz",
22 | "integrity": "sha512-PcYz4FDGblO6tM2kSC+VzhhK62vml6k6/YAkiWtyPvrgJVfnDRoHGDtKn5UiaRRUrvUTTocBpvc2rRgTCqxjsg=="
23 | },
24 | "node_modules/@isaacs/cliui": {
25 | "version": "8.0.2",
26 | "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
27 | "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
28 | "dependencies": {
29 | "string-width": "^5.1.2",
30 | "string-width-cjs": "npm:string-width@^4.2.0",
31 | "strip-ansi": "^7.0.1",
32 | "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
33 | "wrap-ansi": "^8.1.0",
34 | "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
35 | },
36 | "engines": {
37 | "node": ">=12"
38 | }
39 | },
40 | "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
41 | "version": "6.1.0",
42 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
43 | "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
44 | "engines": {
45 | "node": ">=12"
46 | },
47 | "funding": {
48 | "url": "https://github.com/chalk/ansi-regex?sponsor=1"
49 | }
50 | },
51 | "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
52 | "version": "6.2.1",
53 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
54 | "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
55 | "engines": {
56 | "node": ">=12"
57 | },
58 | "funding": {
59 | "url": "https://github.com/chalk/ansi-styles?sponsor=1"
60 | }
61 | },
62 | "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
63 | "version": "9.2.2",
64 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
65 | "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
66 | },
67 | "node_modules/@isaacs/cliui/node_modules/string-width": {
68 | "version": "5.1.2",
69 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
70 | "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
71 | "dependencies": {
72 | "eastasianwidth": "^0.2.0",
73 | "emoji-regex": "^9.2.2",
74 | "strip-ansi": "^7.0.1"
75 | },
76 | "engines": {
77 | "node": ">=12"
78 | },
79 | "funding": {
80 | "url": "https://github.com/sponsors/sindresorhus"
81 | }
82 | },
83 | "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
84 | "version": "7.1.0",
85 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
86 | "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
87 | "dependencies": {
88 | "ansi-regex": "^6.0.1"
89 | },
90 | "engines": {
91 | "node": ">=12"
92 | },
93 | "funding": {
94 | "url": "https://github.com/chalk/strip-ansi?sponsor=1"
95 | }
96 | },
97 | "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
98 | "version": "8.1.0",
99 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
100 | "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
101 | "dependencies": {
102 | "ansi-styles": "^6.1.0",
103 | "string-width": "^5.0.1",
104 | "strip-ansi": "^7.0.1"
105 | },
106 | "engines": {
107 | "node": ">=12"
108 | },
109 | "funding": {
110 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
111 | }
112 | },
113 | "node_modules/ansi-escapes": {
114 | "version": "4.3.2",
115 | "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
116 | "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
117 | "dependencies": {
118 | "type-fest": "^0.21.3"
119 | },
120 | "engines": {
121 | "node": ">=8"
122 | },
123 | "funding": {
124 | "url": "https://github.com/sponsors/sindresorhus"
125 | }
126 | },
127 | "node_modules/ansi-regex": {
128 | "version": "5.0.1",
129 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
130 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
131 | "engines": {
132 | "node": ">=8"
133 | }
134 | },
135 | "node_modules/ansi-styles": {
136 | "version": "4.3.0",
137 | "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
138 | "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
139 | "dependencies": {
140 | "color-convert": "^2.0.1"
141 | },
142 | "engines": {
143 | "node": ">=8"
144 | },
145 | "funding": {
146 | "url": "https://github.com/chalk/ansi-styles?sponsor=1"
147 | }
148 | },
149 | "node_modules/async": {
150 | "version": "3.2.5",
151 | "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz",
152 | "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg=="
153 | },
154 | "node_modules/async-mutex": {
155 | "version": "0.3.2",
156 | "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz",
157 | "integrity": "sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==",
158 | "dependencies": {
159 | "tslib": "^2.3.1"
160 | }
161 | },
162 | "node_modules/balanced-match": {
163 | "version": "1.0.2",
164 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
165 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
166 | },
167 | "node_modules/base64-js": {
168 | "version": "1.5.1",
169 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
170 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
171 | "funding": [
172 | {
173 | "type": "github",
174 | "url": "https://github.com/sponsors/feross"
175 | },
176 | {
177 | "type": "patreon",
178 | "url": "https://www.patreon.com/feross"
179 | },
180 | {
181 | "type": "consulting",
182 | "url": "https://feross.org/support"
183 | }
184 | ]
185 | },
186 | "node_modules/big-integer": {
187 | "version": "1.6.51",
188 | "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz",
189 | "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==",
190 | "engines": {
191 | "node": ">=0.6"
192 | }
193 | },
194 | "node_modules/bl": {
195 | "version": "4.1.0",
196 | "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
197 | "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
198 | "dependencies": {
199 | "buffer": "^5.5.0",
200 | "inherits": "^2.0.4",
201 | "readable-stream": "^3.4.0"
202 | }
203 | },
204 | "node_modules/bl/node_modules/buffer": {
205 | "version": "5.7.1",
206 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
207 | "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
208 | "funding": [
209 | {
210 | "type": "github",
211 | "url": "https://github.com/sponsors/feross"
212 | },
213 | {
214 | "type": "patreon",
215 | "url": "https://www.patreon.com/feross"
216 | },
217 | {
218 | "type": "consulting",
219 | "url": "https://feross.org/support"
220 | }
221 | ],
222 | "dependencies": {
223 | "base64-js": "^1.3.1",
224 | "ieee754": "^1.1.13"
225 | }
226 | },
227 | "node_modules/brace-expansion": {
228 | "version": "1.1.11",
229 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
230 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
231 | "dependencies": {
232 | "balanced-match": "^1.0.0",
233 | "concat-map": "0.0.1"
234 | }
235 | },
236 | "node_modules/buffer": {
237 | "version": "6.0.3",
238 | "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
239 | "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
240 | "funding": [
241 | {
242 | "type": "github",
243 | "url": "https://github.com/sponsors/feross"
244 | },
245 | {
246 | "type": "patreon",
247 | "url": "https://www.patreon.com/feross"
248 | },
249 | {
250 | "type": "consulting",
251 | "url": "https://feross.org/support"
252 | }
253 | ],
254 | "dependencies": {
255 | "base64-js": "^1.3.1",
256 | "ieee754": "^1.2.1"
257 | }
258 | },
259 | "node_modules/bufferutil": {
260 | "version": "4.0.7",
261 | "resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.7.tgz",
262 | "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==",
263 | "hasInstallScript": true,
264 | "dependencies": {
265 | "node-gyp-build": "^4.3.0"
266 | },
267 | "engines": {
268 | "node": ">=6.14.2"
269 | }
270 | },
271 | "node_modules/chalk": {
272 | "version": "4.1.2",
273 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
274 | "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
275 | "dependencies": {
276 | "ansi-styles": "^4.1.0",
277 | "supports-color": "^7.1.0"
278 | },
279 | "engines": {
280 | "node": ">=10"
281 | },
282 | "funding": {
283 | "url": "https://github.com/chalk/chalk?sponsor=1"
284 | }
285 | },
286 | "node_modules/chardet": {
287 | "version": "0.7.0",
288 | "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
289 | "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
290 | },
291 | "node_modules/cli-cursor": {
292 | "version": "3.1.0",
293 | "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
294 | "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
295 | "dependencies": {
296 | "restore-cursor": "^3.1.0"
297 | },
298 | "engines": {
299 | "node": ">=8"
300 | }
301 | },
302 | "node_modules/cli-spinners": {
303 | "version": "2.9.2",
304 | "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
305 | "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
306 | "engines": {
307 | "node": ">=6"
308 | },
309 | "funding": {
310 | "url": "https://github.com/sponsors/sindresorhus"
311 | }
312 | },
313 | "node_modules/cli-width": {
314 | "version": "3.0.0",
315 | "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
316 | "integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
317 | "engines": {
318 | "node": ">= 10"
319 | }
320 | },
321 | "node_modules/clone": {
322 | "version": "1.0.4",
323 | "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
324 | "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
325 | "engines": {
326 | "node": ">=0.8"
327 | }
328 | },
329 | "node_modules/color-convert": {
330 | "version": "2.0.1",
331 | "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
332 | "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
333 | "dependencies": {
334 | "color-name": "~1.1.4"
335 | },
336 | "engines": {
337 | "node": ">=7.0.0"
338 | }
339 | },
340 | "node_modules/color-name": {
341 | "version": "1.1.4",
342 | "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
343 | "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
344 | },
345 | "node_modules/concat-map": {
346 | "version": "0.0.1",
347 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
348 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
349 | },
350 | "node_modules/cross-spawn": {
351 | "version": "7.0.6",
352 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
353 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
354 | "dependencies": {
355 | "path-key": "^3.1.0",
356 | "shebang-command": "^2.0.0",
357 | "which": "^2.0.1"
358 | },
359 | "engines": {
360 | "node": ">= 8"
361 | }
362 | },
363 | "node_modules/d": {
364 | "version": "1.0.1",
365 | "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
366 | "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
367 | "dependencies": {
368 | "es5-ext": "^0.10.50",
369 | "type": "^1.0.1"
370 | }
371 | },
372 | "node_modules/debug": {
373 | "version": "2.6.9",
374 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
375 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
376 | "dependencies": {
377 | "ms": "2.0.0"
378 | }
379 | },
380 | "node_modules/defaults": {
381 | "version": "1.0.4",
382 | "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
383 | "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
384 | "dependencies": {
385 | "clone": "^1.0.2"
386 | },
387 | "funding": {
388 | "url": "https://github.com/sponsors/sindresorhus"
389 | }
390 | },
391 | "node_modules/dom-serializer": {
392 | "version": "1.4.1",
393 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz",
394 | "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==",
395 | "dependencies": {
396 | "domelementtype": "^2.0.1",
397 | "domhandler": "^4.2.0",
398 | "entities": "^2.0.0"
399 | },
400 | "funding": {
401 | "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
402 | }
403 | },
404 | "node_modules/domelementtype": {
405 | "version": "2.3.0",
406 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
407 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
408 | "funding": [
409 | {
410 | "type": "github",
411 | "url": "https://github.com/sponsors/fb55"
412 | }
413 | ]
414 | },
415 | "node_modules/domhandler": {
416 | "version": "4.3.1",
417 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz",
418 | "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==",
419 | "dependencies": {
420 | "domelementtype": "^2.2.0"
421 | },
422 | "engines": {
423 | "node": ">= 4"
424 | },
425 | "funding": {
426 | "url": "https://github.com/fb55/domhandler?sponsor=1"
427 | }
428 | },
429 | "node_modules/domutils": {
430 | "version": "2.8.0",
431 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz",
432 | "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==",
433 | "dependencies": {
434 | "dom-serializer": "^1.0.1",
435 | "domelementtype": "^2.2.0",
436 | "domhandler": "^4.2.0"
437 | },
438 | "funding": {
439 | "url": "https://github.com/fb55/domutils?sponsor=1"
440 | }
441 | },
442 | "node_modules/eastasianwidth": {
443 | "version": "0.2.0",
444 | "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
445 | "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
446 | },
447 | "node_modules/ejs": {
448 | "version": "3.1.10",
449 | "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
450 | "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
451 | "dependencies": {
452 | "jake": "^10.8.5"
453 | },
454 | "bin": {
455 | "ejs": "bin/cli.js"
456 | },
457 | "engines": {
458 | "node": ">=0.10.0"
459 | }
460 | },
461 | "node_modules/emoji-regex": {
462 | "version": "8.0.0",
463 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
464 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
465 | },
466 | "node_modules/entities": {
467 | "version": "2.2.0",
468 | "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz",
469 | "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==",
470 | "funding": {
471 | "url": "https://github.com/fb55/entities?sponsor=1"
472 | }
473 | },
474 | "node_modules/es5-ext": {
475 | "version": "0.10.62",
476 | "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz",
477 | "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==",
478 | "hasInstallScript": true,
479 | "dependencies": {
480 | "es6-iterator": "^2.0.3",
481 | "es6-symbol": "^3.1.3",
482 | "next-tick": "^1.1.0"
483 | },
484 | "engines": {
485 | "node": ">=0.10"
486 | }
487 | },
488 | "node_modules/es6-iterator": {
489 | "version": "2.0.3",
490 | "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
491 | "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
492 | "dependencies": {
493 | "d": "1",
494 | "es5-ext": "^0.10.35",
495 | "es6-symbol": "^3.1.1"
496 | }
497 | },
498 | "node_modules/es6-symbol": {
499 | "version": "3.1.3",
500 | "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
501 | "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
502 | "dependencies": {
503 | "d": "^1.0.1",
504 | "ext": "^1.1.2"
505 | }
506 | },
507 | "node_modules/escape-string-regexp": {
508 | "version": "1.0.5",
509 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
510 | "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
511 | "engines": {
512 | "node": ">=0.8.0"
513 | }
514 | },
515 | "node_modules/ext": {
516 | "version": "1.7.0",
517 | "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
518 | "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
519 | "dependencies": {
520 | "type": "^2.7.2"
521 | }
522 | },
523 | "node_modules/ext/node_modules/type": {
524 | "version": "2.7.2",
525 | "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz",
526 | "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw=="
527 | },
528 | "node_modules/external-editor": {
529 | "version": "3.1.0",
530 | "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
531 | "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
532 | "dependencies": {
533 | "chardet": "^0.7.0",
534 | "iconv-lite": "^0.4.24",
535 | "tmp": "^0.0.33"
536 | },
537 | "engines": {
538 | "node": ">=4"
539 | }
540 | },
541 | "node_modules/figures": {
542 | "version": "3.2.0",
543 | "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
544 | "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
545 | "dependencies": {
546 | "escape-string-regexp": "^1.0.5"
547 | },
548 | "engines": {
549 | "node": ">=8"
550 | },
551 | "funding": {
552 | "url": "https://github.com/sponsors/sindresorhus"
553 | }
554 | },
555 | "node_modules/filelist": {
556 | "version": "1.0.4",
557 | "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
558 | "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==",
559 | "dependencies": {
560 | "minimatch": "^5.0.1"
561 | }
562 | },
563 | "node_modules/filelist/node_modules/brace-expansion": {
564 | "version": "2.0.1",
565 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
566 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
567 | "dependencies": {
568 | "balanced-match": "^1.0.0"
569 | }
570 | },
571 | "node_modules/filelist/node_modules/minimatch": {
572 | "version": "5.1.6",
573 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
574 | "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
575 | "dependencies": {
576 | "brace-expansion": "^2.0.1"
577 | },
578 | "engines": {
579 | "node": ">=10"
580 | }
581 | },
582 | "node_modules/foreground-child": {
583 | "version": "3.3.0",
584 | "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
585 | "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
586 | "dependencies": {
587 | "cross-spawn": "^7.0.0",
588 | "signal-exit": "^4.0.1"
589 | },
590 | "engines": {
591 | "node": ">=14"
592 | },
593 | "funding": {
594 | "url": "https://github.com/sponsors/isaacs"
595 | }
596 | },
597 | "node_modules/foreground-child/node_modules/signal-exit": {
598 | "version": "4.1.0",
599 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
600 | "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
601 | "engines": {
602 | "node": ">=14"
603 | },
604 | "funding": {
605 | "url": "https://github.com/sponsors/isaacs"
606 | }
607 | },
608 | "node_modules/glob": {
609 | "version": "11.0.0",
610 | "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz",
611 | "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==",
612 | "dependencies": {
613 | "foreground-child": "^3.1.0",
614 | "jackspeak": "^4.0.1",
615 | "minimatch": "^10.0.0",
616 | "minipass": "^7.1.2",
617 | "package-json-from-dist": "^1.0.0",
618 | "path-scurry": "^2.0.0"
619 | },
620 | "bin": {
621 | "glob": "dist/esm/bin.mjs"
622 | },
623 | "engines": {
624 | "node": "20 || >=22"
625 | },
626 | "funding": {
627 | "url": "https://github.com/sponsors/isaacs"
628 | }
629 | },
630 | "node_modules/glob/node_modules/brace-expansion": {
631 | "version": "2.0.1",
632 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
633 | "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
634 | "dependencies": {
635 | "balanced-match": "^1.0.0"
636 | }
637 | },
638 | "node_modules/glob/node_modules/minimatch": {
639 | "version": "10.0.1",
640 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz",
641 | "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==",
642 | "dependencies": {
643 | "brace-expansion": "^2.0.1"
644 | },
645 | "engines": {
646 | "node": "20 || >=22"
647 | },
648 | "funding": {
649 | "url": "https://github.com/sponsors/isaacs"
650 | }
651 | },
652 | "node_modules/graceful-fs": {
653 | "version": "4.2.10",
654 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz",
655 | "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA=="
656 | },
657 | "node_modules/has-flag": {
658 | "version": "4.0.0",
659 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
660 | "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
661 | "engines": {
662 | "node": ">=8"
663 | }
664 | },
665 | "node_modules/htmlparser2": {
666 | "version": "6.1.0",
667 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz",
668 | "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==",
669 | "funding": [
670 | "https://github.com/fb55/htmlparser2?sponsor=1",
671 | {
672 | "type": "github",
673 | "url": "https://github.com/sponsors/fb55"
674 | }
675 | ],
676 | "dependencies": {
677 | "domelementtype": "^2.0.1",
678 | "domhandler": "^4.0.0",
679 | "domutils": "^2.5.2",
680 | "entities": "^2.0.0"
681 | }
682 | },
683 | "node_modules/iconv-lite": {
684 | "version": "0.4.24",
685 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
686 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
687 | "dependencies": {
688 | "safer-buffer": ">= 2.1.2 < 3"
689 | },
690 | "engines": {
691 | "node": ">=0.10.0"
692 | }
693 | },
694 | "node_modules/ieee754": {
695 | "version": "1.2.1",
696 | "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
697 | "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
698 | "funding": [
699 | {
700 | "type": "github",
701 | "url": "https://github.com/sponsors/feross"
702 | },
703 | {
704 | "type": "patreon",
705 | "url": "https://www.patreon.com/feross"
706 | },
707 | {
708 | "type": "consulting",
709 | "url": "https://feross.org/support"
710 | }
711 | ]
712 | },
713 | "node_modules/imurmurhash": {
714 | "version": "0.1.4",
715 | "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
716 | "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
717 | "engines": {
718 | "node": ">=0.8.19"
719 | }
720 | },
721 | "node_modules/inherits": {
722 | "version": "2.0.4",
723 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
724 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
725 | },
726 | "node_modules/inquirer": {
727 | "version": "8.2.6",
728 | "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz",
729 | "integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==",
730 | "dependencies": {
731 | "ansi-escapes": "^4.2.1",
732 | "chalk": "^4.1.1",
733 | "cli-cursor": "^3.1.0",
734 | "cli-width": "^3.0.0",
735 | "external-editor": "^3.0.3",
736 | "figures": "^3.0.0",
737 | "lodash": "^4.17.21",
738 | "mute-stream": "0.0.8",
739 | "ora": "^5.4.1",
740 | "run-async": "^2.4.0",
741 | "rxjs": "^7.5.5",
742 | "string-width": "^4.1.0",
743 | "strip-ansi": "^6.0.0",
744 | "through": "^2.3.6",
745 | "wrap-ansi": "^6.0.1"
746 | },
747 | "engines": {
748 | "node": ">=12.0.0"
749 | }
750 | },
751 | "node_modules/ip": {
752 | "version": "2.0.0",
753 | "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz",
754 | "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ=="
755 | },
756 | "node_modules/is-fullwidth-code-point": {
757 | "version": "3.0.0",
758 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
759 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
760 | "engines": {
761 | "node": ">=8"
762 | }
763 | },
764 | "node_modules/is-interactive": {
765 | "version": "1.0.0",
766 | "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
767 | "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
768 | "engines": {
769 | "node": ">=8"
770 | }
771 | },
772 | "node_modules/is-typedarray": {
773 | "version": "1.0.0",
774 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
775 | "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
776 | },
777 | "node_modules/is-unicode-supported": {
778 | "version": "0.1.0",
779 | "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
780 | "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
781 | "engines": {
782 | "node": ">=10"
783 | },
784 | "funding": {
785 | "url": "https://github.com/sponsors/sindresorhus"
786 | }
787 | },
788 | "node_modules/isexe": {
789 | "version": "2.0.0",
790 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
791 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
792 | },
793 | "node_modules/jackspeak": {
794 | "version": "4.0.2",
795 | "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz",
796 | "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==",
797 | "dependencies": {
798 | "@isaacs/cliui": "^8.0.2"
799 | },
800 | "engines": {
801 | "node": "20 || >=22"
802 | },
803 | "funding": {
804 | "url": "https://github.com/sponsors/isaacs"
805 | }
806 | },
807 | "node_modules/jake": {
808 | "version": "10.8.7",
809 | "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz",
810 | "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==",
811 | "dependencies": {
812 | "async": "^3.2.3",
813 | "chalk": "^4.0.2",
814 | "filelist": "^1.0.4",
815 | "minimatch": "^3.1.2"
816 | },
817 | "bin": {
818 | "jake": "bin/cli.js"
819 | },
820 | "engines": {
821 | "node": ">=10"
822 | }
823 | },
824 | "node_modules/lodash": {
825 | "version": "4.17.21",
826 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
827 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
828 | },
829 | "node_modules/log-symbols": {
830 | "version": "4.1.0",
831 | "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
832 | "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
833 | "dependencies": {
834 | "chalk": "^4.1.0",
835 | "is-unicode-supported": "^0.1.0"
836 | },
837 | "engines": {
838 | "node": ">=10"
839 | },
840 | "funding": {
841 | "url": "https://github.com/sponsors/sindresorhus"
842 | }
843 | },
844 | "node_modules/lru-cache": {
845 | "version": "11.0.2",
846 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.2.tgz",
847 | "integrity": "sha512-123qHRfJBmo2jXDbo/a5YOQrJoHF/GNQTLzQ5+IdK5pWpceK17yRc6ozlWd25FxvGKQbIUs91fDFkXmDHTKcyA==",
848 | "engines": {
849 | "node": "20 || >=22"
850 | }
851 | },
852 | "node_modules/mime": {
853 | "version": "3.0.0",
854 | "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
855 | "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
856 | "bin": {
857 | "mime": "cli.js"
858 | },
859 | "engines": {
860 | "node": ">=10.0.0"
861 | }
862 | },
863 | "node_modules/mime-db": {
864 | "version": "1.52.0",
865 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
866 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
867 | "engines": {
868 | "node": ">= 0.6"
869 | }
870 | },
871 | "node_modules/mimic-fn": {
872 | "version": "2.1.0",
873 | "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
874 | "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
875 | "engines": {
876 | "node": ">=6"
877 | }
878 | },
879 | "node_modules/minimatch": {
880 | "version": "3.1.2",
881 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
882 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
883 | "dependencies": {
884 | "brace-expansion": "^1.1.7"
885 | },
886 | "engines": {
887 | "node": "*"
888 | }
889 | },
890 | "node_modules/minipass": {
891 | "version": "7.1.2",
892 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
893 | "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
894 | "engines": {
895 | "node": ">=16 || 14 >=14.17"
896 | }
897 | },
898 | "node_modules/ms": {
899 | "version": "2.0.0",
900 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
901 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
902 | },
903 | "node_modules/mute-stream": {
904 | "version": "0.0.8",
905 | "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
906 | "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
907 | },
908 | "node_modules/next-tick": {
909 | "version": "1.1.0",
910 | "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
911 | "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ=="
912 | },
913 | "node_modules/node-gyp-build": {
914 | "version": "4.5.0",
915 | "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz",
916 | "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==",
917 | "bin": {
918 | "node-gyp-build": "bin.js",
919 | "node-gyp-build-optional": "optional.js",
920 | "node-gyp-build-test": "build-test.js"
921 | }
922 | },
923 | "node_modules/node-localstorage": {
924 | "version": "2.2.1",
925 | "resolved": "https://registry.npmjs.org/node-localstorage/-/node-localstorage-2.2.1.tgz",
926 | "integrity": "sha512-vv8fJuOUCCvSPjDjBLlMqYMHob4aGjkmrkaE42/mZr0VT+ZAU10jRF8oTnX9+pgU9/vYJ8P7YT3Vd6ajkmzSCw==",
927 | "dependencies": {
928 | "write-file-atomic": "^1.1.4"
929 | },
930 | "engines": {
931 | "node": ">=0.12"
932 | }
933 | },
934 | "node_modules/onetime": {
935 | "version": "5.1.2",
936 | "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
937 | "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
938 | "dependencies": {
939 | "mimic-fn": "^2.1.0"
940 | },
941 | "engines": {
942 | "node": ">=6"
943 | },
944 | "funding": {
945 | "url": "https://github.com/sponsors/sindresorhus"
946 | }
947 | },
948 | "node_modules/ora": {
949 | "version": "5.4.1",
950 | "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
951 | "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
952 | "dependencies": {
953 | "bl": "^4.1.0",
954 | "chalk": "^4.1.0",
955 | "cli-cursor": "^3.1.0",
956 | "cli-spinners": "^2.5.0",
957 | "is-interactive": "^1.0.0",
958 | "is-unicode-supported": "^0.1.0",
959 | "log-symbols": "^4.1.0",
960 | "strip-ansi": "^6.0.0",
961 | "wcwidth": "^1.0.1"
962 | },
963 | "engines": {
964 | "node": ">=10"
965 | },
966 | "funding": {
967 | "url": "https://github.com/sponsors/sindresorhus"
968 | }
969 | },
970 | "node_modules/os-tmpdir": {
971 | "version": "1.0.2",
972 | "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
973 | "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
974 | "engines": {
975 | "node": ">=0.10.0"
976 | }
977 | },
978 | "node_modules/package-json-from-dist": {
979 | "version": "1.0.1",
980 | "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
981 | "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="
982 | },
983 | "node_modules/pako": {
984 | "version": "2.1.0",
985 | "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
986 | "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
987 | },
988 | "node_modules/path-browserify": {
989 | "version": "1.0.1",
990 | "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
991 | "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g=="
992 | },
993 | "node_modules/path-key": {
994 | "version": "3.1.1",
995 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
996 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
997 | "engines": {
998 | "node": ">=8"
999 | }
1000 | },
1001 | "node_modules/path-scurry": {
1002 | "version": "2.0.0",
1003 | "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz",
1004 | "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==",
1005 | "dependencies": {
1006 | "lru-cache": "^11.0.0",
1007 | "minipass": "^7.1.2"
1008 | },
1009 | "engines": {
1010 | "node": "20 || >=22"
1011 | },
1012 | "funding": {
1013 | "url": "https://github.com/sponsors/isaacs"
1014 | }
1015 | },
1016 | "node_modules/readable-stream": {
1017 | "version": "3.6.2",
1018 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
1019 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
1020 | "dependencies": {
1021 | "inherits": "^2.0.3",
1022 | "string_decoder": "^1.1.1",
1023 | "util-deprecate": "^1.0.1"
1024 | },
1025 | "engines": {
1026 | "node": ">= 6"
1027 | }
1028 | },
1029 | "node_modules/real-cancellable-promise": {
1030 | "version": "1.1.1",
1031 | "resolved": "https://registry.npmjs.org/real-cancellable-promise/-/real-cancellable-promise-1.1.1.tgz",
1032 | "integrity": "sha512-vxanUX4Aff5sRX6Rb1CSeCDWhO20L0hKQXWTLOYbtRo9WYFMjlhEBX0E75iz3+7ucrmFdPpDolwLC7L65P7hag=="
1033 | },
1034 | "node_modules/restore-cursor": {
1035 | "version": "3.1.0",
1036 | "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
1037 | "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
1038 | "dependencies": {
1039 | "onetime": "^5.1.0",
1040 | "signal-exit": "^3.0.2"
1041 | },
1042 | "engines": {
1043 | "node": ">=8"
1044 | }
1045 | },
1046 | "node_modules/run-async": {
1047 | "version": "2.4.1",
1048 | "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
1049 | "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==",
1050 | "engines": {
1051 | "node": ">=0.12.0"
1052 | }
1053 | },
1054 | "node_modules/rxjs": {
1055 | "version": "7.8.1",
1056 | "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
1057 | "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
1058 | "dependencies": {
1059 | "tslib": "^2.1.0"
1060 | }
1061 | },
1062 | "node_modules/safe-buffer": {
1063 | "version": "5.2.1",
1064 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1065 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1066 | "funding": [
1067 | {
1068 | "type": "github",
1069 | "url": "https://github.com/sponsors/feross"
1070 | },
1071 | {
1072 | "type": "patreon",
1073 | "url": "https://www.patreon.com/feross"
1074 | },
1075 | {
1076 | "type": "consulting",
1077 | "url": "https://feross.org/support"
1078 | }
1079 | ]
1080 | },
1081 | "node_modules/safer-buffer": {
1082 | "version": "2.1.2",
1083 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1084 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1085 | },
1086 | "node_modules/shebang-command": {
1087 | "version": "2.0.0",
1088 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
1089 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
1090 | "dependencies": {
1091 | "shebang-regex": "^3.0.0"
1092 | },
1093 | "engines": {
1094 | "node": ">=8"
1095 | }
1096 | },
1097 | "node_modules/shebang-regex": {
1098 | "version": "3.0.0",
1099 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
1100 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
1101 | "engines": {
1102 | "node": ">=8"
1103 | }
1104 | },
1105 | "node_modules/signal-exit": {
1106 | "version": "3.0.7",
1107 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
1108 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
1109 | },
1110 | "node_modules/slide": {
1111 | "version": "1.1.6",
1112 | "resolved": "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz",
1113 | "integrity": "sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==",
1114 | "engines": {
1115 | "node": "*"
1116 | }
1117 | },
1118 | "node_modules/smart-buffer": {
1119 | "version": "4.2.0",
1120 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
1121 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
1122 | "engines": {
1123 | "node": ">= 6.0.0",
1124 | "npm": ">= 3.0.0"
1125 | }
1126 | },
1127 | "node_modules/socks": {
1128 | "version": "2.7.1",
1129 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
1130 | "integrity": "sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==",
1131 | "dependencies": {
1132 | "ip": "^2.0.0",
1133 | "smart-buffer": "^4.2.0"
1134 | },
1135 | "engines": {
1136 | "node": ">= 10.13.0",
1137 | "npm": ">= 3.0.0"
1138 | }
1139 | },
1140 | "node_modules/store2": {
1141 | "version": "2.14.2",
1142 | "resolved": "https://registry.npmjs.org/store2/-/store2-2.14.2.tgz",
1143 | "integrity": "sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w=="
1144 | },
1145 | "node_modules/string_decoder": {
1146 | "version": "1.3.0",
1147 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
1148 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
1149 | "dependencies": {
1150 | "safe-buffer": "~5.2.0"
1151 | }
1152 | },
1153 | "node_modules/string-width": {
1154 | "version": "4.2.3",
1155 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
1156 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
1157 | "dependencies": {
1158 | "emoji-regex": "^8.0.0",
1159 | "is-fullwidth-code-point": "^3.0.0",
1160 | "strip-ansi": "^6.0.1"
1161 | },
1162 | "engines": {
1163 | "node": ">=8"
1164 | }
1165 | },
1166 | "node_modules/string-width-cjs": {
1167 | "name": "string-width",
1168 | "version": "4.2.3",
1169 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
1170 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
1171 | "dependencies": {
1172 | "emoji-regex": "^8.0.0",
1173 | "is-fullwidth-code-point": "^3.0.0",
1174 | "strip-ansi": "^6.0.1"
1175 | },
1176 | "engines": {
1177 | "node": ">=8"
1178 | }
1179 | },
1180 | "node_modules/strip-ansi": {
1181 | "version": "6.0.1",
1182 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
1183 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
1184 | "dependencies": {
1185 | "ansi-regex": "^5.0.1"
1186 | },
1187 | "engines": {
1188 | "node": ">=8"
1189 | }
1190 | },
1191 | "node_modules/strip-ansi-cjs": {
1192 | "name": "strip-ansi",
1193 | "version": "6.0.1",
1194 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
1195 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
1196 | "dependencies": {
1197 | "ansi-regex": "^5.0.1"
1198 | },
1199 | "engines": {
1200 | "node": ">=8"
1201 | }
1202 | },
1203 | "node_modules/supports-color": {
1204 | "version": "7.2.0",
1205 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
1206 | "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
1207 | "dependencies": {
1208 | "has-flag": "^4.0.0"
1209 | },
1210 | "engines": {
1211 | "node": ">=8"
1212 | }
1213 | },
1214 | "node_modules/telegram": {
1215 | "version": "2.21.2",
1216 | "resolved": "https://registry.npmjs.org/telegram/-/telegram-2.21.2.tgz",
1217 | "integrity": "sha512-fvLY49uXsL+TyDdGv7K1JOMMdd0AUkBx+kb5NRC3UYB30Y0h4IQPmTyAAQI6i6/6x6Of6ZK0nXezlSXC9es+EA==",
1218 | "dependencies": {
1219 | "@cryptography/aes": "^0.1.1",
1220 | "async-mutex": "^0.3.0",
1221 | "big-integer": "^1.6.48",
1222 | "buffer": "^6.0.3",
1223 | "htmlparser2": "^6.1.0",
1224 | "mime": "^3.0.0",
1225 | "node-localstorage": "^2.2.1",
1226 | "pako": "^2.0.3",
1227 | "path-browserify": "^1.0.1",
1228 | "real-cancellable-promise": "^1.1.1",
1229 | "socks": "^2.6.2",
1230 | "store2": "^2.13.0",
1231 | "ts-custom-error": "^3.2.0",
1232 | "websocket": "^1.0.34"
1233 | },
1234 | "optionalDependencies": {
1235 | "bufferutil": "^4.0.3",
1236 | "utf-8-validate": "^5.0.5"
1237 | }
1238 | },
1239 | "node_modules/through": {
1240 | "version": "2.3.8",
1241 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
1242 | "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg=="
1243 | },
1244 | "node_modules/tmp": {
1245 | "version": "0.0.33",
1246 | "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
1247 | "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
1248 | "dependencies": {
1249 | "os-tmpdir": "~1.0.2"
1250 | },
1251 | "engines": {
1252 | "node": ">=0.6.0"
1253 | }
1254 | },
1255 | "node_modules/ts-custom-error": {
1256 | "version": "3.3.1",
1257 | "resolved": "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz",
1258 | "integrity": "sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==",
1259 | "engines": {
1260 | "node": ">=14.0.0"
1261 | }
1262 | },
1263 | "node_modules/tslib": {
1264 | "version": "2.4.1",
1265 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz",
1266 | "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA=="
1267 | },
1268 | "node_modules/type": {
1269 | "version": "1.2.0",
1270 | "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz",
1271 | "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg=="
1272 | },
1273 | "node_modules/type-fest": {
1274 | "version": "0.21.3",
1275 | "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
1276 | "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
1277 | "engines": {
1278 | "node": ">=10"
1279 | },
1280 | "funding": {
1281 | "url": "https://github.com/sponsors/sindresorhus"
1282 | }
1283 | },
1284 | "node_modules/typedarray-to-buffer": {
1285 | "version": "3.1.5",
1286 | "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
1287 | "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
1288 | "dependencies": {
1289 | "is-typedarray": "^1.0.0"
1290 | }
1291 | },
1292 | "node_modules/utf-8-validate": {
1293 | "version": "5.0.10",
1294 | "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
1295 | "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
1296 | "hasInstallScript": true,
1297 | "dependencies": {
1298 | "node-gyp-build": "^4.3.0"
1299 | },
1300 | "engines": {
1301 | "node": ">=6.14.2"
1302 | }
1303 | },
1304 | "node_modules/util-deprecate": {
1305 | "version": "1.0.2",
1306 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
1307 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
1308 | },
1309 | "node_modules/wcwidth": {
1310 | "version": "1.0.1",
1311 | "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
1312 | "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
1313 | "dependencies": {
1314 | "defaults": "^1.0.3"
1315 | }
1316 | },
1317 | "node_modules/websocket": {
1318 | "version": "1.0.34",
1319 | "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz",
1320 | "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==",
1321 | "dependencies": {
1322 | "bufferutil": "^4.0.1",
1323 | "debug": "^2.2.0",
1324 | "es5-ext": "^0.10.50",
1325 | "typedarray-to-buffer": "^3.1.5",
1326 | "utf-8-validate": "^5.0.2",
1327 | "yaeti": "^0.0.6"
1328 | },
1329 | "engines": {
1330 | "node": ">=4.0.0"
1331 | }
1332 | },
1333 | "node_modules/which": {
1334 | "version": "2.0.2",
1335 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
1336 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
1337 | "dependencies": {
1338 | "isexe": "^2.0.0"
1339 | },
1340 | "bin": {
1341 | "node-which": "bin/node-which"
1342 | },
1343 | "engines": {
1344 | "node": ">= 8"
1345 | }
1346 | },
1347 | "node_modules/wrap-ansi": {
1348 | "version": "6.2.0",
1349 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
1350 | "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
1351 | "dependencies": {
1352 | "ansi-styles": "^4.0.0",
1353 | "string-width": "^4.1.0",
1354 | "strip-ansi": "^6.0.0"
1355 | },
1356 | "engines": {
1357 | "node": ">=8"
1358 | }
1359 | },
1360 | "node_modules/wrap-ansi-cjs": {
1361 | "name": "wrap-ansi",
1362 | "version": "7.0.0",
1363 | "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
1364 | "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
1365 | "dependencies": {
1366 | "ansi-styles": "^4.0.0",
1367 | "string-width": "^4.1.0",
1368 | "strip-ansi": "^6.0.0"
1369 | },
1370 | "engines": {
1371 | "node": ">=10"
1372 | },
1373 | "funding": {
1374 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
1375 | }
1376 | },
1377 | "node_modules/write-file-atomic": {
1378 | "version": "1.3.4",
1379 | "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz",
1380 | "integrity": "sha512-SdrHoC/yVBPpV0Xq/mUZQIpW2sWXAShb/V4pomcJXh92RuaO+f3UTWItiR3Px+pLnV2PvC2/bfn5cwr5X6Vfxw==",
1381 | "dependencies": {
1382 | "graceful-fs": "^4.1.11",
1383 | "imurmurhash": "^0.1.4",
1384 | "slide": "^1.1.5"
1385 | }
1386 | },
1387 | "node_modules/yaeti": {
1388 | "version": "0.0.6",
1389 | "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
1390 | "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==",
1391 | "engines": {
1392 | "node": ">=0.10.32"
1393 | }
1394 | }
1395 | }
1396 | }
1397 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "telegram-channel-downloader",
3 | "version": "1.0.0",
4 | "description": "scrap and download all media and message from telegram channel, group or a user",
5 | "main": "index.js",
6 | "scripts": {
7 | "dev": "nodemon index.js",
8 | "start": "node index.js"
9 | },
10 | "nodemonConfig": {
11 | "ignore": [
12 | "config.json",
13 | "./export/*"
14 | ]
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://github.com/abhishekjnvk/telegram-channel-downloader.git"
19 | },
20 | "keywords": [
21 | "telegram",
22 | "node.js",
23 | "scrapper"
24 | ],
25 | "author": "abhishekjnvk",
26 | "license": "ISC",
27 | "bugs": {
28 | "url": "https://github.com/abhishekjnvk/telegram-channel-downloader/issues"
29 | },
30 | "homepage": "https://github.com/abhishekjnvk/telegram-channel-downloader#readme",
31 | "dependencies": {
32 | "ejs": "^3.1.10",
33 | "glob": "^11.0.0",
34 | "inquirer": "^8.2.6",
35 | "mime-db": "^1.52.0",
36 | "telegram": "^2.21.2"
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/scripts/download-channel.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | const fs = require("fs");
3 | const path = require("path");
4 | const { initAuth } = require("../modules/auth");
5 | const {
6 | getMessages,
7 | getMessageDetail,
8 | downloadMessageMedia,
9 | } = require("../modules/messages");
10 | const {
11 | getMediaType,
12 | getMediaPath,
13 | checkFileExist,
14 | appendToJSONArrayFile,
15 | wait,
16 | } = require("../utils/helper");
17 | const {
18 | updateLastSelection,
19 | getLastSelection,
20 | } = require("../utils/file-helper");
21 | const logger = require("../utils/logger");
22 | const { getDialogName, getAllDialogs } = require("../modules/dialoges");
23 | const {
24 | downloadOptionInput,
25 | selectInput,
26 | } = require("../utils/input-helper");
27 |
28 | const MAX_PARALLEL_DOWNLOAD = 5;
29 | const MESSAGE_LIMIT = 10;
30 |
31 | /**
32 | * Handles downloading media from a Telegram channel
33 | */
34 | class DownloadChannel {
35 | constructor() {
36 | this.outputFolder = null;
37 | this.downloadableFiles = null;
38 |
39 | const exportPath = path.resolve(process.cwd(), "./export");
40 | if (!fs.existsSync(exportPath)) {
41 | fs.mkdirSync(exportPath);
42 | }
43 | }
44 |
45 | static description() {
46 | return "Download all media from a channel";
47 | }
48 |
49 | /**
50 | * Checks if a message contains media
51 | * @param {Object} message The Telegram message object
52 | */
53 | hasMedia(message) {
54 | return Boolean(message.media);
55 | }
56 |
57 | /**
58 | * Determines if a message's media should be downloaded
59 | * @param {Object} message The Telegram message object
60 | */
61 | canDownload(message) {
62 | if (!this.hasMedia(message)) return false;
63 | const mediaType = getMediaType(message);
64 | const mediaPath = getMediaPath(message, this.outputFolder);
65 | const fileExists = checkFileExist(message, this.outputFolder);
66 | const extension = path.extname(mediaPath).toLowerCase().replace(".", "");
67 | const allowed =
68 | this.downloadableFiles?.[mediaType] ||
69 | this.downloadableFiles?.[extension] ||
70 | this.downloadableFiles?.all;
71 |
72 | return allowed && !fileExists;
73 | }
74 |
75 | /**
76 | * Records messages to a JSON file
77 | * @param {Array} messages The message objects
78 | */
79 | recordMessages(messages) {
80 | const filePath = path.join(this.outputFolder, "all_message.json");
81 | if (!fs.existsSync(this.outputFolder)) {
82 | fs.mkdirSync(this.outputFolder, { recursive: true });
83 | }
84 |
85 | const data = messages.map((msg) => ({
86 | id: msg.id,
87 | message: msg.message,
88 | date: msg.date,
89 | out: msg.out,
90 | hasMedia: !!msg.media,
91 | sender: msg.fromId?.userId || msg.peerId?.userId,
92 | mediaType: this.hasMedia(msg) ? getMediaType(msg) : undefined,
93 | mediaPath: this.hasMedia(msg)
94 | ? getMediaPath(msg, this.outputFolder)
95 | : undefined,
96 | mediaName: this.hasMedia(msg)
97 | ? path.basename(getMediaPath(msg, this.outputFolder))
98 | : undefined,
99 | }));
100 | appendToJSONArrayFile(filePath, data);
101 | }
102 |
103 | /**
104 | * Recursively fetches and downloads all available media from the channel
105 | * @param {Object} client The Telegram client instance
106 | * @param {Number} channelId The channel ID
107 | * @param {Number} offsetMsgId The message offset
108 | */
109 | async downloadChannel(client, channelId, offsetMsgId = 0) {
110 | try {
111 | this.outputFolder = path.join(
112 | process.cwd(),
113 | "export",
114 | channelId.toString()
115 | );
116 | const messages = await getMessages(
117 | client,
118 | channelId,
119 | MESSAGE_LIMIT,
120 | offsetMsgId
121 | );
122 | if (!messages.length) {
123 | logger.info("No more messages to download");
124 | return;
125 | }
126 | const ids = messages.map((m) => m.id);
127 | const details = await getMessageDetail(client, channelId, ids);
128 | const downloadQueue = [];
129 |
130 | for (const msg of details) {
131 | if (this.canDownload(msg)) {
132 | logger.info(`Downloading ${msg.id}`);
133 | downloadQueue.push(
134 | downloadMessageMedia(
135 | client,
136 | msg,
137 | getMediaPath(msg, this.outputFolder)
138 | )
139 | );
140 | } else {
141 | // logger.info(`No media to download for ${msg.id}`);
142 | }
143 | if (downloadQueue.length >= MAX_PARALLEL_DOWNLOAD) {
144 | logger.info(`Processing ${MAX_PARALLEL_DOWNLOAD} downloads`);
145 | await Promise.all(downloadQueue);
146 | downloadQueue.length = 0;
147 | await wait(3);
148 | }
149 | }
150 |
151 | await Promise.all(downloadQueue);
152 | this.recordMessages(details);
153 | updateLastSelection({
154 | messageOffsetId: messages[messages.length - 1].id,
155 | });
156 |
157 | await wait(1);
158 | await this.downloadChannel(
159 | client,
160 | channelId,
161 | messages[messages.length - 1].id
162 | );
163 | } catch (err) {
164 | logger.error("An error occurred:");
165 | console.error(err);
166 | }
167 | }
168 |
169 | async configureDownload(options, client) {
170 | let channelId = options.channelId;
171 | let downloadableFiles = options.downloadableFiles;
172 | if (!channelId) {
173 | logger.info("Please select a channel to download media from");
174 | const allChannels = await getAllDialogs(client);
175 | const options = allChannels.map((d) => ({
176 | name: d.name,
177 | value: d.id,
178 | }));
179 |
180 | const selectedChannel = await selectInput(
181 | "Please select a channel",
182 | options
183 | );
184 | channelId = selectedChannel;
185 | }
186 | if (!downloadableFiles) downloadableFiles = await downloadOptionInput();
187 |
188 | this.downloadableFiles = downloadableFiles;
189 |
190 | const lastSelection = getLastSelection();
191 | let messageOffsetId = lastSelection.messageOffsetId || 0;
192 |
193 | if (Number(lastSelection.channelId) !== Number(channelId)) {
194 | messageOffsetId = 0;
195 | }
196 | updateLastSelection({ messageOffsetId, channelId });
197 | return { channelId, messageOffsetId };
198 | }
199 |
200 | /**
201 | * Main entry point: initializes auth, sets up output folder, and starts download
202 | */
203 | async handle(options = {}) {
204 | let client;
205 | await wait(1);
206 | try {
207 | client = await initAuth();
208 | const { channelId, messageOffsetId } = await this.configureDownload(
209 | options,
210 | client
211 | );
212 |
213 | const dialogName = await getDialogName(client, channelId);
214 | logger.info(`Downloading media from channel ${dialogName}`);
215 | await this.downloadChannel(client, channelId, messageOffsetId);
216 | } catch (err) {
217 | logger.error("An error occurred:");
218 | console.error(err);
219 | } finally {
220 | if (client) await client.disconnect();
221 | process.exit(0);
222 | }
223 | }
224 | }
225 |
226 | module.exports = DownloadChannel;
227 |
--------------------------------------------------------------------------------
/scripts/download-selected-message.js:
--------------------------------------------------------------------------------
1 | const { initAuth } = require("../modules/auth");
2 | const { getMessageDetail } = require("../modules/messages");
3 | const { logMessage, getMediaPath } = require("../utils/helper");
4 | const { textInput } = require("../utils/input-helper");
5 |
6 | class DownloadMessage {
7 | // -------------------------------
8 | // Accepts the following parameters:
9 | // - Channel ID
10 | // - Message ID(s) (separated by comma)
11 | // -------------------------------
12 | static description() {
13 | return "Download media from a messages";
14 | }
15 |
16 | async downloadMessage(client, channelId, messageIds) {
17 | const outputFolder = (outputFolder = path.join(
18 | process.cwd(),
19 | "export",
20 | channelId.toString()
21 | ));
22 |
23 | const messageArr = await getMessageDetail(client, channelId, messageIds);
24 | for (const message of messageArr) {
25 | downloadMessageMedia(
26 | client,
27 | message,
28 | getMediaPath(message, outputFolder)
29 | );
30 | }
31 | logMessage.success("Done with downloading messages");
32 | }
33 |
34 | async handle() {
35 | try {
36 | const client = await initAuth();
37 | const channelId = textInput("Please Enter Channel ID: ");
38 | const messageIdsText = textInput(
39 | "Please Enter Message Id(s) (separated by comma): "
40 | );
41 | const messageIds = messageIdsText.split(",").map(Number);
42 |
43 | await this.downloadMessage(client, channelId, messageIds);
44 | } catch (error) {
45 | logMessage.error("An error occurred:", error);
46 | } finally {
47 | if (client) {
48 | await client.disconnect();
49 | }
50 |
51 | process.exit(0);
52 | }
53 | }
54 | }
55 |
56 | module.exports = DownloadMessage;
57 |
--------------------------------------------------------------------------------
/scripts/listen-channel.js:
--------------------------------------------------------------------------------
1 | const { NewMessage } = require("telegram/events");
2 | const { getAllDialogs, getDialogName } = require("../modules/dialoges");
3 | const { downloadMessageMedia, getMessageDetail } = require("../modules/messages");
4 | const { getMediaPath, wait } = require("../utils/helper");
5 | const logger = require("../utils/logger");
6 | const { initAuth } = require("../modules/auth");
7 | const { selectInput } = require("../utils/input-helper");
8 | const path = require("path");
9 |
10 | class ListenChannel {
11 | constructor() {
12 | this.channelId = null;
13 | this.client = null;
14 | this.handleNewMessage = this.handleNewMessage.bind(this);
15 | }
16 |
17 | static description() {
18 | return "Listen to a channel and download media from incoming messages";
19 | }
20 |
21 | async handleNewMessage(event) {
22 | const messageChatId =
23 | event.message?.peerId?.chatId ||
24 | event.message?.peerId?.channelId ||
25 | event.message?.peerId?.userId;
26 | if (Number(messageChatId) !== Number(this.channelId)) {
27 | logger.info("Message from another channel");
28 | return;
29 | }
30 |
31 | const messageId = event.message?.id;
32 | const isMedia = !!event.message?.media;
33 | if (isMedia) {
34 | const outputFolder = path.join(
35 | process.cwd(),
36 | "export",
37 | this.channelId.toString()
38 | );
39 |
40 | const details = await getMessageDetail(this.client, this.channelId, [
41 | messageId,
42 | ]);
43 | for (const msg of details) {
44 | await downloadMessageMedia(
45 | this.client,
46 | msg,
47 | getMediaPath(msg, outputFolder)
48 | );
49 | logger.info(`Downloaded media from message: ${msg.id}`);
50 | }
51 | } else {
52 | logger.info("No media found in the message");
53 | }
54 | }
55 |
56 | async handle(options = {}) {
57 | let channelId = Number(options.channelId);
58 | let client;
59 | await wait(1);
60 | try {
61 | client = await initAuth();
62 |
63 | if (!channelId) {
64 | logger.info("Please select a channel to download media from");
65 | const allChannels = await getAllDialogs(client);
66 | const options = allChannels.map((d) => ({
67 | name: d.name,
68 | value: d.id,
69 | }));
70 |
71 | channelId = await selectInput("Please select a channel", options);
72 | }
73 |
74 | this.channelId = channelId;
75 | this.client = client;
76 |
77 | const dialogName = await getDialogName(client, channelId);
78 | logger.info(`Listening to: ${dialogName}`);
79 | client.addEventHandler(this.handleNewMessage, new NewMessage({}));
80 | } catch (err) {
81 | logger.error("An error occurred:");
82 | console.error(err);
83 | }
84 | }
85 | }
86 |
87 | module.exports = ListenChannel;
88 |
--------------------------------------------------------------------------------
/templates/channels.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Contact List
9 |
51 |
52 |
53 |
54 |
55 |
Contact List
56 |
57 |
Total Contacts: <%= channels.length %>
58 |
59 |
60 | <% channels.forEach(function(contact) { %>
61 |
62 |
63 |
64 | <% if(contact.type == "User"){ %> 👤 <% } %>
65 | <% if(contact.type == "Group"){ %> 👥 <% } %>
66 | <% if(contact.type == "Channel"){ %> 📢 <% } %>
67 |
68 |
69 |
<%= contact.name || "Deleted Account" %>
70 | <% if (contact.phone) { %>
71 | Mobile: +<%= contact.phone %>
72 | <% } %>
73 | <% if (contact.lastMessage) { %>
74 | <%- contact.lastMessage.replace(/\n/g, '
') %>
75 | <% } %>
76 |
77 |
78 |
79 | <%= new Date(Number(contact.lastMessageTimestamp) * 1000).toLocaleDateString('en-GB', { day: '2-digit', month: 'long', year: 'numeric' }) %>
80 |
81 |
82 | <% }); %>
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/utils/file-helper.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 | const path = require("path");
3 | const { logMessage } = require("./helper");
4 |
5 | const CONFIG_FILE = path.join(__dirname, "../config.json");
6 | const LAST_SELECTION_FILE = path.join(
7 | __dirname,
8 | "../export/last_selection.json"
9 | );
10 |
11 | /**
12 | * Reads the content of a file synchronously.
13 | *
14 | * @param {string} filePath - The path to the file to be read.
15 | * @returns {string} The content of the file.
16 | * @throws Will throw an error if the file cannot be read.
17 | */
18 | const readFileSync = (filePath, showError = true) => {
19 | try {
20 | return fs.readFileSync(filePath, "utf8");
21 | } catch (err) {
22 | showError ? logMessage.error(`Error reading file: ${filePath}`, err) : null;
23 | throw err;
24 | }
25 | };
26 |
27 | /**
28 | * Writes data to a file synchronously.
29 | *
30 | * @param {string} filePath - The path to the file where data should be written.
31 | * @param {Object} data - The data to be written to the file.
32 | * @throws Will throw an error if writing to the file fails.
33 | */
34 | const writeFileSync = (filePath, data) => {
35 | try {
36 | fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf8");
37 | logMessage.success(`File written successfully: ${filePath}`);
38 | } catch (err) {
39 | logMessage.error(`Error writing file: ${filePath}`, err);
40 | throw err;
41 | }
42 | };
43 |
44 | /**
45 | * Updates the credentials in the configuration file with the provided object.
46 | *
47 | * @param {Object} obj - The object containing the new credentials to be updated.
48 | * @throws Will log an error message if reading or writing to the configuration file fails.
49 | */
50 | const updateCredentials = (obj) => {
51 | try {
52 | const data = readFileSync(CONFIG_FILE);
53 | const credentials = { ...JSON.parse(data), ...obj };
54 | writeFileSync(CONFIG_FILE, credentials);
55 | } catch (err) {
56 | logMessage.error("Failed to update credentials", err);
57 | }
58 | };
59 |
60 | /**
61 | * Reads and parses the credentials from the configuration file.
62 | *
63 | * @returns {Object} The parsed credentials from the config file.
64 | * @throws Will log an error message and exit the process if the config file cannot be read or parsed.
65 | */
66 | const getCredentials = () => {
67 | try {
68 | const data = readFileSync(CONFIG_FILE);
69 | return JSON.parse(data);
70 | } catch (err) {
71 | logMessage.error(
72 | "Please add your credentials in config.json file, follow https://github.com/abhishekjnvk/telegram-channel-downloader#setup for more info"
73 | );
74 | process.exit(1);
75 | }
76 | };
77 |
78 | /**
79 | * Retrieves the last selection from a file.
80 | *
81 | * @returns {Object} The last selection data parsed from the file. Returns an empty object if an error occurs.
82 | */
83 | const getLastSelection = () => {
84 | try {
85 | const data = readFileSync(LAST_SELECTION_FILE, false);
86 | return JSON.parse(data);
87 | } catch (_) {
88 | return {};
89 | }
90 | };
91 |
92 | /**
93 | * Updates the last selection with the provided object.
94 | *
95 | * This function merges the provided object with the last selection
96 | * and writes the result to the LAST_SELECTION_FILE. If an error occurs
97 | * during the process, it logs an error message.
98 | *
99 | * @param {Object} object - The object to merge with the last selection.
100 | */
101 | const updateLastSelection = (object) => {
102 | try {
103 | const last = { ...getLastSelection(), ...object };
104 | writeFileSync(LAST_SELECTION_FILE, last);
105 | } catch (err) {
106 | logMessage.error("Failed to update last selection", err);
107 | }
108 | };
109 |
110 | module.exports = {
111 | updateCredentials,
112 | getCredentials,
113 | getLastSelection,
114 | updateLastSelection,
115 | };
116 |
--------------------------------------------------------------------------------
/utils/helper.js:
--------------------------------------------------------------------------------
1 | const mimeDB = require("mime-db");
2 | const fs = require("fs");
3 | const path = require("path");
4 |
5 | // Define media types
6 | const MEDIA_TYPES = {
7 | IMAGE: "image",
8 | VIDEO: "video",
9 | AUDIO: "audio",
10 | WEBPAGE: "webpage",
11 | POLL: "poll",
12 | GEO: "geo",
13 | VENUE: "venue",
14 | CONTACT: "contact",
15 | STICKER: "sticker",
16 | DOCUMENT: "document",
17 | OTHERS: "others",
18 | };
19 |
20 | // Define console colors for logging
21 | const consoleColors = {
22 | red: "\x1b[31m",
23 | green: "\x1b[32m",
24 | yellow: "\x1b[33m",
25 | blue: "\x1b[34m",
26 | magenta: "\x1b[35m",
27 | cyan: "\x1b[36m",
28 | white: "\x1b[37m",
29 | reset: "\x1b[0m",
30 | };
31 |
32 | // Get the media type of a message
33 | const getMediaType = (message) => {
34 | if (!message.media) return MEDIA_TYPES.OTHERS;
35 |
36 | const { media } = message;
37 | if (media.photo) return MEDIA_TYPES.IMAGE;
38 | if (media.video) return MEDIA_TYPES.VIDEO;
39 | if (media.audio) return MEDIA_TYPES.AUDIO;
40 | if (media.webpage) return MEDIA_TYPES.WEBPAGE;
41 | if (media.poll) return MEDIA_TYPES.POLL;
42 | if (media.geo) return MEDIA_TYPES.GEO;
43 | if (media.contact) return MEDIA_TYPES.CONTACT;
44 | if (media.venue) return MEDIA_TYPES.VENUE;
45 | if (media.sticker) return MEDIA_TYPES.STICKER;
46 | if (media.document) {
47 | const { mimeType } = media.document;
48 | if (mimeType) {
49 | if (mimeType.includes(MEDIA_TYPES.IMAGE)) return MEDIA_TYPES.IMAGE;
50 | if (mimeType.includes(MEDIA_TYPES.VIDEO)) return MEDIA_TYPES.VIDEO;
51 | if (mimeType.includes(MEDIA_TYPES.AUDIO)) return MEDIA_TYPES.AUDIO;
52 | if (mimeType.includes(MEDIA_TYPES.STICKER)) return MEDIA_TYPES.STICKER;
53 | }
54 | return MEDIA_TYPES.DOCUMENT;
55 | }
56 |
57 | return MEDIA_TYPES.OTHERS;
58 | };
59 |
60 | // Check if a file already exists
61 | const checkFileExist = (message, outputFolder) => {
62 | if (!message || !message.media) return false;
63 |
64 | let fileName = `${message.id}_file`;
65 | const { media } = message;
66 |
67 | if (media.document) {
68 | const docAttributes = media.document.attributes;
69 | if (docAttributes) {
70 | const fileNameObj = docAttributes.find(
71 | (e) => e.className === "DocumentAttributeFilename"
72 | );
73 | if (fileNameObj) {
74 | fileName = fileNameObj.fileName;
75 | } else {
76 | const ext = mimeDB[media.document.mimeType]?.extensions[0];
77 | if (ext) fileName += `.${ext}`;
78 | }
79 | }
80 | }
81 |
82 | if (media.video) fileName += ".mp4";
83 | if (media.audio) fileName += ".mp3";
84 | if (media.photo) fileName += ".jpg";
85 |
86 | const folderType = filterString(getMediaType(message));
87 | const filePath = path.join(outputFolder, folderType, fileName);
88 |
89 | return fs.existsSync(filePath);
90 | };
91 |
92 | // Get the path to save the media file
93 | const getMediaPath = (message, outputFolder) => {
94 | if (!message || !message.media) return "unknown";
95 |
96 | let fileName = `${message.id}_file`;
97 | const { media } = message;
98 |
99 | if (media.document) {
100 | const docAttributes = media.document.attributes;
101 | if (docAttributes) {
102 | const fileNameObj = docAttributes.find(
103 | (e) => e.className === "DocumentAttributeFilename"
104 | );
105 | if (fileNameObj) {
106 | fileName = fileNameObj.fileName;
107 | } else {
108 | const ext = mimeDB[media.document.mimeType]?.extensions[0];
109 | if (ext) fileName += `.${ext}`;
110 | }
111 | }
112 | }
113 |
114 | if (media.video) fileName += ".mp4";
115 | if (media.audio) fileName += ".mp3";
116 | if (media.photo) fileName += ".jpg";
117 |
118 | const folderType = filterString(getMediaType(message));
119 | const filePath = path.join(outputFolder, folderType, fileName);
120 |
121 | if (fs.existsSync(filePath)) {
122 | logMessage.info(`File already exists: ${filePath}, Changing name`);
123 | const ext = path.extname(filePath);
124 | const baseName = path.basename(filePath, ext);
125 | fileName = `${baseName}_${message.id}${ext}`;
126 | }
127 |
128 | const finalPath = path.join(outputFolder, folderType, fileName);
129 | if (!fs.existsSync(path.dirname(finalPath))) {
130 | fs.mkdirSync(path.dirname(finalPath), { recursive: true });
131 | }
132 |
133 | return finalPath;
134 | };
135 |
136 | // Get the type of dialog
137 | const getDialogType = (dialog) => {
138 | if (dialog.isChannel) return "Channel";
139 | if (dialog.isGroup) return "Group";
140 | if (dialog.isUser) return "User";
141 | return "Unknown";
142 | };
143 |
144 | // Logging utility
145 | const logMessage = {
146 | info: (message, icon=true) => {
147 | console.log(`📢: ${consoleColors.magenta}${message}${consoleColors.reset}`);
148 | },
149 | error: (message) => {
150 | console.log(`❌ ${consoleColors.red}${message}${consoleColors.reset}`);
151 | },
152 | success: (message) => {
153 | console.log(`✅ ${consoleColors.cyan}${message}${consoleColors.reset}`);
154 | },
155 | debug: (message) => {
156 | console.log(`⚠️ ${message}`);
157 | },
158 | };
159 |
160 | // Wait for a specified number of seconds
161 | const wait = (seconds) => {
162 | return new Promise((resolve) => {
163 | setTimeout(resolve, seconds * 1000);
164 | });
165 | };
166 |
167 | // Filter a string to remove non-alphanumeric characters
168 | const filterString = (string) => {
169 | return string.replace(/[^a-zA-Z0-9]/g, "");
170 | };
171 |
172 | // Stringify an object with circular references
173 | const circularStringify = (obj, indent = 2) => {
174 | const cache = new Set();
175 | const retVal = JSON.stringify(
176 | obj,
177 | (key, value) =>
178 | typeof value === "object" && value !== null
179 | ? cache.has(value)
180 | ? undefined
181 | : cache.add(value) && value
182 | : value,
183 | indent
184 | );
185 | cache.clear();
186 | return retVal;
187 | };
188 |
189 | // Append data to a JSON array file
190 | const appendToJSONArrayFile = (filePath, dataToAppend) => {
191 | try {
192 | if (!fs.existsSync(filePath)) {
193 | fs.writeFileSync(filePath, circularStringify(dataToAppend, null, 2));
194 | } else {
195 | const data = fs.readFileSync(filePath);
196 | const json = JSON.parse(data);
197 | json.push(dataToAppend);
198 | fs.writeFileSync(filePath, circularStringify(json, null, 2));
199 | }
200 | } catch (e) {
201 | logMessage.error(`Error appending to JSON Array file ${filePath}`);
202 | console.error(e);
203 | }
204 | };
205 |
206 | module.exports = {
207 | getMediaType,
208 | checkFileExist,
209 | getMediaPath,
210 | getDialogType,
211 | logMessage,
212 | wait,
213 | filterString,
214 | appendToJSONArrayFile,
215 | circularStringify,
216 | MEDIA_TYPES,
217 | };
218 |
--------------------------------------------------------------------------------
/utils/input-helper.js:
--------------------------------------------------------------------------------
1 | const inquirer = require("inquirer");
2 | const { MEDIA_TYPES } = require("./helper");
3 |
4 | /**
5 | * Prompts the user to enter their mobile number with country code.
6 | * @returns {Promise} The entered mobile number.
7 | */
8 | const mobileNumberInput = async () => {
9 | const question = {
10 | type: "input",
11 | name: "phoneNumber",
12 | message: "Please enter your mobile number with country code (without +):",
13 | validate: (input) => {
14 | const regex = /^\d{1,3}\d{9,11}$/;
15 | return regex.test(input) ? true : "Please enter a valid mobile number with country code (without +).";
16 | },
17 | };
18 |
19 | const { phoneNumber } = await inquirer.prompt(question);
20 | return phoneNumber;
21 | };
22 |
23 | /**
24 | * Prompts the user to enter a 5-digit OTP.
25 | * @returns {Promise} The entered OTP.
26 | */
27 | const otpInput = async () => {
28 | const question = {
29 | type: "input",
30 | name: "otp",
31 | message: "Please enter the 5-digit OTP:",
32 | validate: (input) => {
33 | const regex = /^\d{5}$/;
34 | return regex.test(input) ? true : "Please enter a valid 5-digit numeric OTP.";
35 | },
36 | };
37 |
38 | const { otp } = await inquirer.prompt(question);
39 | return otp;
40 | };
41 |
42 | /**
43 | * Prompts the user to enter a text input.
44 | * @param {string} [message="Please Enter"] - The message to display.
45 | * @returns {Promise} The entered text.
46 | */
47 | const textInput = async (message = "Please Enter") => {
48 | const question = {
49 | type: "input",
50 | name: "text",
51 | message: message,
52 | };
53 |
54 | const { text } = await inquirer.prompt(question);
55 | return text;
56 | };
57 |
58 | /**
59 | * Prompts the user to enter a number within a specified range.
60 | * @param {string} [message="Please enter a number"] - The message to display.
61 | * @param {number} [min=-Infinity] - The minimum value.
62 | * @param {number} [max=Infinity] - The maximum value.
63 | * @returns {Promise} The entered number.
64 | */
65 | const numberInput = async (message = "Please enter a number", min = -Infinity, max = Infinity) => {
66 | const question = {
67 | type: "input",
68 | name: "number",
69 | message: message,
70 | validate: (input) => {
71 | const number = parseFloat(input);
72 | if (isNaN(number)) {
73 | return "Please enter a valid number.";
74 | }
75 | if (number > max || number < min) {
76 | return `Entered number - ${number} is not between ${min} and ${max}.`;
77 | }
78 | return true;
79 | },
80 | };
81 |
82 | const { number } = await inquirer.prompt(question);
83 | return parseFloat(number);
84 | };
85 |
86 | /**
87 | * Prompts the user to answer with yes or no.
88 | * @param {string} [message="Please answer with yes or no"] - The message to display.
89 | * @returns {Promise} The user's response.
90 | */
91 | const booleanInput = async (message = "Please answer with yes or no") => {
92 | const question = {
93 | type: "confirm",
94 | name: "confirm",
95 | message: message,
96 | };
97 |
98 | const { confirm } = await inquirer.prompt(question);
99 | return confirm;
100 | };
101 |
102 | /**
103 | * Prompts the user to select an option from a list.
104 | * @param {string} [message="Please select"] - The message to display.
105 | * @param {Array} [optionsArr=[]] - The list of options.
106 | * @returns {Promise} The selected option.
107 | */
108 | const selectInput = async (message = "Please select", optionsArr = []) => {
109 | const question = {
110 | type: "list",
111 | name: "input",
112 | message: message,
113 | choices: optionsArr,
114 | };
115 |
116 | const { input } = await inquirer.prompt(question);
117 | return input;
118 | };
119 |
120 | /**
121 | * Prompts the user to select multiple choices from a list.
122 | * @param {string} [message="Please select multiple choices"] - The message to display.
123 | * @param {Array} optionsArr - The list of options.
124 | * @param {Array} [defaultOptions=[]] - The default selected options.
125 | * @returns {Promise>} The selected options.
126 | */
127 | const multipleChoice = async (message = "Please select multiple choices", optionsArr, defaultOptions = []) => {
128 | const question = {
129 | type: "checkbox",
130 | name: "input",
131 | message: message,
132 | default: defaultOptions,
133 | choices: optionsArr,
134 | };
135 |
136 | const { input } = await inquirer.prompt(question);
137 | return input;
138 | };
139 |
140 | /**
141 | * Prompts the user to select file types to download and handles custom file extensions.
142 | * @returns {Promise