pupgram 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +105 -0
  3. package/dist/actions/create-post/click-on-create-button.action.d.ts +2 -0
  4. package/dist/actions/create-post/click-on-create-button.action.js +18 -0
  5. package/dist/actions/create-post/click-on-next-button.action.d.ts +2 -0
  6. package/dist/actions/create-post/click-on-next-button.action.js +18 -0
  7. package/dist/actions/create-post/click-on-post-button.action.d.ts +2 -0
  8. package/dist/actions/create-post/click-on-post-button.action.js +18 -0
  9. package/dist/actions/create-post/click-on-share-button.action.d.ts +2 -0
  10. package/dist/actions/create-post/click-on-share-button.action.js +18 -0
  11. package/dist/actions/create-post/index.d.ts +7 -0
  12. package/dist/actions/create-post/index.js +23 -0
  13. package/dist/actions/create-post/put-post-caption.action.d.ts +2 -0
  14. package/dist/actions/create-post/put-post-caption.action.js +24 -0
  15. package/dist/actions/create-post/select-files.action.d.ts +2 -0
  16. package/dist/actions/create-post/select-files.action.js +24 -0
  17. package/dist/actions/create-post/wait-for-configmation.action.d.ts +3 -0
  18. package/dist/actions/create-post/wait-for-configmation.action.js +32 -0
  19. package/dist/actions/delay.action.d.ts +2 -0
  20. package/dist/actions/delay.action.js +12 -0
  21. package/dist/actions/login/click-on-login-button.action.d.ts +2 -0
  22. package/dist/actions/login/click-on-login-button.action.js +23 -0
  23. package/dist/actions/login/index.d.ts +4 -0
  24. package/dist/actions/login/index.js +20 -0
  25. package/dist/actions/login/try-close-notifications-dialog.action.d.ts +2 -0
  26. package/dist/actions/login/try-close-notifications-dialog.action.js +16 -0
  27. package/dist/actions/login/type-password.action.d.ts +2 -0
  28. package/dist/actions/login/type-password.action.js +16 -0
  29. package/dist/actions/login/type-username.action.d.ts +2 -0
  30. package/dist/actions/login/type-username.action.js +16 -0
  31. package/dist/actions/needs-login.action.d.ts +2 -0
  32. package/dist/actions/needs-login.action.js +19 -0
  33. package/dist/configs/en.config.d.ts +2 -0
  34. package/dist/configs/en.config.js +19 -0
  35. package/dist/configs/index.d.ts +2 -0
  36. package/dist/configs/index.js +18 -0
  37. package/dist/configs/pt-br.config.d.ts +2 -0
  38. package/dist/configs/pt-br.config.js +19 -0
  39. package/dist/error.d.ts +3 -0
  40. package/dist/error.js +10 -0
  41. package/dist/helpers/logger.helper.d.ts +2 -0
  42. package/dist/helpers/logger.helper.js +15 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.js +18 -0
  45. package/dist/instagram.d.ts +52 -0
  46. package/dist/instagram.js +155 -0
  47. package/dist/interfaces/action-factory.interface.d.ts +2 -0
  48. package/dist/interfaces/action-factory.interface.js +2 -0
  49. package/dist/interfaces/action.interface.d.ts +10 -0
  50. package/dist/interfaces/action.interface.js +2 -0
  51. package/dist/interfaces/config.interface.d.ts +16 -0
  52. package/dist/interfaces/config.interface.js +2 -0
  53. package/dist/interfaces/index.d.ts +3 -0
  54. package/dist/interfaces/index.js +19 -0
  55. package/dist/interfaces/post-data.interface.d.ts +6 -0
  56. package/dist/interfaces/post-data.interface.js +2 -0
  57. package/dist/utils/delay.util.d.ts +1 -0
  58. package/dist/utils/delay.util.js +6 -0
  59. package/dist/utils/evaluated-click.util.d.ts +2 -0
  60. package/dist/utils/evaluated-click.util.js +11 -0
  61. package/dist/utils/index.d.ts +1 -0
  62. package/dist/utils/index.js +17 -0
  63. package/package.json +48 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marcuth
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # Pupgram
2
+
3
+ **Pupgram** is a powerful automation library for Instagram built on top of `puppeteer`. It simplifies the process of interacting with Instagram programmatically, allowing you to log in, create posts, and manage your account with ease.
4
+
5
+ ## 📦 Installation
6
+
7
+ Installation is straightforward; just use your preferred package manager. Here is an example using NPM:
8
+
9
+ ```bash
10
+ npm i pupgram
11
+ ```
12
+
13
+ ## 🚀 Usage
14
+
15
+ <a href="https://www.buymeacoffee.com/marcuth">
16
+ <img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" width="200">
17
+ </a>
18
+
19
+ ### Basic Example
20
+
21
+ Here is how to use `Pupgram` to log in and create a post.
22
+
23
+ ```ts
24
+ import { Instagram, enConfig } from "pupgram"
25
+
26
+ async function main() {
27
+ // 1. Initialize the Instagram instance
28
+ const instagram = await Instagram.create({
29
+ puppeteer: {
30
+ userDataDir: ".user-data", // Persist session
31
+ headless: "shell",
32
+ },
33
+ logLevel: "debug",
34
+ screenshotOnError: true,
35
+ config: enConfig, // Use English selectors/config
36
+ })
37
+
38
+ // 2. Ensure you are logged in
39
+ await instagram.ensureLoggedIn({
40
+ username: "your-username",
41
+ password: "your-password",
42
+ })
43
+
44
+ // 3. Create a post
45
+ const postData = await instagram.createPost({
46
+ filePaths: ["./path/to/image.png"],
47
+ caption: "Hello from Pupgram! 🚀",
48
+ })
49
+
50
+ console.log(`Post created! URL: https://www.instagram.com/p/${postData.code}`)
51
+ console.log(postData)
52
+
53
+ // 4. Close the instance
54
+ await instagram.close()
55
+ }
56
+
57
+ main()
58
+ ```
59
+
60
+ ---
61
+
62
+ ### Features
63
+
64
+ #### 🤖 Automated Interactions
65
+
66
+ Pupgram handles the complex DOM interactions required to navigate Instagram, including handling dialogues, buttons, and inputs.
67
+
68
+ #### 🔐 Login Management
69
+
70
+ Easily manage user sessions. `ensureLoggedIn` checks if a valid session exists and only performs a full login flow if necessary, saving time and reducing friction.
71
+
72
+ #### 📸 Post Creation
73
+
74
+ Automate content publishing with support for:
75
+ - **Multiple Files**: Upload one or more images/videos.
76
+ - **Captions**: Add rich text captions to your posts.
77
+ - **Confirmation**: Waiting for server confirmation to ensure the post is live before proceeding.
78
+
79
+ #### ⚙️ Configurable
80
+
81
+ Pupgram is designed to be flexible. You can provide custom configurations for different locales or UI variations using the `config` option in `Instagram.create`.
82
+
83
+ ---
84
+
85
+ ## 🧪 Tests (Not included yet, CONTRIBUTE! :D)
86
+
87
+ Automated tests are located in `__tests__`. To run them:
88
+
89
+ ```bash
90
+ npm run test
91
+ ```
92
+
93
+ ## 🤝 Contributing
94
+
95
+ Want to contribute? Follow these steps:
96
+
97
+ 1. Fork the repository.
98
+ 2. Create a new branch (`git checkout -b feature-new`).
99
+ 3. Commit your changes (`git commit -m 'Add new feature'`).
100
+ 4. Push to the branch (`git push origin feature-new`).
101
+ 5. Open a Pull Request.
102
+
103
+ ## 📝 License
104
+
105
+ This project is licensed under the MIT License.
@@ -0,0 +1,2 @@
1
+ import { Action } from "../../interfaces";
2
+ export declare const clickOnCreateButtonAction: Action;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clickOnCreateButtonAction = void 0;
4
+ const utils_1 = require("../../utils");
5
+ const error_1 = require("../../error");
6
+ const clickOnCreateButtonAction = async ({ page, config, logger, defaultTimeout }) => {
7
+ logger.info("Clicking on create button");
8
+ const createButton = await page.waitForSelector(`xpath///*[contains(text(), '${config.createButtonText}')]`, {
9
+ timeout: defaultTimeout,
10
+ });
11
+ if (!createButton) {
12
+ throw new error_1.InstagramError("Create button not found");
13
+ }
14
+ logger.debug("Create button found. Clicking on it");
15
+ await (0, utils_1.evaluatedClick)(page, createButton);
16
+ logger.debug("Create button clicked");
17
+ };
18
+ exports.clickOnCreateButtonAction = clickOnCreateButtonAction;
@@ -0,0 +1,2 @@
1
+ import { Action } from "../../interfaces";
2
+ export declare const clickOnNextButtonAction: Action;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clickOnNextButtonAction = void 0;
4
+ const utils_1 = require("../../utils");
5
+ const error_1 = require("../../error");
6
+ const clickOnNextButtonAction = async ({ page, config, logger, defaultTimeout }) => {
7
+ logger.info("Clicking on next button");
8
+ const nextButton = await page.waitForSelector(`xpath///*[contains(text(), '${config.nextButtonText}')]`, {
9
+ timeout: defaultTimeout,
10
+ });
11
+ if (!nextButton) {
12
+ throw new error_1.InstagramError("Next button not found");
13
+ }
14
+ logger.debug("Next button found. Clicking on it");
15
+ await (0, utils_1.evaluatedClick)(page, nextButton);
16
+ logger.debug("Next button clicked");
17
+ };
18
+ exports.clickOnNextButtonAction = clickOnNextButtonAction;
@@ -0,0 +1,2 @@
1
+ import { Action } from "../../interfaces";
2
+ export declare const clickOnPostButtonAction: Action;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clickOnPostButtonAction = void 0;
4
+ const utils_1 = require("../../utils");
5
+ const error_1 = require("../../error");
6
+ const clickOnPostButtonAction = async ({ page, config, logger, defaultTimeout }) => {
7
+ logger.info("Clicking on post button");
8
+ const postButton = await page.waitForSelector(`xpath///*[contains(text(), '${config.postButtonText}')]`, {
9
+ timeout: defaultTimeout,
10
+ });
11
+ if (!postButton) {
12
+ throw new error_1.InstagramError("Post button not found");
13
+ }
14
+ logger.debug("Post button found. Clicking on it");
15
+ await (0, utils_1.evaluatedClick)(page, postButton);
16
+ logger.debug("Post button clicked");
17
+ };
18
+ exports.clickOnPostButtonAction = clickOnPostButtonAction;
@@ -0,0 +1,2 @@
1
+ import { Action } from "../../interfaces";
2
+ export declare const clickOnShareButtonAction: Action;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clickOnShareButtonAction = void 0;
4
+ const utils_1 = require("../../utils");
5
+ const error_1 = require("../../error");
6
+ const clickOnShareButtonAction = async ({ page, config, logger, defaultTimeout }) => {
7
+ logger.info("Clicking on share button");
8
+ const shareButton = await page.waitForSelector(`xpath///div[@role='button' and (text()='${config.shareButtonText}')]`, {
9
+ timeout: defaultTimeout,
10
+ });
11
+ if (!shareButton) {
12
+ throw new error_1.InstagramError("Share button not found");
13
+ }
14
+ logger.debug("Share button found. Clicking on it");
15
+ await (0, utils_1.evaluatedClick)(page, shareButton);
16
+ logger.debug("Share button clicked");
17
+ };
18
+ exports.clickOnShareButtonAction = clickOnShareButtonAction;
@@ -0,0 +1,7 @@
1
+ export * from "./click-on-create-button.action";
2
+ export * from "./click-on-post-button.action";
3
+ export * from "./select-files.action";
4
+ export * from "./put-post-caption.action";
5
+ export * from "./click-on-next-button.action";
6
+ export * from "./click-on-share-button.action";
7
+ export * from "./wait-for-configmation.action";
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./click-on-create-button.action"), exports);
18
+ __exportStar(require("./click-on-post-button.action"), exports);
19
+ __exportStar(require("./select-files.action"), exports);
20
+ __exportStar(require("./put-post-caption.action"), exports);
21
+ __exportStar(require("./click-on-next-button.action"), exports);
22
+ __exportStar(require("./click-on-share-button.action"), exports);
23
+ __exportStar(require("./wait-for-configmation.action"), exports);
@@ -0,0 +1,2 @@
1
+ import { ActionFactory } from "../../interfaces";
2
+ export declare const putPostCaptionAction: ActionFactory;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.putPostCaptionAction = void 0;
4
+ const error_1 = require("../../error");
5
+ const delay_util_1 = require("../../utils/delay.util");
6
+ const putPostCaptionAction = (caption) => {
7
+ return async ({ page, config, logger, defaultTimeout }) => {
8
+ logger.info("Starting to put post caption");
9
+ const captionElement = await page.waitForSelector(config.captionInputSelector, {
10
+ timeout: defaultTimeout,
11
+ });
12
+ if (!captionElement) {
13
+ logger.error("Failed to find caption input element");
14
+ throw new error_1.InstagramError("Caption element not found");
15
+ }
16
+ logger.debug("Caption element located. Focusing and typing caption...");
17
+ await captionElement.focus();
18
+ await page.keyboard.type(caption);
19
+ await page.keyboard.press("Tab");
20
+ await (0, delay_util_1.delay)(300);
21
+ logger.info("Post caption successfully entered");
22
+ };
23
+ };
24
+ exports.putPostCaptionAction = putPostCaptionAction;
@@ -0,0 +1,2 @@
1
+ import { ActionFactory } from "../../interfaces";
2
+ export declare const selectFilesAction: ActionFactory;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.selectFilesAction = void 0;
4
+ const error_1 = require("../../error");
5
+ const selectFilesAction = (filePaths) => {
6
+ return async ({ page, config, logger, defaultTimeout }) => {
7
+ logger.info("Selecting files");
8
+ const fileInputElement = (await page.waitForSelector(config.fileInputSelector, {
9
+ timeout: defaultTimeout,
10
+ }));
11
+ if (!fileInputElement) {
12
+ throw new error_1.InstagramError("File input element not found");
13
+ }
14
+ logger.debug("File input element found. Making it visible");
15
+ await page.evaluate((selector) => {
16
+ const fileInputElement = document.querySelector(selector);
17
+ fileInputElement.style.display = "block";
18
+ }, config.fileInputSelector);
19
+ logger.debug("File input element visible. Uploading files");
20
+ await fileInputElement.uploadFile(...filePaths);
21
+ logger.debug("Files selected");
22
+ };
23
+ };
24
+ exports.selectFilesAction = selectFilesAction;
@@ -0,0 +1,3 @@
1
+ import { PostData } from "../../interfaces/post-data.interface";
2
+ import { Action } from "../../interfaces";
3
+ export declare const waitForConfirmationAction: Action<PostData>;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.waitForConfirmationAction = void 0;
4
+ const error_1 = require("../../error");
5
+ const waitForConfirmationAction = async ({ page, config, logger, defaultTimeout }) => {
6
+ var _a;
7
+ logger.info("Waiting for confirmation");
8
+ let result;
9
+ const configureEndpoint = "/api/v1/media/configure/";
10
+ logger.debug("Waiting for response");
11
+ const response = await page.waitForResponse((res) => res.url().includes(configureEndpoint) && res.status() === 200 && res.request().method() === "POST", { timeout: 60000 * 2 });
12
+ const json = await response.json();
13
+ if (json.status === "ok" && json.media) {
14
+ const media = json.media;
15
+ result = {
16
+ id: media.id,
17
+ pk: media.pk,
18
+ code: media.code,
19
+ caption: ((_a = media.caption) === null || _a === void 0 ? void 0 : _a.text) || "",
20
+ };
21
+ }
22
+ else {
23
+ throw new error_1.InstagramError("Failed to create post");
24
+ }
25
+ logger.debug("Response received. Waiting for confirmation text message");
26
+ await page.waitForSelector(`xpath///*[contains(text(), "${config.confirmationText}")]`, {
27
+ timeout: defaultTimeout,
28
+ });
29
+ logger.debug("Confirmation text message found");
30
+ return result;
31
+ };
32
+ exports.waitForConfirmationAction = waitForConfirmationAction;
@@ -0,0 +1,2 @@
1
+ import { ActionFactory } from "../interfaces";
2
+ export declare const delayAction: ActionFactory<void>;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.delayAction = void 0;
4
+ const delay_util_1 = require("../utils/delay.util");
5
+ const delayAction = (delayTime) => {
6
+ return async ({ logger }) => {
7
+ logger.info("Delaying");
8
+ await (0, delay_util_1.delay)(delayTime);
9
+ logger.debug("Delaying");
10
+ };
11
+ };
12
+ exports.delayAction = delayAction;
@@ -0,0 +1,2 @@
1
+ import { Action } from "../../interfaces";
2
+ export declare const clickOnLoginButtonAction: Action<void>;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.clickOnLoginButtonAction = void 0;
4
+ const utils_1 = require("../../utils");
5
+ const error_1 = require("../../error");
6
+ const clickOnLoginButtonAction = async ({ config, logger, page, defaultTimeout }) => {
7
+ logger.debug("Clicking on login button");
8
+ const loginButton = await Promise.race([
9
+ page.waitForSelector(`xpath///div[@role='button'][.//text()[normalize-space()='${config.loginButtonText}']]`, {
10
+ timeout: defaultTimeout,
11
+ }),
12
+ page.waitForSelector(config.alternativeLoginButtonSelector, { timeout: defaultTimeout }),
13
+ ]);
14
+ if (!loginButton) {
15
+ logger.error("Login button not found");
16
+ throw new error_1.InstagramError("Login button not found");
17
+ }
18
+ logger.debug("Login button found. Clicking on it...");
19
+ await (0, utils_1.evaluatedClick)(page, loginButton);
20
+ await page.waitForNavigation();
21
+ logger.debug("Login button clicked");
22
+ };
23
+ exports.clickOnLoginButtonAction = clickOnLoginButtonAction;
@@ -0,0 +1,4 @@
1
+ export * from "./type-username.action";
2
+ export * from "./type-password.action";
3
+ export * from "./click-on-login-button.action";
4
+ export * from "./try-close-notifications-dialog.action";
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./type-username.action"), exports);
18
+ __exportStar(require("./type-password.action"), exports);
19
+ __exportStar(require("./click-on-login-button.action"), exports);
20
+ __exportStar(require("./try-close-notifications-dialog.action"), exports);
@@ -0,0 +1,2 @@
1
+ import { Action } from "../../interfaces";
2
+ export declare const tryCloseNotificationsDialogAction: Action<void>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tryCloseNotificationsDialogAction = void 0;
4
+ const utils_1 = require("../../utils");
5
+ const tryCloseNotificationsDialogAction = async ({ config, logger, page, defaultTimeout }) => {
6
+ logger.debug("Trying to close notifications dialog");
7
+ const closeButton = await page.waitForSelector(`xpath///*[contains(text(), '${config.closeNotificationsDialogText}')]`, { timeout: defaultTimeout });
8
+ if (!closeButton) {
9
+ logger.debug("Close button not found");
10
+ return;
11
+ }
12
+ logger.debug("Close button found. Clicking on it...");
13
+ await (0, utils_1.evaluatedClick)(page, closeButton);
14
+ logger.debug("Close button clicked");
15
+ };
16
+ exports.tryCloseNotificationsDialogAction = tryCloseNotificationsDialogAction;
@@ -0,0 +1,2 @@
1
+ import { ActionFactory } from "../../interfaces";
2
+ export declare const typePasswordAction: ActionFactory<void>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.typePasswordAction = void 0;
4
+ const typePasswordAction = (password) => {
5
+ return async ({ config, logger, page, defaultTimeout }) => {
6
+ logger.debug("Typing password");
7
+ const passwordInput = await page.waitForSelector(config.passwordInputSelector, { timeout: defaultTimeout });
8
+ if (!passwordInput) {
9
+ logger.error("Password input not found");
10
+ throw new Error("Password input not found");
11
+ }
12
+ await passwordInput.type(password);
13
+ logger.debug("Password typed");
14
+ };
15
+ };
16
+ exports.typePasswordAction = typePasswordAction;
@@ -0,0 +1,2 @@
1
+ import { ActionFactory } from "../../interfaces";
2
+ export declare const typeUsernameAction: ActionFactory<void>;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.typeUsernameAction = void 0;
4
+ const typeUsernameAction = (username) => {
5
+ return async ({ config, logger, page, defaultTimeout }) => {
6
+ logger.debug("Typing username");
7
+ const usernameInput = await page.waitForSelector(config.usernameInputSelector, { timeout: defaultTimeout });
8
+ if (!usernameInput) {
9
+ logger.error("Username input not found");
10
+ throw new Error("Username input not found");
11
+ }
12
+ await usernameInput.type(username);
13
+ logger.debug("Username typed");
14
+ };
15
+ };
16
+ exports.typeUsernameAction = typeUsernameAction;
@@ -0,0 +1,2 @@
1
+ import { Action } from "../interfaces";
2
+ export declare const needsLoginAction: Action<boolean>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.needsLoginAction = void 0;
4
+ const needsLoginAction = async ({ config, logger, page }) => {
5
+ let needsLogin = true;
6
+ logger.debug("Checking if needs login");
7
+ const loginTextElement = await page.$(`xpath///*[contains(text(), "${config.loginText}") or contains(text(), "${config.loginTextAlternative}")]`);
8
+ logger.debug("Login text element found: ", loginTextElement);
9
+ if (loginTextElement) {
10
+ logger.debug("Needs login");
11
+ needsLogin = true;
12
+ }
13
+ else {
14
+ logger.debug("Does not need login");
15
+ needsLogin = false;
16
+ }
17
+ return needsLogin;
18
+ };
19
+ exports.needsLoginAction = needsLoginAction;
@@ -0,0 +1,2 @@
1
+ import { Config } from "../interfaces";
2
+ export declare const enConfig: Config;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.enConfig = void 0;
4
+ exports.enConfig = {
5
+ loginText: "Log in",
6
+ loginTextAlternative: "Don't have an account?",
7
+ usernameInputSelector: "input[name='email'], input[name='username']",
8
+ passwordInputSelector: "input[name='pass'], input[name='password']",
9
+ loginButtonText: "Log in",
10
+ alternativeLoginButtonSelector: "button[type='submit']",
11
+ closeNotificationsDialogText: "Not now",
12
+ createButtonText: "Create",
13
+ postButtonText: "Post",
14
+ fileInputSelector: "input[type='file'][multiple]",
15
+ captionInputSelector: "div[role='textbox']",
16
+ nextButtonText: "Next",
17
+ confirmationText: "Your post has been shared.",
18
+ shareButtonText: "Share",
19
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./en.config";
2
+ export * from "./pt-br.config";
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./en.config"), exports);
18
+ __exportStar(require("./pt-br.config"), exports);
@@ -0,0 +1,2 @@
1
+ import { Config } from "../interfaces";
2
+ export declare const ptBrConfig: Config;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ptBrConfig = void 0;
4
+ exports.ptBrConfig = {
5
+ loginText: "Entrar no Instagram",
6
+ loginTextAlternative: "Não tem uma conta?",
7
+ usernameInputSelector: "input[name='email'], input[name='username']",
8
+ passwordInputSelector: "input[name='pass'], input[name='password']",
9
+ loginButtonText: "Entrar",
10
+ alternativeLoginButtonSelector: "button[type='submit']",
11
+ closeNotificationsDialogText: "Agora não",
12
+ createButtonText: "Criar",
13
+ postButtonText: "Postar",
14
+ fileInputSelector: "input[type='file'][multiple]",
15
+ captionInputSelector: "div[role='textbox']",
16
+ nextButtonText: "Avançar",
17
+ confirmationText: "Seu post foi compartilhado.",
18
+ shareButtonText: "Compartilhar",
19
+ };
@@ -0,0 +1,3 @@
1
+ export declare class InstagramError extends Error {
2
+ constructor(message: string);
3
+ }
package/dist/error.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InstagramError = void 0;
4
+ class InstagramError extends Error {
5
+ constructor(message) {
6
+ super(message);
7
+ this.name = "InstagramError";
8
+ }
9
+ }
10
+ exports.InstagramError = InstagramError;
@@ -0,0 +1,2 @@
1
+ import winston from "winston";
2
+ export declare function createLogger(name: string, level?: string): winston.Logger;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createLogger = createLogger;
7
+ const winston_1 = __importDefault(require("winston"));
8
+ function createLogger(name, level = "info") {
9
+ return winston_1.default.createLogger({
10
+ level: level,
11
+ format: winston_1.default.format.json(),
12
+ defaultMeta: { service: name },
13
+ transports: [new winston_1.default.transports.Console()],
14
+ });
15
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./configs";
2
+ export * from "./instagram";
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./configs"), exports);
18
+ __exportStar(require("./instagram"), exports);
@@ -0,0 +1,52 @@
1
+ import { Browser } from "puppeteer";
2
+ import { PostData } from "./interfaces/post-data.interface";
3
+ import { Config } from "./interfaces";
4
+ export declare const defaultTimeout = 10000;
5
+ export type CreateInstagramOptions = {
6
+ puppeteer: {
7
+ headless?: boolean | "new" | "shell";
8
+ executablePath?: string;
9
+ userDataDir: string;
10
+ extraArgs?: string[];
11
+ };
12
+ logLevel?: "debug" | "info" | "warn" | "error";
13
+ screenshotOnError?: boolean;
14
+ htmlContentOnError?: boolean;
15
+ config: Config;
16
+ defaultTimeout?: number;
17
+ };
18
+ export type InstagramOptions = {
19
+ browser: Browser;
20
+ logLevel: "debug" | "info" | "warn" | "error";
21
+ screenshotOnError: boolean;
22
+ htmlContentOnError: boolean;
23
+ config: Config;
24
+ defaultTimeout?: number;
25
+ };
26
+ export type LogInOptions = {
27
+ username: string;
28
+ password: string;
29
+ };
30
+ export type CreatePostOptions = {
31
+ filePaths: string[];
32
+ caption: string;
33
+ };
34
+ export declare class Instagram {
35
+ readonly baseUrl = "https://www.instagram.com";
36
+ private readonly browser;
37
+ readonly logLevel: "debug" | "info" | "warn" | "error";
38
+ private readonly logger;
39
+ private readonly screenshotOnError;
40
+ private readonly htmlContentOnError;
41
+ readonly config: Config;
42
+ readonly defaultTimeout: number;
43
+ constructor({ browser, logLevel, screenshotOnError, htmlContentOnError, config, defaultTimeout: optionsDefaultTimeout, }: InstagramOptions);
44
+ static create(options: CreateInstagramOptions): Promise<Instagram>;
45
+ close(): Promise<void>;
46
+ private initalizePage;
47
+ private executeWithDiagnostics;
48
+ needsLogin(): Promise<boolean>;
49
+ logIn({ username, password }: LogInOptions): Promise<void>;
50
+ ensureLoggedIn({ username, password }: LogInOptions): Promise<void>;
51
+ createPost({ caption, filePaths }: CreatePostOptions): Promise<PostData>;
52
+ }
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Instagram = exports.defaultTimeout = void 0;
7
+ const puppeteer_extra_plugin_stealth_1 = __importDefault(require("puppeteer-extra-plugin-stealth"));
8
+ const puppeteer_extra_1 = __importDefault(require("puppeteer-extra"));
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const create_post_1 = require("./actions/create-post");
11
+ const login_1 = require("./actions/login");
12
+ const needs_login_action_1 = require("./actions/needs-login.action");
13
+ const logger_helper_1 = require("./helpers/logger.helper");
14
+ const delay_action_1 = require("./actions/delay.action");
15
+ puppeteer_extra_1.default.use((0, puppeteer_extra_plugin_stealth_1.default)());
16
+ exports.defaultTimeout = 10000;
17
+ class Instagram {
18
+ constructor({ browser, logLevel, screenshotOnError, htmlContentOnError, config, defaultTimeout: optionsDefaultTimeout, }) {
19
+ this.baseUrl = "https://www.instagram.com";
20
+ this.browser = browser;
21
+ this.logLevel = logLevel;
22
+ this.logger = (0, logger_helper_1.createLogger)("Instagram", logLevel);
23
+ this.screenshotOnError = screenshotOnError;
24
+ this.htmlContentOnError = htmlContentOnError;
25
+ this.config = config;
26
+ this.defaultTimeout = optionsDefaultTimeout !== null && optionsDefaultTimeout !== void 0 ? optionsDefaultTimeout : exports.defaultTimeout;
27
+ }
28
+ static async create(options) {
29
+ var _a, _b, _c, _d;
30
+ const browser = await puppeteer_extra_1.default.launch({
31
+ headless: options.puppeteer.headless,
32
+ executablePath: options.puppeteer.executablePath,
33
+ userDataDir: options.puppeteer.userDataDir,
34
+ args: [
35
+ "--start-maximized",
36
+ "--lang=pt-BR",
37
+ "--accept-lang=pt-BR,pt;q=0.9",
38
+ "--no-sandbox",
39
+ "--disable-setuid-sandbox",
40
+ "--disable-dev-shm-usage",
41
+ "--disable-blink-features=AutomationControlled",
42
+ ...((_a = options.puppeteer.extraArgs) !== null && _a !== void 0 ? _a : []),
43
+ ],
44
+ });
45
+ return new Instagram({
46
+ browser: browser,
47
+ logLevel: (_b = options.logLevel) !== null && _b !== void 0 ? _b : "info",
48
+ screenshotOnError: (_c = options.screenshotOnError) !== null && _c !== void 0 ? _c : true,
49
+ htmlContentOnError: (_d = options.htmlContentOnError) !== null && _d !== void 0 ? _d : true,
50
+ config: options.config,
51
+ defaultTimeout: options.defaultTimeout,
52
+ });
53
+ }
54
+ async close() {
55
+ this.logger.info("Closing browser");
56
+ await this.browser.close();
57
+ this.logger.debug("Browser closed");
58
+ }
59
+ async initalizePage() {
60
+ this.logger.info("Initalizing page");
61
+ const page = await this.browser.newPage();
62
+ await page.setViewport({ width: 1920, height: 1080 });
63
+ await page.goto(this.baseUrl, { waitUntil: "networkidle2" });
64
+ this.logger.debug("Page initalized");
65
+ return page;
66
+ }
67
+ async executeWithDiagnostics(actions) {
68
+ const page = await this.initalizePage();
69
+ try {
70
+ let result;
71
+ for (const action of actions) {
72
+ result = await action({
73
+ config: this.config,
74
+ logger: this.logger,
75
+ page: page,
76
+ defaultTimeout: this.defaultTimeout,
77
+ });
78
+ }
79
+ return result;
80
+ }
81
+ catch (error) {
82
+ this.logger.error(error);
83
+ const errorsDir = "errors";
84
+ const currentErrorDir = `${errorsDir}/error-${Date.now()}`;
85
+ if (this.screenshotOnError || this.htmlContentOnError) {
86
+ this.logger.info("Creating error directory");
87
+ await node_fs_1.default.promises.mkdir(currentErrorDir, { recursive: true });
88
+ this.logger.debug("Error directory created");
89
+ }
90
+ if (this.screenshotOnError) {
91
+ this.logger.info("Taking screenshot");
92
+ await page.screenshot({ path: `${currentErrorDir}/error.png` });
93
+ this.logger.debug("Screenshot taken");
94
+ }
95
+ if (this.htmlContentOnError) {
96
+ const htmlContent = await page.content();
97
+ await node_fs_1.default.promises.writeFile(`${currentErrorDir}/error.html`, htmlContent);
98
+ }
99
+ throw error;
100
+ }
101
+ finally {
102
+ await page.close();
103
+ }
104
+ }
105
+ async needsLogin() {
106
+ this.logger.info("Checking if needs login");
107
+ const page = await this.initalizePage();
108
+ const needsLogin = await (0, needs_login_action_1.needsLoginAction)({
109
+ config: this.config,
110
+ logger: this.logger,
111
+ page: page,
112
+ defaultTimeout: this.defaultTimeout,
113
+ });
114
+ await page.close();
115
+ this.logger.debug(`Needs login: ${needsLogin}`);
116
+ return needsLogin;
117
+ }
118
+ async logIn({ username, password }) {
119
+ this.logger.info("Logging in");
120
+ await this.executeWithDiagnostics([
121
+ (0, login_1.typeUsernameAction)(username),
122
+ (0, login_1.typePasswordAction)(password),
123
+ login_1.clickOnLoginButtonAction,
124
+ login_1.tryCloseNotificationsDialogAction,
125
+ ]);
126
+ this.logger.debug("Logged in");
127
+ }
128
+ async ensureLoggedIn({ username, password }) {
129
+ const needsLogin = await this.needsLogin();
130
+ if (needsLogin) {
131
+ this.logger.info("Needs login");
132
+ await this.logIn({ username, password });
133
+ }
134
+ else {
135
+ this.logger.info("Already logged in");
136
+ }
137
+ }
138
+ async createPost({ caption, filePaths }) {
139
+ this.logger.info("Creating post");
140
+ const result = await this.executeWithDiagnostics([
141
+ (0, delay_action_1.delayAction)(1500),
142
+ create_post_1.clickOnCreateButtonAction,
143
+ create_post_1.clickOnPostButtonAction,
144
+ (0, create_post_1.selectFilesAction)(filePaths),
145
+ create_post_1.clickOnNextButtonAction,
146
+ create_post_1.clickOnNextButtonAction,
147
+ (0, create_post_1.putPostCaptionAction)(caption),
148
+ create_post_1.clickOnShareButtonAction,
149
+ create_post_1.waitForConfirmationAction,
150
+ ]);
151
+ this.logger.debug("Post created");
152
+ return result;
153
+ }
154
+ }
155
+ exports.Instagram = Instagram;
@@ -0,0 +1,2 @@
1
+ import { Action } from "./action.interface";
2
+ export type ActionFactory<T = void> = (...args: any[]) => Action<T>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,10 @@
1
+ import { Page } from "puppeteer";
2
+ import { Logger } from "winston";
3
+ import { Config } from "./config.interface";
4
+ export type ActionOptions = {
5
+ config: Config;
6
+ logger: Logger;
7
+ page: Page;
8
+ defaultTimeout?: number;
9
+ };
10
+ export type Action<T = void> = (options: ActionOptions) => Promise<T>;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,16 @@
1
+ export interface Config {
2
+ loginText: string;
3
+ loginTextAlternative: string;
4
+ usernameInputSelector: string;
5
+ passwordInputSelector: string;
6
+ loginButtonText: string;
7
+ alternativeLoginButtonSelector: string;
8
+ closeNotificationsDialogText: string;
9
+ createButtonText: string;
10
+ postButtonText: string;
11
+ fileInputSelector: string;
12
+ captionInputSelector: string;
13
+ nextButtonText: string;
14
+ shareButtonText: string;
15
+ confirmationText: string;
16
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,3 @@
1
+ export * from "./action.interface";
2
+ export * from "./config.interface";
3
+ export * from "./action-factory.interface";
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./action.interface"), exports);
18
+ __exportStar(require("./config.interface"), exports);
19
+ __exportStar(require("./action-factory.interface"), exports);
@@ -0,0 +1,6 @@
1
+ export interface PostData {
2
+ id: string;
3
+ pk: string;
4
+ code: string;
5
+ caption: string;
6
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1 @@
1
+ export declare function delay(ms: number): Promise<unknown>;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.delay = delay;
4
+ function delay(ms) {
5
+ return new Promise((resolve) => setTimeout(resolve, ms));
6
+ }
@@ -0,0 +1,2 @@
1
+ import { ElementHandle, Page } from "puppeteer";
2
+ export declare const evaluatedClick: (page: Page, elementHandle: ElementHandle<Element>) => Promise<void>;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.evaluatedClick = void 0;
4
+ const evaluatedClick = async (page, elementHandle) => {
5
+ await page.evaluate((el) => {
6
+ const element = el;
7
+ element.scrollIntoView({ behavior: "smooth", block: "center" });
8
+ element.click();
9
+ }, elementHandle);
10
+ };
11
+ exports.evaluatedClick = evaluatedClick;
@@ -0,0 +1 @@
1
+ export * from "./evaluated-click.util";
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./evaluated-click.util"), exports);
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "pupgram",
3
+ "version": "0.1.0",
4
+ "type": "commonjs",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist/*",
10
+ "!/**/__tests__"
11
+ ],
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "ts-node ./src/index.ts",
15
+ "format": "prettier --write \"src/**/*.ts\"",
16
+ "playground": "ts-node ./playground/index.ts"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "keywords": [
22
+ "instagram",
23
+ "puppeteer",
24
+ "pupgram",
25
+ "instagram-bot",
26
+ "instagram-automation"
27
+ ],
28
+ "author": "Marcuth",
29
+ "license": "MIT",
30
+ "devDependencies": {
31
+ "@marcuth/env": "^0.0.1",
32
+ "@types/node": "^25.1.0",
33
+ "eslint": "^9.39.2",
34
+ "eslint-config-prettier": "^10.1.8",
35
+ "eslint-plugin-prettier": "^5.5.5",
36
+ "prettier": "^3.8.1",
37
+ "prettier-plugin-sort-imports": "^1.8.9",
38
+ "ts-node": "^10.9.2",
39
+ "typescript": "^5.9.3"
40
+ },
41
+ "dependencies": {
42
+ "clipboardy": "^5.1.0",
43
+ "puppeteer": "^24.36.1",
44
+ "puppeteer-extra": "^3.3.6",
45
+ "puppeteer-extra-plugin-stealth": "^2.11.2",
46
+ "winston": "^3.19.0"
47
+ }
48
+ }