def check_materials(np): #print('Plushie Tycoon: Checking on materials.') np.get(path_materials) material_prices = [] material_owned = [] buy_links = [] accessories_owned = defaultdict(int) for cat in range(0, 4): np.get(path_materials, f'Cat={cat}') tbl = np.search(r"<table width='80%'.*?>(.*?)</table>")[1] rows = lib.table_to_tuples(tbl, raw=True) material_prices.append([amt(x) for x in rows[-1]]) if len(rows[-3]) > 1: material_owned.append([amt(x) for x in rows[-3]]) else: material_owned.append([0, 0, 0, 0]) links = [] for link_text in rows[1]: links.append(re.search(r"onClick=buying\('(.*?)',", link_text)[1]) buy_links.append(links) if cat == 2 and np.contains("table width='80%'"): tbl2 = np.search(r"<table width='80%' .*?>(.*?)</table>")[1] rows = lib.table_to_tuples(tbl2)[2:] for s, quant, kind, _ in rows: if quant: accessories_owned[s] += amt(quant) return material_prices, material_owned, accessories_owned, buy_links
def check_store(np): #print('Plushie Tycoon: Checking on store.') species_stats = {} np.get(path_store) np.get(path_store, 'Cat=0', 'invent=2') tbl = np.search(r"<table width='75%'.*?>(.*?)</table>") inv = [] past_sales = [] if tbl: tbl = tbl[1] inv = lib.table_to_tuples(tbl)[1:] plushies_to_sell = sum(int(x[1]) - int(x[4]) for x in inv) jobs_to_sell = len(inv) else: plushies_to_sell = 0 jobs_to_sell = 0 print( f'Plushie Tycoon: Store has {plushies_to_sell} plushies over {jobs_to_sell} jobs left to sell.' ) np.get(path_store, 'Cat=0', 'invent=1') tbl = np.search(r"<table width='75%'.*?>(.*?)</table>") if tbl: tbl = tbl[1] past_sales = lib.table_to_tuples(tbl)[1:] # TODO: Is inv in the correct order? prices = defaultdict(list) for row in past_sales + inv: species = row[0] price = int(row[2]) prices[species].append(price) for species in prices.keys(): ps = prices[species] n = len(ps) last = ps[-1] avg = sum(ps) / n stdev = (sum((p - avg)**2 for p in ps) / n)**0.5 species_stats[species] = { 'n': n, 'last': last, 'min': min(ps), 'max': max(ps), 'avg': avg, 'med': ps[len(ps) // 2], 'stdev': stdev, } #np.get(path_store, 'Cat=2') #size = int(np.search(r'You now have a size (\d+) store')[1]) return species_stats
def check_store(np): #print('Plushie Tycoon: Checking on store.') species_stats = {} np.get(path_store) np.get(path_store, 'Cat=0', 'invent=2') tbl = np.search(r"<table width='75%'.*?>(.*?)</table>") inv = [] past_sales = [] if tbl: tbl = tbl[1] inv = lib.table_to_tuples(tbl)[1:] plushies_to_sell = sum(int(x[1]) - int(x[4]) for x in inv) jobs_to_sell = len(inv) else: plushies_to_sell = 0 jobs_to_sell = 0 print(f'Plushie Tycoon: Store has {plushies_to_sell} plushies over {jobs_to_sell} jobs left to sell.') np.get(path_store, 'Cat=0', 'invent=1') tbl = np.search(r"<table width='75%'.*?>(.*?)</table>") if tbl: tbl = tbl[1] past_sales = lib.table_to_tuples(tbl)[1:] # TODO: Is inv in the correct order? prices = defaultdict(list) for row in past_sales + inv: species = row[0] price = int(row[2]) prices[species].append(price) for species in prices.keys(): ps = prices[species] n = len(ps) last = ps[-1] avg = sum(ps) / n stdev = (sum((p - avg)**2 for p in ps) / n)**0.5 species_stats[species] = { 'n': n, 'last': last, 'min': min(ps), 'max': max(ps), 'avg': avg, 'med': ps[len(ps) // 2], 'stdev': stdev, } #np.get(path_store, 'Cat=2') #size = int(np.search(r'You now have a size (\d+) store')[1]) return species_stats
def check_factory(np, hire=False): # Check status of jobs. # If less than 500 plushies remain, fire all workers. # Else, hire up to 250 Trainees and 25 Managers. #print('Plushie Tycoon: Checking on factory.') np.get(path_factory, 'exist=1') tbl = np.search(r"<table border='0' width='70%'.*?>(.*?)</table>")[1] jobs = lib.table_to_tuples(tbl)[1:-1] num_factory_jobs = len(jobs) num_plushies = sum(int(total) - int(done) for pet, total, done in jobs) print( f'Plushie Tycoon: Factory has {num_plushies} plushies in {num_factory_jobs} jobs.' ) if hire: if num_plushies > 400 or num_factory_jobs >= 5: np.get(path_factory, 'personnel=1') if np.contains('many unemployed pets'): # Use the default workforce np.get(path_factory, 'set_def=2', 'personnel=1') print('Plushie Tycoon: Hired the default workforce.') # TODO: Calculate a good workforce and hire it. #np.get(path_factory, 'personnel=2', 'hire=1') #np.get(path_workers_hire, 'worker=2') #np.post(path_process_hire, 'amt=250', 'worker=2') #np.get(path_workers_hire, 'worker=4') #np.post(path_process_hire, 'amt=10', 'worker=4') #np.get(path_workers_hire, 'worker=4') #np.post(path_process_hire, 'amt=10', 'worker=4') #np.get(path_workers_hire, 'worker=4') #np.post(path_process_hire, 'amt=4', 'worker=4') #print('Plushie Tycoon: Hired up to 250 Trainees and 25 Managers.') else: print('Plushie Tycoon: Already have workers.') else: np.get(path_factory, 'personnel=1') if np.contains('factory.phtml?personnel=2&fire=1'): np.get(path_factory, 'personnel=2', 'fire=1') if np.contains('fire_all=1'): np.get(path_factory, 'fire_all=1') print('Plushie Tycoon: Fired all workers.') else: print('Plushie Tycoon: Already fired all workers.') return num_factory_jobs
def check_factory(np, hire=False): # Check status of jobs. # If less than 500 plushies remain, fire all workers. # Else, hire up to 250 Trainees and 25 Managers. #print('Plushie Tycoon: Checking on factory.') np.get(path_factory, 'exist=1') tbl = np.search(r"<table border='0' width='70%'.*?>(.*?)</table>")[1] jobs = lib.table_to_tuples(tbl)[1:-1] num_factory_jobs = len(jobs) num_plushies = sum(int(total) - int(done) for pet, total, done in jobs) print(f'Plushie Tycoon: Factory has {num_plushies} plushies in {num_factory_jobs} jobs.') if hire: if num_plushies > 400 or num_factory_jobs >= 5: np.get(path_factory, 'personnel=1') if np.contains('many unemployed pets'): # Use the default workforce np.get(path_factory, 'set_def=2', 'personnel=1') print('Plushie Tycoon: Hired the default workforce.') # TODO: Calculate a good workforce and hire it. #np.get(path_factory, 'personnel=2', 'hire=1') #np.get(path_workers_hire, 'worker=2') #np.post(path_process_hire, 'amt=250', 'worker=2') #np.get(path_workers_hire, 'worker=4') #np.post(path_process_hire, 'amt=10', 'worker=4') #np.get(path_workers_hire, 'worker=4') #np.post(path_process_hire, 'amt=10', 'worker=4') #np.get(path_workers_hire, 'worker=4') #np.post(path_process_hire, 'amt=4', 'worker=4') #print('Plushie Tycoon: Hired up to 250 Trainees and 25 Managers.') else: print('Plushie Tycoon: Already have workers.') else: np.get(path_factory, 'personnel=1') if np.contains('factory.phtml?personnel=2&fire=1'): np.get(path_factory, 'personnel=2', 'fire=1') if np.contains('fire_all=1'): np.get(path_factory, 'fire_all=1') print('Plushie Tycoon: Fired all workers.') else: print('Plushie Tycoon: Already fired all workers.') return num_factory_jobs
def check_warehouse(np): #print('Plushie Tycoon: Checking on warehouse.') np.get(path_warehouse) tbl = np.search(r"<table border='0' width='90%'.*?>(.*?)</table>")[1] to_ship = lib.table_to_tuples(tbl, raw=True)[2:-2] if to_ship: print(f'Plushie Tycoon: Warehouse is loading {len(to_ship)} jobs.') args = [] for row in to_ship: status = row[4] if status == 'Loaded': chkbox = row[5] result = re.search(r"name='(.*?)' value='(.*?)'", chkbox) name = result[1] value = result[2] args.append(f'{name}={value}') if args: # TODO: Only ship items to the store if it is advantageous. # (tax situations?) print(f'Plushie Tycoon: Shipping {len(args)} jobs to the store.') args.append('submit=Ship Plushies') np.post(path_warehouse, *args)
def update_prices(item_name, laxness=1): char_groups = 'an0 bo1 cp2 dq3 er4 fs5 gt6 hu7 iv8 jw9 kx_ ly mz'.split() c2g = dict(sum(([(c, i) for c in cs] for i, cs in enumerate(char_groups)), [])) markets = defaultdict(dict) ub_count = 0 search_count = 0 np = NeoPage('/market.phtml?type=wizard') opts = [] opts.append('type=process_wizard') opts.append('feedset=0') opts.append(f'shopwizard={item_name}') opts.append('table=shop') opts.append('criteria=exact') opts.append('min_price=0') opts.append('max_price=999999') # Repeatedly search the shop wizard, collecting all results seen. while not markets or any(len(market_data) < len(char_groups) - laxness for market_data in markets.values()): print(f'\r({sum(len(md) for md in markets.values())}/{len(markets) * len(char_groups)}) ', end='') np.post('/market.phtml', *opts) tbl = np.search(r'<table width="600".*?>(.*?)</table>') if not tbl: if np.contains('Whoa there, too many'): print('Shop wizard banned.') raise ShopWizardBannedException ub_count += 1 if ub_count >= 5: break continue tbl = tbl[1] rows = lib.table_to_tuples(tbl, raw=True)[1:] search_count += 1 if search_count >= 50: break market_data = [] obj_info_id = None for owner, item, stock, price in rows: result = re.search(r'<a href="(.*?)"><b>(.*?)</b></a>', owner) link = result[1] owner = result[2] result = re.search(r'/browseshop.phtml\?owner=(.*?)&buy_obj_info_id=(.*?)&buy_cost_neopoints=(\d+)', link) obj_info_id = int(result[2]) price = lib.amt(lib.strip_tags(price)) stock = lib.amt(stock) market_data.append((price, stock, link)) g = c2g[lib.strip_tags(rows[0][0])[0]] markets[obj_info_id][g] = market_data level2_by_item = {} # Consolidate results for each item into a quote. if markets: for obj_info_id, market_data in markets.items(): # TODO: Check suspiciously cheap prices to see if owners are frozen. level2 = sorted(sum(market_data.values(), [])) level2_by_item[obj_info_id] = level2 # The price of an item for our purposes is the price of the 2nd # cheapest item in the market. cur_amt = 0 cur_price = UNBUYABLE_PRICE for price, stock, link in level2: cur_price = price cur_amt += stock if cur_amt >= 2: break print(f'The price of {item_name} (id {obj_info_id}) is {cur_price} NP.') cur_amt = 0 for price, stock, link in level2: cur_amt += stock if cur_amt >= 30: break c = conn.cursor() c.execute(''' SELECT name FROM items WHERE obj_info_id=? ''', (obj_info_id,)) result = c.fetchone() if not result or result[0][0] != item_name: for market_data in level2: # Visit the shop and populate a bunch of fields np.get(market_data[2]) res = np.search(r'''<A href=".*?" onClick=".*?"><img src="http://images.neopets.com/items/(.*?)" .*? title="(.*?)" border="1"></a> <br> <b>(.*?)</b>''') if not res: print(f'{market_data[2]} is froze?') continue image = res[1] desc = res[2] name = res[3] c.execute(''' INSERT INTO items (name, image, desc, obj_info_id, last_updated) VALUES (?, ?, ?, ?, datetime('now')) ON CONFLICT (name, image) DO UPDATE SET desc=?, obj_info_id=?, last_updated=datetime('now') ''', (name, image, desc, obj_info_id, desc, obj_info_id)) print(f'The object id of {name} is {obj_info_id}') break else: print('Unable to find legit seller for {obj_info_id}. Will not store it in itemdb.') continue c.execute(''' UPDATE items SET price=?, price_last_updated=datetime('now') WHERE obj_info_id=? ''', (cur_price, obj_info_id)) else: print(f'It seems {item_name} is unbuyable.') # Item could not be found; assume it's unbuyable. # We use a cheap price estimate of 1,000,001 NP. # TODO: Items inserted in this way will have a wonky image property. c = conn.cursor() c.execute(''' INSERT INTO items (name, image, last_updated, price, price_last_updated) VALUES (?, NULL, datetime('now'), 1000001, datetime('now')) ON CONFLICT (name, image) DO UPDATE SET last_updated=datetime('now'), price=1000001, price_last_updated=datetime('now') ''', (item_name,)) conn.commit() return level2_by_item
def update_prices(item_name, laxness=5): now = datetime.now() if g.last_ban and (now - last_ban < timedelta(minutes=1) or (now - last_ban < timedelta(hours=1) and now.hour == last_ban.hour)): print('Still wiz banned.') raise ShopWizardBannedException char_groups = 'an0 bo1 cp2 dq3 er4 fs5 gt6 hu7 iv8 jw9 kx_ ly mz'.split() c2grp = dict(sum(([(c, i) for c in cs] for i, cs in enumerate(char_groups)), [])) obj_info_ids = set() ub_count = 0 search_count = 0 lowest_price = UNBUYABLE_PRICE np = NeoPage('/market.phtml?type=wizard') opts = [] opts.append('type=process_wizard') opts.append('feedset=0') opts.append(f'shopwizard={item_name}') opts.append('table=shop') opts.append('criteria=exact') opts.append('min_price=0') opts.append('max_price=999999') grps_needed = len(char_groups) - laxness def print_status(): found = sum(min(grps_needed, len(g.markets[i])) for i in obj_info_ids) total = len(obj_info_ids) * grps_needed print(f'\r({item_name}: {lowest_price} NP; {found}/{total}; {search_count}) ', end='') # Repeatedly search the shop wizard, collecting all results seen. try: while not obj_info_ids or any(len(g.markets[i]) < grps_needed for i in obj_info_ids): print_status() np.post('/market.phtml', *opts) if np.contains('Whoa there, too many'): print('Shop wizard banned.') last_ban = datetime.now() raise ShopWizardBannedException search_count += 1 if not '<table width="600"' in np.content: ub_count += 1 if ub_count >= 15: break continue tbl = np.search(r'<table width="600".*?>(.*?)</table>') tbl = tbl[1] rows = lib.table_to_tuples(tbl, raw=True)[1:] market_data = [] obj_info_id = None for owner, item, stock, price in rows: result = re.search(r'<a href="(.*?)"><b>(.*?)</b></a>', owner) link = result[1] owner = result[2] result = re.search(r'/browseshop.phtml\?owner=(.*?)&buy_obj_info_id=(.*?)&buy_cost_neopoints=(\d+)', link) obj_info_id = int(result[2]) price = lib.amt(lib.strip_tags(price)) stock = lib.amt(stock) market_data.append((price, stock, link)) lowest_price = min(lowest_price, price) obj_info_ids.add(obj_info_id) grp = c2grp[lib.strip_tags(rows[0][0])[0]] g.markets[obj_info_id][grp] = market_data if search_count >= 30 * max(1, len(obj_info_ids)): break except KeyboardInterrupt: laxness = len(char_groups) - min(len(g.markets[i]) for i in obj_info_ids) if obj_info_ids else 0 print(f'Interrupted. Actual laxness is {laxness}') print_status() # Consolidate results for each item into a quote. if obj_info_ids: for obj_info_id in obj_info_ids: level2 = sorted(sum(g.markets[obj_info_id].values(), [])) g.level2_cache[obj_info_id] = level2 cur_amt = 0 cur_price = 0 image, desc, name = None, None, None # The price of an item for our purposes is the cheapest price such # that: # - it is the second cheapest price, OR # - the next cheapest is at most 10% more expensive. print() if len(obj_info_ids) > 1: print(f'({obj_info_id}):') for price, stock, link in level2: # Our own prices don't count for pricing purposes if f'owner={os.environ.get("NP_USER")}' in link: print('Skipping our own shop') continue np.get(link) res = np.search(r'''<A href=".*?" onClick=".*?"><img src="http://images.neopets.com/items/(.*?)" .*? title="(.*?)" border="1"></a> <br> <b>(.*?)</b>''') if not res: print(f'{link} is frozen?') continue image, desc, name = res[1], res[2], res[3] print(f'{link} has {stock}') if price < cur_price * 1.1: break cur_price = price if stock >= 2: break cur_amt += stock if cur_amt >= 2: break else: print(f'Unable to find enough sellers for {obj_info_id}. Assuming unbuyable.') cur_price = UNBUYABLE_PRICE print(f'The price of {item_name} (id {obj_info_id}) is {cur_price} NP.') for _ in range(2): try: update_item(name, image=image, desc=desc, obj_info_id=obj_info_id, price=cur_price, price_laxness=laxness) break except sqlite3.IntegrityError: # TODO: IDK how we end up with bad obj_info_ids c = conn.cursor() c.execute('''DELETE FROM items WHERE obj_info_id = ?''', (obj_info_id,)) else: print(f'It seems {item_name} is unbuyable.') # Item could not be found; assume it's unbuyable. # We use a cheap price estimate of 1,000,001 NP. # TODO: Items inserted in this way will have a wonky image property # since we have no way to look up their image. update_item(item_name, price=1000001, price_laxness=laxness) conn.commit()