diff --git a/.eslintrc b/.eslintrc index 45a8885..4554fff 100644 --- a/.eslintrc +++ b/.eslintrc @@ -8,5 +8,6 @@ }, "env": { "es6": true - } + }, + "parser": "babel-eslint" } \ No newline at end of file diff --git a/.gitignore b/.gitignore index 05c652a..04a484a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules cookies.json gen-docs.cmd test.js -dev.js \ No newline at end of file +dev.js +session.json \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..5c457d7 --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +docs \ No newline at end of file diff --git a/cache.js b/cache.js new file mode 100644 index 0000000..b2a82e0 --- /dev/null +++ b/cache.js @@ -0,0 +1,101 @@ +const { SearchResult, User, UserDetails, Post, PostDetails } = require("./class.js"); + +class FollowingList { + + /** + * @constructor + * @param {User} user + * @param {User[]} following + * @property {User} user + * @property {User[]} following + */ + constructor(user, following) { + this.user = user; + this.following = following; + } + +} +class FollowerList { + + /** + * @constructor + * @param {User} user + * @param {User[]} follower + * @property {User} user + * @property {User[]} follower + */ + constructor(user, follower) { + this.user = user; + this.follower = follower; + } + +} + +class Cache { + + /** + * @constructor + * @param {FollowingList[]} followingLists + * @param {FollowerList[]} followerLists + * @property {FollowingList[]} [ followingLists = [] ] + * @property {FollowerList[]} [ followerLists = [] ] + */ + constructor(followingLists, followerLists) { + this.followingLists = followingLists ? followingLists : []; + this.followerLists = followerLists ? followerLists : []; + } + + /** + * + * @returns {Cache} empty cache + */ + static empty() { + return new Cache([], []); + } + + /** + * + * @param {User} user + * @param {User[]} following + * @returns {Cache} + */ + addFollowingList(user, following) { + const newFollowingLists = this.followingLists.concat(new FollowingList(user, following)); + const newCache = new Cache(newFollowingLists, this.followerLists); + return newCache; + } + + /** + * + * @param {User} user + * @param {User[]} followers + * @returns {Cache} + */ + addFollowerList(user, followers) { + const newFollowerLists = this.followerLists.concat(new FollowerList(user, followers)); + return new Cache(this.followingLists, newFollowerLists); + } + + /** + * + * @param {User} user + * @returns {User[]} list of people who the specified user follows + */ + findFollowingList(user) { + const selectedFollowingList = this.followingLists.find(followingList => followingList.user.username === user.username); + return selectedFollowingList ? selectedFollowingList.following : []; + } + + /** + * + * @param {User} user + * @returns {User[]} list of people who the specified user follows + */ + findFollowerList(user) { + const selectedFollowerList = this.followerLists.find(followerList => followerList.user.username === user.username); + return selectedFollowerList ? selectedFollowerList.follower : []; + } + +} + +module.exports = { Cache }; \ No newline at end of file diff --git a/docs/Cache.html b/docs/Cache.html index 5b9a82a..61d4fc5 100644 --- a/docs/Cache.html +++ b/docs/Cache.html @@ -146,8 +146,12 @@
Properties:
Type + Attributes + + Default + Description @@ -171,7 +175,21 @@
Properties:
+ + + <optional>
+ + + + + + + + + [] + + @@ -194,7 +212,21 @@
Properties:
+ + + <optional>
+ + + + + + + + + [] + + @@ -368,6 +400,10 @@

(static) empty<

Returns:
+
+ empty cache +
+
@@ -514,7 +550,7 @@
Parameters:
Source:
@@ -737,6 +773,316 @@
Returns:
+ + + + + +

findFollowerList(user) → {Array.<User>}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
user + + +User + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ list of people who the specified user follows +
+ + + +
+
+ Type +
+
+ +Array.<User> + + +
+
+ + + + + + + + + + + + + +

findFollowingList(user) → {Array.<User>}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
user + + +User + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ list of people who the specified user follows +
+ + + +
+
+ Type +
+
+ +Array.<User> + + +
+
+ + + + + + + + @@ -752,13 +1098,13 @@
Returns:

diff --git a/docs/Comment.html b/docs/Comment.html index ca6994b..2c0e7c0 100644 --- a/docs/Comment.html +++ b/docs/Comment.html @@ -344,13 +344,13 @@
Properties:

diff --git a/docs/FollowerList.html b/docs/FollowerList.html index 4ea123b..5e2aeaa 100644 --- a/docs/FollowerList.html +++ b/docs/FollowerList.html @@ -298,13 +298,13 @@
Properties:

diff --git a/docs/FollowingList.html b/docs/FollowingList.html index 0eb38a7..12e7c97 100644 --- a/docs/FollowingList.html +++ b/docs/FollowingList.html @@ -298,13 +298,13 @@
Properties:

diff --git a/docs/Fragment.html b/docs/Fragment.html deleted file mode 100644 index e85f191..0000000 --- a/docs/Fragment.html +++ /dev/null @@ -1,1359 +0,0 @@ - - - - - JSDoc: Class: Fragment - - - - - - - - - - -
- -

Class: Fragment

- - - - - - -
- -
- -

Fragment(timestamp, type, value)

- - -
- -
-
- - - - - - -

new Fragment(timestamp, type, value)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
timestamp - - -TimeStamp - - - -
type - - -FragmentType - - - -
value - - -Object - - - - -
Properties
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeAttributesDescription
link - - -String - - - - - - <optional>
- - - - - -
username - - -String - - - - - - <optional>
- - - - - -
description - - -String - - - - - - <optional>
- - - - - -
postsCount - - -Number - - - - - - <optional>
- - - - - -
followersCount - - -Number - - - - - - <optional>
- - - - - -
followingCount - - -Number - - - - - - <optional>
- - - - - -
posts - - -Array.<Fragment> - - - - - - <optional>
- - - - - -
followers - - -Array.<Fragment> - - - - - - <optional>
- - - - - -
following - - -Array.<Fragment> - - - - - - <optional>
- - - - - -
author - - -Fragment - - - - - - <optional>
- - - - - -
likes - - -Number - - - - - - <optional>
- - - - - -
title - - -String - - - - - - <optional>
- - - - - -
isHashtag - - -Boolean - - - - - - <optional>
- - - - - -
- -
- - - - - - -
Properties:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeAttributesDescription
timestamp - - -TimeStamp - - - - - - - -
type - - -FragmentType - - - - - - - -
link - - -String - - - - - - <optional>
- - - -
username - - -String - - - - - - <optional>
- - - -
description - - -String - - - - - - <optional>
- - - -
postsCount - - -Number - - - - - - <optional>
- - - -
followersCount - - -Number - - - - - - <optional>
- - - -
followingCount - - -Number - - - - - - <optional>
- - - -
posts - - -Array.<Fragment> - - - - - - <optional>
- - - -
followers - - -Array.<Fragment> - - - - - - <optional>
- - - -
following - - -Array.<Fragment> - - - - - - <optional>
- - - -
author - - -Fragment - - - - - - <optional>
- - - -
likes - - -Number - - - - - - <optional>
- - - -
title - - -String - - - - - - <optional>
- - - -
isHashtag - - -Boolean - - - - - - <optional>
- - - -
- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - -

Methods

- - - - - - - -

(static) from(element)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
element - - -User -| - -Post -| - -SearchResult -| - -UserDetails -| - -PostDetails - - - -
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
- - - - -
- - - -
- - - - - - - \ No newline at end of file diff --git a/docs/FragmentType.html b/docs/FragmentType.html deleted file mode 100644 index a6043c6..0000000 --- a/docs/FragmentType.html +++ /dev/null @@ -1,627 +0,0 @@ - - - - - JSDoc: Class: FragmentType - - - - - - - - - - -
- -

Class: FragmentType

- - - - - - -
- -
- -

FragmentType(type)

