├── requirements.txt
├── README.md
├── spammy.py
└── InstagramAPI.py
/requirements.txt:
--------------------------------------------------------------------------------
1 | colorama
2 | discord_webhook
3 | requests_toolbelt
4 | datetime
5 | math
6 | copy
7 | hashlib
8 | uuid
9 | hmac
10 | datetime
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # spammy
2 | SPAMMY: A Python spamming tool, including SMS bomb, Instagram spammer, and more! - I'm not responsible for any damage caused by this!
3 |
4 | 
5 |
6 |
7 | # Requirements
8 | All requirements can be installed using the following command: `pip3 install -r requirements.txt`.
9 | If that command returns an error for you, type the following:
10 | ```
11 | pip3 install colorama
12 | pip3 install discord_webhook
13 | pip3 install requests_toolbelt
14 | pip3 install datetime
15 | pip3 install math
16 | pip3 install copy
17 | pip3 install hashlib
18 | pip3 install uuid
19 | pip3 install hmac
20 | pip3 install datetime
21 | ```
22 |
23 |
24 | # Options:
25 | 1 - SMS Bomb: **The SMS Bomb is currently working poorly because the russian SMS verification code API's are trash.**
26 |
27 |
28 |
29 | 2 - Instagram Spammer: **The Instagram spammer logs onto your account and quickly spams a message.**
30 |
31 |
32 |
33 | 3 - Email Spammer: **The email spammer logs onto your GMail/Yahoo account and quickly sends emails.**
34 |
35 |
36 |
37 | 4 - Discord Webhook Spammer: **The Discord webhook spammer quickly sends message to a Discord webhook URL.**
38 |
39 |
40 | Do you wnat to add a feature, patch, etc? Feel free to create a pull request!
41 |
42 |
43 | # Disclaimer:
44 | This tool may only be used in legal ways. I'm not responsible for any damage, trouble, drama, etc. caused by SPAMMY.
45 |
46 |
47 | # SMS Bomb:
48 | 
49 |
50 |
51 | # Instagram spammer:
52 | Image unavailable
53 |
54 |
55 | # Email spammer:
56 |
57 |
58 | 
59 |
60 |
61 | # Discord webhook spammer:
62 |
63 |
64 | 
65 |
66 |
67 |
68 | 
69 |
--------------------------------------------------------------------------------
/spammy.py:
--------------------------------------------------------------------------------
1 | from colorama import Fore, init
2 | from os import system, name
3 | from time import sleep
4 | from InstagramAPI import InstagramAPI
5 | from discord_webhook import DiscordWebhook, DiscordEmbed
6 | import requests
7 | import random
8 | import json
9 | import smtplib
10 |
11 | def clear():
12 | if name == 'nt':
13 | _ = system('cls')
14 |
15 | else:
16 | _ = system('clear')
17 |
18 | options = ['1', '2', '3', '4']
19 | banner = f"""
20 | {Fore.MAGENTA}
21 | https://www.github.com/XxB1a/spammy
22 | ███████╗██████╗ █████╗ ███╗ ███╗███╗ ███╗██╗ ██╗
23 | ██╔════╝██╔══██╗██╔══██╗████╗ ████║████╗ ████║╚██╗ ██╔╝
24 | ███████╗██████╔╝███████║██╔████╔██║██╔████╔██║ ╚████╔╝
25 | ╚════██║██╔═══╝ ██╔══██║██║╚██╔╝██║██║╚██╔╝██║ ╚██╔╝
26 | ███████║██║ ██║ ██║██║ ╚═╝ ██║██║ ╚═╝ ██║ ██║
27 | ╚══════╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ {Fore.RED} Made by:{Fore.GREEN} XxBiancaXx{Fore.MAGENTA}
28 | {Fore.RED} Discord:{Fore.GREEN} XxBiancaXx#4356 {Fore.MAGENTA}|{Fore.RED} IG:{Fore.GREEN} @moron420 {Fore.MAGENTA}|{Fore.RED} GitHub: {Fore.GREEN}@XxB1a
29 |
30 | {Fore.RED}[{Fore.MAGENTA}1{Fore.RED}]{Fore.GREEN} SMS Bomb (poor)
31 | {Fore.RED}[{Fore.MAGENTA}2{Fore.RED}]{Fore.GREEN} Instagram Spammer
32 | {Fore.RED}[{Fore.MAGENTA}3{Fore.RED}]{Fore.GREEN} Email spammer
33 | {Fore.RED}[{Fore.MAGENTA}4{Fore.RED}]{Fore.GREEN} Discord webhook spammer
34 | {Fore.RESET}"""
35 |
36 | def smsbomb():
37 | clear()
38 | print(f'{Fore.RED} [{Fore.GREEN}-{Fore.RED}] NOTE: DO NOT TYPE + WITH THE COUNTRY CODE! ALSO, 1 LOOP IS EQUAL TO 15 REQUESTS!')
39 | print(Fore.RESET)
40 |
41 | cc = int(input(f' {Fore.RED}[{Fore.GREEN}-{Fore.RED}]{Fore.GREEN} Country code:{Fore.MAGENTA} '))
42 | ph = int(input(f' {Fore.RED}[{Fore.GREEN}-{Fore.RED}]{Fore.GREEN} Phone number:{Fore.MAGENTA} '))
43 | tm = int(input(f" {Fore.RED}[{Fore.GREEN}-{Fore.RED}]{Fore.GREEN} Amount of loops:{Fore.MAGENTA} "))
44 | fp = int(str(f'{cc}{ph}'))
45 | cn = 0
46 |
47 | print(Fore.RESET)
48 | for i in range(tm):
49 | cn = cn + 1
50 | try:
51 | requests.post('https://eda.yandex/api/v1/user/request_authentication_code', json={"phone_number": f'+{fp}'})
52 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!\n')
53 |
54 | except:
55 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :(\n')
56 |
57 | cn = cn + 1
58 | try:
59 | requests.post('https://youla.ru/web-api/auth/request_code', data={'phone': fp})
60 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!\n')
61 |
62 | except:
63 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :(\n')
64 |
65 | cn = cn + 1
66 | try:
67 | requests.post(url=f'https://rutube.ru/api/accounts/sendpass/phone?phone=%2B{fp}')
68 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!\n')
69 |
70 | except:
71 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :(\n')
72 |
73 | cn = cn + 1
74 | try:
75 | requests.post(url=f'https://www.tvzavr.ru/api/3.1/sms/send_confirm_code?plf=tvz&phone={fp}&csrf_value=1148ec45f24c4090b3ec7882a57831af')
76 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!\n')
77 |
78 | except:
79 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :(\n')
80 |
81 | cn = cn + 1
82 | try:
83 | username = f"ThisSABot{random.randint(1023010, 129419930)}"
84 | password = f"BottiAcc_{random.randint(111, 9999)}@!"
85 | requests.post('https://passport.twitch.tv/register?trusted_request=true',json={"birthday": {"day": 11, "month": 11, "year": 1999},"client_id": "kd1unb4b3q4t58fwlpcbzcbnm76a8fp", "include_verification_code": True,"password": password, "phone_number": fp, "username": username})
86 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!\n')
87 |
88 | except:
89 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :(\n')
90 |
91 | cn = cn + 1
92 | try:
93 | requests.post('https://www.icq.com/smsreg/requestPhoneValidation.php',data={'msisdn': fp, "locale": 'en', 'countryCode': 'ru', 'version': '1',"k": "ic1rtwz1s1Hj1O0r", "r": "46763"})
94 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!\n')
95 |
96 | except:
97 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :(\n')
98 |
99 | cn = cn + 1
100 | try:
101 | requests.post('https://cloud.mail.ru/api/v2/notify/applink',json={"phone": "+" + fp, "api": 2, "email": "email", "x-email": "x-email"})
102 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!{Fore.RESET}\n')
103 |
104 | except:
105 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :({Fore.RESET}\n')
106 |
107 | cn = cn + 1
108 | try:
109 | requests.post("https://qlean.ru/clients-api/v2/sms_codes/auth/request_code",json = {"phone": fn})
110 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!{Fore.RESET}\n')
111 |
112 | except:
113 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :({Fore.RESET}\n')
114 |
115 | cn = cn + 1
116 | try:
117 | requests.post('https://app-api.kfc.ru/api/v1/common/auth/send-validation-sms', json={'phone': f"+{fn}"}, headers=HEADERS)
118 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!{Fore.RESET}\n')
119 |
120 | except:
121 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :({Fore.RESET}\n')
122 |
123 | cn = cn + 1
124 | try:
125 | requests.post("https://ok.ru/dk?cmd=AnonymRegistrationEnterPhone&st.cmd=anonymRegistrationEnterPhone", data = {"st.r.phone": f'+{fn}'})
126 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!{Fore.RESET}\n')
127 |
128 | except:
129 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :({Fore.RESET}\n')
130 |
131 | cn = cn + 1
132 | try:
133 | requests.post('https://youla.ru/web-api/auth/request_code', json = {"phone":f'+{fn}'})
134 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!{Fore.RESET}\n')
135 |
136 | except:
137 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :({Fore.RESET}\n')
138 |
139 | cn = cn + 1
140 | try:
141 | requests.post('https://eda.yandex/api/v1/user/request_authentication_code',json={"phone_number": f'+{fn}'})
142 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!{Fore.RESET}\n')
143 |
144 | except:
145 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :({Fore.RESET}\n')
146 |
147 | cn = cn + 1
148 | try:
149 | requests.post("https://api.ivi.ru/mobileapi/user/register/phone/v6", data= {"phone": number_7})
150 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!{Fore.RESET}\n')
151 |
152 | except:
153 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :({Fore.RESET}\n')
154 |
155 | cn = cn + 1
156 | try:
157 | requests.post("https://api.delitime.ru/api/v2/signup",data={"SignupForm[username]": fn, "SignupForm[device_type]": 3})
158 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!{Fore.RESET}\n')
159 |
160 | except:
161 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :({Fore.RESET}\n')
162 |
163 | cn = cn + 1
164 | try:
165 | requests.post('https://www.icq.com/smsreg/requestPhoneValidation.php',data={'msisdn': fn, "locale": 'en', 'countryCode': cc,'version': '1', "k": "ic1rtwz1s1Hj1O0r", "r": "46763"})
166 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!{Fore.RESET}\n')
167 |
168 | except:
169 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :({Fore.RESET}\n')
170 |
171 | cn = cn + 1
172 | try:
173 | requests.post('https://www.icq.com/smsreg/requestPhoneValidation.php',data={'msisdn': ph, "locale": 'en', 'countryCode': cc,'version': '1', "k": "ic1rtwz1s1Hj1O0r", "r": "46763"})
174 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} successfull!{Fore.RESET}\n')
175 |
176 | except:
177 | print(f' {Fore.RED}[-]{Fore.MAGENTA} sent MSG {Fore.GREEN}{cn}{Fore.MAGENTA} unsuccessfull! :({Fore.RESET}\n')
178 |
179 | clear()
180 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Finished the SMS bomb! Returning...')
181 | sleep(1.5)
182 | main()
183 |
184 | def igbomb():
185 | clear()
186 | print(f' {Fore.RED}[{Fore.GREEN}-{Fore.RED}] InstagramAPI.py by {Fore.GREEN}https://www.github.com/Gumbraise{Fore.RED}. Thank you a lot!')
187 | print(f' {Fore.RED}[{Fore.GREEN}-{Fore.RED}] This might fail because of a {Fore.CYAN}429 - Too Many Requests{Fore.RED}. This method works by logging into your account and spamming a message, therefore this does not work if you have 2FA enabled! Combolist compatibility will be added soon.')
188 | print(Fore.RESET)
189 |
190 | victim = str(input(f" {Fore.RED}[{Fore.GREEN}-{Fore.RED}]{Fore.MAGENTA} Victim's @:{Fore.GREEN} "))
191 | accie = str(input(f" {Fore.RED}[{Fore.GREEN}-{Fore.RED}]{Fore.MAGENTA} Your @:{Fore.GREEN} "))
192 | passie = str(input(f" {Fore.RED}[{Fore.GREEN}-{Fore.RED}]{Fore.MAGENTA} Your password:{Fore.GREEN} "))
193 | amount = int(input(f" {Fore.RED}[{Fore.GREEN}-{Fore.RED}]{Fore.MAGENTA} Amount of MSG's:{Fore.GREEN} "))
194 | message = str(input(f" {Fore.RED}[{Fore.GREEN}-{Fore.RED}]{Fore.MAGENTA} Message to spam:{Fore.GREEN} "))
195 | msgs = 0
196 |
197 | api = InstagramAPI(accie, passie)
198 | api.login()
199 |
200 | r = requests.get(f"https://www.instagram.com/{victim}/?__a=1")
201 | respJSON = r.json()
202 | victim_user_id = str(respJSON['graphql'].get("user").get("id"))
203 |
204 | print(Fore.RESET)
205 | for i in range(amount):
206 | msgs = msgs + 1
207 | api.sendMessage(victim_user_id, message)
208 | print(f" {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent {Fore.GREEN}'{message}'{Fore.MAGENTA} to victim {Fore.CYAN}@{victim} ({victim_user_id}){Fore.MAGENTA}. I sent {Fore.YELLOW}{msgs}{Fore.MAGENTA} in total!{Fore.RESET}\n")
209 |
210 | clear()
211 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Finished the DM Spamming! Returning...')
212 | sleep(1.5)
213 | main()
214 |
215 | def mailbomb():
216 | clear()
217 | print(f' {Fore.RED}[{Fore.GREEN}-{Fore.RED}] NOTE: PLEASE ENABLE "LESS SECURE APPS"! ALSO, THIS SPAMMER ONLY WORKS FOR GMAIL AND YAHOO. ALSO, GMAIL HAS A LIMIT OF 500 MAILS PER DAY! ALSO, THIS WORKS BY LOGGING ONTO YOUR EMAIL AND SPAMMING!')
218 | print(Fore.RESET)
219 | goy = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} GMail/Yahoo? [{Fore.CYAN}G{Fore.MAGENTA}/{Fore.CYAN}Y{Fore.MAGENTA}]:{Fore.GREEN} '))
220 | victim = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Victim\'s email:{Fore.GREEN} '))
221 | mail = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Your email:{Fore.GREEN} '))
222 | password = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Your password:{Fore.GREEN} '))
223 | subject = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Spam subject:{Fore.GREEN} '))
224 | body = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Message:{Fore.GREEN} '))
225 | amount = int(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Amount of mails:{Fore.GREEN} '))
226 | msgs = 0
227 |
228 | if goy == 'g' or goy == 'G':
229 | smtp_server = 'smtp.gmail.com'
230 | port = 587
231 | set_server = "gmail"
232 |
233 | elif goy == 'y' or goy == 'Y':
234 | smtp_server = 'smtp.mail.yahoo.com'
235 | port = 25
236 | set_server = "yahoo"
237 |
238 | else:
239 | print(f" {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} You didn't pick G or Y. Going with GMail!")
240 |
241 | smtp_server = 'smtp.gmail.com'
242 | port = 587
243 | set_server = "gmail"
244 |
245 | server = smtplib.SMTP(smtp_server, port)
246 | server.ehlo()
247 |
248 | if smtp_server == 'smtp.gmail.com':
249 | server.starttls()
250 |
251 | try:
252 | server.login(mail, password)
253 |
254 | except smtplib.SMTPAuthenticationError:
255 | clear()
256 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Invalid mail or password! Returning...')
257 | sleep(1.5)
258 | main()
259 |
260 | msg = f'From: {mail}\nSubject: {subject}\n{body}'
261 | print(Fore.RESET)
262 | for i in range(amount):
263 | server.sendmail(mail, victim, msg)
264 | msgs = msgs + 1
265 | print(f" {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent {Fore.GREEN}'{body}'{Fore.MAGENTA} to victim {Fore.CYAN}{victim}{Fore.MAGENTA}. I sent {Fore.YELLOW}{msgs}{Fore.MAGENTA} in total!{Fore.RESET}\n")
266 |
267 | server.quit()
268 |
269 | clear()
270 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Finished the email spamming! Returning...')
271 | sleep(1.5)
272 | main()
273 |
274 | def webhookspammer():
275 | clear()
276 | nom = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Message or Embed? [{Fore.CYAN}M{Fore.MAGENTA}/{Fore.CYAN}E{Fore.MAGENTA}]:{Fore.GREEN} '))
277 | msgs = 0
278 |
279 | if nom == 'm' or nom == 'M':
280 | webhook_url = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Webhook URL:{Fore.GREEN} '))
281 | message = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Message:{Fore.GREEN} '))
282 | amount = int(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Amount:{Fore.GREEN} '))
283 |
284 | webhook = DiscordWebhook(url=webhook_url, content=message)
285 |
286 | elif nom == 'e' or nom == 'E':
287 | webhook_url = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Webhook URL:{Fore.GREEN} '))
288 | embed_title = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Embed title:{Fore.GREEN} '))
289 | embed_description = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Embed discription:{Fore.GREEN} '))
290 | amount = int(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Amount:{Fore.GREEN} '))
291 |
292 | webhook = DiscordWebhook(url=webhook_url)
293 | embed = DiscordEmbed(title=embed_title, description=embed_description, color=242424)
294 | webhook.add_embed(embed)
295 |
296 | else:
297 | print(f'\n {Fore.RED}[-]Invalid option! Going with M... ')
298 | nom = 'M'
299 |
300 | webhook_url = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Webhook URL:{Fore.GREEN} '))
301 | message = str(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Message:{Fore.GREEN} '))
302 | amount = int(input(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Amount:{Fore.GREEN} '))
303 |
304 | webhook = DiscordWebhook(url=webhook_url, content=message)
305 |
306 | print(Fore.RESET)
307 |
308 | for i in range(amount):
309 | response = webhook.execute()
310 | msgs = msgs + 1
311 |
312 | if nom == 'm' or nom == 'M':
313 | print(f" {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent {Fore.GREEN}'{message}'{Fore.MAGENTA} to webhook {Fore.CYAN}{webhook_url}{Fore.MAGENTA}. I sent {Fore.YELLOW}{msgs}{Fore.MAGENTA} in total!{Fore.RESET}\n")
314 |
315 | else:
316 | print(f" {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} sent {Fore.GREEN}'{embed_description}'{Fore.MAGENTA} to webhook {Fore.CYAN}{webhook_url}{Fore.MAGENTA}. I sent {Fore.YELLOW}{msgs}{Fore.MAGENTA} in total!{Fore.RESET}\n")
317 |
318 | clear()
319 | print(f' {Fore.RED}[{Fore.GREEN}+{Fore.RED}]{Fore.MAGENTA} Finished the webhook spamming! Returning...')
320 | sleep(1.5)
321 | main()
322 |
323 | def main():
324 | clear()
325 | print(f'{banner}')
326 | option = str(input(f' {Fore.RED}[{Fore.MAGENTA}-{Fore.RED}]{Fore.GREEN} Your choice:{Fore.MAGENTA} '))
327 | print(Fore.RESET)
328 |
329 | if option not in options:
330 | clear()
331 | print(f'{Fore.RED} Option {Fore.GREEN}{option}{Fore.RED} is not valid! Returning in {Fore.GREEN}5{Fore.RED} Seconds!{Fore.RESET}')
332 | sleep(1)
333 |
334 | clear()
335 | print(f'{Fore.RED} Option {Fore.GREEN}{option}{Fore.RED} is not valid! Returning in {Fore.GREEN}4{Fore.RED} Seconds!{Fore.RESET}')
336 | sleep(1)
337 |
338 | clear()
339 | print(f'{Fore.RED} Option {Fore.GREEN}{option}{Fore.RED} is not valid! Returning in {Fore.GREEN}3{Fore.RED} Seconds!{Fore.RESET}')
340 | sleep(1)
341 |
342 | clear()
343 | print(f'{Fore.RED} Option {Fore.GREEN}{option}{Fore.RED} is not valid! Returning in {Fore.GREEN}2{Fore.RED} Seconds!{Fore.RESET}')
344 | sleep(1)
345 |
346 | clear()
347 | print(f'{Fore.RED} Option {Fore.GREEN}{option}{Fore.RED} is not valid! Returning in {Fore.GREEN}1{Fore.RED} Second!{Fore.RESET}')
348 | sleep(1)
349 |
350 | clear()
351 | print(f'{Fore.RED} Returning!')
352 | main()
353 |
354 | else:
355 | if option == '1':
356 | smsbomb()
357 |
358 | elif option == '2':
359 | igbomb()
360 |
361 | elif option == '3':
362 | mailbomb()
363 |
364 | elif option == '4':
365 | webhookspammer()
366 |
367 | if __name__ == '__main__':
368 | init(convert=True)
369 | main()
370 |
--------------------------------------------------------------------------------
/InstagramAPI.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 |
4 | import requests
5 | import random
6 | import json
7 | import hashlib
8 | import hmac
9 | import urllib
10 | import uuid
11 | import time
12 | import copy
13 | import math
14 | import sys
15 | from datetime import datetime
16 | import calendar
17 | import os
18 | from requests_toolbelt import MultipartEncoder
19 |
20 | # Turn off InsecureRequestWarning
21 | from requests.packages.urllib3.exceptions import InsecureRequestWarning
22 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
23 |
24 |
25 | # The urllib library was split into other modules from Python 2 to Python 3
26 | if sys.version_info.major == 3:
27 | import urllib.parse
28 |
29 |
30 |
31 | class InstagramAPI:
32 | API_URL = 'https://i.instagram.com/api/v1/'
33 | DEVICE_SETTINTS = {'manufacturer': 'Xiaomi',
34 | 'model': 'HM 1SW',
35 | 'android_version': 18,
36 | 'android_release': '4.3'}
37 | USER_AGENT = 'Instagram 10.26.0 Android ({android_version}/{android_release}; 320dpi; 720x1280; {manufacturer}; {model}; armani; qcom; en_US)'.format(**DEVICE_SETTINTS)
38 | IG_SIG_KEY = '4f8732eb9ba7d1c8e8897a75d6474d4eb3f5279137431b2aafb71fafe2abe178'
39 | EXPERIMENTS = 'ig_promote_reach_objective_fix_universe,ig_android_universe_video_production,ig_search_client_h1_2017_holdout,ig_android_live_follow_from_comments_universe,ig_android_carousel_non_square_creation,ig_android_live_analytics,ig_android_follow_all_dialog_confirmation_copy,ig_android_stories_server_coverframe,ig_android_video_captions_universe,ig_android_offline_location_feed,ig_android_direct_inbox_retry_seen_state,ig_android_ontact_invite_universe,ig_android_live_broadcast_blacklist,ig_android_insta_video_reconnect_viewers,ig_android_ad_async_ads_universe,ig_android_search_clear_layout_universe,ig_android_shopping_reporting,ig_android_stories_surface_universe,ig_android_verified_comments_universe,ig_android_preload_media_ahead_in_current_reel,android_instagram_prefetch_suggestions_universe,ig_android_reel_viewer_fetch_missing_reels_universe,ig_android_direct_search_share_sheet_universe,ig_android_business_promote_tooltip,ig_android_direct_blue_tab,ig_android_async_network_tweak_universe,ig_android_elevate_main_thread_priority_universe,ig_android_stories_gallery_nux,ig_android_instavideo_remove_nux_comments,ig_video_copyright_whitelist,ig_react_native_inline_insights_with_relay,ig_android_direct_thread_message_animation,ig_android_draw_rainbow_client_universe,ig_android_direct_link_style,ig_android_live_heart_enhancements_universe,ig_android_rtc_reshare,ig_android_preload_item_count_in_reel_viewer_buffer,ig_android_users_bootstrap_service,ig_android_auto_retry_post_mode,ig_android_shopping,ig_android_main_feed_seen_state_dont_send_info_on_tail_load,ig_fbns_preload_default,ig_android_gesture_dismiss_reel_viewer,ig_android_tool_tip,ig_android_ad_logger_funnel_logging_universe,ig_android_gallery_grid_column_count_universe,ig_android_business_new_ads_payment_universe,ig_android_direct_links,ig_android_audience_control,ig_android_live_encore_consumption_settings_universe,ig_perf_android_holdout,ig_android_cache_contact_import_list,ig_android_links_receivers,ig_android_ad_impression_backtest,ig_android_list_redesign,ig_android_stories_separate_overlay_creation,ig_android_stop_video_recording_fix_universe,ig_android_render_video_segmentation,ig_android_live_encore_reel_chaining_universe,ig_android_sync_on_background_enhanced_10_25,ig_android_immersive_viewer,ig_android_mqtt_skywalker,ig_fbns_push,ig_android_ad_watchmore_overlay_universe,ig_android_react_native_universe,ig_android_profile_tabs_redesign_universe,ig_android_live_consumption_abr,ig_android_story_viewer_social_context,ig_android_hide_post_in_feed,ig_android_video_loopcount_int,ig_android_enable_main_feed_reel_tray_preloading,ig_android_camera_upsell_dialog,ig_android_ad_watchbrowse_universe,ig_android_internal_research_settings,ig_android_search_people_tag_universe,ig_android_react_native_ota,ig_android_enable_concurrent_request,ig_android_react_native_stories_grid_view,ig_android_business_stories_inline_insights,ig_android_log_mediacodec_info,ig_android_direct_expiring_media_loading_errors,ig_video_use_sve_universe,ig_android_cold_start_feed_request,ig_android_enable_zero_rating,ig_android_reverse_audio,ig_android_branded_content_three_line_ui_universe,ig_android_live_encore_production_universe,ig_stories_music_sticker,ig_android_stories_teach_gallery_location,ig_android_http_stack_experiment_2017,ig_android_stories_device_tilt,ig_android_pending_request_search_bar,ig_android_fb_topsearch_sgp_fork_request,ig_android_seen_state_with_view_info,ig_android_animation_perf_reporter_timeout,ig_android_new_block_flow,ig_android_story_tray_title_play_all_v2,ig_android_direct_address_links,ig_android_stories_archive_universe,ig_android_save_collections_cover_photo,ig_android_live_webrtc_livewith_production,ig_android_sign_video_url,ig_android_stories_video_prefetch_kb,ig_android_stories_create_flow_favorites_tooltip,ig_android_live_stop_broadcast_on_404,ig_android_live_viewer_invite_universe,ig_android_promotion_feedback_channel,ig_android_render_iframe_interval,ig_android_accessibility_logging_universe,ig_android_camera_shortcut_universe,ig_android_use_one_cookie_store_per_user_override,ig_profile_holdout_2017_universe,ig_android_stories_server_brushes,ig_android_ad_media_url_logging_universe,ig_android_shopping_tag_nux_text_universe,ig_android_comments_single_reply_universe,ig_android_stories_video_loading_spinner_improvements,ig_android_collections_cache,ig_android_comment_api_spam_universe,ig_android_facebook_twitter_profile_photos,ig_android_shopping_tag_creation_universe,ig_story_camera_reverse_video_experiment,ig_android_direct_bump_selected_recipients,ig_android_ad_cta_haptic_feedback_universe,ig_android_vertical_share_sheet_experiment,ig_android_family_bridge_share,ig_android_search,ig_android_insta_video_consumption_titles,ig_android_stories_gallery_preview_button,ig_android_fb_auth_education,ig_android_camera_universe,ig_android_me_only_universe,ig_android_instavideo_audio_only_mode,ig_android_user_profile_chaining_icon,ig_android_live_video_reactions_consumption_universe,ig_android_stories_hashtag_text,ig_android_post_live_badge_universe,ig_android_swipe_fragment_container,ig_android_search_users_universe,ig_android_live_save_to_camera_roll_universe,ig_creation_growth_holdout,ig_android_sticker_region_tracking,ig_android_unified_inbox,ig_android_live_new_watch_time,ig_android_offline_main_feed_10_11,ig_import_biz_contact_to_page,ig_android_live_encore_consumption_universe,ig_android_experimental_filters,ig_android_search_client_matching_2,ig_android_react_native_inline_insights_v2,ig_android_business_conversion_value_prop_v2,ig_android_redirect_to_low_latency_universe,ig_android_ad_show_new_awr_universe,ig_family_bridges_holdout_universe,ig_android_background_explore_fetch,ig_android_following_follower_social_context,ig_android_video_keep_screen_on,ig_android_ad_leadgen_relay_modern,ig_android_profile_photo_as_media,ig_android_insta_video_consumption_infra,ig_android_ad_watchlead_universe,ig_android_direct_prefetch_direct_story_json,ig_android_shopping_react_native,ig_android_top_live_profile_pics_universe,ig_android_direct_phone_number_links,ig_android_stories_weblink_creation,ig_android_direct_search_new_thread_universe,ig_android_histogram_reporter,ig_android_direct_on_profile_universe,ig_android_network_cancellation,ig_android_background_reel_fetch,ig_android_react_native_insights,ig_android_insta_video_audio_encoder,ig_android_family_bridge_bookmarks,ig_android_data_usage_network_layer,ig_android_universal_instagram_deep_links,ig_android_dash_for_vod_universe,ig_android_modular_tab_discover_people_redesign,ig_android_mas_sticker_upsell_dialog_universe,ig_android_ad_add_per_event_counter_to_logging_event,ig_android_sticky_header_top_chrome_optimization,ig_android_rtl,ig_android_biz_conversion_page_pre_select,ig_android_promote_from_profile_button,ig_android_live_broadcaster_invite_universe,ig_android_share_spinner,ig_android_text_action,ig_android_own_reel_title_universe,ig_promotions_unit_in_insights_landing_page,ig_android_business_settings_header_univ,ig_android_save_longpress_tooltip,ig_android_constrain_image_size_universe,ig_android_business_new_graphql_endpoint_universe,ig_ranking_following,ig_android_stories_profile_camera_entry_point,ig_android_universe_reel_video_production,ig_android_power_metrics,ig_android_sfplt,ig_android_offline_hashtag_feed,ig_android_live_skin_smooth,ig_android_direct_inbox_search,ig_android_stories_posting_offline_ui,ig_android_sidecar_video_upload_universe,ig_android_promotion_manager_entry_point_universe,ig_android_direct_reply_audience_upgrade,ig_android_swipe_navigation_x_angle_universe,ig_android_offline_mode_holdout,ig_android_live_send_user_location,ig_android_direct_fetch_before_push_notif,ig_android_non_square_first,ig_android_insta_video_drawing,ig_android_swipeablefilters_universe,ig_android_live_notification_control_universe,ig_android_analytics_logger_running_background_universe,ig_android_save_all,ig_android_reel_viewer_data_buffer_size,ig_direct_quality_holdout_universe,ig_android_family_bridge_discover,ig_android_react_native_restart_after_error_universe,ig_android_startup_manager,ig_story_tray_peek_content_universe,ig_android_profile,ig_android_high_res_upload_2,ig_android_http_service_same_thread,ig_android_scroll_to_dismiss_keyboard,ig_android_remove_followers_universe,ig_android_skip_video_render,ig_android_story_timestamps,ig_android_live_viewer_comment_prompt_universe,ig_profile_holdout_universe,ig_android_react_native_insights_grid_view,ig_stories_selfie_sticker,ig_android_stories_reply_composer_redesign,ig_android_streamline_page_creation,ig_explore_netego,ig_android_ig4b_connect_fb_button_universe,ig_android_feed_util_rect_optimization,ig_android_rendering_controls,ig_android_os_version_blocking,ig_android_encoder_width_safe_multiple_16,ig_search_new_bootstrap_holdout_universe,ig_android_snippets_profile_nux,ig_android_e2e_optimization_universe,ig_android_comments_logging_universe,ig_shopping_insights,ig_android_save_collections,ig_android_live_see_fewer_videos_like_this_universe,ig_android_show_new_contact_import_dialog,ig_android_live_view_profile_from_comments_universe,ig_fbns_blocked,ig_formats_and_feedbacks_holdout_universe,ig_android_reduce_view_pager_buffer,ig_android_instavideo_periodic_notif,ig_search_user_auto_complete_cache_sync_ttl,ig_android_marauder_update_frequency,ig_android_suggest_password_reset_on_oneclick_login,ig_android_promotion_entry_from_ads_manager_universe,ig_android_live_special_codec_size_list,ig_android_enable_share_to_messenger,ig_android_background_main_feed_fetch,ig_android_live_video_reactions_creation_universe,ig_android_channels_home,ig_android_sidecar_gallery_universe,ig_android_upload_reliability_universe,ig_migrate_mediav2_universe,ig_android_insta_video_broadcaster_infra_perf,ig_android_business_conversion_social_context,android_ig_fbns_kill_switch,ig_android_live_webrtc_livewith_consumption,ig_android_destroy_swipe_fragment,ig_android_react_native_universe_kill_switch,ig_android_stories_book_universe,ig_android_all_videoplayback_persisting_sound,ig_android_draw_eraser_universe,ig_direct_search_new_bootstrap_holdout_universe,ig_android_cache_layer_bytes_threshold,ig_android_search_hash_tag_and_username_universe,ig_android_business_promotion,ig_android_direct_search_recipients_controller_universe,ig_android_ad_show_full_name_universe,ig_android_anrwatchdog,ig_android_qp_kill_switch,ig_android_2fac,ig_direct_bypass_group_size_limit_universe,ig_android_promote_simplified_flow,ig_android_share_to_whatsapp,ig_android_hide_bottom_nav_bar_on_discover_people,ig_fbns_dump_ids,ig_android_hands_free_before_reverse,ig_android_skywalker_live_event_start_end,ig_android_live_join_comment_ui_change,ig_android_direct_search_story_recipients_universe,ig_android_direct_full_size_gallery_upload,ig_android_ad_browser_gesture_control,ig_channel_server_experiments,ig_android_video_cover_frame_from_original_as_fallback,ig_android_ad_watchinstall_universe,ig_android_ad_viewability_logging_universe,ig_android_new_optic,ig_android_direct_visual_replies,ig_android_stories_search_reel_mentions_universe,ig_android_threaded_comments_universe,ig_android_mark_reel_seen_on_Swipe_forward,ig_internal_ui_for_lazy_loaded_modules_experiment,ig_fbns_shared,ig_android_capture_slowmo_mode,ig_android_live_viewers_list_search_bar,ig_android_video_single_surface,ig_android_offline_reel_feed,ig_android_video_download_logging,ig_android_last_edits,ig_android_exoplayer_4142,ig_android_post_live_viewer_count_privacy_universe,ig_android_activity_feed_click_state,ig_android_snippets_haptic_feedback,ig_android_gl_drawing_marks_after_undo_backing,ig_android_mark_seen_state_on_viewed_impression,ig_android_live_backgrounded_reminder_universe,ig_android_live_hide_viewer_nux_universe,ig_android_live_monotonic_pts,ig_android_search_top_search_surface_universe,ig_android_user_detail_endpoint,ig_android_location_media_count_exp_ig,ig_android_comment_tweaks_universe,ig_android_ad_watchmore_entry_point_universe,ig_android_top_live_notification_universe,ig_android_add_to_last_post,ig_save_insights,ig_android_live_enhanced_end_screen_universe,ig_android_ad_add_counter_to_logging_event,ig_android_blue_token_conversion_universe,ig_android_exoplayer_settings,ig_android_progressive_jpeg,ig_android_offline_story_stickers,ig_android_gqls_typing_indicator,ig_android_chaining_button_tooltip,ig_android_video_prefetch_for_connectivity_type,ig_android_use_exo_cache_for_progressive,ig_android_samsung_app_badging,ig_android_ad_holdout_watchandmore_universe,ig_android_offline_commenting,ig_direct_stories_recipient_picker_button,ig_insights_feedback_channel_universe,ig_android_insta_video_abr_resize,ig_android_insta_video_sound_always_on'''
40 | SIG_KEY_VERSION = '4'
41 |
42 | # username # Instagram username
43 | # password # Instagram password
44 | # debug # Debug
45 | # uuid # UUID
46 | # device_id # Device ID
47 | # username_id # Username ID
48 | # token # _csrftoken
49 | # isLoggedIn # Session status
50 | # rank_token # Rank token
51 | # IGDataPath # Data storage path
52 |
53 | def __init__(self, username, password, debug=False, IGDataPath=None):
54 | m = hashlib.md5()
55 | m.update(username.encode('utf-8') + password.encode('utf-8'))
56 | self.device_id = self.generateDeviceId(m.hexdigest())
57 | self.setUser(username, password)
58 | self.isLoggedIn = False
59 | self.LastResponse = None
60 | self.s = requests.Session()
61 |
62 | def setUser(self, username, password):
63 | self.username = username
64 | self.password = password
65 | self.uuid = self.generateUUID(True)
66 |
67 | def setProxy(self, proxy=None):
68 | """Set proxy for all requests::
69 | Proxy format - user:password@ip:port"""
70 |
71 | if proxy is not None:
72 | print('Set proxy!')
73 | proxies = {'http': 'http://' + proxy, 'https': 'http://' + proxy}
74 | self.s.proxies.update(proxies)
75 |
76 | def login(self, force=False):
77 | if (not self.isLoggedIn or force):
78 | if (self.SendRequest('si/fetch_headers/?challenge_type=signup&guid=' + self.generateUUID(False), None, True)):
79 |
80 | data = {'phone_id': self.generateUUID(True),
81 | '_csrftoken': self.LastResponse.cookies['csrftoken'],
82 | 'username': self.username,
83 | 'guid': self.uuid,
84 | 'device_id': self.device_id,
85 | 'password': self.password,
86 | 'login_attempt_count': '0'}
87 |
88 | if (self.SendRequest('accounts/login/', self.generateSignature(json.dumps(data)), True)):
89 | self.isLoggedIn = True
90 | self.username_id = self.LastJson["logged_in_user"]["pk"]
91 | self.rank_token = "%s_%s" % (self.username_id, self.uuid)
92 | self.token = self.LastResponse.cookies["csrftoken"]
93 | self.syncFeatures()
94 | self.autoCompleteUserList()
95 | self.timelineFeed()
96 | self.getv2Inbox()
97 | self.getRecentActivity()
98 | print("Login success!\n")
99 | return True
100 | else:
101 | print("\nWhoops. Couldn't login. Check the above flags to know more.")
102 | exit(1)
103 |
104 | def syncFeatures(self):
105 | data = json.dumps({'_uuid': self.uuid,
106 | '_uid': self.username_id,
107 | 'id': self.username_id,
108 | '_csrftoken': self.token,
109 | 'experiments': self.EXPERIMENTS})
110 | return self.SendRequest('qe/sync/', self.generateSignature(data))
111 |
112 | def autoCompleteUserList(self):
113 | return self.SendRequest('friendships/autocomplete_user_list/')
114 |
115 | def timelineFeed(self):
116 | return self.SendRequest('feed/timeline/')
117 |
118 | def megaphoneLog(self):
119 | return self.SendRequest('megaphone/log/')
120 |
121 | def expose(self):
122 | data = json.dumps({'_uuid': self.uuid,
123 | '_uid': self.username_id,
124 | 'id': self.username_id,
125 | '_csrftoken': self.token,
126 | 'experiment': 'ig_android_profile_contextual_feed'})
127 | return self.SendRequest('qe/expose/', self.generateSignature(data))
128 |
129 | def uploadPhoto(self, photo, caption=None, upload_id=None, is_sidecar=None):
130 | if upload_id is None:
131 | upload_id = str(int(time.time() * 1000))
132 | data = {'upload_id': upload_id,
133 | '_uuid': self.uuid,
134 | '_csrftoken': self.token,
135 | 'image_compression': '{"lib_name":"jt","lib_version":"1.3.0","quality":"87"}',
136 | 'photo': ('pending_media_%s.jpg' % upload_id, open(photo, 'rb'), 'application/octet-stream', {'Content-Transfer-Encoding': 'binary'})}
137 | if is_sidecar:
138 | data['is_sidecar'] = '1'
139 | m = MultipartEncoder(data, boundary=self.uuid)
140 | self.s.headers.update({'X-IG-Capabilities': '3Q4=',
141 | 'X-IG-Connection-Type': 'WIFI',
142 | 'Cookie2': '$Version=1',
143 | 'Accept-Language': 'en-US',
144 | 'Accept-Encoding': 'gzip, deflate',
145 | 'Content-type': m.content_type,
146 | 'Connection': 'close',
147 | 'User-Agent': self.USER_AGENT})
148 | response = self.s.post(self.API_URL + "upload/photo/", data=m.to_string())
149 | if response.status_code == 200:
150 | if self.configure(upload_id, photo, caption):
151 | self.expose()
152 | return False
153 |
154 | def uploadVideo(self, video, thumbnail, caption=None, upload_id=None, is_sidecar=None):
155 | if upload_id is None:
156 | upload_id = str(int(time.time() * 1000))
157 | data = {'upload_id': upload_id,
158 | '_csrftoken': self.token,
159 | 'media_type': '2',
160 | '_uuid': self.uuid}
161 | if is_sidecar:
162 | data['is_sidecar'] = '1'
163 | m = MultipartEncoder(data, boundary=self.uuid)
164 | self.s.headers.update({'X-IG-Capabilities': '3Q4=',
165 | 'X-IG-Connection-Type': 'WIFI',
166 | 'Host': 'i.instagram.com',
167 | 'Cookie2': '$Version=1',
168 | 'Accept-Language': 'en-US',
169 | 'Accept-Encoding': 'gzip, deflate',
170 | 'Content-type': m.content_type,
171 | 'Connection': 'keep-alive',
172 | 'User-Agent': self.USER_AGENT})
173 | response = self.s.post(self.API_URL + "upload/video/", data=m.to_string())
174 | if response.status_code == 200:
175 | body = json.loads(response.text)
176 | upload_url = body['video_upload_urls'][3]['url']
177 | upload_job = body['video_upload_urls'][3]['job']
178 |
179 | videoData = open(video, 'rb').read()
180 | # solve issue #85 TypeError: slice indices must be integers or None or have an __index__ method
181 | request_size = int(math.floor(len(videoData) / 4))
182 | lastRequestExtra = (len(videoData) - (request_size * 3))
183 |
184 | headers = copy.deepcopy(self.s.headers)
185 | self.s.headers.update({'X-IG-Capabilities': '3Q4=',
186 | 'X-IG-Connection-Type': 'WIFI',
187 | 'Cookie2': '$Version=1',
188 | 'Accept-Language': 'en-US',
189 | 'Accept-Encoding': 'gzip, deflate',
190 | 'Content-type': 'application/octet-stream',
191 | 'Session-ID': upload_id,
192 | 'Connection': 'keep-alive',
193 | 'Content-Disposition': 'attachment; filename="video.mov"',
194 | 'job': upload_job,
195 | 'Host': 'upload.instagram.com',
196 | 'User-Agent': self.USER_AGENT})
197 | for i in range(0, 4):
198 | start = i * request_size
199 | if i == 3:
200 | end = i * request_size + lastRequestExtra
201 | else:
202 | end = (i + 1) * request_size
203 | length = lastRequestExtra if i == 3 else request_size
204 | content_range = "bytes {start}-{end}/{lenVideo}".format(start=start, end=(end - 1),
205 | lenVideo=len(videoData)).encode('utf-8')
206 |
207 | self.s.headers.update({'Content-Length': str(end - start), 'Content-Range': content_range, })
208 | response = self.s.post(upload_url, data=videoData[start:start + length])
209 | self.s.headers = headers
210 |
211 | if response.status_code == 200:
212 | if self.configureVideo(upload_id, video, thumbnail, caption):
213 | self.expose()
214 | return False
215 |
216 | def uploadAlbum(self, media, caption=None, upload_id=None):
217 | if not media:
218 | raise Exception("List of media to upload can't be empty.")
219 |
220 | if len(media) < 2 or len(media) > 10:
221 | raise Exception('Instagram requires that albums contain 2-10 items. You tried to submit {}.'.format(len(media)))
222 |
223 | # Figure out the media file details for ALL media in the album.
224 | # NOTE: We do this first, since it validates whether the media files are
225 | # valid and lets us avoid wasting time uploading totally invalid albums!
226 | for idx, item in enumerate(media):
227 | if not item.get('file', '') or item.get('tipe', ''):
228 | raise Exception('Media at index "{}" does not have the required "file" and "type" keys.'.format(idx))
229 |
230 | # $itemInternalMetadata = new InternalMetadata();
231 | # If usertags are provided, verify that the entries are valid.
232 | if item.get('usertags', []):
233 | self.throwIfInvalidUsertags(item['usertags'])
234 |
235 | # Pre-process media details and throw if not allowed on Instagram.
236 | if item.get('type', '') == 'photo':
237 | # Determine the photo details.
238 | # $itemInternalMetadata->setPhotoDetails(Constants::FEED_TIMELINE_ALBUM, $item['file']);
239 | pass
240 |
241 | elif item.get('type', '') == 'video':
242 | # Determine the video details.
243 | # $itemInternalMetadata->setVideoDetails(Constants::FEED_TIMELINE_ALBUM, $item['file']);
244 | pass
245 |
246 | else:
247 | raise Exception('Unsupported album media type "{}".'.format(item['type']))
248 |
249 | itemInternalMetadata = {}
250 | item['internalMetadata'] = itemInternalMetadata
251 |
252 | # Perform all media file uploads.
253 | for idx, item in enumerate(media):
254 | itemInternalMetadata = item['internalMetadata']
255 | item_upload_id = self.generateUploadId()
256 | if item.get('type', '') == 'photo':
257 | self.uploadPhoto(item['file'], caption=caption, is_sidecar=True, upload_id=item_upload_id)
258 | # $itemInternalMetadata->setPhotoUploadResponse($this->ig->internal->uploadPhotoData(Constants::FEED_TIMELINE_ALBUM, $itemInternalMetadata));
259 |
260 | elif item.get('type', '') == 'video':
261 | # Attempt to upload the video data.
262 | self.uploadVideo(item['file'], item['thumbnail'], caption=caption, is_sidecar=True, upload_id=item_upload_id)
263 | # $itemInternalMetadata = $this->ig->internal->uploadVideo(Constants::FEED_TIMELINE_ALBUM, $item['file'], $itemInternalMetadata);
264 | # Attempt to upload the thumbnail, associated with our video's ID.
265 | # $itemInternalMetadata->setPhotoUploadResponse($this->ig->internal->uploadPhotoData(Constants::FEED_TIMELINE_ALBUM, $itemInternalMetadata));
266 | pass
267 | item['internalMetadata']['upload_id'] = item_upload_id
268 |
269 | albumInternalMetadata = {}
270 | return self.configureTimelineAlbum(media, albumInternalMetadata, captionText=caption)
271 |
272 | def throwIfInvalidUsertags(self, usertags):
273 | for user_position in usertags:
274 | # Verify this usertag entry, ensuring that the entry is format
275 | # ['position'=>[0.0,1.0],'user_id'=>'123'] and nothing else.
276 | correct = True
277 | if isinstance(user_position, dict):
278 | position = user_position.get('position', None)
279 | user_id = user_position.get('user_id', None)
280 |
281 | if isinstance(position, list) and len(position) == 2:
282 | try:
283 | x = float(position[0])
284 | y = float(position[1])
285 | if x < 0.0 or x > 1.0:
286 | correct = False
287 | if y < 0.0 or y > 1.0:
288 | correct = False
289 | except:
290 | correct = False
291 | try:
292 | user_id = long(user_id)
293 | if user_id < 0:
294 | correct = False
295 | except:
296 | correct = False
297 | if not correct:
298 | raise Exception('Invalid user entry in usertags array.')
299 |
300 | def configureTimelineAlbum(self, media, albumInternalMetadata, captionText='', location=None):
301 | endpoint = 'media/configure_sidecar/'
302 | albumUploadId = self.generateUploadId()
303 |
304 | date = datetime.utcnow().isoformat()
305 | childrenMetadata = []
306 | for item in media:
307 | itemInternalMetadata = item['internalMetadata']
308 | uploadId = itemInternalMetadata.get('upload_id', self.generateUploadId())
309 | if item.get('type', '') == 'photo':
310 | # Build this item's configuration.
311 | photoConfig = {'date_time_original': date,
312 | 'scene_type': 1,
313 | 'disable_comments': False,
314 | 'upload_id': uploadId,
315 | 'source_type': 0,
316 | 'scene_capture_type': 'standard',
317 | 'date_time_digitized': date,
318 | 'geotag_enabled': False,
319 | 'camera_position': 'back',
320 | 'edits': {'filter_strength': 1,
321 | 'filter_name': 'IGNormalFilter'}
322 | }
323 | # This usertag per-file EXTERNAL metadata is only supported for PHOTOS!
324 | if item.get('usertags', []):
325 | # NOTE: These usertags were validated in Timeline::uploadAlbum.
326 | photoConfig['usertags'] = json.dumps({'in': item['usertags']})
327 |
328 | childrenMetadata.append(photoConfig)
329 | if item.get('type', '') == 'video':
330 | # Get all of the INTERNAL per-VIDEO metadata.
331 | videoDetails = itemInternalMetadata.get('video_details', {})
332 | # Build this item's configuration.
333 | videoConfig = {'length': videoDetails.get('duration', 1.0),
334 | 'date_time_original': date,
335 | 'scene_type': 1,
336 | 'poster_frame_index': 0,
337 | 'trim_type': 0,
338 | 'disable_comments': False,
339 | 'upload_id': uploadId,
340 | 'source_type': 'library',
341 | 'geotag_enabled': False,
342 | 'edits': {
343 | 'length': videoDetails.get('duration', 1.0),
344 | 'cinema': 'unsupported',
345 | 'original_length': videoDetails.get('duration', 1.0),
346 | 'source_type': 'library',
347 | 'start_time': 0,
348 | 'camera_position': 'unknown',
349 | 'trim_type': 0}
350 | }
351 |
352 | childrenMetadata.append(videoConfig)
353 | # Build the request...
354 | data = {'_csrftoken': self.token,
355 | '_uid': self.username_id,
356 | '_uuid': self.uuid,
357 | 'client_sidecar_id': albumUploadId,
358 | 'caption': captionText,
359 | 'children_metadata': childrenMetadata}
360 | self.SendRequest(endpoint, self.generateSignature(json.dumps(data)))
361 | response = self.LastResponse
362 | if response.status_code == 200:
363 | self.LastResponse = response
364 | self.LastJson = json.loads(response.text)
365 | return True
366 | else:
367 | print("Request return " + str(response.status_code) + " error!")
368 | # for debugging
369 | try:
370 | self.LastResponse = response
371 | self.LastJson = json.loads(response.text)
372 | except ValueError:
373 | return False
374 |
375 | def sendMessage(self, target_user, msgText):
376 | target_user = '[[{}]]'.format(','.join([target_user]))
377 | url = 'direct_v2/threads/broadcast/text/'
378 | data = {
379 | 'text': msgText,
380 | '_uuid': self.uuid,
381 | '_csrftoken': self.token,
382 | 'recipient_users': target_user,
383 | '_uid': self.username_id,
384 | 'action': 'send_item',
385 | 'client_context': self.generateUUID(True)}
386 | return self.SendRequest(url, data)
387 |
388 | def direct_share(self, media_id, recipients, text=None):
389 | if not isinstance(position, list):
390 | recipients = [str(recipients)]
391 | recipient_users = '"",""'.join(str(r) for r in recipients)
392 | endpoint = 'direct_v2/threads/broadcast/media_share/?media_type=photo'
393 | boundary = self.uuid
394 | bodies = [
395 | {
396 | 'type': 'form-data',
397 | 'name': 'media_id',
398 | 'data': media_id,
399 | },
400 | {
401 | 'type': 'form-data',
402 | 'name': 'recipient_users',
403 | 'data': '[["{}"]]'.format(recipient_users),
404 | },
405 | {
406 | 'type': 'form-data',
407 | 'name': 'client_context',
408 | 'data': self.uuid,
409 | },
410 | {
411 | 'type': 'form-data',
412 | 'name': 'thread',
413 | 'data': '["0"]',
414 | },
415 | {
416 | 'type': 'form-data',
417 | 'name': 'text',
418 | 'data': text or '',
419 | },
420 | ]
421 | data = self.buildBody(bodies, boundary)
422 | self.s.headers.update({'User-Agent': self.USER_AGENT,
423 | 'Proxy-Connection': 'keep-alive',
424 | 'Connection': 'keep-alive',
425 | 'Accept': '*/*',
426 | 'Content-Type': 'multipart/form-data; boundary={}'.format(boundary),
427 | 'Accept-Language': 'en-en'})
428 | # self.SendRequest(endpoint,post=data) #overwrites 'Content-type' header and boundary is missed
429 | response = self.s.post(self.API_URL + endpoint, data=data)
430 |
431 | if response.status_code == 200:
432 | self.LastResponse = response
433 | self.LastJson = json.loads(response.text)
434 | return True
435 | else:
436 | print("Request return " + str(response.status_code) + " error!")
437 | # for debugging
438 | try:
439 | self.LastResponse = response
440 | self.LastJson = json.loads(response.text)
441 | except:
442 | pass
443 | return False
444 |
445 | def configureVideo(self, upload_id, video, thumbnail, caption=''):
446 | clip = VideoFileClip(video)
447 | self.uploadPhoto(photo=thumbnail, caption=caption, upload_id=upload_id)
448 | data = json.dumps({
449 | 'upload_id': upload_id,
450 | 'source_type': 3,
451 | 'poster_frame_index': 0,
452 | 'length': 0.00,
453 | 'audio_muted': False,
454 | 'filter_type': 0,
455 | 'video_result': 'deprecated',
456 | 'clips': {
457 | 'length': clip.duration,
458 | 'source_type': '3',
459 | 'camera_position': 'back',
460 | },
461 | 'extra': {
462 | 'source_width': clip.size[0],
463 | 'source_height': clip.size[1],
464 | },
465 | 'device': self.DEVICE_SETTINTS,
466 | '_csrftoken': self.token,
467 | '_uuid': self.uuid,
468 | '_uid': self.username_id,
469 | 'caption': caption,
470 | })
471 | return self.SendRequest('media/configure/?video=1', self.generateSignature(data))
472 |
473 | def configure(self, upload_id, photo, caption=''):
474 | (w, h) = getImageSize(photo)
475 | data = json.dumps({'_csrftoken': self.token,
476 | 'media_folder': 'Instagram',
477 | 'source_type': 4,
478 | '_uid': self.username_id,
479 | '_uuid': self.uuid,
480 | 'caption': caption,
481 | 'upload_id': upload_id,
482 | 'device': self.DEVICE_SETTINTS,
483 | 'edits': {
484 | 'crop_original_size': [w * 1.0, h * 1.0],
485 | 'crop_center': [0.0, 0.0],
486 | 'crop_zoom': 1.0
487 | },
488 | 'extra': {
489 | 'source_width': w,
490 | 'source_height': h
491 | }})
492 | return self.SendRequest('media/configure/?', self.generateSignature(data))
493 |
494 | def editMedia(self, mediaId, captionText=''):
495 | data = json.dumps({'_uuid': self.uuid,
496 | '_uid': self.username_id,
497 | '_csrftoken': self.token,
498 | 'caption_text': captionText})
499 | return self.SendRequest('media/' + str(mediaId) + '/edit_media/', self.generateSignature(data))
500 |
501 | def removeSelftag(self, mediaId):
502 | data = json.dumps({'_uuid': self.uuid,
503 | '_uid': self.username_id,
504 | '_csrftoken': self.token})
505 | return self.SendRequest('media/' + str(mediaId) + '/remove/', self.generateSignature(data))
506 |
507 | def mediaInfo(self, mediaId):
508 | data = json.dumps({'_uuid': self.uuid,
509 | '_uid': self.username_id,
510 | '_csrftoken': self.token,
511 | 'media_id': mediaId})
512 | return self.SendRequest('media/' + str(mediaId) + '/info/', self.generateSignature(data))
513 |
514 | def deleteMedia(self, mediaId):
515 | data = json.dumps({'_uuid': self.uuid,
516 | '_uid': self.username_id,
517 | '_csrftoken': self.token,
518 | 'media_id': mediaId})
519 | return self.SendRequest('media/' + str(mediaId) + '/delete/', self.generateSignature(data))
520 |
521 | def changePassword(self, newPassword):
522 | data = json.dumps({'_uuid': self.uuid,
523 | '_uid': self.username_id,
524 | '_csrftoken': self.token,
525 | 'old_password': self.password,
526 | 'new_password1': newPassword,
527 | 'new_password2': newPassword})
528 | return self.SendRequest('accounts/change_password/', self.generateSignature(data))
529 |
530 | def explore(self):
531 | return self.SendRequest('discover/explore/')
532 |
533 | def comment(self, mediaId, commentText):
534 | data = json.dumps({'_uuid': self.uuid,
535 | '_uid': self.username_id,
536 | '_csrftoken': self.token,
537 | 'comment_text': commentText})
538 | return self.SendRequest('media/' + str(mediaId) + '/comment/', self.generateSignature(data))
539 |
540 | def deleteComment(self, mediaId, commentId):
541 | data = json.dumps({'_uuid': self.uuid,
542 | '_uid': self.username_id,
543 | '_csrftoken': self.token})
544 | return self.SendRequest('media/' + str(mediaId) + '/comment/' + str(commentId) + '/delete/', self.generateSignature(data))
545 |
546 | def changeProfilePicture(self, photo):
547 | # TODO Instagram.php 705-775
548 | return False
549 |
550 | def removeProfilePicture(self):
551 | data = json.dumps({'_uuid': self.uuid,
552 | '_uid': self.username_id,
553 | '_csrftoken': self.token})
554 | return self.SendRequest('accounts/remove_profile_picture/', self.generateSignature(data))
555 |
556 | def setPrivateAccount(self):
557 | data = json.dumps({'_uuid': self.uuid,
558 | '_uid': self.username_id,
559 | '_csrftoken': self.token})
560 | return self.SendRequest('accounts/set_private/', self.generateSignature(data))
561 |
562 | def setPublicAccount(self):
563 | data = json.dumps({'_uuid': self.uuid,
564 | '_uid': self.username_id,
565 | '_csrftoken': self.token})
566 | return self.SendRequest('accounts/set_public/', self.generateSignature(data))
567 |
568 | def getProfileData(self):
569 | data = json.dumps({'_uuid': self.uuid,
570 | '_uid': self.username_id,
571 | '_csrftoken': self.token})
572 | return self.SendRequest('accounts/current_user/?edit=true', self.generateSignature(data))
573 |
574 | def editProfile(self, url, phone, first_name, biography, email, gender):
575 | data = json.dumps({'_uuid': self.uuid,
576 | '_uid': self.username_id,
577 | '_csrftoken': self.token,
578 | 'external_url': url,
579 | 'phone_number': phone,
580 | 'username': self.username,
581 | 'full_name': first_name,
582 | 'biography': biography,
583 | 'email': email,
584 | 'gender': gender})
585 | return self.SendRequest('accounts/edit_profile/', self.generateSignature(data))
586 |
587 | def getUsernameInfo(self, usernameId):
588 | return self.SendRequest('users/' + str(usernameId) + '/info/')
589 |
590 | def getSelfUsernameInfo(self):
591 | return self.getUsernameInfo(self.username_id)
592 |
593 | def getSelfSavedMedia(self):
594 | return self.SendRequest('feed/saved')
595 |
596 | def getRecentActivity(self):
597 | activity = self.SendRequest('news/inbox/?')
598 | return activity
599 |
600 | def getFollowingRecentActivity(self):
601 | activity = self.SendRequest('news/?')
602 | return activity
603 |
604 | def getv2Inbox(self):
605 | inbox = self.SendRequest('direct_v2/inbox/?')
606 | return inbox
607 |
608 | def getv2Threads(self, thread, cursor=None):
609 | endpoint = 'direct_v2/threads/{0}'.format(thread)
610 | if cursor is not None:
611 | endpoint += '?cursor={0}'.format(cursor)
612 | inbox = self.SendRequest(endpoint)
613 | return inbox
614 |
615 | def getUserTags(self, usernameId):
616 | tags = self.SendRequest('usertags/' + str(usernameId) + '/feed/?rank_token=' + str(self.rank_token) + '&ranked_content=true&')
617 | return tags
618 |
619 | def getSelfUserTags(self):
620 | return self.getUserTags(self.username_id)
621 |
622 | def tagFeed(self, tag):
623 | userFeed = self.SendRequest('feed/tag/' + str(tag) + '/?rank_token=' + str(self.rank_token) + '&ranked_content=true&')
624 | return userFeed
625 |
626 | def getMediaLikers(self, mediaId):
627 | likers = self.SendRequest('media/' + str(mediaId) + '/likers/?')
628 | return likers
629 |
630 | def getGeoMedia(self, usernameId):
631 | locations = self.SendRequest('maps/user/' + str(usernameId) + '/')
632 | return locations
633 |
634 | def getSelfGeoMedia(self):
635 | return self.getGeoMedia(self.username_id)
636 |
637 | def fbUserSearch(self, query):
638 | query = self.SendRequest('fbsearch/topsearch/?context=blended&query=' + str(query) + '&rank_token=' + str(self.rank_token))
639 | return query
640 |
641 | def searchUsers(self, query):
642 | query = self.SendRequest('users/search/?ig_sig_key_version=' + str(self.SIG_KEY_VERSION) + '&is_typeahead=true&query=' + str(query) + '&rank_token=' + str(self.rank_token))
643 | return query
644 |
645 | def searchUsername(self, usernameName):
646 | query = self.SendRequest('users/' + str(usernameName) + '/usernameinfo/')
647 | return query
648 |
649 | def syncFromAdressBook(self, contacts):
650 | return self.SendRequest('address_book/link/?include=extra_display_name,thumbnails', "contacts=" + json.dumps(contacts))
651 |
652 | def searchTags(self, query):
653 | query = self.SendRequest('tags/search/?is_typeahead=true&q=' + str(query) + '&rank_token=' + str(self.rank_token))
654 | return query
655 |
656 | def getTimeline(self):
657 | query = self.SendRequest('feed/timeline/?rank_token=' + str(self.rank_token) + '&ranked_content=true&')
658 | return query
659 |
660 | def getUserFeed(self, usernameId, maxid='', minTimestamp=None):
661 | query = self.SendRequest('feed/user/%s/?max_id=%s&min_timestamp=%s&rank_token=%s&ranked_content=true'
662 | % (usernameId, maxid, minTimestamp, self.rank_token))
663 | return query
664 |
665 | def getSelfUserFeed(self, maxid='', minTimestamp=None):
666 | return self.getUserFeed(self.username_id, maxid, minTimestamp)
667 |
668 | def getHashtagFeed(self, hashtagString, maxid=''):
669 | return self.SendRequest('feed/tag/' + hashtagString + '/?max_id=' + str(maxid) + '&rank_token=' + self.rank_token + '&ranked_content=true&')
670 |
671 | def searchLocation(self, query):
672 | locationFeed = self.SendRequest('fbsearch/places/?rank_token=' + str(self.rank_token) + '&query=' + str(query))
673 | return locationFeed
674 |
675 | def getLocationFeed(self, locationId, maxid=''):
676 | return self.SendRequest('feed/location/' + str(locationId) + '/?max_id=' + maxid + '&rank_token=' + self.rank_token + '&ranked_content=true&')
677 |
678 | def getPopularFeed(self):
679 | popularFeed = self.SendRequest('feed/popular/?people_teaser_supported=1&rank_token=' + str(self.rank_token) + '&ranked_content=true&')
680 | return popularFeed
681 |
682 | def getUserFollowings(self, usernameId, maxid=''):
683 | url = 'friendships/' + str(usernameId) + '/following/?'
684 | query_string = {'ig_sig_key_version': self.SIG_KEY_VERSION,
685 | 'rank_token': self.rank_token}
686 | if maxid:
687 | query_string['max_id'] = maxid
688 | if sys.version_info.major == 3:
689 | url += urllib.parse.urlencode(query_string)
690 | else:
691 | url += urllib.urlencode(query_string)
692 | return self.SendRequest(url)
693 |
694 | def getSelfUsersFollowing(self):
695 | return self.getUserFollowings(self.username_id)
696 |
697 | def getUserFollowers(self, usernameId, maxid=''):
698 | if maxid == '':
699 | return self.SendRequest('friendships/' + str(usernameId) + '/followers/?rank_token=' + self.rank_token)
700 | else:
701 | return self.SendRequest('friendships/' + str(usernameId) + '/followers/?rank_token=' + self.rank_token + '&max_id=' + str(maxid))
702 |
703 | def getSelfUserFollowers(self):
704 | return self.getUserFollowers(self.username_id)
705 |
706 | def like(self, mediaId):
707 | data = json.dumps({'_uuid': self.uuid,
708 | '_uid': self.username_id,
709 | '_csrftoken': self.token,
710 | 'media_id': mediaId})
711 | return self.SendRequest('media/' + str(mediaId) + '/like/', self.generateSignature(data))
712 |
713 | def unlike(self, mediaId):
714 | data = json.dumps({'_uuid': self.uuid,
715 | '_uid': self.username_id,
716 | '_csrftoken': self.token,
717 | 'media_id': mediaId})
718 | return self.SendRequest('media/' + str(mediaId) + '/unlike/', self.generateSignature(data))
719 |
720 | def getMediaComments(self, mediaId, max_id=''):
721 | return self.SendRequest('media/' + mediaId + '/comments/?max_id=' + max_id)
722 |
723 | def setNameAndPhone(self, name='', phone=''):
724 | data = json.dumps({'_uuid': self.uuid,
725 | '_uid': self.username_id,
726 | 'first_name': name,
727 | 'phone_number': phone,
728 | '_csrftoken': self.token})
729 | return self.SendRequest('accounts/set_phone_and_name/', self.generateSignature(data))
730 |
731 | def getDirectShare(self):
732 | return self.SendRequest('direct_share/inbox/?')
733 |
734 | def backup(self):
735 | # TODO Instagram.php 1470-1485
736 | return False
737 |
738 | def follow(self, userId):
739 | data = json.dumps({'_uuid': self.uuid,
740 | '_uid': self.username_id,
741 | 'user_id': userId,
742 | '_csrftoken': self.token})
743 | return self.SendRequest('friendships/create/' + str(userId) + '/', self.generateSignature(data))
744 |
745 | def unfollow(self, userId):
746 | data = json.dumps({'_uuid': self.uuid,
747 | '_uid': self.username_id,
748 | 'user_id': userId,
749 | '_csrftoken': self.token})
750 | return self.SendRequest('friendships/destroy/' + str(userId) + '/', self.generateSignature(data))
751 |
752 | def block(self, userId):
753 | data = json.dumps({'_uuid': self.uuid,
754 | '_uid': self.username_id,
755 | 'user_id': userId,
756 | '_csrftoken': self.token})
757 | return self.SendRequest('friendships/block/' + str(userId) + '/', self.generateSignature(data))
758 |
759 | def unblock(self, userId):
760 | data = json.dumps({'_uuid': self.uuid,
761 | '_uid': self.username_id,
762 | 'user_id': userId,
763 | '_csrftoken': self.token})
764 | return self.SendRequest('friendships/unblock/' + str(userId) + '/', self.generateSignature(data))
765 |
766 | def userFriendship(self, userId):
767 | data = json.dumps({'_uuid': self.uuid,
768 | '_uid': self.username_id,
769 | 'user_id': userId,
770 | '_csrftoken': self.token})
771 | return self.SendRequest('friendships/show/' + str(userId) + '/', self.generateSignature(data))
772 |
773 | def getLikedMedia(self, maxid=''):
774 | return self.SendRequest('feed/liked/?max_id=' + str(maxid))
775 |
776 | def generateSignature(self, data, skip_quote=False):
777 | if not skip_quote:
778 | try:
779 | parsedData = urllib.parse.quote(data)
780 | except AttributeError:
781 | parsedData = urllib.quote(data)
782 | else:
783 | parsedData = data
784 | return 'ig_sig_key_version=' + self.SIG_KEY_VERSION + '&signed_body=' + hmac.new(self.IG_SIG_KEY.encode('utf-8'), data.encode('utf-8'), hashlib.sha256).hexdigest() + '.' + parsedData
785 |
786 | def generateDeviceId(self, seed):
787 | volatile_seed = "12345"
788 | m = hashlib.md5()
789 | m.update(seed.encode('utf-8') + volatile_seed.encode('utf-8'))
790 | return 'android-' + m.hexdigest()[:16]
791 |
792 | def generateUUID(self, type):
793 | generated_uuid = str(uuid.uuid4())
794 | if (type):
795 | return generated_uuid
796 | else:
797 | return generated_uuid.replace('-', '')
798 |
799 | def generateUploadId(self):
800 | return str(calendar.timegm(datetime.utcnow().utctimetuple()))
801 |
802 | def buildBody(self, bodies, boundary):
803 | body = u''
804 | for b in bodies:
805 | body += u'--{boundary}\r\n'.format(boundary=boundary)
806 | body += u'Content-Disposition: {b_type}; name="{b_name}"'.format(b_type=b['type'], b_name=b['name'])
807 | _filename = b.get('filename', None)
808 | _headers = b.get('headers', None)
809 | if _filename:
810 | _filename, ext = os.path.splitext(_filename)
811 | _body += u'; filename="pending_media_{uid}.{ext}"'.format(uid=self.generateUploadId(), ext=ext)
812 | if _headers and isinstance(_headers, list):
813 | for h in _headers:
814 | _body += u'\r\n{header}'.format(header=h)
815 | body += u'\r\n\r\n{data}\r\n'.format(data=b['data'])
816 | body += u'--{boundary}--'.format(boundary=boundary)
817 | return body
818 |
819 | def SendRequest(self, endpoint, post=None, login=False):
820 | verify = False # don't show request warning
821 |
822 | if (not self.isLoggedIn and not login):
823 | raise Exception("Not logged in!\n")
824 |
825 | self.s.headers.update({'Connection': 'close',
826 | 'Accept': '*/*',
827 | 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
828 | 'Cookie2': '$Version=1',
829 | 'Accept-Language': 'en-US',
830 | 'User-Agent': self.USER_AGENT})
831 |
832 | while True:
833 | try:
834 | if (post is not None):
835 | response = self.s.post(self.API_URL + endpoint, data=post, verify=verify)
836 | else:
837 | response = self.s.get(self.API_URL + endpoint, verify=verify)
838 | break
839 | except Exception as e:
840 | print('Except on SendRequest (wait 60 sec and resend): ' + str(e))
841 | time.sleep(60)
842 |
843 | if response.status_code == 200:
844 | self.LastResponse = response
845 | self.LastJson = json.loads(response.text)
846 | return True
847 | else:
848 | print("Request return " + str(response.status_code) + " error!")
849 | # for debugging
850 | try:
851 | self.LastResponse = response
852 | self.LastJson = json.loads(response.text)
853 | print("\n🛑 Title: ", self.LastJson["error_title"])
854 | print("🛑 Error Message: ", self.LastJson["message"])
855 | print("🛑 Status: ", self.LastJson["status"])
856 | print("🛑 Error Type: ", self.LastJson["error_type"])
857 | except:
858 | pass
859 | return False
860 |
861 | def getTotalFollowers(self, usernameId):
862 | followers = []
863 | next_max_id = ''
864 | while 1:
865 | self.getUserFollowers(usernameId, next_max_id)
866 | temp = self.LastJson
867 |
868 | for item in temp["users"]:
869 | followers.append(item)
870 |
871 | if temp["big_list"] is False:
872 | return followers
873 | next_max_id = temp["next_max_id"]
874 |
875 | def getTotalFollowings(self, usernameId):
876 | followers = []
877 | next_max_id = ''
878 | while True:
879 | self.getUserFollowings(usernameId, next_max_id)
880 | temp = self.LastJson
881 |
882 | for item in temp["users"]:
883 | followers.append(item)
884 |
885 | if temp["big_list"] is False:
886 | return followers
887 | next_max_id = temp["next_max_id"]
888 |
889 | def getTotalUserFeed(self, usernameId, minTimestamp=None):
890 | user_feed = []
891 | next_max_id = ''
892 | while True:
893 | self.getUserFeed(usernameId, next_max_id, minTimestamp)
894 | temp = self.LastJson
895 | for item in temp["items"]:
896 | user_feed.append(item)
897 | if temp["more_available"] is False:
898 | return user_feed
899 | next_max_id = temp["next_max_id"]
900 |
901 | def getTotalSelfUserFeed(self, minTimestamp=None):
902 | return self.getTotalUserFeed(self.username_id, minTimestamp)
903 |
904 | def getTotalSelfFollowers(self):
905 | return self.getTotalFollowers(self.username_id)
906 |
907 | def getTotalSelfFollowings(self):
908 | return self.getTotalFollowings(self.username_id)
909 |
910 | def getTotalLikedMedia(self, scan_rate=1):
911 | next_id = ''
912 | liked_items = []
913 | for x in range(0, scan_rate):
914 | temp = self.getLikedMedia(next_id)
915 | temp = self.LastJson
916 | try:
917 | next_id = temp["next_max_id"]
918 | for item in temp["items"]:
919 | liked_items.append(item)
920 | except KeyError:
921 | break
922 | return liked_items
923 |
--------------------------------------------------------------------------------