instagram-private-api 0.1.5.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: efb18574c9777d39682c3f43af28b74c243ff80f
4
- data.tar.gz: 932785b19d1353d25265763d84ecd24cd5d99a90
2
+ SHA256:
3
+ metadata.gz: 2207b129dd5717404254fe01477f4d7187dcea4a8296e4f8ba5f7b0088dd7624
4
+ data.tar.gz: 10eb79723993e4bb1d246717d05df3f77a3cbcecd5b976931d3683bd932caf48
5
5
  SHA512:
6
- metadata.gz: '08babf9c8d62f36e5a62ac3b504675df7f4c18920c979e45d37d92c01e45cfd9ce891ed7eafc518ed140437b0b8df9aedac3a7d8582792d02f2f7ba7d9b93cc3'
7
- data.tar.gz: a83137d0a11db33047504ec92b4657075863221b750617e367ba939ae959bbae6936939abaf7cf705b81bbeebdd5fb6cd073bbd2162ebab5c5a2f5073dede65e
6
+ metadata.gz: 840be2d6f859320faf70cde2271952fb419f46d8e0054e1f8c014469b945476c37b6a920268696881c2c5a29a79286ab5e12e0d28f47e9b00979b91629c11d47
7
+ data.tar.gz: 6291205d99b5739a8e02e687df22475eea851be492b1ea76451d0523ddc42aaf51305c65a2c31d204c965292424d934f7e8945befc39e8d12e00f06aaf6320c7
@@ -0,0 +1,2 @@
1
+ .idea
2
+ Gemfile.lock
@@ -0,0 +1,4 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+ Layout/EndOfLine:
4
+ EnforcedStyle: lf
@@ -1,36 +1,36 @@
1
-
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'Instagram/API/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = 'instagram-private-api'
8
- spec.version = Instagram::API::VERSION
9
- spec.authors = ['vicoerv']
10
- spec.email = ['vicoerv@gmail.com']
11
-
12
- spec.summary = 'Instagram private api, implemented from huttarichard/instagram-private-api as best NODE-JS Instagram api'
13
- spec.description = spec.summary
14
- spec.homepage = 'http://www.vicoervanda.com'
15
- spec.license = 'MIT'
16
-
17
- # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
- # to allow pushing to a single host or delete this section to allow pushing to any host.
19
- if spec.respond_to?(:metadata)
20
- spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
- else
22
- raise 'RubyGems 2.0 or newer is required to protect against ' \
23
- 'public gem pushes.'
24
- end
25
-
26
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
- f.match(%r{^(test|spec|features)/})
28
- end
29
- spec.bindir = 'exe'
30
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
- spec.require_paths = ['lib']
32
-
33
- spec.add_development_dependency 'bundler', '~> 1.16'
34
- spec.add_development_dependency 'rake', '~> 10.0'
35
- spec.add_development_dependency 'rspec', '~> 3.0'
36
- end
1
+
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'Instagram/API/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'instagram-private-api'
8
+ spec.version = Instagram::API::VERSION
9
+ spec.authors = ['vicoerv']
10
+ spec.email = ['vicoerv@gmail.com']
11
+
12
+ spec.summary = 'Instagram private api, implemented from huttarichard/instagram-private-api as best NODE-JS Instagram api'
13
+ spec.description = spec.summary
14
+ spec.homepage = 'http://www.vicoervanda.com'
15
+ spec.license = 'MIT'
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ if spec.respond_to?(:metadata)
20
+ spec.metadata['allowed_push_host'] = "https://rubygems.org"
21
+ else
22
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
23
+ 'public gem pushes.'
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
27
+ f.match(%r{^(test|spec|features)/})
28
+ end
29
+ spec.bindir = 'exe'
30
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
31
+ spec.require_paths = ['lib']
32
+
33
+ spec.add_development_dependency 'bundler', '~> 1.16'
34
+ spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rspec', '~> 3.0'
36
+ end
data/README.md CHANGED
@@ -20,8 +20,7 @@ Or install it yourself as:
20
20
  ## Usage
