Commit Graph
105 Commits
Author SHA1 Message Date
abhijitgorde5-crypto 03636ac984 Replace print() with logger calls in llm_utils, random_image_for_visual_search, and rewards_tasks 2026-09-08 23:02:34 +05:30
abhijitgorde5-crypto 049a119e36 Pacing Bing searches to 5.5-7.5s delay to prevent MS Rewards rate-limiting throttling 2026-09-08 00:23:24 +05:30
abhijitgorde5-crypto 7686537dc1 Address code review: refactor move_to_and_click retries, simplify wait_for_then_click, resolve conflicts with main 2026-09-07 23:55:04 +05:30
Carl Furtado 4121c42552 Merge pull request #69 from TheMallyGuy/main
feature: spoof the reward app's `X-Rewards-Source` to unlock rewards app only quests
2026-09-07 10:23:08 -04:00
Carl Furtado de669da145 normalize indentation to tabs 2026-09-07 10:13:24 -04:00
Mally Dang 3c6b29e857 chore: move header setting thing to RewardsTaskUtils 2026-09-07 18:06:43 +07:00
Carl Furtado bc6fda1fd5 Merge pull request #76 from mardausdennis/fix/skip-vs-timeout-messages
tell absence and a slow render apart when a task fails
2026-09-06 21:58:36 -04:00
mardausdennis 15782d1836 tell absence and a slow render apart when a task fails
Every failed task printed the same reason, because the blanket catch in
complete_all_tasks treated NoSuchElementException and TimeoutException as one
finding. A wait that expires says nothing about whether the section was there,
since wait_for_element swallowed whatever the getter raised and returned False.
A market that does not ship visual search and a points panel that was on screen
and slow both arrived as TimeoutException

wait_for_element now keeps the last reason the getter gave and raises
ElementNeverAppeared when nothing was in the DOM for the whole wait.
_container_by_id raises ElementNotReady when the id is present but no visible
copy has content yet, which is the hydrating case it used to report as missing.
Both subclass what they refine, so claim_bonus_points and
complete_bing_daily_set keep working untouched

A task that was reached and then ran out of time is now a [FAIL] rather than a
[SKIP], because it may have left points behind. Absence stays a [SKIP]

56 tests pass, 20 of them new. A mutation check backs them, forcing every
expired wait to count as absence fails 2, and collapsing the two branches back
into one [SKIP] fails 4

Refs #52
2026-09-03 22:38:01 +02:00
abhijitgorde5-crypto 4c67f3f46e Merge upstream main and resolve merge conflict in src/tab_utils.py 2026-09-02 18:22:39 +05:30
Mally Dang afccf81f8f feat: spoof the rewards app headers 2026-09-02 12:54:14 +07:00
Carl Furtado 0a3ab5ba6f Merge pull request #36 from ethanstoner/feat/query-sources
logging, a query source without an LLM, multi-account and docker
2026-08-31 09:33:29 -04:00
Carl Furtado ddbc6512a3 Merge pull request #55 from mardausdennis/fix/search-close-button
read the search row before waiting on the panel's close button
2026-08-31 09:12:25 -04:00
Carl Furtado ad9a1aadd4 Merge pull request #49 from mardausdennis/fix/daily-set-promo-links
match daily set activities by target instead of by position
2026-08-31 09:08:40 -04:00
mardausdennis 8ff99c0e9d cover the rewards urls the daily set actually uses
Matching on bing.com/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, so the filter skipped it and the run came up short without saying why.

Three shapes now count, bing.com/search, bing.com/rewards and rewards.bing.com. The bing app promo from #45 sits on bingapp.microsoft.com and stays out of all three, which is what keeps that fix intact. Five tests, one per shape plus a mixed panel.
2026-08-30 18:15:24 +02:00
mardausdennis 28f3bdb101 Merge remote-tracking branch 'origin/main' into fix/daily-set-promo-links 2026-08-30 18:15:23 +02:00
Ethan Stoner cdafcc59da merge upstream: NoSuchWindowException when closing a tab
Upstream widened the except in close_all_tabs_except to catch
NoSuchWindowException; this branch had rewritten the same two lines from print
to logging. Both are wanted, so the resolution keeps the wider except and the
logger call, and takes the new import.

Claude-Session: https://claude.ai/code/session_01RCncv1HC4UvoJcYqBtwxrt
2026-08-30 08:47:35 -07:00
Carl Furtado 619b282fd8 catch NoSuchWindowException in tab_utils 2026-08-30 11:44:04 -04:00
Ethan Stoner a85e88ac1a record that a Linux host does hand its sign-in to the container
The Docker section still said the Linux case was untested. It is not: the
two-account run was done that way, with a profile signed in on a Linux host
opening in the container already on the dashboard. Only macOS is still
unmeasured, and it is expected to fail the way Windows does.

Also say what a killed browser leaves behind. A profile whose browser was
killed keeps a SingletonLock naming the machine that wrote it, and the
container reads that as the profile being open elsewhere, which produces the
same startup error a genuinely open window does and is not obvious from it.