- - -
- -
-
- - - - - - -

new FragmentType(type)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
type - - -String - - - -
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - -

Methods

- - - - - - - -

(static) none() → {FragmentType}

- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -FragmentType - - -
-
- - - - - - - - - - - - - -

(static) post() → {FragmentType}

- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -FragmentType - - -
-
- - - - - - - - - - - - - -

(static) searchResult() → {FragmentType}

- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -FragmentType - - -
-
- - - - - - - - - - - - - -

(static) user() → {FragmentType}

- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -FragmentType - - -
-
- - - - - - - - - - - - - -
- -
- - - - -
- - - -
- - - - - - - \ No newline at end of file diff --git a/docs/IBError.html b/docs/IBError.html index a03f781..5fb91df 100644 --- a/docs/IBError.html +++ b/docs/IBError.html @@ -366,6 +366,17 @@
Properties:
+

Extends

+ + + + + + + + @@ -394,13 +405,13 @@
Properties:

diff --git a/docs/IBGotoError.html b/docs/IBGotoError.html index d52d834..403ed9d 100644 --- a/docs/IBGotoError.html +++ b/docs/IBGotoError.html @@ -426,6 +426,17 @@
Properties:
+

Extends

+ + + + + + + + @@ -454,13 +465,13 @@
Properties:

diff --git a/docs/IBLoginError.html b/docs/IBLoginError.html index a5c11b8..41ada9d 100644 --- a/docs/IBLoginError.html +++ b/docs/IBLoginError.html @@ -426,6 +426,17 @@
Properties:
+

Extends

+ + + + + + + + @@ -454,13 +465,13 @@
Properties:

diff --git a/docs/InstagramBot.html b/docs/InstagramBot.html index f0c707b..2a0a8ee 100644 --- a/docs/InstagramBot.html +++ b/docs/InstagramBot.html @@ -333,6 +333,390 @@
Properties:
+ + + + + + + + cache + + + + + +Cache + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

Methods

+ + + + + + + +

(async, static) launch(headlessopt, sessionopt) → {Promise.<InstagramBot>}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeAttributesDefaultDescription
headless + + +Boolean + + + + + + <optional>
+ + + + + +
+ + false + +
session + + +Object + + + + + + <optional>
+ + + + + +
+ + {} + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<InstagramBot> + + +
+
+ + + + + + + + + + + + + +

(async, static) loadSession(filePath) → {Promise.<Object>}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -343,6 +727,8 @@
Properties:
+ +
@@ -359,51 +745,152 @@
Properties:
- + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + +
+ session, which probably stores your credentials +
+ + + +
+
+ Type +
+
+ +Promise.<Object> + + +
+
+ + + + + + + + + + + + + +

(async) addObserver(func) → {Promise}

+ + + + + + + + + - - - - - -
Source:
-
+
Parameters:
- +
NameTypeDescription
filePath + + +String + + + +
+ + + + + - + - - + + + + + + + + + + + + + + + + + + +
NameTypeDescription
func + + +function + +
+
+ + - @@ -420,18 +907,22 @@
Properties:
-

Methods

- - + + +
Source:
+
-

(async, static) launch(headlessopt, cookiesopt) → {Promise.<InstagramBot>}

+ +
@@ -443,112 +934,56 @@

(async, static) Parameters:

- - - - - - - - - - - - - +
Returns:
+ - - - - - +
+
+ Type +
+
+ +Promise -
- - - - - - - - - + - - + +

(async) close() → {Promise.<void>}

+ - - - - + - +
+ closes the browser +
+ - - - - - - - - - - -
NameTypeAttributesDefaultDescription
headless - - -Boolean + + + - - - - <optional>
- - - -
- - false - -
cookies - - -Object - - - - <optional>
- - - -
- - [] - -
@@ -584,7 +1019,7 @@
Parameters:
Source:
@@ -620,7 +1055,7 @@
Returns:
-Promise.<InstagramBot> +Promise.<void>
@@ -761,7 +1196,7 @@
Parameters:
Source:
@@ -918,7 +1353,7 @@
Parameters:
Source:
@@ -1020,7 +1455,7 @@

(async) get
Source:
@@ -1232,7 +1667,7 @@

Parameters:
Source:
@@ -1444,7 +1879,7 @@
Parameters:
Source:
@@ -1653,7 +2088,7 @@
Parameters:
Source:
@@ -1807,7 +2242,7 @@
Parameters:
Source:
@@ -1981,9 +2416,111 @@
Parameters:
- - - + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<Array.<Post>> + + +
+
+ + + + + + + + + + + + + +

(async) getSession() → {Promise.<Object>}

+ + + + + + + + + + + + @@ -2019,7 +2556,7 @@
Parameters:
Source:
@@ -2047,6 +2584,10 @@
Parameters:
Returns:
+
+ session, which normally stores credentials +
+
@@ -2055,7 +2596,7 @@
Returns:
-Promise.<Array.<Post>> +Promise.<Object>
@@ -2176,7 +2717,7 @@
Parameters:
Source:
@@ -2336,7 +2877,7 @@
Parameters:
Source:
@@ -2493,7 +3034,7 @@
Parameters:
Source:
@@ -2651,7 +3192,7 @@
Parameters:
Source:
@@ -2825,7 +3366,7 @@
Parameters:
Source:
@@ -2927,7 +3468,7 @@

(async) logout<
Source:
@@ -2981,6 +3522,157 @@

Returns:
+

(async) saveSession(filePath) → {Promise}

+ + + + + + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
filePath + + +String + + + +
+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise + + +
+
+ + + + + + + + + + + + + @@ -3078,7 +3770,7 @@
Parameters:
Source:
@@ -3132,6 +3824,112 @@
Returns:
+

(async) stop() → {Promise.<void>}

+ + + + + + +
+ stops the bot +
+ + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + + + +
Returns:
+ + + + +
+
+ Type +
+
+ +Promise.<void> + + +
+
+ + + + + + + + + + + + +

(async) unfollow(identifier) → {Promise.<any>}

@@ -3235,7 +4033,7 @@
Parameters:
Source:
@@ -3389,7 +4187,7 @@
Parameters:
Source:
@@ -3453,13 +4251,13 @@
Returns:

- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/Post.html b/docs/Post.html index 45b9041..732a8fe 100644 --- a/docs/Post.html +++ b/docs/Post.html @@ -252,13 +252,13 @@
Properties:

- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/PostDetails.html b/docs/PostDetails.html index 1baf19c..2174fb8 100644 --- a/docs/PostDetails.html +++ b/docs/PostDetails.html @@ -405,13 +405,13 @@

Extends


- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/Reference.html b/docs/Reference.html deleted file mode 100644 index 0437c84..0000000 --- a/docs/Reference.html +++ /dev/null @@ -1,284 +0,0 @@ - - - - - JSDoc: Class: Reference - - - - - - - - - - -
- -

Class: Reference

- - - - - - -
- -
- -

Reference(timestamp, value, relation, referencedBy)

- - -
- -
-
- - - - - - -

new Reference(timestamp, value, relation, referencedBy)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
timestamp - - -TimeStamp - - - -
value - - -any - - - -
relation - - -Relation - - - -
referencedBy - - -any - - - -
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - -
- -
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) -
- - - - - \ No newline at end of file diff --git a/docs/Relation.html b/docs/Relation.html deleted file mode 100644 index 1ece15e..0000000 --- a/docs/Relation.html +++ /dev/null @@ -1,373 +0,0 @@ - - - - - JSDoc: Class: Relation - - - - - - - - - - -
- -

Class: Relation

- - - - - - -
- -
- -

Relation(type)

- - -
- -
-
- - - - - - -

new Relation(type)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
type - - -String - - - -
- - - - - - -
Properties:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
type - - -String - - - -
- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - -

Methods

- - - - - - - -

(static) followedBy() → {Relation}

- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -Relation - - -
-
- - - - - - - - - - - - - -
- -
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) -
- - - - - \ No newline at end of file diff --git a/docs/SearchResult.html b/docs/SearchResult.html index 43ee38c..bda73c4 100644 --- a/docs/SearchResult.html +++ b/docs/SearchResult.html @@ -390,13 +390,13 @@
Properties:

- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/Snapshot.html b/docs/Snapshot.html deleted file mode 100644 index ab70c57..0000000 --- a/docs/Snapshot.html +++ /dev/null @@ -1,235 +0,0 @@ - - - - - JSDoc: Class: Snapshot - - - - - - - - - - -
- -

Class: Snapshot

- - - - - - -
- -
- -

Snapshot(referencesopt)

- - -
- -
-
- - - - - - -

new Snapshot(referencesopt)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeAttributesDefaultDescription
references - - -Array.<Reference> - - - - - - <optional>
- - - - - -
- - [] - -
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - -
- -
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) -
- - - - - \ No newline at end of file diff --git a/docs/TimeStamp.html b/docs/TimeStamp.html deleted file mode 100644 index ee5e6d1..0000000 --- a/docs/TimeStamp.html +++ /dev/null @@ -1,373 +0,0 @@ - - - - - JSDoc: Class: TimeStamp - - - - - - - - - - -
- -

Class: TimeStamp

- - - - - - -
- -
- -

TimeStamp(date)

- - -
- -
-
- - - - - - -

new TimeStamp(date)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
date - - -Date - - - -
- - - - - - -
Properties:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
date - - -Date - - - -
- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - -

Methods

- - - - - - - -

now() → {TimeStamp}

- - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - -
Returns:
- - - - -
-
- Type -
-
- -TimeStamp - - -
-
- - - - - - - - - - - - - -
- -
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) -
- - - - - \ No newline at end of file diff --git a/docs/User.html b/docs/User.html index 9ccaf62..f24b0f3 100644 --- a/docs/User.html +++ b/docs/User.html @@ -394,13 +394,13 @@
Properties:

- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/UserDetails.html b/docs/UserDetails.html index de7f772..a380f09 100644 --- a/docs/UserDetails.html +++ b/docs/UserDetails.html @@ -493,13 +493,13 @@

Extends


- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/UserSnapshot.html b/docs/UserSnapshot.html deleted file mode 100644 index d084ee9..0000000 --- a/docs/UserSnapshot.html +++ /dev/null @@ -1,902 +0,0 @@ - - - - - JSDoc: Class: UserSnapshot - - - - - - - - - - -
- -

Class: UserSnapshot

- - - - - - -
- -
- -

UserSnapshot(timestamp, user)

- - -
- -
-
- - - - - - -

new UserSnapshot(timestamp, user)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
timestamp - - -TimeStamp - - - -
user - - -Object - - - - -
Properties
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeAttributesDescription
link - - -String - - - - - - <optional>
- - - - - -
username - - -String - - - - - - <optional>
- - - - - -
description - - -String - - - - - - <optional>
- - - - - -
postsCount - - -Number - - - - - - <optional>
- - - - - -
followersCount - - -Number - - - - - - <optional>
- - - - - -
followingCount - - -Number - - - - - - <optional>
- - - - - -
posts - - -Array.<Post> - - - - - - <optional>
- - - - - -
followers - - -Array.<User> - - - - - - <optional>
- - - - - -
following - - -Array.<User> - - - - - - <optional>
- - - - - -
- -
- - - - - - -
Properties:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeAttributesDescription
timestamp - - -TimeStamp - - - - - - - -
link - - -String - - - - - - <optional>
- - - -
username - - -String - - - - - - <optional>
- - - -
description - - -String - - - - - - <optional>
- - - -
postsCount - - -Number - - - - - - <optional>
- - - -
followersCount - - -Number - - - - - - <optional>
- - - -
followingCount - - -Number - - - - - - <optional>
- - - -
posts - - -Array.<Post> - - - - - - <optional>
- - - -
followers - - -Array.<User> - - - - - - <optional>
- - - -
following - - -Array.<User> - - - - - - <optional>
- - - -
- - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- - - - - - - - - - - - - - - - - - - - -
- -
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:55 GMT+0200 (Mitteleuropäische Sommerzeit) -
- - - - - \ No newline at end of file diff --git a/docs/cache.js.html b/docs/cache.js.html index 714e594..6c4e430 100644 --- a/docs/cache.js.html +++ b/docs/cache.js.html @@ -65,17 +65,17 @@

Source: cache.js

