mirror of
https://github.com/User0332/rewards-farmer.git
synced 2026-09-16 01:31:36 +00:00
Merge pull request #49 from mardausdennis/fix/daily-set-promo-links
match daily set activities by target instead of by position
This commit is contained in:
+12
-1
@@ -24,6 +24,9 @@ from constants import USER_DATA_DIR, PROFILE_NAME
|
|||||||
|
|
||||||
RENDER_TIMEOUT = 60
|
RENDER_TIMEOUT = 60
|
||||||
|
|
||||||
|
# How many activities a fully rendered daily set panel holds.
|
||||||
|
DAILY_SET_ACTIVITIES = 3
|
||||||
|
|
||||||
|
|
||||||
def build_driver():
|
def build_driver():
|
||||||
options = webdriver.EdgeOptions()
|
options = webdriver.EdgeOptions()
|
||||||
@@ -149,7 +152,15 @@ def main():
|
|||||||
driver.execute_script("arguments[0].scrollIntoView({block:'center'});", opener)
|
driver.execute_script("arguments[0].scrollIntoView({block:'center'});", opener)
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
driver.execute_script("arguments[0].click();", opener)
|
driver.execute_script("arguments[0].click();", opener)
|
||||||
wait_until(lambda: elements.get_sidebar_section() is not None, 30)
|
|
||||||
|
# Waiting for the section only tells you the panel opened, not that
|
||||||
|
# it filled. It hydrates progressively, so a check that runs on the
|
||||||
|
# first non-empty state reports whatever happened to be rendered at
|
||||||
|
# that moment, which is why this came out differently run to run.
|
||||||
|
# Same wait as complete_bing_daily_set: hold out for the full set,
|
||||||
|
# and report what is there if it never arrives.
|
||||||
|
wait_until(lambda: len(elements.get_daily_set_elements()) >= DAILY_SET_ACTIVITIES, 30)
|
||||||
|
|
||||||
report.check("get_daily_set_elements", elements.get_daily_set_elements)
|
report.check("get_daily_set_elements", elements.get_daily_set_elements)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -154,11 +154,66 @@ class ElementSelectionUtils:
|
|||||||
try:
|
try:
|
||||||
return self._button_containing(Labels.DAILY_SET_STREAK)
|
return self._button_containing(Labels.DAILY_SET_STREAK)
|
||||||
except NoSuchElementException:
|
except NoSuchElementException:
|
||||||
return self._streaks_button(3)
|
pass
|
||||||
|
|
||||||
|
# The positional fallback only helps if what sits there really is the
|
||||||
|
# daily set entry. On a partially rendered streaks section it is not:
|
||||||
|
# observed returning the mobile app entry, and clicking that opens the
|
||||||
|
# app store page instead of the panel, which is what the reports in #45
|
||||||
|
# and #46 describe. Check before handing it back, and skip the task
|
||||||
|
# rather than click the wrong streak.
|
||||||
|
candidate = self._streaks_button(3)
|
||||||
|
label = (candidate.text or "").strip()
|
||||||
|
|
||||||
|
if "daily set" not in label.lower():
|
||||||
|
raise NoSuchElementException(
|
||||||
|
"daily set opener not found by label, and position 3 holds "
|
||||||
|
f"{label.splitlines()[0] if label else '<empty>'!r} instead"
|
||||||
|
)
|
||||||
|
|
||||||
|
return candidate
|
||||||
|
|
||||||
def get_daily_set_elements(self):
|
def get_daily_set_elements(self):
|
||||||
# The first link in the opened panel is the progress row, not an activity.
|
"""The daily set activities in the opened panel.
|
||||||
return self.get_sidebar_section().find_elements(By.TAG_NAME, "a")[1:]
|
|
||||||
|
Everything after the first link is not reliably an activity. The panel
|
||||||
|
also carries promotional links, a referral card and a Bing app promo have
|
||||||
|
both been observed sitting between the progress row and the activities.
|
||||||
|
Handing one of those back gets it clicked, which navigates away from
|
||||||
|
rewards.bing.com, and every element captured beforehand then goes stale.
|
||||||
|
|
||||||
|
Matching on a Bing search alone was too narrow. "Turn referrals into
|
||||||
|
rewards" is a real daily set activity that awards points, and it points
|
||||||
|
at a rewards URL rather than a search. Three shapes have been observed:
|
||||||
|
|
||||||
|
1. `bing.com/search?q=...`, the classic search activity,
|
||||||
|
2. `bing.com/rewards/...`, seen on daily sets alongside the searches,
|
||||||
|
3. `rewards.bing.com/...`, the same activity written against the
|
||||||
|
rewards host.
|
||||||
|
|
||||||
|
The Bing app promo behind #45 is on `bingapp.microsoft.com`, so it stays
|
||||||
|
out of all three, and so does anything else off those hosts. If nothing
|
||||||
|
matches, return nothing: clicking a promo is worse than skipping the
|
||||||
|
task, and the caller already reports the shortfall.
|
||||||
|
"""
|
||||||
|
activities = []
|
||||||
|
|
||||||
|
for link in self.get_sidebar_section().find_elements(By.TAG_NAME, "a"):
|
||||||
|
try:
|
||||||
|
if self._is_daily_set_activity(link.get_dom_attribute("href") or ""):
|
||||||
|
activities.append(link)
|
||||||
|
except StaleElementReferenceException:
|
||||||
|
continue
|
||||||
|
|
||||||
|
return activities
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _is_daily_set_activity(href: str) -> bool:
|
||||||
|
"""Whether an href in the daily set panel is an activity rather than a promo."""
|
||||||
|
return any(
|
||||||
|
marker in href
|
||||||
|
for marker in ("bing.com/search", "bing.com/rewards", "rewards.bing.com/")
|
||||||
|
)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# explore on bing (absent in en-US, present in some other markets)
|
# explore on bing (absent in en-US, present in some other markets)
|
||||||
|
|||||||
@@ -190,5 +190,59 @@ class DailySetOpener(unittest.TestCase):
|
|||||||
selectors_for(driver).get_open_daily_set_button()
|
selectors_for(driver).get_open_daily_set_button()
|
||||||
|
|
||||||
|
|
||||||
|
class DailySetActivityUrls(unittest.TestCase):
|
||||||
|
"""Which hrefs in the panel count as activities and which are promos."""
|
||||||
|
|
||||||
|
def _links(self, *hrefs):
|
||||||
|
links = [FakeElement(text="activity", attributes={"href": h}) for h in hrefs]
|
||||||
|
panel = FakeElement(
|
||||||
|
attributes={"id": "react-aria-42"},
|
||||||
|
children={(By.TAG_NAME, "a"): links},
|
||||||
|
)
|
||||||
|
|
||||||
|
return FakeDriver(children={(By.TAG_NAME, "section"): [panel]})
|
||||||
|
|
||||||
|
def test_matches_a_plain_search_activity(self):
|
||||||
|
found = selectors_for(
|
||||||
|
self._links("https://www.bing.com/search?q=weather")
|
||||||
|
).get_daily_set_elements()
|
||||||
|
|
||||||
|
self.assertEqual(len(found), 1)
|
||||||
|
|
||||||
|
def test_matches_a_rewards_path_activity(self):
|
||||||
|
# "Turn referrals into rewards" awards points and is not a search.
|
||||||
|
found = selectors_for(
|
||||||
|
self._links("https://www.bing.com/rewards/panelflyout")
|
||||||
|
).get_daily_set_elements()
|
||||||
|
|
||||||
|
self.assertEqual(len(found), 1)
|
||||||
|
|
||||||
|
def test_matches_an_activity_on_the_rewards_host(self):
|
||||||
|
found = selectors_for(
|
||||||
|
self._links("https://rewards.bing.com/redeem/12345")
|
||||||
|
).get_daily_set_elements()
|
||||||
|
|
||||||
|
self.assertEqual(len(found), 1)
|
||||||
|
|
||||||
|
def test_skips_the_bing_app_promo(self):
|
||||||
|
# The link behind #45. Clicking it leaves the rewards host and every
|
||||||
|
# element captured beforehand goes stale.
|
||||||
|
found = selectors_for(
|
||||||
|
self._links("https://bingapp.microsoft.com/bing?adjust=14u4j3kz")
|
||||||
|
).get_daily_set_elements()
|
||||||
|
|
||||||
|
self.assertEqual(found, [])
|
||||||
|
|
||||||
|
def test_keeps_activities_and_drops_promos_from_the_same_panel(self):
|
||||||
|
found = selectors_for(self._links(
|
||||||
|
"https://bingapp.microsoft.com/bing?adjust=14u4j3kz",
|
||||||
|
"https://www.bing.com/search?q=news",
|
||||||
|
"https://www.bing.com/rewards/panelflyout",
|
||||||
|
"https://play.google.com/store/apps/details?id=com.microsoft.bing",
|
||||||
|
)).get_daily_set_elements()
|
||||||
|
|
||||||
|
self.assertEqual(len(found), 2)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user