Commit Graph
24 Commits
Author SHA1 Message Date
Ethan Stoner c212ba8131 note that a Windows host cannot hand its sign-in to the container 2026-08-29 11:45:30 -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
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
RealEvoranz 1dada212a8 Update README.md 2026-08-27 09:12:30 -04:00
RealEvoranz b97613b5b8 Merge remote-tracking branch 'upstream/main' 2026-08-27 08:59:48 -04: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
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 bfe30dd9a0 run in docker, and work through more than one account
Both taken from TheNetsky/Microsoft-Rewards-Script, which packages a container
and handles several accounts. Approach only: that project is GPL-3.0 and this
one MIT, so no code crosses over.

**Accounts.** Rewards is per Microsoft account and the browser profile holds
the sign-in, so an account here is a profile directory. REWARDS_ACCOUNTS takes
a comma separated list and gives each its own directory under the configured
one. They run in sequence, and a profile that will not start is reported and
skipped rather than ending the run. Left unset, a run uses the single profile
exactly as before.

Names are validated rather than trusted: they become directory names, so
"../escape" is refused instead of quietly writing outside data-dir.

**Docker.** The image carries only what main.py actually reaches, selenium and
numpy. pygetwindow, keyboard, matplotlib and pygame are used solely by the
recording and visualisation scripts, and two of those are Windows-only, so
none of them belong in a container. msedgedriver is pinned at build time to
the Edge the image installed rather than to latest, which drifts from it
between releases.

QUERY_SOURCE defaults to trends in the image, so a container needs no Ollama
account and no model download at all.

That default turned out to require a fix. queries.py imported llm_utils at
module scope, which imports ollama, so a trends-only install still needed the
ollama package: exactly what running in a minimal image is good at exposing.
The import is now made inside the llm branch, and the wordlist fallback reads
nouns.txt directly rather than borrowing llm_utils.get_random_noun.

REWARDS_HEADLESS drives the headless flags. The window size is set explicitly
because the pointer code works in viewport coordinates and the default
headless window is small enough to put cards out of reach, which is the
MoveTargetOutOfBoundsException from #19. Verified on the host that
move_to_element and human_like_click both work headless before relying on it.

Verified in the built image: Edge 151.0.4129.107 with a driver of exactly the
same build, the trends feed reachable from inside, Edge driven to bing.com and
rewards.bing.com at 1920x1080, and REWARDS_ACCOUNTS producing separate profile
directories with traversal refused.
2026-08-26 15:13:02 -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 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 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 2ef072f17d Update README.md 2026-08-25 22:10:32 -04:00
RealEvoranz cafc5097bd Merge remote-tracking branch 'upstream/main' into random_image_downloader 2026-08-25 22:02:13 -04:00
Carl Furtado 2b5fb84620 add readme notice for EU consent banner 2026-08-25 11:14:01 -04:00
RealEvoranz a8c081e5b7 random_image.png 2026-08-24 14:41:16 -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
RealEvoranz d5b608c340 Update README.md 2026-08-24 13:52:02 -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 d050f9d0d2 add readme 2026-08-21 13:42:15 -04:00