@@ -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:
+
+
+
+
+
+
+ Name
+
+
+ Type
+
+
+
+
+
+ Description
+
+
+
+
+
+
+
+
+ filePath
+
+
+
+
+
+String
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Source:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Returns:
+
+
+
+
+
+
+ Type
+
+
+
+Promise
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
(async) search(searchTerm) → {Promise.<Array.<SearchResult >>}
@@ -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:
- Classes Global
+ Classes Global
- 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:
- Classes Global
+ Classes Global
- 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
- Classes Global
+ Classes Global
- 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:
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- timestamp
-
-
-
-
-
-TimeStamp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- value
-
-
-
-
-
-any
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- relation
-
-
-
-
-
-Relation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- referencedBy
-
-
-
-
-
-any
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Classes Global
-
-
-
-
-
- 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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
new Relation(type)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Parameters:
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- type
-
-
-
-
-
-String
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Properties:
-
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- type
-
-
-
-
-
-String
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Methods
-
-
-
-
-
-
-
- (static) followedBy() → {Relation }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Returns:
-
-
-
-
-
-
- Type
-
-
-
-Relation
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Classes Global
-
-
-
-
-
- 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:
- Classes Global
+ Classes Global
- 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:
-
-
-
-
-
-
- Name
-
-
- Type
-
-
- Attributes
-
-
-
- Default
-
-
- Description
-
-
-
-
-
-
-
-
- references
-
-
-
-
-
-Array.<Reference >
-
-
-
-
-
-
-
-
- <optional>
-
-
-
-
-
-
-
-
-
-
-
- []
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Classes Global
-
-
-
-
-
- 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:
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- date
-
-
-
-
-
-Date
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Properties:
-
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- date
-
-
-
-
-
-Date
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Methods
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-Returns:
-
-
-
-
-
-
- Type
-
-
-
-TimeStamp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Classes Global
-
-
-
-
-
- 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:
- Classes Global
+ Classes Global
- 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
- Classes Global
+ Classes Global
- 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:
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- timestamp
-
-
-
-
-
-TimeStamp
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- user
-
-
-
-
-
-Object
-
-
-
-
-
-
-
-
-
-
- Properties
-
-
-
-
-
-
- Name
-
-
- Type
-
-
- Attributes
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- 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:
-
-
-
-
-
-
-
- Name
-
-
- Type
-
-
- Attributes
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- 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:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Classes Global
-
-
-
-
-
- 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
- Classes Global
+ Classes Global
- 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
- Classes Global
+ Classes Global
- 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
- Classes Global
+ Classes Global
- 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
- Classes Global
+ Classes Global
- 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:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Parameters:
-
-
-
-
-
-
- Name
-
-
- Type
-
-
-
-
-
- Description
-
-
-
-
-
-
-
-
- page
-
-
-
-
-
-puppeteer.Page
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Source:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -2221,13 +2088,13 @@ Returns:
- Classes Global
+ Classes Global
- 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 @@
- Classes Global
+ Classes Global
- 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
- Classes Global
+ Classes Global
- 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,
-};
-
-
-
-
-
-
-
-
-
- Classes Global
-
-
-
-
-
- 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
-};
-
-
-
-
-
-
-
-
-
- Classes Global
-
-
-
-
-
- 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
- Classes Global
+ Classes Global
- 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