├── 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 | ![spammy](https://media.discordapp.net/attachments/802869820717531136/802869836677644359/unknown.png) 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 | ![smsbombing](https://media.discordapp.net/attachments/802869820717531136/802871479791517716/unknown.png) 49 |
50 | 51 | # Instagram spammer: 52 | Image unavailable 53 |
54 | 55 | # Email spammer: 56 |
57 | 58 | ![emailraiduwu](https://media.discordapp.net/attachments/802869820717531136/802872937572859904/unknown.png) 59 |
60 | 61 | # Discord webhook spammer: 62 |
63 | 64 | ![dwr1](https://media.discordapp.net/attachments/802869820717531136/802873636792041480/unknown.png) 65 | 66 |
67 | 68 | ![dwr2](https://media.discordapp.net/attachments/802869820717531136/802874009372590080/unknown.png) 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 | --------------------------------------------------------------------------------