Claude-Session: https://claude.ai/code/session_01LVjQemhtfybkHjJFxqMu19
2026-08-29 15:20:56 -07:00
Ethan Stoner f064468db0 match the unittest convention the selector tests introduced
#50 landed a tests directory using stdlib unittest, discovered with
python -m unittest discover -s tests. The multi-account checks were a
standalone script with their own runner, so discovery would have walked
straight past them.

Same cases, rewritten as TestCases. The layer that starts Edge twice is
behind REWARDS_BROWSER_TESTS rather than a --browser argument, since
discovery does not pass arguments through.

Claude-Session: https://claude.ai/code/session_019PWUtibJn81iDbxZ4hzeU3
2026-08-29 14:36:08 -07:00
Ethan Stoner 9c703d2d57 Merge remote-tracking branch 'origin/main' into feat/query-sources 2026-08-29 14:33:06 -07:00
Ethan Stoner 31b2173380 check multi-account against two real profiles
Five layers, cheapest first: which accounts a configuration produces, the
flags each one hands Edge, the run loop's ordering and exit codes, one
account failing every way it can without ending the batch, and two real
Edge profiles holding two independent, persistent identities.

Layers 1 to 4 need nothing installed beyond selenium and run in a second.
Layer 5 starts Edge twice and reaches bing.com, so it is opt in behind
--browser. It keys on bing.com's own MUID rather than an injected cookie:
a cookie added through webdriver is not written to the profile the way a
Set-Cookie is, so it would prove nothing about a sign-in surviving.

Claude-Session: https://claude.ai/code/session_019PWUtibJn81iDbxZ4hzeU3
2026-08-29 12:51:52 -07:00
Ethan Stoner c212ba8131 note that a Windows host cannot hand its sign-in to the container 2026-08-29 11:45:30 -07:00
abhijitgorde5-crypto 27a98d9b93 Fix StaleElementReferenceException, add Ollama offline fallback, and PIL visual search generator 2026-08-29 23:41:57 +05:30
Carl Furtado 62881d6d96 Merge pull request #50 from mardausdennis/tests/selector-logic
add tests for the selector logic that keeps breaking
2026-08-29 13:29:51 -04:00
mardausdennis 806156c6c6 do not click a different streak when the daily set label is missing
The positional fallback returned whatever sat at index 3 of the streaks section. On a partially rendered page that is not the daily set: observed live returning 'Mobile App | Check-in: 0/1', and clicking it opens the app store page instead of the panel, which is what #45 and #46 describe. It now checks the label before handing the button back and raises otherwise, so the task is skipped rather than the wrong streak clicked.
2026-08-29 18:46:47 +02:00
mardausdennis dd92cface3 read the search row before waiting on the panel's close button
read_search_points waited for the close button before reading anything, so a panel that rendered its content but not its button failed the whole search task while the number was already on screen. Traced to that wait with a stacktrace. Closing is best effort now.
2026-08-28 23:46:41 +02:00
mardausdennis 238050ba9a wait for the daily set panel to fill before checking it
The panel hydrates progressively, so waiting for the section only tells you it opened, not that it filled. A check running on the first non-empty state reports whatever happened to be rendered at that moment, which is why the daily set came out differently run to run. Same wait as complete_bing_daily_set: hold out for the full set, report what is there if it never arrives.
2026-08-28 22:44:01 +02:00
Ethan Stoner 0f18795f51 refuse profile names that Win32 resolves onto another account's directory
Win32 strips a trailing dot off a path component and python's normalisation
does not, so such a name is not the directory it reads as. REWARDS_ACCOUNTS=...
resolved to data-dir itself, which is the default profile the resolved-path
check was added to keep named accounts out of, and personal,personal. passed
the duplicate check as two entries while sharing one profile on disk. Both
break the one-directory-per-account guarantee this module exists for.

Reject the shape by name, and resolve with realpath rather than abspath so a
link or a junction under data-dir is followed to where it really goes.
2026-08-28 08:53:37 -07:00
Ethan Stoner 3e9e953c75 keep one account's failure from ending a batched run
Only SessionNotCreatedException was isolated. Every other way an account can
fail reached main() and took the accounts after it with them: a driver that
will not start for another reason, an unwritable profile directory, the first
page not loading, the browser dying mid-run, or quit() raising because it was
already gone. With REWARDS_ACCOUNTS=one,two,three and the middle one failing,
three never ran and main() exited on a traceback instead of an exit code.

Catch it around run_account, report it the way a failed task is reported, and
carry on. KeyboardInterrupt is left alone so Ctrl-C still stops the run. The
quit() in run_account is guarded too, so a tidy-up that raises no longer hides
the failure it was tidying up after.
2026-08-28 08:53:28 -07:00
mardausdennis 36428d5cb2 add tests for the selector logic that keeps breaking
Four bugs that reached real runs are covered: a point value the parser could not read, the wrong row of the breakdown panel, a container that looks right but is empty, and a label that matches two different buttons.

