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.
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.
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.
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.