* @constructor * @param {FollowingList[]} followingLists * @param {FollowerList[]} followerLists - * @property {FollowingList[]} followingLists - * @property {FollowerList[]} followerLists + * @property {FollowingList[]} [ followingLists = [] ] + * @property {FollowerList[]} [ followerLists = [] ] */ constructor(followingLists, followerLists) { - this.followingLists = followingLists; - this.followerList = followerLists; + this.followingLists = followingLists ? followingLists : []; + this.followerLists = followerLists ? followerLists : []; } /** * - * @returns {Cache} + * @returns {Cache} empty cache */ static empty() { return new Cache([], []); @@ -88,8 +88,11 @@

Source: cache.js

* @returns {Cache} */ addFollowingList(user, following) { - return new Cache(this.followingLists.concat(new FollowingList(user, following)), this.followerList); + const newFollowingLists = this.followingLists.concat(new FollowingList(user, following)); + const newCache = new Cache(newFollowingLists, this.followerLists); + return newCache; } + /** * * @param {User} user @@ -97,10 +100,28 @@

Source: cache.js

* @returns {Cache} */ addFollowerList(user, followers) { - return new Cache(this.followingLists, this.followerLists.concat(new FollowerList(user, followers))); + const newFollowerLists = this.followerLists.concat(new FollowerList(user, followers)); + return new Cache(this.followingLists, newFollowerLists); } - findFollowingList() { - + + /** + * + * @param {User} user + * @returns {User[]} list of people who the specified user follows + */ + findFollowingList(user) { + const selectedFollowingList = this.followingLists.find(followingList => followingList.user.username === user.username); + return selectedFollowingList ? selectedFollowingList.following : []; + } + + /** + * + * @param {User} user + * @returns {User[]} list of people who the specified user follows + */ + findFollowerList(user) { + const selectedFollowerList = this.followerLists.find(followerList => followerList.user.username === user.username); + return selectedFollowerList ? selectedFollowerList.follower : []; } } @@ -115,13 +136,13 @@

Source: cache.js


- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:38 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/class.js.html b/docs/class.js.html index 90d1a0a..66b23d8 100644 --- a/docs/class.js.html +++ b/docs/class.js.html @@ -159,13 +159,13 @@

Source: class.js


- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/error.js.html b/docs/error.js.html index 43506d1..c740009 100644 --- a/docs/error.js.html +++ b/docs/error.js.html @@ -29,7 +29,7 @@

Source: error.js

class IBError extends Error {
 
     /**
-     * 
+     * @augments Error
      * @param {Number} code 
      * @param {String} message 
      * @param {Error} [error]
@@ -49,7 +49,7 @@ 

Source: error.js

class IBLoginError extends IBError { /** - * + * @augments IBError * @param {Number} code * @param {String} message * @param {String} username @@ -69,7 +69,7 @@

Source: error.js

class IBGotoError extends IBError { /** - * + * @augments IBError * @param {Number} code * @param {String} message * @param {any} goal @@ -101,13 +101,13 @@

Source: error.js


- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/format.js.html b/docs/format.js.html index 10559d1..a574e3e 100644 --- a/docs/format.js.html +++ b/docs/format.js.html @@ -46,13 +46,13 @@

Source: format.js


- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/global.html b/docs/global.html index 90027bd..319e1dd 100644 --- a/docs/global.html +++ b/docs/global.html @@ -790,139 +790,6 @@
Parameters:
- - - - - - -

(async) dismissCookiePopup(page)

- - - - - - - - - - - - - - -
Parameters:
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NameTypeDescription
page - - -puppeteer.Page - - - -
- - - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Source:
-
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - @@ -2221,13 +2088,13 @@
Returns:

- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/index.html b/docs/index.html index d21453a..5556bb4 100644 --- a/docs/index.html +++ b/docs/index.html @@ -50,13 +50,13 @@


- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/index.js.html b/docs/index.js.html index 99a324c..aec20eb 100644 --- a/docs/index.js.html +++ b/docs/index.js.html @@ -30,26 +30,37 @@

Source: index.js

const data = require("./scripts/data.js"); const misc = require("./scripts/misc.js"); const navigation = require("./scripts/navigation.js"); +const popup = require("./scripts/popup.js"); +const observer = require("./scripts/observer.js"); const puppeteer = require("puppeteer"); -const { IBError } = require("./error.js"); +const fs = require("fs"); + +const { IBError, IBLoginError } = require("./error.js"); const { errorMessage } = require("./message.js"); const { SearchResult, User, UserDetails } = require("./class.js"); +const { Cache } = require("./cache.js"); class Queue { constructor() { this.list = []; + this.shouldRun = true; // start running Queue this.run(); } async run() { + if(!this.shouldRun) return; + if(this.list.length > 0) { await this.list[0](); this.list.splice(0, 1); } - setTimeout(() => this.run(), 1000); + this.timeout = setTimeout(() => this.run(), 1000); + } + stop() { + this.shouldRun = false; } push(func) { return new Promise((resolve, reject) => { @@ -77,6 +88,7 @@

Source: index.js

* @property {Queue} queue * @property {Boolean} authenticated * @property {String} username + * @property {Cache} cache */ constructor(browser, page, authenticated = false) { this.browser = browser; @@ -84,33 +96,121 @@

Source: index.js

this.queue = new Queue(); this.authenticated = authenticated; this.username = null; + this.cache = Cache.empty(); } /** * * @param {Boolean} [ headless = false ] - * @param {Object} [ cookies = [] ] + * @param {Object} [ session = {} ] * @returns {Promise<InstagramBot>} */ - static async launch(headless = false, cookies = []) { + static async launch(headless = false, session = {}) { const browser = await misc.launchBrowser({ headless }); + const cookies = session.cookies ? session.cookies : []; const page = await misc.newPage(browser, "en", cookies); // check if page is already authenticated const isAuthenticated = await data.isAuthenticated(page); - return new InstagramBot(browser, page, isAuthenticated); + // create bot + const bot = await new InstagramBot(browser, page, isAuthenticated); + + // add observer to bot + await bot.addObserver(async () => { + // will execute everytime the page changes + await popup.dismissCookiePopup(page); + await popup.dismissNotificationPopup(page); + }); + + return bot; + } + + /** + * + * @param {String} filePath + * @returns {Promise<Object>} session, which probably stores your credentials + */ + static async loadSession(filePath) { + try { + + const raw = await fs.promises.readFile(filePath); + const session = JSON.parse(raw); + return session; + + } catch(e) { + throw new IBError(errorMessage.failedToLoadSession.code, errorMessage.failedToLoadSession.message, e); + } + } + + /** + * closes the browser + * @returns {Promise<void>} + */ + async close() { + await this.page.close(); + await this.browser.close(); + await this.browser.disconnect(); + await this.queue.stop(); + } + + /** + * stops the bot + * @returns {Promise<void>} + */ + async stop() { + await this.page.close(); + await this.browser.close(); + await this.browser.disconnect(); + await this.queue.stop(); + } + + /** + * + * @param {Function} func + * @returns {Promise} + */ + async addObserver(func) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); + + await observer.addObserver(this.page, func); } /** * @returns {Promise<Object>} */ async getCookies() { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); + const cookies = await this.queue.push(() => data.getCookies(this.page)); return cookies; } + /** + * + * @returns {Promise<Object>} session, which normally stores credentials + */ + async getSession() { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); + + const cookies = await this.getCookies(); + return { cookies }; + } + + /** + * + * @param {String} filePath + * @returns {Promise} + */ + async saveSession(filePath) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); + + const session = await this.getSession(); + const text = JSON.stringify(session); + await fs.promises.writeFile(filePath, text); + } + /** * * @param {String} username @@ -118,6 +218,9 @@

Source: index.js

* @returns {Promise<any>} */ async login(username, password) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); + if(this.authenticated) throw new IBLoginError(errorMessage.botAlreadyAuthenticated.code, errorMessage.botAlreadyAuthenticated.message); + await this.queue.push(() => actions.login(this.page, username, password)); this.username = username; @@ -129,6 +232,7 @@

Source: index.js

* @returns {Promise<any>} */ async logout() { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) return; await this.queue.push(() => actions.logout(this.page, this.username)); @@ -144,10 +248,16 @@

Source: index.js

* @returns {Promise<User[]>} */ async getFollowing(identifier, minLength = 50) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const following = await this.queue.push(() => data.getFollowing(this.page, identifier, minLength)); + // store data in cache, if types are compatible + if(identifier instanceof User) { + this.cache = this.cache.addFollowingList(identifier, following); + } + return following; } @@ -158,10 +268,16 @@

Source: index.js

* @returns {Promise<User[]>} */ async getFollower(identifier, minLength = 50) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const follower = await this.queue.push(() => data.getFollower(this.page, identifier, minLength)); + // store data in cache, if types are compatible + if(identifier instanceof User) { + this.cache = this.cache.addFollowerList(identifier, follower); + } + return follower; } @@ -171,6 +287,7 @@

Source: index.js

* @returns {Promise<any>} */ async follow(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => actions.follow(this.page, identifier)); @@ -182,6 +299,7 @@

Source: index.js

* @returns {Promise<any>} */ async unfollow(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => actions.unfollow(this.page, identifier)); @@ -193,6 +311,7 @@

Source: index.js

* @returns {Promise<Boolean>} whether you are following the specified user or not */ async isFollowing(userIdentifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const result = await this.queue.push(() => data.isFollowing(this.page, userIdentifier)); @@ -205,6 +324,7 @@

Source: index.js

* @returns {Promise<SearchResult[]>} */ async search(searchTerm) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const searchResults = await this.queue.push(() => navigation.search(this.page, searchTerm)); @@ -217,6 +337,7 @@

Source: index.js

* @returns {Promise<any>} */ async goto(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => navigation.goto(this.page, identifier)); @@ -228,6 +349,7 @@

Source: index.js

* @returns {Promise<UserDetails>} */ async getUserDetails(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const userDetails = await this.queue.push(() => data.getUserDetails(this.page, identifier)); @@ -241,6 +363,7 @@

Source: index.js

* @returns {Promise<Post[]>} */ async getPosts(identifier, minLength = 50) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const posts = await this.queue.push(() => data.getPosts(this.page, identifier, minLength)); @@ -253,6 +376,7 @@

Source: index.js

* @returns {Promise<PostDetails>} */ async getPostDetails(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const postDetails = await this.queue.push(() => data.getPostDetails(this.page, identifier)); @@ -265,6 +389,7 @@

Source: index.js

* @returns {Promise<any>} */ async likePost(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => actions.likePost(this.page, identifier)); @@ -276,6 +401,7 @@

Source: index.js

* @returns {Promise<any>} */ async unlikePost(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => actions.unlikePost(this.page, identifier)); @@ -288,6 +414,7 @@

Source: index.js

* @returns {Promise<any>} */ async commentPost(postIdentifier, comment) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => actions.commentPost(this.page, postIdentifier, comment)); @@ -300,6 +427,7 @@

Source: index.js

* @returns {Promise<Comment[]>} */ async getPostComments(postIdentifier, minComments = 5) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const comments = await this.queue.push(() => data.getPostComments(this.page, postIdentifier, minComments)); @@ -318,13 +446,13 @@

Source: index.js


- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/docs/popup.js.html b/docs/popup.js.html deleted file mode 100644 index 8b697e7..0000000 --- a/docs/popup.js.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - JSDoc: Source: popup.js - - - - - - - - - - -
- -

Source: popup.js

- - - - - - -
-
-
const puppeteer = require("puppeteer");
-const tools = require("./tools.js");
-
-/**
- * 
- * @param {puppeteer.Page} page 
- */
-const dismissCookiePopup = async (page) => {
-
-    await tools.clickOnButton(page, "Accept All");
-
-};
-const dismissNotificationPopup = async (page) => {
-
-    await tools.clickOnButton(page, "Not Now");
-
-};
-
-module.exports = {
-    dismissCookiePopup,
-    dismissNotificationPopup,
-};
-
-
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) -
- - - - - diff --git a/docs/snapshot.js.html b/docs/snapshot.js.html deleted file mode 100644 index 684fd4f..0000000 --- a/docs/snapshot.js.html +++ /dev/null @@ -1,319 +0,0 @@ - - - - - JSDoc: Source: snapshot.js - - - - - - - - - - -
- -

Source: snapshot.js

- - - - - - -
-
-
const { SearchResult, User, UserDetails, Post, PostDetails } = require("./class.js");
-
-class TimeStamp {
-
-    /**
-     * @class
-     * @param {Date} date 
-     * @property {Date} date
-     */
-    constructor(date) {
-        this.date = date;
-    }
-
-    /**
-     * @returns {TimeStamp}
-     */
-    now() {
-        return new TimeStamp(new Date());
-    }
-
-}
-class Relation {
-
-    /**
-     * @constructor 
-     * @param {String} type 
-     * @property {String} type
-     */
-     constructor(type) {
-        this.type = type;
-    }
-
-    /**
-     * 
-     * @returns {Relation}
-     */
-     static followedBy() {
-        return Relation("followed by");
-    }
-
-}
-class Reference {
-
-    /**
-     * 
-     * @param {TimeStamp} timestamp 
-     * @param {any} value 
-     * @param {Relation} relation 
-     * @param {any} referencedBy 
-     */
-    constructor(timestamp, value, relation, referencedBy) {
-        this.timestamp = timestamp;
-        this.value = value;
-        this.relation = relation;
-        this.referencedBy = referencedBy;
-    }
-
-}
-class UserSnapshot {
-
-    /**
-     * @class
-     * @param {TimeStamp} timestamp
-     * @param {Object} user 
-     * @param {String} [ user.link ]
-     * @param {String} [ user.username ]
-     * @param {String} [ user.description ]
-     * @param {Number} [ user.postsCount ]
-     * @param {Number} [ user.followersCount ]
-     * @param {Number} [ user.followingCount ]
-     * @param {Post[]} [ user.posts ]
-     * @param {User[]} [ user.followers ]
-     * @param {User[]} [ user.following ]
-     * @property {TimeStamp} timestamp
-     * @property {String} [ link ]
-     * @property {String} [ username ]
-     * @property {String} [ description ]
-     * @property {Number} [ postsCount ]
-     * @property {Number} [ followersCount ]
-     * @property {Number} [ followingCount ]
-     * @property {Post[]} [ posts ]
-     * @property {User[]} [ followers ]
-     * @property {User[]} [ following ]
-     */
-    constructor(timestamp, { link, username, description, postsCount, followersCount, followingCount, posts, followers, following }) {
-        this.timestamp = timestamp;
-        this.link = link;
-        this.username = username;
-        this.description = description;
-        this.postsCount = postsCount;
-        this.followersCount = followersCount;
-        this.followingCount = followingCount;
-        this.posts = posts;
-        this.followers = followers;
-        this.following = following;
-    }
-
-}
-
-class FragmentType {
-
-    /**
-     * @class
-     * @param {String} type 
-     */
-    constructor(type) {
-        this.type = type;
-    }
-
-    /**
-     * 
-     * @returns {FragmentType}
-     */
-    static user() {
-        return new FragmentType("user");
-    }
-    /**
-     * 
-     * @returns {FragmentType}
-     */
-    static post() {
-        return new FragmentType("post");
-    } 
-    /**
-     * 
-     * @returns {FragmentType}
-     */
-    static searchResult() {
-        return new FragmentType("searchResult");
-    }
-    /**
-     * @returns {FragmentType}
-     */
-    static none() {
-        return new FragmentType("none");
-    }
-
-}
-class Fragment {
-
-    /**
-     * @class
-     * @param {TimeStamp} timestamp
-     * @param {FragmentType} type
-     * @param {Object} value 
-     * @param {String} [ value.link ]
-     * @param {String} [ value.username ]
-     * @param {String} [ value.description ]
-     * @param {Number} [ value.postsCount ]
-     * @param {Number} [ value.followersCount ]
-     * @param {Number} [ value.followingCount ]
-     * @param {Fragment[]} [ value.posts ]
-     * @param {Fragment[]} [ value.followers ]
-     * @param {Fragment[]} [ value.following ]
-     * @param {Fragment} [ value.author ]
-     * @param {Number} [ value.likes ]
-     * @param {String} [ value.title ]
-     * @param {Boolean} [ value.isHashtag ]
-     * @property {TimeStamp} timestamp
-     * @property {FragmentType} type
-     * @property {String} [ link ]
-     * @property {String} [ username ]
-     * @property {String} [ description ]
-     * @property {Number} [ postsCount ]
-     * @property {Number} [ followersCount ]
-     * @property {Number} [ followingCount ]
-     * @property {Fragment[]} [ posts ]
-     * @property {Fragment[]} [ followers ]
-     * @property {Fragment[]} [ following ]
-     * @property {Fragment} [ author ]
-     * @property {Number} [ likes ]
-     * @property {String} [ title ]
-     * @property {Boolean} [ isHashtag ]
-     */
-    constructor(timestamp, type, { link, username, description, postsCount, followersCount, followingCount, posts, followers, following, author, likes, title, isHashtag }) {
-        this.timestamp = timestamp;
-        this.type = type;
-
-        this.link = link;
-        this.username = username;
-        this.description = description;
-        this.postsCount = postsCount;
-        this.followersCount = followersCount;
-        this.followingCount = followingCount;
-        this.posts = posts;
-        this.followers = followers;
-        this.following = following;
-        this.author = author;
-        this.likes = likes;
-        this.title = title;
-        this.isHashtag = isHashtag;
-    }
-
-    /**
-     * 
-     * @param {User | Post | SearchResult | UserDetails | PostDetails} element 
-     */
-    static from(element) {
-        const timestamp = TimeStamp.now();
-
-        if(typeof element === UserDetails) {
-            return new Fragment(timestamp, FragmentType.user(), {
-                link: element.link,
-                username: element.username,
-                description: element.description,
-                postsCount: element.posts,
-                followersCount: element.followersCount,
-                followingCount: element.followingCount,
-            });
-        } else if(typeof element === User) {
-            return new Fragment(timestamp, FragmentType.user(), {
-                link: element.link,
-                username: element.username,
-                description: element.description,
-            });
-        } else if(typeof element === PostDetails) {
-            return new Fragment(timestamp, FragmentType.post(), {
-                link: element.link,
-                author: element.author,
-                likes: element.likes
-            });
-        } else if(typeof element === Post) {
-            return new Fragment(timestamp, FragmentType.post(), {
-                link: element.link
-            });
-        } else if(typeof element === SearchResult) {
-            return new Fragment(timestamp, FragmentType.searchResult(), {
-                link: element.link,
-                title: element.title,
-                description: element.description,
-                isHashtag: element.isHashtag
-            });
-        } else {
-            return new Fragment(timestamp, FragmentType.none, {});
-        }
-    }
-
-
-
-}
-
-class Snapshot {
-
-    /**
-     * @class
-     * @param {Reference[]} [ references = []]
-     */
-    constructor(references = []) {
-        this.references = references;
-    }
-
-    find(username) {
-
-
-
-    }
-
-    insertSearchResult(element) {
-        
-    }
-
-}
-
-const timestamp = TimeStamp.now();
-const reference = new Reference(timestamp, );
-
-module.exports = {
-    Snapshot 
-};
-
-
- - - - -
- - - -
- -
- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) -
- - - - - diff --git a/docs/tools.js.html b/docs/tools.js.html index d2802e0..36aea07 100644 --- a/docs/tools.js.html +++ b/docs/tools.js.html @@ -241,13 +241,13 @@

Source: tools.js


- Documentation generated by JSDoc 3.6.6 on Wed Jun 02 2021 21:59:54 GMT+0200 (Mitteleuropäische Sommerzeit) + Documentation generated by JSDoc 3.6.6 on Fri Jun 04 2021 12:52:39 GMT+0200 (Mitteleuropäische Sommerzeit)
diff --git a/error.js b/error.js index fc90e34..b9aacd4 100644 --- a/error.js +++ b/error.js @@ -1,7 +1,7 @@ class IBError extends Error { /** - * + * @augments Error * @param {Number} code * @param {String} message * @param {Error} [error] @@ -21,7 +21,7 @@ class IBError extends Error { class IBLoginError extends IBError { /** - * + * @augments IBError * @param {Number} code * @param {String} message * @param {String} username @@ -41,7 +41,7 @@ class IBLoginError extends IBError { class IBGotoError extends IBError { /** - * + * @augments IBError * @param {Number} code * @param {String} message * @param {any} goal diff --git a/index.js b/index.js index d5840ae..7ce1148 100644 --- a/index.js +++ b/index.js @@ -2,26 +2,37 @@ const actions = require("./scripts/actions.js"); const data = require("./scripts/data.js"); const misc = require("./scripts/misc.js"); const navigation = require("./scripts/navigation.js"); +const popup = require("./scripts/popup.js"); +const observer = require("./scripts/observer.js"); const puppeteer = require("puppeteer"); -const { IBError } = require("./error.js"); +const fs = require("fs"); + +const { IBError, IBLoginError } = require("./error.js"); const { errorMessage } = require("./message.js"); const { SearchResult, User, UserDetails } = require("./class.js"); +const { Cache } = require("./cache.js"); class Queue { constructor() { this.list = []; + this.shouldRun = true; // start running Queue this.run(); } async run() { + if(!this.shouldRun) return; + if(this.list.length > 0) { await this.list[0](); this.list.splice(0, 1); } - setTimeout(() => this.run(), 1000); + this.timeout = setTimeout(() => this.run(), 1000); + } + stop() { + this.shouldRun = false; } push(func) { return new Promise((resolve, reject) => { @@ -49,6 +60,7 @@ class InstagramBot { * @property {Queue} queue * @property {Boolean} authenticated * @property {String} username + * @property {Cache} cache */ constructor(browser, page, authenticated = false) { this.browser = browser; @@ -56,33 +68,121 @@ class InstagramBot { this.queue = new Queue(); this.authenticated = authenticated; this.username = null; + this.cache = Cache.empty(); } /** * * @param {Boolean} [ headless = false ] - * @param {Object} [ cookies = [] ] + * @param {Object} [ session = {} ] * @returns {Promise} */ - static async launch(headless = false, cookies = []) { + static async launch(headless = false, session = {}) { const browser = await misc.launchBrowser({ headless }); + const cookies = session.cookies ? session.cookies : []; const page = await misc.newPage(browser, "en", cookies); // check if page is already authenticated const isAuthenticated = await data.isAuthenticated(page); - return new InstagramBot(browser, page, isAuthenticated); + // create bot + const bot = await new InstagramBot(browser, page, isAuthenticated); + + // add observer to bot + await bot.addObserver(async () => { + // will execute everytime the page changes + await popup.dismissCookiePopup(page); + await popup.dismissNotificationPopup(page); + }); + + return bot; + } + + /** + * + * @param {String} filePath + * @returns {Promise} session, which probably stores your credentials + */ + static async loadSession(filePath) { + try { + + const raw = await fs.promises.readFile(filePath); + const session = JSON.parse(raw); + return session; + + } catch(e) { + throw new IBError(errorMessage.failedToLoadSession.code, errorMessage.failedToLoadSession.message, e); + } + } + + /** + * closes the browser + * @returns {Promise} + */ + async close() { + await this.page.close(); + await this.browser.close(); + await this.browser.disconnect(); + await this.queue.stop(); + } + + /** + * stops the bot + * @returns {Promise} + */ + async stop() { + await this.page.close(); + await this.browser.close(); + await this.browser.disconnect(); + await this.queue.stop(); + } + + /** + * + * @param {Function} func + * @returns {Promise} + */ + async addObserver(func) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); + + await observer.addObserver(this.page, func); } /** * @returns {Promise} */ async getCookies() { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); + const cookies = await this.queue.push(() => data.getCookies(this.page)); return cookies; } + /** + * + * @returns {Promise} session, which normally stores credentials + */ + async getSession() { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); + + const cookies = await this.getCookies(); + return { cookies }; + } + + /** + * + * @param {String} filePath + * @returns {Promise} + */ + async saveSession(filePath) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); + + const session = await this.getSession(); + const text = JSON.stringify(session); + await fs.promises.writeFile(filePath, text); + } + /** * * @param {String} username @@ -90,6 +190,9 @@ class InstagramBot { * @returns {Promise} */ async login(username, password) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); + if(this.authenticated) throw new IBLoginError(errorMessage.botAlreadyAuthenticated.code, errorMessage.botAlreadyAuthenticated.message); + await this.queue.push(() => actions.login(this.page, username, password)); this.username = username; @@ -101,6 +204,7 @@ class InstagramBot { * @returns {Promise} */ async logout() { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) return; await this.queue.push(() => actions.logout(this.page, this.username)); @@ -116,10 +220,16 @@ class InstagramBot { * @returns {Promise} */ async getFollowing(identifier, minLength = 50) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const following = await this.queue.push(() => data.getFollowing(this.page, identifier, minLength)); + // store data in cache, if types are compatible + if(identifier instanceof User) { + this.cache = this.cache.addFollowingList(identifier, following); + } + return following; } @@ -130,10 +240,16 @@ class InstagramBot { * @returns {Promise} */ async getFollower(identifier, minLength = 50) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const follower = await this.queue.push(() => data.getFollower(this.page, identifier, minLength)); + // store data in cache, if types are compatible + if(identifier instanceof User) { + this.cache = this.cache.addFollowerList(identifier, follower); + } + return follower; } @@ -143,6 +259,7 @@ class InstagramBot { * @returns {Promise} */ async follow(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => actions.follow(this.page, identifier)); @@ -154,6 +271,7 @@ class InstagramBot { * @returns {Promise} */ async unfollow(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => actions.unfollow(this.page, identifier)); @@ -165,6 +283,7 @@ class InstagramBot { * @returns {Promise} whether you are following the specified user or not */ async isFollowing(userIdentifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const result = await this.queue.push(() => data.isFollowing(this.page, userIdentifier)); @@ -177,6 +296,7 @@ class InstagramBot { * @returns {Promise} */ async search(searchTerm) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const searchResults = await this.queue.push(() => navigation.search(this.page, searchTerm)); @@ -189,6 +309,7 @@ class InstagramBot { * @returns {Promise} */ async goto(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => navigation.goto(this.page, identifier)); @@ -200,6 +321,7 @@ class InstagramBot { * @returns {Promise} */ async getUserDetails(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const userDetails = await this.queue.push(() => data.getUserDetails(this.page, identifier)); @@ -213,6 +335,7 @@ class InstagramBot { * @returns {Promise} */ async getPosts(identifier, minLength = 50) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const posts = await this.queue.push(() => data.getPosts(this.page, identifier, minLength)); @@ -225,6 +348,7 @@ class InstagramBot { * @returns {Promise} */ async getPostDetails(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const postDetails = await this.queue.push(() => data.getPostDetails(this.page, identifier)); @@ -237,6 +361,7 @@ class InstagramBot { * @returns {Promise} */ async likePost(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => actions.likePost(this.page, identifier)); @@ -248,6 +373,7 @@ class InstagramBot { * @returns {Promise} */ async unlikePost(identifier) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => actions.unlikePost(this.page, identifier)); @@ -260,6 +386,7 @@ class InstagramBot { * @returns {Promise} */ async commentPost(postIdentifier, comment) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); await this.queue.push(() => actions.commentPost(this.page, postIdentifier, comment)); @@ -272,6 +399,7 @@ class InstagramBot { * @returns {Promise} */ async getPostComments(postIdentifier, minComments = 5) { + if(!this.browser.isConnected()) throw new IBError(errorMessage.browserNotRunning.code, errorMessage.browserNotRunning.message); if(!this.authenticated) throw new IBError(errorMessage.notAuthenticated.code, errorMessage.notAuthenticated.message); const comments = await this.queue.push(() => data.getPostComments(this.page, postIdentifier, minComments)); diff --git a/message.js b/message.js index d1988eb..3dfc769 100644 --- a/message.js +++ b/message.js @@ -9,7 +9,10 @@ const errorCode = { 8: "The page isn't available!", 9: "Can't comment on post!", 10: "Account couldn't be found!", - 11: "Account is private. You need to follow the account first, to request data!" + 11: "Account is private. You need to follow the account first, to request data!", + 12: "Browser is not running!", + 13: "Failed to load session!", + 14: "The bot is already authenticated!" }; const errorTemplate = (code) => { return { @@ -28,7 +31,10 @@ const errorMessage = { "pageNotAvailable": errorTemplate(8), "cantCommentPost": errorTemplate(9), "accountNotFound": errorTemplate(10), - "accountPrivate": errorTemplate(11) + "accountPrivate": errorTemplate(11), + "browserNotRunning": errorTemplate(12), + "failedToLoadSession": errorTemplate(13), + "botAlreadyAuthenticated": errorTemplate(14) }; module.exports = { errorMessage }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 2877db0..2602f11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@paul-hanneforth/instagram-bot", - "version": "3.0.6", + "version": "3.0.7", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -13,6 +13,46 @@ "@babel/highlight": "^7.10.4" } }, + "@babel/generator": { + "version": "7.14.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", + "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", + "dev": true, + "requires": { + "@babel/types": "^7.14.2", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", + "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.12.13", + "@babel/template": "^7.12.13", + "@babel/types": "^7.14.2" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", + "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", + "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", + "dev": true, + "requires": { + "@babel/types": "^7.12.13" + } + }, "@babel/helper-validator-identifier": { "version": "7.14.0", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", @@ -43,6 +83,77 @@ } } }, + "@babel/parser": { + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.4.tgz", + "integrity": "sha512-ArliyUsWDUqEGfWcmzpGUzNfLxTdTp6WU4IuP6QFSp9gGfWS6boxFCkJSJ/L4+RG8z/FnIU3WxCk6hPL9SSWeA==", + "dev": true + }, + "@babel/template": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", + "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/parser": "^7.12.13", + "@babel/types": "^7.12.13" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + } + } + }, + "@babel/traverse": { + "version": "7.14.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", + "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@babel/generator": "^7.14.2", + "@babel/helper-function-name": "^7.14.2", + "@babel/helper-split-export-declaration": "^7.12.13", + "@babel/parser": "^7.14.2", + "@babel/types": "^7.14.2", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", + "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "dev": true, + "requires": { + "@babel/highlight": "^7.12.13" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.14.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.4.tgz", + "integrity": "sha512-lCj4aIs0xUefJFQnwwQv2Bxg7Omd6bgquZ6LGC+gGMh6/s5qDVfjuCMlDmYQ15SLsWHd9n+X3E75lKIhl5Lkiw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.0", + "to-fast-properties": "^2.0.0" + } + }, "@eslint/eslintrc": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", @@ -159,6 +270,28 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -582,6 +715,12 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", @@ -635,6 +774,15 @@ } } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", @@ -691,6 +839,15 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "is-core-module": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", + "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -734,6 +891,12 @@ "esprima": "^4.0.0" } }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -894,6 +1057,12 @@ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, "pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -979,6 +1148,16 @@ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, "resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -1059,6 +1238,12 @@ } } }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -1177,6 +1362,12 @@ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", diff --git a/package.json b/package.json index 7ccab1c..7720b6b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paul-hanneforth/instagram-bot", - "version": "3.0.6", + "version": "3.0.7", "description": "", "main": "index.js", "scripts": { @@ -17,6 +17,7 @@ "author": "Paul Hanneforth", "license": "ISC", "devDependencies": { + "babel-eslint": "^10.1.0", "eslint": "^7.25.0" }, "dependencies": { diff --git a/scripts/actions.js b/scripts/actions.js index 36fa305..15019f1 100644 --- a/scripts/actions.js +++ b/scripts/actions.js @@ -1,6 +1,6 @@ const puppeteer = require("puppeteer"); const tools = require("./../tools.js"); -const popup = require("./../popup.js"); +const popup = require("./popup.js"); const { errorMessage } = require("./../message.js"); const { IBError, IBLoginError } = require("./../error.js"); const { SearchResult, User, Post } = require("./../class.js"); @@ -208,6 +208,5 @@ module.exports = { unfollow, commentPost, likePost, - unlikePost, - + unlikePost }; \ No newline at end of file diff --git a/scripts/observer.js b/scripts/observer.js new file mode 100644 index 0000000..82fdf05 --- /dev/null +++ b/scripts/observer.js @@ -0,0 +1,40 @@ +const puppeteer = require("puppeteer"); + +/** + * + * @param {puppeteer.Page} page + * @param {Function} func + * @returns {Promise} + */ +const addObserver = async (page, func) => { + + const functionName = "puppeteerLogMutation" + [...Array(10)].map(i => (~~(Math.random() * 36)).toString(36)).join(""); + + await page.exposeFunction(functionName, () => { + func(); + }); + await page.evaluate((functionName) => { + const target = document.querySelector("body"); + const observer = new MutationObserver( mutations => { + for (const mutation of mutations) { + eval(`${functionName}()`); + if (mutation.type === "childList") { + // eval(`${functionName}()`); + } + } + }); + observer.observe(target, { attributes: true, childList: true, subtree: true }); + }, functionName); + await page.evaluateOnNewDocument((functionName) => { + addEventListener("hashchange", e => eval(`${functionName}()`)); + }, functionName); + + page.on("framenavigated", (frame) => { + func(); + }); + +}; + +module.exports = { + addObserver +}; \ No newline at end of file diff --git a/popup.js b/scripts/popup.js similarity index 90% rename from popup.js rename to scripts/popup.js index 8efaea5..d38c1ba 100644 --- a/popup.js +++ b/scripts/popup.js @@ -1,5 +1,5 @@ const puppeteer = require("puppeteer"); -const tools = require("./tools.js"); +const tools = require("../tools.js"); /** * diff --git a/tests/actions.js b/tests/actions.js new file mode 100644 index 0000000..88cf8bb --- /dev/null +++ b/tests/actions.js @@ -0,0 +1,99 @@ +const InstagramBot = require("./../index.js"); +const log = require("@paul-hanneforth/log"); + +const runTest = async (username, password) => { + + // initialize bot + log("Trying to launch bot ..."); + var bot; + try { + bot = await InstagramBot.launch(false); + } catch(e) { + log("Failed to launch bot!", log.type.error); + throw(e); + } + log("Successfully launched bot!", log.type.success); + + // login + log("Trying to login ..."); + try { + await bot.login(username, password); + } catch(e) { + log("Failed to login!", log.type.error); + throw(e); + } + log("Successfully authenticated bot!", log.type.success); + + // follow + log("Trying to follow user ..."); + try { + await bot.follow("therock"); + } catch(e) { + log("Failed to follow user!", log.type.error); + throw(e); + } + log("Successfully followed user!", log.type.success); + + // unfollow + log("Trying to unfollow user ..."); + try { + await bot.unfollow("therock"); + } catch(e) { + log("Failed to unfollow user!", log.type.error); + throw(e); + } + log("Successfully unfollowed user!", log.type.success); + + // like post + log("Trying to like post ..."); + try { + await bot.likePost("https://www.instagram.com/p/CPnmAQcF341/"); + } catch(e) { + log("Failed to like post!", log.type.error); + throw(e); + } + log("Successfully liked post!", log.type.success); + + // unlike post + log("Trying to unlike post ..."); + try { + await bot.unlikePost("https://www.instagram.com/p/CPnmAQcF341/"); + } catch(e) { + log("Failed to unlike post!", log.type.error); + throw(e); + } + log("Successfully unliked post!", log.type.success); + + // comment post + log("Trying to comment on post ..."); + try { + await bot.commentPost("https://www.instagram.com/p/CPnmAQcF341/", "This is a comment!"); + } catch(e) { + log("Failed to comment on post!", log.type.error); + throw(e); + } + log("Successfully commented post!", log.type.success); + + // logout + log("Trying to logout ..."); + try { + await bot.logout(); + } catch(e) { + log("Failed to logout!", log.type.error); + throw(e); + } + log("Successfully logged out!", log.type.success); + + // close + log("Trying to close browser ..."); + try { + await bot.close(); + } catch(e) { + log("Failed to close browser!", log.type.error); + throw(e); + } + log("Successfully closed browser!", log.type.success); + +}; + +module.exports = { runTest }; \ No newline at end of file diff --git a/tests/data.js b/tests/data.js new file mode 100644 index 0000000..4513063 --- /dev/null +++ b/tests/data.js @@ -0,0 +1,129 @@ +const InstagramBot = require("./../index.js"); +const log = require("@paul-hanneforth/log"); + +const runTest = async (username, password) => { + + // initialize bot + log("Trying to launch bot ..."); + var bot; + try { + bot = await InstagramBot.launch(false); + } catch(e) { + log("Failed to launch bot!", log.type.error); + throw(e); + } + log("Successfully launched bot!", log.type.success); + + // login + log("Trying to login ..."); + try { + await bot.login(username, password); + } catch(e) { + log("Failed to login!", log.type.error); + throw(e); + } + log("Successfully authenticated bot!", log.type.success); + + // try to get cookies + log("Trying to get cookies ..."); + try { + await bot.getCookies(); + } catch(e) { + log("Failed to get cookies!", log.type.error); + throw(e); + } + log("Successfully got cookies!", log.type.success); + + // get following + log("Trying to get following ..."); + try { + await bot.getFollowing("therock"); + } catch(e) { + log("Failed to get following!", log.type.error); + throw(e); + } + log("Successfully got following!", log.type.success); + + // get follower + log("Trying to get follower ..."); + try { + await bot.getFollower("therock"); + } catch(e) { + log("Failed to get follower!", log.type.error); + throw(e); + } + log("Successfully got follower!", log.type.success); + + // isFollowing + log("Check if you're following a user ..."); + try { + await bot.isFollowing("therock"); + } catch(e) { + log("Failed to check if you're following a user!", log.type.error); + throw(e); + } + log("Successfully checked if you're following a user!", log.type.success); + + // get user details + log("Trying to get user details ..."); + try { + await bot.getUserDetails("therock"); + } catch(e) { + log("Failed to get user details!", log.type.error); + throw(e); + } + log("Successfully got user details!", log.type.success); + + // get user posts + log("Trying to get user posts ..."); + try { + await bot.getPosts("therock"); + } catch(e) { + log("Failed to get user posts!", log.type.error); + throw(e); + } + log("Successfully got user posts!", log.type.success); + + // get post details + log("Trying to get post details ..."); + try { + await bot.getPostDetails("https://www.instagram.com/p/CPnmAQcF341/"); + } catch(e) { + log("Failed to get post details!", log.type.error); + throw(e); + } + log("Successfully got post details!", log.type.success); + + // get post comments + log("Trying to get post comments ..."); + try { + await bot.getPostComments("https://www.instagram.com/p/CPnmAQcF341/"); + } catch(e) { + log("Failed to get post comments!", log.type.error); + throw(e); + } + log("Successfully got post comments!", log.type.success); + + // logout + log("Trying to logout ..."); + try { + await bot.logout(); + } catch(e) { + log("Failed to logout!", log.type.error); + throw(e); + } + log("Successfully logged out!", log.type.success); + + // close + log("Trying to close browser ..."); + try { + await bot.close(); + } catch(e) { + log("Failed to close browser!", log.type.error); + throw(e); + } + log("Successfully closed browser!", log.type.success); + +}; + +module.exports = { runTest }; \ No newline at end of file diff --git a/tests/misc.js b/tests/misc.js new file mode 100644 index 0000000..e425413 --- /dev/null +++ b/tests/misc.js @@ -0,0 +1,29 @@ +const InstagramBot = require("./../index.js"); +const log = require("@paul-hanneforth/log"); + +const runTest = async (username, password) => { + + // initialize bot + log("Trying to launch bot ..."); + var bot; + try { + bot = await InstagramBot.launch(false); + } catch(e) { + log("Failed to launch bot!", log.type.error); + throw(e); + } + log("Successfully launched bot!", log.type.success); + + // close + log("Trying to close browser ..."); + try { + await bot.close(); + } catch(e) { + log("Failed to close browser!", log.type.error); + throw(e); + } + log("Successfully closed browser!", log.type.success); + +}; + +module.exports = { runTest }; \ No newline at end of file diff --git a/tests/navigation.js b/tests/navigation.js new file mode 100644 index 0000000..05c4b67 --- /dev/null +++ b/tests/navigation.js @@ -0,0 +1,69 @@ +const InstagramBot = require("./../index.js"); +const log = require("@paul-hanneforth/log"); + +const runTest = async (username, password) => { + + // initialize bot + log("Trying to launch bot ..."); + var bot; + try { + bot = await InstagramBot.launch(false); + } catch(e) { + log("Failed to launch bot!", log.type.error); + throw(e); + } + log("Successfully launched bot!", log.type.success); + + // login + log("Trying to login ..."); + try { + await bot.login(username, password); + } catch(e) { + log("Failed to login!", log.type.error); + throw(e); + } + log("Successfully authenticated bot!", log.type.success); + + // goto + log("Trying to go to user ..."); + try { + await bot.goto("therock"); + } catch(e) { + log("Failed to go to user!", log.type.error); + throw(e); + } + log("Successfully went to user!", log.type.success); + + // search + log("Trying to search ..."); + try { + await bot.search("therock"); + } catch(e) { + log("Failed to search!", log.type.error); + throw(e); + } + log("Sucessfully searched!", log.type.success); + + // logout + log("Trying to logout ..."); + try { + await bot.logout(); + } catch(e) { + log("Failed to logout!", log.type.error); + throw(e); + } + log("Successfully logged out!", log.type.success); + + // close + log("Trying to close browser ..."); + try { + await bot.close(); + } catch(e) { + log("Failed to close browser!", log.type.error); + throw(e); + } + log("Successfully closed browser!", log.type.success); + +}; + +module.exports = { runTest }; \ No newline at end of file