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.
- package/LICENSE +21 -0
- package/README.md +105 -0
- package/dist/actions/create-post/click-on-create-button.action.d.ts +2 -0
- package/dist/actions/create-post/click-on-create-button.action.js +18 -0
- package/dist/actions/create-post/click-on-next-button.action.d.ts +2 -0
- package/dist/actions/create-post/click-on-next-button.action.js +18 -0
- package/dist/actions/create-post/click-on-post-button.action.d.ts +2 -0
- package/dist/actions/create-post/click-on-post-button.action.js +18 -0
- package/dist/actions/create-post/click-on-share-button.action.d.ts +2 -0
- package/dist/actions/create-post/click-on-share-button.action.js +18 -0
- package/dist/actions/create-post/index.d.ts +7 -0
- package/dist/actions/create-post/index.js +23 -0
- package/dist/actions/create-post/put-post-caption.action.d.ts +2 -0
- package/dist/actions/create-post/put-post-caption.action.js +24 -0
- package/dist/actions/create-post/select-files.action.d.ts +2 -0
- package/dist/actions/create-post/select-files.action.js +24 -0
- package/dist/actions/create-post/wait-for-configmation.action.d.ts +3 -0
- package/dist/actions/create-post/wait-for-configmation.action.js +32 -0
- package/dist/actions/delay.action.d.ts +2 -0
- package/dist/actions/delay.action.js +12 -0
- package/dist/actions/login/click-on-login-button.action.d.ts +2 -0
- package/dist/actions/login/click-on-login-button.action.js +23 -0
- package/dist/actions/login/index.d.ts +4 -0
- package/dist/actions/login/index.js +20 -0
- package/dist/actions/login/try-close-notifications-dialog.action.d.ts +2 -0
- package/dist/actions/login/try-close-notifications-dialog.action.js +16 -0
- package/dist/actions/login/type-password.action.d.ts +2 -0
- package/dist/actions/login/type-password.action.js +16 -0
- package/dist/actions/login/type-username.action.d.ts +2 -0
- package/dist/actions/login/type-username.action.js +16 -0
- package/dist/actions/needs-login.action.d.ts +2 -0
- package/dist/actions/needs-login.action.js +19 -0
- package/dist/configs/en.config.d.ts +2 -0
- package/dist/configs/en.config.js +19 -0
- package/dist/configs/index.d.ts +2 -0
- package/dist/configs/index.js +18 -0
- package/dist/configs/pt-br.config.d.ts +2 -0
- package/dist/configs/pt-br.config.js +19 -0
- package/dist/error.d.ts +3 -0
- package/dist/error.js +10 -0
- package/dist/helpers/logger.helper.d.ts +2 -0
- package/dist/helpers/logger.helper.js +15 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/instagram.d.ts +52 -0
- package/dist/instagram.js +155 -0
- package/dist/interfaces/action-factory.interface.d.ts +2 -0
- package/dist/interfaces/action-factory.interface.js +2 -0
- package/dist/interfaces/action.interface.d.ts +10 -0
- package/dist/interfaces/action.interface.js +2 -0
- package/dist/interfaces/config.interface.d.ts +16 -0
- package/dist/interfaces/config.interface.js +2 -0
- package/dist/interfaces/index.d.ts +3 -0
- package/dist/interfaces/index.js +19 -0
- package/dist/interfaces/post-data.interface.d.ts +6 -0
- package/dist/interfaces/post-data.interface.js +2 -0
- package/dist/utils/delay.util.d.ts +1 -0
- package/dist/utils/delay.util.js +6 -0
- package/dist/utils/evaluated-click.util.d.ts +2 -0
- package/dist/utils/evaluated-click.util.js +11 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +17 -0
- 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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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,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
|
+
};
|
package/dist/error.d.ts
ADDED
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,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
|
+
}
|
package/dist/index.d.ts
ADDED
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,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,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,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 @@
|
|
|
1
|
+
export declare function delay(ms: number): Promise<unknown>;
|
|
@@ -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
|
+
}
|