Stdlib unittest with small fakes for the selenium calls the selectors make, so this adds no dependency and needs no browser. Run with python -m unittest discover -s tests.
2026-08-28 11:08:17 +02:00
mardausdennis f23afb7568 match daily set activities by target instead of by position
get_daily_set_elements returned everything after the first link in the panel. The panel also carries promotional links, so position hands one back as an activity. Clicking it leaves rewards.bing.com and every element captured before that goes stale, which is the exception reported in #45.

Activities always point at a Bing search, so match on that. When nothing matches it returns nothing rather than falling back to position, since clicking a promo is worse than skipping the task and complete_bing_daily_set already reports the shortfall.
2026-08-28 11:00:44 +02:00
Ethan Stoner 1a224919b2 reject profile names that resolve outside data-dir
The character check allowed "." and "..", which are made entirely of allowed
characters and still walk out of the directory, so the traversal guard only
stopped the cases containing a separator. Reject both by name and check the
resolved path against the profile root as well, since the character set
constrains the characters rather than where they point.

Also drop the MouseUtils and KeyboardUtils built in run_account and never
used, RewardsTaskUtils builds its own, along with the two imports that
leaves unused, and say "lowercased" in the no-source warning, which is what
the code returns.
2026-08-27 15:26:01 -07:00
Ethan Stoner 2f960c6a03 mount the visual search image into the container
visual_search.jpg is gitignored and excluded by .dockerignore, so the
container had no file at the path rewards_tasks.py uploads and the visual
search task was the one task that could not run in it. Bind mount it from
the project root, where src/random_image_for_visual_search.py writes it.

Also add the blank line the Logging heading needs to render.
2026-08-27 15:16:08 -07:00
Ethan Stoner b697ea198b merge upstream: the random image downloader
Both sides added a line after *.txt in .gitignore, so keep both: *.log
from the logging change here and visual_search.json from upstream.
2026-08-27 15:13:01 -07:00
Carl Furtado 9e919e4ae2 Remove visual_search.jpg from repo root 2026-08-27 16:19:06 -04:00
Carl Furtado 9a19da1737 Merge pull request #3 from RealEvoranz/random_image_downloader
Random image downloader helper script
2026-08-27 13:58:01 -04:00
RealEvoranz 1dada212a8 Update README.md 2026-08-27 09:12:30 -04:00
RealEvoranz e964d83042 fixed old metadata 2026-08-27 09:02:04 -04:00
RealEvoranz 31426c174a Merge branch 'main' into random_image_downloader 2026-08-27 09:00:08 -04:00
RealEvoranz 2cb12f2443 Update poetry.lock 2026-08-27 08:59:56 -04:00
RealEvoranz b97613b5b8 Merge remote-tracking branch 'upstream/main' 2026-08-27 08:59:48 -04:00
Ethan Stoner 817d5bb63d carry the daily set warning from #37 into logging 2026-08-27 00:27:29 -07:00
Ethan Stoner 8059e4a6eb merge upstream: daily set hydration and the readme image name 2026-08-27 00:21:29 -07:00
Carl Furtado 414a6a589f ensure image name is visual_search.jpg in readme 2026-08-26 23:54:17 -04:00
Carl Furtado aa0d07841b Merge pull request #37 from mardausdennis/fix/daily-set-hydration
wait for the daily set panel to fill, and give the breakdown time to render
2026-08-26 22:45:49 -04:00
Ethan Stoner dc9860189b merge upstream: #24's wheel scrolling 2026-08-26 19:41:22 -07:00
Carl Furtado 3f5e02c83d Merge pull request #33 from ethanstoner/fix/breakdown-wait
wait for the breakdown panel's content, not just its container
2026-08-26 22:40:56 -04:00
Carl Furtado b4116a14e1 Merge pull request #24 from mardausdennis/fix/wheel-scroll-misc-cards
drive the misc cards scrolling with wheel input instead of fixed bursts
2026-08-26 22:39:01 -04:00
mardausdennis efe4b07eb8 wait for the daily set panel to fill, and give the breakdown time to render
complete_bing_daily_set indexed [0] [1] [2] on whatever wait_for_element returned first. The panel hydrates progressively, so that can be a single activity, and the task died with IndexError before touching the other two. Wait for the full set, fall back to what is there, and re-read per index since a click can re-render the panel.

read_search_points ran into the default 10s timeout because it starts after the earlier tasks navigated away, so the earn page re-renders from scratch first. That skipped the whole search task while points were still available.
2026-08-27 00:38:58 +02:00
Ethan Stoner 823dcbfc75 merge the logging work into the feature branch
Both change main.py and README.md, and three separate pull requests touching
the same entry point is worse for review than one. Resolved by keeping every
section of the README and folding the logging setup into the new multi-account
main, so the profile-in-use message from #35 is now a logger.error and uses
log_utils.exception_summary rather than repeating the truncation inline.

queries.py moves to logging with the rest of the runtime.
2026-08-26 15:19:04 -07:00