21
21
  - Login _for clearly new user_
22
22
  ```ruby
23
- user = Instagram::User.new 'username', 'password'
24
- p Instagram::API::Accounts.login user
23
+ user = Instagram::Account.login 'username', 'password'
25
24
  ```
26
25
 
27
26
  - Initiate existing user
@@ -38,23 +37,23 @@ Or install it yourself as:
38
37
  session = logged_in_user.session
39
38
 
40
39
  user = Instagram::User.new 'username', nil, data, session #password isn't mandatory, already have session
41
- p user.search_for_user 'ogiyuka_ippaiwarae216' #then you can use it for any purpose
40
+ p user.search_for_user 'instagram' #then you can use it for any purpose
42
41
  ```
43
42
 
44
43
  - Search for user
45
44
  ```ruby
46
- p user.search_for_user 'ogiyuka_ippaiwarae216'
45
+ p user.search_for_user 'instagram'
47
46
  ```
48
47
 
49
48
  - User feed
50
49
  ```ruby
51
- p user.user_media #your feed
50
+ p user.media #your feed
52
51
 
53
- user_target = user.search_for_user 'ogiyuka_ippaiwarae216'
54
- p user.user_media user_id: user_target.data[:id] #ogiyuka_ippaiwarae216 feed, or
55
- media = user_target.user_media #ogiyuka_ippaiwarae216 feed as shorthand
52
+ user_target = user.search_for_user 'instagram'
53
+ p user.media user_id: user_target.data[:id] #instagram feed, or
54
+ media = user_target.media #instagram feed as shorthand
56
55
  if media['next_available']
57
- p user_target.user_media max_id: media['next_max_id'] #next page
56
+ p user_target.media max_id: media['next_max_id'] #next page
58
57
  end
