├── proxies.txt ├── log.py ├── LICENSE ├── README.md ├── .gitignore └── ebay-watcher.py /proxies.txt: -------------------------------------------------------------------------------- 1 | 127.0.0.1:8888 -------------------------------------------------------------------------------- /log.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | def log(tag, text): 4 | # Info tag 5 | if(tag == 'i'): 6 | print("[" + str(datetime.datetime.now()) + " - INFO] " + text) 7 | # Error tag 8 | elif(tag == 'e'): 9 | print("[" + str(datetime.datetime.now()) + " - ERROR] " + text) 10 | # Success tag 11 | elif(tag == 's'): 12 | print("[" + str(datetime.datetime.now()) + " - SUCCESS] " + text) 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Niveen Jegatheeswaran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # THIS PROJECT IS NO LONGER SUPPORTED. EBAY HAS UPGRADED THEIR SECURITY AND THIS SCRIPT NO LONGER WORKS AND SHOULD ONLY BE USED FOR EDUCATIONAL PURPOSES. 2 | 3 | # ebay-watcher 4 | Get your listing to the top of eBay search results! 5 | 6 | ## About 7 | Script that adds to the "Watch" counter of a specified eBay listing in an attempt to make the listing more popular, increasing the number of real visitors that see the listing, which in turn helps the listing appear higher in search results and also makes the product appear as though it's in high-demand since it has a lot of "Watchers". Simply put, an SEO tool for eBay listings. 8 | 9 | ## Requirements 10 | Python dependencies: requests, bs4 11 | Catch-All Domain 12 | Proxies (more/rotating == better) 13 | 14 | ## Example 15 | What link do you want to watch: https://www.ebay.ca/itm/283116687683 16 | What is your catch-all domain (ex. ebae.com): ebae.com 17 | How many accounts do you want to watch the page: 100 18 | 19 | [Click here for a demo video](https://www.youtube.com/watch?v=wmvFjg8bkVE) 20 | 21 | ## Disclaimer 22 | This project was made for educational purposes only. Please use responsibly. 23 | 24 | ## What's next for eBae 25 | - Integration through a Discord bot for users to use by entering a command such as "!ebay watch 500 https://www.ebay.ca/itm/283116687683" 26 | - Add option to just view items instead of watching them 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /ebay-watcher.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup as soup 3 | import random 4 | import datetime 5 | from threading import Thread 6 | from log import log as log 7 | 8 | def read_from_txt(path): 9 | ''' 10 | (str) -> list of str 11 | Loads up all sites from the sitelist.txt file in the root directory. 12 | Returns the sites as a list 13 | ''' 14 | # Initialize variables 15 | raw_lines = [] 16 | lines = [] 17 | 18 | # Load data from the txt file 19 | try: 20 | f = open(path, "r") 21 | raw_lines = f.readlines() 22 | f.close() 23 | 24 | # Raise an error if the file couldn't be found 25 | except: 26 | log('e', "Couldn't locate <" + path + ">.") 27 | raise FileNotFound() 28 | 29 | # Parse the data 30 | for line in raw_lines: 31 | lines.append(line.strip("\n")) 32 | 33 | # Return the data 34 | return lines 35 | 36 | def get_proxy(proxy_list): 37 | ''' 38 | (list) -> dict 39 | Given a proxy list , a proxy is selected and returned. 40 | ''' 41 | # Choose a random proxy 42 | proxy = random.choice(proxy_list) 43 | 44 | # Split up the proxy 45 | proxy_parts = proxy.split(':') 46 | 47 | # Set up the proxy to be used 48 | try: 49 | proxies = { 50 | "http": "http://" + proxy_parts[2] + ":" + proxy_parts[3] + "@" +\ 51 | proxy_parts[0] + ":" + proxy_parts[1], 52 | "https": "https://" + proxy_parts[2] + ":" + proxy_parts[3] + "@" +\ 53 | proxy_parts[0] + ":" + proxy_parts[1] 54 | } 55 | except: 56 | proxies = { 57 | "http": str(proxy), 58 | "https": str(proxy) 59 | } 60 | 61 | # Return the proxy 62 | return proxies 63 | 64 | def gen_email(domain): 65 | ''' 66 | (str) -> str 67 | Given a domain, a random email address is generated. 68 | 69 | REQ: Valid is provided 70 | ''' 71 | return str(random.randint(100000000, 999999999)) + "@" + domain 72 | 73 | 74 | class eBae: 75 | def __init__(self, product_link, domain): 76 | ''' 77 | (str, str) -> eBae 78 | Given a link to an eBay product and a catch-all domain 79 | address , a random email address is generated and an eBae 80 | object is returned. 81 | 82 | REQ: domain is a catch-all domain 83 | REQ: product_link is a link to a product listed on eBay 84 | ''' 85 | self.s = requests.session() 86 | self.product_link = product_link 87 | self.s.headers = { 88 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36" 89 | } 90 | self.proxy_list = read_from_txt("proxies.txt") 91 | self.email = gen_email(domain) 92 | 93 | def register(self): 94 | ''' 95 | () -> bool 96 | Attempts to create an eBay account. No error checking is done to ensure 97 | success. The assumption is that if a connection could be made through 98 | the proxy, no issues should occur during the registration phase. 99 | ''' 100 | # Set up request to be made 101 | link = "https://reg.ebay.ca/reg/PartialReg" 102 | payload = { 103 | "isSug": "false", 104 | "countryId": "2", 105 | "userid": "", 106 | "ru": "http://www.ebay.ca", 107 | "firstname": "Bill", 108 | "lastname": "Nye", 109 | "email": self.email, 110 | "PASSWORD": "G2-kennyS", 111 | "promotion": "true", 112 | "iframeMigration1": "true", 113 | "mode": "1", 114 | "frmaction": "submit", 115 | "tagInfo": "ht5%3DAQAAAWYTLNkMAAUxNjYyM2QzN2E5ZC5hOTliMTliLjIzZjA2LmZmZmY1OWZiUII8pysAhR1B%252F7QeEEkR2JhM6UE*%7Cht5new%3Dfalse%26usid%3D23d486fd1660aa16b5f1511fffff63de", 116 | "isGuest": "0", 117 | "idlstate": "", 118 | "profilePicture": "", 119 | "agreement": "Terms and conditions", 120 | "signInUrl": "https%3A%2F%2Fsignin.ebay.ca%2Fws%2FeBayISAPI.dll%3FSignIn%26ru%3Dhttp%253A%252F%252Fwww.ebay.ca%26regUrl%3Dhttps%253A%252F%252Freg.ebay.ca%252Freg%252FPartialReg", 121 | "personalFlag": "true", 122 | "isMobilePhone": "", 123 | "_trksid": "p2052190", 124 | "ets": "AQADAAAAEEI86Oq8oYGjne4i2ZMNKME" 125 | } 126 | 127 | try: 128 | self.s.post(link, data=payload, proxies=get_proxy(self.proxy_list), verify=False) 129 | except: 130 | try: 131 | self.s.post(link, data=payload, proxies=get_proxy(self.proxy_list), verify=False) 132 | except: 133 | log('e', "Connection failed while creating account with email " + self.email) 134 | return False 135 | 136 | # Alert user of progress: Account creation attempted 137 | log('i', "Created account with email " + self.email) 138 | return True 139 | 140 | def watch(self): 141 | ''' 142 | () -> None 143 | Attempts to watch a product on eBay. 144 | ''' 145 | # Get product watch link 146 | try: 147 | r = self.s.get(self.product_link, proxies=get_proxy(self.proxy_list), verify=False) 148 | except: 149 | try: 150 | r = self.s.get(self.product_link, proxies=get_proxy(self.proxy_list), verify=False) 151 | except: 152 | log('e', "Connection failed while loading product on " + self.product_link) 153 | return 154 | 155 | try: 156 | watch_link = soup(r.text, "html.parser").find("div", {"id": "vi-atl-lnk"}).a["href"] 157 | except: 158 | log('e', "Connection failed while loading product on " + self.product_link) 159 | return 160 | 161 | # Watch the product (the second GET actually adds it to watch list) 162 | try: 163 | r = self.s.get(watch_link, proxies=get_proxy(self.proxy_list), verify=False) 164 | r = self.s.get(watch_link, proxies=get_proxy(self.proxy_list), verify=False) 165 | except: 166 | try: 167 | r = self.s.get(watch_link, proxies=get_proxy(self.proxy_list), verify=False) 168 | r = self.s.get(watch_link, proxies=get_proxy(self.proxy_list), verify=False) 169 | except: 170 | log('e', "Failed to add " + self.product_link + " to watch list.") 171 | return 172 | 173 | 174 | # Alert user of progress: Watch product success/failure 175 | if("saved in your" in r.text.lower()): 176 | log('s', "Added " + self.product_link + " to watch list.") 177 | else: 178 | log('e', "Couldn't add " + self.product_link + " to watch list.") 179 | 180 | def start(self): 181 | ''' 182 | () -> None 183 | Creates an eBay account and attempts to watch a previously defined 184 | product. 185 | ''' 186 | # Create an account 187 | logged_in = self.register() 188 | 189 | # Watch the product 190 | if(logged_in): 191 | self.watch() 192 | 193 | def intro(): 194 | ''' 195 | () -> None 196 | Short intro text outlining what the project does, requirements, usage, and 197 | a short disclaimer at the end. 198 | ''' 199 | print( 200 | ''' 201 | \t\t\t\teBae v1.0 202 | \tMade for Sneaker Hackathon by snivyn#0416/@snivynGOD 203 | 204 | Cool likkle thing to try and help with finessing eBay's algorithm to 205 | get your listing to the top when someone searches for a product. Useful 206 | to sell shoes/Funkos where there are a lot of sellers and you need 207 | your listing to be seen first by potential buyers. The password to all 208 | created accounts is (excluding < and >). If you don't care 209 | for the accounts that are made, you can put a random domain as your 210 | catch-all domain and it should still work fine. 211 | 212 | Requirements: 213 | \tPython dependencies: requests, bs4 214 | \tCatch-All Domain 215 | \tProxies (more/rotating == better) 216 | 217 | Usage: 218 | What link do you want to watch: https://www.ebay.ca/itm/283116687683 219 | What is your catch-all domain (ex. ebae.com): ebae.com 220 | How many accounts do you want to watch the page: 5 221 | \n\n\n\n\n\n\n\n 222 | Disclaimer: This project was made for educational purposes only. 223 | Please use responsibly. 224 | ''' 225 | ) 226 | 227 | if(__name__ == "__main__"): 228 | # Ignore insecure messages 229 | requests.packages.urllib3.disable_warnings() 230 | 231 | # Display intro text 232 | intro() 233 | 234 | # Get the link to watch 235 | in_link = "" 236 | while(not in_link.startswith("http")): 237 | in_link = input("What link do you want to watch: ").strip() 238 | if(not in_link.startswith("http")): 239 | print("Hold up, that doesn't make sense. Let's try again.") 240 | 241 | # Get the user's catch-all domain 242 | in_domain = "" 243 | while(in_domain == ""): 244 | in_domain = input("What is your catch-all domain (ex. ebae.com): ").strip() 245 | 246 | # Do some basic error checking on the domain 247 | if(in_domain.startswith('@')): 248 | in_domain = in_domain[1:] 249 | 250 | if(in_domain.count('.') == 0): 251 | print("Hold up, that doesn't make sense. Let's try again.") 252 | in_domain = "" 253 | 254 | # Get number of tasks to start 255 | watches = 0 256 | while(watches == 0): 257 | in_watches = input("How many accounts do you want to watch the page: ") 258 | try: 259 | watches = int(in_watches) 260 | except: 261 | print("That's not a number! Try again.") 262 | 263 | # Ensure proxies are loaded properly 264 | proxy_check = False 265 | print("Please ensure your proxies in a file named proxies.txt in the same folder as this script.") 266 | while(not proxy_check): 267 | raw_proxy = input("Type the name of the proxy file to continue: ") 268 | if(raw_proxy == "proxies.txt"): 269 | proxy_check = True 270 | else: 271 | print("Please ensure your proxy file is named proxies.txt and try again.") 272 | 273 | # Load proxies 274 | proxy_list = read_from_txt("proxies.txt") 275 | 276 | # Start tasks 277 | tasks = [] 278 | for i in range(0, watches): 279 | log('i', "Starting task " + str(i + 1) + "...") 280 | eInstance = eBae(in_link, in_domain) 281 | t = Thread(target=eInstance.start) 282 | t.start() 283 | tasks.append(t) 284 | 285 | # Finish all the tasks 286 | for t in tasks: 287 | t.join() 288 | 289 | input("Done all tasks. Press any key to exit.") 290 | --------------------------------------------------------------------------------