botinsta 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +16 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +117 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/botinsta.gemspec +33 -0
- data/example/example.rb +12 -0
- data/lib/botinsta.rb +56 -0
- data/lib/botinsta/actions.rb +108 -0
- data/lib/botinsta/class_methods.rb +21 -0
- data/lib/botinsta/data/media_data.rb +42 -0
- data/lib/botinsta/data/page_data.rb +36 -0
- data/lib/botinsta/data/user_data.rb +31 -0
- data/lib/botinsta/helpers.rb +156 -0
- data/lib/botinsta/login.rb +48 -0
- data/lib/botinsta/modes.rb +66 -0
- data/lib/botinsta/pages.rb +79 -0
- data/lib/botinsta/requests.rb +43 -0
- data/lib/botinsta/version.rb +3 -0
- data/spec/botinsta_spec.rb +5 -0
- data/spec/spec_helper.rb +103 -0
- metadata +241 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6aaa7af1d1799524017bfd68e099f63b940b8f99d7c5a39d1bf105c351419cdb
|
4
|
+
data.tar.gz: 8d38665b27b5a6aa1af0edfe34fc0f1129afe67682290ce8225e852c375f48ad
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5876356f6bd056b4ed0e6ba5109ecd1cc70ac166186dc3be7bfaee94f9f50b2e214d256d2aafd9cdccd584ed9d1c47999b3fb0ca3a24abccbeb07476a7d034d0
|
7
|
+
data.tar.gz: 28fbf4d409672370fce4d796fcac09e0d45c0cfc024ff7318d13299599647c870c9a8f7c79987b824c2bd384fa833f87800373d8aee30218fdb9a7ccbb637596
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 andreyuhai
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
# Botinsta [![Build Status](https://travis-ci.com/andreyuhai/botinsta.svg?branch=master)](https://travis-ci.com/andreyuhai/botinsta) [![Gem Version](https://badge.fury.io/rb/botinsta.svg)](https://badge.fury.io/rb/botinsta)
|
2
|
+
|
3
|
+
## Description
|
4
|
+
|
5
|
+
This is a **tag-based Instagram bot** I've created under the influence of other cool Instagram bots [instabot.py](https://github.com/instabot-py) and [instabot.rb](https://github.com/eVanilla/instabot.rb/) to improve my Ruby skills.
|
6
|
+
|
7
|
+
#### What do you mean tag-based?!
|
8
|
+
|
9
|
+
Well, tag-based means that the bot works based on solely the tags you specified. You specify the tags you want the bot to like medias and follow users from and it loops through all tags liking a number of medias specified by you and also following the owners of medias it liked. See [how it works](#usage--how-it-works)
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
gem 'botinsta'
|
17
|
+
```
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install botinsta
|
26
|
+
|
27
|
+
## Features
|
28
|
+
|
29
|
+
* Follow
|
30
|
+
* Like
|
31
|
+
* Unfollow people followed after a day. Creates a local database for that.
|
32
|
+
* Avoid liking blacklisted tags
|
33
|
+
|
34
|
+
## Features to come
|
35
|
+
|
36
|
+
* Comments
|
37
|
+
* Unlike medias
|
38
|
+
* Avoid following blacklisted users
|
39
|
+
|
40
|
+
I am still not sure what else to add but if you have any idea don't hesitate to hit me up or send me a pull request!
|
41
|
+
|
42
|
+
## Usage & How it works
|
43
|
+
|
44
|
+
You can use the bot simply like below. It already has default parameters so you could just input your `username` and `password` and let it use the defaults for the rest.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
require 'botinsta'
|
48
|
+
|
49
|
+
bot = Botinsta.new ({ username: 'YOUR_USERNAME',
|
50
|
+
password: 'YOUR_PASSWORD',
|
51
|
+
tags: ['photography','vsco','fotografia'],
|
52
|
+
tag_blacklist: ['nsfw','sexy','hot'],
|
53
|
+
likes_per_tag: 20,
|
54
|
+
unfollows_per_run: 200,
|
55
|
+
follows_per_tag: 10
|
56
|
+
})
|
57
|
+
|
58
|
+
bot.start
|
59
|
+
```
|
60
|
+
|
61
|
+
The bot loops through each tag liking as many images as `@likes_per_tag` and following as many users as `@follows_per_tag`.
|
62
|
+
|
63
|
+
Liking medias and following users from the tag's first page is easy because all you have to do is:
|
64
|
+
|
65
|
+
https://instagram.com/explore/tags/YOURTAG/?__a=1
|
66
|
+
|
67
|
+
to navigate to above link and get the JSON string then parse it accordingly
|
68
|
+
to extract necessary information (i.e. media ID, owner ID).
|
69
|
+
|
70
|
+
It gets complicated when you liked all the medias on the first page and need to get the next page—with next page I am referring to when you scroll down the page to load more content—to continue extracting data, liking medias & following users. So we can do the same with a `GET` request instead since we are using `Mechanize` for automation.
|
71
|
+
|
72
|
+
To get the next page you need two things:
|
73
|
+
|
74
|
+
* query\_id (query\_hash)
|
75
|
+
* end\_cursor
|
76
|
+
|
77
|
+
which will be used in GET requests to get the JSON string for the next page.
|
78
|
+
|
79
|
+
An example of the link:
|
80
|
+
|
81
|
+
https://www.instagram.com/graphql/query/?query_hash=1780c1b186e2c37de9f7da95ce41bb67&variables={"tag_name":"photography","first":4,"after":"AQAiksCP1Uzk5-bXZ3qnwUsA89YRn1LBia9_yDFeWm5S1KTfzyU8eH8EFjq8LPuFOemdkRjzWb8_5vmyQ8Gnj-sTCfVGwRHs8WoKhPtBncmLbg"}
|
82
|
+
|
83
|
+
As you can see we need to provide the query\_id (query\_hash) and end\_cursor (the same as `after`) in the link.
|
84
|
+
|
85
|
+
We get the above parameters with the help of methods in the module [Pages]() below:
|
86
|
+
|
87
|
+
## Documentation
|
88
|
+
|
89
|
+
* [Documentation](https://www.rubydoc.info/github/andreyuhai/botinsta/master)
|
90
|
+
|
91
|
+
I will eventually complete all method descriptions and usages. You can help me if you would like to!
|
92
|
+
|
93
|
+
## Development
|
94
|
+
|
95
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
96
|
+
|
97
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
98
|
+
|
99
|
+
## Gems used
|
100
|
+
|
101
|
+
* Colorize
|
102
|
+
* Hashie
|
103
|
+
* Json
|
104
|
+
* Mechanize
|
105
|
+
* Nokogiri
|
106
|
+
* Sequel
|
107
|
+
* Sqlite3
|
108
|
+
|
109
|
+
## Contributing
|
110
|
+
|
111
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/andreyuhai/botinsta.
|
112
|
+
|
113
|
+
If you like the bot and want to see the new features very soon, please do not forget to star the repo to let me now you are interested. Boost me! :rocket: :blush:
|
114
|
+
|
115
|
+
## License
|
116
|
+
|
117
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'botinsta'
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/botinsta.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'botinsta/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'botinsta'
|
7
|
+
spec.version = Botinsta::VERSION
|
8
|
+
spec.authors = ['andreyuhai']
|
9
|
+
spec.email = ['yuhai.ndre@gmail.com']
|
10
|
+
|
11
|
+
spec.summary = 'Ruby Instagram bot'
|
12
|
+
spec.description = 'A tag-based Instagram bot which works without using any Instagram API.'
|
13
|
+
spec.homepage = 'https://github.com/andreyuhai/botinsta'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split("\n")
|
17
|
+
spec.bindir = 'exe'
|
18
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_runtime_dependency 'colorize', ['~> 0.8.1']
|
22
|
+
spec.add_runtime_dependency 'hashie', ['~> 3.6']
|
23
|
+
spec.add_runtime_dependency 'mechanize', ['~> 2.7', '>= 2.7.6']
|
24
|
+
spec.add_runtime_dependency 'nokogiri', ['~> 1.8', '>= 1.8.4']
|
25
|
+
spec.add_runtime_dependency 'pry', ['~> 0.11.3']
|
26
|
+
spec.add_runtime_dependency 'rb-readline', ['~> 0.5.5']
|
27
|
+
spec.add_runtime_dependency 'sequel', ['~> 5.12']
|
28
|
+
spec.add_runtime_dependency 'sqlite3', ['~> 1.3', '>= 1.3.13']
|
29
|
+
|
30
|
+
spec.add_development_dependency 'bundler', '~> 1.16'
|
31
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
32
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
33
|
+
end
|
data/example/example.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'botinsta'
|
2
|
+
|
3
|
+
bot = Botinsta.new ({ username: 'YOUR_USERNAME',
|
4
|
+
password: 'YOUR_PASSWORD',
|
5
|
+
tags: ['photography','vsco','b&w'],
|
6
|
+
likes_per_tag: 60,
|
7
|
+
tag_blacklist: ['nsfw','sexy','hot'],
|
8
|
+
unfollows_per_run: 200,
|
9
|
+
follows_per_tag: 10
|
10
|
+
})
|
11
|
+
|
12
|
+
bot.start
|
data/lib/botinsta.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'colorize'
|
2
|
+
require 'hashie'
|
3
|
+
require 'json'
|
4
|
+
require 'mechanize'
|
5
|
+
require 'sequel'
|
6
|
+
require 'sqlite3'
|
7
|
+
require 'nokogiri'
|
8
|
+
|
9
|
+
require_relative 'botinsta/class_methods'
|
10
|
+
|
11
|
+
# This is our main class from which we will be
|
12
|
+
# instantiating our bot.
|
13
|
+
class Botinsta
|
14
|
+
|
15
|
+
include ClassMethods
|
16
|
+
|
17
|
+
DEFAULT_PARAMETERS = { tags: %w[photography fotografia vsco],
|
18
|
+
tag_blacklist: %w[nsfw hot sexy],
|
19
|
+
user_blacklist: [],
|
20
|
+
likes_per_tag: 10,
|
21
|
+
unfollows_per_run: 200,
|
22
|
+
follows_per_tag: 50
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
def initialize(**params)
|
26
|
+
|
27
|
+
params = DEFAULT_PARAMETERS.merge(params)
|
28
|
+
|
29
|
+
@username = params[:username]
|
30
|
+
@password = params[:password]
|
31
|
+
@tags = params[:tags]
|
32
|
+
@tag_blacklist = params[:tag_blacklist]
|
33
|
+
@user_blacklist = params[:user_blacklist]
|
34
|
+
@likes_per_tag = params[:likes_per_tag]
|
35
|
+
@follows_per_tag = params[:follows_per_tag]
|
36
|
+
@unfollows_per_run = params[:unfollows_per_run]
|
37
|
+
|
38
|
+
@total_likes = 0
|
39
|
+
@total_follows = 0
|
40
|
+
@total_unfollows = 0
|
41
|
+
|
42
|
+
@agent = Mechanize.new
|
43
|
+
|
44
|
+
handle_database_creation
|
45
|
+
return if @table_follows.empty?
|
46
|
+
|
47
|
+
@first_db_entry = @table_follows.first
|
48
|
+
@last_follow_time = @table_follows.first[:follow_time]
|
49
|
+
end
|
50
|
+
|
51
|
+
def start
|
52
|
+
login
|
53
|
+
tag_based_mode
|
54
|
+
logout
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# Various actions you can do on Instagram
|
2
|
+
# and other related methods.
|
3
|
+
module Actions
|
4
|
+
|
5
|
+
# Likes media given by media id.
|
6
|
+
#
|
7
|
+
# @param media_id [String]
|
8
|
+
# @return [true, false] returns true on success, false otherwise.
|
9
|
+
def like_media(media_id)
|
10
|
+
url_like = "https://www.instagram.com/web/likes/#{media_id}/like/"
|
11
|
+
print_try_message(action: :like, data: media_id)
|
12
|
+
begin
|
13
|
+
set_request_params
|
14
|
+
response = @agent.post url_like, @params, @request_headers
|
15
|
+
rescue Mechanize::ResponseCodeError
|
16
|
+
return false
|
17
|
+
end
|
18
|
+
response_data = JSON.parse(response.body)
|
19
|
+
response.code == '200' && response_data['status'] == 'ok' ? true : false
|
20
|
+
end
|
21
|
+
|
22
|
+
# Unlikes media given by media id.
|
23
|
+
#
|
24
|
+
# @param media_id [String]
|
25
|
+
# @return (see #like_media)
|
26
|
+
def unlike_media(media_id)
|
27
|
+
url_unlike = "https://www.instagram.com/web/likes/#{media_id}/unlike/"
|
28
|
+
print_try_message(action: :unlike, data: media_id)
|
29
|
+
begin
|
30
|
+
set_request_params
|
31
|
+
response = @agent.post url_unlike, @params, @request_headers
|
32
|
+
rescue Mechanize::ResponseCodeError
|
33
|
+
return false
|
34
|
+
end
|
35
|
+
response_data = JSON.parse(response.body)
|
36
|
+
response.code == '200' && response_data['status'] == 'ok' ? true : false
|
37
|
+
end
|
38
|
+
|
39
|
+
# Follows user given by user_id.
|
40
|
+
#
|
41
|
+
# @param user_id [String]
|
42
|
+
# @return (see #like_media)
|
43
|
+
def follow_user(user_id)
|
44
|
+
url_follow = "https://www.instagram.com/web/friendships/#{user_id}/follow/"
|
45
|
+
print_try_message(action: :follow, data: user_id)
|
46
|
+
begin
|
47
|
+
set_request_params
|
48
|
+
response = @agent.post url_follow, @params, @request_headers
|
49
|
+
rescue Mechanize::ResponseCodeError
|
50
|
+
return false
|
51
|
+
end
|
52
|
+
response_data = JSON.parse(response.body)
|
53
|
+
response.code == '200' && response_data['result'] == 'following' ? true : false
|
54
|
+
end
|
55
|
+
|
56
|
+
# Unfollows user given by user_id.
|
57
|
+
#
|
58
|
+
# @param user_id [String]
|
59
|
+
# @return (see #like_media)
|
60
|
+
def unfollow_user(user_id)
|
61
|
+
url_unfollow = "https://www.instagram.com/web/friendships/#{user_id}/unfollow/"
|
62
|
+
print_try_message(action: :unfollow, data: user_id)
|
63
|
+
begin
|
64
|
+
set_request_params
|
65
|
+
response = @agent.post url_unfollow, @params, @request_headers
|
66
|
+
rescue Mechanize::ResponseCodeError
|
67
|
+
return false
|
68
|
+
end
|
69
|
+
response_data = JSON.parse(response.body)
|
70
|
+
response.code == '200' && response_data['status'] == 'ok' ? true : false
|
71
|
+
end
|
72
|
+
|
73
|
+
# Likes the media if it doesn't exist in the database.
|
74
|
+
#
|
75
|
+
# @param media [MediaData] a MediaData instance.
|
76
|
+
# @return (see #like_media)
|
77
|
+
def like_if_not_in_db(media)
|
78
|
+
return false if media.exists_in_db?(@table_likes)
|
79
|
+
|
80
|
+
if like_media(media.id)
|
81
|
+
@total_likes += 1
|
82
|
+
print_success_message(action: :like, number: @total_likes, data: @media.id)
|
83
|
+
media.insert_into_db(@table_likes)
|
84
|
+
sleep_rand(28, 36)
|
85
|
+
true
|
86
|
+
else
|
87
|
+
false
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Follows the user if it doesn't exist in the database.
|
92
|
+
#
|
93
|
+
# @param user [UserData] a UserData instance.
|
94
|
+
# @return (see #like_media)
|
95
|
+
def follow_if_not_in_db(user)
|
96
|
+
return false if user.exists_in_db?(@table_follows)
|
97
|
+
|
98
|
+
if follow_user(user.id)
|
99
|
+
@total_follows += 1
|
100
|
+
print_success_message(action: :follow, number: @total_follows, data: @user.username)
|
101
|
+
user.insert_into_db(@table_follows)
|
102
|
+
sleep_rand(28, 36)
|
103
|
+
true
|
104
|
+
else
|
105
|
+
false
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'actions'
|
2
|
+
require_relative 'data/media_data'
|
3
|
+
require_relative 'data/user_data'
|
4
|
+
require_relative 'data/page_data'
|
5
|
+
require_relative 'helpers'
|
6
|
+
require_relative 'login'
|
7
|
+
require_relative 'modes'
|
8
|
+
require_relative 'pages'
|
9
|
+
require_relative 'requests'
|
10
|
+
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
include Actions
|
15
|
+
include Helpers
|
16
|
+
include Login
|
17
|
+
include Modes
|
18
|
+
include Pages
|
19
|
+
include Requests
|
20
|
+
|
21
|
+
end
|