· 10 years ago · Apr 07, 2016, 11:48 AM
1import csv
2import hashlib
3import json
4import lxml.html
5import random
6import requests
7import time
8import os.path
9
10
11class MusicallyFame():
12 phone_profiles = [
13 "lava",
14 "samsung",
15 "oneplusone",
16 "htc",
17 "nexus"
18 ]
19 phone_details = {
20 "lava": {
21 "os": "android 4.1.2",
22 "User-Agent": "Musical.ly/2016020601 (Android; LAVA LAVA 4.1.2;rv:16)",
23 "build": "2016020601",
24 "mobile": "LAVA Iris 501",
25 "model": "SM-G730V",
26 "sdk": "19"
27 },
28 "samsung": {
29 "os": "android 5.1.1",
30 "User-Agent": "Musical.ly/2016040101 (Android; Samsung Galaxy J3 5.1.1;rv:22)",
31 "build": "2016040101",
32 "mobile": "Samsung Galaxy J3 SM-J320Y",
33 "model": "SM-J320Y",
34 "sdk": "22"
35 },
36 "oneplusone": {
37 "os": "android 5.1.1",
38 "User-Agent": "Musical.ly/2016040101 (Android; OnePlus A0001 5.1.1;rv:22)",
39 "build": "2016040101",
40 "mobile": "OnePlus A0001",
41 "model": "A0001",
42 "sdk": "22"
43 },
44 "nexus": {
45 "os": "android 6.0.1",
46 "User-Agent": "Musical.ly/2016040101 (Android; LGE Nexus 5 6.0.1;rv:23)",
47 "build": "2016040101",
48 "mobile": "LGE Nexus 5",
49 "model": "Nexus 5",
50 "sdk": "22"
51 },
52 "htc": {
53 "os": "android 5.1.1",
54 "User-Agent": "Musical.ly/2016040101 (Android; HTC One A9 5.1.1;rv:22)",
55 "build": "2016040101",
56 "mobile": "HTC One A9",
57 "model": "A9",
58 "sdk": "22"
59 },
60 }
61
62 def __init__(self, input_file):
63 self.proxies = self.proxy_list()
64 self.input_file = input_file
65 self.common_header = {
66 "os": "android 4.1.2",
67 "User-Agent": "Musical.ly/2016020601 (Android; LAVA LAVA 4.1.2;rv:16)",
68 "build": "2016020601",
69 "mobile": "LAVA Iris 501",
70 "version": "4.7.5",
71 "X-Requested-With": "XMLHttpRequest",
72 "language": "en_US",
73 "Connection": "close",
74 "network": "WiFi",
75 "Host": "www.musical.ly",
76 "Accept-Encoding": "gzip",
77 "MIME-Version": "1.0"
78 }
79 self.signing_server = "http://45.55.203.163:8888/sign"
80 self.signature_data = {
81 "app": {
82 "-r": "Gvt1",
83 "os": "android 4.1.2",
84 "version": "4.7.5"
85 },
86 "ostype": "and",
87 "imei": "51a4454f",
88 "mac": "4C:3C:16:82:EE:F3",
89 "model": "SM-G730V",
90 "sdk": "19",
91 }
92 self.session = requests.Session()
93 # self.session.mount('https://', HTTPAdapter(max_retries=50))
94
95 def proxy_list(self):
96 content = requests.get("http://free-proxy-list.net").text
97 doc = lxml.html.document_fromstring(content)
98 lines = doc.cssselect('table.fpltable tr')
99 temp_proxies = []
100 for line in lines:
101 tds = line.cssselect('td')
102 if not tds:
103 continue
104 anonymity = tds[4].text
105 if anonymity == 'elite proxy' and len(tds) > 7:
106 temp_proxies.append({
107 'ip_address': tds[0].text,
108 'port': tds[1].text,
109 'country_code': tds[2].text,
110 })
111 print len(temp_proxies)
112
113 return temp_proxies
114
115 def alter_handle(self, handle):
116 alter_option = random.choice([1, 2, 3])
117 if alter_option == 1:
118 handle += random.choice([str(n) for n in range(10)])
119 elif alter_option == 2:
120 _, __ = handle[-1], handle[-2]
121 handle = handle[:-3] + _ + __
122 else:
123 handle = handle[:-1]
124
125 return handle
126
127 def generate_user(self):
128 mail_servers = ["@newmail.com", "@gmail.com", "@hotmail.com", "@yahoo.com", "@outlook.com"]
129 with open(self.input_file, "rb") as csvfile:
130 reader = csv.reader(csvfile)
131 reader.next() # Skip the header row
132 for row in reader:
133 scraped_data = dict()
134 scraped_data["country"] = row[0].decode('string-escape').decode("utf-8")
135 scraped_data["nick_name"] = row[2].decode('string-escape').decode("utf-8")
136 scraped_data["photo"] = "./photos"+row[3]+".jpg"
137
138 handle = row[3].decode('string-escape').decode("utf-8")
139 scraped_data["handle"] = self.alter_handle(handle)
140 scraped_data["bio"] = row[4].decode('string-escape').decode("utf-8")
141 if scraped_data["bio"] == "n/a":
142 scraped_data["bio"] = "live with passion, live musical.ly"
143 scraped_data["email"] = scraped_data["handle"]+random.choice(mail_servers)
144 scraped_data["password"] = scraped_data["handle"][::-1]+random.choice(["1", "2", "3", "4", "5"])
145 yield scraped_data
146
147 def get_likes(self, account, like_counts):
148 return
149
150 def get_follows(self, account, follow_counts):
151 return
152
153 def get_a_phone(self, phone):
154 #random value for imei and mac no
155 imei = ''.join(random.choice('0123456789abcdef') for i in range(8))
156 val = ''.join(random.choice('0123456789ABCDEF') for i in range(12))
157 mac = ':'.join(val[i:i+2] for i in xrange(0, 12, 2))
158 self.signature_data["imei"] = imei
159 self.signature_data["mac"] = mac
160
161 if phone in self.phone_profiles:
162 self.common_header["os"] = self.phone_details[phone]["os"]
163 self.common_header["User-Agent"] = self.phone_details[phone]["User-Agent"]
164 self.common_header["build"] = self.phone_details[phone]["build"]
165 self.common_header["mobile"] = self.phone_details[phone]["mobile"]
166
167 self.signature_data["app"]["os"] = self.phone_details[phone]["os"]
168 self.signature_data["model"] = self.phone_details[phone]["model"]
169 self.signature_data["sdk"] = self.phone_details[phone]["sdk"]
170
171 return
172
173 def get_proxy(self, country):
174 if not self.proxies:
175 self.proxies = self.proxy_list()
176
177 temp_proxies = list(self.proxies)
178 retries = 0
179
180 for proxy in temp_proxies:
181 if proxy['country_code'] == country:
182 address = "%s:%s" % (proxy['ip_address'], proxy['port'])
183 self.proxies.remove(proxy)
184 try:
185 _proxy = {"http": "http://"+address, "https": "https://"+address, "ftp":"ftp://"+address}
186 response = self.session.get("http://ipecho.net/plain", proxies=_proxy)
187 if address.split(':')[0] == response.text:
188 return _proxy
189
190 raise Exception()
191 except:
192 print 'get error, when checking proxy, try to get next proxy'
193 retries += 1
194 if retries == 5:
195 self.proxy_list()
196
197 if retries == 10:
198 break
199
200 return False
201
202 def unset_proxy(self):
203 self.session.proxies = None
204
205 # Function to get signature
206 def get_signature(self):
207 payload = {"data": json.dumps(self.signature_data)}
208 response = self.session.get(self.signing_server, data=payload)
209 try:
210 return str(json.loads(response.text)["signature"])
211 except ValueError:
212 return "signature not received"
213
214 # function to generate get request Id
215 def get_unique_id(self):
216 request_id = hashlib.md5(str(random.random())).hexdigest()
217 request_id = request_id[:8]+"-"+request_id[8:12]+"-"+request_id[12:16]+"-"+request_id[16:20]+"-"+request_id[20:]
218 return request_id
219
220 # function for time stamp
221 def get_time_stamp(self):
222 a = time.time()*1000
223 return "%.0f" % a
224
225 def prepare_header(self, url, method):
226 request_id = self.get_unique_id()
227 timestamp = self.get_time_stamp()
228 self.session.headers.clear()
229 self.session.headers = dict(self.common_header)
230 self.session.headers["X-Request-ID"] = request_id
231 if "register" in url or "login.do" in url:
232 self.signature_data["app"].pop("slider-show-cookie",None)
233 self.signature_data["app"]["-r"] = ''.join(random.choice('0123456789abcdefGHIJSTUVW') for i in range(4))
234 self.signature_data["app"]["X-Request-ID"] = request_id
235 self.signature_data["serviceTime"] = timestamp
236 self.signature_data["app"]["method"] = method
237 self.signature_data["app"]["url"] = url
238 request_signature = self.get_signature()
239 self.session.headers["X-Request-Sign2"] = request_signature
240 return
241
242 def prepare_registration_data(self, handle, email, password):
243 user = '''{"handle":"%s","email":"%s","password":"%s"}''' % (handle, email, password)
244 post_url = "http://www.musical.ly/rest/v2/users/register"
245 boundary = "27c8b11a-9b7e-4849-a665-7f1a04edf2b7"
246 data = (
247 """
248--%(boundary)s
249Content-Disposition: form-data; name="user"
250Content-Type: application/json; charset=utf-8
251Content-Length: %(content_length)s
252\r\n%(user)s
253--%(boundary)s--
254 """ % {
255 "boundary": boundary,
256 "content_length": len(user),
257 "user": user
258 }).replace("\n", "\r\n") # HTTP uses DOS-style line endings.
259
260 # update current headers
261 self.session.headers["Content-Type"] = "multipart/mixed; boundary=\"%s\"" % boundary
262 return data
263
264 def prepare_profile_data(self, user_setting_dto, bio, gender, handle, nick_name, user_id):
265 user = '''{"userSettingDTO":%s,"instagramID":"%s","userDesc":"%s","gender":"%s","handle":"%s","nickName":"%s","userId":"%s"}'''%(user_setting_dto,handle,bio,gender,handle,nick_name,user_id)
266 data = (
267 """
268--2b8a2224-07c6-4463-bb4b-364576e556ad
269Content-Disposition: form-data; name="user"
270Content-Type: application/json; charset=utf-8
271Content-Length: %(content_length)s
272\r\n%(user)s
273--2b8a2224-07c6-4463-bb4b-364576e556ad--
274 """ % {
275 "content_length": len(user),
276 "user": user
277 }).replace("\n","\r\n")
278 self.session.headers["Content-Type"] = '''multipart/mixed; boundary="2b8a2224-07c6-4463-bb4b-364576e556ad"'''
279 return data
280
281 def register_accounts(self, count):
282 output_fp = open('success.csv', 'w')
283 output_writer = csv.writer(output_fp, delimiter=',')
284
285 all_users = self.generate_user()
286 for i in xrange(count):
287 user_data = all_users.next()
288 self.get_a_phone(random.choice(self.phone_profiles))
289 country = user_data["country"]
290 proxy = self.get_proxy(country)
291 if not proxy:
292 proxy = self.get_proxy("US")
293
294 if not proxy:
295 continue
296
297 method = "POST"
298 url = "https://www.musical.ly/rest/v2/users/register"
299 self.prepare_header(url, method)
300 handle = user_data["handle"]
301 email = user_data["email"]
302 password = user_data["password"]
303 data = self.prepare_registration_data(handle, email, password)
304
305 try:
306 self.session.proxies = proxy
307 response = self.session.post(url, data=data, proxies=proxy, headers=self.session.headers)
308 print response.content
309 print response.status_code
310 # profile edit
311 if response.status_code == 200:
312 response_status = response.json().get('success', False)
313 if response_status:
314 output_writer.writerow([handle, password, email])
315 args = {
316 "username": "@"+handle,
317 "password": password,
318 "remember_me": "on"
319 }
320 login_url = "https://www.musical.ly/v2/login.do"
321 self.prepare_header(login_url, "POST")
322 self.session.proxies = proxy
323 response = self.session.post(login_url, params=args, headers=self.session.headers, proxies=proxy)
324 print response.content
325 print response.status_code
326 if response.status_code == 200:
327 response_status = response.json().get('success', False)
328 if response_status:
329 self.signature_data["app"].pop("-r",None)
330 self.signature_data["app"]["slider-show-cookie"] = self.session.cookies["SLIDER_SHOW_COOKIE"]
331 user_setting_dto = json.dumps(response.json()["result"]["userSettingDTO"])
332 gender = response.json()["result"]["gender"]
333 user_id = response.json()["result"]["userId"]
334 bio = user_data["bio"]
335 nick_name = user_data["nick_name"].decode('string-escape').decode("utf-8")
336 edit_url = "https://www.musical.ly/rest/v2/users/edit"
337 self.prepare_header(edit_url, "POST")
338 data = self.prepare_profile_data(user_setting_dto, bio, gender, handle, nick_name, user_id)
339 self.session.proxies = proxy
340 response = self.session.post(edit_url, headers = self.session.headers, data = data, proxies = proxy)
341 if response.status_code == 200:
342 response_status = response.json().get('success', False)
343 if response_status:
344 photo = user_data["photo"]
345 if not os.path(photo):
346 continue
347 post_photo_url = "https://www.musical.ly/rest/v2/users/signIcon"
348 payload = {
349 "endpoint":None,
350 "headers":None,
351 "length":8035,
352 "md5":hashlib.md5(str(user_id)+str(timestamp)).hexdigest(),
353 "method":None,
354 "parameters":None,
355 "presignedUrl":None,
356 "resourcePath":None
357 }
358 self.prepare_header(post_photo_url, "POST")
359 self.session.headers["Content-Type"] = "application/json; charset=utf-8"
360 response = self.session.post(post_url, data=json.dumps(payload))
361 if response.status_code == 200:
362 response_status = response.json().get('success', False)
363 if response_status:
364 put_photo_url = response.json()["result"]["endpoint"] + "/" + response.json()["result"]["resourcePath"]
365 self.prepare_header(put_photo_url, "PUT")
366 self.session.headers = response.json()["result"]["headers"]
367 with open(photo, "rb") as image_file:
368 encoded_string = base64.b64encode(image_file.read())
369 response = self.session.put(put_photo_url, data = encoded_string, proxies = proxy)
370 change_photo_url = "https://www.musical.ly/rest/v2/users/changeIcon"
371 prepare_header(change_photo_url, "POST")
372 payload = {"icon": put_photo_url}
373 response = session.post(change_photo_url, data = payload)
374
375
376 except Exception, e:
377 print proxy
378 print self.session.proxies
379 print e
380 self.session.cookies.clear()
381 self.unset_proxy()
382 pass
383
384 self.session.cookies.clear()
385 self.unset_proxy()
386
387 output_fp.close()