59
58
  ```
60
59
  ## Development
@@ -63,6 +62,9 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
63
62
 
64
63
  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).
65
64
 
65
+ ## Testing
66
+ testcase is using real instagram account, you can safely store your credential in environment variables.
67
+
66
68
  ## Contributing
67
69
 
68
70
  Bug reports and pull requests are welcome on GitHub at [https://github.com/vicoerv/Instagram-API](https://github.com/vicoerv/instagram-private-api). This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
@@ -1,7 +1,11 @@
1
- module Instagram
2
- end
3
-
4
- require 'Instagram/API'
5
- require 'Instagram/CONSTANTS'
6
- require 'Instagram/Device'
7
- require 'Instagram/User'
1
+ # frozen_string_literal: true
2
+
3
+ # Root module
4
+ module Instagram
5
+ end
6
+
7
+ require 'Instagram/API'
8
+ require 'Instagram/CONSTANTS'
9
+ require 'Instagram/Device'
10
+ require 'Instagram/User'
11
+ require 'Instagram/Relationship'
@@ -1,5 +1,5 @@
1
- module Instagram
2
- module API
3
- VERSION = "0.1.5.2"
4
- end
5
- end
1
+ module Instagram
2
+ module API
3
+ VERSION = "0.2.0"
4
+ end
5
+ end
@@ -1,69 +1,101 @@
1
- module Instagram
2
- module Account
3
- def self.login(user)
4
- request = Instagram::API.http(
5
- url: CONSTANTS::URL + 'accounts/login/',
6
- method: 'POST',
7
- user: user,
8
- body: format(
9
- 'ig_sig_key_version=4&signed_body=%s',
10
- Instagram::API.generate_signature(
11
- device_id: user.device_id,
12
- login_attempt_user: 0, password: user.password, username: user.username,
13
- _csrftoken: 'missing', _uuid: Instagram::API.generate_uuid
14
- ))
15
- )
16
- if request.code == '200'
17
- json_body = JSON.parse request.body
18
- logged_in_user = json_body['logged_in_user']
19
- user.data = {
20
- id: logged_in_user['pk'],
21
- full_name: logged_in_user['full_name'],
22
- is_private: logged_in_user['is_private'],
23
- profile_pic_url: logged_in_user['profile_pic_url'],
24
- profile_pic_id: logged_in_user['profile_pic_id'],
25
- is_verified: logged_in_user['is_verified'],
26
- is_business: logged_in_user['is_business']
27
- }
28
- cookies_array = []
29
- all_cookies = request.get_fields('set-cookie')
30
- all_cookies.each do |cookie|
31
- cookies_array.push(cookie.split('; ')[0])
32
- end
33
- cookies = cookies_array.join('; ')
34
- user.session = cookies
35
- true
36
- else
37
- false
38
- end
39
- end
40
-
41
- def self.search_for_user(user, username)
42
- rank_token = Instagram::API.generate_rank_token user.session.scan(/ds_user_id=([\d]+);/)[0][0]
43
- endpoint = 'https://i.instagram.com/api/v1/users/search/'
44
- param = format('?is_typehead=true&q=%s&rank_token=%s', username, rank_token)
45
- result = Instagram::API.http(
46
- url: endpoint + param,
47
- method: 'GET',
48
- user: user
49
- )
50
-
51
- json_result = JSON.parse result.body
52
- if json_result['num_results'] > 0
53
- user_result = json_result['users'][0]
54
- user_object = Instagram::User.new username, nil
55
- user_object.data = {
56
- id: user_result['pk'],
57
- full_name: user_result['full_name'],
58
- is_private: user_result['is_prive'],
59
- profile_pic_url: user_result['profile_pic_url'],
60
- profile_pic_id: user_result['profile_pic_id'],
61
- is_verified: user_result['is_verified'],
62
- is_business: user_result['is_business']
63
- }
64
- user_object.session = user.session
65
- user_object
66
- end
67
- end
68
- end
69
- end
1
+ module Instagram
2
+ class Account
3
+ def initialized
4
+ @api = nil
5
+ end
6
+
7
+ def api
8
+ @api = Instagram::V1.new if @api.nil?
9
+
10
+ @api
11
+ end
12
+
13
+ def login(username, password, config = Instagram::Configuration.new)
14
+ user = User.new username, password
15
+
16
+ request = api.post(
17
+ CONSTANTS::URL + 'accounts/login/',
18
+ format(
19
+ 'ig_sig_key_version=4&signed_body=%s',
20
+ Instagram::V1.generate_signature(
21
+ device_id: user.device_id,
22
+ login_attempt_user: 0, password: user.password, username: user.username,
23
+ _csrftoken: 'missing', _uuid: Instagram::V1.generate_uuid
24
+ )
25
+ )
26
+ ).with(ua: user.useragent).exec
27
+
28
+ json_body = JSON.parse request.body
29
+ logged_in_user = json_body['logged_in_user']
30
+ user.data = {
31
+ id: logged_in_user['pk'],
32
+ full_name: logged_in_user['full_name'],
33
+ is_private: logged_in_user['is_private'],
34
+ profile_pic_url: logged_in_user['profile_pic_url'],
35
+ profile_pic_id: logged_in_user['profile_pic_id'],
36
+ is_verified: logged_in_user['is_verified'],
37
+ is_business: logged_in_user['is_business']
38
+ }
39
+ cookies_array = []
40
+ all_cookies = request.get_fields('set-cookie')
41
+ all_cookies.each do |cookie|
42
+ cookies_array.push(cookie.split('; ')[0])
43
+ end
44
+ cookies = cookies_array.join('; ')
45
+ user.config = config
46
+ user.session = cookies
47
+
48
+ user
49
+ end
50
+
51
+ def self.search_for_user_graphql(user, username)
52
+ endpoint = "https://www.instagram.com/#{username}/?__a=1"
53
+ result = Instagram::API.http(
54
+ url: endpoint,
55
+ method: 'GET',
56
+ user: user
57
+ )
58
+ response = JSON.parse result.body, symbolize_names: true
59
+ return nil unless response[:user].any?
60
+ {
61
+ profile_id: response[:user][:id],
62
+ external_url: response[:user][:external_url],
63
+ followers: response[:user][:followed_by][:count],
64
+ following: response[:user][:follows][:count],
65
+ full_name: response[:user][:full_name],
66
+ avatar_url: response[:user][:profile_pic_url],
67
+ avatar_url_hd: response[:user][:profile_pic_url_hd],
68
+ username: response[:user][:username],
69
+ biography: response[:user][:biography],
70
+ verified: response[:user][:is_verified],
71
+ medias_count: response[:user][:media][:count],
72
+ is_private: response[:user][:is_private]
73
+ }
74
+ end
75
+
76
+ def search_for_user(user, username)
77
+ rank_token = Instagram::V1.generate_rank_token user.session.scan(/ds_user_id=([\d]+);/)[0][0]
78
+ endpoint = 'https://i.instagram.com/api/v1/users/search/'
79
+ param = format('?is_typehead=true&q=%s&rank_token=%s', username, rank_token)
80
+ result = api.get(endpoint + param)
81
+ .with(session: user.session, ua: user.useragent).exec
82
+
83
+ json_result = JSON.parse result.body
84
+ if json_result['num_results'] > 0
85
+ user_result = json_result['users'][0]
86
+ user_object = Instagram::User.new username, nil
87
+ user_object.data = {
88
+ id: user_result['pk'],
89
+ full_name: user_result['full_name'],
90
+ is_private: user_result['is_prive'],
91
+ profile_pic_url: user_result['profile_pic_url'],
92
+ profile_pic_id: user_result['profile_pic_id'],
93
+ is_verified: user_result['is_verified'],
94
+ is_business: user_result['is_business']
95
+ }
96
+ user_object.session = user.session
97
+ user_object
98
+ end
99
+ end
100
+ end
101
+ end
@@ -1,66 +1,103 @@
1
- require 'Instagram/API/version'
2
- require 'openssl'
3
- require 'Base64'
4
- require 'digest/md5'
5
- require 'net/http'
6
- require 'json'
7
- require 'Instagram/User'
8
- require 'Instagram/account'
9
- require 'Instagram/feed'
10
-
11
- module Instagram
12
- module API
13
- def self.compute_hash(data)
14
- OpenSSL::HMAC.hexdigest OpenSSL::Digest.new('sha256'), CONSTANTS::PRIVATE_KEY[:SIG_KEY], data
15
- end
16
-
17
- def self.generate_uuid
18
- 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.gsub(/[xy]/) do |c|
19
- r = (Random.rand * 16).round | 0
20
- v = c == 'x' ? r : (r & 0x3 | 0x8)
21
- c.gsub(c, v.to_s(16))
22
- end.downcase
23
- end
24
-
25
- def self.create_md5(data)
26
- Digest::MD5.hexdigest(data).to_s
27
- end
28
-
29
- def self.generate_device_id
30
- timestamp = Time.now.to_i.to_s
31
- 'android-' + create_md5(timestamp)[0..16]
32
- end
33
-
34
- def self.generate_signature(data)
35
- data = data.to_json
36
- compute_hash(data) + '.' + data
37
- end
38
-
39
- def self.http(args)
40
- args[:url] = URI.parse(args[:url])
41
- http = Net::HTTP.new(args[:url].host, args[:url].port, '127.0.0.1', '8888')
42
- http.use_ssl = true
43
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
44
- request = nil
45
- if args[:method] == 'POST'
46
- request = Net::HTTP::Post.new(args[:url].path)
47
- elsif args[:method] == 'GET'
48
- request = Net::HTTP::Get.new(args[:url].path + (!args[:url].nil? ? '?' + args[:url].query : ''))
49
- end
50
-
51
- request.initialize_http_header(:'User-Agent' => args[:user].useragent,
52
- :Accept => Instagram::CONSTANTS::HEADER[:accept],
53
- :'Accept-Encoding' => Instagram::CONSTANTS::HEADER[:encoding],
54
- :'Accept-Language' => args[:user].language,
55
- :'X-IG-Capabilities' => Instagram::CONSTANTS::HEADER[:capabilities],
56
- :'X-IG-Connection-Type' => Instagram::CONSTANTS::HEADER[:type],
57
- :Cookie => (args[:user].session.nil? ? '' : args[:user].session))
58
- request.body = args.key?(:body) ? args[:body] : nil
59
- http.request(request)
60
- end
61
-
62
- def self.generate_rank_token(pk)
63
- format('%s_%s', pk, Instagram::API.generate_uuid)
64
- end
65
- end
66
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'Instagram/API/version'
4
+ require 'openssl'
5
+ require 'Base64'
6
+ require 'digest/md5'
7
+ require 'net/http'
8
+ require 'json'
9
+ require 'Instagram/User'
10
+ require 'Instagram/account'
11
+ require 'Instagram/feed'
12
+ require 'Instagram/Configuration'
13
+
14
+ module Instagram
15
+ class V1
16
+ def self.compute_hash(data)
17
+ OpenSSL::HMAC.hexdigest OpenSSL::Digest.new('sha256'), CONSTANTS::PRIVATE_KEY[:SIG_KEY], data
18
+ end
19
+
20
+ def self.__obj=value
21
+ @@obj = value
22
+ end
23
+
24
+ def self.__obj
25
+ @@obj
26
+ end
27
+
28
+ def self.singleton
29
+ @@obj = V1.new unless defined? @@obj
30
+
31
+ @@obj
32
+ end
33
+
34
+ def self.generate_uuid
35
+ 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.gsub(/[xy]/) do |c|
36
+ r = (Random.rand * 16).round | 0
37
+ v = c == 'x' ? r : (r & 0x3 | 0x8)
38
+ c.gsub(c, v.to_s(16))
39
+ end.downcase
40
+ end
41
+
42
+ def self.create_md5(data)
43
+ Digest::MD5.hexdigest(data).to_s
44
+ end
45
+
46
+ def self.generate_device_id
47
+ timestamp = Time.now.to_i.to_s
48
+ 'android-' + create_md5(timestamp)[0..16]
49
+ end
50
+
51
+ def self.generate_signature(data)
52
+ data = data.to_json
53
+ compute_hash(data) + '.' + data
54
+ end
55
+
56
+ def post(url, body = nil)
57
+ @data = { method: 'POST', url: url, body: body }
58
+ self
59
+ end
60
+
61
+ def with(data)
62
+ data.each { |k, v| @data[k] = v }
63
+ self
64
+ end
65
+
66
+ def exec
67
+ http @data
68
+ end
69
+
70
+ def get(url)
71
+ @data = {method: 'GET', url: url}
72
+ self
73
+ end
74
+
75
+ def http(args)
76
+ args[:url] = URI.parse(args[:url])
77
+ http = Net::HTTP.new(args[:url].host, args[:url].port,
78
+ ENV['INSTAGRAM_PROXY_HOST'], ENV['INSTAGRAM_PROXY_PORT'])
79
+ http.use_ssl = true
80
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
81
+ request = nil
82
+ if args[:method] == 'POST'
83
+ request = Net::HTTP::Post.new(args[:url].path)
84
+ elsif args[:method] == 'GET'
85
+ request = Net::HTTP::Get.new(args[:url].path + (!args[:url].nil? ? '?' + args[:url].query : ''))
86
+ end
87
+
88
+ request.initialize_http_header('User-Agent': args[:ua],
89
+ Accept: Instagram::CONSTANTS::HEADER[:accept],
90
+ 'Accept-Encoding': Instagram::CONSTANTS::HEADER[:encoding],
91
+ 'Accept-Language': args.dig(:user)&.language,
92
+ 'X-IG-Capabilities': Instagram::CONSTANTS::HEADER[:capabilities],
93
+ 'X-IG-Connection-Type': Instagram::CONSTANTS::HEADER[:type],
94
+ Cookie: args[:session])
95
+ request.body = args.key?(:body) ? args[:body] : nil
96
+ http.request(request)
97
+ end
98
+
99
+ def self.generate_rank_token(pk)
100
+ format('%s_%s', pk, Instagram::V1.generate_uuid)
101
+ end
102
+ end
103
+ end