Commit Graph
103 Commits
Author SHA1 Message Date
Ethan Stoner 92159cbc13 wait for the breakdown panel's content, not just its container
check_selectors reports FAILED for two selectors that are fine.

The wait before them is satisfied by a placeholder. get_sidebar_section
returns the first section whose id starts with react-aria, and that section is
in the DOM as soon as the panel opens, holding a "Loading..." placeholder. So
`wait_until(get_sidebar_section() is not None)` returns immediately, and the
two selectors that read the panel's text then read "Loading..." and raise.

The report's own output shows it: the section that resolves OK has the text
"Loading...", and the two entries under it fail.

Before, on a healthy en-US account:

    OK       get_sidebar_section        'Loading...'
    FAILED   get_points_earned_from_searches_on_points_breakdown
    FAILED   get_close_button_on_points_breakdown
    OK=10  ABSENT=1  FAILED=2

After:

    OK       get_sidebar_section        "Points breakdown | Today's points | 480 | To"
    OK       get_points_earned_from_searches_on_points_breakdown (25, 25)
    OK       get_close_button_on_points_breakdown
    OK=12  ABSENT=1  FAILED=0

This matters more than a cosmetic miscount. The README asks people to paste
this output into bug reports and says FAILED is what needs fixing, so a false
FAILED sends both the reporter and whoever triages it after selectors that
work. The bot itself was never affected, read_search_points reaches the same
selector through wait_for_element and so does wait.

Waiting on the content rather than the container keeps a genuine breakage
reporting FAILED; it just costs the timeout first.
2026-08-26 14:38:46 -07:00
Carl Furtado 6f6ffa3fa4 Merge pull request #32 from ethanstoner/fix/python-floor
lower the python floor to 3.12
2026-08-26 15:48:38 -04:00
Ethan Stoner e3a15aa9cd lower the python floor to 3.12
Fixes #26.

`requires-python` was `>=3.14`. On anything older poetry declines to create an
environment, and it says so on stderr while leaving stdout empty. The README
tells the user to run `iex (poetry env activate)`, `iex` only sees stdout, and
PowerShell reports

    Invoke-Expression : Cannot bind argument to parameter 'Command' because it
    is null.

which points at `iex` rather than at the install that never happened. The
first reply on the issue guessed the reporter was missing `iex`, which is a
built-in alias, so the error is actively misleading.

3.14 looks stricter than anything the code needs. Every file in src/ compiles
on 3.10, no 3.13 or 3.14 only syntax appears anywhere in the tree, and the
full task set was run end to end on 3.12.10 against a live account with all
six tasks completing. The dependency graph agrees: numpy is the highest floor
in the lock at >=3.12, so 3.12 is where the real constraint sits.

Verified after the change on 3.12.10: `poetry install` creates the
environment, `poetry env activate` emits a real activation command instead of
nothing, `iex (poetry env activate)` activates in PowerShell, and every
dependency plus every module under src/ imports.

The lock is regenerated rather than left stale, since `requires-python` feeds
its content hash. The only substantive change is a typing_extensions marker
for python_version < 3.13; the package set is unchanged at 203. Regenerated
with poetry 2.4.1 to match the version that wrote the existing file.

The README gains the version bump and a short note on the failure, so anyone
who hits it while running an older Python can recognise it.
2026-08-26 12:18:09 -07:00
Ethan Stoner 78a4aff513 quieten routine tab logging and harden the failure summary
Follow-ups from running the conversion against a live account.

Tab open/close bookkeeping moves from info to debug. It was 19 of the 33
records in a full run, so the six task outcomes that are the point of the
summary were outnumbered three to one by tab handles and query strings. The
"could not close" case stays at warning, a tab that will not close is a real
problem rather than bookkeeping.

The [FAIL] summary moves into log_utils.exception_summary, which takes the
first line, drops the "(Session info: ...)" fragment and caps the result. A
selenium exception embeds the whole msedgedriver stacktrace in str(), and the
cap means a pathological message cannot push a screenful of text into one
record. The cut marker is ASCII because this can land on a Windows console
whose encoding cannot represent an ellipsis.

The suppressed-library list was checked rather than guessed: with the root
logger wide open, a real browser session plus one ollama call produced records
from httpx, httpcore, urllib3 and selenium only, and nothing else. That set is
already pinned. Worth noting selenium alone emits 45 records for a single page
load, so without the pinning the debug mode this PR recommends for bug reports
would be unusable.
2026-08-26 11:51:58 -07:00
Ethan Stoner 5c475cab05 switch the bot runtime from print to logging
Closes #14.

The runtime modules now log through the stdlib logging module. A new
log_utils.setup_logging is called once from main.py, and each module holds
its own logging.getLogger(__name__) so every line says which module it came
from.

The [INFO] and [WARNING] prefixes are gone, since the level field carries
that now. [OK], [SKIP] and [FAIL] stay in the message text: they are the
per-task outcome summary from complete_all_tasks rather than severities, and
folding them into the level would erase the run summary. They map to info,
warning and error, which is the one thing print could not express, a real
failure now sorts above a task the current UI variant simply does not ship.

Two things fall out of having levels at all:

- REWARDS_FARMER_LOG_LEVEL=DEBUG attaches the traceback to every [FAIL],
  which is the stack trace that bug reports keep having to be asked for.
- REWARDS_FARMER_LOG_FILE writes the same output to a file, so an unattended
  run can be read after the fact.

Both are off by default, so a normal run looks the same as before apart from
the timestamp and level columns.

The [FAIL] summary keeps only the first line of the exception message. A
selenium exception carries the whole msedgedriver stacktrace inside str(),
tens of lines of it, which would turn one task into one screenful and make
the log file impossible to scan. The full detail is still there with the
traceback on debug.

The console stream is stdout rather than the StreamHandler default of stderr,
so anyone already redirecting stdout keeps getting the output there, and its
error handler is set to replace. Card descriptions are scraped from the page
and are not ASCII outside the en-US market, and the Windows console encoding
raises on them.

check_selectors.py, fitts_law.py and analyze_keypresses.py are left on print.
Their output is formatted report text, and prefixing every row of a
diagnostic table with a timestamp and a level makes it harder to read.
2026-08-26 11:12:20 -07:00
RealEvoranz 37b0050166 renamed 2026-08-25 22:28:51 -04:00
RealEvoranz c75afb7ce6 updated poetry.lock 2026-08-25 22:20:07 -04:00
RealEvoranz 44ae02eb2a refining 2026-08-25 22:14:13 -04:00
RealEvoranz 2ef072f17d Update README.md 2026-08-25 22:10:32 -04:00
RealEvoranz 255c8b05fc outputs as jpg not png 2026-08-25 22:09:50 -04:00
RealEvoranz cafc5097bd Merge remote-tracking branch 'upstream/main' into random_image_downloader 2026-08-25 22:02:13 -04:00
mardausdennis 43aadf7ded drive the misc cards scrolling with wheel input instead of fixed bursts
The card loop fired fixed 100px scroll events back to back with no pauses, which is the jumpy scrolling, and its while-not-in-viewport loop was unbounded, so a card that never fits the viewport completely would hang the run forever. The way back up unwound a counted number of steps, which lands wrong when the page height changes while cards update.

Scrolling is now wheel input with varying step sizes and short pauses, bounded, aimed at centering the target. The return reads the actual scroll position instead of counting.
2026-08-26 00:28:01 +02:00
Carl Furtado e2a06275f3 Merge pull request #4 from mardausdennis/fix/market-independent-selectors
fix selectors that only resolve in the en-US market
2026-08-25 16:50:13 -04:00
Carl Furtado 0ee5e1c9bb Merge pull request #20 from mardausdennis/fix/measure-search-quota
fill the search quota by measuring instead of assuming a rate
2026-08-25 16:40:42 -04:00
mardausdennis 77d9ec2a0f fill the search quota by measuring instead of assuming a rate
searches_needed was computed once as (max - earned) // 5 and never re-checked. Two assumptions fail in practice: some markets award 3 points per search rather than 5, and the daily maximum itself is not stable, observed as 15, 30 and 60 on one account within a day with the counter resetting. The run therefore stopped around 18/30 and still reported success.

Search in rounds instead: measure, run a batch sized on the lower known rate, measure again, stop when the quota is full or a round gains nothing, and warn instead of claiming success when it is not filled.

Also give the ollama client a timeout and bound the empty-response retry, since both were unbounded and an unattended run hung for 14 minutes with 2.3 CPU-seconds. The bare while-not-response loop spins forever on empty responses.
2026-08-25 22:28:14 +02:00
mardausdennis bf79e188f2 address review: scroll only when needed and smoothly, tighten the visual search needle
scrollIntoView re-centered every element unconditionally and instantly, which is the page jump between the daily set and explore on bing. The pointer move now scrolls only when the element is actually outside the viewport, smoothly, and polls the rect until the async scroll settles before computing the path.

Visual search matches the full 'visual search streak' label. Plain 'visual search' can also hit a dashboard element that goes stale mid-interaction, and the guessed 'image search' needle is gone.
2026-08-25 22:23:13 +02:00
Carl Furtado 31d4031477 append -noai to search queries 2026-08-25 11:48:07 -04:00
Carl Furtado 2b5fb84620 add readme notice for EU consent banner 2026-08-25 11:14:01 -04:00
mardausdennis 3dba5aa849 add a selector self check and stop reporting skipped tasks as done
check_selectors.py walks every selector and prints what resolved, what is absent and what broke, along with browser, driver, page language and the earn section ids. Absent is a normal result for a task a variant does not ship. It completes no activities and claims nothing, so it is safe to run for a bug report.

complete_explore_on_bing_tasks now raises when the section is missing instead of returning quietly, which made complete_all_tasks print [OK] for a task that never ran.

The visible labels the lookups match on are collected in one Labels class. The selectors are market independent but still language dependent, and this makes that explicit and fixable in one place.
2026-08-25 14:14:04 +02:00
mardausdennis 2773018161 scroll elements into view before moving the pointer
The pointer is moved to viewport coordinates, so an element below the fold produced a target outside the window and the driver raised MoveTargetOutOfBoundsException. Observed on the daily set opener during a scheduled run. Also clamps the bezier path to the viewport.
2026-08-25 13:14:39 +02:00
mardausdennis 9842d608c8 address review: find the daily set opener outside a dedicated section
The daily set opener lives in the streaks section, not in a section of its own, so scoping the lookup to a dedicated section disabled the task on every layout. Match on 'daily set streak' instead, since 'daily set' also hits the level up entry.

Visual search keeps a positional fallback inside streaks because its label is not confirmed on layouts that ship it. Claim now prefers an exact label match so the 'Ready to claim' heading cannot win.
2026-08-25 00:04:28 +02:00
mardausdennis bea185b94d fix selectors that only resolve in the en-US market
Absolute XPaths break outside en-US, where an extra exploreonbing section shifts every positional section index, so every task failed before it started.

Select by visible text, id suffix and visibility instead of position, take the visible copy of ids that are emitted twice for responsive layout, and raise NoSuchElementException for tasks a variant does not ship so the run skips them instead of aborting.
2026-08-24 23:03:09 +02:00
RealEvoranz 045f5cf431 Merge branch 'main' into random_image_downloader 2026-08-24 14:48:34 -04:00
RealEvoranz a8c081e5b7 random_image.png 2026-08-24 14:41:16 -04:00
RealEvoranz b06b6f7b07 Merge branch 'main' into random_image_downloader 2026-08-24 14:35:59 -04:00
RealEvoranz 2d0a31e485 Update pyproject.toml 2026-08-24 14:33:27 -04:00
RealEvoranz 9d867b29c6 added random image finding and downloading function
downloads a random image from Wikipedia, converts it to png, and renames it to random_image.png - will rewrite the image if one already exists.
Filters images:
| Filter             |     Current value | Meaning                              |
| ------------------ | ----------------: | ------------------------------------ |
| MIME type          | JPEG / PNG / WebP | Rejects SVG, GIF, TIFF, etc.         |
| Width              |          ≥ 300 px | Rejects very narrow/small images     |
| Height             |          ≥ 300 px | Rejects very short/small images      |
| Original file size |           ≤ 20 MB | Avoids huge files                    |
| Thumbnail          |           1280 px | Downloads a reasonably sized version |
2026-08-24 14:32:48 -04:00
Carl Furtado 17830f0872 Merge pull request #2 from RealEvoranz/added-nouns.txt
Nouns.txt included by default
2026-08-24 14:17:34 -04:00
RealEvoranz 0b68f7a2c9 Merge branch 'added-nouns.txt' into set-to-default-profile 2026-08-24 14:02:05 -04:00
RealEvoranz 1614e09259 Update README.md 2026-08-24 14:00:17 -04:00
Carl Furtado 1a67432211 Merge pull request #1 from RealEvoranz/set-to-default-profile
Made profile directory default by default
2026-08-24 13:57:53 -04:00
RealEvoranz ae7924d9ac added a default nouns.txt
added a default nouns.txt thanks to hugsy/stuff · random-word/english-nouns.txt
2026-08-24 13:57:38 -04:00
RealEvoranz d5b608c340 Update README.md 2026-08-24 13:52:02 -04:00
RealEvoranz db30ce8f49 made profile directory default by default 2026-08-24 13:48:47 -04:00
Carl Furtado 62cd2b35cb add disclaimer to README 2026-08-24 10:19:14 -04:00
Carl Furtado a785b29494 add visual search custom image instructions 2026-08-24 09:19:51 -04:00
Carl Furtado 06e4f426a2 add instructions for setup to readme 2026-08-24 09:17:14 -04:00
Carl Furtado 602c01d14b add license 2026-08-22 09:26:17 -04:00
Carl Furtado d050f9d0d2 add readme 2026-08-21 13:42:15 -04:00
Carl Furtado 1b0e45d52c use searches_needed when displaying telemetry 2026-08-21 10:36:30 -04:00
Carl Furtado e336eea265 change search completion functionality back to using the number of required searches 2026-08-21 10:31:43 -04:00
Carl Furtado 320a6de8cd make sure claim_bonus_points waits for the sidebar to render 2026-08-21 10:27:53 -04:00
Carl Furtado f31d0cd711 fix race condition when grabbing mouse position 2026-08-21 10:09:48 -04:00
Carl Furtado d0e813bb15 tune LLM prompt 2026-08-21 10:04:50 -04:00
Carl Furtado 4b2978a732 ensure mutliple URLs of ghost tabs are recognized 2026-08-21 10:04:40 -04:00
Carl Furtado 3835fad251 have misc card method clean up after itself 2026-08-20 11:19:35 -04:00
Carl Furtado af2eb68940 add exception handling for possible redefinition of document focus handlers 2026-08-20 10:43:07 -04:00
Carl Furtado a089b1f209 ensure tabs always feign focus 2026-08-20 10:35:35 -04:00
Carl Furtado f392d3e7a3 optimize search completion speed 2026-08-20 10:29:34 -04:00
Carl Furtado 2ffa64684c ensure order of initialization is correct in RewardsTaskUtils 2026-08-20 09:34:43 -04:00