diff --git a/.eslintrc.json b/.eslintrc.json
index 58c59461..76548097 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -18,6 +18,9 @@
"parserOptions": {
"ecmaVersion": 11
},
+ "plugins": [
+ "jsdoc"
+ ],
"rules": {
"no-var": "error",
"no-redeclare": "off",
@@ -37,7 +40,23 @@
"semi-spacing": "error",
"semi-style": ["error", "last"],
"quotes": ["error", "double", { "avoidEscape": true }],
- "spaced-comment": "warn"
+ "spaced-comment": "warn",
+
+ // JsDoc - https://github.com/gajus/eslint-plugin-jsdoc
+ "jsdoc/check-alignment": "warn",
+ "jsdoc/check-indentation": "warn",
+ "jsdoc/check-types": "warn",
+ "jsdoc/informative-docs": "warn",
+ "jsdoc/require-asterisk-prefix": "warn",
+ "jsdoc/require-description": "warn",
+ "jsdoc/require-jsdoc": "warn",
+ "jsdoc/require-param": "warn",
+ "jsdoc/require-param-name": "warn",
+ "jsdoc/valid-types": "warn",
+ //"jsdoc/require-returns": "warn", // Always requires @return, even when function does not return something
+ "jsdoc/require-returns-type": "warn",
+ //"jsdoc/require-returns-check": "warn", // Unnecessary when require-returns above is disabled
+ "jsdoc/require-returns-description": "warn"
},
"ignorePatterns": ["src/libraryPatches/*"]
}
\ No newline at end of file
diff --git a/accounts.txt b/accounts.txt
index 48dbbba4..54197aa8 100644
--- a/accounts.txt
+++ b/accounts.txt
@@ -1 +1 @@
-//Comment: This file is used to provide your bot accounts in the form of username:password. Read the instructions here: https://github.com/HerrEurobeat/steam-comment-service-bot#accounts
\ No newline at end of file
+//Comment: This file is used to provide your bot accounts in the form of username:password. Read the instructions here: https://github.com/3urobeat/steam-comment-service-bot#accounts
\ No newline at end of file
diff --git a/docs/wiki/adding_proxies.md b/docs/wiki/adding_proxies.md
index f5595e6b..38264f97 100644
--- a/docs/wiki/adding_proxies.md
+++ b/docs/wiki/adding_proxies.md
@@ -1,6 +1,8 @@
# Adding proxies
[⬅️ Go back to wiki home](./)
+
+
If you are using 10+ accounts I encourage you to use proxies.
It drastically reduces the amount of requests on one IP by equally spreading all accounts onto different IPs, allowing you to comment more and faster, reducing the chance of an IP ban.
diff --git a/docs/wiki/advancedconfig_doc.md b/docs/wiki/advancedconfig_doc.md
index d0081b18..708d6f7e 100644
--- a/docs/wiki/advancedconfig_doc.md
+++ b/docs/wiki/advancedconfig_doc.md
@@ -1,11 +1,14 @@
# Advanced Config Documentation
[⬅️ Go back to wiki home](./)
+
+
This is the full documentation to customize your `advancedconfig.json`.
**Note:** These settings are meant for advanced users. Everything important is located in the `config.json` file and you probably don't need to change anything here.
-
+
+
| Key | Usage | Description |
| ------------- | ---------------- | ------------ |
| \_disclaimer\_ | String | No functionality. Just a comment pointing to the normal config. |
diff --git a/docs/wiki/changelogs/CHANGELOG_v2.13.md b/docs/wiki/changelogs/CHANGELOG_v2.13.md
index 42b029b3..12795086 100644
--- a/docs/wiki/changelogs/CHANGELOG_v2.13.md
+++ b/docs/wiki/changelogs/CHANGELOG_v2.13.md
@@ -9,6 +9,7 @@
- [2.13.2](#2.13.2)
- [2.13.3](#2.13.3)
- [2.13.4](#2.13.4)
+- [2.13.5](#2.13.5)
@@ -35,8 +36,8 @@ If you are using a `customlang.json`, make sure to read the language string chan
- Added a library patches system to load my changes until [my PR to the SteamCommunity library gets accepted](https://github.com/DoctorMcKay/node-steamcommunity/pull/306)
- Added support for figuring out sharedfile IDs to the handleSteamIdResolving() helper
- Added a ratingHistory database to track which bot accounts have voted on or favorized which item
-- Added a completely new plugin system, co-author @DerDeathraven #174
- - Added a plugin loader which dynamically loads all installed npm packages with the prefix `steam-comment-bot-` @DerDeathraven #174
+- Added a completely new plugin system, co-author @DerDeathraven [#174](https://github.com/3urobeat/steam-comment-service-bot/pull/174)
+ - Added a plugin loader which dynamically loads all installed npm packages with the prefix `steam-comment-bot-` @DerDeathraven [#174](https://github.com/3urobeat/steam-comment-service-bot/pull/174)
- Added new template plugin which you can [fork here to create your own plugin](https://github.com/3urobeat/steam-comment-bot-template-plugin)
- Added plugin functions: `load`, `ready`, `statusUpdate`, `steamGuardInput`
- Added plugin data directory and functions to handle data reading & writing: `getPluginDataPath`, `loadPluginData`, `writePluginData`, `loadPluginConfig`, `writePluginConfig`
@@ -120,7 +121,7 @@ If you are using a `customlang.json`, make sure to read the language string chan
**Fixes:**
-- Fixed the webserver plugin from being completely broken #172
+- Fixed the webserver plugin from being completely broken [#172](https://github.com/3urobeat/steam-comment-service-bot/issues/172)
- Fixed return parameters of checkConnection
- Fixed webSession looping caused by broken botsgroup check
- Fixed accounts.txt import still checking for loginfo.json
@@ -140,7 +141,7 @@ If you are using a `customlang.json`, make sure to read the language string chan
- Fixed cooldown issues when cooldown is disabled and process got aborted
- Fixed a data check error on broken internet connection by checking it beforehand
- Fixed handleLoginTimeout causing a DuplicateRequest error on 2FA input
-- Fixed output-logger causing crash when running bot with pm2 #48
+- Fixed output-logger causing crash when running bot with pm2 [#48](https://github.com/3urobeat/steam-comment-service-bot/issues/48)
- Fixed potential bug where whenAvailableStr in comment command could display wrong information if allAccounts was empty or if more accounts got removed after the activeRequests loop ran 9e6c569
- Fixed/Removed minor unnecessary checks in comment command
- Fixed user added while offline message being able to fail because it was sent too early after logging in
@@ -151,8 +152,8 @@ If you are using a `customlang.json`, make sure to read the language string chan
- Removed support for the old login flow: f5957bb
- Removed v2.13 login flow change notification message
- Removed `disableCommentCmd` setting from `advancedconfig.json` and replaced it with `restrictAdditionalCommandsToOwners` array
-- Removed `enableurltocomment` setting from `advancedconfig.json`. Toggling the webserver plugin is now done in the plugin config in the `plugins` directory #172
-- Removed `urlrequestsecretkey` from `data.json`. This key is now stored in the plugin config in the `plugins` directory. The update will cause a new key to be generated. #172
+- Removed `enableurltocomment` setting from `advancedconfig.json`. Toggling the webserver plugin is now done in the plugin config in the `plugins` directory [#172](https://github.com/3urobeat/steam-comment-service-bot/issues/172)
+- Removed `urlrequestsecretkey` from `data.json`. This key is now stored in the plugin config in the `plugins` directory. The update will cause a new key to be generated. [#172](https://github.com/3urobeat/steam-comment-service-bot/issues/172)
- Removed most global variables
- Removed (nearly) every usage of `var`
- Removed lots of unnecessary variables that are now replaced by better systems
@@ -165,7 +166,7 @@ If you are using a `customlang.json`, make sure to read the language string chan
- Moved various updater, controller & bot helper files to new objects
- Moved various log messages to log at more appropriate times
- Shortened most comment error descriptions
-- Improved eslint styling rules, formatting & code quality and added prettier config @DerDeathraven #171
+- Improved eslint styling rules, formatting & code quality and added prettier config @DerDeathraven [#171](https://github.com/3urobeat/steam-comment-service-bot/pull/171)
- Updated dependencies
- Tons of fixes for features that have been added in this update and are therefore not listed here
- Minor other changes
@@ -365,4 +366,36 @@ Commit: [24984d6](https://github.com/3urobeat/steam-comment-service-bot/commit/2
- Updated dependencies
- Minor other changes
-Commit: [829c387](https://github.com/3urobeat/steam-comment-service-bot/commit/829c387)
\ No newline at end of file
+Commit: [829c387](https://github.com/3urobeat/steam-comment-service-bot/commit/829c387)
+
+
+
+
+
+## **2023-07-09, Version 2.13.5**, co-author [@DerDeathraven](https://github.com/DerDeathraven)
+**Additions:**
+- Aggregate old config files for plugins [@DerDeathraven](https://github.com/DerDeathraven) [#188](https://github.com/3urobeat/steam-comment-service-bot/pull/188)
+- Added arguments documentation to all commands
+- Added command descriptions from wiki to all commands
+- Added data export functions to DataManager to write all data files back to the disk
+- Added JsDoc eslint rules and enforced them
+- Added Command and CommandArg typedef in CommandHandler
+
+**Fixes:**
+- Potentially fixed SteamCommunity library scraping sharedfile error caused by non-English page being returned by Steam
+- Fixed reload not clearing plugin cache when using 'npm link' [@DerDeathraven](https://github.com/DerDeathraven) [#192](https://github.com/3urobeat/steam-comment-service-bot/pull/192)
+- Fixed reload not clearing plugin cache of subfolders
+- Fixed plugins reading core commands on load getting an empty array because `_importCoreCommands()` wasn't being awaited
+
+**Changes:**
+- Wiki: Updated Integrating into your app and Creating Plugins [@DerDeathraven](https://github.com/DerDeathraven) [#189](https://github.com/3urobeat/steam-comment-service-bot/pull/189)
+- Wiki: Rewrote Steam Limitations page, rewrote Integrating into your app page, updated various command descriptions, added missing step to npm link explanation and more misc improvements
+- Plugin data handling functions will now throw errors on missing parameters
+- Data Check will now throw errors instead of rejecting with a string
+- Cleaned up a few unnecessarily nested promises
+- Enforced lowercase primitive types in JsDocs
+- Updated every mention of my old username
+- Updated dependencies
+- Minor other changes
+
+Commit: [](https://github.com/3urobeat/steam-comment-service-bot/commit/)
\ No newline at end of file
diff --git a/docs/wiki/commands_doc.md b/docs/wiki/commands_doc.md
index db12c98c..7ff6aae2 100644
--- a/docs/wiki/commands_doc.md
+++ b/docs/wiki/commands_doc.md
@@ -1,9 +1,13 @@
# Commands documentation
[⬅️ Go back to wiki home](./)
+
+
This is the full documentation of all commands. Most commands have aliases but some might be missing in this list.
[Try them out!](https://steamcommunity.com/id/3urobeatscommentbot/)
+
+
| Command | Usage/Arguments | Description |
| ------------- | ---------------- | ------------ |
| !help | No arguments | Returns a list of commands available to you and a link to this page. |
@@ -12,30 +16,31 @@ This is the full documentation of all commands. Most commands have aliases but s
| !downvote | `amount ID` | Downvotes a sharedfile with all bot accounts that haven't yet voted on that item. Requires unlimited accounts! (Owner only.) |
| !favorite | `amount ID` | Favorizes a sharedfile with all bot accounts that haven't yet favorized that item. |
| !unfavorite | `amount ID` | Unfavorizes a sharedfile with all bot accounts that have favorized that item. (Owners only.) |
-| !ping | No arguments | Returns a `'Pong!'` and ping to Steam's servers. Can be used to check if the bot is responsive |
+| !ping | No arguments | Returns ping in ms to Steam's servers. Can be used to check if the bot is responsive |
| !info | No arguments | Returns useful information and statistics about the bot and you. |
| !owner | No arguments | Returns a link to the owner's profile set in the config.json. |
| !group | No arguments | Sends an invite or responds with the group link set as yourgroup in the config. |
-| !abort | `profileid` | Abort your own comment process. Owners can also provide a profile id to abort a comment process for that profile. |
+| !abort | `ID` | Abort your own comment process or one on another ID you have started. Owners can also abort requests started by other users. |
| !resetcooldown | `profileid` or `global` | Clear your, the profileid's or the comment cooldown of all bot accounts (global). Alias: !rc (Owner only.) |
| !settings | `config key` `new value` | Change a value in the config. (Owner only.) |
-| !failed | `profileid` | See the exact errors of your last comment request. Owners can also provide a profile id to show errors for that profile. |
-| !sessions | No arguments | Displays all active comment processes. (Owner only.) |
-| !mysessions | No arguments | Displays all active comment processes for your profile. |
-| !about | No arguments | Returns the link to this repository, my steam profile and a donation link. The message also contains a disclaimer as well as a link to the owner's profile set in the config.json. |
-| !addfriend | `profileid` | Adds the profileid with all bot accounts. **This only works with unlimited accounts!** (Owner only.) |
-| !unfriend | `profileid` | Unfriends the associated user from all logged in accounts if he is still on their friendlist. (Owner only.) Providing no argument will let all bots unfriend you. (Available to normal users) |
+| !failed | `ID` | See the exact errors of the last comment request on your profile or provide an ID to see the errors of the last request you started. Owners can also view errors for requests started by other users. |
+| !sessions | No arguments | Displays all active requests. (Owner only.) |
+| !mysessions | No arguments | Displays all active requests that you have started. |
+| !about | No arguments | Displays information about this project. The message also contains a disclaimer as well as a link to the owner's profile set in the config.json. |
+| !addfriend | `profileid` | Adds the profileid with all bot accounts. Requires unlimited accounts! (Owner only.) |
+| !unfriend | `profileid` | Unfriends a user from all logged in accounts. (Owner only.) Providing no argument will let all bots unfriend you. (Available to normal users) |
| !unfriendall | `"abort"` | Unfriends everyone with all bot accounts. (Owner only.) |
-| !leavegroup | `groupid64` or `group url` | Leave this group with all bot accounts. (Owner only.) |
+| !joingroup | `groupid64` or `group url` | Joins a Steam Group with all bot accounts. (Owner only.) |
+| !leavegroup | `groupid64` or `group url` | Leaves a group with all bot accounts. (Owner only.) |
| !leaveallgroups | `"abort"` | Leaves all groups with all bot accounts. (Owner only.) |
-| !block | `profileid` | Blocks the user on Steam. (Owner only.) |
-| !unblock | `profileid` | Unblocks the user on Steam. Note: The user still seems to be ignored for a few days by Steam. (Owner only.) |
+| !block | `profileid` | Blocks a user with all bot accounts on Steam. (Owner only.) |
+| !unblock | `profileid` | Unblocks a user with all bot accounts on Steam. Note: The user can still get ignored by Steam for a while. (Owner only.) |
| !reload | No arguments | Reloads all commands and plugins without needing to restart. Please only use it for testing/development. Alias: !rl (Owner only.) |
| !restart | No arguments | Restarts the bot and checks for available updates. Alias: !rs (Owner only.) |
| !stop | No arguments | Stops the bot. (Owner only.) |
-| !update | `true` | Checks for an available update and installs it if automatic updates are enabled and no comment sessions are active. 'true' forces an update. Blocks new comment requests if it currently waits for one to be completed. |
+| !update | `true` | Checks for an available update and installs it if automatic updates are enabled and no requests are active. 'true' forces an update. Blocks new requests if it currently waits for one to be completed. (Owner only.) |
| !log | No arguments | Shows the last 15 lines of the log. (Owner only.) |
-| !eval | `javascript code` | Disabled by default, needs to be toggled on with `enableevalcmd` in config.json.
**Warning!** This will run any javascript code that was provided. It is strongly advised to leave this feature off unless you know exactly what this means! If you have multiple owners configured they also can run code on **your** machine!
(Owner only.) |
+| !eval | `javascript code` | Disabled by default, needs to be toggled on with `enableevalcmd` in config.json.
**Warning!** This will run any javascript code that was provided. It is strongly advised to leave this feature off unless you know exactly what this means! If you have multiple owners configured they can also run code on **your** machine!
(Owner only.) |
To get more information about responses in form of an error that one of these commands could return, visit the `Errors & FAQ` page in this wiki.
diff --git a/docs/wiki/config_doc.md b/docs/wiki/config_doc.md
index 0dd7eaf8..7bb8c6a7 100644
--- a/docs/wiki/config_doc.md
+++ b/docs/wiki/config_doc.md
@@ -1,10 +1,13 @@
# Config Documentation
[⬅️ Go back to wiki home](./)
+
+
This is the full documentation to customize your `config.json`.
**Note:** You don't need to change any values except the owner and ownerid values. Everything else is configured to work out of the box but there might be better settings for your specific situation. Recommendation: Try the default values and customize them if you get errors/problems.
-
+
+
| Key | Usage | Description |
| ------------- | ---------------- | ------------ |
diff --git a/docs/wiki/creating_plugins.md b/docs/wiki/creating_plugins.md
index bff601e8..9b07ff4b 100644
--- a/docs/wiki/creating_plugins.md
+++ b/docs/wiki/creating_plugins.md
@@ -1,45 +1,50 @@
# Creating plugins
[⬅️ Go back to wiki home](./)
+
+
The functionality of this bot can be extended using plugins.
-They allow you to intercept events from the bots, run commands, edit or supply data and much much more.
+They allow you to intercept events from the bots, run commands, edit or supply data and much much more.
This guide will explain you the basics to get started and go through the template plugin!
-I'm expecting you already have some experience programming and have worked with JavaScript, as well as NPM packages and Git.
+I'm expecting you already have some experience programming and have worked with JavaScript, as well as NPM packages and Git.
You do not need to read this quite long article. Feel free to [set up](#getting-started) and just start playing around with parts of the bot using your code editor's IntelliSense.
-You should definitely take a look at the developer documentation though, it explains everything every module does and the functions exposed by it.
+You should definitely take a look at the developer documentation though, it explains everything every module does and the functions exposed by it.
## Table Of Contents
-- [Getting started](#getting-started)
-- [The filestructure](#filestructure)
-- [Exposed functions and events](#functions)
-- [Logging messages](#logging)
-- [Plugin System Interface](#pluginsystem)
-- [Controller](#controller)
-- [Command System](#commandhandler)
-- [Packing and installing your plugin using npm](#npm)
-- [Additional information](#additional-info)
+
+- [Getting started](#getting-started)
+- [The filestructure](#filestructure)
+- [Exposed functions and events](#functions)
+- [Logging messages](#logging)
+- [Plugin System Interface](#pluginsystem)
+- [Controller](#controller)
+- [Command System](#commandhandler)
+- [Typescript](#typescript)
+- [Packing and installing your plugin using npm](#npm)
+- [Additional information](#additional-info)
## **Getting started**
+
First, fork my [plugin template repository](https://github.com/3urobeat/steam-comment-bot-template-plugin).
-You can do this easily through the GitHub web interface.
+You can do this easily through the GitHub web interface.
Give your fork a fitting name, but make sure to keep the `steam-comment-bot-` prefix. This is important later.
-It probably also makes sense to keep the `-plugin` at the end, to indicate that your fork is a plugin.
+It probably also makes sense to keep the `-plugin` at the end, to indicate that your fork is a plugin.
Now, clone your fork, for example using the git cli:
`git clone https://github.com/your_username/your_fork ./folder/path/to/clone/to`
Open the folder you cloned to with your code editor and open the package.json file inside.
Change the name to the name you gave your fork. For the bot to recognize your plugin, it must have the `steam-comment-bot-` prefix I mentioned above.
-Populate description, author and version as well. The plugin will be packed into an NPM package later.
+Populate description, author and version as well. The plugin will be packed into an NPM package later.
Open the entry file `plugin.js` and edit the PluginSystem import file path at the top.
It should point to your `steam-comment-service-bot` installation. This makes sure your code editor's IntelliSense will work.
@@ -51,59 +56,64 @@ If your plugin folder is right beside the bot folder, the default path should al
## **The filestructure**
-Each plugin consists of three important files.
-- `plugin.js` - The entry file of your plugin. This one will be loaded by the bot and contains all the functions exposed by your plugin. It must contain an exposed constructor and load function.
-- `config.json` - The default configuration file of your plugin. This one will be copied into the plugin config folder by the bot the first time your plugin gets loaded. It must contain the parameter "enabled", everything else is up to you.
-- `package.json` - The NPM package config file of your plugin. This one will be read by NPM to package and install your plugin. The bot will go through all installed npm packages with the `steam-comment-bot-` name prefix and attempt to load their `plugin.js` file.
-You can of course add more files and folders as you like and load them from the `plugin.js` file.
+Each plugin consists of three important files.
+
+- `plugin.js` - The entry file of your plugin. This one will be loaded by the bot and contains all the functions exposed by your plugin. It must contain an exposed constructor and load function.
+- `config.json` - The default configuration file of your plugin. This one will be copied into the plugin config folder by the bot the first time your plugin gets loaded. It must contain the parameter "enabled", everything else is up to you.
+- `package.json` - The NPM package config file of your plugin. This one will be read by NPM to package and install your plugin. The bot will go through all installed npm packages with the `steam-comment-bot-` name prefix and attempt to load their `plugin.js` file.
+
+You can of course add more files and folders as you like and load them from the `plugin.js` file.
## **Exposed functions and events**
+
Let's take a look at what the template plugin does:
-Your plugin file `plugin.js` must expose a constructor and load function, just like the template does.
+Your plugin file `plugin.js` must expose a constructor and load function, just like the template does.
**Constructor:**
The template constructor stores references to the pluginSystem 'sys', the controller (which is the central part of the bot, "controlling" everything), the dataManager 'data' (which imports, checks and stores all config files) and the commandHandler (which loads, stores and runs all commands).
This makes using those interesting parts from your plugin easier and is probably a good idea to keep. You **need** the 'sys' reference, otherwise your plugin won't be able to communicate with the bot, making it pretty much useless.
-Please also keep the `logger` overwrite. It makes sure that the log hold-back functionality during login is working.
+Please also keep the `logger` overwrite. It makes sure that the log hold-back functionality during login is working.
**Load function:**
The load function is being called when the plugin is loaded. This happens right before the bot starts logging in accounts or right after the '!reload' command was used.
It makes sense to load your plugin config file from your plugin config folder, just like the template does.
-We are also registering a super cool command here with the names '!hello' and '!cool-alias'. If someone executes it, it will respond with 'Hello World!'. Registering commands and responding to the user is further explained below.
+We are also registering a super cool command here with the names '!hello' and '!cool-alias'. If someone executes it, it will respond with 'Hello World!'. Registering commands and responding to the user is further explained below.
**Event functions:**
The template plugin also exposes a 'ready', 'statusUpdate', 'steamGuardInput' function.
-These are functions that will be called by the plugin system when the bot emits those events.
+These are functions that will be called by the plugin system when the bot emits those events.
The ready event function is called when the bot has finished logging in all accounts. Should the plugin load be caused by '!reload', this function is executed milliseconds after `load()` has been called.
The statusUpdate event function is called when any bot account changes their status. Every status a bot can have is documented in the [EStatus enum](../../src/bot/EStatus.js).
-The steamGuardInput event function is called when any bot account is currently being logged in, but a Steam Guard Code is requested. The bot has a built in handler that will request code input from the terminal on this event.
+The steamGuardInput event function is called when any bot account is currently being logged in, but a Steam Guard Code is requested. The bot has a built in handler that will request code input from the terminal on this event.
## **Logging messages**
+
I'm using my own [logging library](https://github.com/3urobeat/output-logger) to log everything to the terminal and the `output.txt` file. It is easy to use, do not be afraid of this wall of text ^^
-Please do not use any `console.log` calls in your plugins (unless maybe for debugging).
+Please do not use any `console.log` calls in your plugins (unless maybe for debugging).
+
+Here is the parameter structure, first to last:
-Here is the parameter structure, first to last:
-- One of these types: 'debug', 'info', 'warn', 'error'. Debug mesages are only logged if `printDebug` is set to true in `advancedconfig.json`
-- The message you want to log. If not of datatype string, the library will attempt to colorize the data, just like console.log does.
-- Optional - nodate: true if the message should not have a date
-- Optional - remove: true if the next message should overwrite this one
-- Optional - animation: An array containing strings. If this is specified, it will display each element of this array after another in the front of the message as an animation. The logger library has some default animations, check them out using your IntelliSense at: `logger.animations` or [here](https://github.com/3urobeat/output-logger/blob/master/lib/data/animations.json)
-- Optional - printNow: true to force print this message now. This will skip the log hold back system explained below
-- Optional - cutToWidth: true to force cut this message to the current width of the terminal
+- One of these types: 'debug', 'info', 'warn', 'error'. Debug mesages are only logged if `printDebug` is set to true in `advancedconfig.json`
+- The message you want to log. If not of datatype string, the library will attempt to colorize the data, just like console.log does.
+- Optional - nodate: true if the message should not have a date
+- Optional - remove: true if the next message should overwrite this one
+- Optional - animation: An array containing strings. If this is specified, it will display each element of this array after another in the front of the message as an animation. The logger library has some default animations, check them out using your IntelliSense at: `logger.animations` or [here](https://github.com/3urobeat/output-logger/blob/master/lib/data/animations.json)
+- Optional - printNow: true to force print this message now. This will skip the log hold back system explained below
+- Optional - cutToWidth: true to force cut this message to the current width of the terminal
Check out the JsDoc of the logger function directly: [controller logger.js](../../src/controller/helpers/logger.js)
-You can also use this library to read input from the user, display a progress bar, managing animations and much more. Please check out the [output-logger README](https://github.com/3urobeat/output-logger) for more information.
+You can also use this library to read input from the user, display a progress bar, managing animations and much more. Please check out the [output-logger README](https://github.com/3urobeat/output-logger) for more information.
Do not be confused if your log messages are not showing up instantly while the bot is still logging in.
The bot has a logger hold-back functionality which holds back every log message that is not of type 'debug' or 'error' or has `printNow` set to true during login. These messages will be logged as soon as the bot is started, aka the ready event has fired.
@@ -113,27 +123,30 @@ The bot has a logger hold-back functionality which holds back every log message
## **Plugin System Interface**
+
As mentioned above, your constructor gets a reference to the Plugin System as the first parameter.
The Plugin System is responsible for loading, checking and calling plugin functions, as well as providing you functions to write to and read from your plugin data folder.
-It also stores references to the controller, giving you access to every part of the bot.
+It also stores references to the controller, giving you access to every part of the bot.
The Plugin System is probably the most important part for you when developing a plugin.
-Check it out using your IntelliSense and take a look at the developer documentation (TODO).
+Check it out using your IntelliSense and take a look at the developer documentation (TODO).
## **Controller**
+
The Controller is the most important part of the application itself.
-It is responsible for loading all modules on start, storing references to them, logging in all bot accounts and handling any issues.
+It is responsible for loading all modules on start, storing references to them, logging in all bot accounts and handling any issues.
You can access it from your plugin through the Plugin System: `sys.controller`
-From there, every other module of the bot is accessible. Worth noting:
-- Functions to restart/stop the application, resolving steamIDs, getting bot accounts, etc.
-- `sys.controller.data` - The DataManager object which contains every loaded datafile (e.g. logininfo, config, quotes, proxies, etc.)
-- `sys.controller.bots` - Object which holds references to all Bot objects, mapped to their account names. Access any bot account that is in use right now from there. I recommend using the `getBots()` function instead of accessing this object directly.
-- `sys.controller.commandHandler` - The CommandHandler object, read more [below](#commandhandler).
+From there, every other module of the bot is accessible. Worth noting:
+
+- Functions to restart/stop the application, resolving steamIDs, getting bot accounts, etc.
+- `sys.controller.data` - The DataManager object which contains every loaded datafile (e.g. logininfo, config, quotes, proxies, etc.)
+- `sys.controller.bots` - Object which holds references to all Bot objects, mapped to their account names. Access any bot account that is in use right now from there. I recommend using the `getBots()` function instead of accessing this object directly.
+- `sys.controller.commandHandler` - The CommandHandler object, read more [below](#commandhandler).
...and much more. Check it out using your code editor's IntelliSense.
@@ -142,28 +155,52 @@ From there, every other module of the bot is accessible. Worth noting:
## **Command System**
+
The CommandHandler allows you to register and run commands.
Commands are the core functionalities that allow you to request comments, retrieve information and manage the bot.
-Any command you register will instantly be available to all message handlers.
+Any command you register will instantly be available to all message handlers.
By default the bot has built-in Steam Chat message handling.
You can implement your own message handler as well, allowing you to integrate all registered commands into other platforms.
-Take a look at the developer documentation (TODO) for more information about how to write a custom message handler.
+Take a look at the developer documentation (TODO) for more information about how to write a custom message handler.
+
+
+
+
+
+## **Typescript:**
+
+For the best development environment you can utilize our typing file in TS
+just take the [typing file](https://github.com/3urobeat/steam-comment-service-bot/blob/beta-testing/types/types.d.ts) and copy it to your project
+
+there you can point your ts compiler to the file by adding the following to your tsconfig.json
+
+```json
+{
+ "compilerOptions": {
+ "types": ["path/to/typing-file"]
+ }
+}
+```
+
+The [web server](https://github.com/DerDeathraven/steam-comment-bot-rest-api) plugin is written in TS and can be used as a example
## **Packing and installing your plugin using npm**
+
If you have a version that you would like to **pack locally**, follow these steps:
-- Open the `package.json` file of your plugin and give it a proper version. NPM packages use [semantic versioning](https://semver.org/). In short, this means:
- - The version number is split into three parts: MAJOR.MINOR.PATCH
- - Increment MAJOR if you make breaking changes, e.g. user interaction is required to update your package
- - Increment MINOR if you add new functionality that is backwards compatible, e.g. no direct user interaction is required to update
- - Increment PATCH if you made bugfixes or other small changes which also do not require user interaction
- - If you do not have a full release finished yet, e.g. a beta version, start with the version number `0.1.0`. Your first full release `1.0.0` is appropiate when the core functionality has been finished and no major bugs are to be expected
-- Open a command line/terminal window in your plugin project folder and run `npm pack`. On success a new `.tgz` archive appeared in your folder.
-- Copy the package archive to your steam-comment-service-bot folder, open a new terminal there and run `npm install ./the-archive-name.tgz`
+
+- Open the `package.json` file of your plugin and give it a proper version. NPM packages use [semantic versioning](https://semver.org/). In short, this means:
+ - The version number is split into three parts: MAJOR.MINOR.PATCH
+ - Increment MAJOR if you make breaking changes, e.g. user interaction is required to update your package
+ - Increment MINOR if you add new functionality that is backwards compatible, e.g. no direct user interaction is required to update
+ - Increment PATCH if you made bugfixes or other small changes which also do not require user interaction
+ - If you do not have a full release finished yet, e.g. a beta version, start with the version number `0.1.0`. Your first full release `1.0.0` is appropiate when the core functionality has been finished and no major bugs are to be expected
+- Open a command line/terminal window in your plugin project folder and run `npm pack`. On success a new `.tgz` archive appeared in your folder.
+- Copy the package archive to your steam-comment-service-bot folder, open a new terminal there and run `npm install ./the-archive-name.tgz`
On restart (or by running the command `!reload`) you should see your plugin get loaded!
The bot will automatically create a new data folder for your plugin in the `plugins` folder. It will already contain the default config you shipped with your plugin.
@@ -171,10 +208,11 @@ The bot will automatically create a new data folder for your plugin in the `plug
If you have a finished version of your plugin that you would like to **publish to NPM**, follow these steps:
-- If this is your first time, create an [NPM account](https://www.npmjs.com/signup), open a command line/terminal in your plugin project folder and run `npm login`. (I assume you have npm installed alongside node)
-- Once that is done, give your plugin a proper version number in `package.json`. NPM packages use semantic versioning which is explained above.
-- Run `npm publish` in the command line/terminal window from step 1.
-- If everything goes well, your package should now be accessible to anyone. Check it out by searching for it [on the npm webpage](https://www.npmjs.com/)!
+
+- If this is your first time, create an [NPM account](https://www.npmjs.com/signup), open a command line/terminal in your plugin project folder and run `npm login`. (I assume you have npm installed alongside node)
+- Once that is done, give your plugin a proper version number in `package.json`. NPM packages use semantic versioning which is explained above.
+- Run `npm publish` in the command line/terminal window from step 1.
+- If everything goes well, your package should now be accessible to anyone. Check it out by searching for it [on the npm webpage](https://www.npmjs.com/)!
To install and use your plugin anyone can now run the command `npm install steam-comment-bot-your-plugin-name` in their steam-comment-service-bot folder.
On restart (or by running the command `!reload`) you should see your plugin get loaded!
@@ -185,22 +223,27 @@ The bot will automatically create a new data folder for your plugin in the `plug
## **Additional information**
+
**Debug Mode:**
It may make sense to enable `printDebug` in the `advancedconfig.json` while you are working on your plugin.
This will log way more stuff while the bot is running to maybe help you debug your plugin.
-Feel free to include 'debug' log calls in your plugin as well!
+Feel free to include 'debug' log calls in your plugin as well!
**More efficient NPM setup for development:**
To improve your plugin development experience it is recommended to link your project and the bot using npm.
-This will allow you to test changes without needing to pack or publish the plugin.
+This will allow you to test changes without needing to pack or publish the plugin.
+
+To do this, follow these steps:
-To do this, follow these steps:
-- Open a command line/terminal window in the folder of your plugin project
-- Run the command `npm link`. On Linux you might have to precede the command with `sudo` (or doas, or whatever you are using).
- This will make the package available locally to all projects
-- Open a terminal window in the folder of your bot installation. Run the command `npm link name-of-your-package`
+- Open a command line/terminal window in the folder of your plugin project
+- Run the command `npm link`. On Linux you might have to precede the command with `sudo` (or doas, or whatever you are using).
+ This will make the package available locally to all projects
+- Open a terminal window in the folder of your bot installation. Run the command `npm link name-of-your-package`
+- Open the package.json in the folder of your bot installation and add your plugin as a dependency with a random version. This is important so that the plugin loader will find your package.
+ Example: `"name-of-your-package": "^0.0.0"`
+ Don't forget to add a comma at the end of the previous line so that your JSON object is valid.
On restart (or by running the command `!reload`) you should see your plugin get loaded!
If you make changes in your plugin project you now only need to run the `!reload` command to test them.
@@ -209,4 +252,4 @@ If you make changes in your plugin project you now only need to run the `!reload
**Issues and Pull Requests:**
You found a bug or you think something should be changed in the bot itself?
-Feel free to [open an issue](https://github.com/3urobeat/steam-comment-service-bot/issues/new/choose) or fork the bot, make changes and [open a pull request](https://github.com/3urobeat/steam-comment-service-bot/compare) yourself! Every contribution is welcome!
\ No newline at end of file
+Feel free to [open an issue](https://github.com/3urobeat/steam-comment-service-bot/issues/new/choose) or fork the bot, make changes and [open a pull request](https://github.com/3urobeat/steam-comment-service-bot/compare) yourself! Every contribution is welcome!
diff --git a/docs/wiki/customlang_doc.md b/docs/wiki/customlang_doc.md
index c6b1d85e..8ed82b01 100644
--- a/docs/wiki/customlang_doc.md
+++ b/docs/wiki/customlang_doc.md
@@ -1,6 +1,8 @@
# How to customize chat messages sent by the bot
[⬅️ Go back to wiki home](./)
+
+
This page will instruct you on how to modify the messages the bot will send to a user.
This includes nearly all messages and isn't difficult!
diff --git a/docs/wiki/data_doc.md b/docs/wiki/data_doc.md
index 36291a06..b9928f4c 100644
--- a/docs/wiki/data_doc.md
+++ b/docs/wiki/data_doc.md
@@ -1,9 +1,13 @@
# Data Documentation
[⬅️ Go back to wiki home](./)
+
+
This is the full documentation of the `data.json` file. This file must **not** be edited!
It stores various persistent data about what version the bot is running, which file to start, etc.
+
+
| Key | Description |
| ------------- | ------------ |
| version | Defines the installed version. This number will be used by the updater to determine if an update is available. |
diff --git a/docs/wiki/errors_doc.md b/docs/wiki/errors_doc.md
index d51e9317..92df7133 100644
--- a/docs/wiki/errors_doc.md
+++ b/docs/wiki/errors_doc.md
@@ -9,10 +9,14 @@ Please remember that the steam errors list is incomplete and the descriptions ar
Bot errors are errors that are being directly returned from my bot, not from steam. I created this documentation so that my bot doesn't suffer the same fate as the steam errors.
-- [Errors, FAQ \& Common problems](#errors-faq--common-problems)
- - [Bot Errors:](#bot-errors)
- - [Steam Errors:](#steam-errors)
- - [FAQ/Common problems](#faqcommon-problems)
+
+
+**Table of Contents:**
+- [Bot Errors:](#bot-errors)
+- [Steam Errors:](#steam-errors)
+- [FAQ/Common problems](#faqcommon-problems)
+
+
## Bot Errors:
| Error message | Description |
@@ -20,6 +24,8 @@ Bot errors are errors that are being directly returned from my bot, not from ste
Coming soon...
+
+
## Steam Errors:
Please don't take all error descriptions for granted. Steam sometimes seems to throw random errors that might not actually have an impact or have completely different reasons.
@@ -49,6 +55,8 @@ You can find all HTTP status codes here: [Wikipedia](https://en.wikipedia.org/wi
| ----- | ----- |
`Error: AccessDenied` | This can happen if your session got invalidated for some reason. This can often be solved by deleting the `tokens.db` file, located at `src/data/` inside the bot folder, or by only removing the one line of the affected account inside the `tokens.db` file.
+
+
## FAQ/Common problems
**Q: I got a problem/bug that isn't listed here.**
diff --git a/docs/wiki/integrating_into_your_app.md b/docs/wiki/integrating_into_your_app.md
index 2ce25d3e..9dd048b4 100644
--- a/docs/wiki/integrating_into_your_app.md
+++ b/docs/wiki/integrating_into_your_app.md
@@ -1,30 +1,24 @@
# Integrating into your own application
[⬅️ Go back to wiki home](./)
-If you would like to integrate the comment command into your own application or you just don't want to deal with the Steam Chat then that is no problem! This can easily be done by pinging an URL.
+
-Note: The current webserver will be replaced with a better one in version 2.14, featuring a full fledged frontend.
+This project provides you two ways to integrate it into your own application.
+You can either write a **native plugin** or use the official **REST API** to communicate with another application.
-First make sure that you started the bot at least once.
-Go into your `plugins` and then `steam-comment-bot-webserver` folder, open `config.json` with a text editor, set `enabled` to `true` and save the file.
-Start the bot again. You should see this message when the bot is ready:
-```
-[INFO] Webserver is enabled: Server is listening on port 3034.
- Visit it in your browser: http://localhost:3034
-```
+
-If you now reopen the `config.json` you should see a new value at `requestKey`. Copy it as we will need it in a minute.
-Do not share this key! It is used to authenticate yourself to the webserver and allow comment requests to be made in the name of the first owner!
+**Native plugin:**
+A native plugin allows you to call functions provided by the bot, as well as reading & writing data directly.
+This is the best way to add features to the bot directly. Check out the [creating plugins page](./creating_plugins.md) for a detailed walkthrough.
-Visit the URL in your webbrowser and you will see a page that instructs you how to use it.
-If your bot is running on a different machine you will need to provide the IP of that machine instead of localhost but I will assume that you know basic networking here. To be able to reach the URL from outside your private network you will need to allow port 3034 in your router's settings and provide your public IP instead of localhost but this should also be basic networking knowledge.
-I however do not recommend exposing this port without any further security measures as I can't guarantee for any security here whatsoever!
+
-Requesting comments is very easy now. Just ping the `/comment` page with the required query parameters shown on the page you just opened and the webserver will respond with a HTTP status code and a message.
-Webserver comment requests will always be credited to the first owner set in the config.
+**REST API:**
+The REST API is an official web server plugin developed by [DerDeathraven](https://github.com/DerDeathraven). It is shipped with this project by default.
+It uses this RPC style pattern `https://localhost:4000/rpc/${Class}.${Method}?${params}` to easily call various methods.
-Example:
-`http://localhost:3034/comment?n=5&id=76561198260031749&key=pc90qtmzfk`
+This is the best way to get data from the bot and run commands from another, completely detached application.
+For an example, check out the official [Web UI project](https://github.com/DerDeathraven/steam-comment-service-bot-frontend) which is also made by [DerDeathraven](https://github.com/DerDeathraven)!
-This will request 5 comments for the steam profile `76561198260031749`.
-You can of course also provide group or sharedfile IDs here.
\ No newline at end of file
+The full API documentation and a TS SDK file can be found on the [plugin project page](https://github.com/DerDeathraven/steam-comment-bot-rest-api).
diff --git a/docs/wiki/steam_limitations.md b/docs/wiki/steam_limitations.md
index 27e97c86..16fdb093 100644
--- a/docs/wiki/steam_limitations.md
+++ b/docs/wiki/steam_limitations.md
@@ -1,10 +1,38 @@
# Steam Limitations
[⬅️ Go back to wiki home](./)
-Steam sadly has some strict restrictions for commenting.
-You can read all of them here: [Steam Support](https://help.steampowered.com/en/faqs/view/71D3-35C2-AD96-AA3A)
+
-If you use *limited accounts* (spent <$5 on the Store) the user will need to send all of these bot accounts a friend request before requesting a comment!
+Steam has some strict restrictions for commenting.
+This page attempts to explain what you can and cannot do with limited & unlimited accounts.
+Steam also has their own page [which you can view here](https://help.steampowered.com/en/faqs/view/71D3-35C2-AD96-AA3A).
-Please be also advised that in order to comment on a profile the accounts need to have at least **email steam guard** activated.
-There are generators out there that can directly generate accounts with steam guard, however it does not take long to do yourself if you'd like to avoid shady generators.
\ No newline at end of file
+
+
+**Steam Guard:**
+Most interactions in the community need you to have Steam Guard enabled, especially for commenting.
+Please make sure that all your bot accounts have at least E-Mail Steam Guard enabled.
+
+
+
+**What is a limited account?**
+Limited accounts are Steam accounts which have spent less than $5 USD on the Steam Store.
+They have stricter limitations and ratelimits to reduce the amount of spam and scams in the SteamCommunity.
+
+
+
+If your account is **limited**, you can:
+- ...comment on profiles of friends
+- ...comment on sharedfiles (screenshots, artworks & guides)
+- ...favorize and unfavorize sharedfiles
+
+If your account is **unlimited**, you can do all of the above and additionally:
+- ...comment on non-friend profiles
+- ...comment in groups
+- ...upvote and downvote sharedfiles
+- ...add other users as friends
+
+
+
+This list is not exhaustive, it just lists features supported by this bot.
+For more information, please check out the Steam Support article linked at the top of this page.
\ No newline at end of file
diff --git a/docs/wiki/version_changelogs.md b/docs/wiki/version_changelogs.md
index eecc4f20..9991baec 100644
--- a/docs/wiki/version_changelogs.md
+++ b/docs/wiki/version_changelogs.md
@@ -1,6 +1,8 @@
# Version Changelogs
[⬅️ Go back to wiki home](./)
+
+
This is a list of links to the changelogs of all versions.
Each changelog contains a date, all changes made and a link to the commit.
diff --git a/package-lock.json b/package-lock.json
index 10e0b574..db321084 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "steam-comment-service-bot",
- "version": "2.13.4",
+ "version": "2.13.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "steam-comment-service-bot",
- "version": "2.13.4",
+ "version": "2.13.5",
"license": "GPL-3.0",
"dependencies": {
"@seald-io/nedb": "^4.0.2",
@@ -15,22 +15,33 @@
"https": "^1.0.0",
"output-logger": "^2.3.7",
"request": "^2.88.2",
- "steam-comment-bot-rest": "^1.0.3",
+ "steam-comment-bot-rest": "^1.0.4",
"steam-comment-bot-webserver": "file:plugins/steam-comment-bot-webserver-1.0.0.tgz",
"steam-session": "^1.2.4",
- "steam-user": "^4.28.8",
+ "steam-user": "^4.28.9",
"steamcommunity": "^3.46.1",
"steamid": "^2.0.0",
"steamid-resolver": "^1.3.3"
},
"devDependencies": {
+ "eslint": "^8.44.0",
+ "eslint-plugin-jsdoc": "^46.4.3",
"tsd-jsdoc": "^2.5.0"
}
},
+ "node_modules/@aashutoshrathi/word-wrap": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+ "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/@babel/parser": {
- "version": "7.22.5",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
- "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
+ "version": "7.22.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.7.tgz",
+ "integrity": "sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q==",
"dev": true,
"peer": true,
"bin": {
@@ -62,9 +73,9 @@
}
},
"node_modules/@doctormckay/stdlib": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-2.6.0.tgz",
- "integrity": "sha512-8g1600orAg+5dZJWy/CgZw7lC6a/1gOUXEGT8owUz6ep8xoQoxxq6elZcDMpopjOAFGPB8yarll/IR+ZOiGuYg==",
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-2.7.0.tgz",
+ "integrity": "sha512-mNHFwj/U5kBxsh00vuqILEw95keMAsbxIIewkSZ9ORUoOxMIaA4WWlbqx3BFjU7aF+0/QLuA4ey9y4MFw4cj0w==",
"dependencies": {
"psl": "^1.9.0"
},
@@ -77,6 +88,144 @@
"resolved": "https://registry.npmjs.org/@doctormckay/steam-crypto/-/steam-crypto-1.2.0.tgz",
"integrity": "sha512-lsxgLw640gEdZBOXpVIcYWcYD+V+QbtEsMPzRvjmjz2XXKc7QeEMyHL07yOFRmay+cUwO4ObKTJO0dSInEuq5g=="
},
+ "node_modules/@es-joy/jsdoccomment": {
+ "version": "0.39.4",
+ "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz",
+ "integrity": "sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg==",
+ "dev": true,
+ "dependencies": {
+ "comment-parser": "1.3.1",
+ "esquery": "^1.5.0",
+ "jsdoc-type-pratt-parser": "~4.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+ "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "dev": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz",
+ "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==",
+ "dev": true,
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.0.tgz",
+ "integrity": "sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A==",
+ "dev": true,
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.44.0.tgz",
+ "integrity": "sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz",
+ "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==",
+ "dev": true,
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.5"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz",
+ "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==",
+ "dev": true
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/@protobufjs/aspromise": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
@@ -283,9 +432,9 @@
"integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw=="
},
"node_modules/@types/node": {
- "version": "20.3.2",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.2.tgz",
- "integrity": "sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw=="
+ "version": "20.4.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.1.tgz",
+ "integrity": "sha512-JIzsAvJeA/5iY6Y/OxZbv1lUcc8dNSE77lb2gnBH+/PJ3lFR1Ccvgwl5JWnHAkNHcRsT0TbpVOsiMKZ1F/yyJg=="
},
"node_modules/@types/qs": {
"version": "6.9.7",
@@ -383,6 +532,27 @@
"node": ">= 0.6"
}
},
+ "node_modules/acorn": {
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz",
+ "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==",
+ "dev": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
"node_modules/adm-zip": {
"version": "0.5.10",
"resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.10.tgz",
@@ -402,27 +572,6 @@
"node": ">= 6.0.0"
}
},
- "node_modules/agent-base/node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/agent-base/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -438,11 +587,29 @@
"url": "https://github.com/sponsors/epoberezkin"
}
},
- "node_modules/appdirectory": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/appdirectory/-/appdirectory-0.1.0.tgz",
- "integrity": "sha512-DJ5DV8vZXBbusyiyPlH28xppwS8eAMRuuyMo88xeEcf4bV64lbLtbxRxqixZuJBXsZzLtXFmA13GwVjJc7vdQw==",
- "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info."
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
},
"node_modules/archive-type": {
"version": "4.0.0",
@@ -463,12 +630,20 @@
"node": ">=4"
}
},
+ "node_modules/are-docs-informative": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz",
+ "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
"integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
- "dev": true,
- "peer": true
+ "dev": true
},
"node_modules/array-flatten": {
"version": "1.1.1",
@@ -551,6 +726,12 @@
"node": ">= 6"
}
},
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true
+ },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -644,6 +825,19 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
+ "node_modules/body-parser/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/body-parser/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
"node_modules/body-parser/node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -663,6 +857,16 @@
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
"integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
},
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
"node_modules/buffer": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
@@ -713,6 +917,18 @@
"resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
"integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ=="
},
+ "node_modules/builtin-modules": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz",
+ "integrity": "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/bytebuffer": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz",
@@ -782,6 +998,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
@@ -800,6 +1025,22 @@
"node": ">= 10"
}
},
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
"node_modules/cheerio": {
"version": "0.22.0",
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz",
@@ -896,6 +1137,24 @@
"mimic-response": "^1.0.0"
}
},
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true
+ },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -912,6 +1171,21 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
},
+ "node_modules/comment-parser": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.3.1.tgz",
+ "integrity": "sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true
+ },
"node_modules/content-disposition": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -961,6 +1235,20 @@
"node": ">= 0.10"
}
},
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/css-select": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
@@ -1028,11 +1316,19 @@
}
},
"node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dependencies": {
- "ms": "2.0.0"
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
}
},
"node_modules/decode-uri-component": {
@@ -1206,6 +1502,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -1231,6 +1533,18 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+ "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+ "dev": true,
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
@@ -1374,27 +1688,6 @@
"node": ">= 0.6"
}
},
- "node_modules/engine.io/node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/engine.io/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
"node_modules/entities": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
@@ -1412,30 +1705,198 @@
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
},
"node_modules/escape-string-regexp": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
- "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
"dev": true,
- "peer": true,
"engines": {
- "node": ">=8"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "node_modules/eslint": {
+ "version": "8.44.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.44.0.tgz",
+ "integrity": "sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A==",
+ "dev": true,
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.4.0",
+ "@eslint/eslintrc": "^2.1.0",
+ "@eslint/js": "8.44.0",
+ "@humanwhocodes/config-array": "^0.11.10",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.0",
+ "eslint-visitor-keys": "^3.4.1",
+ "espree": "^9.6.0",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "strip-json-comments": "^3.1.0",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
"engines": {
- "node": ">= 0.6"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/express": {
- "version": "4.18.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
- "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+ "node_modules/eslint-plugin-jsdoc": {
+ "version": "46.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.3.tgz",
+ "integrity": "sha512-Prc7ol+vCIghPeECpwZq5+P+VZfoi87suywvbYCiCnkI1kTmVSdcOC2M8mioglWxBbd28wbb1OVjg/8OzGzatA==",
+ "dev": true,
"dependencies": {
- "accepts": "~1.3.8",
- "array-flatten": "1.1.1",
+ "@es-joy/jsdoccomment": "~0.39.4",
+ "are-docs-informative": "^0.0.2",
+ "comment-parser": "1.3.1",
+ "debug": "^4.3.4",
+ "escape-string-regexp": "^4.0.0",
+ "esquery": "^1.5.0",
+ "is-builtin-module": "^3.2.1",
+ "semver": "^7.5.1",
+ "spdx-expression-parse": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz",
+ "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==",
+ "dev": true,
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz",
+ "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.0.tgz",
+ "integrity": "sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A==",
+ "dev": true,
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+ "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.18.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
+ "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
"body-parser": "1.20.1",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
@@ -1493,6 +1954,19 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
+ "node_modules/express/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
"node_modules/express/node_modules/qs": {
"version": "6.11.0",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
@@ -1567,6 +2041,12 @@
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
},
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true
+ },
"node_modules/fast-xml-parser": {
"version": "4.2.5",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.2.5.tgz",
@@ -1588,6 +2068,15 @@
"fxparser": "src/cli/cli.js"
}
},
+ "node_modules/fastq": {
+ "version": "1.15.0",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
+ "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==",
+ "dev": true,
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
"node_modules/fd-slicer": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
@@ -1596,6 +2085,18 @@
"pend": "~1.2.0"
}
},
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dev": true,
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
"node_modules/file-manager": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/file-manager/-/file-manager-2.0.1.tgz",
@@ -1608,9 +2109,9 @@
}
},
"node_modules/file-manager/node_modules/@doctormckay/stdlib": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.15.2.tgz",
- "integrity": "sha512-PRb0kXvHbAzYZT3wIaXTBg1JxiPGZJ54KNg8z5uXkuQ07PWqA1gbXmb15NrMd6EnDa4evC/ErHXVvb9EgySR2w==",
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.16.0.tgz",
+ "integrity": "sha512-mObNOnuEgEb+hZKkd6mY2PoB+9gLfuYkmv8ggN/R3JFjaRIWDOR1QlBV3psQZs7TqGpe3ZFB6bgddQGE02psOA==",
"engines": {
"node": ">=6.0.0"
}
@@ -1661,6 +2162,54 @@
"node": ">= 0.8"
}
},
+ "node_modules/finalhandler/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/finalhandler/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz",
+ "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==",
+ "dev": true,
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz",
+ "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
+ "dev": true
+ },
"node_modules/follow-redirects": {
"version": "1.15.2",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
@@ -1744,6 +2293,12 @@
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
},
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true
+ },
"node_modules/function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -1782,6 +2337,53 @@
"assert-plus": "^1.0.0"
}
},
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "dev": true,
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "13.20.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz",
+ "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/gopd": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
@@ -1841,6 +2443,12 @@
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
},
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true
+ },
"node_modules/har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
@@ -1873,6 +2481,15 @@
"node": ">= 0.4.0"
}
},
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/has-proto": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
@@ -2015,6 +2632,15 @@
}
]
},
+ "node_modules/ignore": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
+ "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==",
+ "dev": true,
+ "engines": {
+ "node": ">= 4"
+ }
+ },
"node_modules/image-size": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/image-size/-/image-size-0.8.3.tgz",
@@ -2034,6 +2660,41 @@
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
"integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
},
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+ "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+ "dev": true,
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "dev": true,
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
"node_modules/inherits": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
@@ -2079,6 +2740,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-builtin-module": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-3.2.1.tgz",
+ "integrity": "sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==",
+ "dev": true,
+ "dependencies": {
+ "builtin-modules": "^3.3.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
@@ -2090,6 +2766,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/is-generator-function": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz",
@@ -2104,6 +2789,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/is-natural-number": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz",
@@ -2117,6 +2814,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+ "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-plain-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz",
@@ -2169,6 +2875,12 @@
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
},
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
"node_modules/isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
@@ -2186,6 +2898,18 @@
"node": ">= 4"
}
},
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
"node_modules/js2xmlparser": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-4.0.2.tgz",
@@ -2231,6 +2955,25 @@
"node": ">=12.0.0"
}
},
+ "node_modules/jsdoc-type-pratt-parser": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.0.0.tgz",
+ "integrity": "sha512-YtOli5Cmzy3q4dP26GraSOeAhqecewG04hoO8DY56CH4KJ9Fvv5qKWUCCo3HZob7esJQHCv6/+bnTy72xZZaVQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/jsdoc/node_modules/escape-string-regexp": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz",
+ "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==",
+ "dev": true,
+ "peer": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/json-buffer": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz",
@@ -2246,6 +2989,12 @@
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true
+ },
"node_modules/json-stringify-safe": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -2291,6 +3040,19 @@
"node": ">=4.0.0"
}
},
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/lie": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
@@ -2317,6 +3079,21 @@
"lie": "3.1.1"
}
},
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
@@ -2395,6 +3172,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/lzma": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/lzma/-/lzma-2.3.2.tgz",
@@ -2415,6 +3204,14 @@
"node": ">=6"
}
},
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
"node_modules/markdown-it": {
"version": "12.3.2",
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz",
@@ -2532,6 +3329,18 @@
"node": ">=4"
}
},
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
"node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
@@ -2546,9 +3355,15 @@
}
},
"node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true
},
"node_modules/negotiator": {
"version": "0.6.3",
@@ -2630,6 +3445,23 @@
"wrappy": "1"
}
},
+ "node_modules/optionator": {
+ "version": "0.9.3",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+ "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+ "dev": true,
+ "dependencies": {
+ "@aashutoshrathi/word-wrap": "^1.2.3",
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/output-logger": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/output-logger/-/output-logger-2.3.7.tgz",
@@ -2674,6 +3506,36 @@
"node": ">=4"
}
},
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/p-timeout": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz",
@@ -2685,6 +3547,18 @@
"node": ">=4"
}
},
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -2693,6 +3567,33 @@
"node": ">= 0.8"
}
},
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/path-to-regexp": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
@@ -2746,6 +3647,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/prepend-http": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz",
@@ -2850,6 +3760,26 @@
"inherits": "~2.0.3"
}
},
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -2937,6 +3867,15 @@
"lodash": "^4.17.21"
}
},
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/responselike": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz",
@@ -2945,6 +3884,54 @@
"lowercase-keys": "^1.0.0"
}
},
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "dev": true,
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -2987,11 +3974,18 @@
}
},
"node_modules/semver": {
- "version": "5.7.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
- "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
"bin": {
- "semver": "bin/semver"
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
}
},
"node_modules/send": {
@@ -3017,6 +4011,19 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/send/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/send/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
+ },
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
@@ -3041,6 +4048,27 @@
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
},
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/side-channel": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz",
@@ -3100,48 +4128,6 @@
"node": ">=10.0.0"
}
},
- "node_modules/socket.io-parser/node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/socket.io-parser/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
- "node_modules/socket.io/node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/socket.io/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
"node_modules/socks": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/socks/-/socks-2.7.1.tgz",
@@ -3168,27 +4154,6 @@
"node": ">= 10"
}
},
- "node_modules/socks-proxy-agent/node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/socks-proxy-agent/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
"node_modules/sort-keys": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz",
@@ -3222,6 +4187,28 @@
"node": ">=0.10.0"
}
},
+ "node_modules/spdx-exceptions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
+ "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
+ "dev": true
+ },
+ "node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-license-ids": {
+ "version": "3.0.13",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.13.tgz",
+ "integrity": "sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==",
+ "dev": true
+ },
"node_modules/sshpk": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz",
@@ -3270,9 +4257,9 @@
}
},
"node_modules/steam-appticket/node_modules/@doctormckay/stdlib": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.15.2.tgz",
- "integrity": "sha512-PRb0kXvHbAzYZT3wIaXTBg1JxiPGZJ54KNg8z5uXkuQ07PWqA1gbXmb15NrMd6EnDa4evC/ErHXVvb9EgySR2w==",
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.16.0.tgz",
+ "integrity": "sha512-mObNOnuEgEb+hZKkd6mY2PoB+9gLfuYkmv8ggN/R3JFjaRIWDOR1QlBV3psQZs7TqGpe3ZFB6bgddQGE02psOA==",
"engines": {
"node": ">=6.0.0"
}
@@ -3321,9 +4308,9 @@
}
},
"node_modules/steam-comment-bot-rest": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/steam-comment-bot-rest/-/steam-comment-bot-rest-1.0.3.tgz",
- "integrity": "sha512-WEwNNRtQvNwxa0wOzgrEyjVCYU3uH1NUU1QqC8VOaLiPDteHaz9AYZjnczcpT3zI0IsUBq4oGvQmZ7W+blMStQ==",
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/steam-comment-bot-rest/-/steam-comment-bot-rest-1.0.4.tgz",
+ "integrity": "sha512-WkZIsRcN44E4TBgki7DSurvs3xt4N3hZuKZqookst++o8aXrzDLw7L6B8QTyXoVMNakjYQBClGZGQTvlpXSWsg==",
"hasInstallScript": true,
"dependencies": {
"@types/axios": "^0.14.0",
@@ -3367,27 +4354,6 @@
"node": ">=12.22.0"
}
},
- "node_modules/steam-session/node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
- "dependencies": {
- "ms": "2.1.2"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/steam-session/node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
- },
"node_modules/steam-totp": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/steam-totp/-/steam-totp-2.1.2.tgz",
@@ -3397,15 +4363,14 @@
}
},
"node_modules/steam-user": {
- "version": "4.28.8",
- "resolved": "https://registry.npmjs.org/steam-user/-/steam-user-4.28.8.tgz",
- "integrity": "sha512-DQAoLxpKFFVL3jKb8eMXZ4ne+j/CF4ppjwyTMQY3cvJ/SPGZ4UMKKm2x8V9is+YBE2Gif10ehQLXU9dBdvJwVg==",
+ "version": "4.28.9",
+ "resolved": "https://registry.npmjs.org/steam-user/-/steam-user-4.28.9.tgz",
+ "integrity": "sha512-95M5ALWtVMPWKQKD9xW/bI9a9GVcf3bQcHsR0FEpEA/yGiQJnBSUS2TxBK0XvUTEsYA3bhNu7RAUae7v9eN35Q==",
"dependencies": {
"@bbob/parser": "^2.2.0",
- "@doctormckay/stdlib": "^1.15.2",
+ "@doctormckay/stdlib": "^1.16.0",
"@doctormckay/steam-crypto": "^1.2.0",
"adm-zip": "^0.5.10",
- "appdirectory": "^0.1.0",
"binarykvparser": "^2.2.0",
"bytebuffer": "^5.0.0",
"file-manager": "^2.0.0",
@@ -3424,9 +4389,9 @@
}
},
"node_modules/steam-user/node_modules/@doctormckay/stdlib": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.15.2.tgz",
- "integrity": "sha512-PRb0kXvHbAzYZT3wIaXTBg1JxiPGZJ54KNg8z5uXkuQ07PWqA1gbXmb15NrMd6EnDa4evC/ErHXVvb9EgySR2w==",
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.16.0.tgz",
+ "integrity": "sha512-mObNOnuEgEb+hZKkd6mY2PoB+9gLfuYkmv8ggN/R3JFjaRIWDOR1QlBV3psQZs7TqGpe3ZFB6bgddQGE02psOA==",
"engines": {
"node": ">=6.0.0"
}
@@ -3558,6 +4523,18 @@
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-dirs": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz",
@@ -3571,7 +4548,6 @@
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
"integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
"dev": true,
- "peer": true,
"engines": {
"node": ">=8"
},
@@ -3603,6 +4579,18 @@
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz",
"integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA=="
},
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/taffydb": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz",
@@ -3627,6 +4615,12 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+ "dev": true
+ },
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -3730,6 +4724,30 @@
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="
},
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/type-is": {
"version": "1.6.18",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
@@ -3896,13 +4914,28 @@
}
},
"node_modules/websocket13/node_modules/@doctormckay/stdlib": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.15.2.tgz",
- "integrity": "sha512-PRb0kXvHbAzYZT3wIaXTBg1JxiPGZJ54KNg8z5uXkuQ07PWqA1gbXmb15NrMd6EnDa4evC/ErHXVvb9EgySR2w==",
+ "version": "1.16.0",
+ "resolved": "https://registry.npmjs.org/@doctormckay/stdlib/-/stdlib-1.16.0.tgz",
+ "integrity": "sha512-mObNOnuEgEb+hZKkd6mY2PoB+9gLfuYkmv8ggN/R3JFjaRIWDOR1QlBV3psQZs7TqGpe3ZFB6bgddQGE02psOA==",
"engines": {
"node": ">=6.0.0"
}
},
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/which-typed-array": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz",
@@ -3982,6 +5015,12 @@
"node": ">=0.4"
}
},
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true
+ },
"node_modules/yauzl": {
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
@@ -3990,6 +5029,18 @@
"buffer-crc32": "~0.2.3",
"fd-slicer": "~1.1.0"
}
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
}
}
}
diff --git a/package.json b/package.json
index 7ba3e418..addca217 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "steam-comment-service-bot",
- "version": "2.13.4",
+ "version": "2.13.5",
"description": "Request a ton of steam profile/group comments from a bot network with just one command!",
"main": "start.js",
"dependencies": {
@@ -10,10 +10,10 @@
"https": "^1.0.0",
"output-logger": "^2.3.7",
"request": "^2.88.2",
- "steam-comment-bot-rest": "^1.0.3",
+ "steam-comment-bot-rest": "^1.0.4",
"steam-comment-bot-webserver": "file:plugins/steam-comment-bot-webserver-1.0.0.tgz",
"steam-session": "^1.2.4",
- "steam-user": "^4.28.8",
+ "steam-user": "^4.28.9",
"steamcommunity": "^3.46.1",
"steamid": "^2.0.0",
"steamid-resolver": "^1.3.3"
@@ -30,6 +30,8 @@
"homepage": "https://github.com/3urobeat",
"repository": "https://github.com/3urobeat/steam-comment-service-bot",
"devDependencies": {
+ "eslint": "^8.44.0",
+ "eslint-plugin-jsdoc": "^46.4.3",
"tsd-jsdoc": "^2.5.0"
},
"types": "./types/types.d.ts"
diff --git a/plugins/README.md b/plugins/README.md
index 98e4717c..081a80ec 100644
--- a/plugins/README.md
+++ b/plugins/README.md
@@ -5,4 +5,4 @@ This folder holds folders for all your installed plugins, which contain a config
You can also store locally packed `.tgz` packages here to install them via `npm install ./plugins/steam-comment-bot-myplugin-1.0.0.tgz` from the project root.
-To create your own plugin, fork my template: https://github.com/HerrEurobeat/steam-comment-bot-template-plugin
\ No newline at end of file
+To create your own plugin, fork my template: https://github.com/3urobeat/steam-comment-bot-template-plugin
\ No newline at end of file
diff --git a/proxies.txt b/proxies.txt
index c8e621c9..300aa211 100755
--- a/proxies.txt
+++ b/proxies.txt
@@ -1 +1 @@
-//Comment: This file is used to provide proxies to spread your accounts over multiple IPs. Read the instructions here: https://github.com/HerrEurobeat/steam-comment-service-bot/wiki/Adding-proxies
\ No newline at end of file
+//Comment: This file is used to provide proxies to spread your accounts over multiple IPs. Read the instructions here: https://github.com/3urobeat/steam-comment-service-bot/wiki/Adding-proxies
\ No newline at end of file
diff --git a/src/bot/bot.js b/src/bot/bot.js
index 3390af26..dbc7aa6f 100644
--- a/src/bot/bot.js
+++ b/src/bot/bot.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:05:34
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -28,7 +28,7 @@ const SessionHandler = require("../sessions/sessionHandler.js");
* Constructor - Initializes an object which represents a user steam account
* @class
* @param {Controller} controller Reference to the controller object
- * @param {Number} index The index of this account in the logininfo object
+ * @param {number} index The index of this account in the logininfo object
*/
const Bot = function(controller, index) {
@@ -46,7 +46,7 @@ const Bot = function(controller, index) {
/**
* Login index of this bot account
- * @type {Number}
+ * @type {number}
*/
this.index = index;
@@ -58,7 +58,7 @@ const Bot = function(controller, index) {
/**
* SteamID64's to ignore in the friendMessage event handler. This is used by readChatMessage() to prevent duplicate responses.
- * @type {Array.}
+ * @type {string[]}
*/
this.friendMessageBlock = [];
@@ -104,6 +104,7 @@ const Bot = function(controller, index) {
this.community = new SteamCommunity({ request: request.defaults({ "proxy": this.loginData.proxy }) }); // Pass proxy to community library as well
// Load my library patches
+ require("../libraryPatches/CSteamSharedFile.js");
require("../libraryPatches/profile.js");
require("../libraryPatches/sharedfiles.js");
require("../libraryPatches/helpers.js");
@@ -175,9 +176,9 @@ module.exports = Bot;
/**
* Checks if user is blocked, has an active cooldown for spamming or isn't a friend
- * @param {Object} steamID64 The steamID64 of the message sender
- * @param {String} message The message string provided by steam-user friendMessage event
- * @returns {Boolean} `true` if friendMessage event shouldn't be handled, `false` if user is allowed to be handled
+ * @param {object} steamID64 The steamID64 of the message sender
+ * @param {string} message The message string provided by steam-user friendMessage event
+ * @returns {boolean} `true` if friendMessage event shouldn't be handled, `false` if user is allowed to be handled
*/
Bot.prototype.checkMsgBlock = function(steamID64, message) {}; // eslint-disable-line
@@ -193,18 +194,18 @@ Bot.prototype.handleMissingGameLicenses = function() {};
/**
* Our commandHandler respondModule implementation - Sends a message to a Steam user
- * @param {Object} _this The Bot object context
- * @param {Object} resInfo Object containing information passed to command by friendMessage event
- * @param {String} txt The text to send
- * @param {Boolean} retry Internal: true if this message called itself again to send failure message
- * @param {Number} part Internal: Index of which part to send for messages larger than 750 chars
+ * @param {object} _this The Bot object context
+ * @param {object} resInfo Object containing information passed to command by friendMessage event
+ * @param {string} txt The text to send
+ * @param {boolean} retry Internal: true if this message called itself again to send failure message
+ * @param {number} part Internal: Index of which part to send for messages larger than 750 chars
*/
Bot.prototype.sendChatMessage = function(_this, resInfo, txt, retry, part = 0) {}; // eslint-disable-line
/**
* Waits for a Steam Chat message from this user to this account and resolves their message content. The "normal" friendMessage event handler will be blocked for this user.
- * @param {String} steamID64 The steamID64 of the user to read a message from
- * @param {Number} timeout Time in ms after which the Promise will be resolved if user does not respond. Pass 0 to disable (not recommended)
+ * @param {string} steamID64 The steamID64 of the user to read a message from
+ * @param {number} timeout Time in ms after which the Promise will be resolved if user does not respond. Pass 0 to disable (not recommended)
* @returns {Promise.} Resolved with `String` on response or `null` on timeout.
*/
Bot.prototype.readChatMessage = function(steamID64, timeout) {}; // eslint-disable-line
\ No newline at end of file
diff --git a/src/bot/helpers/checkMsgBlock.js b/src/bot/helpers/checkMsgBlock.js
index 42d38fce..195900cb 100644
--- a/src/bot/helpers/checkMsgBlock.js
+++ b/src/bot/helpers/checkMsgBlock.js
@@ -4,7 +4,7 @@
* Created Date: 20.03.2023 12:46:47
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 17:55:45
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -22,9 +22,9 @@ const lastmessage = {}; // Tracks the last cmd usage of a normal command to appl
/**
* Checks if user is blocked, has an active cooldown for spamming or isn't a friend
- * @param {Object} steamID64 The steamID64 of the message sender
- * @param {String} message The message string provided by steam-user friendMessage event
- * @returns {Boolean} `true` if friendMessage event shouldn't be handled, `false` if user is allowed to be handled
+ * @param {object} steamID64 The steamID64 of the message sender
+ * @param {string} message The message string provided by steam-user friendMessage event
+ * @returns {boolean} `true` if friendMessage event shouldn't be handled, `false` if user is allowed to be handled
*/
Bot.prototype.checkMsgBlock = function(steamID64, message) {
diff --git a/src/bot/helpers/steamChatInteraction.js b/src/bot/helpers/steamChatInteraction.js
index 130b01ee..7b052a80 100644
--- a/src/bot/helpers/steamChatInteraction.js
+++ b/src/bot/helpers/steamChatInteraction.js
@@ -4,7 +4,7 @@
* Created Date: 01.04.2023 21:09:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 17:51:07
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -29,10 +29,10 @@ const { cutStringsIntelligently } = require("../../controller/helpers/misc.js");
/**
* Our commandHandler respondModule implementation - Sends a message to a Steam user
* @param {Bot} _this The Bot object context
- * @param {Object} resInfo Object containing information passed to command by friendMessage event. Supported by this handler: prefix, charLimit, cutChars
- * @param {String} txt The text to send
- * @param {Number} retry Internal: Counter of retries for this part if sending failed
- * @param {Number} part Internal: Index of which part to send for messages larger than charLimit chars
+ * @param {object} resInfo Object containing information passed to command by friendMessage event. Supported by this handler: prefix, charLimit, cutChars
+ * @param {string} txt The text to send
+ * @param {number} retry Internal: Counter of retries for this part if sending failed
+ * @param {number} part Internal: Index of which part to send for messages larger than charLimit chars
*/
Bot.prototype.sendChatMessage = function(_this, resInfo, txt, retry = 0, part = 0) {
if (!resInfo) return logger("warn", "sendChatMessage() was called without a resInfo object! Ignoring call...");
@@ -110,8 +110,8 @@ Bot.prototype.sendChatMessage = function(_this, resInfo, txt, retry = 0, part =
/**
* Waits for a Steam Chat message from this user to this account and resolves their message content. The "normal" friendMessage event handler will be blocked for this user.
- * @param {String} steamID64 The steamID64 of the user to read a message from
- * @param {Number} timeout Time in ms after which the Promise will be resolved if user does not respond. Pass 0 to disable (not recommended)
+ * @param {string} steamID64 The steamID64 of the user to read a message from
+ * @param {number} timeout Time in ms after which the Promise will be resolved if user does not respond. Pass 0 to disable (not recommended)
* @returns {Promise.} Resolved with `String` on response or `null` on timeout.
*/
Bot.prototype.readChatMessage = function(steamID64, timeout) {
diff --git a/src/commands/commandHandler.js b/src/commands/commandHandler.js
index 11e72c15..a7c05eea 100644
--- a/src/commands/commandHandler.js
+++ b/src/commands/commandHandler.js
@@ -4,7 +4,7 @@
* Created Date: 01.04.2023 21:54:21
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 15:52:46
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -19,6 +19,26 @@ const fs = require("fs");
const Controller = require("../controller/controller.js"); // eslint-disable-line
+/**
+ * @typedef Command Documentation of the Command structure
+ * @type {object}
+ * @property {[string]} names All names that should trigger this command
+ * @property {string} description Description of what this command does
+ * @property {Array.} args Array of objects containing information about each parameter supported by this command
+ * @property {boolean} ownersOnly Set to true to only allow owners to use this command.
+ * @property {function(CommandHandler, Array, string, function(object, object, string): void, object, object): void} run Function that will be executed when the command runs. Arguments: commandHandler, args, steamID64, respondModule, context, resInfo
+ */
+
+/**
+ * @typedef CommandArg Documentation of the Command argument structure
+ * @type {object}
+ * @property {string} name Name of this argument. Use common phrases like "ID" or "amount" if possible. If a specific word is expected, put the word inside quotation marks.
+ * @property {string} description Description of this argument
+ * @property {string} type Expected datatype of this argument. If read from a chat it will usually be "string"
+ * @property {boolean} isOptional True if this argument is optional, false if it must be provided. Make sure to check for missing arguments and return an error if false.
+ * @property {boolean} ownersOnly True if this argument is only allowed to be provided by owners set in the config. If the command itself is `ownersOnly`, set this property to `true` as well.
+ */
+
/**
* Constructor - Initializes the commandHandler which allows you to integrate core commands into your plugin or add new commands from your plugin.
@@ -30,56 +50,68 @@ const CommandHandler = function(controller) {
this.controller = controller;
this.data = controller.data;
- this.commands = []; // Array of objects, where each object represents a command
+ /**
+ * Array of objects, where each object represents a registered command
+ * @type {Array.}
+ */
+ this.commands = [];
};
/**
* Internal: Imports core commands on startup
+ * @returns {Promise.} Resolved when all commands have been imported
*/
CommandHandler.prototype._importCoreCommands = function() {
+ return new Promise((resolve) => {
- logger("info", "CommandHandler: Loading all core commands...", false, true, logger.animation("loading"));
+ logger("info", "CommandHandler: Loading all core commands...", false, true, logger.animation("loading"));
- fs.readdir("./src/commands/core", (err, files) => {
+ fs.readdir("./src/commands/core", (err, files) => {
- // Stop now on error or if nothing was found
- if (err) return logger("error", "Error while reading core dir: " + err, true);
- if (files.length == 0) return logger("info", "No commands in ./core found!", false, true, logger.animation("loading"));
+ // Stop now on error or if nothing was found
+ if (err) {
+ logger("error", "Error while reading core dir: " + err, true);
+ return resolve();
+ }
+ if (files.length == 0) {
+ logger("info", "No commands in ./core found!", false, true, logger.animation("loading"));
+ return resolve();
+ }
- // Iterate over all files in this dir
- files.forEach((e, i) => {
- let thisFile;
+ // Iterate over all files in this dir
+ files.forEach((e, i) => {
+ let thisFile;
- // Try to load plugin
- try {
- // Load the plugin file
- thisFile = require(`./core/${e}`);
+ // Try to load plugin
+ try {
+ // Load the plugin file
+ thisFile = require(`./core/${e}`);
- // Push all exported commands in this file into the command list
- Object.values(thisFile).every(val => this.commands.push(val));
+ // Push all exported commands in this file into the command list
+ Object.values(thisFile).every(val => this.commands.push(val));
- } catch (err) {
+ } catch (err) {
- logger("error", `Error loading core command '${e}'! ${err.stack}`, true);
- }
+ logger("error", `Error loading core command '${e}'! ${err.stack}`, true);
+ }
- if (i + 1 == files.length) logger("info", `CommandHandler: Successfully loaded ${this.commands.length} core commands!`, false, true, logger.animation("loading"));
+ if (i + 1 == files.length) {
+ logger("info", `CommandHandler: Successfully loaded ${this.commands.length} core commands!`, false, true, logger.animation("loading"));
+ resolve();
+ }
+ });
});
- });
+ });
};
/**
* Registers a new command during runtime
- * @param {Object} command The command object to register
- * @param {[String]} command.names All names that should trigger this command
- * @param {String} command.description Description of what this command does
- * @param {Boolean} command.ownersOnly Set to true to only allow owners to use this command.
- * @param {function(CommandHandler, Array, String, function(object, object, string), Object, Object)} command.run Function that will be executed when the command runs. Arguments: commandHandler, args, steamID64, respondModule, context, resInfo
- * @returns true if the command was successfully registered, false otherwise
+ * @param {Command} command The command object to register
+ * @returns {boolean} true if the command was successfully registered, false otherwise
*/
CommandHandler.prototype.registerCommand = function(command) {
@@ -116,8 +148,8 @@ CommandHandler.prototype.registerCommand = function(command) {
/**
* The name of the command to unregister during runtime
- * @param {String} commandName Name of the command to unregister
- * @returns true if the command was successfully unregistered, false otherwise
+ * @param {string} commandName Name of the command to unregister
+ * @returns {boolean} `true` if the command was successfully unregistered, `false` otherwise
*/
CommandHandler.prototype.unregisterCommand = function(commandName) {
@@ -142,13 +174,13 @@ CommandHandler.prototype.unregisterCommand = function(commandName) {
/**
* Finds a loaded command by name and runs it
- * @param {String} name The name of the command
+ * @param {string} name The name of the command
* @param {Array} args Array of arguments that will be passed to the command
- * @param {Number} steamID64 SteamID64 of the requesting user which is used to check for ownerOnly and will be passed to the command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command). Please also include a "cmdprefix" key & value pair if your command handler uses a prefix other than "!".
- * @returns `true` if command was found, `false` if not
+ * @param {number} steamID64 SteamID64 of the requesting user which is used to check for ownerOnly and will be passed to the command
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command). Please also include a "cmdprefix" key & value pair if your command handler uses a prefix other than "!".
+ * @returns {boolean} `true` if command was found, `false` if not
*/
CommandHandler.prototype.runCommand = function(name, args, steamID64, respondModule, context, resInfo) {
diff --git a/src/commands/core/block.js b/src/commands/core/block.js
index 6061e76e..be315c5d 100644
--- a/src/commands/core/block.js
+++ b/src/commands/core/block.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 15:51:40
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -22,7 +22,16 @@ const CommandHandler = require("../commandHandler.js"); // eslint-disable-line
module.exports.block = {
names: ["block"],
- description: "",
+ description: "Blocks a user with all bot accounts on Steam",
+ args: [
+ {
+ name: "ID",
+ description: "The link, steamID64 or vanity of the profile to block",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -30,9 +39,9 @@ module.exports.block = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -58,7 +67,16 @@ module.exports.block = {
module.exports.unblock = {
names: ["unblock"],
- description: "",
+ description: "Unblocks a user with all bot accounts on Steam. Note: The user can still get ignored by Steam for a while",
+ args: [
+ {
+ name: "ID",
+ description: "The link, steamID64 or vanity of the profile to unblock",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -66,9 +84,9 @@ module.exports.unblock = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
diff --git a/src/commands/core/comment.js b/src/commands/core/comment.js
index 95bbf8f9..edde4058 100644
--- a/src/commands/core/comment.js
+++ b/src/commands/core/comment.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:49
+ * Last Modified: 07.07.2023 15:51:55
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -26,7 +26,30 @@ const { logCommentError, handleIterationSkip } = require("../helpers/handleComme
module.exports.comment = {
names: ["comment", "gcomment", "groupcomment"],
- description: "",
+ description: "Request comments from all available bot accounts for a profile, group or sharedfile",
+ args: [
+ {
+ name: "amount",
+ description: "The amount of comments to request",
+ type: "string",
+ isOptional: false,
+ ownersOnly: false
+ },
+ {
+ name: "ID",
+ description: "The link, steamID64 or vanity of the profile, group or sharedfile to comment on",
+ type: "string",
+ isOptional: true,
+ ownersOnly: true
+ },
+ {
+ name: "custom quotes",
+ description: "Array of strings to use as quotes in this comment request instead of the default quotes.txt set",
+ type: "string",
+ isOptional: true,
+ ownersOnly: true
+ }
+ ],
ownersOnly: false,
/**
@@ -34,9 +57,9 @@ module.exports.comment = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: async (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -193,11 +216,11 @@ module.exports.comment = {
/**
* Internal: Do the actual commenting, activeRequests entry with all relevant information was processed by the comment command function above.
* @param {CommandHandler} commandHandler The commandHandler object
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
- * @param {function(string)} respond Shortened respondModule call
- * @param {function} postComment The correct postComment function for this idType. Context from the correct bot account is being applied later.
- * @param {Object} commentArgs All arguments this postComment function needs, without callback. It will be applied and a callback added as last param. Include a key called "quote" to dynamically replace it with a random quote.
- * @param {String} receiverSteamID64 steamID64 of the profile to receive the comments
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(string): void} respond The shortened respondModule call
+ * @param {Function} postComment The correct postComment function for this idType. Context from the correct bot account is being applied later.
+ * @param {object} commentArgs All arguments this postComment function needs, without callback. It will be applied and a callback added as last param. Include a key called "quote" to dynamically replace it with a random quote.
+ * @param {string} receiverSteamID64 steamID64 of the profile to receive the comments
*/
function comment(commandHandler, resInfo, respond, postComment, commentArgs, receiverSteamID64) {
let activeReqEntry = commandHandler.controller.activeRequests[receiverSteamID64]; // Make using the obj shorter
diff --git a/src/commands/core/favorite.js b/src/commands/core/favorite.js
index 899601f4..b5640242 100644
--- a/src/commands/core/favorite.js
+++ b/src/commands/core/favorite.js
@@ -4,7 +4,7 @@
* Created Date: 02.06.2023 13:23:01
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:49
+ * Last Modified: 07.07.2023 15:53:13
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -24,7 +24,23 @@ const { handleFavoriteIterationSkip, logFavoriteError } = require("../helpers/ha
module.exports.favorite = {
names: ["favorite", "fav"],
- description: "Favorites a sharedfile",
+ description: "Favorizes a sharedfile with all bot accounts that haven't yet favorized that item",
+ args: [
+ {
+ name: "amount",
+ description: "The amount of favorites to request",
+ type: "string",
+ isOptional: false,
+ ownersOnly: false
+ },
+ {
+ name: "ID",
+ description: "The link or sharedfile ID to vote on",
+ type: "string",
+ isOptional: false,
+ ownersOnly: false
+ }
+ ],
ownersOnly: false,
/**
@@ -32,7 +48,7 @@ module.exports.favorite = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
* @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
* @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
@@ -191,7 +207,23 @@ module.exports.favorite = {
module.exports.unfavorite = {
names: ["unfavorite", "unfav"],
- description: "Unfavorites a sharedfile",
+ description: "Unfavorizes a sharedfile with all bot accounts that have favorized that item",
+ args: [
+ {
+ name: "amount",
+ description: "The amount of favorites to request",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ },
+ {
+ name: "ID",
+ description: "The link or sharedfile ID to vote on",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -199,7 +231,7 @@ module.exports.unfavorite = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
* @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
* @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
diff --git a/src/commands/core/friend.js b/src/commands/core/friend.js
index 3be5b164..f4dc85df 100644
--- a/src/commands/core/friend.js
+++ b/src/commands/core/friend.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 15:57:36
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -22,7 +22,16 @@ const CommandHandler = require("../commandHandler.js"); // eslint-disable-line
module.exports.addFriend = {
names: ["addfriend"],
- description: "",
+ description: "Adds the ID with all bot accounts. Requires unlimited accounts!",
+ args: [
+ {
+ name: "ID",
+ description: "The link, steamID64 or vanity of the profile to add",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -30,9 +39,9 @@ module.exports.addFriend = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -82,7 +91,16 @@ module.exports.addFriend = {
module.exports.unfriend = {
names: ["unfriend"],
- description: "",
+ description: "Unfriends a user from all logged in accounts. (Owner only) Providing no argument will let all bots unfriend you. (Available to normal users)",
+ args: [
+ {
+ name: "ID",
+ description: "The link, steamID64 or vanity of the profile to unfriend",
+ type: "string",
+ isOptional: true, // If not provided all bots will unfriend the requesting user
+ ownersOnly: false
+ }
+ ],
ownersOnly: false,
/**
@@ -90,9 +108,9 @@ module.exports.unfriend = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -135,7 +153,16 @@ module.exports.unfriend = {
module.exports.unfriendall = {
names: ["unfriendall"],
- description: "",
+ description: "Unfriends everyone with all bot accounts",
+ args: [
+ {
+ name: '"abort"',
+ description: "Aborts a unfriendall request if it did not start yet",
+ type: "string",
+ isOptional: true,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -143,9 +170,9 @@ module.exports.unfriendall = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
diff --git a/src/commands/core/general.js b/src/commands/core/general.js
index 12a13775..f730b959 100644
--- a/src/commands/core/general.js
+++ b/src/commands/core/general.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 11:25:17
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -17,15 +17,15 @@
// General commands
-
-const https = require("https");
+const https = require("https");
const CommandHandler = require("../commandHandler.js"); // eslint-disable-line
module.exports.help = {
names: ["h", "help", "commands"],
- description: "",
+ description: "Returns a list of commands available to you and a link to the commands documentation wiki page",
+ args: [],
ownersOnly: false,
/**
@@ -33,9 +33,9 @@ module.exports.help = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -75,7 +75,8 @@ module.exports.help = {
module.exports.info = {
names: ["info"],
- description: "",
+ description: "Returns useful information and statistics about the bot and you",
+ args: [],
ownersOnly: false,
/**
@@ -83,9 +84,9 @@ module.exports.info = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -118,7 +119,8 @@ module.exports.info = {
module.exports.ping = {
names: ["ping", "pong"],
- description: "Pings SteamCommunity and measures the time it took.",
+ description: "Returns ping in ms to Steam's servers. Can be used to check if the bot is responsive",
+ args: [],
ownersOnly: false,
/**
@@ -126,9 +128,9 @@ module.exports.ping = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -145,7 +147,8 @@ module.exports.ping = {
module.exports.about = {
names: ["about"],
- description: "",
+ description: "Displays information about this project. The message also contains a disclaimer as well as a link to the owner's profile set in the config.json",
+ args: [],
ownersOnly: false,
/**
@@ -153,9 +156,9 @@ module.exports.about = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -167,7 +170,8 @@ module.exports.about = {
module.exports.owner = {
names: ["owner"],
- description: "",
+ description: "Returns a link to the owner's profile set in the config.json",
+ args: [],
ownersOnly: false,
/**
@@ -175,9 +179,9 @@ module.exports.owner = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -193,7 +197,8 @@ module.exports.owner = {
// Test Command for debugging
module.exports.test = {
names: ["test"],
- description: "",
+ description: "Test Command for debugging",
+ args: [],
ownersOnly: true,
/**
@@ -201,14 +206,14 @@ module.exports.test = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: async (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // eslint-disable-line
- // Do not remove, these are handleSteamIdResolving test cases. Might be useful to include later in steamid-resolving lib test suite
+ /* // Do not remove, these are handleSteamIdResolving test cases. Might be useful to include later in steamid-resolving lib test suite
let handleSteamIdResolving = commandHandler.controller.handleSteamIdResolving;
// With type param
@@ -229,6 +234,6 @@ module.exports.test = {
// We already provide the correct id
handleSteamIdResolving("76561198260031749", null, console.log);
- handleSteamIdResolving("103582791464712227", null, console.log);
+ handleSteamIdResolving("103582791464712227", null, console.log); */
}
};
diff --git a/src/commands/core/group.js b/src/commands/core/group.js
index da38b241..61da6386 100644
--- a/src/commands/core/group.js
+++ b/src/commands/core/group.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 16:01:25
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -22,7 +22,8 @@ const CommandHandler = require("../commandHandler.js"); // eslint-disable-line
module.exports.group = {
names: ["group"],
- description: "",
+ description: "Sends an invite or responds with the group link set as yourgroup in the config",
+ args: [],
ownersOnly: false,
/**
@@ -30,9 +31,9 @@ module.exports.group = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -57,6 +58,15 @@ module.exports.group = {
module.exports.joinGroup = {
names: ["joingroup"],
description: "Joins a Steam Group with all bot accounts",
+ args: [
+ {
+ name: "ID",
+ description: "The link or groupID64 of the group to join",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -64,9 +74,9 @@ module.exports.joinGroup = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -93,7 +103,16 @@ module.exports.joinGroup = {
module.exports.leaveGroup = {
names: ["leavegroup"],
- description: "",
+ description: "Leaves a group with all bot accounts",
+ args: [
+ {
+ name: "ID",
+ description: "The link or groupID64 of the group to leave",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -101,9 +120,9 @@ module.exports.leaveGroup = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -130,7 +149,16 @@ module.exports.leaveGroup = {
module.exports.leaveAllGroups = {
names: ["leaveallgroups"],
- description: "",
+ description: "Leaves all groups with all bot accounts",
+ args: [
+ {
+ name: '"abort"',
+ description: "Aborts a leaveallgroups request if it did not start yet",
+ type: "string",
+ isOptional: true,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -138,9 +166,9 @@ module.exports.leaveAllGroups = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
diff --git a/src/commands/core/requests.js b/src/commands/core/requests.js
index 35285247..e8ccd349 100644
--- a/src/commands/core/requests.js
+++ b/src/commands/core/requests.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 15:58:36
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -21,7 +21,16 @@ const { failedCommentsObjToString } = require("../helpers/handleCommentSkips.js"
module.exports.abort = {
names: ["abort"],
- description: "",
+ description: "Abort your own comment process or one on another ID you have started. Owners can also abort requests started by other users",
+ args: [
+ {
+ name: "ID",
+ description: "The link, steamID64 or vanity of the profile, group or sharedfile to abort the request of",
+ type: "string",
+ isOptional: true,
+ ownersOnly: false // Providing an ID for a request of another user is ownerOnly
+ },
+ ],
ownersOnly: false,
/**
@@ -29,9 +38,9 @@ module.exports.abort = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -62,7 +71,16 @@ module.exports.abort = {
module.exports.resetCooldown = {
names: ["resetcooldown", "rc"],
- description: "",
+ description: "Clear your, the ID's or the comment cooldown of all bot accounts (global)",
+ args: [
+ {
+ name: 'ID or "global"',
+ description: "The link, steamID64 or vanity of the profile to clear the cooldown of or the word global to clear the cooldown of all bot accounts",
+ type: "string",
+ isOptional: true,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -70,9 +88,9 @@ module.exports.resetCooldown = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -106,7 +124,16 @@ module.exports.resetCooldown = {
module.exports.failed = {
names: ["failed"],
- description: "",
+ description: "See the exact errors of the last comment request on your profile or provide an ID to see the errors of the last request you started. Owners can also view errors for requests started by other users",
+ args: [
+ {
+ name: "ID",
+ description: "The link, steamID64 or vanity of the profile, group or sharedfile to view the errors of",
+ type: "string",
+ isOptional: true,
+ ownersOnly: false // Providing an ID for a request of another user is ownerOnly
+ }
+ ],
ownersOnly: false,
/**
@@ -114,9 +141,9 @@ module.exports.failed = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -152,7 +179,8 @@ module.exports.failed = {
module.exports.sessions = {
names: ["sessions"],
- description: "",
+ description: "Displays all active requests",
+ args: [],
ownersOnly: true,
/**
@@ -160,9 +188,9 @@ module.exports.sessions = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
@@ -196,7 +224,8 @@ module.exports.sessions = {
module.exports.mySessions = {
names: ["mysessions"],
- description: "",
+ description: "Displays all active requests that you have started",
+ args: [],
ownersOnly: false,
/**
@@ -204,9 +233,9 @@ module.exports.mySessions = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
diff --git a/src/commands/core/settings.js b/src/commands/core/settings.js
index b40db996..229acd66 100644
--- a/src/commands/core/settings.js
+++ b/src/commands/core/settings.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 15:59:10
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -22,7 +22,23 @@ const CommandHandler = require("../commandHandler.js"); // eslint-disable-line
module.exports.settings = {
names: ["set", "settings", "config"],
- description: "",
+ description: "Change a value in the config",
+ args: [
+ {
+ name: "config key",
+ description: "Name of the config key to update",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ },
+ {
+ name: "new value",
+ description: "New value of the config key",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -30,9 +46,9 @@ module.exports.settings = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
diff --git a/src/commands/core/system.js b/src/commands/core/system.js
index 8674dd95..30d9ed5a 100644
--- a/src/commands/core/system.js
+++ b/src/commands/core/system.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 15:59:30
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -22,7 +22,8 @@ const CommandHandler = require("../commandHandler.js"); // eslint-disable-line
module.exports.restart = {
names: ["restart", "rs"],
- description: "",
+ description: "Restarts the bot and checks for available updates",
+ args: [],
ownersOnly: true,
/**
@@ -30,9 +31,9 @@ module.exports.restart = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
respondModule(context, { prefix: "/me", ...resInfo }, commandHandler.data.lang.restartcmdrestarting); // Pass new resInfo object which contains prefix and everything the original resInfo obj contained
@@ -44,7 +45,8 @@ module.exports.restart = {
module.exports.stop = {
names: ["stop"],
- description: "",
+ description: "Stops the bot",
+ args: [],
ownersOnly: true,
/**
@@ -52,9 +54,9 @@ module.exports.stop = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
respondModule(context, { prefix: "/me", ...resInfo }, commandHandler.data.lang.stopcmdstopping); // Pass new resInfo object which contains prefix and everything the original resInfo obj contained
@@ -66,7 +68,8 @@ module.exports.stop = {
module.exports.reload = {
names: ["reload", "rl"],
- description: "Reloads all commands and plugins without needing to restart. Please only use it for testing, this isn't clean.",
+ description: "Reloads all commands and plugins without needing to restart. Please only use it for testing/development",
+ args: [],
ownersOnly: true,
/**
@@ -74,9 +77,9 @@ module.exports.reload = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
@@ -95,7 +98,16 @@ module.exports.reload = {
module.exports.update = {
names: ["update"],
- description: "",
+ description: "Checks for an available update and installs it if automatic updates are enabled and no requests are active. 'true' forces an update. Blocks new requests if it currently waits for one to be completed",
+ args: [
+ {
+ name: '"true"',
+ description: "Forces an update",
+ type: "string",
+ isOptional: true,
+ ownersOnly: true
+ },
+ ],
ownersOnly: true,
/**
@@ -103,9 +115,9 @@ module.exports.update = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((modResInfo, txt) => respondModule(context, modResInfo, txt)); // Shorten each call. Updater takes resInfo as param and can modify it, so we need to pass the modified resInfo object here
@@ -125,7 +137,8 @@ module.exports.update = {
module.exports.output = {
names: ["output", "log"],
- description: "",
+ description: "Shows the last 15 lines of the log",
+ args: [],
ownersOnly: true,
/**
@@ -133,9 +146,9 @@ module.exports.output = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
fs.readFile("./output.txt", function (err, data) {
@@ -150,7 +163,16 @@ module.exports.output = {
module.exports.eval = {
names: ["eval"],
- description: "",
+ description: "Disabled by default, needs to be toggled on with `enableevalcmd` in config.json. **Warning!** This will run any javascript code that was provided. It is strongly advised to leave this feature off unless you know exactly what this means! If you have multiple owners configured they can also run code on **your** machine!",
+ args: [
+ {
+ name: "javascript code",
+ description: "The code to run",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true,
/**
@@ -158,9 +180,9 @@ module.exports.eval = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
diff --git a/src/commands/core/vote.js b/src/commands/core/vote.js
index f297870d..eac39433 100644
--- a/src/commands/core/vote.js
+++ b/src/commands/core/vote.js
@@ -4,7 +4,7 @@
* Created Date: 28.05.2023 12:02:24
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 15:59:45
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -24,7 +24,23 @@ const { handleVoteIterationSkip, logVoteError } = require("../helpers/handleShar
module.exports.upvote = {
names: ["upvote"],
- description: "Upvotes a sharedfile",
+ description: "Upvotes a sharedfile with all bot accounts that haven't yet voted on that item. Requires unlimited accounts!",
+ args: [
+ {
+ name: "amount",
+ description: "The amount of upvotes to request",
+ type: "string",
+ isOptional: false,
+ ownersOnly: false
+ },
+ {
+ name: "ID",
+ description: "The link or sharedfile ID to vote on",
+ type: "string",
+ isOptional: false,
+ ownersOnly: false
+ }
+ ],
ownersOnly: false,
/**
@@ -32,7 +48,7 @@ module.exports.upvote = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
* @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
* @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
@@ -196,7 +212,23 @@ module.exports.upvote = {
module.exports.downvote = {
names: ["downvote"],
- description: "Downvotes a sharedfile",
+ description: "Downvotes a sharedfile with all bot accounts that haven't yet voted on that item. Requires unlimited accounts!",
+ args: [
+ {
+ name: "amount",
+ description: "The amount of downvotes to request",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ },
+ {
+ name: "ID",
+ description: "The link or sharedfile ID to vote on",
+ type: "string",
+ isOptional: false,
+ ownersOnly: true
+ }
+ ],
ownersOnly: true, // I would like to prevent users from abusing this feature to dislike other peoples creations
/**
@@ -204,9 +236,9 @@ module.exports.downvote = {
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args Array of arguments that will be passed to the command
* @param {string} steamID64 Steam ID of the user that executed this command
- * @param {function(object, object, string)} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
- * @param {Object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, object, string): void} respondModule Function that will be called to respond to the user's request. Passes context, resInfo and txt as parameters.
+ * @param {object} context The context (this.) of the object calling this command. Will be passed to respondModule() as first parameter.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
*/
run: async (commandHandler, args, steamID64, respondModule, context, resInfo) => {
let respond = ((txt) => respondModule(context, resInfo, txt)); // Shorten each call
diff --git a/src/commands/helpers/getCommentArgs.js b/src/commands/helpers/getCommentArgs.js
index 0ab9b001..36c9c435 100644
--- a/src/commands/helpers/getCommentArgs.js
+++ b/src/commands/helpers/getCommentArgs.js
@@ -4,7 +4,7 @@
* Created Date: 28.02.2022 11:55:06
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:30:48
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
@@ -22,10 +22,10 @@ const CommandHandler = require("../commandHandler.js"); // eslint-disable-line
* Retrieves arguments from a comment request. If request is invalid (for example too many comments requested) an error message will be sent
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args The command arguments
- * @param {String} requesterSteamID64 The steamID64 of the requesting user
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
- * @param {Function} respond The function to send messages to the requesting user
- * @returns {Promise.<{ maxRequestAmount: number, commentcmdUsage: string, numberOfComments: number, profileID: string, idType: string, quotesArr: array }>}
+ * @param {string} requesterSteamID64 The steamID64 of the requesting user
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(string): void} respond The shortened respondModule call
+ * @returns {Promise.<{ maxRequestAmount: number, commentcmdUsage: string, numberOfComments: number, profileID: string, idType: string, quotesArr: Array. }>} Resolves promise with object containing all relevant data when done
*/
module.exports.getCommentArgs = (commandHandler, args, requesterSteamID64, resInfo, respond) => {
return new Promise((resolve) => {
diff --git a/src/commands/helpers/getCommentBots.js b/src/commands/helpers/getCommentBots.js
index a8183336..f0bc3bc4 100644
--- a/src/commands/helpers/getCommentBots.js
+++ b/src/commands/helpers/getCommentBots.js
@@ -4,7 +4,7 @@
* Created Date: 09.04.2023 12:49:53
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:30:59
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -22,11 +22,11 @@ const { timeToString } = require("../../controller/helpers/misc.js");
/**
* Finds all needed and currently available bot accounts for a comment request.
* @param {CommandHandler} commandHandler The commandHandler object
- * @param {Number} numberOfComments Number of requested comments
- * @param {Boolean} canBeLimited If the accounts are allowed to be limited
- * @param {String} idType Type of the request. This can either be "profile", "group" or "sharedfile". This is used to determine if limited accs need to be added first.
- * @param {String} receiverSteamID Optional: steamID64 of the receiving user. If set, accounts that are friend with the user will be prioritized and accsToAdd will be calculated.
- * @returns {{ accsNeeded: number, availableAccounts: array., accsToAdd: array., whenAvailable: number, whenAvailableStr: string }} `availableAccounts` contains all account names from bot object, `accsToAdd` account names which are limited and not friend, `whenAvailable` is a timestamp representing how long to wait until accsNeeded accounts will be available and `whenAvailableStr` is formatted human-readable as time from now
+ * @param {number} numberOfComments Number of requested comments
+ * @param {boolean} canBeLimited If the accounts are allowed to be limited
+ * @param {string} idType Type of the request. This can either be "profile", "group" or "sharedfile". This is used to determine if limited accs need to be added first.
+ * @param {string} receiverSteamID Optional: steamID64 of the receiving user. If set, accounts that are friend with the user will be prioritized and accsToAdd will be calculated.
+ * @returns {{ accsNeeded: number, availableAccounts: Array., accsToAdd: Array., whenAvailable: number, whenAvailableStr: string }} `availableAccounts` contains all account names from bot object, `accsToAdd` account names which are limited and not friend, `whenAvailable` is a timestamp representing how long to wait until accsNeeded accounts will be available and `whenAvailableStr` is formatted human-readable as time from now
*/
module.exports.getAvailableBotsForCommenting = function(commandHandler, numberOfComments, canBeLimited, idType, receiverSteamID = null) {
diff --git a/src/commands/helpers/getFavoriteBots.js b/src/commands/helpers/getFavoriteBots.js
index 689371c2..d2d7c9c7 100644
--- a/src/commands/helpers/getFavoriteBots.js
+++ b/src/commands/helpers/getFavoriteBots.js
@@ -4,7 +4,7 @@
* Created Date: 02.06.2023 14:07:27
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:31:26
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -25,80 +25,76 @@ const { timeToString } = require("../../controller/helpers/misc.js");
* @param {number|"all"} amount Amount of favs requested or "all" to get the max available amount
* @param {string} id The sharedfile id to favorize
* @param {string} favType Either "favorite" or "unfavorite", depending on which request this is
- * @returns {Promise.<{ amount: number, availableAccounts: array, whenAvailable: number, whenAvailableStr: string }>} Promise with obj: `availableAccounts` contains all account names from bot object, `whenAvailable` is a timestamp representing how long to wait until accsNeeded accounts will be available and `whenAvailableStr` is formatted human-readable as time from now
+ * @returns {Promise.<{ amount: number, availableAccounts: Array., whenAvailable: number, whenAvailableStr: string }>} Resolves with obj: `availableAccounts` contains all account names from bot object, `whenAvailable` is a timestamp representing how long to wait until accsNeeded accounts will be available and `whenAvailableStr` is formatted human-readable as time from now
*/
-module.exports.getAvailableBotsForFavorizing = (commandHandler, amount, id, favType) => {
- return new Promise((resolve) => {
- (async () => { // Lets us use await inside a Promise without creating an antipattern
+module.exports.getAvailableBotsForFavorizing = async (commandHandler, amount, id, favType) => {
- /* --------- Get all bots which haven't favorized this id yet and aren't currently in another favorite request --------- */
- let whenAvailable; // We will save the until value of the account that the user has to wait for here
- let whenAvailableStr;
- let allAccsOnline = commandHandler.controller.getBots(null, true);
- let allAccounts = [ ... Object.keys(allAccsOnline) ]; // Clone keys array (bot usernames) of bots object
+ /* --------- Get all bots which haven't favorized this id yet and aren't currently in another favorite request --------- */
+ let whenAvailable; // We will save the until value of the account that the user has to wait for here
+ let whenAvailableStr;
+ let allAccsOnline = commandHandler.controller.getBots(null, true);
+ let allAccounts = [ ... Object.keys(allAccsOnline) ]; // Clone keys array (bot usernames) of bots object
- // Remove bot accounts from allAccounts which have already favorized this id, or only allow them for type unfavorite
- let previousLengthFavorized = allAccounts.length;
- let alreadyFavorized = await commandHandler.data.ratingHistoryDB.findAsync({ id: id, type: "favorite" }, {});
+ // Remove bot accounts from allAccounts which have already favorized this id, or only allow them for type unfavorite
+ let previousLengthFavorized = allAccounts.length;
+ let alreadyFavorized = await commandHandler.data.ratingHistoryDB.findAsync({ id: id, type: "favorite" }, {});
- if (favType == "favorite") {
- alreadyFavorized.forEach((e) => {
- if (allAccounts.indexOf(e.accountName) != -1) allAccounts.splice(allAccounts.indexOf(e.accountName), 1); // Remove all accounts that already favorized
- });
- } else {
- allAccounts.forEach((e) => {
- if (!alreadyFavorized.some(e => e.accountName)) allAccounts.splice(allAccounts.indexOf(e), 1); // Remove all accounts that have not favorized
- });
- }
+ if (favType == "favorite") {
+ alreadyFavorized.forEach((e) => {
+ if (allAccounts.indexOf(e.accountName) != -1) allAccounts.splice(allAccounts.indexOf(e.accountName), 1); // Remove all accounts that already favorized
+ });
+ } else {
+ allAccounts.forEach((e) => {
+ if (!alreadyFavorized.some(e => e.accountName)) allAccounts.splice(allAccounts.indexOf(e), 1); // Remove all accounts that have not favorized
+ });
+ }
- if (previousLengthFavorized - allAccounts.length > 0) logger("info", `${previousLengthFavorized - allAccounts.length} of ${previousLengthFavorized} bot accounts were removed from available accounts because we know that they have already favorized this item!`);
+ if (previousLengthFavorized - allAccounts.length > 0) logger("info", `${previousLengthFavorized - allAccounts.length} of ${previousLengthFavorized} bot accounts were removed from available accounts because we know that they have already favorized this item!`);
- // Loop over activeRequests and remove all active entries from allAccounts if both are not empty
- if (allAccounts.length > 0 && Object.keys(commandHandler.controller.activeRequests).length > 0) {
- Object.keys(commandHandler.controller.activeRequests).forEach((e) => {
- if (!commandHandler.controller.activeRequests[e].type.includes("favorite")) return; // Ignore entry if not of this type
+ // Loop over activeRequests and remove all active entries from allAccounts if both are not empty
+ if (allAccounts.length > 0 && Object.keys(commandHandler.controller.activeRequests).length > 0) {
+ Object.keys(commandHandler.controller.activeRequests).forEach((e) => {
+ if (!commandHandler.controller.activeRequests[e].type.includes("favorite")) return; // Ignore entry if not of this type
- if (Date.now() < commandHandler.controller.activeRequests[e].until + (commandHandler.data.config.botaccountcooldown * 60000)) { // Check if entry is not finished yet
- commandHandler.controller.activeRequests[e].accounts.forEach((f) => { // Loop over every account used in this request
- allAccounts.splice(allAccounts.indexOf(f), 1); // Remove that accountindex from the allAccounts array
- });
-
- // If this removal causes the user to need to wait, update whenAvailable. Don't bother if user provided "all"
- if (amount != "all" && allAccounts.length - commandHandler.controller.activeRequests[e].accounts.length < amount) {
- whenAvailable = commandHandler.controller.activeRequests[e].until + (commandHandler.data.config.botaccountcooldown * 60000);
- whenAvailableStr = timeToString(whenAvailable);
- }
- } else {
- delete commandHandler.controller.activeRequests[e]; // Remove entry from object if it is finished to keep the object clean
- }
+ if (Date.now() < commandHandler.controller.activeRequests[e].until + (commandHandler.data.config.botaccountcooldown * 60000)) { // Check if entry is not finished yet
+ commandHandler.controller.activeRequests[e].accounts.forEach((f) => { // Loop over every account used in this request
+ allAccounts.splice(allAccounts.indexOf(f), 1); // Remove that accountindex from the allAccounts array
});
+
+ // If this removal causes the user to need to wait, update whenAvailable. Don't bother if user provided "all"
+ if (amount != "all" && allAccounts.length - commandHandler.controller.activeRequests[e].accounts.length < amount) {
+ whenAvailable = commandHandler.controller.activeRequests[e].until + (commandHandler.data.config.botaccountcooldown * 60000);
+ whenAvailableStr = timeToString(whenAvailable);
+ }
+ } else {
+ delete commandHandler.controller.activeRequests[e]; // Remove entry from object if it is finished to keep the object clean
}
+ });
+ }
- // Update amount if "all"
- if (amount == "all") {
- amount = allAccounts.length;
- logger("debug", `CommandHandler getFavoriteBots(): User provided max amount keyword "all", updating it to ${allAccounts.length}`);
- }
+ // Update amount if "all"
+ if (amount == "all") {
+ amount = allAccounts.length;
+ logger("debug", `CommandHandler getFavoriteBots(): User provided max amount keyword "all", updating it to ${allAccounts.length}`);
+ }
- // Cut result to only include needed accounts
- if (allAccounts.length > amount) allAccounts = allAccounts.slice(0, amount);
+ // Cut result to only include needed accounts
+ if (allAccounts.length > amount) allAccounts = allAccounts.slice(0, amount);
- // Log result to debug
- if (allAccounts.length < amount) logger("debug", `CommandHandler getFavoriteBots(): Found ${allAccounts.length} available bot accounts to un-/favorize ${id} but ${amount} are needed. If accs will become available, the user needs to wait: ${whenAvailableStr || "/"}`);
- else logger("debug", `CommandHandler getFavoriteBots(): Found ${allAccounts.length} available bot accounts to un-/favorize ${id}: ${allAccounts}`);
+ // Log result to debug
+ if (allAccounts.length < amount) logger("debug", `CommandHandler getFavoriteBots(): Found ${allAccounts.length} available bot accounts to un-/favorize ${id} but ${amount} are needed. If accs will become available, the user needs to wait: ${whenAvailableStr || "/"}`);
+ else logger("debug", `CommandHandler getFavoriteBots(): Found ${allAccounts.length} available bot accounts to un-/favorize ${id}: ${allAccounts}`);
- // Resolve promise with values
- resolve({
- "amount": amount,
- "availableAccounts": allAccounts,
- "whenAvailable": whenAvailable,
- "whenAvailableStr": whenAvailableStr
- });
- })();
- });
+ // Resolve promise with values
+ return {
+ "amount": amount,
+ "availableAccounts": allAccounts,
+ "whenAvailable": whenAvailable,
+ "whenAvailableStr": whenAvailableStr
+ };
};
\ No newline at end of file
diff --git a/src/commands/helpers/getSharedfileArgs.js b/src/commands/helpers/getSharedfileArgs.js
index a8d88272..038fe00e 100644
--- a/src/commands/helpers/getSharedfileArgs.js
+++ b/src/commands/helpers/getSharedfileArgs.js
@@ -4,7 +4,7 @@
* Created Date: 28.05.2023 12:18:49
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:31:41
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -22,47 +22,44 @@ const CommandHandler = require("../commandHandler.js"); // eslint-disable-line
* Retrieves arguments from a vote request. If request is invalid, an error message will be sent
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Array} args The command arguments
- * @param {String} cmd Either "upvote", "downvote", "favorite" or "unfavorite", depending on which command is calling this function
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
- * @param {Function} respond The function to send messages to the requesting user
+ * @param {string} cmd Either "upvote", "downvote", "favorite" or "unfavorite", depending on which command is calling this function
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(string): void} respond The shortened respondModule call
* @returns {Promise.<{ amount: number|string, id: string }>} If the user provided a specific amount, amount will be a number. If user provided "all" or "max", it will be returned as an unmodified string for getVoteBots.js to handle
*/
module.exports.getSharedfileArgs = (commandHandler, args, cmd, resInfo, respond) => {
return new Promise((resolve) => {
- (async () => { // Lets us use await inside a Promise without creating an antipattern
- // Check for missing params
- let voteCmdUsage = `'${resInfo.cmdprefix}${cmd} amount/"all" id/link'`;
+ // Check for missing params
+ let voteCmdUsage = `'${resInfo.cmdprefix}${cmd} amount/"all" id/link'`;
- if (args[0]) args[0] = args[0].toLowerCase();
- if (args[0] == "max") args[0] = "all"; // Convert "all" alias
- let amount = args[0] == "all" ? args[0] : Number(args[0]); // If user provides "all" then keep it as is and update it later to how many accounts are available, otherwise convert it to a number
+ if (args[0]) args[0] = args[0].toLowerCase();
+ if (args[0] == "max") args[0] = "all"; // Convert "all" alias
+ let amount = args[0] == "all" ? args[0] : Number(args[0]); // If user provides "all" then keep it as is and update it later to how many accounts are available, otherwise convert it to a number
- if (args.length == 0 || (amount != "all" && isNaN(amount)) || amount == 0) {
- respond(commandHandler.data.lang.invalidnumber.replace("cmdusage", voteCmdUsage)); // An empty string will become a 0
- return resolve({});
- }
-
-
- // Process input and check if ID is valid
- commandHandler.controller.handleSteamIdResolving(args[1], "sharedfile", (err, id, idType) => { // eslint-disable-line no-unused-vars
+ if (args.length == 0 || (amount != "all" && isNaN(amount)) || amount == 0) {
+ respond(commandHandler.data.lang.invalidnumber.replace("cmdusage", voteCmdUsage)); // An empty string will become a 0
+ return resolve({});
+ }
- // Send error if item could not be found
- if (err || !id) {
- respond(commandHandler.data.lang.invalidsharedfileid.replace("cmdusage", voteCmdUsage));
- return resolve({});
- }
+ // Process input and check if ID is valid
+ commandHandler.controller.handleSteamIdResolving(args[1], "sharedfile", (err, id, idType) => { // eslint-disable-line no-unused-vars
- // ...otherwise resolve
- logger("debug", `CommandHandler getSharedfileArgs() success. amount: ${amount} | id: ${id}`);
+ // Send error if item could not be found
+ if (err || !id) {
+ respond(commandHandler.data.lang.invalidsharedfileid.replace("cmdusage", voteCmdUsage));
+ return resolve({});
+ }
- resolve({
- "amountRaw": amount,
- "id": id
- });
+ // ...otherwise resolve
+ logger("debug", `CommandHandler getSharedfileArgs() success. amount: ${amount} | id: ${id}`);
+ resolve({
+ "amountRaw": amount,
+ "id": id
});
- })();
+ });
+
});
};
\ No newline at end of file
diff --git a/src/commands/helpers/getVoteBots.js b/src/commands/helpers/getVoteBots.js
index 30188094..d2a40e74 100644
--- a/src/commands/helpers/getVoteBots.js
+++ b/src/commands/helpers/getVoteBots.js
@@ -4,7 +4,7 @@
* Created Date: 28.05.2023 12:21:02
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:31:54
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -25,80 +25,77 @@ const { timeToString } = require("../../controller/helpers/misc.js");
* @param {number|"all"} amount Amount of votes requested or "all" to get the max available amount
* @param {string} id The sharedfile id to vote on
* @param {string} voteType "upvote" or "downvote", depending on which request this is
- * @returns {Promise.<{ amount: number, availableAccounts: array, whenAvailable: number, whenAvailableStr: string }>} Promise with obj: `availableAccounts` contains all account names from bot object, `whenAvailable` is a timestamp representing how long to wait until accsNeeded accounts will be available and `whenAvailableStr` is formatted human-readable as time from now
+ * @returns {Promise.<{ amount: number, availableAccounts: Array., whenAvailable: number, whenAvailableStr: string }>} Resolves with obj: `availableAccounts` contains all account names from bot object, `whenAvailable` is a timestamp representing how long to wait until accsNeeded accounts will be available and `whenAvailableStr` is formatted human-readable as time from now
*/
-module.exports.getAvailableBotsForVoting = (commandHandler, amount, id, voteType) => {
- return new Promise((resolve) => {
- (async () => { // Lets us use await inside a Promise without creating an antipattern
+module.exports.getAvailableBotsForVoting = async (commandHandler, amount, id, voteType) => {
- /* --------- Get all bots which haven't voted on this id yet and aren't currently in another vote request --------- */
- let whenAvailable; // We will save the until value of the account that the user has to wait for here
- let whenAvailableStr;
- let allAccsOnline = commandHandler.controller.getBots(null, true);
- let allAccounts = [ ... Object.keys(allAccsOnline) ]; // Clone keys array (bot usernames) of bots object
+ /* --------- Get all bots which haven't voted on this id yet and aren't currently in another vote request --------- */
+ let whenAvailable; // We will save the until value of the account that the user has to wait for here
+ let whenAvailableStr;
+ let allAccsOnline = commandHandler.controller.getBots(null, true);
+ let allAccounts = [ ... Object.keys(allAccsOnline) ]; // Clone keys array (bot usernames) of bots object
- // Remove limited accounts from allAccounts array as they are unable to vote
- let previousLengthLimited = allAccounts.length;
- allAccounts = allAccounts.filter(e => allAccsOnline[e].user.limitations && !allAccsOnline[e].user.limitations.limited);
+ // Remove limited accounts from allAccounts array as they are unable to vote
+ let previousLengthLimited = allAccounts.length;
+ allAccounts = allAccounts.filter(e => allAccsOnline[e].user.limitations && !allAccsOnline[e].user.limitations.limited);
- if (previousLengthLimited - allAccounts.length > 0) logger("info", `${previousLengthLimited - allAccounts.length} of ${previousLengthLimited} bot accounts were removed from available accounts as they are limited and can't be used for this request!`);
+ if (previousLengthLimited - allAccounts.length > 0) logger("info", `${previousLengthLimited - allAccounts.length} of ${previousLengthLimited} bot accounts were removed from available accounts as they are limited and can't be used for this request!`);
- // Remove bot accounts from allAccounts which have already voted on this id with this voteType
- let previousLengthVoted = allAccounts.length;
- let alreadyVoted = await commandHandler.data.ratingHistoryDB.findAsync({ id: id, type: voteType }, {});
+ // Remove bot accounts from allAccounts which have already voted on this id with this voteType
+ let previousLengthVoted = allAccounts.length;
+ let alreadyVoted = await commandHandler.data.ratingHistoryDB.findAsync({ id: id, type: voteType }, {});
- alreadyVoted.forEach((e) => {
- if (allAccounts.indexOf(e.accountName) != -1) allAccounts.splice(allAccounts.indexOf(e.accountName), 1);
- });
-
- if (previousLengthVoted - allAccounts.length > 0) logger("info", `${previousLengthVoted - allAccounts.length} of ${previousLengthVoted} bot accounts were removed from available accounts because we know that they have already voted on this item!`);
+ alreadyVoted.forEach((e) => {
+ if (allAccounts.indexOf(e.accountName) != -1) allAccounts.splice(allAccounts.indexOf(e.accountName), 1);
+ });
+ if (previousLengthVoted - allAccounts.length > 0) logger("info", `${previousLengthVoted - allAccounts.length} of ${previousLengthVoted} bot accounts were removed from available accounts because we know that they have already voted on this item!`);
- // Loop over activeRequests and remove all active entries from allAccounts if both are not empty
- if (allAccounts.length > 0 && Object.keys(commandHandler.controller.activeRequests).length > 0) {
- Object.keys(commandHandler.controller.activeRequests).forEach((e) => {
- if (!commandHandler.controller.activeRequests[e].type.includes("vote")) return; // Ignore entry if not of this type
- if (Date.now() < commandHandler.controller.activeRequests[e].until + (commandHandler.data.config.botaccountcooldown * 60000)) { // Check if entry is not finished yet
- commandHandler.controller.activeRequests[e].accounts.forEach((f) => { // Loop over every account used in this request
- allAccounts.splice(allAccounts.indexOf(f), 1); // Remove that accountindex from the allAccounts array
- });
+ // Loop over activeRequests and remove all active entries from allAccounts if both are not empty
+ if (allAccounts.length > 0 && Object.keys(commandHandler.controller.activeRequests).length > 0) {
+ Object.keys(commandHandler.controller.activeRequests).forEach((e) => {
+ if (!commandHandler.controller.activeRequests[e].type.includes("vote")) return; // Ignore entry if not of this type
- // If this removal causes the user to need to wait, update whenAvailable. Don't bother if user provided "all"
- if (amount != "all" && allAccounts.length - commandHandler.controller.activeRequests[e].accounts.length < amount) {
- whenAvailable = commandHandler.controller.activeRequests[e].until + (commandHandler.data.config.botaccountcooldown * 60000);
- whenAvailableStr = timeToString(whenAvailable);
- }
- } else {
- delete commandHandler.controller.activeRequests[e]; // Remove entry from object if it is finished to keep the object clean
- }
+ if (Date.now() < commandHandler.controller.activeRequests[e].until + (commandHandler.data.config.botaccountcooldown * 60000)) { // Check if entry is not finished yet
+ commandHandler.controller.activeRequests[e].accounts.forEach((f) => { // Loop over every account used in this request
+ allAccounts.splice(allAccounts.indexOf(f), 1); // Remove that accountindex from the allAccounts array
});
+
+ // If this removal causes the user to need to wait, update whenAvailable. Don't bother if user provided "all"
+ if (amount != "all" && allAccounts.length - commandHandler.controller.activeRequests[e].accounts.length < amount) {
+ whenAvailable = commandHandler.controller.activeRequests[e].until + (commandHandler.data.config.botaccountcooldown * 60000);
+ whenAvailableStr = timeToString(whenAvailable);
+ }
+ } else {
+ delete commandHandler.controller.activeRequests[e]; // Remove entry from object if it is finished to keep the object clean
}
+ });
+ }
- // Update amount if "all"
- if (amount == "all") {
- amount = allAccounts.length;
- logger("debug", `CommandHandler getVoteBots(): User provided max amount keyword "all", updating it to ${allAccounts.length}`);
- }
+ // Update amount if "all"
+ if (amount == "all") {
+ amount = allAccounts.length;
+ logger("debug", `CommandHandler getVoteBots(): User provided max amount keyword "all", updating it to ${allAccounts.length}`);
+ }
- // Cut result to only include needed accounts
- if (allAccounts.length > amount) allAccounts = allAccounts.slice(0, amount);
+ // Cut result to only include needed accounts
+ if (allAccounts.length > amount) allAccounts = allAccounts.slice(0, amount);
- // Log result to debug
- if (allAccounts.length < amount) logger("debug", `CommandHandler getVoteBots(): Found ${allAccounts.length} available bot accounts to vote on ${id} but ${amount} are needed. If accs will become available, the user needs to wait: ${whenAvailableStr || "/"}`);
- else logger("debug", `CommandHandler getVoteBots(): Found ${allAccounts.length} available bot accounts to vote on ${id}: ${allAccounts}`);
+ // Log result to debug
+ if (allAccounts.length < amount) logger("debug", `CommandHandler getVoteBots(): Found ${allAccounts.length} available bot accounts to vote on ${id} but ${amount} are needed. If accs will become available, the user needs to wait: ${whenAvailableStr || "/"}`);
+ else logger("debug", `CommandHandler getVoteBots(): Found ${allAccounts.length} available bot accounts to vote on ${id}: ${allAccounts}`);
+
+ // Resolve promise with values
+ return {
+ "amount": amount,
+ "availableAccounts": allAccounts,
+ "whenAvailable": whenAvailable,
+ "whenAvailableStr": whenAvailableStr
+ };
- // Resolve promise with values
- resolve({
- "amount": amount,
- "availableAccounts": allAccounts,
- "whenAvailable": whenAvailable,
- "whenAvailableStr": whenAvailableStr
- });
- })();
- });
};
\ No newline at end of file
diff --git a/src/commands/helpers/handleCommentSkips.js b/src/commands/helpers/handleCommentSkips.js
index c8c8c617..cf995eab 100644
--- a/src/commands/helpers/handleCommentSkips.js
+++ b/src/commands/helpers/handleCommentSkips.js
@@ -4,7 +4,7 @@
* Created Date: 28.02.2022 12:22:48
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:33:05
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
@@ -23,10 +23,10 @@ const CommandHandler = require("../commandHandler.js"); // eslint-disable-line
* Checks if the following comment process iteration should be skipped
* Aborts comment process on critical error.
* @param {CommandHandler} commandHandler The commandHandler object
- * @param {{ next: function, break: function, index: function }} loop Object returned by misc.js syncLoop() helper
+ * @param {{ next: function(): void, break: function(): void, index: function(): number }} loop Object returned by misc.js syncLoop() helper
* @param {Bot} bot Bot object of the account posting this comment
- * @param {String} receiverSteamID64 steamID64 of the receiving user/group
- * @returns {Boolean} true if iteration should continue, false if iteration should be skipped using return
+ * @param {string} receiverSteamID64 steamID64 of the receiving user/group
+ * @returns {boolean} true if iteration should continue, false if iteration should be skipped using return
*/
module.exports.handleIterationSkip = (commandHandler, loop, bot, receiverSteamID64) => {
let activeReqEntry = commandHandler.controller.activeRequests[receiverSteamID64]; // Make using the obj shorter
@@ -99,10 +99,10 @@ module.exports.handleIterationSkip = (commandHandler, loop, bot, receiverSteamID
/**
* Adds a description to comment errors and applies additional cooldowns for certain errors
- * @param {String} error The error string returned by steamcommunity
+ * @param {string} error The error string returned by steamcommunity
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Bot} bot Bot object of the account posting this comment
- * @param {String} receiverSteamID64 steamID64 of the receiving user/group
+ * @param {string} receiverSteamID64 steamID64 of the receiving user/group
*/
module.exports.logCommentError = (error, commandHandler, bot, receiverSteamID64) => {
let activeReqEntry = commandHandler.controller.activeRequests[receiverSteamID64]; // Make using the obj shorter
@@ -176,7 +176,7 @@ module.exports.logCommentError = (error, commandHandler, bot, receiverSteamID64)
/**
* Helper function to sort failed object by comment number so that it is easier to read
- * @param {Object} failedObj Current state of failed object
+ * @param {object} failedObj Current state of failed object
*/
function sortFailedCommentsObject(failedObj) {
let sortedvals = Object.keys(failedObj).sort((a, b) => {
@@ -192,8 +192,8 @@ function sortFailedCommentsObject(failedObj) {
/**
* Groups same error messages together, counts amount, lists affected bots and converts it to a String.
- * @param {Object} obj failedcomments object that should be converted
- * @returns {String} String that looks like this: `amount`x - `indices`\n`error message`
+ * @param {object} obj failedcomments object that should be converted
+ * @returns {string} String that looks like this: `amount`x - `indices`\n`error message`
*/
module.exports.failedCommentsObjToString = (obj) => {
// Count amount of each string
diff --git a/src/commands/helpers/handleSharedfileErrors.js b/src/commands/helpers/handleSharedfileErrors.js
index 17e260d1..8190b00c 100644
--- a/src/commands/helpers/handleSharedfileErrors.js
+++ b/src/commands/helpers/handleSharedfileErrors.js
@@ -4,7 +4,7 @@
* Created Date: 31.05.2023 16:57:21
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:51:58
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -21,10 +21,10 @@ const Bot = require("../../bot/bot.js"); // eslint-disable-line
/**
* Checks if the following vote process iteration should be skipped
* @param {CommandHandler} commandHandler The commandHandler object
- * @param {{ next: function, break: function, index: function }} loop Object returned by misc.js syncLoop() helper
+ * @param {{ next: function(): void, break: function(): void, index: function(): number }} loop Object returned by misc.js syncLoop() helper
* @param {Bot} bot Bot object of the account making this request
- * @param {String} id ID of the sharedfile that receives the votes
- * @returns true if iteration should continue, false if iteration should be skipped using return
+ * @param {string} id ID of the sharedfile that receives the votes
+ * @returns {boolean} `true` if iteration should continue, `false` if iteration should be skipped using return
*/
module.exports.handleVoteIterationSkip = function(commandHandler, loop, bot, id) {
let activeReqEntry = commandHandler.controller.activeRequests[id]; // Make using the obj shorter
@@ -55,10 +55,10 @@ module.exports.handleVoteIterationSkip = function(commandHandler, loop, bot, id)
/**
* Checks if the following favorite process iteration should be skipped
* @param {CommandHandler} commandHandler The commandHandler object
- * @param {{ next: function, break: function, index: function }} loop Object returned by misc.js syncLoop() helper
+ * @param {{ next: function(): void, break: function(): void, index: function(): number }} loop Object returned by misc.js syncLoop() helper
* @param {Bot} bot Bot object of the account making this request
- * @param {String} id ID of the sharedfile that receives the votes
- * @returns true if iteration should continue, false if iteration should be skipped using return
+ * @param {string} id ID of the sharedfile that receives the votes
+ * @returns {boolean} `true` if iteration should continue, `false` if iteration should be skipped using return
*/
module.exports.handleFavoriteIterationSkip = function(commandHandler, loop, bot, id) {
let activeReqEntry = commandHandler.controller.activeRequests[id]; // Make using the obj shorter
@@ -88,10 +88,10 @@ module.exports.handleFavoriteIterationSkip = function(commandHandler, loop, bot,
/**
* Logs vote errors
- * @param {String} error The error string returned by steam-community
+ * @param {string} error The error string returned by steam-community
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Bot} bot Bot object of the account making this request
- * @param {String} id ID of the sharedfile that receives the votes
+ * @param {string} id ID of the sharedfile that receives the votes
*/
module.exports.logVoteError = (error, commandHandler, bot, id) => {
let activeReqEntry = commandHandler.controller.activeRequests[id]; // Make using the obj shorter
@@ -114,10 +114,10 @@ module.exports.logVoteError = (error, commandHandler, bot, id) => {
/**
* Logs favorite errors
- * @param {String} error The error string returned by steam-community
+ * @param {string} error The error string returned by steam-community
* @param {CommandHandler} commandHandler The commandHandler object
* @param {Bot} bot Bot object of the account making this request
- * @param {String} id ID of the sharedfile that receives the favorites
+ * @param {string} id ID of the sharedfile that receives the favorites
*/
module.exports.logFavoriteError = (error, commandHandler, bot, id) => {
let activeReqEntry = commandHandler.controller.activeRequests[id]; // Make using the obj shorter
@@ -140,7 +140,7 @@ module.exports.logFavoriteError = (error, commandHandler, bot, id) => {
/**
* Helper function to sort failed object by number so that it is easier to read
- * @param {Object} failedObj Current state of failed object
+ * @param {object} failedObj Current state of failed object
*/
function sortFailedCommentsObject(failedObj) {
let sortedvals = Object.keys(failedObj).sort((a, b) => {
diff --git a/src/controller/controller.js b/src/controller/controller.js
index 5bf52830..9ec3a47b 100644
--- a/src/controller/controller.js
+++ b/src/controller/controller.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 08.07.2023 00:36:10
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -34,30 +34,31 @@ const Controller = function() {
this.misc = {
/**
* Implementation of a synchronous for loop in JS (Used as reference: https://whitfin.io/handling-synchronous-asynchronous-loops-javascriptnode-js/)
- * @param {Number} iterations The amount of iterations
- * @param {Function} func The function to run each iteration (Params: loop, index)
- * @param {Function} exit This function will be called when the loop is finished
+ * @param {number} iterations The amount of iterations
+ * @param {function(): void} func The function to run each iteration (Params: loop, index)
+ * @param {function(): void} exit This function will be called when the loop is finished
*/
syncLoop: (iterations, func, exit) => {}, // eslint-disable-line
/**
* Rounds a number with x decimals
- * @param {Number} value Number to round
- * @param {Number} decimals Amount of decimals
- * @returns {Number} Rounded number
+ * @param {number} value Number to round
+ * @param {number} decimals Amount of decimals
+ * @returns {number} Rounded number
*/
round: (value, decimals) => {}, // eslint-disable-line
/**
- * Converts a timestamp to a human-readable until from now format. Does not care about past/future.
- * @returns {String} "x seconds/minutes/hours/days"
+ * Converts a timestamp to a human-readable "until from now" format. Does not care about past/future.
+ * @param {number} timestamp UNIX timestamp to convert
+ * @returns {string} "x seconds/minutes/hours/days"
*/
timeToString: () => {}, // eslint-disable-line
/**
* Pings an URL to check if the service and this internet connection is working
- * @param {String} url The URL of the service to check
- * @param {Boolean} throwTimeout If true, the function will throw a timeout error if Steam can't be reached after 20 seconds
+ * @param {string} url The URL of the service to check
+ * @param {boolean} throwTimeout If true, the function will throw a timeout error if Steam can't be reached after 20 seconds
* @returns {Promise.<{ statusMessage: string, statusCode: number|null }>} Resolves on response code 2xx and rejects on any other response code. Both are called with parameter `response` (Object) which has a `statusMessage` (String) and `statusCode` (Number) key. `statusCode` is `null` if request failed.
*/
checkConnection: (url, throwTimeout) => {}, // eslint-disable-line
@@ -65,10 +66,10 @@ const Controller = function() {
/**
* Helper function which attempts to cut Strings intelligently and returns all parts. It will attempt to not cut words & links in half.
* It is used by the steamChatInteraction helper but can be used in plugins as well.
- * @param {String} txt The string to cut
- * @param {Number} limit Maximum length for each part. The function will attempt to cut txt into parts that don't exceed this amount.
+ * @param {string} txt The string to cut
+ * @param {number} limit Maximum length for each part. The function will attempt to cut txt into parts that don't exceed this amount.
* @param {Array} cutChars Optional: Custom chars to search after for cutting string in parts. Default: [" ", "\n", "\r"]
- * @param {Number} threshold Optional: Maximum amount that limit can be reduced to find the last space or line break. If no match is found within this limit a word will be cut. Default: 15% of total length
+ * @param {number} threshold Optional: Maximum amount that limit can be reduced to find the last space or line break. If no match is found within this limit a word will be cut. Default: 15% of total length
* @returns {Array} Returns all parts of the string in an array
*/
cutStringsIntelligently: (txt, limit, cutChars, threshold) => {} // eslint-disable-line
@@ -250,7 +251,7 @@ Controller.prototype._preLogin = async function() {
/**
* Stores references to all bot account objects mapped to their accountName
- * @type {Object.}
+ * @type {{[key: string]: Bot}}
*/
this.bots = {};
@@ -279,7 +280,7 @@ Controller.prototype._preLogin = async function() {
* @type {CommandHandler}
*/
this.commandHandler = new CommandHandler(this);
- this.commandHandler._importCoreCommands();
+ await this.commandHandler._importCoreCommands();
// Load pluginSystem
@@ -306,6 +307,7 @@ module.exports = Controller;
/**
* Process data that should be kept over restarts
+ * @param {string} data Stringified data received by previous process
*/
function restartdata(data) {
data = JSON.parse(data); // Convert the stringified object back to an object
@@ -352,7 +354,7 @@ if (parseInt(process.argv[3]) + 2500 > Date.now()) { // Check if this process ju
/**
* Restarts the whole application
- * @param {String} data Stringified restartdata object that will be kept through restarts
+ * @param {string} data Stringified restartdata object that will be kept through restarts
*/
Controller.prototype.restart = function(data) { process.send(`restart(${data})`); };
@@ -364,7 +366,7 @@ Controller.prototype.stop = function() { process.send("stop()"); };
/**
* Attempts to log in all bot accounts which are currently offline one after another.
* Creates a new bot object for every new account and reuses existing one if possible
- * @param {Boolean} firstLogin Is set to true by controller if this is the first login to display more information
+ * @param {boolean} firstLogin Is set to true by controller if this is the first login to display more information
*/
Controller.prototype.login = function(firstLogin) {}; // eslint-disable-line
@@ -376,14 +378,14 @@ Controller.prototype._readyEvent = function() {};
/**
* Runs internal statusUpdate event code and emits statusUpdate event for plugins
* @param {Bot} bot Bot instance
- * @param {Bot.EStatus} newStatus The new status
+ * @param {Bot.EStatus} newStatus The new status of this bot
*/
Controller.prototype._statusUpdateEvent = function(bot, newStatus) {}; // eslint-disable-line
/**
* Emits steamGuardInput event for bot & plugins
* @param {Bot} bot Bot instance of the affected account
- * @param {function(string)} submitCode Function to submit a code. Pass an empty string to skip the account.
+ * @param {function(string): void} submitCode Function to submit a code. Pass an empty string to skip the account.
*/
Controller.prototype._steamGuardInputEvent = function(bot, submitCode) {}; // eslint-disable-line
@@ -396,7 +398,7 @@ Controller.prototype.checkLastcommentDB = function(bot) {}; // eslint-disable-li
/**
* Checks the remaining space on the friendlist of a bot account, sends a warning message if it is less than 10 and force unfriends oldest lastcomment db user to always keep room for 1 friend.
* @param {Bot} bot Bot object of the account to check
- * @param {function} [callback] Called with `remaining` (Number) on completion
+ * @param {function(number|null): void} callback Called with `remaining` (Number) on success or `null` on failure
*/
Controller.prototype.friendListCapacityCheck = function(bot, callback) {}; // eslint-disable-line
@@ -408,8 +410,8 @@ Controller.prototype._lastcommentUnfriendCheck = function() {} // eslint-disable
/**
* Retrieves all matching bot accounts and returns them.
* @param {(EStatus|EStatus[]|string)} [statusFilter=EStatus.ONLINE] Optional: EStatus or Array of EStatus's including account statuses to filter. Pass '*' to get all accounts. If omitted, only accs with status 'EStatus.ONLINE' will be returned.
- * @param {Boolean} mapToObject Optional: If true, an object will be returned where every bot object is mapped to their accountName.
- * @returns {Array|Object} An array or object if `mapToObject == true` containing all matching bot accounts.
+ * @param {boolean} mapToObject Optional: If true, an object will be returned where every bot object is mapped to their accountName.
+ * @returns {Array|object} An array or object if `mapToObject == true` containing all matching bot accounts.
*/
Controller.prototype.getBots = function(statusFilter = EStatus.ONLINE, mapToObject = false) {}; // eslint-disable-line
@@ -421,24 +423,27 @@ Controller.prototype._handleErrors = function() {} // eslint-disable-line
/**
* Handles converting URLs to steamIDs, determining their type if unknown and checking if it matches your expectation
- * @param {String} str The profileID argument provided by the user
- * @param {String} expectedIdType The type of SteamID expected ("profile", "group" or "sharedfile") or `null` if type should be assumed.
- * @param {function} [callback] Called with `err` (String or null), `steamID64` (String or null), `idType` (String or null) parameters on completion
+ * @param {string} str The profileID argument provided by the user
+ * @param {string} expectedIdType The type of SteamID expected ("profile", "group" or "sharedfile") or `null` if type should be assumed.
+ * @param {function(string|null, string|null, string|null): void} callback Called with `err` (String or null), `steamID64` (String or null), `idType` (String or null) parameters on completion
*/
Controller.prototype.handleSteamIdResolving = (str, expectedIdType, callback) => {} // eslint-disable-line
/**
* Logs text to the terminal and appends it to the output.txt file.
- * @param {String} type String that determines the type of the log message. Can be info, warn, error, debug or an empty string to not use the field.
- * @param {String} str The text to log into the terminal
- * @param {Boolean} nodate Setting to true will hide date and time in the message
- * @param {Boolean} remove Setting to true will remove this message with the next one
- * @param {Boolean} printNow Ignores the readyafterlogs check and force prints the message now
+ * @param {string} type String that determines the type of the log message. Can be info, warn, error, debug or an empty string to not use the field.
+ * @param {string} str The text to log into the terminal
+ * @param {boolean} nodate Setting to true will hide date and time in the message
+ * @param {boolean} remove Setting to true will remove this message with the next one
+ * @param {Array.} animation Array containing animation frames as elements
+ * @param {boolean} printNow Ignores the readyafterlogs check and force prints the message now
+ * @param {boolean} cutToWidth Cuts the string to the width of the terminal
*/
Controller.prototype.logger = function(type, str, nodate, remove, animation, printNow) {}; // eslint-disable-line
/**
* Internal: Call this function after loading advancedconfig.json to set previously inaccessible options
+ * @param {object} advancedconfig The advancedconfig object imported by the DataManager
*/
Controller.prototype._loggerOptionsUpdateAfterConfigLoad = function(advancedconfig) {}; // eslint-disable-line
diff --git a/src/controller/events/statusUpdate.js b/src/controller/events/statusUpdate.js
index e4a0ca33..f2b80bde 100644
--- a/src/controller/events/statusUpdate.js
+++ b/src/controller/events/statusUpdate.js
@@ -4,7 +4,7 @@
* Created Date: 30.03.2023 21:05:13
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:36:26
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -22,7 +22,7 @@ const Controller = require("../controller");
/**
* Runs internal statusUpdate event code and emits statusUpdate event for plugins
* @param {Bot} bot Bot instance
- * @param {Bot.EStatus} newStatus The new status
+ * @param {Bot.EStatus} newStatus The new status of this bot
*/
Controller.prototype._statusUpdateEvent = function(bot, newStatus) {
let oldStatus = bot.status;
diff --git a/src/controller/events/steamGuardInput.js b/src/controller/events/steamGuardInput.js
index de8c1d6b..f747a7d4 100644
--- a/src/controller/events/steamGuardInput.js
+++ b/src/controller/events/steamGuardInput.js
@@ -4,7 +4,7 @@
* Created Date: 04.06.2023 12:00:48
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:36:34
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -22,7 +22,7 @@ const Controller = require("../controller");
/**
* Emits steamGuardInput event for bot & plugins
* @param {Bot} bot Bot instance of the affected account
- * @param {function(string)} submitCode Function to submit a code. Pass an empty string to skip the account.
+ * @param {function(string): void} submitCode Function to submit a code. Pass an empty string to skip the account.
*/
Controller.prototype._steamGuardInputEvent = function(bot, submitCode) {
diff --git a/src/controller/helpers/friendlist.js b/src/controller/helpers/friendlist.js
index facf9775..ebc3bb7f 100644
--- a/src/controller/helpers/friendlist.js
+++ b/src/controller/helpers/friendlist.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 08.07.2023 00:36:19
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -52,7 +52,7 @@ Controller.prototype.checkLastcommentDB = function(bot) {
/**
* Checks the remaining space on the friendlist of a bot account, sends a warning message if it is less than 10 and force unfriends oldest lastcomment db user to always keep room for 1 friend.
* @param {Bot} bot Bot object of the account to check
- * @param {function} [callback] Called with `remaining` (Number) on completion
+ * @param {function(number|null): void} callback Called with `remaining` (Number) on success or `null` on failure
*/
Controller.prototype.friendListCapacityCheck = function(bot, callback) {
try {
diff --git a/src/controller/helpers/getBots.js b/src/controller/helpers/getBots.js
index 26bed5da..ef8d2463 100644
--- a/src/controller/helpers/getBots.js
+++ b/src/controller/helpers/getBots.js
@@ -4,7 +4,7 @@
* Created Date: 02.05.2023 13:46:21
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:38:06
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -22,8 +22,8 @@ const EStatus = require("../../bot/EStatus.js");
/**
* Retrieves all matching bot accounts and returns them.
* @param {(EStatus|EStatus[]|string)} [statusFilter=EStatus.ONLINE] Optional: EStatus or Array of EStatus's including account statuses to filter. Pass '*' to get all accounts. If omitted, only accs with status 'EStatus.ONLINE' will be returned.
- * @param {Boolean} mapToObject Optional: If true, an object will be returned where every bot object is mapped to their accountName.
- * @returns {Array|Object} An array or object if `mapToObject == true` containing all matching bot accounts.
+ * @param {boolean} mapToObject Optional: If true, an object will be returned where every bot object is mapped to their accountName.
+ * @returns {Array|object} An array or object if `mapToObject == true` containing all matching bot accounts.
*/
Controller.prototype.getBots = function(statusFilter, mapToObject) {
if (!statusFilter) statusFilter = EStatus.ONLINE;
diff --git a/src/controller/helpers/handleErrors.js b/src/controller/helpers/handleErrors.js
index d91b9da8..e1274d04 100644
--- a/src/controller/helpers/handleErrors.js
+++ b/src/controller/helpers/handleErrors.js
@@ -4,7 +4,7 @@
* Created Date: 21.03.2023 22:53:37
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 05.07.2023 10:48:43
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -40,7 +40,7 @@ Controller.prototype._handleErrors = function() {
require("./npminteraction.js").reinstallAll(logger, (err, stdout) => { //eslint-disable-line
if (err) {
- logger("error", "I was unable to reinstall all modules. Please try running 'npm install' manually. Error: " + err);
+ logger("error", "I was unable to reinstall all modules. Please try running 'npm install --production' manually. Error: " + err);
return this.stop();
} else {
// Logger("info", `NPM Log:\n${stdout}`, true) //entire log (not using it rn to avoid possible confusion with vulnerabilities message)
diff --git a/src/controller/helpers/handleSteamIdResolving.js b/src/controller/helpers/handleSteamIdResolving.js
index bd508076..18320eb1 100644
--- a/src/controller/helpers/handleSteamIdResolving.js
+++ b/src/controller/helpers/handleSteamIdResolving.js
@@ -4,7 +4,7 @@
* Created Date: 09.03.2022 12:58:17
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 08.07.2023 00:36:23
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
@@ -26,9 +26,9 @@ const Controller = require("../controller.js");
/**
* Handles converting URLs to steamIDs, determining their type if unknown and checking if it matches your expectation
- * @param {String} str The profileID argument provided by the user
- * @param {String} expectedIdType The type of SteamID expected ("profile", "group" or "sharedfile") or `null` if type should be assumed.
- * @param {function} [callback] Called with `err` (String or null), `steamID64` (String or null), `idType` (String or null) parameters on completion
+ * @param {string} str The profileID argument provided by the user
+ * @param {string} expectedIdType The type of SteamID expected ("profile", "group" or "sharedfile") or `null` if type should be assumed.
+ * @param {function(string|null, string|null, string|null): void} callback Called with `err` (String or null), `steamID64` (String or null), `idType` (String or null) parameters on completion
*/
Controller.prototype.handleSteamIdResolving = (str, expectedIdType, callback) => {
diff --git a/src/controller/helpers/logger.js b/src/controller/helpers/logger.js
index 323977d4..7a003ae2 100644
--- a/src/controller/helpers/logger.js
+++ b/src/controller/helpers/logger.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:39:07
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -37,13 +37,13 @@ let botIsReady = false;
// Modfied output-logger function to hold back certain messages until ready event fired
/**
* Logs text to the terminal and appends it to the output.txt file.
- * @param {String} type String that determines the type of the log message. Can be info, warn, error, debug or an empty string to not use the field.
- * @param {String} str The text to log into the terminal
- * @param {Boolean} nodate Setting to true will hide date and time in the message
- * @param {Boolean} remove Setting to true will remove this message with the next one
- * @param {array.} animation Array containing animation frames as elements
- * @param {Boolean} printNow Ignores the readyafterlogs check and force prints the message now
- * @param {Boolean} cutToWidth Cuts the string to the width of the terminal
+ * @param {string} type String that determines the type of the log message. Can be info, warn, error, debug or an empty string to not use the field.
+ * @param {string} str The text to log into the terminal
+ * @param {boolean} nodate Setting to true will hide date and time in the message
+ * @param {boolean} remove Setting to true will remove this message with the next one
+ * @param {Array.} animation Array containing animation frames as elements
+ * @param {boolean} printNow Ignores the readyafterlogs check and force prints the message now
+ * @param {boolean} cutToWidth Cuts the string to the width of the terminal
*/
Controller.prototype.logger = function(type, str, nodate, remove, animation, printNow, cutToWidth) {
@@ -75,6 +75,7 @@ global.logger = Controller.prototype.logger;
/**
* Internal: Call this function after loading advancedconfig.json to set previously inaccessible options
+ * @param {object} advancedconfig The advancedconfig object imported by the DataManager
*/
Controller.prototype._loggerOptionsUpdateAfterConfigLoad = function(advancedconfig) {
logger.options({
diff --git a/src/controller/helpers/misc.js b/src/controller/helpers/misc.js
index 11d1436a..d246b501 100644
--- a/src/controller/helpers/misc.js
+++ b/src/controller/helpers/misc.js
@@ -4,7 +4,7 @@
* Created Date: 25.03.2023 14:02:56
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:40:22
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -20,9 +20,9 @@ const https = require("https");
/**
* Implementation of a synchronous for loop in JS (Used as reference: https://whitfin.io/handling-synchronous-asynchronous-loops-javascriptnode-js/)
- * @param {Number} iterations The amount of iterations
- * @param {Function} func The function to run each iteration (Params: loop, index)
- * @param {Function} exit This function will be called when the loop is finished
+ * @param {number} iterations The amount of iterations
+ * @param {function(): void} func The function to run each iteration (Params: loop, index)
+ * @param {function(): void} exit This function will be called when the loop is finished
*/
module.exports.syncLoop = (iterations, func, exit) => {
let currentIndex = 0;
@@ -55,9 +55,9 @@ module.exports.syncLoop = (iterations, func, exit) => {
/**
* Rounds a number with x decimals
- * @param {Number} value Number to round
- * @param {Number} decimals Amount of decimals
- * @returns {Number} Rounded number
+ * @param {number} value Number to round
+ * @param {number} decimals Amount of decimals
+ * @returns {number} Rounded number
*/
module.exports.round = (value, decimals) => {
return Number(Math.round(value+"e"+decimals)+"e-"+decimals);
@@ -65,8 +65,9 @@ module.exports.round = (value, decimals) => {
/**
- * Converts a timestamp to a human-readable until from now format. Does not care about past/future.
- * @returns {String} "x seconds/minutes/hours/days"
+ * Converts a timestamp to a human-readable "until from now" format. Does not care about past/future.
+ * @param {number} timestamp UNIX timestamp to convert
+ * @returns {string} "x seconds/minutes/hours/days"
*/
module.exports.timeToString = (timestamp) => {
let until = Math.abs((Date.now() - timestamp) / 1000);
@@ -90,8 +91,8 @@ module.exports.timeToString = (timestamp) => {
/**
* Pings an URL to check if the service and this internet connection is working
- * @param {String} url The URL of the service to check
- * @param {Boolean} throwTimeout If true, the function will throw a timeout error if Steam can't be reached after 20 seconds
+ * @param {string} url The URL of the service to check
+ * @param {boolean} throwTimeout If true, the function will throw a timeout error if Steam can't be reached after 20 seconds
* @returns {Promise.<{ statusMessage: string, statusCode: number|null }>} Resolves on response code 2xx and rejects on any other response code. Both are called with parameter `response` (Object) which has a `statusMessage` (String) and `statusCode` (Number) key. `statusCode` is `null` if request failed.
*/
module.exports.checkConnection = (url, throwTimeout) => {
@@ -124,10 +125,10 @@ module.exports.checkConnection = (url, throwTimeout) => {
/**
* Helper function which attempts to cut Strings intelligently and returns all parts. It will attempt to not cut words & links in half.
* It is used by the steamChatInteraction helper but can be used in plugins as well.
- * @param {String} txt The string to cut
- * @param {Number} limit Maximum length for each part. The function will attempt to cut txt into parts that don't exceed this amount.
+ * @param {string} txt The string to cut
+ * @param {number} limit Maximum length for each part. The function will attempt to cut txt into parts that don't exceed this amount.
* @param {Array} cutChars Optional: Custom chars to search after for cutting string in parts. Default: [" ", "\n", "\r"]
- * @param {Number} threshold Optional: Maximum amount that limit can be reduced to find the last space or line break. If no match is found within this limit a word will be cut. Default: 15% of total length
+ * @param {number} threshold Optional: Maximum amount that limit can be reduced to find the last space or line break. If no match is found within this limit a word will be cut. Default: 15% of total length
* @returns {Array} Returns all parts of the string in an array
*/
module.exports.cutStringsIntelligently = (txt, limit, cutChars, threshold) => {
diff --git a/src/controller/helpers/npminteraction.js b/src/controller/helpers/npminteraction.js
index ea1b3aec..b1cf1066 100644
--- a/src/controller/helpers/npminteraction.js
+++ b/src/controller/helpers/npminteraction.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 08.07.2023 00:36:35
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -21,8 +21,8 @@ const { exec } = require("child_process"); // Wanted to do it with the npm packa
/**
* Attempts to reinstall all modules
- * @param {function} logger The currently used logger function (real or fake, the caller decides)
- * @param {function} [callback] Called with `err` (String) and `stdout` (String) (npm response) parameters on completion
+ * @param {function(string, string): void} logger The currently used logger function (real or fake, the caller decides)
+ * @param {function(string|null, string|null): void} callback Called with `err` (String) and `stdout` (String) (npm response) parameters on completion
*/
module.exports.reinstallAll = (logger, callback) => {
if (!fs.existsSync(srcdir + "/../node_modules")) {
@@ -36,12 +36,12 @@ module.exports.reinstallAll = (logger, callback) => {
fs.rm(srcdir + "/../node_modules", { recursive: true }, (err) => {
if (err) return callback(err, null);
- logger("info", "Running 'npm install'...");
+ logger("info", "Running 'npm install --production'...");
exec("npm install --production", { cwd: srcdir + "/.." }, (err, stdout) => {
if (err) return callback(err, null);
- logger("info", "Successfully ran 'npm install'");
+ logger("info", "Successfully ran 'npm install --production'");
callback(null, stdout);
});
@@ -51,7 +51,7 @@ module.exports.reinstallAll = (logger, callback) => {
/**
* Updates all installed packages to versions listed in package.json from the project root directory.
- * @param {function} [callback] Called with `err` (String) and `stdout` (String) (npm response) parameters on completion
+ * @param {function(string|null, string|null): void} callback Called with `err` (String) and `stdout` (String) (npm response) parameters on completion
*/
module.exports.update = (callback) => {
module.exports.updateFromPath(srcdir + "/..", callback);
@@ -60,11 +60,11 @@ module.exports.update = (callback) => {
/**
* Updates all installed packages to versions listed in package.json
- * @param {String} path Custom path to read package.json from and install packages to
- * @param {function} [callback] Called with `err` (String) and `stdout` (String) (npm response) parameters on completion
+ * @param {string} path Custom path to read package.json from and install packages to
+ * @param {function(string|null, string|null): void} callback Called with `err` (String) and `stdout` (String) (npm response) parameters on completion
*/
module.exports.updateFromPath = (path, callback) => {
- logger("debug", `npminteraction update(): Running 'npm install' in '${path}'...`);
+ logger("debug", `npminteraction update(): Running 'npm install --production' in '${path}'...`);
exec("npm install --production", { cwd: path }, (err, stdout) => {
if (err) return callback(err, null);
diff --git a/src/controller/login.js b/src/controller/login.js
index a7eb6b37..3d805d36 100644
--- a/src/controller/login.js
+++ b/src/controller/login.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 17:55:45
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -26,7 +26,7 @@ const misc = require("./helpers/misc.js");
/**
* Attempts to log in all bot accounts which are currently offline one after another.
* Creates a new bot object for every new account and reuses existing one if possible
- * @param {Boolean} firstLogin Is set to true by controller if this is the first login to display more information
+ * @param {boolean} firstLogin Is set to true by controller if this is the first login to display more information
*/
Controller.prototype.login = function(firstLogin) {
diff --git a/src/data/data.json b/src/data/data.json
index 367c9f86..adfc906c 100644
--- a/src/data/data.json
+++ b/src/data/data.json
@@ -1,6 +1,6 @@
{
- "version": "21304",
- "versionstr": "2.13.4",
+ "version": "21305",
+ "versionstr": "2.13.5",
"branch": "master",
"filetostart": "./src/starter.js",
"filetostarturl": "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/master/src/starter.js",
@@ -8,7 +8,7 @@
"aboutstr": "This bot was created by 3urobeat.\nGitHub: https://github.com/3urobeat/steam-comment-service-bot \nSteam: https://steamcommunity.com/id/3urobeat \nIf you like my work, any donation would be appreciated! https://paypal.me/3urobeat",
"firststart": true,
"compatibilityfeaturedone": false,
- "whatsnew": "Added REST API plugin @DerDeathraven, added proper handling of >50 missing licenses, fixed error when acc looses connection during request, fixed non-profile behaviour of !failed & !abort for non-owners and more.",
+ "whatsnew": "Improved plugin system and other parts related to it, (potentially) fixed getting sharedfile error, improved code quality, improved a few wiki pages and minor other changes and fixes.",
"timesloggedin": 0,
"totallogintime": 0
}
\ No newline at end of file
diff --git a/src/data/lang/russian.json b/src/data/lang/russian.json
new file mode 100644
index 00000000..ec803d7e
--- /dev/null
+++ b/src/data/lang/russian.json
@@ -0,0 +1,132 @@
+{
+ "note": "Этот файл содержит почти все сообщения, которые бот посылает пользователям. Если вы хотите изменить сообщения, прочтите эту страницу: https://github.com/3urobeat/steam-comment-service-bot/wiki/How-to-add-custom-messages",
+
+ "updaterautoupdatedisabled": "Вы отключили автоматическое обновление. Хотите ли вы обновить его сейчас?\nЕсли да, пожалуйста, запустите обновление с помощью команды: cmdprefixupdate true",
+
+ "commentcmdusageowner": "«cmdprefixcomment кол-во/\"all\" id» для запроса amount комментариев (или максимально amount).\nУкажите ID профиля, группы или общего файла (скриншот, иллюстрация или руководство), если вы хотите комментировать не на своем профиле, а где-то ещё.",
+ "commentcmdusageowner2": "«cmdprefixcomment 1 id». Укажите ID профиля, группы или общего файла (скриншота, иллюстрации или руководства), если вы хотите комментировать не в своём профиле, а где-то ещё.",
+ "commentcmdusage": "«cmdprefixcomment кол-во/\"all\"» для запроса amount комментариев (или максимально amount) на вашем профиле.",
+ "commentcmdusage2": "«cmdprefixcomment», чтобы запросить комментарий к своему профилю!",
+ "commentrequesttoohigh": "Вы можете запросить максимум комментариев maxRequestAmount.\nИспользование команды: commentcmdusage",
+ "commentinvalidid": "Это не похоже на действительный ID профиля, группы или общего файла (скриншот, иллюстрация или руководство).\nПожалуйста, убедитесь, что вы предоставили либо полную ссылку, либо только пустое значение, либо только ID, указывающий на существующий профиль, группу или общий файл.\n\nИспользование команды: commentcmdusage",
+ "commentprofileidowneronly": "Указание ID разрешено только для владельцев ботов.\nЕсли вы являетесь владельцем бота, пожалуйста, убедитесь, что вы добавили свой ID в параметр ownerid в config.json.",
+ "commentmissingnumberofcomments": "Пожалуйста, укажите, сколько комментариев из maxRequestAmount вы хотите запросить.\nИспользование команды: commentcmdusage",
+ "commentzeroavailableaccs": "Извините, но в настоящее время нет достаточного количества аккаунтов для выполнения вашего запроса. Пожалуйста, подождите waittime и повторите попытку!",
+ "commentnotenoughavailableaccs": "Извините, но в настоящее время нет достаточного количества аккаунтов для выполнения вашего запроса. Пожалуйста, подождите waittime и повторите попытку или запросите availablenow комментариев.",
+ "commentnoaccounts": "Извините, но для выполнения этого запроса нет аккаунтов. Пожалуйста, свяжитесь с администратором бота этого экземпляра.\nИспользуйте команду cmdprefixowner, чтобы получить информацию о том, кто управляет этим экземпляром.",
+ "commentnounlimitedaccs": "Извините, но нет неограниченных аккаунтов, которые необходимы для выполнения этого запроса. Пожалуйста, свяжитесь с администратором бота этого экземпляра.\nИспользуйте команду cmdprefixowner, чтобы получить информацию о том, кто управляет этим экземпляром.",
+ "commentaddbotaccounts": "Пожалуйста, добавьте эти аккаунты, а затем запросите снова: (ограниченные аккаунты)",
+ "commentuserprofileprivate": "Ваш/полученный профиль кажется скрытым. Пожалуйста, измените настройки приватности, чтобы разрешить комментарии, и повторите попытку!",
+ "commenterroroccurred": "Опаньки, произошла ошибка! К сожалению, я не смог её прокомментировать.",
+ "commentprocessstarted": "Примерное время ожидания для numberOfComments комментариев: waittime.",
+ "commentfailedcmdreference": "Чтобы получить подробную информацию о том, почему комментарий не сработал, введите «cmdprefixfailed». Вы можете прочитать, что, вероятно, вызвало вашу ошибку, здесь: https://github.com/3urobeat/steam-comment-service-bot/wiki/Errors,-FAQ-&-Common-problems",
+
+ "comment429stop": "Остановлен процесс комментирования, поскольку все прокси-серверы выдали ошибку HTTP 429 (восстановление IP). Пожалуйста, повторите попытку позже. Не удалось: failedamount/numberOfComments",
+ "commentretrying": "failedamount/numberOfComments комментариев были неудачными. Я собираюсь повторить попытку неудачных комментариев в untilStr. (Попытка thisattempt/maxattempt)",
+ "commentsuccess": "Все комментарии были отправлены. Не удалось: failedamount/numberOfComments\nЕсли вы хороший человек, то, пожалуйста, прокомментируйте и мой профиль!",
+
+ "votenoaccounts": "Извините, но нет неограниченных аккаунтов, которые ещё не голосовали по этому пункту.",
+ "voterequestless": "В настоящее время доступно availablenow бот-аккаунтов, которые ещё не голосовали по этому пункту.",
+ "votenotenoughavailableaccs": "Извините, но в настоящее время недостаточно доступных аккаунтов, которые ещё не голосовали по этому пункту. Пожалуйста, подождите waittime и повторите попытку или запросите availablenow доступных сейчас.",
+ "voteprocessstarted": "Примерное время ожидания для numberOfVotes голосов: waittime.",
+ "votesuccess": "Все голоса были отправлены. Не удалось: failedamount/numberOfVotes",
+
+ "favoritenoaccounts": "Извините, но для данного запроса нет доступных аккаунтов на этом ID.",
+ "favoriterequestless": "Сейчас для этого запроса на этом ID доступны только availablenow бот-аккаунтов.",
+ "favoritenotenoughavailableaccs": "Извините, но в настоящее время для этого запроса не хватает аккаунтов на этом ID. Пожалуйста, подождите waittime и повторите попытку, или запросите availablenow доступных сейчас.",
+ "favoriteprocessstarted": "Примерное время ожидания для numberOfFavs добавлений/удалений в избранное: waittime.",
+ "favoritesuccess": "Все добавления/удаления в избранное были отправлены. Не удалось: failedamount/numberOfFavs",
+
+ "useradded": "Здравствуйте! Спасибо, что добавили меня!\nЗапросите бесплатный комментарий с помощью cmdprefixcomment\nВведите cmdprefixhelp для получения дополнительных команд или cmdprefixabout для получения дополнительной информации!",
+ "userunfriend": "Вы были удалены из друзей за неактивность в течение ForceFriendlistSpaceTime дн., так как в списке друзей было мало места.\nЕсли я вам снова понадоблюсь, добавляйте меня снова!",
+ "userforceunfriend": "Вы были удалены из друзей за неактивность в течение unfriendtime дн.\nЕсли я вам снова понадоблюсь, не стесняйтесь добавить меня снова!",
+
+ "userspamblock": "Вы были заблокированы на 90 секунд за спам.",
+ "usernotfriend": "Пожалуйста, добавьте меня, прежде чем использовать команду!",
+ "botnotready": "Бот ещё не полностью запущен. Пожалуйста, подождите немного, прежде чем использовать команду.",
+ "commandnotfound": "Я не знаю этой команды. Введите cmdprefixhelp для получения дополнительной информации.",
+ "commandowneronly": "Эта команда доступна только для владельцев.\nЕсли вы являетесь владельцем бота, убедитесь, что вы добавили свой ownerid в config.json.",
+
+ "invalidnumber": "Это не похоже на действительное число!\n\nИспользование команды: cmdusage",
+ "invalidprofileid": "Похоже, что это не действительный ID или ссылка, или вы указали неправильный тип ID для этой команды!\nПожалуйста, убедитесь, что вы указали либо полную ссылку, либо только пустое значение, либо только ID, указывающий на существующий профиль, группу или общий файл.",
+ "invalidgroupid": "Это не правильный ID группы или ссылка группы! \ngroupid должен выглядеть следующим образом: «103582791464712227» \n...или ссылка группы должна выглядеть следующим образом: «https://steamcommunity.com/groups/3urobeatGroup»",
+ "invalidsharedfileid": "Похоже, что это не действительный ID общего файла!\nПожалуйста, убедитесь, что вы указали либо полную ссылку: https://steamcommunity.com/sharedfiles/filedetails/?id=2980913451 ...либо только ID: 2980913451 ...который указывает на существующий скриншот, иллюстрацию или руководство.\n\nИспользование команды: cmdusage",
+ "errloadingsharedfile": "Извините, но при загрузке предоставленного вами файла sharedfile произошла ошибка: ",
+ "idisownererror": "Вам не разрешается предоставлять ID владельца!",
+ "idalreadyreceiving": "У этого пользователя или ID уже есть активный запрос! Пожалуйста, подождите, пока он будет завершён, прежде чем запрашивать снова.",
+ "idoncooldown": "Вы только недавно начали запрос. Пожалуйста, подождите оставшиеся remainingcooldown, прежде чем начинать другой запрос.",
+ "requestaborted": "Ваш активный запрос был вручную прерван вами или владельцем.\nsuccessAmount/totalAmount были отправлены успешно.",
+
+ "reloadcmdreloaded": "Перезагружены все команды и плагины.",
+ "activerelog": "В настоящее время бот ожидает завершения последних активных запросов, чтобы заново войти.\nПожалуйста, подождите несколько минут и повторите попытку.",
+ "updatecmdforce": "Принудительное обновление из ветки branchname...",
+ "updatecmdcheck": "Проверка наличия обновления в ветке branchname...",
+ "restartcmdrestarting": "Перезагрузка...",
+ "stopcmdstopping": "Остановка...",
+
+ "helpcommandlist": "Список команд:",
+ "helpcommentowner1": "Запросить x или максимальное количество комментариев (максимум maxOwnerComments). Укажите profileid, чтобы прокомментировать конкретный профиль.",
+ "helpcommentowner2": "Запросить 1 комментарий (максимально amount при текущих настройках). Укажите ID профиля, чтобы прокомментировать конкретный профиль.",
+ "helpcommentuser1": "Запросить x или максимальное количество комментариев (максимум maxComments).",
+ "helpcommentuser2": "Запросить комментарий к своему профилю!",
+ "helpping": "Получить пинг и время отклика от Steam в мс.",
+ "helpinfo": "Получить полезную информацию о боте и о себе.",
+ "helpabort": "Прервать процесс комментирования по собственному запросу.",
+ "helpabout": "Выдать информацию об этом боте, включая ссылку на GitHub.",
+ "helpowner": "Получить ссылку на профиль оператора/организатора данного экземпляра бота.",
+ "helpjoingroup": "Присоединяйтесь к моей «cmdprefixgroup»!",
+ "helpreadothercmdshere": "Чтобы сделать это сообщение коротким, пожалуйста, прочитайте все остальные команды здесь:",
+
+ "pingcmdmessage": "Понг! 🏓\nВремя ответа steamcommunity.com/: pingtimems",
+ "ownercmdnolink": "Оператор этого бота не указал ссылку на себя).",
+ "ownercmdmsg": "Посмотрите профиль моего владельца: (для получения дополнительной информации о боте введите cmdprefixabout)",
+ "groupcmdnolink": "Ботовладелец данного экземпляра не предоставил никакой группы или группа не существует.",
+ "groupcmdinvitesent": "Я отправил вам приглашение! Спасибо, что присоединились!",
+ "groupcmdinvitelink": "Присоединяйтесь к моей группе здесь: ",
+ "abortcmdnoprocess": "Для этого ID нет действующего процесса комментирования.\nЕсли вы запрашивали комментарии для другого профиля, группы или общего файла, укажите этот ID в качестве аргумента!",
+ "abortcmdsuccess": "Прерывание действующего процесса комментирования...",
+
+ "resetcooldowncmdcooldowndisabled": "В конфигурации отключено время восстановления!",
+ "resetcooldowncmdglobalreset": "Время восстановления всех аккаунтов ботов было сброшено.",
+ "resetcooldowncmdsuccess": "Время восстановления profileid было сброшено.",
+
+ "settingscmdfailedread": "Не удалось прочитать конфигурацию для вывода текущих настроек: ",
+ "settingscmdcurrentsettings": "Текущие настройки:",
+ "settingscmdblockedvalues": "enableevalcmd, ownerid и owner не могут быть изменены через команду settings по соображениям безопасности. Пожалуйста, сделайте это непосредственно в конфигурационном файле.",
+ "settingscmdkeynotfound": "Не могу найти этот ключ в конфигурации.",
+ "settingscmdsamevalue": "Запрашиваемый ключ уже является value.",
+ "settingscmdvaluetoobig": "Ваше новое value слишком велико. (Ограничение на 32-разрядное целое число)\nПожалуйста, выберите меньшее value.",
+ "settingscmdvaluechanged": "targetkey был изменен со oldvalue на newvalue.\nПомните, что для вступления в силу некоторых значений может потребоваться перезапуск. Вы можете сделать это, введя cmdprefixrestart.",
+
+ "failedcmdnothingfound": "Я не могу вспомнить ни одного неудачного комментария на вашем/этом ID.\nЕсли вы запрашивали комментарии для другого профиля, группы или общего файла, пожалуйста, укажите этот ID в качестве аргумента!",
+ "failedcmdmsg": "Ваш последний запрос «steamID64», выполненный в «requesttime» (время GMT), содержал эти ошибки:",
+
+ "sessionscmdnosessions": "В настоящее время нет активных сессий и нет бот-аккаунтов в режиме ожидания.",
+ "sessionscmdmsg": "В настоящее время есть amount активных сессий:",
+ "mysessionscmdnosessions": "В настоящее время нет активных сессий, которые вы начали.",
+
+ "addfriendcmdacclimited": "Невозможно добавить profileid в друзья с помощью bot0, потому что аккаунт бота ограничен.",
+ "addfriendcmdsuccess": "Добавление profileid в друзья ко всем аккаунтам ботов... Это займёт ~estimatedtime сек. Пожалуйста, проверьте журнал на наличие возможных ошибок.",
+ "unfriendcmdsuccess": "Я удаляю вас из друзей со всех аккаунтов ботов. Это займёт некоторое время...\nВы можете отправить мне запрос в друзья в любое время.",
+ "unfriendidcmdsuccess": "Удалён profileid из друзей из всех аккаунтов ботов.",
+
+ "unfriendallcmdabort": "Прерывание процесса удаления из списка друзей, если он активен...",
+ "unfriendallcmdpending": "Удаление всех людей (кроме владельцев) со всех аккаунтов бота за 30 секунд...\nВведите «cmdprefixunfriendall abort» для прерывания/остановки процесса.",
+ "unfriendallcmdstart": "Начинаю всех удалять из друзей...",
+
+ "joingroupcmdsuccess": "Вступление в группу «groupid» со всеми аккаунтами ботов...",
+
+ "leavegroupcmdsuccess": "Покидаю группу «groupid» со всеми аккаунтами ботов...",
+
+ "leaveallgroupscmdabort": "Прерывание процесса покидания группы, если он активен...",
+ "leaveallgroupscmdpending": "Выход из всех групп (кроме yourgroup и botsgroup) со всеми аккаунтами ботов через 15 секунд...\nВведите «cmdprefixleaveallgroups abort» для прерывания/остановки процесса.",
+ "leaveallgroupscmdstart": "Начинаю покидать все группы...",
+
+ "blockcmdsuccess": "profileid заблокирован во всех бот-аккаунтах.",
+ "unblockcmdsuccess": "profileid разблокирован во всех бот-аккаунтах.",
+
+ "evalcmdturnedoff": "Команда eval была отключена!",
+ "evalcmdlogininfoblock": "Ваш код включает «logininfo». В целях защиты паролей это запрещено.",
+
+ "childbotmessage": "Это один аккаунт, работающий в группе ботов.\nПожалуйста, добавьте главного бота и отправьте ему сообщение cmdprefixhelp.\nЕсли вы хотите проверить, в чём дело, введите: cmdprefixabout"
+}
\ No newline at end of file
diff --git a/src/dataManager/dataCheck.js b/src/dataManager/dataCheck.js
index 99c9d3aa..18582ae9 100644
--- a/src/dataManager/dataCheck.js
+++ b/src/dataManager/dataCheck.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 15:32:44
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -46,7 +46,7 @@ DataManager.prototype.checkData = function() {
// Filter all invalid ownerids which got replaced with null by processData()
if (this.cachefile.ownerid.filter(e => e != null).length == 0) {
logWarn("error", "Error: You did not set at least one valid ownerid in config.json! Aborting!");
- return reject("no-ownerid-found");
+ return reject(new Error("No ownerid found!"));
}
@@ -90,7 +90,7 @@ DataManager.prototype.checkData = function() {
if (Object.keys(this.logininfo).length == 0) { // Check real quick if logininfo is empty
logWarn("error", `${logger.colors.fgred}Your accounts.txt or logininfo.json file doesn't seem to contain any valid login credentials! Aborting...`, true);
- return reject("no-logininfo-found");
+ return reject(new Error("No logininfo found!"));
}
if (this.config.maxOwnerComments < 1) {
logWarn("info", `${logger.colors.fgred}Your maxOwnerComments value in config.json can't be smaller than 1! Automatically setting it to 1...`, true);
@@ -105,14 +105,14 @@ DataManager.prototype.checkData = function() {
}
if (this.config.commentdelay * maxCommentsOverall > 2147483647) { // Check for 32-bit integer limit for commentcmd timeout
logWarn("error", `${logger.colors.fgred}Your maxComments and/or maxOwnerComments and/or commentdelay value in the config are too high.\n Please lower these values so that 'commentdelay * maxComments' is not bigger than 2147483647 (32-bit integer limit).\n\nThis will otherwise cause an error when trying to comment. Aborting...\n`, true);
- return reject("commentdelay-times-maxcomments-exceeds-32bit-limit");
+ return reject(new Error("Commentdelay times maxcomments exceeds 32bit integer limit!"));
}
if (this.config.randomizeAccounts && Object.keys(this.logininfo).length <= 5 && maxCommentsOverall > Object.keys(this.logininfo).length * 2) {
logWarn("warn", `${logger.colors.fgred}I wouldn't recommend using randomizeAccounts with 5 or less accounts when each account can/has to comment multiple times. The chance of an account getting a cooldown is higher.\n Please make sure your commentdelay is set adequately to reduce the chance of this happening.`, true);
}
if (this.advancedconfig.loginDelay < 500) { // Don't allow a logindelay below 500ms
logWarn("error", `${logger.colors.fgred}I won't allow a logindelay below 500ms as this will probably get you blocked by Steam nearly instantly. I recommend setting it to 2500.\n If you are using one proxy per account you might try setting it to 500 (on your own risk!). Aborting...`, true);
- return reject("logindelay-below-500ms");
+ return reject(new Error("Logindelay is set below 500ms!"));
}
if (this.advancedconfig.lastQuotesSize >= this.quotes) { // Force clear lastQuotes array if we have less or equal amount of quotes to choose from than lastQuotesSize to avoid infinite loop
logWarn("warn", "lastQuoteSize in 'advancedconfig.json' is greater or equal than the amount of quotes found in 'quotes.txt'. I'm therefore unable to filter recently used quotes when choosing a new one!", true);
diff --git a/src/dataManager/dataExport.js b/src/dataManager/dataExport.js
new file mode 100644
index 00000000..91cb8b80
--- /dev/null
+++ b/src/dataManager/dataExport.js
@@ -0,0 +1,184 @@
+/*
+ * File: dataExport.js
+ * Project: steam-comment-service-bot
+ * Created Date: 04.07.2023 21:29:42
+ * Author: 3urobeat
+ *
+ * Last Modified: 04.07.2023 23:04:19
+ * Modified By: 3urobeat
+ *
+ * Copyright (c) 2023 3urobeat
+ *
+ * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
+ * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License along with this program. If not, see .
+ */
+
+
+const fs = require("fs");
+
+const DataManager = require("./dataManager.js");
+
+
+/**
+ * Writes (all) files imported by DataManager back to the disk
+ */
+DataManager.prototype.writeAllFilesToDisk = function() {
+ this.writeCachefileToDisk();
+ this.writeDatafileToDisk();
+ this.writeConfigToDisk();
+ this.writeAdvancedconfigToDisk();
+ this.writeLogininfoToDisk();
+ this.writeProxiesToDisk();
+ this.writeQuotesToDisk();
+};
+
+
+/**
+ * Writes cachefile to cache.json on disk
+ */
+DataManager.prototype.writeCachefileToDisk = function() {
+ logger("debug", "DataManager dataExport: Writing to cache.json...");
+
+ fs.writeFile("./src/data/cache.json", JSON.stringify(this.cachefile, null, 4), (err) => {
+ if (err) logger("error", "DataManager: Error writing cachefile to cache.json: " + err);
+ });
+};
+
+
+/**
+ * Writes datafile to data.json on disk
+ */
+DataManager.prototype.writeDatafileToDisk = function() {
+ logger("debug", "DataManager dataExport: Writing to data.json...");
+
+ fs.writeFile("./src/data/data.json", JSON.stringify(this.datafile, null, 4), (err) => {
+ if (err) logger("error", "DataManager: Error writing datafile to data.json: " + err);
+ });
+};
+
+
+/**
+ * Writes config to config.json on disk
+ */
+DataManager.prototype.writeConfigToDisk = function() {
+ logger("debug", "DataManager dataExport: Writing to config.json...");
+
+ // Get arrays on one line
+ let stringifiedconfig = JSON.stringify(this.config, function(k, v) { // Credit: https://stackoverflow.com/a/46217335/12934162
+ if (v instanceof Array) return JSON.stringify(v);
+ return v;
+ }, 4)
+ .replace(/"\[/g, "[")
+ .replace(/\]"/g, "]")
+ .replace(/\\"/g, '"')
+ .replace(/""/g, '""');
+
+ fs.writeFile(srcdir + "/../config.json", stringifiedconfig, (err) => {
+ if (err) logger("error", "DataManager: Error writing config to config.json: " + err);
+ });
+};
+
+
+/**
+ * Writes advancedconfig to advancedconfig.json on disk
+ */
+DataManager.prototype.writeAdvancedconfigToDisk = function() {
+ logger("debug", "DataManager dataExport: Writing to advancedconfig.json...");
+
+ // Get arrays on one line
+ let stringifiedadvancedconfig = JSON.stringify(this.advancedconfig, function(k, v) { // Credit: https://stackoverflow.com/a/46217335/12934162
+ if (v instanceof Array) return JSON.stringify(v);
+ return v;
+ }, 4)
+ .replace(/"\[/g, "[")
+ .replace(/\]"/g, "]")
+ .replace(/\\"/g, '"')
+ .replace(/""/g, '""');
+
+ fs.writeFile(srcdir + "/../advancedconfig.json", stringifiedadvancedconfig, (err) => {
+ if (err) logger("error", "DataManager: Error writing advancedconfig to advancedconfig.json: " + err);
+ });
+};
+
+
+/**
+ * Writes logininfo to logininfo.json and accounts.txt on disk, depending on which of the files exist
+ */
+DataManager.prototype.writeLogininfoToDisk = function() {
+
+ if (fs.existsSync(srcdir + "/../logininfo.json")) {
+ logger("debug", "DataManager dataExport: Writing to logininfo.json...");
+
+ let logininfojson = {};
+
+ // Re-Construct logininfo object. Iterate over bots instead of logininfo to retain a changed bots hierarchy
+ for (let e of this.controller.getBots("*")) {
+ logininfojson[`bot${e.index}`] = [ e.loginData.logOnOptions.accountName, e.loginData.logOnOptions.password, e.loginData.logOnOptions.sharedSecret ];
+ }
+
+ // Get arrays on one line
+ let stringifiedlogininfo = JSON.stringify(logininfojson, function(k, v) { // Credit: https://stackoverflow.com/a/46217335/12934162
+ if (v instanceof Array) return JSON.stringify(v);
+ return v;
+ }, 4)
+ .replace(/"\[/g, "[")
+ .replace(/\]"/g, "]")
+ .replace(/\\"/g, '"')
+ .replace(/""/g, '""');
+
+ fs.writeFile(srcdir + "/../logininfo.json", stringifiedlogininfo, (err) => {
+ if (err) logger("error", "DataManager: Error writing logininfo to logininfo.json: " + err);
+ });
+ }
+
+ if (fs.existsSync(srcdir + "/../accounts.txt")) {
+ logger("debug", "DataManager dataExport: Writing to accounts.txt...");
+
+ let accountstxt = [ "//Comment: This file is used to provide your bot accounts in the form of username:password. Read the instructions here: https://github.com/3urobeat/steam-comment-service-bot#accounts" ]; // Re-add comment
+
+ // Re-construct accounts.txt string. Iterate over bots instead of logininfo to retain a changed bots hierarchy
+ for (let e of this.controller.getBots("*")) {
+ if (e.loginData.logOnOptions.sharedSecret) accountstxt.push(`${e.loginData.logOnOptions.accountName}:${e.loginData.logOnOptions.password}:${e.loginData.logOnOptions.sharedSecret}`);
+ else accountstxt.push(`${e.loginData.logOnOptions.accountName}:${e.loginData.logOnOptions.password}`);
+ }
+
+ fs.writeFile(srcdir + "/../accounts.txt", accountstxt.join("\n"), (err) => {
+ if (err) logger("error", "DataManager: Error writing accounts to accounts.txt: " + err);
+ });
+ }
+
+};
+
+
+/**
+ * Writes proxies to proxies.txt on disk
+ */
+DataManager.prototype.writeProxiesToDisk = function() {
+ logger("debug", "DataManager dataExport: Writing to proxies.txt...");
+
+ let comment = "//Comment: This file is used to provide proxies to spread your accounts over multiple IPs. Read the instructions here: https://github.com/3urobeat/steam-comment-service-bot/wiki/Adding-proxies";
+
+ fs.writeFile(srcdir + "/../proxies.txt", comment + this.proxies.join("\n"), (err) => {
+ if (err) logger("error", "DataManager: Error writing proxies to proxies.txt: " + err);
+ });
+};
+
+
+/**
+ * Writes quotes to quotes.txt on disk
+ */
+DataManager.prototype.writeQuotesToDisk = function() {
+ logger("debug", "DataManager dataExport: Writing to quotes.txt...");
+ let quotesArr = [];
+
+ // Replace every \n with \\n so that writeFile won't parse them to actual newlines
+ this.quotes.forEach(e => quotesArr.push(e.replace(/\n/g, "\\n")));
+
+ fs.writeFile(srcdir + "/../quotes.txt", quotesArr.join("\n"), (err) => {
+ if (err) logger("error", "DataManager: Error writing quotes to quotes.txt: " + err);
+ });
+};
+
+
+// No function to write language
\ No newline at end of file
diff --git a/src/dataManager/dataImport.js b/src/dataManager/dataImport.js
index 04dbc57d..e6f3515a 100644
--- a/src/dataManager/dataImport.js
+++ b/src/dataManager/dataImport.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 22:47:47
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -15,9 +15,9 @@
*/
-const fs = require("fs");
-const path = require("path");
-const nedb = require("@seald-io/nedb");
+const fs = require("fs");
+const path = require("path");
+const nedb = require("@seald-io/nedb");
const DataManager = require("./dataManager.js");
@@ -25,327 +25,315 @@ const DataManager = require("./dataManager.js");
* Internal: Loads all config & data files from disk and handles potential errors
* @returns {Promise.} Resolves promise when all files have been loaded successfully. The function will log an error and terminate the application should a fatal error occur.
*/
-DataManager.prototype._importFromDisk = function() {
+DataManager.prototype._importFromDisk = async function () {
let _this = this; // Make this accessible within the functions below
- return new Promise((resolve) => {
- (async () => { // Lets us use await inside a Promise without creating an antipattern
-
- function loadCache() {
- return new Promise((resolve) => {
- try {
- resolve(require(srcdir + "/data/cache.json"));
-
- } catch (err) {
-
+ /* eslint-disable jsdoc/require-jsdoc */
+ function loadCache() {
+ return new Promise((resolve) => {
+ try {
+ resolve(require(srcdir + "/data/cache.json"));
+ } catch (err) {
+ if (err) {
+ logger("", "", true, true);
+ logger("warn", "cache.json seems to have lost it's data/is corrupted. Trying to write/create...", true, true);
+
+ // Create the underlying folder structure to avoid error when trying to write the downloaded file
+ fs.mkdirSync(path.dirname("./src/data/cache.json"), { recursive: true });
+
+ fs.writeFile("./src/data/cache.json", "{}", (err) => {
+ // Write empty valid json
if (err) {
- logger("", "", true, true);
- logger("warn", "cache.json seems to have lost it's data/is corrupted. Trying to write/create...", true, true);
-
- // Create the underlying folder structure to avoid error when trying to write the downloaded file
- fs.mkdirSync(path.dirname("./src/data/cache.json"), { recursive: true });
-
- fs.writeFile("./src/data/cache.json", "{}", (err) => { // Write empty valid json
- if (err) {
- logger("error", "Error writing {} to cache.json.\nPlease do this manually: Go into 'src' folder, open 'cache.json', write '{}' and save.\nOtherwise the bot will always crash.\nError: " + err + "\n\nAborting...", true);
- return _this.controller.stop(); // Abort since writeFile was unable to write and any further execution would crash
- } else {
- logger("info", "Successfully cleared/created cache.json.\n", true, true);
- resolve(require(srcdir + "/data/cache.json"));
- }
- });
+ logger("error", "Error writing {} to cache.json.\nPlease do this manually: Go into 'src' folder, open 'cache.json', write '{}' and save.\nOtherwise the bot will always crash.\nError: " + err + "\n\nAborting...", true);
+ return _this.controller.stop(); // Abort since writeFile was unable to write and any further execution would crash
+ } else {
+ logger("info", "Successfully cleared/created cache.json.\n", true, true);
+ resolve(require(srcdir + "/data/cache.json"));
}
- }
- });
+ });
+ }
}
-
- function loadData() {
- return new Promise((resolve) => {
- try {
- resolve(require(srcdir + "/data/data.json"));
-
- } catch (err) {
-
- if (err) { // Corrupted!
- logger("", "", true, true);
- logger("warn", "'data.json' seems to have lost it's data/is corrupted. Trying to restore from backup...", true);
-
- // Check if cache.json has a backup of config.json and try to restore it. If not then pull the file directly from GitHub.
- if (_this.cachefile.datajson) _this._restoreBackup("data.json", srcdir + "/data/data.json", _this.cachefile.datajson, "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/master/src/data/data.json", resolve);
- else _this._pullNewFile("data.json", "./src/data/data.json", resolve);
- }
- }
- });
+ });
+ }
+
+ function loadData() {
+ return new Promise((resolve) => {
+ try {
+ resolve(require(srcdir + "/data/data.json"));
+ } catch (err) {
+ if (err) {
+ // Corrupted!
+ logger("", "", true, true);
+ logger("warn", "'data.json' seems to have lost it's data/is corrupted. Trying to restore from backup...", true);
+
+ // Check if cache.json has a backup of config.json and try to restore it. If not then pull the file directly from GitHub.
+ if (_this.cachefile.datajson) _this._restoreBackup("data.json", srcdir + "/data/data.json", _this.cachefile.datajson, "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/master/src/data/data.json", resolve);
+ else _this._pullNewFile("data.json", "./src/data/data.json", resolve);
+ }
}
-
- function loadConfig() {
- return new Promise((resolve) => {
- try {
- resolve(require(srcdir + "/../config.json"));
-
- } catch (err) {
-
- if (err) { // Corrupted!
- logger("", "", true, true);
- logger("warn", "'config.json' seems to have lost it's data/is corrupted. Trying to restore from backup...", true);
-
- let restoreTimeout = 0; // Allow the following firststart check to delay the restore process so the user has time to read the info message
-
- // Display an informational message about what happened if datafile firststart is true
- if (_this.datafile && _this.datafile.firststart) {
- logger("", logger.colors.fgred + "\n--------------------------------------" + logger.colors.reset, true);
- logger("", `${logger.colors.fgcyan}Hey!${logger.colors.reset} It seems like this is your first start and you made a formatting mistake in your '${logger.colors.fgcyan}config.json${logger.colors.reset}' file. Because of this I'm sadly ${logger.colors.fgcyan}unable to load${logger.colors.reset} the file.`, true);
- logger("", `You can stop the bot now by pressing ${logger.colors.fgcyan}CTRL+C${logger.colors.reset} to fix the issue. Please make sure that you exactly follow the format of the provided 'config.json' when filling in your settings.`, true);
- logger("", `Take a look at the default config here and pay attention to every ${logger.colors.fgcyan}"${logger.colors.reset} and ${logger.colors.fgcyan},${logger.colors.reset} as you most likely forgot one of them: ${logger.colors.fgcyan}${logger.colors.underscore}https://github.com/3urobeat/steam-comment-service-bot/blob/master/config.json${logger.colors.reset}`, true);
- logger("", `You can also take a look at this blog post to learn more about JSON formatting: ${logger.colors.fgcyan}${logger.colors.underscore}https://stackoverflow.blog/2022/06/02/a-beginners-guide-to-json-the-data-format-for-the-internet/${logger.colors.reset}`, true);
- logger("", logger.colors.fgred + "--------------------------------------\n" + logger.colors.reset, true);
- logger("", "Restoring the config to default in 15 seconds...", true, false, logger.animation("waiting"));
-
- restoreTimeout = 15000; // Delay restore process by 10 secs
- }
-
- // Wait restoreTimeout ms if set by firststart check from above
- setTimeout(() => {
- // Check if cache.json has a backup of config.json and try to restore it. If not then pull the file directly from GitHub.
- if (_this.cachefile.configjson) _this._restoreBackup("config.json", srcdir + "/../config.json", _this.cachefile.configjson, "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/master/config.json", resolve);
- else _this._pullNewFile("config.json", "./config.json", resolve);
- }, restoreTimeout);
- }
+ });
+ }
+
+ function loadConfig() {
+ return new Promise((resolve) => {
+ try {
+ resolve(require(srcdir + "/../config.json"));
+ } catch (err) {
+ if (err) {
+ // Corrupted!
+ logger("", "", true, true);
+ logger("warn", "'config.json' seems to have lost it's data/is corrupted. Trying to restore from backup...", true);
+
+ let restoreTimeout = 0; // Allow the following firststart check to delay the restore process so the user has time to read the info message
+
+ // Display an informational message about what happened if datafile firststart is true
+ if (_this.datafile && _this.datafile.firststart) {
+ logger("", logger.colors.fgred + "\n--------------------------------------" + logger.colors.reset, true);
+ logger("", `${logger.colors.fgcyan}Hey!${logger.colors.reset} It seems like this is your first start and you made a formatting mistake in your '${logger.colors.fgcyan}config.json${logger.colors.reset}' file. Because of this I'm sadly ${logger.colors.fgcyan}unable to load${logger.colors.reset} the file.`, true);
+ logger("", `You can stop the bot now by pressing ${logger.colors.fgcyan}CTRL+C${logger.colors.reset} to fix the issue. Please make sure that you exactly follow the format of the provided 'config.json' when filling in your settings.`, true);
+ logger("", `Take a look at the default config here and pay attention to every ${logger.colors.fgcyan}"${logger.colors.reset} and ${logger.colors.fgcyan},${logger.colors.reset} as you most likely forgot one of them: ${logger.colors.fgcyan}${logger.colors.underscore}https://github.com/3urobeat/steam-comment-service-bot/blob/master/config.json${logger.colors.reset}`, true);
+ logger("", `You can also take a look at this blog post to learn more about JSON formatting: ${logger.colors.fgcyan}${logger.colors.underscore}https://stackoverflow.blog/2022/06/02/a-beginners-guide-to-json-the-data-format-for-the-internet/${logger.colors.reset}`, true);
+ logger("", logger.colors.fgred + "--------------------------------------\n" + logger.colors.reset, true);
+ logger("", "Restoring the config to default in 15 seconds...", true, false, logger.animation("waiting"));
+
+ restoreTimeout = 15000; // Delay restore process by 10 secs
}
- });
- }
- function loadAdvancedConfig() {
- return new Promise((resolve) => {
- try {
- resolve(require(srcdir + "/../advancedconfig.json"));
- } catch (err) {
- if (err) { // Corrupted!
- logger("", "", true, true);
- logger("warn", "advancedconfig.json seems to have lost it's data/is corrupted. Trying to restore from backup...", true);
-
- // Check if cache.json has a backup of config.json and try to restore it. If not then pull the file directly from GitHub.
- if (_this.cachefile.advancedconfigjson) _this._restoreBackup("advancedconfig.json", srcdir + "/../advancedconfig.json", _this.cachefile.advancedconfigjson, "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/master/advancedconfig.json", resolve);
- else _this._pullNewFile("advancedconfig.json", "./advancedconfig.json", resolve);
- }
- }
- });
+ // Wait restoreTimeout ms if set by firststart check from above
+ setTimeout(() => {
+ // Check if cache.json has a backup of config.json and try to restore it. If not then pull the file directly from GitHub.
+ if (_this.cachefile.configjson) _this._restoreBackup("config.json", srcdir + "/../config.json", _this.cachefile.configjson, "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/master/config.json", resolve);
+ else _this._pullNewFile("config.json", "./config.json", resolve);
+ }, restoreTimeout);
+ }
}
-
- function loadLoginInfo() {
- return new Promise((resolve) => {
- let logininfo = {};
-
- // Check accounts.txt first so we can ignore potential syntax errors in logininfo
- if (fs.existsSync("./accounts.txt")) {
- let data = fs.readFileSync("./accounts.txt", "utf8").split("\n");
-
- if (data.length > 0 && data[0].startsWith("//Comment")) data = data.slice(1); // Remove comment from array
-
- if (data != "") {
- logininfo = {}; // Set empty object
- data.forEach((e) => {
- if (e.length < 2) return; // If the line is empty ignore it to avoid issues like this: https://github.com/3urobeat/steam-comment-service-bot/issues/80
- e = e.split(":");
- e[e.length - 1] = e[e.length - 1].replace("\r", ""); // Remove Windows next line character from last index (which has to be the end of the line)
-
- // Format logininfo object and use accountName as key to allow the order to change
- logininfo[e[0]] = {
- accountName: e[0],
- password: e[1],
- sharedSecret: e[2],
- steamGuardCode: null,
- machineName: `${_this.datafile.mestr}'s Comment Bot`, // For steam-user
- deviceFriendlyName: `${_this.datafile.mestr}'s Comment Bot`, // For steam-session
- };
- });
-
- logger("info", `Found ${Object.keys(logininfo).length} accounts in accounts.txt, not checking for logininfo.json...`, false, true, logger.animation("loading"));
-
- return resolve(logininfo);
- }
- }
-
- // Check logininfo for Syntax errors and display custom error message
- try {
- // Only check if file exists (it is not shipped by default anymore since 2.12.1). If it doesn't an empty obj will be returned, leading to empty logininfo err msg in checkData()
- if (fs.existsSync("./logininfo.json")) {
- logininfo = require(srcdir + "/../logininfo.json");
-
- // Reformat to use new logininfo object structure and use accountName as key instead of bot0 etc to allow the order to change
- Object.keys(logininfo).forEach((k) => {
- logininfo[logininfo[k][0]] = {
- accountName: logininfo[k][0],
- password: logininfo[k][1],
- sharedSecret: logininfo[k][2],
- steamGuardCode: null,
- machineName: `${_this.datafile.mestr}'s Comment Bot`, // For steam-user
- deviceFriendlyName: `${_this.datafile.mestr}'s Comment Bot` // For steam-session
- };
-
- delete logininfo[k]; // Remove old entry
- });
- }
-
- logger("info", `Found ${Object.keys(logininfo).length} accounts in logininfo.json...`, false, true, logger.animation("loading"));
-
- resolve(logininfo);
- } catch (err) {
- logger("error", "It seems like you made a mistake in your logininfo.json. Please check if your Syntax looks exactly like in the example/template and try again.\n " + err, true);
- return _this.controller.stop();
- }
- });
+ });
+ }
+
+ function loadAdvancedConfig() {
+ return new Promise((resolve) => {
+ try {
+ resolve(require(srcdir + "/../advancedconfig.json"));
+ } catch (err) {
+ if (err) {
+ // Corrupted!
+ logger("", "", true, true);
+ logger("warn", "advancedconfig.json seems to have lost it's data/is corrupted. Trying to restore from backup...", true);
+
+ // Check if cache.json has a backup of config.json and try to restore it. If not then pull the file directly from GitHub.
+ if (_this.cachefile.advancedconfigjson) _this._restoreBackup("advancedconfig.json", srcdir + "/../advancedconfig.json", _this.cachefile.advancedconfigjson, "https://raw.githubusercontent.com/3urobeat/steam-comment-service-bot/master/advancedconfig.json", resolve);
+ else _this._pullNewFile("advancedconfig.json", "./advancedconfig.json", resolve);
+ }
}
+ });
+ }
+
+ function loadLoginInfo() {
+ return new Promise((resolve) => {
+ let logininfo = {};
+
+ // Check accounts.txt first so we can ignore potential syntax errors in logininfo
+ if (fs.existsSync("./accounts.txt")) {
+ let data = fs.readFileSync("./accounts.txt", "utf8").split("\n");
+
+ if (data.length > 0 && data[0].startsWith("//Comment")) data = data.slice(1); // Remove comment from array
+
+ if (data != "") {
+ logininfo = {}; // Set empty object
+ data.forEach((e) => {
+ if (e.length < 2) return; // If the line is empty ignore it to avoid issues like this: https://github.com/3urobeat/steam-comment-service-bot/issues/80
+ e = e.split(":");
+ e[e.length - 1] = e[e.length - 1].replace("\r", ""); // Remove Windows next line character from last index (which has to be the end of the line)
+
+ // Format logininfo object and use accountName as key to allow the order to change
+ logininfo[e[0]] = {
+ accountName: e[0],
+ password: e[1],
+ sharedSecret: e[2],
+ steamGuardCode: null,
+ machineName: `${_this.datafile.mestr}'s Comment Bot`, // For steam-user
+ deviceFriendlyName: `${_this.datafile.mestr}'s Comment Bot`, // For steam-session
+ };
+ });
- function loadProxies() {
- return new Promise((resolve) => {
- let proxies = []; // When the file is just created there can't be proxies in it (this bot doesn't support magic)
+ logger("info", `Found ${Object.keys(logininfo).length} accounts in accounts.txt, not checking for logininfo.json...`, false, true, logger.animation("loading"));
- if (!fs.existsSync("./proxies.txt")) {
- logger("info", "Creating proxies.txt file as it doesn't exist yet...", false, true, logger.animation("loading"));
+ return resolve(logininfo);
+ }
+ }
- fs.writeFile(srcdir + "/../proxies.txt", "", err => {
- if (err) logger("error", "error creating proxies.txt file: " + err);
- else logger("info", "Successfully created proxies.txt file.", false, true, logger.animation("loading"));
- });
+ // Check logininfo for Syntax errors and display custom error message
+ try {
+ // Only check if file exists (it is not shipped by default anymore since 2.12.1). If it doesn't an empty obj will be returned, leading to empty logininfo err msg in checkData()
+ if (fs.existsSync("./logininfo.json")) {
+ logininfo = require(srcdir + "/../logininfo.json");
+
+ // Reformat to use new logininfo object structure and use accountName as key instead of bot0 etc to allow the order to change
+ Object.keys(logininfo).forEach((k) => {
+ logininfo[logininfo[k][0]] = {
+ accountName: logininfo[k][0],
+ password: logininfo[k][1],
+ sharedSecret: logininfo[k][2],
+ steamGuardCode: null,
+ machineName: `${_this.datafile.mestr}'s Comment Bot`, // For steam-user
+ deviceFriendlyName: `${_this.datafile.mestr}'s Comment Bot`, // For steam-session
+ };
+
+ delete logininfo[k]; // Remove old entry
+ });
+ }
- } else { // File does seem to exist so now we can try and read it
- proxies = fs.readFileSync("./proxies.txt", "utf8").split("\n");
- proxies = proxies.filter(str => str != ""); // Remove empty lines
+ logger("info", `Found ${Object.keys(logininfo).length} accounts in logininfo.json...`, false, true, logger.animation("loading"));
- if (proxies.length > 0 && proxies[0].startsWith("//Comment")) proxies = proxies.slice(1); // Remove comment from array
+ resolve(logininfo);
+ } catch (err) {
+ logger("error", "It seems like you made a mistake in your logininfo.json. Please check if your Syntax looks exactly like in the example/template and try again.\n " + err, true);
+ return _this.controller.stop();
+ }
+ });
+ }
- if (_this.advancedconfig.useLocalIP) proxies.unshift(null); // Add no proxy (local ip) if useLocalIP is true
+ function loadProxies() {
+ return new Promise((resolve) => {
+ let proxies = []; // When the file is just created there can't be proxies in it (this bot doesn't support magic)
- // Check if no proxies were found (can only be the case when useLocalIP is false)
- if (proxies.length == 0) {
- logger("", "", true);
- logger("error", "useLocalIP is turned off in advancedconfig.json but I couldn't find any proxies in proxies.txt!\n Aborting as I don't have at least one IP to log in with!", true);
- return _this.controller.stop();
- }
- }
+ if (!fs.existsSync("./proxies.txt")) {
+ logger("info", "Creating proxies.txt file as it doesn't exist yet...", false, true, logger.animation("loading"));
- resolve(proxies);
+ fs.writeFile(srcdir + "/../proxies.txt", "", (err) => {
+ if (err) logger("error", "error creating proxies.txt file: " + err);
+ else logger("info", "Successfully created proxies.txt file.", false, true, logger.animation("loading"));
});
- }
+ } else {
+ // File does seem to exist so now we can try and read it
+ proxies = fs.readFileSync("./proxies.txt", "utf8").split("\n");
+ proxies = proxies.filter((str) => str != ""); // Remove empty lines
- function loadQuotes() {
- return new Promise((resolve) => {
- let quotes = [];
-
- // Pull new file and call loadQuotes again, wait for it to resolve, and then resolve this promise. This is slightly hacky but relatively clean
- if (!fs.existsSync(srcdir + "/../quotes.txt")) {
- return _this._pullNewFile("quotes.txt", "./quotes.txt", async () => {
- resolve(await loadQuotes());
- }, true);
- }
+ if (proxies.length > 0 && proxies[0].startsWith("//Comment")) proxies = proxies.slice(1); // Remove comment from array
- quotes = fs.readFileSync(srcdir + "/../quotes.txt", "utf8").split("\n"); // Get all quotes from the quotes.txt file into an array
- quotes = quotes.filter(str => str != ""); // Remove empty quotes
+ if (_this.advancedconfig.useLocalIP) proxies.unshift(null); // Add no proxy (local ip) if useLocalIP is true
- quotes.forEach((e, i) => { // Multi line strings that contain \n will get split to \\n -> remove second \ so that node-steamcommunity understands the quote when commenting
- if (e.length > 999) {
- logger("warn", `The quote.txt line ${i} is longer than the limit of 999 characters. This quote will be ignored for now.`, true, false, logger.animation("loading"));
- quotes.splice(i, 1); // Remove this item from the array
- return;
- }
+ // Check if no proxies were found (can only be the case when useLocalIP is false)
+ if (proxies.length == 0) {
+ logger("", "", true);
+ logger("error", "useLocalIP is turned off in advancedconfig.json but I couldn't find any proxies in proxies.txt!\n Aborting as I don't have at least one IP to log in with!", true);
+ return _this.controller.stop();
+ }
+ }
- quotes[i] = e.replace(/\\n/g, "\n").replace("\\n", "\n");
- });
+ resolve(proxies);
+ });
+ }
- if (quotes.length == 0) { // Check if quotes.txt is empty to avoid errors further down when trying to comment
- logger("error", `${logger.colors.fgred}You haven't put any comment quotes into the quotes.txt file! Aborting...`, true);
- return _this.controller.stop();
- } else {
- logger("info", `Successfully loaded ${quotes.length} quotes from quotes.txt...`, false, true, logger.animation("loading"));
- }
+ function loadQuotes() {
+ return new Promise((resolve) => {
+ let quotes = [];
- resolve(quotes);
- });
+ // Pull new file and call loadQuotes again, wait for it to resolve, and then resolve this promise. This is slightly hacky but relatively clean
+ if (!fs.existsSync(srcdir + "/../quotes.txt")) {
+ return _this._pullNewFile("quotes.txt", "./quotes.txt", async () => { resolve(await loadQuotes()); }, true);
}
- function loadLanguage() {
- return new Promise((resolve) => {
- try {
- resolve(require(srcdir + "/data/lang/defaultlang.json"));
- } catch (err) {
- if (err) { // Corrupted!
- logger("", "", true, true);
-
- // Pull the file directly from GitHub.
- _this._pullNewFile("defaultlang.json", "./src/data/lang/defaultlang.json", resolve);
- }
- }
- });
+ quotes = fs.readFileSync(srcdir + "/../quotes.txt", "utf8").split("\n"); // Get all quotes from the quotes.txt file into an array
+ quotes = quotes.filter((str) => str != ""); // Remove empty quotes
+
+ quotes.forEach((e, i) => {
+ // Multi line strings that contain \n will get split to \\n -> remove second \ so that node-steamcommunity understands the quote when commenting
+ if (e.length > 999) {
+ logger("warn", `The quote.txt line ${i} is longer than the limit of 999 characters. This quote will be ignored for now.`, true, false, logger.animation("loading"));
+ quotes.splice(i, 1); // Remove this item from the array
+ return;
+ }
+
+ quotes[i] = e.replace(/\\n/g, "\n").replace("\\n", "\n");
+ });
+
+ if (quotes.length == 0) {
+ // Check if quotes.txt is empty to avoid errors further down when trying to comment
+ logger("error", `${logger.colors.fgred}You haven't put any comment quotes into the quotes.txt file! Aborting...`, true);
+ return _this.controller.stop();
+ } else {
+ logger("info", `Successfully loaded ${quotes.length} quotes from quotes.txt...`, false, true, logger.animation("loading"));
}
- function loadCustomLang() {
- return new Promise((resolve) => {
- // Check before trying to import if the user even created the file
- if (fs.existsSync(srcdir + "/../customlang.json")) {
- let customlang;
- let customlangkeys = 0;
-
- // Try importing customlang.json
- try {
- customlang = require(srcdir + "/../customlang.json");
- } catch (err) {
- logger("error", "It seems like you made a mistake (probably Syntax) in your customlang.json! I will not use any custom message.\nError: " + err);
-
- resolve(_this.lang); // Resolve with default lang object
- }
-
- // Overwrite values in lang object with values from customlang
- Object.keys(customlang).forEach((e, i) => {
- if (e != "" && e != "note") {
- _this.lang[e] = customlang[e]; // Overwrite each defaultlang key with a corresponding customlang key if one is set
-
- customlangkeys++;
- }
-
- if (i == Object.keys(customlang).length - 1) { // Check for last iteration
- if (customlangkeys > 0) logger("info", `${customlangkeys} customlang key imported!`, false, true, logger.animation("loading"));
- else logger("info", "No customlang keys found.", false, true, logger.animation("loading"));
-
- resolve(_this.lang); // Resolve lang object with our new keys
- }
- });
+ resolve(quotes);
+ });
+ }
+
+ function loadLanguage() {
+ return new Promise((resolve) => {
+ try {
+ resolve(require(srcdir + "/data/lang/defaultlang.json"));
+ } catch (err) {
+ if (err) {
+ // Corrupted!
+ logger("", "", true, true);
+
+ // Pull the file directly from GitHub.
+ _this._pullNewFile("defaultlang.json", "./src/data/lang/defaultlang.json", resolve);
+ }
+ }
+ });
+ }
+
+ function loadCustomLang() {
+ return new Promise((resolve) => {
+ // Check before trying to import if the user even created the file
+ if (fs.existsSync(srcdir + "/../customlang.json")) {
+ let customlang;
+ let customlangkeys = 0;
+
+ // Try importing customlang.json
+ try {
+ customlang = require(srcdir + "/../customlang.json");
+ } catch (err) {
+ logger("error", "It seems like you made a mistake (probably Syntax) in your customlang.json! I will not use any custom message.\nError: " + err);
+
+ resolve(_this.lang); // Resolve with default lang object
+ }
+
+ // Overwrite values in lang object with values from customlang
+ Object.keys(customlang).forEach((e, i) => {
+ if (e != "" && e != "note") {
+ _this.lang[e] = customlang[e]; // Overwrite each defaultlang key with a corresponding customlang key if one is set
+
+ customlangkeys++;
+ }
- } else {
+ if (i == Object.keys(customlang).length - 1) {
+ // Check for last iteration
+ if (customlangkeys > 0) logger("info", `${customlangkeys} customlang key imported!`, false, true, logger.animation("loading"));
+ else logger("info", "No customlang keys found.", false, true, logger.animation("loading"));
- logger("info", "No customlang.json file found...", false, true, logger.animation("loading"));
- resolve(_this.lang); // Resolve with default lang object
+ resolve(_this.lang); // Resolve lang object with our new keys
}
});
+ } else {
+ logger("info", "No customlang.json file found...", false, true, logger.animation("loading"));
+ resolve(_this.lang); // Resolve with default lang object
}
-
-
- // Call all functions from above after another. This must be done async to avoid a check failing that depends on something from a previous function
- logger("info", "Importing data files and settings...", false, true, logger.animation("loading"));
-
- this.cachefile = await loadCache();
- this.datafile = await loadData();
- this.config = await loadConfig();
- this.advancedconfig = await loadAdvancedConfig();
- this.logininfo = await loadLoginInfo();
- this.proxies = await loadProxies();
- this.quotes = await loadQuotes();
- this.lang = await loadLanguage();
- this.lang = await loadCustomLang();
-
- this.lastCommentDB = new nedb({ filename: srcdir + "/data/lastcomment.db", autoload: true }); // Autoload
- this.ratingHistoryDB = new nedb({ filename: srcdir + "/data/ratingHistory.db", autoload: true });
- this.tokensDB = new nedb({ filename: srcdir + "/data/tokens.db", autoload: true });
-
-
- // Check tokens.db every 24 hours for expired tokens to allow users to refresh them beforehand
- this._startExpiringTokensCheckInterval();
-
-
- // Resolve our promise to let caller know the dataImport is finished
- resolve();
-
- })();
- });
-
-};
\ No newline at end of file
+ });
+ }
+ /* eslint-enable jsdoc/require-jsdoc */
+
+ // Call all functions from above after another. This must be done async to avoid a check failing that depends on something from a previous function. We sadly cannot use Promise.all() because of this.
+ logger("info", "Importing data files and settings...", false, true, logger.animation("loading"));
+
+ this.cachefile = await loadCache();
+ this.datafile = await loadData();
+ this.config = await loadConfig();
+ this.advancedconfig = await loadAdvancedConfig();
+ this.logininfo = await loadLoginInfo();
+ this.proxies = await loadProxies();
+ this.quotes = await loadQuotes();
+ this.lang = await loadLanguage();
+ this.lang = await loadCustomLang();
+
+ this.lastCommentDB = new nedb({ filename: srcdir + "/data/lastcomment.db", autoload: true }); // Autoload
+ this.ratingHistoryDB = new nedb({ filename: srcdir + "/data/ratingHistory.db", autoload: true });
+ this.tokensDB = new nedb({ filename: srcdir + "/data/tokens.db", autoload: true });
+
+ // Check tokens.db every 24 hours for expired tokens to allow users to refresh them beforehand
+ this._startExpiringTokensCheckInterval();
+
+};
diff --git a/src/dataManager/dataManager.js b/src/dataManager/dataManager.js
index 41e9237b..a9125d4e 100644
--- a/src/dataManager/dataManager.js
+++ b/src/dataManager/dataManager.js
@@ -4,7 +4,7 @@
* Created Date: 21.03.2023 22:34:51
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 22:26:01
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -14,20 +14,17 @@
* You should have received a copy of the GNU General Public License along with this program. If not, see .
*/
-
const fs = require("fs");
const { default: Nedb } = require("@seald-io/nedb"); // eslint-disable-line
const Controller = require("../controller/controller.js"); // eslint-disable-line
-
/**
* Constructor - The dataManager system imports, checks, handles errors and provides a file updating service for all configuration files
* @class
* @param {Controller} controller Reference to the controller object
*/
const DataManager = function (controller) {
-
/**
* Reference to the controller object
* @type {Controller}
@@ -45,44 +42,44 @@ const DataManager = function (controller) {
/**
* Stores all `config.json` settings.
- * @type {Object.}
+ * @type {{[key: string]: any}}
*/
this.config = {};
/**
* Stores all `advancedconfig.json` settings.
- * @type {Object.}
+ * @type {{[key: string]: any}}
*/
this.advancedconfig = {};
/**
* Stores all language strings used for responding to a user.
* All default strings have already been replaced with corresponding matches from `customlang.json`.
- * @type {Object.}
+ * @type {{[key: string]: string}}
*/
this.lang = {};
/**
* Stores all quotes used for commenting provided via the `quotes.txt` file.
- * @type {Array}
+ * @type {Array.}
*/
this.quotes = [];
/**
* Stores all proxies provided via the `proxies.txt` file.
- * @type {Array}
+ * @type {Array.}
*/
this.proxies = [];
/**
* Stores IDs from config files converted at runtime and backups for all config & data files.
- * @type {{ ownerid: Array, botsgroup: string, botsgroupid: string, configgroup: string, configgroup64id: string, ownerlinkid: string, botaccid: Array, configjson: {}, advancedconfigjson: {}, datajson: {} }}
+ * @type {{ ownerid: Array., botsgroup: string, botsgroupid: string, configgroup: string, configgroup64id: string, ownerlinkid: string, botaccid: Array., pluginVersions: {[key: string]: string}, configjson: {}, advancedconfigjson: {}, datajson: {} }}
*/
this.cachefile = {};
/**
* Stores the login information for every bot account provided via the `logininfo.json` or `accounts.txt` files.
- * @type {Object.}
+ * @type {{[key: string]: { accountName: string, password: string, sharedSecret: string, steamGuardCode: null, machineName: string, deviceFriendlyName: string }}}
*/
this.logininfo = {};
@@ -107,7 +104,6 @@ const DataManager = function (controller) {
*/
this.tokensDB = {};
-
// Dynamically load all helper files
const loadHelpersFromFolder = (folder) => {
fs.readdirSync(folder).forEach(async (file) => {
@@ -122,7 +118,6 @@ const DataManager = function (controller) {
loadHelpersFromFolder("./src/dataManager");
loadHelpersFromFolder("./src/dataManager/helpers");
-
};
/* -------- Register functions to let the IntelliSense know what's going on in helper files -------- */
@@ -134,15 +129,55 @@ const DataManager = function (controller) {
DataManager.prototype.checkData = function () {};
/**
- * Converts owners and groups imported from config.json to steam ids and updates cachefile. (Call this after dataImport and before dataCheck)
+ * Writes (all) files imported by DataManager back to the disk
+ */
+DataManager.prototype.writeAllFilesToDisk = function() {};
+
+/**
+ * Writes cachefile to cache.json on disk
+ */
+DataManager.prototype.writeCachefileToDisk = function() {};
+
+/**
+ * Writes datafile to data.json on disk
*/
-DataManager.prototype.processData = async function () {};
+DataManager.prototype.writeDatafileToDisk = function() {};
+
+/**
+ * Writes config to config.json on disk
+ */
+DataManager.prototype.writeConfigToDisk = function() {};
+
+/**
+ * Writes advancedconfig to advancedconfig.json on disk
+ */
+DataManager.prototype.writeAdvancedconfigToDisk = function() {};
+
+/**
+ * Writes logininfo to logininfo.json and accounts.txt on disk, depending on which of the files exist
+ */
+DataManager.prototype.writeLogininfoToDisk = function() {};
+
+/**
+ * Writes proxies to proxies.txt on disk
+ */
+DataManager.prototype.writeProxiesToDisk = function() {};
+
+/**
+ * Writes quotes to quotes.txt on disk
+ */
+DataManager.prototype.writeQuotesToDisk = function() {};
/**
* Internal: Loads all config & data files from disk and handles potential errors
* @returns {Promise.} Resolves promise when all files have been loaded successfully. The function will log an error and terminate the application should a fatal error occur.
*/
-DataManager.prototype._importFromDisk = function () {};
+DataManager.prototype._importFromDisk = async function () {};
+
+/**
+ * Converts owners and groups imported from config.json to steam ids and updates cachefile. (Call this after dataImport and before dataCheck)
+ */
+DataManager.prototype.processData = async function() {};
/**
* Gets a random quote
@@ -153,15 +188,15 @@ DataManager.prototype.getQuote = function (quotesArr = null) {}; // eslint-disab
/**
* Checks if a user ID is currently on cooldown and formats human readable lastRequestStr and untilStr strings.
- * @param {String} id ID of the user to look up
+ * @param {string} id ID of the user to look up
* @returns {Promise.<{ lastRequest: number, until: number, lastRequestStr: string, untilStr: string }|null>} Resolves with object containing `lastRequest` (Unix timestamp of the last interaction received), `until` (Unix timestamp of cooldown end), `lastRequestStr` (How long ago as String), `untilStr` (Wait until as String). If id wasn't found, `null` will be returned.
*/
DataManager.prototype.getUserCooldown = function (id) {}; // eslint-disable-line
/**
* Updates or inserts timestamp of a user
- * @param {String} id ID of the user to update
- * @param {Number} timestamp Unix timestamp of the last interaction the user received
+ * @param {string} id ID of the user to update
+ * @param {number} timestamp Unix timestamp of the last interaction the user received
*/
DataManager.prototype.setUserCooldown = function (id, timestamp) {}; // eslint-disable-line
@@ -172,21 +207,21 @@ DataManager.prototype._startExpiringTokensCheckInterval = () => {};
/**
* Internal: Asks user if he/she wants to refresh the tokens of all expiring accounts when no active request was found and relogs them
- * @param {Object} expiring Object of botobject entries to ask user for
+ * @param {object} expiring Object of botobject entries to ask user for
*/
DataManager.prototype._askForGetNewToken = function (expiring) {}; // eslint-disable-line
/**
* Retrieves the last processed request of anyone or a specific steamID64 from the lastcomment database
- * @param {String} steamID64 Search for a specific user
+ * @param {string} steamID64 Search for a specific user
* @returns {Promise.} Called with the greatest timestamp (Number) found
*/
DataManager.prototype.getLastCommentRequest = function (steamID64 = null) {}; // eslint-disable-line
/**
* Decodes a JsonWebToken - https://stackoverflow.com/a/38552302
- * @param {String} token The token to decode
- * @returns JWT object on success, `null` on failure
+ * @param {string} token The token to decode
+ * @returns {object|null} JWT object on success, `null` on failure
*/
DataManager.prototype.decodeJWT = function (token) {}; // eslint-disable-line
@@ -197,16 +232,20 @@ DataManager.prototype.refreshCache = function () {};
/**
* Internal: Helper function to try and restore backup of corrupted file from cache.json
- * @param {String} name Name of the file
- * @param {String} filepath Absolute path of the file on the disk
- * @param {Object} cacheentry Backup-Object of the file in cache.json
- * @param {String} onlinelink Link to the raw file in the GitHub repository
- * @param {Function} resolve Function to resolve the caller's promise
+ * @param {string} name Name of the file
+ * @param {string} filepath Absolute path of the file on the disk
+ * @param {object} cacheentry Backup-Object of the file in cache.json
+ * @param {string} onlinelink Link to the raw file in the GitHub repository
+ * @param {function(any): void} resolve Function to resolve the caller's promise
*/
DataManager.prototype._restoreBackup = function (name, filepath, cacheentry, onlinelink, resolve) {}; // eslint-disable-line
/**
* Internal: Helper function to pull new file from GitHub
+ * @param {string} name Name of the file
+ * @param {string} filepath Full path, starting from project root with './'
+ * @param {function(any): void} resolve Your promise to resolve when file was pulled
+ * @param {boolean} noRequire Optional: Set to true if resolve() should not be called with require(file) as param
*/
DataManager.prototype._pullNewFile = async function (name, filepath, resolve) {}; // eslint-disable-line
diff --git a/src/dataManager/dataProcessing.js b/src/dataManager/dataProcessing.js
index 27925f0c..e14c3d1d 100644
--- a/src/dataManager/dataProcessing.js
+++ b/src/dataManager/dataProcessing.js
@@ -4,7 +4,7 @@
* Created Date: 27.03.2023 21:34:45
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:59:57
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -25,149 +25,144 @@ const DataManager = require("./dataManager");
/**
* Converts owners and groups imported from config.json to steam ids and updates cachefile. (Call this after dataImport and before dataCheck)
*/
-DataManager.prototype.processData = function() {
+DataManager.prototype.processData = async function() {
let _this = this;
- return new Promise((resolve) => {
- (async () => { // Lets us use await insidea Promise without creating an antipattern
+ /* eslint-disable jsdoc/require-jsdoc */
+ function yourgroup() {
+ return new Promise((resolve) => {
- function yourgroup() {
- return new Promise((resolve) => {
+ if (_this.config.yourgroup.length == 0) { // Check if yourgroup is empty
+ logger("debug", "DataManager processData(): yourgroup is not set, clearing cachefile entry"); // Log to output for debugging
- if (_this.config.yourgroup.length == 0) { // Check if yourgroup is empty
- logger("debug", "DataManager processData(): yourgroup is not set, clearing cachefile entry"); // Log to output for debugging
+ // Reset cachefile values
+ _this.cachefile.configgroup = "";
+ _this.cachefile.configgroup64id = "";
- // Reset cachefile values
- _this.cachefile.configgroup = "";
- _this.cachefile.configgroup64id = "";
+ return resolve();
+ }
- return resolve();
- }
+ // Check if URL was processed previously and skip conversion
+ if (_this.cachefile.configgroup == _this.config.yourgroup) {
+ logger("debug", "DataManager processData(): ID of yourgroup is stored in cache.json, skipping...");
+ return resolve();
+ }
+
+ logger("info", "groupID64 of yourgroup is not in cache.json. Requesting information from Steam...", false, true, logger.animation("loading"));
- // Check if URL was processed previously and skip conversion
- if (_this.cachefile.configgroup == _this.config.yourgroup) {
- logger("debug", "DataManager processData(): ID of yourgroup is stored in cache.json, skipping...");
+ steamIdResolver.groupUrlToGroupID64(_this.config.yourgroup, (err, res) => {
+ if (err == "The specified group could not be found.") { // If the group couldn't be found display specific message
+ logger("error", "Your yourgroup in config doesn't seem to be valid!\n Error: " + _this.config.yourgroup + " contains no xml or groupID64 data", true);
+ return resolve();
+ } else {
+ if (err) {
+ logger("error", "Error getting yourgroup information from Steam: " + err); // If a different error then display a generic message with the error
return resolve();
}
+ }
- logger("info", "groupID64 of yourgroup is not in cache.json. Requesting information from Steam...", false, true, logger.animation("loading"));
+ logger("info", `Successfully retrieved yourgroup information. groupID64: ${res}`, false, true, logger.animation("loading"));
- steamIdResolver.groupUrlToGroupID64(_this.config.yourgroup, (err, res) => {
- if (err == "The specified group could not be found.") { // If the group couldn't be found display specific message
- logger("error", "Your yourgroup in config doesn't seem to be valid!\n Error: " + _this.config.yourgroup + " contains no xml or groupID64 data", true);
- return resolve();
- } else {
- if (err) {
- logger("error", "Error getting yourgroup information from Steam: " + err); // If a different error then display a generic message with the error
- return resolve();
- }
- }
+ _this.cachefile.configgroup = _this.config.yourgroup;
+ _this.cachefile.configgroup64id = res;
+ resolve();
+ });
- logger("info", `Successfully retrieved yourgroup information. groupID64: ${res}`, false, true, logger.animation("loading"));
+ });
+ }
- _this.cachefile.configgroup = _this.config.yourgroup;
- _this.cachefile.configgroup64id = res;
- resolve();
- });
+ function botsgroup() {
+ return new Promise((resolve) => {
- });
- }
+ if (_this.config.botsgroup.length == 0) { // Check if botsgroup is empty
+ logger("debug", "DataManager processData(): botsgroup is not set, clearing cachefile entry"); // Log to output for debugging
- function botsgroup() {
- return new Promise((resolve) => {
+ // Reset cachefile values
+ _this.cachefile.botsgroup = "";
+ _this.cachefile.botsgroupid = "";
- if (_this.config.botsgroup.length == 0) { // Check if botsgroup is empty
- logger("debug", "DataManager processData(): botsgroup is not set, clearing cachefile entry"); // Log to output for debugging
+ return resolve();
+ }
- // Reset cachefile values
- _this.cachefile.botsgroup = "";
- _this.cachefile.botsgroupid = "";
+ // Check if URL was processed previously and skip conversion
+ if (_this.cachefile.botsgroup == _this.config.botsgroup) {
+ logger("debug", "DataManager processData(): ID of botsgroup is stored in cache.json, skipping...");
+ return resolve();
+ }
- return resolve();
- }
+ logger("info", "groupID64 of botsgroup is not in cache.json. Requesting information from Steam...", false, true, logger.animation("loading"));
- // Check if URL was processed previously and skip conversion
- if (_this.cachefile.botsgroup == _this.config.botsgroup) {
- logger("debug", "DataManager processData(): ID of botsgroup is stored in cache.json, skipping...");
+ steamIdResolver.groupUrlToGroupID64(_this.config.botsgroup, (err, res) => {
+ if (err == "The specified group could not be found.") { // If the group couldn't be found display specific message
+ logger("warn", "Your botsgroup in config doesn't seem to be valid!\n " + _this.config.botsgroup + " contains no xml or groupID64 data");
+ return resolve();
+ } else {
+ if (err) {
+ logger("error", "Error getting botsgroup information from Steam: " + err); // If a different error then display a generic message with the error
return resolve();
}
+ }
- logger("info", "groupID64 of botsgroup is not in cache.json. Requesting information from Steam...", false, true, logger.animation("loading"));
+ logger("info", `Successfully retrieved botsgroup information. groupID64: ${res}`, false, true, logger.animation("loading"));
- steamIdResolver.groupUrlToGroupID64(_this.config.botsgroup, (err, res) => {
- if (err == "The specified group could not be found.") { // If the group couldn't be found display specific message
- logger("warn", "Your botsgroup in config doesn't seem to be valid!\n " + _this.config.botsgroup + " contains no xml or groupID64 data");
- return resolve();
- } else {
- if (err) {
- logger("error", "Error getting botsgroup information from Steam: " + err); // If a different error then display a generic message with the error
- return resolve();
- }
- }
-
- logger("info", `Successfully retrieved botsgroup information. groupID64: ${res}`, false, true, logger.animation("loading"));
+ _this.cachefile.botsgroup = _this.config.botsgroup;
+ _this.cachefile.botsgroupid = res;
+ resolve();
+ });
- _this.cachefile.botsgroup = _this.config.botsgroup;
- _this.cachefile.botsgroupid = res;
- resolve();
- });
+ });
+ }
- });
- }
+ function owners() {
+ return new Promise((resolve) => {
- function owners() {
- return new Promise((resolve) => {
+ let tempArr = [];
+ logger("debug", `DataManager processData(): Converting ${_this.config.ownerid.length} owner(s)...`);
- let tempArr = [];
- logger("debug", `DataManager processData(): Converting ${_this.config.ownerid.length} owner(s)...`);
+ // Check for last iteration, update cache and resolve Promise
+ function finishedResponse(i) {
+ if (i + 1 == _this.config.ownerid.length) {
+ logger("debug", "DataManager processData(): Finished converting all ownerids. Array:");
+ logger("debug", tempArr);
- // Check for last iteration, update cache and resolve Promise
- function finishedResponse(i) {
- if (i + 1 == _this.config.ownerid.length) {
- logger("debug", "DataManager processData(): Finished converting all ownerids. Array:");
- logger("debug", tempArr);
+ _this.cachefile["ownerid"] = tempArr; // Refresh cache
+ resolve();
+ }
+ }
- _this.cachefile["ownerid"] = tempArr; // Refresh cache
- resolve();
- }
- }
+ // Instantly bail out if the array is empty. DataCheck will abort the bot later on
+ if (_this.config.ownerid.length == 0) finishedResponse(-1);
- // Instantly bail out if the array is empty. DataCheck will abort the bot later on
- if (_this.config.ownerid.length == 0) finishedResponse(-1);
-
- // Either convert to steamID64 or directly push e
- _this.config.ownerid.forEach((e, i) => {
- if (isNaN(e) || !new SteamID(String(e)).isValid()) {
- steamIdResolver.customUrlToSteamID64(String(e), (err, id) => {
- if (err) {
- logger("warn", `ownerid ${e} in your config does not seem to be valid! Error: ${err}`);
- tempArr[i] = null;
- } else {
- logger("debug", `DataManager processData(): Converted ${e} to ${id}`);
- tempArr[i] = id;
- }
-
- finishedResponse(i);
- });
+ // Either convert to steamID64 or directly push e
+ _this.config.ownerid.forEach((e, i) => {
+ if (isNaN(e) || !new SteamID(String(e)).isValid()) {
+ steamIdResolver.customUrlToSteamID64(String(e), (err, id) => {
+ if (err) {
+ logger("warn", `ownerid ${e} in your config does not seem to be valid! Error: ${err}`);
+ tempArr[i] = null;
} else {
- tempArr[i] = e;
- finishedResponse(i);
+ logger("debug", `DataManager processData(): Converted ${e} to ${id}`);
+ tempArr[i] = id;
}
- });
-
- });
- }
+ finishedResponse(i);
+ });
+ } else {
+ tempArr[i] = e;
+ finishedResponse(i);
+ }
+ });
- // Process all three, then update cache.json
- await Promise.all([yourgroup(), botsgroup(), owners()]);
+ });
+ }
+ /* eslint-enable jsdoc/require-jsdoc */
- fs.writeFile(srcdir + "/data/cache.json", JSON.stringify(this.cachefile, null, 4), err => {
- if (err) logger("error", `DataManager processData(): Error updating cache.json: ${err}`);
- resolve();
- });
+ // Process all three, then update cache.json
+ await Promise.all([yourgroup(), botsgroup(), owners()]);
- })();
+ fs.writeFile(srcdir + "/data/cache.json", JSON.stringify(this.cachefile, null, 4), err => {
+ if (err) logger("error", `DataManager processData(): Error updating cache.json: ${err}`);
});
+
};
\ No newline at end of file
diff --git a/src/dataManager/helpers/getQuote.js b/src/dataManager/helpers/getQuote.js
index b6ef2a07..f108194a 100644
--- a/src/dataManager/helpers/getQuote.js
+++ b/src/dataManager/helpers/getQuote.js
@@ -4,7 +4,7 @@
* Created Date: 02.03.2022 16:21:11
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:27:09
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
diff --git a/src/dataManager/helpers/handleCooldowns.js b/src/dataManager/helpers/handleCooldowns.js
index 39ff76fd..46278db2 100644
--- a/src/dataManager/helpers/handleCooldowns.js
+++ b/src/dataManager/helpers/handleCooldowns.js
@@ -4,7 +4,7 @@
* Created Date: 13.04.2023 17:58:23
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 17:51:07
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -20,7 +20,7 @@ const DataManager = require("../dataManager");
/**
* Checks if a user ID is currently on cooldown and formats human readable lastRequestStr and untilStr strings.
- * @param {String} id ID of the user to look up
+ * @param {string} id ID of the user to look up
* @returns {Promise.<{ lastRequest: number, until: number, lastRequestStr: string, untilStr: string }|null>} Resolves with object containing `lastRequest` (Unix timestamp of the last interaction received), `until` (Unix timestamp of cooldown end), `lastRequestStr` (How long ago as String), `untilStr` (Wait until as String). If id wasn't found, `null` will be returned.
*/
DataManager.prototype.getUserCooldown = function(id) {
@@ -89,8 +89,8 @@ DataManager.prototype.getUserCooldown = function(id) {
/**
* Updates or inserts timestamp of a user
- * @param {String} id ID of the user to update
- * @param {Number} timestamp Unix timestamp of the last interaction the user received
+ * @param {string} id ID of the user to update
+ * @param {number} timestamp Unix timestamp of the last interaction the user received
*/
DataManager.prototype.setUserCooldown = function(id, timestamp) {
logger("debug", `DataManager setUserCooldown(): Updating lastcomment db entry for ${id} to ${timestamp}.`);
diff --git a/src/dataManager/helpers/handleExpiringTokens.js b/src/dataManager/helpers/handleExpiringTokens.js
index 8cab1884..7b56108d 100644
--- a/src/dataManager/helpers/handleExpiringTokens.js
+++ b/src/dataManager/helpers/handleExpiringTokens.js
@@ -4,7 +4,7 @@
* Created Date: 14.10.2022 14:58:25
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 20:00:28
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
@@ -24,6 +24,7 @@ const DataManager = require("../dataManager.js");
DataManager.prototype._startExpiringTokensCheckInterval = function() {
let _this = this;
+ /* eslint-disable-next-line jsdoc/require-jsdoc */
function scanDatabase() {
logger("debug", "DataManager detectExpiringTokens(): Scanning tokens.db for expiring tokens...");
@@ -96,12 +97,13 @@ DataManager.prototype._startExpiringTokensCheckInterval = function() {
/**
* Internal: Asks user if he/she wants to refresh the tokens of all expiring accounts when no active request was found and relogs them
- * @param {Object} expiring Object of botobject entries to ask user for
+ * @param {object} expiring Object of botobject entries to ask user for
*/
DataManager.prototype._askForGetNewToken = function(expiring) {
let EStatus = require("../../bot/EStatus.js"); // Import not at top scope as this can be undefined because this helper file gets loaded before updater ran
let _this = this;
+ /* eslint-disable-next-line jsdoc/require-jsdoc */
function askForRelog() { // TODO: Add support for asking in steam chat
// Ask for all accounts once
diff --git a/src/dataManager/helpers/misc.js b/src/dataManager/helpers/misc.js
index a4e5b9d2..632b1dd6 100644
--- a/src/dataManager/helpers/misc.js
+++ b/src/dataManager/helpers/misc.js
@@ -4,7 +4,7 @@
* Created Date: 24.03.2023 18:58:55
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:58:05
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -20,7 +20,7 @@ const DataManager = require("../dataManager.js");
/**
* Retrieves the last processed request of anyone or a specific steamID64 from the lastcomment database
- * @param {String} steamID64 Search for a specific user
+ * @param {string} steamID64 Search for a specific user
* @returns {Promise.} Called with the greatest timestamp (Number) found
*/
DataManager.prototype.getLastCommentRequest = function(steamID64 = null) {
@@ -47,8 +47,8 @@ DataManager.prototype.getLastCommentRequest = function(steamID64 = null) {
/**
* Decodes a JsonWebToken - https://stackoverflow.com/a/38552302
- * @param {String} token The token to decode
- * @returns JWT object on success, `null` on failure
+ * @param {string} token The token to decode
+ * @returns {object|null} JWT object on success, `null` on failure
*/
DataManager.prototype.decodeJWT = function(token) {
let payload = token.split(".")[1]; // Remove header and signature as we only care about the payload
diff --git a/src/dataManager/helpers/refreshCache.js b/src/dataManager/helpers/refreshCache.js
index b905888d..8afc12f5 100644
--- a/src/dataManager/helpers/refreshCache.js
+++ b/src/dataManager/helpers/refreshCache.js
@@ -4,7 +4,7 @@
* Created Date: 29.03.2023 17:44:47
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 02.07.2023 19:07:41
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -14,23 +14,22 @@
* You should have received a copy of the GNU General Public License along with this program. If not, see .
*/
-
-const fs = require("fs");
+const fs = require("fs");
const SteamID = require("steamid");
const DataManager = require("../dataManager");
-
/**
* Refreshes Backups in cache.json with new data
*/
-DataManager.prototype.refreshCache = function() {
+DataManager.prototype.refreshCache = function () {
logger("info", "Refreshing data backups in cache.json...", false, true, logger.animation("loading"));
// Refresh cache of bot account ids, check if they inflict with owner settings
let tempArr = [];
- this.controller.getBots().forEach((e, i) => { // Get all online accounts
+ this.controller.getBots().forEach((e, i) => {
+ // Get all online accounts
tempArr.push(new SteamID(String(e.user.steamID)).getSteamID64());
// Check if this bot account is listed as an owner id and display warning
@@ -45,16 +44,14 @@ DataManager.prototype.refreshCache = function() {
}
});
-
// Update Backups
logger("debug", "Writing backups to cache.json...", false, true, logger.animation("loading"));
this.cachefile["configjson"] = this.config;
this.cachefile["advancedconfigjson"] = this.advancedconfig;
this.cachefile["datajson"] = this.datafile;
-
// Write changes to file
- fs.writeFile(srcdir + "/data/cache.json", JSON.stringify(this.cachefile, null, 4), err => {
+ fs.writeFile(srcdir + "/data/cache.json", JSON.stringify(this.cachefile, null, 4), (err) => {
if (err) logger("error", "error writing file backups to cache.json: " + err);
});
-};
\ No newline at end of file
+};
diff --git a/src/dataManager/helpers/repairFile.js b/src/dataManager/helpers/repairFile.js
index 091d5acc..e32ef9ba 100644
--- a/src/dataManager/helpers/repairFile.js
+++ b/src/dataManager/helpers/repairFile.js
@@ -4,7 +4,7 @@
* Created Date: 22.03.2023 12:35:01
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:59:23
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -22,11 +22,11 @@ const DataManager = require("../dataManager.js");
/**
* Internal: Helper function to try and restore backup of corrupted file from cache.json
- * @param {String} name Name of the file
- * @param {String} filepath Absolute path of the file on the disk
- * @param {Object} cacheentry Backup-Object of the file in cache.json
- * @param {String} onlinelink Link to the raw file in the GitHub repository
- * @param {Function} resolve Function to resolve the caller's promise
+ * @param {string} name Name of the file
+ * @param {string} filepath Absolute path of the file on the disk
+ * @param {object} cacheentry Backup-Object of the file in cache.json
+ * @param {string} onlinelink Link to the raw file in the GitHub repository
+ * @param {function(any): void} resolve Function to resolve the caller's promise
*/
DataManager.prototype._restoreBackup = function(name, filepath, cacheentry, onlinelink, resolve) {
@@ -74,10 +74,10 @@ DataManager.prototype._restoreBackup = function(name, filepath, cacheentry, onli
/**
* Internal: Helper function to pull new file from GitHub
- * @param {String} name Name of the file
- * @param {String} filepath Full path, starting from project root with './'
- * @param {function} resolve Your promise to resolve when file was pulled
- * @param {Boolean} noRequire Optional: Set to true if resolve() should not be called with require(file) as param
+ * @param {string} name Name of the file
+ * @param {string} filepath Full path, starting from project root with './'
+ * @param {function(any): void} resolve Your promise to resolve when file was pulled
+ * @param {boolean} noRequire Optional: Set to true if resolve() should not be called with require(file) as param
*/
DataManager.prototype._pullNewFile = async function(name, filepath, resolve, noRequire) {
logger("warn", "Backup seems to be broken/not available! Pulling file from GitHub...", true);
diff --git a/src/libraryPatches/CSteamSharedFile.js b/src/libraryPatches/CSteamSharedFile.js
new file mode 100644
index 00000000..109e4a88
--- /dev/null
+++ b/src/libraryPatches/CSteamSharedFile.js
@@ -0,0 +1,223 @@
+const Cheerio = require('cheerio');
+const SteamID = require('steamid');
+
+const SteamCommunity = require('steamcommunity');
+const Helpers = require('../../node_modules/steamcommunity/components/helpers.js');
+
+const ESharedFileType = require('../../node_modules/steamcommunity/resources/ESharedFileType.js');
+
+
+/**
+ * Scrape a sharedfile's DOM to get all available information
+ * @param {string} sharedFileId - ID of the sharedfile
+ * @param {function} callback - First argument is null/Error, second is object containing all available information
+ */
+SteamCommunity.prototype.getSteamSharedFile = function(sharedFileId, callback) {
+ // Construct object holding all the data we can scrape
+ let sharedfile = {
+ id: sharedFileId,
+ type: null,
+ appID: null,
+ owner: null,
+ fileSize: null,
+ postDate: null,
+ resolution: null,
+ uniqueVisitorsCount: null,
+ favoritesCount: null,
+ upvoteCount: null,
+ guideNumRatings: null,
+ isUpvoted: null,
+ isDownvoted: null
+ };
+
+ // Get DOM of sharedfile
+ this.httpRequestGet(`https://steamcommunity.com/sharedfiles/filedetails/?id=${sharedFileId}&l=english`, (err, res, body) => { // Request page in english so that the Posted scraping below works
+ try {
+
+ /* --------------------- Preprocess output --------------------- */
+
+ // Load output into cheerio to make parsing easier
+ let $ = Cheerio.load(body);
+
+ // Dynamically map detailsStatsContainerLeft to detailsStatsContainerRight in an object to make readout easier. It holds size, post date and resolution.
+ let detailsStatsObj = {};
+ let detailsLeft = $(".detailsStatsContainerLeft").children();
+ let detailsRight = $(".detailsStatsContainerRight").children();
+
+ Object.keys(detailsLeft).forEach((e) => { // Dynamically get all details. Don't hardcore so that this also works for guides.
+ if (isNaN(e)) {
+ return; // Ignore invalid entries
+ }
+
+ detailsStatsObj[detailsLeft[e].children[0].data.trim()] = detailsRight[e].children[0].data;
+ });
+
+ // Dynamically map stats_table descriptions to values. This holds Unique Visitors and Current Favorites
+ let statsTableObj = {};
+ let statsTable = $(".stats_table").children();
+
+ Object.keys(statsTable).forEach((e) => {
+ if (isNaN(e)) {
+ return; // Ignore invalid entries
+ }
+
+ // Value description is at index 3, value data at index 1
+ statsTableObj[statsTable[e].children[3].children[0].data] = statsTable[e].children[1].children[0].data.replace(/,/g, ""); // Remove commas from 1k+ values
+ });
+
+
+ /* --------------------- Find and map values --------------------- */
+
+ // Find appID in share button onclick event
+ sharedfile.appID = Number($("#ShareItemBtn").attr()["onclick"].replace(`ShowSharePublishedFilePopup( '${sharedFileId}', '`, "").replace("' );", ""));
+
+
+ // Find fileSize if not guide
+ sharedfile.fileSize = detailsStatsObj["File Size"] || null; // TODO: Convert to bytes? It seems like to always be MB but no guarantee
+
+
+ // Find postDate and convert to timestamp
+ let posted = detailsStatsObj["Posted"] || null; // Set to null if "posted" could not be found as Steam translates dates and parsing it below will return a wrong result
+
+ if (posted) {
+ sharedfile.postDate = Helpers.decodeSteamTime(posted.trim()); // Only parse if posted is defined to avoid errors
+ }
+
+
+ // Find resolution if artwork or screenshot
+ sharedfile.resolution = detailsStatsObj["Size"] || null;
+
+
+ // Find uniqueVisitorsCount. We can't use ' || null' here as Number("0") casts to false
+ if (statsTableObj["Unique Visitors"]) {
+ sharedfile.uniqueVisitorsCount = Number(statsTableObj["Unique Visitors"]);
+ }
+
+
+ // Find favoritesCount. We can't use ' || null' here as Number("0") casts to false
+ if (statsTableObj["Current Favorites"]) {
+ sharedfile.favoritesCount = Number(statsTableObj["Current Favorites"]);
+ }
+
+
+ // Find upvoteCount. We can't use ' || null' here as Number("0") casts to false
+ let upvoteCount = $("#VotesUpCountContainer > #VotesUpCount").text();
+
+ if (upvoteCount) {
+ sharedfile.upvoteCount = Number(upvoteCount);
+ }
+
+
+ // Find numRatings if this is a guide as they use a different voting system
+ let numRatings = $(".ratingSection > .numRatings").text().replace(" ratings", "");
+
+ sharedfile.guideNumRatings = Number(numRatings) || null; // Set to null if not a guide or if the guide does not have enough ratings to show a value
+
+
+ // Determine if this account has already voted on this sharedfile
+ sharedfile.isUpvoted = String($(".workshopItemControlCtn > #VoteUpBtn")[0].attribs["class"]).includes("toggled"); // Check if upvote btn class contains "toggled"
+ sharedfile.isDownvoted = String($(".workshopItemControlCtn > #VoteDownBtn")[0].attribs["class"]).includes("toggled"); // Check if downvote btn class contains "toggled"
+
+
+ // Determine type by looking at the second breadcrumb. Find the first separator as it has a unique name and go to the next element which holds our value of interest
+ let breadcrumb = $(".breadcrumbs > .breadcrumb_separator").next().get(0).children[0].data || "";
+
+ if (breadcrumb.includes("Screenshot")) {
+ sharedfile.type = ESharedFileType.Screenshot;
+ }
+
+ if (breadcrumb.includes("Artwork")) {
+ sharedfile.type = ESharedFileType.Artwork;
+ }
+
+ if (breadcrumb.includes("Guide")) {
+ sharedfile.type = ESharedFileType.Guide;
+ }
+
+
+ // Find owner profile link, convert to steamID64 using SteamIdResolver lib and create a SteamID object
+ let ownerHref = $(".friendBlockLinkOverlay").attr()["href"];
+
+ Helpers.resolveVanityURL(ownerHref, (err, data) => { // This request takes <1 sec
+ if (err) {
+ callback(err);
+ return;
+ }
+
+ sharedfile.owner = new SteamID(data.steamID);
+
+ // Make callback when ID was resolved as otherwise owner will always be null
+ callback(null, new CSteamSharedFile(this, sharedfile));
+ });
+
+ } catch (err) {
+ callback(err, null);
+ }
+ }, "steamcommunity");
+};
+
+/**
+ * Constructor - Creates a new SharedFile object
+ * @class
+ * @param {SteamCommunity} community
+ * @param {{ id: string, type: ESharedFileType, appID: number, owner: SteamID|null, fileSize: string|null, postDate: number, resolution: string|null, uniqueVisitorsCount: number, favoritesCount: number, upvoteCount: number|null, guideNumRatings: Number|null, isUpvoted: boolean, isDownvoted: boolean }} data
+ */
+function CSteamSharedFile(community, data) {
+ /**
+ * @type {SteamCommunity}
+ */
+ this._community = community;
+
+ // Clone all the data we received
+ Object.assign(this, data);
+}
+
+/**
+ * Deletes a comment from this sharedfile's comment section
+ * @param {string} cid - ID of the comment to delete
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedFile.prototype.deleteComment = function(cid, callback) {
+ this._community.deleteSharedFileComment(this.owner, this.id, cid, callback);
+};
+
+/**
+ * Favorites this sharedfile
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedFile.prototype.favorite = function(callback) {
+ this._community.favoriteSharedFile(this.id, this.appID, callback);
+};
+
+/**
+ * Posts a comment to this sharedfile
+ * @param {string} message - Content of the comment to post
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedFile.prototype.comment = function(message, callback) {
+ this._community.postSharedFileComment(this.owner, this.id, message, callback);
+};
+
+/**
+ * Subscribes to this sharedfile's comment section. Note: Checkbox on webpage does not update
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedFile.prototype.subscribe = function(callback) {
+ this._community.subscribeSharedFileComments(this.owner, this.id, callback);
+};
+
+/**
+ * Unfavorites this sharedfile
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedFile.prototype.unfavorite = function(callback) {
+ this._community.unfavoriteSharedFile(this.id, this.appID, callback);
+};
+
+/**
+ * Unsubscribes from this sharedfile's comment section. Note: Checkbox on webpage does not update
+ * @param {function} callback - Takes only an Error object/null as the first argument
+ */
+CSteamSharedFile.prototype.unsubscribe = function(callback) {
+ this._community.unsubscribeSharedFileComments(this.owner, this.id, callback);
+};
diff --git a/src/libraryPatches/README.md b/src/libraryPatches/README.md
index 1588623d..bd78cfae 100644
--- a/src/libraryPatches/README.md
+++ b/src/libraryPatches/README.md
@@ -13,6 +13,7 @@ These are the patches being applied:
- Re-enable primaryGroup profile setting: [#287](https://github.com/DoctorMcKay/node-steamcommunity/pull/287) & [#307](https://github.com/DoctorMcKay/node-steamcommunity/pull/307)
- Add sharedfiles voteUp & voteDown support
- Fix resolving vanity for private profiles returning error: [#315](https://github.com/DoctorMcKay/node-steamcommunity/pull/315)
+- (Potentially) Fix sharedfile data scraping failing as a non-english page was returned by Steam
These patches have been applied in the past:
- Add full sharedfiles support: [#306](https://github.com/DoctorMcKay/node-steamcommunity/pull/306)
diff --git a/src/libraryPatches/helpers.js b/src/libraryPatches/helpers.js
index 5bbc6345..e2b39a46 100644
--- a/src/libraryPatches/helpers.js
+++ b/src/libraryPatches/helpers.js
@@ -5,8 +5,8 @@ const xml2js = require('xml2js');
/**
* Resolves a Steam profile URL to get steamID64 and vanityURL
- * @param {String} url - Full steamcommunity profile URL or only the vanity part.
- * @param {Object} callback - First argument is null/Error, second is object containing vanityURL (String) and steamID (String)
+ * @param {string} url - Full steamcommunity profile URL or only the vanity part.
+ * @param {object} callback - First argument is null/Error, second is object containing vanityURL (String) and steamID (String)
*/
helpers.resolveVanityURL = function(url, callback) {
// Precede url param if only the vanity was provided
diff --git a/src/libraryPatches/sharedfiles.js b/src/libraryPatches/sharedfiles.js
index 735dfd63..b4f63107 100644
--- a/src/libraryPatches/sharedfiles.js
+++ b/src/libraryPatches/sharedfiles.js
@@ -2,7 +2,7 @@ const SteamCommunity = require('steamcommunity');
/**
* Downvotes a sharedfile
- * @param {String} sid - ID of the sharedfile
+ * @param {string} sid - ID of the sharedfile
* @param {function} callback - Takes only an Error object/null as the first argument
*/
SteamCommunity.prototype.voteDownSharedFile = function(sid, callback) {
@@ -23,7 +23,7 @@ SteamCommunity.prototype.voteDownSharedFile = function(sid, callback) {
/**
* Upvotes a sharedfile
- * @param {String} sid - ID of the sharedfile
+ * @param {string} sid - ID of the sharedfile
* @param {function} callback - Takes only an Error object/null as the first argument
*/
SteamCommunity.prototype.voteUpSharedFile = function(sid, callback) {
diff --git a/src/pluginSystem/handlePluginData.js b/src/pluginSystem/handlePluginData.js
index a4a07812..d2a80742 100644
--- a/src/pluginSystem/handlePluginData.js
+++ b/src/pluginSystem/handlePluginData.js
@@ -4,7 +4,7 @@
* Created Date: 04.06.2023 17:52:51
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 15:25:16
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -14,18 +14,18 @@
* You should have received a copy of the GNU General Public License along with this program. If not, see .
*/
-
const fs = require("fs");
const PluginSystem = require("./pluginSystem.js");
-
/**
* Gets the path holding all data of a plugin. If no folder exists yet, one will be created
* @param {string} pluginName Name of your plugin
* @returns {string} Path to the folder containing your plugin data
*/
-PluginSystem.prototype.getPluginDataPath = function(pluginName) {
+PluginSystem.prototype.getPluginDataPath = function (pluginName) {
+ if (!pluginName) throw new Error("Plugin name parameter is missing!");
+
let path = `${srcdir}/../plugins/${pluginName}/`;
if (!fs.existsSync(path)) fs.mkdirSync(path);
@@ -33,7 +33,6 @@ PluginSystem.prototype.getPluginDataPath = function(pluginName) {
return path;
};
-
/**
* Loads a file from your plugin data folder. The data will remain unprocessed. Use `loadPluginConfig()` instead if you want to load your plugin config.
* @param {string} pluginName Name of your plugin
@@ -42,6 +41,8 @@ PluginSystem.prototype.getPluginDataPath = function(pluginName) {
*/
PluginSystem.prototype.loadPluginData = function (pluginName, filename) {
return new Promise((resolve, reject) => {
+ // Check for missing parameters
+ if (!pluginName || !filename) return reject(new Error("Plugin name or file name parameter is missing!"));
// Get path
let path = this.getPluginDataPath(pluginName);
@@ -54,11 +55,9 @@ PluginSystem.prototype.loadPluginData = function (pluginName, filename) {
resolve(data);
});
-
});
};
-
/**
* Writes a file to your plugin data folder. The data will remain unprocessed. Use `writePluginConfig()` instead if you want to write your plugin config.
* @param {string} pluginName Name of your plugin
@@ -68,6 +67,8 @@ PluginSystem.prototype.loadPluginData = function (pluginName, filename) {
*/
PluginSystem.prototype.writePluginData = function (pluginName, filename, data) {
return new Promise((resolve, reject) => {
+ // Check for missing parameters
+ if (!pluginName || !filename || !data) return reject(new Error("Plugin name, file name or data parameter is missing!"));
// Get path
let path = this.getPluginDataPath(pluginName);
@@ -80,11 +81,9 @@ PluginSystem.prototype.writePluginData = function (pluginName, filename, data) {
resolve();
});
-
});
};
-
/**
* Deletes a file in your plugin data folder if it exists.
* @param {string} pluginName Name of your plugin
@@ -93,6 +92,8 @@ PluginSystem.prototype.writePluginData = function (pluginName, filename, data) {
*/
PluginSystem.prototype.deletePluginData = function (pluginName, filename) {
return new Promise((resolve, reject) => {
+ // Check for missing parameters
+ if (!pluginName || !filename) return reject(new Error("Plugin name or file name parameter is missing!"));
// Get path
let path = this.getPluginDataPath(pluginName);
@@ -109,11 +110,9 @@ PluginSystem.prototype.deletePluginData = function (pluginName, filename) {
resolve();
});
-
});
};
-
/**
* Loads your plugin config from the filesystem or creates a new one based on the default config provided by your plugin. The JSON data will be processed to an object.
* @param {string} pluginName Name of your plugin
@@ -121,6 +120,8 @@ PluginSystem.prototype.deletePluginData = function (pluginName, filename) {
*/
PluginSystem.prototype.loadPluginConfig = function (pluginName) {
return new Promise((resolve, reject) => {
+ // Check for missing parameters
+ if (!pluginName) return reject(new Error("Plugin name parameter is missing!"));
// Get path
let path = this.getPluginDataPath(pluginName);
@@ -131,7 +132,7 @@ PluginSystem.prototype.loadPluginConfig = function (pluginName) {
try {
fs.copyFileSync(`${srcdir}/../node_modules/${pluginName}/config.json`, path + "config.json");
- } catch(err) {
+ } catch (err) {
logger("error", `Error copying default config provided by plugin '${pluginName}': ` + err);
return reject(err);
}
@@ -144,11 +145,29 @@ PluginSystem.prototype.loadPluginConfig = function (pluginName) {
logger("error", `PluginSystem: Failed to load config for plugin '${pluginName}': ${err.stack}`);
return reject(err);
}
-
});
};
+/**
+ * Integrates changes made to the config to the users config
+ * @param {string} pluginName
+ * @returns {Record} the config
+ */
+PluginSystem.prototype.aggregatePluginConfig = function (pluginName) {
+ let path = this.getPluginDataPath(pluginName);
+
+ if (!fs.existsSync(path + "config.json")) return;
+ if (!fs.existsSync(`${srcdir}/../node_modules/${pluginName}/config.json`)) return;
+
+ const standardConfig = require(`${srcdir}/../node_modules/${pluginName}/config.json`);
+ const config = require(path + "config.json");
+ const aggregatedConfig = Object.assign(standardConfig, config);
+ fs.writeFileSync(path + "config.json", JSON.stringify(aggregatedConfig, null, 4));
+ return aggregatedConfig;
+};
+
+
/**
* Writes your plugin config changes to the filesystem. The object data will be processed to JSON.
* @param {string} pluginName Name of your plugin
@@ -157,6 +176,8 @@ PluginSystem.prototype.loadPluginConfig = function (pluginName) {
*/
PluginSystem.prototype.writePluginConfig = function (pluginName, pluginConfig) {
return new Promise((resolve, reject) => {
+ // Check for missing parameters
+ if (!pluginName || !pluginConfig) return reject(new Error("Plugin name or plugin config parameter is missing!"));
// Get path
let path = this.getPluginDataPath(pluginName);
@@ -170,6 +191,5 @@ PluginSystem.prototype.writePluginConfig = function (pluginName, pluginConfig) {
logger("error", `PluginSystem: Failed to write config for plugin '${pluginName}': ${err.stack}`);
return reject(err);
}
-
});
-};
\ No newline at end of file
+};
diff --git a/src/pluginSystem/loadPlugins.js b/src/pluginSystem/loadPlugins.js
index 7ece030c..7f982308 100644
--- a/src/pluginSystem/loadPlugins.js
+++ b/src/pluginSystem/loadPlugins.js
@@ -4,7 +4,7 @@
* Created Date: 04.06.2023 15:37:17
* Author: DerDeathraven
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 05.07.2023 10:31:36
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -27,12 +27,16 @@ const PLUGIN_EVENTS = {
};
-// Attempt to load all plugins. If a critical check fails loading will be denied
+/**
+ * Attempt to load all plugins. If a critical check fails loading will be denied
+ * @param {string} pluginName Name of the plugin package
+ * @returns {{ pluginName: string, pluginInstance: object, pluginJson: object }} Creates a plugin instance and returns it along with more information
+ */
function loadPlugin(pluginName) {
try {
// Load plugin and pluginJson
const importedPlugin = require(pluginName);
- const pluginJson = require(`${srcdir}/../node_modules/${pluginName}/package.json`);
+ const pluginJson = require(`${srcdir}/../node_modules/${pluginName}/package.json`);
// Check if plugin is missing required functions
if (!(typeof importedPlugin === "function") || !importedPlugin.prototype || !importedPlugin.prototype.load) {
@@ -40,9 +44,11 @@ function loadPlugin(pluginName) {
return {};
}
+ // Display warning if the function is missing a unload function
+ if (!importedPlugin.prototype.unload) logger("warn", `Plugin '${pluginName}' does not have an unload function! This may prevent the reloading function from working properly.`);
+
// Create new plugin object
const pluginInstance = new importedPlugin(this);
-
return { pluginName, pluginInstance, pluginJson };
} catch (e) {
logger("error", `Plugin '${pluginName}' could not be instantiated: ${e.stack}`);
@@ -50,33 +56,38 @@ function loadPlugin(pluginName) {
}
}
-
/**
* Internal: Loads all plugin npm packages and populates pluginList
*/
PluginSystem.prototype._loadPlugins = async function () {
-
// Get all plugins with the matching regex
const plugins = Object.entries(packageJson.dependencies).filter(([key, value]) => PLUGIN_REGEX.test(key)); // eslint-disable-line
const initiatedPlugins = plugins.map(([plugin]) => loadPlugin.bind(this)(plugin)); // Initalize each plugin
- for (const { pluginName, pluginInstance, pluginJson } of initiatedPlugins) {
+ for (const plugin of initiatedPlugins) {
+ const { pluginName, pluginInstance, pluginJson } = plugin;
+
if (!pluginInstance) {
logger("warn", `Skipping plugin '${pluginName}'...`, false, false, null, true); // Force print now
continue;
}
- // Skip plugin if it is disabled
- let pluginConfig = await this.loadPluginConfig(pluginName).catch((err) => logger("error", `The config of plugin '${pluginName}' is fucked, skipping plugin. ${err}`));
+ let pluginConfig = {};
+ const lastSeenVersion = this.controller.data.cachefile.pluginVersions;
+
+ if (lastSeenVersion && lastSeenVersion[pluginName] && lastSeenVersion[pluginName] !== pluginJson.version) {
+ logger("warn", `Plugin '${pluginName}' is outdated! Updating plugin...`, false, false, null, true); // Force print now
+ pluginConfig = this.aggregatePluginConfig(pluginName);
+ } else {
+ pluginConfig = await this.loadPluginConfig(pluginName).catch((err) => logger("error", `The config of plugin '${pluginName}' is fucked, skipping plugin. ${err}`));
+ }
+ // Skip plugin if it is disabled
if (!pluginConfig || !pluginConfig.enabled) {
logger("debug", `Plugin '${pluginName}' is disabled. Skipping plugin...`);
continue;
}
- // Display warning if the function is missing a unload function
- if (!pluginInstance.unload) logger("warn", `Plugin '${pluginName}' does not have an unload function! This may prevent the reloading function from working properly.`);
-
logger("info", `PluginSystem: Loading plugin '${pluginName}' v${pluginJson.version} by ${pluginJson.author}...`, false, true, logger.animation("loading"));
// Add plugin reference to pluginList and call load function
@@ -84,9 +95,13 @@ PluginSystem.prototype._loadPlugins = async function () {
pluginInstance.load();
// Call the exposed event functions if they exist
- Object.entries(PLUGIN_EVENTS).forEach(([eventName, event]) => { // eslint-disable-line
+ Object.entries(PLUGIN_EVENTS).forEach(([eventName, event]) => { // eslint-disable-line no-unused-vars
+ // eslint-disable-line
this.controller.events.on(event, (...args) => pluginInstance[event]?.call(pluginInstance, ...args));
});
- }
+ // Update last seen version of this plugin name
+ if (!lastSeenVersion) this.controller.data.cachefile.pluginVersions = {};
+ this.controller.data.cachefile.pluginVersions[pluginName] = pluginJson.version;
+ }
};
diff --git a/src/pluginSystem/pluginSystem.js b/src/pluginSystem/pluginSystem.js
index b0da3453..f613ef3f 100644
--- a/src/pluginSystem/pluginSystem.js
+++ b/src/pluginSystem/pluginSystem.js
@@ -4,7 +4,7 @@
* Created Date: 19.03.2023 13:34:27
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 07.07.2023 12:43:16
* Modified By: 3urobeat
*
* Copyright (c) 2023 3urobeat
@@ -14,23 +14,20 @@
* You should have received a copy of the GNU General Public License along with this program. If not, see .
*/
-
-const Controller = require("../controller/controller.js"); // eslint-disable-line
+const Controller = require("../controller/controller.js"); // eslint-disable-line
const CommandHandler = require("../commands/commandHandler.js"); // eslint-disable-line
-const Bot = require("../../src/bot/bot.js"); // eslint-disable-line
-
+const Bot = require("../../src/bot/bot.js"); // eslint-disable-line
/**
* @typedef Plugin Documentation of the Plugin structure for IntelliSense support
* @type {object}
- * @property {function} load Called on Plugin load
- * @property {function} unload Called on Plugin unload
- * @property {function} ready Controller ready event
- * @property {function(Bot, Bot.EStatus, Bot.EStatus)} statusUpdate Controller statusUpdate event
- * @property {function(Bot, function(string))} steamGuardInput Controller steamGuardInput event
+ * @property {function(): void} load Called on Plugin load
+ * @property {function(): void} unload Called on Plugin unload
+ * @property {function(): void} ready Controller ready event
+ * @property {function(Bot, Bot.EStatus, Bot.EStatus): void} statusUpdate Controller statusUpdate event
+ * @property {function(Bot, function(string): void): void} steamGuardInput Controller steamGuardInput event
*/
-
/**
* Constructor - The plugin system loads all plugins and provides functions for plugins to hook into
* @class
@@ -45,7 +42,7 @@ const PluginSystem = function (controller) {
/**
* References to all plugin objects
- * @type {Object.}
+ * @type {{[key: string]: Plugin}}
*/
this.pluginList = {};
@@ -62,12 +59,11 @@ const PluginSystem = function (controller) {
// The plugin system loads all plugins and provides functions for plugins to hook into
module.exports = PluginSystem;
-
/**
* Reloads all plugins and calls ready event after ~2.5 seconds.
*/
PluginSystem.prototype.reloadPlugins = function () {
- // Delete all plugin objects. (I'm not sure if this is necessary or if clearing the pluginList obj will garbage collect them)
+ // Delete all plugin objects and their subfiles
Object.keys(this.pluginList).forEach((e) => {
if (this.pluginList[e].unload) {
this.pluginList[e].unload();
@@ -75,12 +71,18 @@ PluginSystem.prototype.reloadPlugins = function () {
logger("warn", `PluginSystem reloadPlugins: Plugin ${e} does not have an unload function, reloading might not work properly!`);
}
- delete this.pluginList[e];
- });
+ // Delete the original path of the plugin, otherwise plugins linked via 'npm link' won't be reloaded correctly
+ delete require.cache[require.resolve(e)];
- // Delete cache so requiring plugins again will load new changes
- Object.keys(require.cache).forEach((key) => {
- if (key.includes("node_modules/steam-comment-bot-") || key.includes("/plugins/")) delete require.cache[key];
+ // Make sure to delete subfiles of this plugin
+ Object.keys(require.cache).forEach((key) => {
+ if (key.includes(e) || key.includes("/plugins/")) {
+ delete require.cache[require.resolve(key)];
+ }
+ });
+
+ // Delete entry from pluginList object
+ delete this.pluginList[e];
});
this.pluginList = {};
@@ -94,7 +96,6 @@ PluginSystem.prototype.reloadPlugins = function () {
}, 3000);
};
-
/* -------- Register functions to let the IntelliSense know what's going on in helper files -------- */
/**
@@ -104,9 +105,9 @@ PluginSystem.prototype._loadPlugins = function () {};
/**
* Internal: Checks a plugin, displays relevant warnings and decides whether the plugin is allowed to be loaded
- * @param {String} folderName Name of the plugin folder. This is used to reference the plugin when thisPluginConf is undefined
- * @param {Object} thisPlugin Plugin file object returned by require()
- * @param {Object} thisPluginConf package.json object of this plugin
+ * @param {string} folderName Name of the plugin folder. This is used to reference the plugin when thisPluginConf is undefined
+ * @param {object} thisPlugin Plugin file object returned by require()
+ * @param {object} thisPluginConf package.json object of this plugin
* @returns {Promise.} Resolved with `true` (can be loaded) or `false` (must not be loaded) on completion
*/
PluginSystem.prototype._checkPlugin = function (folderName, thisPlugin, thisPluginConf) {}; // eslint-disable-line
@@ -116,7 +117,7 @@ PluginSystem.prototype._checkPlugin = function (folderName, thisPlugin, thisPlug
* @param {string} pluginName Name of your plugin
* @returns {string} Path to the folder containing your plugin data
*/
-PluginSystem.prototype.getPluginDataPath = function(pluginName) {}; // eslint-disable-line
+PluginSystem.prototype.getPluginDataPath = function (pluginName) {}; // eslint-disable-line
/**
* Loads a file from your plugin data folder. The data will remain unprocessed. Use `loadPluginConfig()` instead if you want to load your plugin config.
@@ -156,4 +157,10 @@ PluginSystem.prototype.loadPluginConfig = function (pluginName) {}; // eslint-di
* @param {object} pluginConfig Config object of your plugin
* @returns {Promise.} Resolves on success, rejects otherwise with an error
*/
-PluginSystem.prototype.writePluginConfig = function (pluginName, pluginConfig) {}; // eslint-disable-line
\ No newline at end of file
+PluginSystem.prototype.writePluginConfig = function (pluginName, pluginConfig) {}; // eslint-disable-line
+/**
+ * Integrates changes made to the config to the users config
+ * @param {string} pluginName
+ * @returns {Record} the config
+ */
+PluginSystem.prototype.aggregatePluginConfig = function (pluginName) {}; // eslint-disable-line
diff --git a/src/sessions/helpers/handle2FA.js b/src/sessions/helpers/handle2FA.js
index a3fda3b4..29a9b85a 100644
--- a/src/sessions/helpers/handle2FA.js
+++ b/src/sessions/helpers/handle2FA.js
@@ -4,7 +4,7 @@
* Created Date: 09.10.2022 12:59:31
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 17:50:12
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
@@ -22,7 +22,7 @@ const SessionHandler = require("../sessionHandler.js");
/**
* Internal: Handles submitting 2FA code
- * @param {Object} res Response object from startWithCredentials() promise
+ * @param {object} res Response object from startWithCredentials() promise
*/
SessionHandler.prototype._handle2FA = function(res) {
@@ -132,7 +132,7 @@ SessionHandler.prototype._get2FAUserInput = function() {
/**
* Internal: Helper function to make accepting and re-requesting invalid steam guard codes easier
- * @param {String} code Input from user
+ * @param {string} code Input from user
*/
SessionHandler.prototype._acceptSteamGuardCode = function(code) {
diff --git a/src/sessions/helpers/tokenStorageHandler.js b/src/sessions/helpers/tokenStorageHandler.js
index 4042e00f..766dc797 100644
--- a/src/sessions/helpers/tokenStorageHandler.js
+++ b/src/sessions/helpers/tokenStorageHandler.js
@@ -4,7 +4,7 @@
* Created Date: 10.10.2022 12:53:20
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 08.07.2023 00:36:49
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
@@ -20,7 +20,7 @@ const SessionHandler = require("../sessionHandler.js");
/**
* Internal - Attempts to get a token for this account from tokens.db and checks if it's valid
- * @param {function} [callback] Called with `refreshToken` (String) on success or `null` on failure
+ * @param {function(string|null): void} callback Called with `refreshToken` (String) on success or `null` on failure
*/
SessionHandler.prototype._getTokenFromStorage = function(callback) {
@@ -58,7 +58,7 @@ SessionHandler.prototype._getTokenFromStorage = function(callback) {
/**
* Internal - Saves a new token for this account to tokens.db
- * @param {String} token The refreshToken to store
+ * @param {string} token The refreshToken to store
*/
SessionHandler.prototype._saveTokenToStorage = function(token) {
logger("debug", `[${this.bot.logPrefix}] _saveTokenToStorage(): Updating tokens.db entry for accountName '${this.logOnOptions.accountName}'...`);
diff --git a/src/sessions/sessionHandler.js b/src/sessions/sessionHandler.js
index ce0f1c37..ce0bdb75 100644
--- a/src/sessions/sessionHandler.js
+++ b/src/sessions/sessionHandler.js
@@ -4,7 +4,7 @@
* Created Date: 09.10.2022 12:47:27
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 08.07.2023 00:36:45
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
@@ -81,7 +81,7 @@ SessionHandler.prototype.getToken = function() { // I'm not allowed to use arrow
/**
* Internal - Handles resolving the getToken() promise and skipping the account if necessary
- * @param {String} token The token to resolve with or null when account should be skipped
+ * @param {string} token The token to resolve with or null when account should be skipped
*/
SessionHandler.prototype._resolvePromise = function(token) {
@@ -143,7 +143,7 @@ SessionHandler.prototype._attachEvents = function() {};
/**
* Internal: Handles submitting 2FA code
- * @param {Object} res Response object from startWithCredentials() promise
+ * @param {object} res Response object from startWithCredentials() promise
*/
SessionHandler.prototype._handle2FA = function(res) {}; // eslint-disable-line
@@ -154,7 +154,7 @@ SessionHandler.prototype._get2FAUserInput = function() {};
/**
* Internal: Helper function to make accepting and re-requesting invalid steam guard codes easier
- * @param {String} code Input from user
+ * @param {string} code Input from user
*/
SessionHandler.prototype._acceptSteamGuardCode = function(code) {}; // eslint-disable-line
@@ -166,13 +166,13 @@ SessionHandler.prototype._handleCredentialsLoginError = function(err) {}; // esl
/**
* Internal - Attempts to get a token for this account from tokens.db and checks if it's valid
- * @param {function} [callback] Called with `refreshToken` (String) on success or `null` on failure
+ * @param {function(string|null): void} callback Called with `refreshToken` (String) on success or `null` on failure
*/
SessionHandler.prototype._getTokenFromStorage = function(callback) {}; // eslint-disable-line
/**
* Internal - Saves a new token for this account to tokens.db
- * @param {String} token The refreshToken to store
+ * @param {string} token The refreshToken to store
*/
SessionHandler.prototype._saveTokenToStorage = function(token) {}; // eslint-disable-line
diff --git a/src/starter.js b/src/starter.js
index 3a183030..441b1beb 100644
--- a/src/starter.js
+++ b/src/starter.js
@@ -4,7 +4,7 @@
* Created Date: 10.07.2021 10:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 05.07.2023 10:48:48
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -44,7 +44,10 @@ const execArgs = [ "--max-old-space-size=2048", "--optimize-for-size" ];
/* -------- Now, provide functions for attaching/detaching event listeners to the parent and child process -------- */
-// Provide function to only once attach listeners to parent process
+/**
+ * Provide function to only once attach listeners to parent process
+ * @param {function(): void} callback Called on completion
+ */
function attachParentListeners(callback) {
let logafterrestart = [];
@@ -75,7 +78,7 @@ function attachParentListeners(callback) {
require("./controller/helpers/npminteraction.js").reinstallAll(logger, (err, stdout) => { // eslint-disable-line
if (err) {
- logger("error", "I was unable to reinstall all modules. Please try running 'npm install' manually. Error: " + err);
+ logger("error", "I was unable to reinstall all modules. Please try running 'npm install --production' manually. Error: " + err);
process.exit(1);
} else {
// Logger("info", `NPM Log:\n${stdout}`, true) //entire log (not using it rn to avoid possible confusion with vulnerabilities message)
@@ -150,7 +153,10 @@ function attachParentListeners(callback) {
callback();
}
-// Provide function to detach parent process event listeners
+
+/**
+ * Provide function to detach parent process event listeners
+ */
function detachParentListeners() {
logger("info", "Detaching parent's event listeners...", false, true);
@@ -161,7 +167,10 @@ function detachParentListeners() {
if (parentExitEvent) process.removeListener("exit", parentExitEvent);
}
-// Provide function to attach listeners to make communicating with child possible
+
+/**
+ * Provide function to attach listeners to make communicating with child possible
+ */
function attachChildListeners() {
forkedprocess.on("message", (msg) => {
logger("debug", "Received message from child: " + msg);
@@ -220,10 +229,10 @@ function attachChildListeners() {
/**
* Checks if the needed file exists and gets it if it doesn't
- * @param {String} file The file path (from project root) to check and get
- * @param {function} logger Your current logger function
- * @param {Boolean} norequire If set to true the function will return the path instead of importing it
- * @param {Boolean} force If set to true the function will skip checking if the file exists and overwrite it.
+ * @param {string} file The file path (from project root) to check and get
+ * @param {function(string, string): void} logger Your current logger function
+ * @param {boolean} norequire If set to true the function will return the path instead of importing it
+ * @param {boolean} force If set to true the function will skip checking if the file exists and overwrite it.
* @returns {Promise.} Resolves when file was successfully loaded
*/
module.exports.checkAndGetFile = (file, logger, norequire = false, force = false) => {
@@ -234,7 +243,9 @@ module.exports.checkAndGetFile = (file, logger, norequire = false, force = false
return;
}
- // Function that will download a new file, test it and resolve/reject promise
+ /**
+ * Function that will download a new file, test it and resolve/reject promise
+ */
function getNewFile() {
// Determine branch
let branch = "master"; // Default to master
@@ -349,7 +360,7 @@ module.exports.run = () => {
/**
* Restart the application
- * @param {Object} args The argument object that will be passed to `controller.restartargs()`
+ * @param {object} args The argument object that will be passed to `controller.restartargs()`
*/
module.exports.restart = async (args) => {
attachParentListeners(() => {
diff --git a/src/updater/helpers/checkForUpdate.js b/src/updater/helpers/checkForUpdate.js
index 39b5d60f..bff36a93 100644
--- a/src/updater/helpers/checkForUpdate.js
+++ b/src/updater/helpers/checkForUpdate.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 08.07.2023 00:36:54
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -20,10 +20,10 @@ const https = require("https");
/**
* Checks for an available update from the GitHub repo
- * @param {Object} datafile The current `data.json` file from the DataManager
- * @param {String} branch Which branch you want to check. Defaults to the current branch set in `data.json`
- * @param {Boolean} forceUpdate If true an update will be forced, even if disableAutoUpdate is true or the newest version is already installed
- * @param {function} [callback] Called with `updateFound` (Boolean) and `data` (Object) on completion. `updatefound` will be false if the check should fail. `data` includes the full data.json file found online.
+ * @param {object} datafile The current `data.json` file from the DataManager
+ * @param {string} branch Which branch you want to check. Defaults to the current branch set in `data.json`
+ * @param {boolean} forceUpdate If true an update will be forced, even if disableAutoUpdate is true or the newest version is already installed
+ * @param {function(boolean, object): void} callback Called with `updateFound` (Boolean) and `data` (Object) on completion. `updatefound` will be false if the check should fail. `data` includes the full data.json file found online.
*/
module.exports.check = (datafile, branch, forceUpdate, callback) => {
if (!branch) branch = datafile.branch; // Set current branch as default value
diff --git a/src/updater/helpers/createBackup.js b/src/updater/helpers/createBackup.js
index 080f2577..63619951 100644
--- a/src/updater/helpers/createBackup.js
+++ b/src/updater/helpers/createBackup.js
@@ -4,7 +4,7 @@
* Created Date: 26.02.2022 16:54:03
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 20:12:39
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
@@ -31,7 +31,12 @@ module.exports.run = () => {
// Specify which files and folders we can ignore
const dontCopy = [".git", ".github", "node_modules", "backup"];
- // This but slightly modified - Thanks: https://stackoverflow.com/a/26038979/12934162
+ /**
+ * Copy everything in a folder including its subpaths - Thanks (modified): https://stackoverflow.com/a/26038979/12934162
+ * @param {string} src From path
+ * @param {string} dest To path
+ * @param {boolean} firstCall Set to `true` on first call, will be set to `false` on recursive call
+ */
function copyFolderRecursiveSync(src, dest, firstCall) {
let files = [];
diff --git a/src/updater/helpers/customUpdateRules.js b/src/updater/helpers/customUpdateRules.js
index f5c0de90..fc33fe07 100644
--- a/src/updater/helpers/customUpdateRules.js
+++ b/src/updater/helpers/customUpdateRules.js
@@ -4,7 +4,7 @@
* Created Date: 22.02.2022 17:39:21
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 08.07.2023 00:36:58
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
@@ -21,10 +21,10 @@ const fs = require("fs");
/**
* Applies custom update rules for a few files (gets called by downloadUpdate.js)
* @param {any} compatibilityfeaturedone Legacy param, is unused
- * @param {Object} oldconfig The old config from before the update
- * @param {Object} oldadvancedconfig The old advancedconfig from before the update
- * @param {Object} olddatafile The old datafile from before the update
- * @param {function} callback Legacy param, is unused
+ * @param {object} oldconfig The old config from before the update
+ * @param {object} oldadvancedconfig The old advancedconfig from before the update
+ * @param {object} olddatafile The old datafile from before the update
+ * @param {function(): void} callback Legacy param, is unused
* @returns {Promise.} Resolves when we can proceed
*/
module.exports.customUpdateRules = (compatibilityfeaturedone, oldconfig, oldadvancedconfig, olddatafile, callback) => { // eslint-disable-line
@@ -95,8 +95,8 @@ module.exports.customUpdateRules = (compatibilityfeaturedone, oldconfig, oldadva
let newextdata = require(srcdir + "/data/data.json");
// Transfer a few specific values to the new datafile if they exist to avoid errors
- if (olddatafile.timesloggedin) newextdata.timesloggedin = olddatafile.timesloggedin;
- if (olddatafile.totallogintime) newextdata.totallogintime = olddatafile.totallogintime;
+ if (olddatafile.timesloggedin) newextdata.timesloggedin = olddatafile.timesloggedin;
+ if (olddatafile.totallogintime) newextdata.totallogintime = olddatafile.totallogintime;
// Write changes to file
fs.writeFile(srcdir + "/data/data.json", JSON.stringify(newextdata, null, 4), (err) => { // Write the changed file
diff --git a/src/updater/helpers/prepareUpdate.js b/src/updater/helpers/prepareUpdate.js
index 780fa5b7..f6b46344 100644
--- a/src/updater/helpers/prepareUpdate.js
+++ b/src/updater/helpers/prepareUpdate.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 20:16:06
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -21,8 +21,8 @@ const Controller = require("../../controller/controller.js"); // eslint-disable-
/**
* Wait for active requests and log off all bot accounts
* @param {Controller} controller Reference to the controller object
- * @param {function(Object, string)} respondModule If defined, this function will be called with the result of the check. This allows to integrate checking for updates into commands or plugins. Passes resInfo and txt as parameters.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {function(object, string): void} respondModule If defined, this function will be called with the result of the check. This allows to integrate checking for updates into commands or plugins. Passes resInfo and txt as parameters.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
* @returns {Promise.} Resolves when we can proceed
*/
module.exports.run = (controller, respondModule, resInfo) => {
@@ -34,7 +34,7 @@ module.exports.run = (controller, respondModule, resInfo) => {
logger("info", "Bot is logged in. Checking for active requests...", false, true, logger.animation("loading"));
- /* eslint-disable no-inner-declarations */
+ /* eslint-disable no-inner-declarations, jsdoc/require-jsdoc */
function initiateUpdate() { // Make initiating the update a function to simplify the activerequest check below
controller.info.relogAfterDisconnect = false; // Prevents disconnect event (which will be called by logOff) to relog accounts
@@ -76,6 +76,7 @@ module.exports.run = (controller, respondModule, resInfo) => {
}
});
}
+ /* eslint-enable no-inner-declarations, jsdoc/require-jsdoc */
// Check for active request process. If obj not empty then first sort out all invalid/expired entries.
diff --git a/src/updater/helpers/restoreBackup.js b/src/updater/helpers/restoreBackup.js
index 6c9a1d7c..4f8d9803 100644
--- a/src/updater/helpers/restoreBackup.js
+++ b/src/updater/helpers/restoreBackup.js
@@ -4,7 +4,7 @@
* Created Date: 26.02.2022 20:16:44
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 20:16:28
* Modified By: 3urobeat
*
* Copyright (c) 2022 3urobeat
@@ -32,7 +32,12 @@ module.exports.run = () => {
// Specify which files and folders we can ignore
const dontCopy = ["backup"];
- // This but slightly modified - Thanks: https://stackoverflow.com/a/26038979/12934162
+ /**
+ * Copy everything in a folder including its subpaths - Thanks (modified): https://stackoverflow.com/a/26038979/12934162
+ * @param {string} src From path
+ * @param {string} dest To path
+ * @param {boolean} firstCall Set to `true` on first call, will be set to `false` on recursive call
+ */
function copyFolderRecursiveSync(src, dest, firstCall) { // eslint-disable-line no-inner-declarations
let files = [];
diff --git a/src/updater/updater.js b/src/updater/updater.js
index c77a9f48..6839ae38 100644
--- a/src/updater/updater.js
+++ b/src/updater/updater.js
@@ -4,7 +4,7 @@
* Created Date: 09.07.2021 16:26:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 20:17:07
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -47,15 +47,17 @@ module.exports = Updater;
/**
* Checks for any available update and installs it.
- * @param {Boolean} forceUpdate If true an update will be forced, even if disableAutoUpdate is true or the newest version is already installed
- * @param {function(Object, string)} respondModule If defined, this function will be called with the result of the check. This allows to integrate checking for updates into commands or plugins. Passes resInfo and txt as parameters.
- * @param {Object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
+ * @param {boolean} forceUpdate If true an update will be forced, even if disableAutoUpdate is true or the newest version is already installed
+ * @param {function(object, string): void} respondModule If defined, this function will be called with the result of the check. This allows to integrate checking for updates into commands or plugins. Passes resInfo and txt as parameters.
+ * @param {object} resInfo Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
* @returns {Promise.} Promise that will be resolved with false when no update was found or with true when the update check or download was completed. Expect a restart when true was returned.
*/
Updater.prototype.run = function(forceUpdate, respondModule, resInfo) {
let _this = this;
- // Shorthander to abort when a part of the updater is missing and couldn't be repaired
+ /**
+ * Shorthander to abort when a part of the updater is missing and couldn't be repaired
+ */
function stopOnFatalError() {
logger("error", "Fatal Error: Failed to load updater! Please reinstall the bot manually. Aborting...");
_this.stop();
@@ -103,7 +105,9 @@ Updater.prototype.run = function(forceUpdate, respondModule, resInfo) {
}
- // Make initiating the update a function to simplify the permission check below
+ /**
+ * Make initiating the update a function to simplify the permission check below
+ */
async function initiateUpdate() {
_this.controller.info.activeLogin = true; // Block new requests by setting active login to true
diff --git a/start.js b/start.js
index 7d0dd98c..aed97212 100644
--- a/start.js
+++ b/start.js
@@ -4,7 +4,7 @@
* Created Date: 15.01.2020 10:38:00
* Author: 3urobeat
*
- * Last Modified: 29.06.2023 22:35:03
+ * Last Modified: 04.07.2023 19:46:02
* Modified By: 3urobeat
*
* Copyright (c) 2021 3urobeat
@@ -21,6 +21,11 @@
// It is designed to be modular and to start and restart the whole application.
// To be able to change the file it is supposed to start on the fly it pulls the necessary file path from the data.json file
+
+/**
+ * Attempts to load the data.json file.
+ * @returns {object|{ filetostart: string, filetostarturl: string }} Returns an data.json object on success or an object with default values for `filetostart` and `filetostarturl` on failure.
+ */
function getExtdata() {
try { // Just try to require, if it should fail then the actual restoring process will be handled later
return require("./src/data/data.json");
diff --git a/types/types.d.ts b/types/types.d.ts
index 9577b7b0..e86d5e11 100644
--- a/types/types.d.ts
+++ b/types/types.d.ts
@@ -137,35 +137,61 @@ declare namespace Bot {
}
}
+/**
+ * @property description - Description of what this command does
+ * @property args - Array of objects containing information about each parameter supported by this command
+ * @property ownersOnly - Set to true to only allow owners to use this command.
+ * @property run - Function that will be executed when the command runs. Arguments: commandHandler, args, steamID64, respondModule, context, resInfo
+ */
+declare type Command = {
+ description: string;
+ args: CommandArg[];
+ ownersOnly: boolean;
+ run: (...params: any[]) => any;
+};
+
+/**
+ * @property name - Name of this argument. Use common phrases like "ID" or "amount" if possible. If a specific word is expected, put the word inside quotation marks.
+ * @property description - Description of this argument
+ * @property type - Expected datatype of this argument. If read from a chat it will usually be "string"
+ * @property isOptional - True if this argument is optional, false if it must be provided. Make sure to check for missing arguments and return an error if false.
+ * @property ownersOnly - True if this argument is only allowed to be provided by owners set in the config. If the command itself is `ownersOnly`, set this property to `true` as well.
+ */
+declare type CommandArg = {
+ name: string;
+ description: string;
+ type: string;
+ isOptional: boolean;
+ ownersOnly: boolean;
+};
+
/**
* Constructor - Initializes the commandHandler which allows you to integrate core commands into your plugin or add new commands from your plugin.
* @param controller - Reference to the current controller object
*/
declare class CommandHandler {
constructor(controller: Controller);
+ /**
+ * Array of objects, where each object represents a registered command
+ */
+ commands: Command[];
/**
* Internal: Imports core commands on startup
+ * @returns Resolved when all commands have been imported
*/
- _importCoreCommands(): void;
+ _importCoreCommands(): Promise;
/**
* Registers a new command during runtime
* @param command - The command object to register
- * @param command.description - Description of what this command does
- * @param command.ownersOnly - Set to true to only allow owners to use this command.
- * @param command.run - Function that will be executed when the command runs. Arguments: commandHandler, args, steamID64, respondModule, context, resInfo
* @returns true if the command was successfully registered, false otherwise
*/
- registerCommand(command: {
- description: string;
- ownersOnly: boolean;
- run: (...params: any[]) => any;
- }): any;
+ registerCommand(command: Command): boolean;
/**
* The name of the command to unregister during runtime
* @param commandName - Name of the command to unregister
- * @returns true if the command was successfully unregistered, false otherwise
+ * @returns `true` if the command was successfully unregistered, `false` otherwise
*/
- unregisterCommand(commandName: string): any;
+ unregisterCommand(commandName: string): boolean;
/**
* Finds a loaded command by name and runs it
* @param name - The name of the command
@@ -176,7 +202,7 @@ declare class CommandHandler {
* @param resInfo - Object containing additional information your respondModule might need to process the response (for example the userID who executed the command). Please also include a "cmdprefix" key & value pair if your command handler uses a prefix other than "!".
* @returns `true` if command was found, `false` if not
*/
- runCommand(name: string, args: any[], steamID64: number, respondModule: (...params: any[]) => any, context: any, resInfo: any): any;
+ runCommand(name: string, args: any[], steamID64: number, respondModule: (...params: any[]) => any, context: any, resInfo: any): boolean;
/**
* Reloads all core commands. Does NOT reload commands registered at runtime. Please consider reloading the pluginSystem as well.
*/
@@ -187,7 +213,7 @@ declare class CommandHandler {
* Internal: Do the actual commenting, activeRequests entry with all relevant information was processed by the comment command function above.
* @param commandHandler - The commandHandler object
* @param resInfo - Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
- * @param respond - Shortened respondModule call
+ * @param respond - The shortened respondModule call
* @param postComment - The correct postComment function for this idType. Context from the correct bot account is being applied later.
* @param commentArgs - All arguments this postComment function needs, without callback. It will be applied and a callback added as last param. Include a key called "quote" to dynamically replace it with a random quote.
* @param receiverSteamID64 - steamID64 of the profile to receive the comments
@@ -200,7 +226,8 @@ declare function comment(commandHandler: CommandHandler, resInfo: any, respond:
* @param args - The command arguments
* @param requesterSteamID64 - The steamID64 of the requesting user
* @param resInfo - Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
- * @param respond - The function to send messages to the requesting user
+ * @param respond - The shortened respondModule call
+ * @returns Resolves promise with object containing all relevant data when done
*/
declare function getCommentArgs(commandHandler: CommandHandler, args: any[], requesterSteamID64: string, resInfo: any, respond: (...params: any[]) => any): Promise<{ maxRequestAmount: number; commentcmdUsage: string; numberOfComments: number; profileID: string; idType: string; quotesArr: string[]; }>;
@@ -221,7 +248,7 @@ declare function getAvailableBotsForCommenting(commandHandler: CommandHandler, n
* @param amount - Amount of favs requested or "all" to get the max available amount
* @param id - The sharedfile id to favorize
* @param favType - Either "favorite" or "unfavorite", depending on which request this is
- * @returns Promise with obj: `availableAccounts` contains all account names from bot object, `whenAvailable` is a timestamp representing how long to wait until accsNeeded accounts will be available and `whenAvailableStr` is formatted human-readable as time from now
+ * @returns Resolves with obj: `availableAccounts` contains all account names from bot object, `whenAvailable` is a timestamp representing how long to wait until accsNeeded accounts will be available and `whenAvailableStr` is formatted human-readable as time from now
*/
declare function getAvailableBotsForFavorizing(commandHandler: CommandHandler, amount: number | "all", id: string, favType: string): Promise<{ amount: number; availableAccounts: string[]; whenAvailable: number; whenAvailableStr: string; }>;
@@ -231,7 +258,7 @@ declare function getAvailableBotsForFavorizing(commandHandler: CommandHandler, a
* @param args - The command arguments
* @param cmd - Either "upvote", "downvote", "favorite" or "unfavorite", depending on which command is calling this function
* @param resInfo - Object containing additional information your respondModule might need to process the response (for example the userID who executed the command).
- * @param respond - The function to send messages to the requesting user
+ * @param respond - The shortened respondModule call
* @returns If the user provided a specific amount, amount will be a number. If user provided "all" or "max", it will be returned as an unmodified string for getVoteBots.js to handle
*/
declare function getSharedfileArgs(commandHandler: CommandHandler, args: any[], cmd: string, resInfo: any, respond: (...params: any[]) => any): Promise<{ amount: number | string; id: string; }>;
@@ -242,7 +269,7 @@ declare function getSharedfileArgs(commandHandler: CommandHandler, args: any[],
* @param amount - Amount of votes requested or "all" to get the max available amount
* @param id - The sharedfile id to vote on
* @param voteType - "upvote" or "downvote", depending on which request this is
- * @returns Promise with obj: `availableAccounts` contains all account names from bot object, `whenAvailable` is a timestamp representing how long to wait until accsNeeded accounts will be available and `whenAvailableStr` is formatted human-readable as time from now
+ * @returns Resolves with obj: `availableAccounts` contains all account names from bot object, `whenAvailable` is a timestamp representing how long to wait until accsNeeded accounts will be available and `whenAvailableStr` is formatted human-readable as time from now
*/
declare function getAvailableBotsForVoting(commandHandler: CommandHandler, amount: number | "all", id: string, voteType: string): Promise<{ amount: number; availableAccounts: string[]; whenAvailable: number; whenAvailableStr: string; }>;
@@ -285,9 +312,9 @@ declare function failedCommentsObjToString(obj: any): string;
* @param loop - Object returned by misc.js syncLoop() helper
* @param bot - Bot object of the account making this request
* @param id - ID of the sharedfile that receives the votes
- * @returns true if iteration should continue, false if iteration should be skipped using return
+ * @returns `true` if iteration should continue, `false` if iteration should be skipped using return
*/
-declare function handleVoteIterationSkip(commandHandler: CommandHandler, loop: any, bot: Bot, id: string): any;
+declare function handleVoteIterationSkip(commandHandler: CommandHandler, loop: any, bot: Bot, id: string): boolean;
/**
* Checks if the following favorite process iteration should be skipped
@@ -295,9 +322,9 @@ declare function handleVoteIterationSkip(commandHandler: CommandHandler, loop: a
* @param loop - Object returned by misc.js syncLoop() helper
* @param bot - Bot object of the account making this request
* @param id - ID of the sharedfile that receives the votes
- * @returns true if iteration should continue, false if iteration should be skipped using return
+ * @returns `true` if iteration should continue, `false` if iteration should be skipped using return
*/
-declare function handleFavoriteIterationSkip(commandHandler: CommandHandler, loop: any, bot: Bot, id: string): any;
+declare function handleFavoriteIterationSkip(commandHandler: CommandHandler, loop: any, bot: Bot, id: string): boolean;
/**
* Logs vote errors
@@ -347,9 +374,7 @@ declare class Controller {
/**
* Stores references to all bot account objects mapped to their accountName
*/
- bots: {
- [key: string]: Bot;
- };
+ bots: any;
/**
* The main bot account
*/
@@ -384,7 +409,7 @@ declare class Controller {
/**
* Runs internal statusUpdate event code and emits statusUpdate event for plugins
* @param bot - Bot instance
- * @param newStatus - The new status
+ * @param newStatus - The new status of this bot
*/
_statusUpdateEvent(bot: Bot, newStatus: Bot.EStatus): void;
/**
@@ -401,9 +426,8 @@ declare class Controller {
/**
* Checks the remaining space on the friendlist of a bot account, sends a warning message if it is less than 10 and force unfriends oldest lastcomment db user to always keep room for 1 friend.
* @param bot - Bot object of the account to check
- * @param [callback] - Called with `remaining` (Number) on completion
*/
- friendListCapacityCheck(bot: Bot, callback?: (...params: any[]) => any): void;
+ friendListCapacityCheck(bot: Bot, callback: any): void;
/**
* Check for friends who haven't requested comments in config.unfriendtime days and unfriend them
*/
@@ -424,22 +448,24 @@ declare class Controller {
* Handles converting URLs to steamIDs, determining their type if unknown and checking if it matches your expectation
* @param str - The profileID argument provided by the user
* @param expectedIdType - The type of SteamID expected ("profile", "group" or "sharedfile") or `null` if type should be assumed.
- * @param [callback] - Called with `err` (String or null), `steamID64` (String or null), `idType` (String or null) parameters on completion
*/
- handleSteamIdResolving(str: string, expectedIdType: string, callback?: (...params: any[]) => any): void;
+ handleSteamIdResolving(str: string, expectedIdType: string, callback: any): void;
/**
* Logs text to the terminal and appends it to the output.txt file.
* @param type - String that determines the type of the log message. Can be info, warn, error, debug or an empty string to not use the field.
* @param str - The text to log into the terminal
* @param nodate - Setting to true will hide date and time in the message
* @param remove - Setting to true will remove this message with the next one
+ * @param animation - Array containing animation frames as elements
* @param printNow - Ignores the readyafterlogs check and force prints the message now
+ * @param cutToWidth - Cuts the string to the width of the terminal
*/
- logger(type: string, str: string, nodate: boolean, remove: boolean, printNow: boolean): void;
+ logger(type: string, str: string, nodate: boolean, remove: boolean, animation: string[], printNow: boolean, cutToWidth: boolean): void;
/**
* Internal: Call this function after loading advancedconfig.json to set previously inaccessible options
+ * @param advancedconfig - The advancedconfig object imported by the DataManager
*/
- _loggerOptionsUpdateAfterConfigLoad(): void;
+ _loggerOptionsUpdateAfterConfigLoad(advancedconfig: any): void;
/**
* Internal: Logs all held back messages from logAfterReady array
*/
@@ -451,7 +477,7 @@ declare class Controller {
/**
* Runs internal statusUpdate event code and emits statusUpdate event for plugins
* @param bot - Bot instance
- * @param newStatus - The new status
+ * @param newStatus - The new status of this bot
*/
_statusUpdateEvent(bot: Bot, newStatus: Bot.EStatus): void;
/**
@@ -468,9 +494,8 @@ declare class Controller {
/**
* Checks the remaining space on the friendlist of a bot account, sends a warning message if it is less than 10 and force unfriends oldest lastcomment db user to always keep room for 1 friend.
* @param bot - Bot object of the account to check
- * @param [callback] - Called with `remaining` (Number) on completion
*/
- friendListCapacityCheck(bot: Bot, callback?: (...params: any[]) => any): void;
+ friendListCapacityCheck(bot: Bot, callback: any): void;
/**
* Check for friends who haven't requested comments in config.unfriendtime days and unfriend them
*/
@@ -491,22 +516,24 @@ declare class Controller {
* Handles converting URLs to steamIDs, determining their type if unknown and checking if it matches your expectation
* @param str - The profileID argument provided by the user
* @param expectedIdType - The type of SteamID expected ("profile", "group" or "sharedfile") or `null` if type should be assumed.
- * @param [callback] - Called with `err` (String or null), `steamID64` (String or null), `idType` (String or null) parameters on completion
*/
- handleSteamIdResolving(str: string, expectedIdType: string, callback?: (...params: any[]) => any): void;
+ handleSteamIdResolving(str: string, expectedIdType: string, callback: any): void;
/**
* Logs text to the terminal and appends it to the output.txt file.
* @param type - String that determines the type of the log message. Can be info, warn, error, debug or an empty string to not use the field.
* @param str - The text to log into the terminal
* @param nodate - Setting to true will hide date and time in the message
* @param remove - Setting to true will remove this message with the next one
+ * @param animation - Array containing animation frames as elements
* @param printNow - Ignores the readyafterlogs check and force prints the message now
+ * @param cutToWidth - Cuts the string to the width of the terminal
*/
- logger(type: string, str: string, nodate: boolean, remove: boolean, printNow: boolean): void;
+ logger(type: string, str: string, nodate: boolean, remove: boolean, animation: string[], printNow: boolean, cutToWidth: boolean): void;
/**
* Internal: Call this function after loading advancedconfig.json to set previously inaccessible options
+ * @param advancedconfig - The advancedconfig object imported by the DataManager
*/
- _loggerOptionsUpdateAfterConfigLoad(): void;
+ _loggerOptionsUpdateAfterConfigLoad(advancedconfig: any): void;
/**
* Internal: Logs all held back messages from logAfterReady array
*/
@@ -521,8 +548,9 @@ declare class Controller {
/**
* Process data that should be kept over restarts
+ * @param data - Stringified data received by previous process
*/
-declare function restartdata(): void;
+declare function restartdata(data: string): void;
/**
* Implementation of a synchronous for loop in JS (Used as reference: https://whitfin.io/handling-synchronous-asynchronous-loops-javascriptnode-js/)
@@ -541,10 +569,11 @@ declare function syncLoop(iterations: number, func: (...params: any[]) => any, e
declare function round(value: number, decimals: number): number;
/**
- * Converts a timestamp to a human-readable until from now format. Does not care about past/future.
+ * Converts a timestamp to a human-readable "until from now" format. Does not care about past/future.
+ * @param timestamp - UNIX timestamp to convert
* @returns "x seconds/minutes/hours/days"
*/
-declare function timeToString(): string;
+declare function timeToString(timestamp: number): string;
/**
* Pings an URL to check if the service and this internet connection is working
@@ -568,22 +597,19 @@ declare function cutStringsIntelligently(txt: string, limit: number, cutChars: a
/**
* Attempts to reinstall all modules
* @param logger - The currently used logger function (real or fake, the caller decides)
- * @param [callback] - Called with `err` (String) and `stdout` (String) (npm response) parameters on completion
*/
-declare function reinstallAll(logger: (...params: any[]) => any, callback?: (...params: any[]) => any): void;
+declare function reinstallAll(logger: (...params: any[]) => any, callback: any): void;
/**
* Updates all installed packages to versions listed in package.json from the project root directory.
- * @param [callback] - Called with `err` (String) and `stdout` (String) (npm response) parameters on completion
*/
-declare function update(callback?: (...params: any[]) => any): void;
+declare function update(callback: any): void;
/**
* Updates all installed packages to versions listed in package.json
* @param path - Custom path to read package.json from and install packages to
- * @param [callback] - Called with `err` (String) and `stdout` (String) (npm response) parameters on completion
*/
-declare function updateFromPath(path: string, callback?: (...params: any[]) => any): void;
+declare function updateFromPath(path: string, callback: any): void;
/**
* Constructor - The dataManager system imports, checks, handles errors and provides a file updating service for all configuration files
@@ -596,6 +622,38 @@ declare class DataManager {
* @returns Resolves promise when all checks have finished. If promise is rejected you should terminate the application or reset the changes. Reject is called with a String specifying the failed check.
*/
checkData(): Promise;
+ /**
+ * Writes (all) files imported by DataManager back to the disk
+ */
+ writeAllFilesToDisk(): void;
+ /**
+ * Writes cachefile to cache.json on disk
+ */
+ writeCachefileToDisk(): void;
+ /**
+ * Writes datafile to data.json on disk
+ */
+ writeDatafileToDisk(): void;
+ /**
+ * Writes config to config.json on disk
+ */
+ writeConfigToDisk(): void;
+ /**
+ * Writes advancedconfig to advancedconfig.json on disk
+ */
+ writeAdvancedconfigToDisk(): void;
+ /**
+ * Writes logininfo to logininfo.json and accounts.txt on disk, depending on which of the files exist
+ */
+ writeLogininfoToDisk(): void;
+ /**
+ * Writes proxies to proxies.txt on disk
+ */
+ writeProxiesToDisk(): void;
+ /**
+ * Writes quotes to quotes.txt on disk
+ */
+ writeQuotesToDisk(): void;
/**
* Internal: Loads all config & data files from disk and handles potential errors
* @returns Resolves promise when all files have been loaded successfully. The function will log an error and terminate the application should a fatal error occur.
@@ -613,30 +671,24 @@ declare class DataManager {
/**
* Stores all `config.json` settings.
*/
- config: {
- [key: string]: any;
- };
+ config: any;
/**
* Stores all `advancedconfig.json` settings.
*/
- advancedconfig: {
- [key: string]: any;
- };
+ advancedconfig: any;
/**
* Stores all language strings used for responding to a user.
* All default strings have already been replaced with corresponding matches from `customlang.json`.
*/
- lang: {
- [key: string]: string;
- };
+ lang: any;
/**
* Stores all quotes used for commenting provided via the `quotes.txt` file.
*/
- quotes: String[];
+ quotes: string[];
/**
* Stores all proxies provided via the `proxies.txt` file.
*/
- proxies: String[];
+ proxies: string[];
/**
* Stores IDs from config files converted at runtime and backups for all config & data files.
*/
@@ -644,9 +696,7 @@ declare class DataManager {
/**
* Stores the login information for every bot account provided via the `logininfo.json` or `accounts.txt` files.
*/
- logininfo: {
- [key: string]: { accountName: string; password: string; sharedSecret: string; steamGuardCode: null; machineName: string; deviceFriendlyName: string; };
- };
+ logininfo: any;
/**
* Database which stores the timestamp of the last request of every user. This is used to enforce `config.unfriendTime`.
* Document structure: { id: String, time: Number }
@@ -668,14 +718,46 @@ declare class DataManager {
*/
checkData(): Promise;
/**
- * Converts owners and groups imported from config.json to steam ids and updates cachefile. (Call this after dataImport and before dataCheck)
+ * Writes (all) files imported by DataManager back to the disk
*/
- processData(): void;
+ writeAllFilesToDisk(): void;
+ /**
+ * Writes cachefile to cache.json on disk
+ */
+ writeCachefileToDisk(): void;
+ /**
+ * Writes datafile to data.json on disk
+ */
+ writeDatafileToDisk(): void;
+ /**
+ * Writes config to config.json on disk
+ */
+ writeConfigToDisk(): void;
+ /**
+ * Writes advancedconfig to advancedconfig.json on disk
+ */
+ writeAdvancedconfigToDisk(): void;
+ /**
+ * Writes logininfo to logininfo.json and accounts.txt on disk, depending on which of the files exist
+ */
+ writeLogininfoToDisk(): void;
+ /**
+ * Writes proxies to proxies.txt on disk
+ */
+ writeProxiesToDisk(): void;
+ /**
+ * Writes quotes to quotes.txt on disk
+ */
+ writeQuotesToDisk(): void;
/**
* Internal: Loads all config & data files from disk and handles potential errors
* @returns Resolves promise when all files have been loaded successfully. The function will log an error and terminate the application should a fatal error occur.
*/
_importFromDisk(): Promise;
+ /**
+ * Converts owners and groups imported from config.json to steam ids and updates cachefile. (Call this after dataImport and before dataCheck)
+ */
+ processData(): void;
/**
* Gets a random quote
* @param quotesArr - Optional: Custom array of quotes to choose from. If not provided the default quotes set which was imported from the disk will be used.
@@ -714,7 +796,7 @@ declare class DataManager {
* @param token - The token to decode
* @returns JWT object on success, `null` on failure
*/
- decodeJWT(token: string): any;
+ decodeJWT(token: string): any | null;
/**
* Refreshes Backups in cache.json with new data
*/
@@ -730,8 +812,12 @@ declare class DataManager {
_restoreBackup(name: string, filepath: string, cacheentry: any, onlinelink: string, resolve: (...params: any[]) => any): void;
/**
* Internal: Helper function to pull new file from GitHub
+ * @param name - Name of the file
+ * @param filepath - Full path, starting from project root with './'
+ * @param resolve - Your promise to resolve when file was pulled
+ * @param noRequire - Optional: Set to true if resolve() should not be called with require(file) as param
*/
- _pullNewFile(): void;
+ _pullNewFile(name: string, filepath: string, resolve: (...params: any[]) => any, noRequire: boolean): void;
/**
* Converts owners and groups imported from config.json to steam ids and updates cachefile. (Call this after dataImport and before dataCheck)
*/
@@ -774,7 +860,7 @@ declare class DataManager {
* @param token - The token to decode
* @returns JWT object on success, `null` on failure
*/
- decodeJWT(token: string): any;
+ decodeJWT(token: string): any | null;
/**
* Refreshes Backups in cache.json with new data
*/
@@ -790,10 +876,61 @@ declare class DataManager {
_restoreBackup(name: string, filepath: string, cacheentry: any, onlinelink: string, resolve: (...params: any[]) => any): void;
/**
* Internal: Helper function to pull new file from GitHub
+ * @param name - Name of the file
+ * @param filepath - Full path, starting from project root with './'
+ * @param resolve - Your promise to resolve when file was pulled
+ * @param noRequire - Optional: Set to true if resolve() should not be called with require(file) as param
*/
- _pullNewFile(): void;
+ _pullNewFile(name: string, filepath: string, resolve: (...params: any[]) => any, noRequire: boolean): void;
}
+/**
+ * Constructor - Creates a new SharedFile object
+ */
+declare class CSteamSharedFile {
+ constructor(community: SteamCommunity, data: any);
+ _community: SteamCommunity;
+ /**
+ * Deletes a comment from this sharedfile's comment section
+ * @param cid - ID of the comment to delete
+ * @param callback - Takes only an Error object/null as the first argument
+ */
+ deleteComment(cid: string, callback: (...params: any[]) => any): void;
+ /**
+ * Favorites this sharedfile
+ * @param callback - Takes only an Error object/null as the first argument
+ */
+ favorite(callback: (...params: any[]) => any): void;
+ /**
+ * Posts a comment to this sharedfile
+ * @param message - Content of the comment to post
+ * @param callback - Takes only an Error object/null as the first argument
+ */
+ comment(message: string, callback: (...params: any[]) => any): void;
+ /**
+ * Subscribes to this sharedfile's comment section. Note: Checkbox on webpage does not update
+ * @param callback - Takes only an Error object/null as the first argument
+ */
+ subscribe(callback: (...params: any[]) => any): void;
+ /**
+ * Unfavorites this sharedfile
+ * @param callback - Takes only an Error object/null as the first argument
+ */
+ unfavorite(callback: (...params: any[]) => any): void;
+ /**
+ * Unsubscribes from this sharedfile's comment section. Note: Checkbox on webpage does not update
+ * @param callback - Takes only an Error object/null as the first argument
+ */
+ unsubscribe(callback: (...params: any[]) => any): void;
+}
+
+/**
+ * Attempt to load all plugins. If a critical check fails loading will be denied
+ * @param pluginName - Name of the plugin package
+ * @returns Creates a plugin instance and returns it along with more information
+ */
+declare function loadPlugin(pluginName: string): any;
+
/**
* @property load - Called on Plugin load
* @property unload - Called on Plugin unload
@@ -849,6 +986,11 @@ declare class PluginSystem {
* @returns Resolves with your plugin config processed from JSON to an object. If the config failed to load, the promise will be rejected with an error.
*/
loadPluginConfig(pluginName: string): Promise