instagram_native 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6e3aab885f5545a089907df02b9a16d99cd30581
4
+ data.tar.gz: 441a17386a8444516b3d27dbf643311fe7fbe584
5
+ SHA512:
6
+ metadata.gz: d0ab4dbac71165ed6d0e59a79ee90629318a469453fd9f4d1a818678db4dfa31025b7785e93c7c085dc8dfc6ab1e191d2e4a12a9c4750933ce08e0cb47bc60d6
7
+ data.tar.gz: 320b484e43e5be7877b7d020959659444c51f8be2034680532f1e57e3e74da6d607c83898cab1aebcae08ba4673b527170f0324aed5ed5c6d258e1b0d4119680
@@ -0,0 +1,15 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'instagram_native'
3
+ s.version = '0.0.7'
4
+ s.date = '2015-01-31'
5
+ s.summary = "Instagram native API"
6
+ s.description = "Instagram native API realization"
7
+ s.authors = ["Denis Kuznetsov"]
8
+ s.email = 'denis@f7.ru'
9
+ s.files = ["lib/instagram_native.rb", "lib/instagram/native_client.rb", "lib/instagram/invalid_credentials.rb", "lib/instagram/register_exception.rb", "lib/instagram/it_was_me_required.rb", "instagram_native.gemspec"]
10
+ s.license = 'MIT'
11
+
12
+
13
+ s.add_runtime_dependency "httpclient"
14
+ s.add_runtime_dependency "json"
15
+ end
@@ -0,0 +1,4 @@
1
+ module Instagram
2
+ class InvalidCredentials < StandardError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Instagram
2
+ class ItWasMeRequired < StandardError
3
+ end
4
+ end
@@ -0,0 +1,352 @@
1
+ require 'cgi'
2
+
3
+ module Instagram
4
+ class NativeClient
5
+ attr_accessor :http, :private_key, :useragent, :ig_user_id, :ig_user_name, :csrftoken, :device_guid, :proxy, :device_id
6
+
7
+ def initialize(proxy, dg: '4da9753f-4663-4c89-a99a-7c1bc6d188fe', ua: 'Instagram 6.14.1 Android (16/4.1.2; 240dpi; 540x960; Sony/SEMC; LT22i; LT22i; st-ericsson; ru_RU)', device_id: nil)
8
+ @device_guid = generate_device_guid #dg
9
+ @device_id = device_id || generate_device_id
10
+ @useragent = ua
11
+ m = proxy.match(/(\w+):(\w+)@([\w\.]+):(\d+)/) if proxy
12
+ @proxy = proxy
13
+ if m
14
+ host_port = "http://#{m[3]}:#{m[4]}"
15
+ @http = HTTPClient.new(host_port, @useragent)
16
+ @http.set_proxy_auth(m[1], m[2])
17
+ else
18
+ if proxy
19
+ puts "proxy is: #{proxy}"
20
+ host_port = "http://#{proxy}"
21
+ @http = HTTPClient.new(host_port, @useragent)
22
+ else
23
+ @http = HTTPClient.new(nil, @useragent)
24
+ end
25
+ end
26
+ @http.ssl_config.options |= OpenSSL::SSL::OP_NO_SSLv3
27
+ end
28
+
29
+
30
+ def login(login, password)
31
+ login_info = {:_csrftoken => "missing",
32
+ :username => login,
33
+ :guid => @device_guid,
34
+ :password => password,
35
+ :device_id => @device_id
36
+ }
37
+
38
+ result = post_struct "https://i.instagram.com/api/v1/accounts/login/", login_info
39
+ puts "login result: #{result.inspect}"
40
+
41
+ if 'fail' == result['status']
42
+ if result['message'] == 'checkpoint_required'
43
+ raise ItWasMeRequired
44
+ else
45
+ raise result['message']
46
+ end
47
+ else
48
+ begin
49
+ @ig_user_id = result['logged_in_user']['pk']
50
+ @ig_user_name = result['logged_in_user']['username']
51
+ @csrftoken = @http.cookies.select {|c| c.name == 'csrftoken'}.first.value
52
+ rescue => ex
53
+ Rails.logger.error ex.message
54
+ Rails.logger.error "Server answer: #{result}"
55
+ raise ex
56
+ end
57
+ end
58
+ end
59
+
60
+ def upload_photo(file_path, description)
61
+ File.open(file_path) do |file|
62
+ photo_info = {
63
+ :_csrftoken => @csrftoken,
64
+ :upload_id => Time.now.to_i,
65
+ :photo => file
66
+ }
67
+
68
+ @http.cookies.each {|c| c.domain = 'i.instagram.com'}
69
+ res = @http.post("http://i.instagram.com/api/v1/upload/photo/", photo_info)
70
+
71
+ puts "Upload photo result: #{res.body}"
72
+
73
+ result = JSON.parse(res.body)
74
+ upload_id = result['upload_id']
75
+
76
+ caption_info = {:upload_id => upload_id,
77
+ :source_type => "3",
78
+ :filter_type => "0",
79
+ :device => {
80
+ :manufacturer => "Sony",
81
+ :model => "LT22i",
82
+ :android_version => 16,
83
+ :android_release => "4.1.2"
84
+ },
85
+ :_csrftoken => @csrftoken,
86
+ :_uuid => @device_guid,
87
+ :_uid => ig_user_id}
88
+ caption_info.merge!({:caption => description}) unless description.blank?
89
+
90
+ @http.cookies.each {|c| c.domain = 'i.instagram.com'}
91
+ parsed = post_struct "https://i.instagram.com/api/v1/media/configure/", caption_info
92
+
93
+ result_hash = { server_answer: parsed.to_json }
94
+
95
+ if parsed['status'] == 'ok'
96
+ result_hash[:social_shortcode] = parsed['media']['code']
97
+ result_hash[:social_id] = parsed['media']['id']
98
+ end
99
+ result_hash
100
+ end
101
+ end
102
+
103
+ def upload_video(file_path, description)
104
+ File.open(file_path) do |file|
105
+ upload_id = Time.now.to_i
106
+ video_info = {
107
+ :_csrftoken => @csrftoken,
108
+ :media_type => 2,
109
+ :upload_id => upload_id
110
+ }
111
+
112
+ @http.cookies.each {|c| c.domain = 'i.instagram.com'}
113
+ res = @http.post("http://i.instagram.com/api/v1/upload/video/", video_info)
114
+ result = JSON.parse(res.body)
115
+
116
+ chunks_number = result['video_upload_urls'].count
117
+ chunk_size = file.size / chunks_number
118
+ bytes_uploaded = 0
119
+
120
+ puts "File size: #{file.size}, chunks_number: #{chunks_number}, chunk_size: #{chunk_size}"
121
+
122
+ video_result = nil
123
+
124
+ puts result['video_upload_urls'].inspect
125
+
126
+ byebug
127
+
128
+ result['video_upload_urls'].each {|upload_struct|
129
+ bytes_rest = file.size - bytes_uploaded
130
+ is_last_chunk = bytes_rest <= chunk_size * 2
131
+
132
+ to_upload = is_last_chunk ? bytes_rest : chunk_size
133
+ file_part = file.read(to_upload)
134
+
135
+ puts "To upload: #{to_upload}, bytes_rest: #{bytes_rest}, bytes_uploaded: #{bytes_uploaded}"
136
+
137
+
138
+ #uri = URI.parse upload_struct['url']
139
+ #@http.cookies.each {|c| c.domain = uri.host}
140
+ result = @http.post(upload_struct['url'], file_part, { "job" => upload_struct['job'],
141
+ "Content-Range" => "bytes #{bytes_uploaded}-#{bytes_uploaded + to_upload - 1}/#{file.size}"
142
+ # "Content-Type" => "application/octet-stream"
143
+ }
144
+ )
145
+ byebug
146
+
147
+ video_result = JSON.parse(result)['result'] if is_last_chunk
148
+ bytes_uploaded += to_upload
149
+ }
150
+
151
+ end
152
+ end
153
+
154
+ def change_profile_picture(picture_path = 'http://lorempixel.com/150/150/')
155
+ filename = get_temp_filename
156
+ begin
157
+ file = File.new(filename, 'wb')
158
+ res = open(picture_path) { |f| f.read }
159
+ file.write(res.to_s)
160
+ file.close
161
+ file = File.open(filename)
162
+ picture_params = {
163
+ :_csrftoken => @csrftoken,
164
+ :profile_pic => file
165
+ }
166
+ @http.cookies.each {|c| c.domain = 'i.instagram.com'}
167
+ res = @http.post("http://i.instagram.com/api/v1/accounts/change_profile_picture/", picture_params)
168
+ ensure
169
+ file.close
170
+ File.unlink filename
171
+ end
172
+ end
173
+
174
+ def send_sms(phone)
175
+ struct = {
176
+ :_csrftoken => @csrftoken,
177
+ :_uid => @ig_user_id,
178
+ :phone_number => phone,
179
+ :_uuid => @device_guid
180
+ }
181
+ parsed = post_struct 'https://i.instagram.com/api/v1/accounts/send_sms_code/', struct
182
+ #puts "Parsed sms result: #{parsed.inspect}"
183
+ parsed['status'] == 'ok' && parsed['phone_number_valid']
184
+ end
185
+
186
+ def verify_account(phone, code)
187
+ struct = {
188
+ :verification_code => code,
189
+ :_uid => @ig_user_id,
190
+ :_csrftoken => @csrftoken,
191
+ :phone_number => phone,
192
+ :_uuid => @device_guid
193
+ }
194
+ parsed = post_struct 'https://i.instagram.com/api/v1/accounts/verify_sms_code/', struct
195
+ puts "Parsed sms result: #{parsed.inspect}"
196
+ parsed['status'] == 'ok' && parsed['verified']
197
+ end
198
+
199
+
200
+ def check_username(uname)
201
+ login_info = {
202
+ :_csrftoken => @csrftoken,
203
+ :username => uname
204
+ }
205
+ parsed = post_struct "https://instagram.com/api/v1/users/check_username/", login_info
206
+ parsed['status'] == 'ok' && parsed['available']
207
+ end
208
+
209
+ def create_account(uname, pass, email = nil, first_name = "")
210
+ email ||= generate_email(uname)
211
+ account_info = {
212
+ :_csrftoken => @csrftoken,
213
+ :first_name => first_name,
214
+ :username => uname,
215
+ :email => email,
216
+ :guid => @device_guid,
217
+ :password => pass,
218
+ :device_id => @device_id
219
+ }
220
+ parsed = post_struct "https://instagram.com/api/v1/accounts/create/", account_info
221
+ raise RegisterException.new(uname, pass, email, @proxy) unless parsed['status'] == 'ok'
222
+ end
223
+
224
+ def get_timeline
225
+ @http.cookies.each {|c| c.domain = 'i.instagram.com'}
226
+ res = @http.get("http://i.instagram.com/api/v1/feed/timeline/") #, "Content-Type" => "application/json"
227
+ end
228
+
229
+ def get_news
230
+ @http.cookies.each {|c| c.domain = 'i.instagram.com'}
231
+ res = @http.get("http://i.instagram.com/api/v1/news/inbox/") #, "Content-Type" => "application/json"
232
+ end
233
+
234
+ def direct_inbox
235
+ @http.cookies.each {|c| c.domain = 'i.instagram.com'}
236
+ res = @http.get("https://i.instagram.com/api/v1/direct_share/inbox/") #"Content-Type" => "application/json"
237
+ end
238
+
239
+ def follow_user(user_id)
240
+ post_struct = {
241
+ :_uid => @ig_user_id,
242
+ :_csrftoken => @csrftoken,
243
+ :user_id => user_id
244
+ }
245
+ parsed = post_struct "http://i.instagram.com/api/v1/friendships/create/#{user_id}/", post_struct
246
+ parsed['status'] == 'ok'
247
+ end
248
+
249
+ def unfollow_user(user_id)
250
+ post_struct = {
251
+ :_uid => @ig_user_id,
252
+ :_csrftoken => @csrftoken,
253
+ :user_id => user_id
254
+ }
255
+ parsed = post_struct "http://i.instagram.com/api/v1/friendships/destroy/#{user_id}/", post_struct
256
+ parsed['status'] == 'ok'
257
+ end
258
+
259
+ def like_media(media_id)
260
+ post_struct = {
261
+ :_uid => @ig_user_id,
262
+ :_csrftoken => @csrftoken,
263
+ :media_id => media_id
264
+ }
265
+
266
+ parsed = post_struct "http://i.instagram.com/api/v1/media/#{media_id}/like/", post_struct, {src: "timeline", d: 0}, "application/x-www-form-urlencoded; charset=UTF-8"
267
+ parsed['status'] == 'ok'
268
+ end
269
+
270
+ def search_user(query)
271
+ @http.get("http://i.instagram.com/api/v1/users/search/?is_typeahead=true&rank_token=#{@ig_user_id}_e9730a31-df06-4c97-aaef-56cdc2601c1a&q=#{query}")
272
+ end
273
+
274
+ def media_by_tag(tag)
275
+ @http.get("http://i.instagram.com/api/v1/feed/tag/#{tag}/?max_id=#{(Time.now.to_f * 1000).to_i}")
276
+ end
277
+
278
+ def user_follows(user_id = nil)
279
+ user_id ||= @ig_user_id
280
+ @http.get("http://i.instagram.com/api/v1/friendships/#{user_id}/following/")
281
+ end
282
+
283
+ def user_followed(user_id = nil)
284
+ user_id ||= @ig_user_id
285
+ @http.get("http://i.instagram.com/api/v1/friendships/#{user_id}/followers/")
286
+ end
287
+
288
+
289
+
290
+ private
291
+
292
+ def sign_structure(struct)
293
+ jsoned = struct.to_json
294
+ signature = get_signature(jsoned)
295
+ signed_body = "#{signature}.#{jsoned}"
296
+ {:signed_body => signed_body, :ig_sig_key_version => 4}
297
+ end
298
+
299
+ def get_signature(jsoned)
300
+ s = Time.now.to_f.to_s.last(10).gsub('.', '')
301
+ filename = "#{s}.txt"
302
+ local_path = "#{Rails.root}/tmp/signatures/#{filename}"
303
+ File.write(local_path, jsoned)
304
+
305
+ %x[#{ADB_PATH} push #{local_path} /data/byte/signatures/]
306
+
307
+ File.delete local_path
308
+
309
+ #e = CGI.escape(jsoned)
310
+ signature = %x[#{ADB_PATH} shell dalvikvm -Djava.library.path=/data/byte/ -cp /data/byte/app.jar App #{filename}]
311
+ puts "signature: #{signature}. for string: #{jsoned}"
312
+ signature
313
+ end
314
+
315
+ def generate_device_id
316
+ res = ""
317
+ arr = ['1', '2', "3", '4', '5', '6', '7', '8', '9', '0']
318
+ 16.times { res += arr.sample }
319
+ "android-#{res}"
320
+ end
321
+
322
+ def generate_device_guid
323
+ "#{hex_digits(8)}-#{hex_digits(4)}-#{hex_digits(4)}-#{hex_digits(4)}-#{hex_digits(12)}"
324
+ #4da9753f-4663-4c89-a99a-7c1bc6d188fe
325
+ end
326
+
327
+ def generate_email(login)
328
+ "#{login}@mail.ru"
329
+ end
330
+
331
+ def hex_digits(count)
332
+ hex = ['1', '2', "3", '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f']
333
+ res = ''
334
+ count.times { res += hex.sample }
335
+ res
336
+ end
337
+
338
+ def get_temp_filename
339
+ s = Time.now.to_f.to_s.last(10).gsub('.', '')
340
+ "#{s}.jpg"
341
+ end
342
+
343
+
344
+ def post_struct(url, structure, options = {}, content_type = "application/json")
345
+ #puts "Making POST to: #{url}, structure: #{structure.inspect}"
346
+ result = @http.post(url, sign_structure(structure).merge(options), "Content-Type" => content_type)
347
+ JSON.parse(result.body)
348
+ end
349
+
350
+ end
351
+
352
+ end
@@ -0,0 +1,17 @@
1
+ module Instagram
2
+ class RegisterException < StandardError
3
+ attr_accessor :login, :password, :email, :proxy
4
+
5
+ def initialize(login, password, email, proxy)
6
+ @login = login
7
+ @password = password
8
+ @email = email
9
+ @proxy = proxy
10
+ end
11
+
12
+ def message
13
+ "Error registering Instagram account: #{@login}:#{@password}, proxy: #{@proxy}"
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,4 @@
1
+ require 'instagram/native_client'
2
+ require 'instagram/register_exception'
3
+ require 'instagram/invalid_credentials'
4
+ require 'instagram/it_was_me_required'
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: instagram_native
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.7
5
+ platform: ruby
6
+ authors:
7
+ - Denis Kuznetsov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: httpclient
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: json
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Instagram native API realization
42
+ email: denis@f7.ru
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - instagram_native.gemspec
48
+ - lib/instagram/invalid_credentials.rb
49
+ - lib/instagram/it_was_me_required.rb
50
+ - lib/instagram/native_client.rb
51
+ - lib/instagram/register_exception.rb
52
+ - lib/instagram_native.rb
53
+ homepage:
54
+ licenses:
55
+ - MIT
56
+ metadata: {}
57
+ post_install_message:
58
+ rdoc_options: []
59
+ require_paths:
60
+ - lib
61
+ required_ruby_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 2.4.5
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Instagram native API
77
+ test_files: []
78
+ has_rdoc: