Commit Graph
30 Commits
Author SHA1 Message Date
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
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
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 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
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
Ethan Stoner af03afcc6d add a query source that does not need a language model
Idea taken from TheNetsky/Microsoft-Rewards-Script, which builds search terms
from public feeds rather than a model. No code from it: that project is
GPL-3.0 and this one is MIT, so only the approach crosses over.

The LLM has exactly two call sites here, both producing a short string to type
into Bing. Everything the dependency costs, an Ollama account, cloud usage and
the provider work in #15, is paid for search strings. Three keyless sources
answer the same question:

  Google Trends RSS    queries people are actually typing right now
  Wikipedia most-read  topic seeds when trends is unavailable
  Bing autosuggest     expands a seed into related queries

Autosuggest is what makes the chaining work. Asking Bing what follows a term
returns queries Bing already expects, which is nearer to what the prompt in
llm_utils was reaching for than a model guessing unaided.

Selected with QUERY_SOURCE=trends. The default stays llm, so no existing setup
changes. stdlib only, no new dependencies.

Measured against the LLM on the same cards from a live account:

  card                     llm                                    trends
  airport parking          best rates airport parking reservations reserve airport parking best rates
  checking vs savings      compare checking vs savings accounts    compare checking savings account options
  cruise deals             best cruise deals and destinations      cruise deals destinations

Verified live with OLLAMA_HOST pointed at a dead port, so nothing could reach
a model: five queries generated from feeds and three typed into Bing, each
landing on a real results page.

Every source degrades to an empty list rather than raising, and both entry
points fall back, to the trimmed task description and to nouns.txt. A search
that does not happen costs points; a run that dies costs the rest of the day.
2026-08-26 15:05: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
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
Carl Furtado 31d4031477 append -noai to search queries 2026-08-25 11:48:07 -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 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
Carl Furtado a785b29494 add visual search custom image instructions 2026-08-24 09:19:51 -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 3835fad251 have misc card method clean up after itself 2026-08-20 11:19:35 -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
Carl Furtado b007f4fe00 move rewards init logic to RewardsTaskUtils class 2026-08-20 09:33:13 -04:00
Carl Furtado 10bf499de1 add better handling for ghost tab 2026-08-20 09:28:54 -04:00
Carl Furtado dd50c850ac first commit 2026-08-19 14:03:22 -04:00