def get_rss_entries(url, prevtime=None): import feedparser import sys sys.path.insert(0, '..') from lib import mb_code, strip_tags try: d = feedparser.parse(url) except: return [] statuses = [] for e in d.entries: title = mb_code(e.title) href = mb_code(e.links[0]['href']) try: content = mb_code(e.content[0].value) content = strip_tags(content) except: content = '' try: publishtime = e.published_parsed except: publishtime = e.updated_parsed msg = { 'title': title, 'url': href, 'content': content, } if prevtime is None or publishtime > prevtime: statuses.append((msg, publishtime)) return statuses
def neoquest(once=False): np = lib.NeoPage(path) move_dirs = cycle(movedir_cycle) #?action=items #?action=items&useitemid=220000&do=use while True: hp_result = np.search(r'Health: <B>(\d+)</B>/(\d+)') hp = int(hp_result[1]) hp_max = int(hp_result[2]) print(f'Health: {hp}/{hp_max}') if np.contains('lupe_combat.gif'): moves = np.findall( r'''<A HREF="javascript:;" onClick="setdata\('(.*?)', (.*?)\); return false;">''' ) if hp_max - hp > 60 and ('special', '200019') in moves: np.post(path, 'fact=special', 'type=200019') elif hp < 80 and ('item', '220004') in moves: np.post(path, 'fact=item', 'type=220004') elif hp < 80 and ('item', '220003') in moves: np.post(path, 'fact=item', 'type=220003') elif hp < 80 and ('item', '220002') in moves: np.post(path, 'fact=item', 'type=220002') elif hp < 80 and ('item', '220001') in moves: np.post(path, 'fact=item', 'type=220001') elif ('special', '4003') in moves: np.post(path, 'fact=special', 'type=4003') elif ('attack', '0') in moves: np.post(path, 'fact=attack', 'type=0') else: np.post(path, 'fact=noop', 'type=0') elif np.contains('Leader Board'): if once: break if hp_max - hp > 80: np.get(path, 'action=items') if np.contains('220003&do=use'): np.get(path, 'action=items', 'useitemid=220003', 'do=use') elif np.contains('220002&do=use'): np.get(path, 'action=items', 'useitemid=220002', 'do=use') elif np.contains('220001&do=use'): np.get(path, 'action=items', 'useitemid=220001', 'do=use') np.post(path) else: movedir = next(move_dirs) np.get(path, 'action=move', f'movedir={movedir}') elif np.contains('You are attacked by'): monster = np.search(r'You are attacked .*? <B>(.*?)</B>')[1] print(f'Fighting {monster}') np.post(path) elif np.contains('You defeated'): rewards = np.search( r'<CENTER><IMG SRC="http://images.neopets.com/nq/n/lupe_win.gif">(.*?)</CENTER>' )[1] rewards = lib.strip_tags(rewards) print(rewards) np.post(path, 'end_fight=1') else: print("I don't know how to handle this.") break
def shrine(): np = lib.NeoPage('/desert/shrine.phtml') np.post('/desert/shrine.phtml', 'type=approach') if np.contains('shrine_win.gif') or np.contains('shrine_scene.gif'): result = lib.strip_tags(np.search(r'\n<p>.*?<br clear="all">')[0]) print(f"Coltzan's Shrine: {result}") elif np.contains('Maybe you should wait a while'): print("Coltzan's Shrine: Already visited.") else: print("Coltzan's Shrine: Error.")
def apple_bobbing(): np = lib.NeoPage(path) if np.contains('Give it a shot!'): np.get(path, bobbing=1) message = np.search("<div id='bob_middle'>(.*?)</div>")[1].strip() message = lib.strip_tags(message) print(message) elif np.contains('blind underneath this hat'): print('Already apple bobbed today.') else: print("Couldn't find apple bobbing.")
def parse_response(data): data = txt_wrap_by_all('<div class="c">', '</div>', data) for i in data: if i.startswith('<span class="kt">[新]</span>'): i = i[len('<span class="kt">[新]</span>'):] msg = txt_wrap_by('</span>', '<span', i) uid = txt_wrap_by('/msg/chat/send?uid=', '&', i) if msg and uid: msg = lib.strip_tags(msg) msg = msg.replace(' ', '') yield (uid, msg)
def neoquest(once=False): np = lib.NeoPage(path) move_dirs = cycle(movedir_cycle) #?action=items #?action=items&useitemid=220000&do=use while True: hp_result = np.search(r'Health: <B>(\d+)</B>/(\d+)') hp = int(hp_result[1]) hp_max = int(hp_result[2]) print(f'Health: {hp}/{hp_max}') if np.contains('lupe_combat.gif'): moves = np.findall(r'''<A HREF="javascript:;" onClick="setdata\('(.*?)', (.*?)\); return false;">''') if hp_max - hp > 60 and ('special', '200019') in moves: np.post(path, 'fact=special', 'type=200019') elif hp < 80 and ('item', '220004') in moves: np.post(path, 'fact=item', 'type=220004') elif hp < 80 and ('item', '220003') in moves: np.post(path, 'fact=item', 'type=220003') elif hp < 80 and ('item', '220002') in moves: np.post(path, 'fact=item', 'type=220002') elif hp < 80 and ('item', '220001') in moves: np.post(path, 'fact=item', 'type=220001') elif ('special', '4003') in moves: np.post(path, 'fact=special', 'type=4003') elif ('attack', '0') in moves: np.post(path, 'fact=attack', 'type=0') else: np.post(path, 'fact=noop', 'type=0') elif np.contains('Leader Board'): if once: break if hp_max - hp > 80: np.get(path, 'action=items') if np.contains('220003&do=use'): np.get(path, 'action=items', 'useitemid=220003', 'do=use') elif np.contains('220002&do=use'): np.get(path, 'action=items', 'useitemid=220002', 'do=use') elif np.contains('220001&do=use'): np.get(path, 'action=items', 'useitemid=220001', 'do=use') np.post(path) else: movedir = next(move_dirs) np.get(path, 'action=move', f'movedir={movedir}') elif np.contains('You are attacked by'): monster = np.search(r'You are attacked .*? <B>(.*?)</B>')[1] print(f'Fighting {monster}') np.post(path) elif np.contains('You defeated'): rewards = np.search(r'<CENTER><IMG SRC="http://images.neopets.com/nq/n/lupe_win.gif">(.*?)</CENTER>')[1] rewards = lib.strip_tags(rewards) print(rewards) np.post(path, 'end_fight=1') else: print("I don't know how to handle this.") break
def lab_ray(): log = open('lab_ray.log', 'a') np = lib.NeoPage('/lab.phtml') np.post('/lab2.phtml', 'donation=100') pet_name = np.active_pet_name() np.post('/process_lab2.phtml', f'chosen={pet_name}') if np.contains('You can only use the lab once a day'): print(f'Lab ray: Already fired.') return result = lib.strip_tags(np.search(r'''The ray is fired (.*?)<FORM action="/lab.phtml">''')[1]) print(f'Lab ray: The ray is fired {result}') log.write(result + '\n')
def fruit_machine(): np = lib.NeoPage(path) if np.contains('Spin, spin, spin!'): ck = np.search(r'<input type="hidden" name="ck" value="(.*?)">')[1] np.post(path, 'spin=1', f'ck={ck}') prize = np.search(r'<div id="fruitResult">(.*?)</div>')[1].strip() prize = lib.strip_tags(prize) print(f'Fruit machine: {prize}') elif np.contains('already had your free spin'): print('Fruit machine: Already played.') else: print('Fruit machine: Error!')
def lab_ray(): log = open('lab_ray.log', 'a') np = lib.NeoPage('/lab.phtml') np.post('/lab2.phtml', 'donation=100') pet_name = np.active_pet_name() np.post('/process_lab2.phtml', f'chosen={pet_name}') if np.contains('You can only use the lab once a day'): print(f'Lab ray: Already fired.') return result = lib.strip_tags( np.search(r'''The ray is fired (.*?)<FORM action="/lab.phtml">''')[1]) print(f'Lab ray: The ray is fired {result}') log.write(result + '\n')
def forgotten_shore(): np = lib.NeoPage(path) if np.contains('already searched the coast'): print('Forgotten shore: Already searched today.') elif np.contains('nothing of interest to be found'): print('Forgotten shore: Nothing today.') elif np.contains('A deserted shore stretches'): _ref_ck = np.search(r"<a href='\?confirm=1&_ref_ck=(.*?)'>")[1] np.get(path, 'confirm=1', f'_ref_ck={_ref_ck}') result = np.search(r"<br>A deserted shore.*?<br><br>\n")[0] result = lib.strip_tags(result) print(f'Forgotten shore: {result}') else: print('Forgotten shore not yet unlocked?')
def wise_king(): np = lib.NeoPage() for _ in range(1): np.get(path) parts = np.findall(r'<div id="(.)p(\d+)Div">(.*?)</div>') params = make_params(parts) params_hum = ' '.join(p.split('=')[1] for p in params) print(f'Wise King: Saying: {params_hum}') np.post(path2, *params) if np.contains('you have already regaled'): print(f'Wise King: Already done.') else: result = np.search( r'''<div align='center'>(.*?)<br clear="all">''')[1] result = lib.strip_tags(result) print(f'Wise King: {result}')
def snowager(): dont_do_again = neotime.now_nst() + datetime.timedelta(hours=1) np = lib.NeoPage(path) if np.contains('The Snowager is awake'): print('Snowager: Awake.') return np.get(path2) if np.contains('You dont want to try and enter again'): print('Snowager: Already done.') return dont_do_again result = np.search(r'<p>(.*?)<p>.*?</center><center>') if result: result = lib.strip_tags(result[1]) print(f'Snowager: {result}') return dont_do_again else: print('Snowager: Error.')
def grumpy_king(): np = lib.NeoPage() for _ in range(2): np.get(path) if np.contains('try back in an hour'): print(f'Grumpy King: At lunch.') return parts = np.findall(r'<div id="(.)p(\d+)Div">(.*?)</div>') params = make_params(parts, fixed_parts) params_hum = ' '.join(p.split('=')[1] for p in params) print(f'Grumpy King: Asking: {params_hum}') np.post(path2, *params) result = np.search(r'''<div align='center'>(.*?)<br clear="all">''')[1] result = lib.strip_tags(result) print(f'Grumpy King: {result}') if 'already told me a joke today' in result: break
def get_rss_entries(url, prevtime=None, nhead=0): import feedparser import sys import time sys.path.insert(0, '..') from lib import mb_code, strip_tags try: d = feedparser.parse(url) except: return [] statuses = [] for e in d.entries: title = mb_code(e.title) href = mb_code(e.links[0]['href']) try: content = mb_code(e.content[0].value) content = strip_tags(content) except: content = '' publishtime = getattr(e, 'published_parsed', getattr( e, 'updated_parsed', None, )) msg = { 'title': title, 'url': href, 'content': content, 'entry': e, } for i in e: msg[i] = mb_code(e[i]) if isinstance(e[i], basestring) else e[i] if prevtime is None or publishtime is None or publishtime > prevtime: statuses.append((msg, publishtime)) if nhead > 0: statuses = statuses[:nhead] return statuses
def get_rss_entries(url, prevtime=None, nhead=0): import feedparser import sys import time sys.path.insert(0, '..') from lib import mb_code, strip_tags try: d = feedparser.parse(url) except: return [] statuses = [] for e in d.entries: title = mb_code(e.title) href = mb_code(e.links[0]['href']) try: content = mb_code(e.content[0].value) content = strip_tags(content) except: content = '' publishtime = getattr( e, 'published_parsed', getattr( e, 'updated_parsed', None, ) ) msg = { 'title': title, 'url': href, 'content': content, 'entry': e, } for i in e: msg[i] = mb_code(e[i]) if isinstance(e[i], basestring) else e[i] if prevtime is None or publishtime is None or publishtime > prevtime: statuses.append((msg, publishtime)) if nhead > 0: statuses = statuses[:nhead] return statuses
def tombola(): np = lib.NeoPage('/island/tombola.phtml') np.post('/island/tombola2.phtml') if np.contains('you are only allowed one'): print('Tombola: Already played.') return if np.contains('YOU ARE A WINNER!!!'): result = np.search(r'\n<center>(.*?)\n')[1] image = re.search(r"<img src='http://images.neopets.com/items/(.*?)'", result)[1] result = lib.strip_tags(result) print(f'Tombola: Won. {result} ({image})') elif np.contains('you win a Booby Prize'): prize = np.search(r'<b>Your Prize - (.*?)</b>')[1] print(f'Tombola: Won booby prize: {prize}') elif np.contains("and you don't even get a booby prize"): print('Tombola: Lost') else: print('Tombola: Unknown result. TODO')
def healing_springs(): np = lib.NeoPage(path) np.post(path, 'type=heal') result = np.search(r'''\n<center>(.*?)<br clear="all">''')[1] result = lib.strip_tags(result) print(f'Healing springs: {result}')
def pirate_academy(): np = lib.NeoPage() np.get(path, 'type=status') # TODO: Handle more than one pet, train particular pet, etc. table = np.search( r'<b>Current Course Status</b>.*?<table.*?>.*?<br clear="all">')[0] tds = re.findall(r'<td.*?>(.*?)</td>', table) status = tds[0] stats = tds[1].split('<br>') stats = [re.sub(r'<.*?>', '', s).split()[-1] for s in stats[1:-2] if s] Lvl, Str, Def, Mov, Hp = map(int, stats) time_til = tds[2] if 'Course Finished!' in time_til: pet_name = np.search( r"<form.*?name='pet_name' value='(.*?)'.*?</form>")[1] np.post('/pirates/process_academy.phtml', 'type=complete', f'pet_name={pet_name}') result = lib.strip_tags(np.content.splitlines()[-1]) print(f'Pirate Academy: Finished course. {result}') return pirate_academy() elif 'is not on a course' in status: np.get(path, 'type=courses') r = np.search( r"<select name='pet_name'><option value='(.*?)'>.*? - (.*?)</select>" ) pet_name = r[1] rank = r[2] skill = None if any(stat > 2 * Lvl for stat in [Str, Def, Mov, Hp]): skill = 'Level' else: skill = min(zip([Str, Hp, Def, Mov], ('Strength', 'Endurance', 'Defence', 'Agility')), key=lambda x: x[0])[1] print(f'Pirate Academy: Training {skill} for {pet_name}') np.post('/pirates/process_academy.phtml', 'type=start', f'course_type={skill}', f'pet_name={pet_name}') return pirate_academy() elif "<input type='submit' value='Pay'>" in np.content: print(f'Pirate Academy: Paying for lesson') pet_name = np.search( f"<input type='hidden' name='pet_name' value='(.*?)'>")[1] np.post('/pirates/process_academy.phtml', f'pet_name={pet_name}', 'type=pay') np.get(path, 'type=status') print(f'Status: {lib.strip_tags(status)}') print(f'Stats: Lvl{Lvl} Str{Str} Def{Def} Mov{Mov} Hp{Hp}') print(time_til) total_time = datetime.timedelta() if np.contains('hr'): result = np.search('(\d+) hr') if result: total_time += datetime.timedelta(hours=int(result[1])) if np.contains('minute'): result = np.search('(\d+) minute') if result: total_time += datetime.timedelta(minutes=int(result[1])) if np.contains('second'): result = np.search('(\d+) second') if result: total_time += datetime.timedelta(seconds=int(result[1])) print(f'Time til: {total_time}') return neotime.now_nst() + total_time + datetime.timedelta(minutes=1)
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()
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 base_academy(path, path_process): np = lib.NeoPage() np.get(path, 'type=status') table = np.search( r'<b>Current Course Status</b>.*?<table.*?>.*?<br clear="all">')[0] trs = re.findall(r'<tr>(.*?)</tr>', table) finish_times = [] for tr1, tr2 in zip(trs[::2], trs[1::2]): pet_name, status = re.search(r'<b>(.*?) \(Level \d+\) is (.*?)</b>', tr1).group(1, 2) print(f'{pet_name}: {status}') Lvl, Str, Def, Mov, Hp = [ int(lib.strip_tags(x).split()[-1]) for x in tr2.split('<br>')[1:6] ] if 'Time till course finishes' in tr2: finish_time = datetime.timedelta() if np.contains('hr'): result = np.search('(\d+) hr') if result: finish_time += datetime.timedelta(hours=int(result[1])) if np.contains('minute'): result = np.search('(\d+) minute') if result: finish_time += datetime.timedelta(minutes=int(result[1])) if np.contains('second'): result = np.search('(\d+) second') if result: finish_time += datetime.timedelta(seconds=int(result[1])) finish_times.append(finish_time) continue # Only train pet specified by PET_NAME. if pet_name != os.environ.get('PET_NAME'): continue # Complete any course that's done. if 'Complete Course!' in tr2: np.post(path_process, 'type=complete', f'pet_name={pet_name}') result = lib.strip_tags(np.content.splitlines()[-1]) print(f'Finished course for {pet_name}. {result}') want_money_indicators = [ "<input type='hidden' name='type' value='pay'>", 'has not been paid for yet', ] if any(x in tr2 for x in want_money_indicators): # Pay for current course. np.get(path) items = re.findall('<b>([\w \-]+?)</b><img', tr2) for item in items: inventory.withdraw_sdb(item) np.post(path_process, f'pet_name={pet_name}', 'type=pay') else: # Start a new course np.get(path, 'type=courses') # Which skill to train? # TODO: Take advantage of island training school's endurance to 3x health feature. skill = which_skill(Lvl, Str, Def, Mov, Hp) print(f'Training {skill} for {pet_name}') np.post(path_process, 'type=start', f'course_type={skill}', f'pet_name={pet_name}') if len(finish_times) == 0: return base_academy(path, path_process) else: return neotime.now_nst() + min(finish_times) + datetime.timedelta( minutes=1)