instagram-private-api 0.1.5.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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