core¶
Domain logic with no browser or network dependencies.
Engine¶
The redemption engine.
Decides which keys are worth an activation attempt and carries those attempts out. The ordering is deliberate and is the whole point of the tool:
Skip anything already owned on Steam. Steam counts a duplicate as a failure, and failures are limited to roughly ten per hour against about fifty successes, so an unchecked run exhausts its budget on games the account already has.
Reveal on Humble only what is about to be redeemed, because revealing forfeits the ability to make a gift link and cannot be undone.
Stop at the first rate limit rather than retrying into a deeper cooldown.
- class humble_steam_key_redeemer.core.engine.PlanEntry(record, decision, skip_reason=None)[source]¶
Bases:
objectOne key’s disposition ahead of redemption.
- Variables:
record – The key under consideration.
decision – Ownership match against the Steam library.
skip_reason – Why the key will not be attempted, when applicable.
- Parameters:
record (KeyRecord)
decision (MatchDecision)
skip_reason (str | None)
- decision: MatchDecision¶
- class humble_steam_key_redeemer.core.engine.RedemptionEngine(store, steam)[source]¶
Bases:
objectPlans and performs Steam redemptions for Humble keys.
- Parameters:
store (
RedeemerStore) – Local store of keys and attempts.steam (
SteamGateway) – Steam gateway used for ownership and redemption.
- plan(records, *, match_threshold=70, confirm_threshold=95)[source]¶
Decide which keys are worth attempting.
- Parameters:
- Return type:
- Returns:
The resulting
RedemptionPlan.
- redeem(plan, *, reveal=None, on_result=None, limit=0)[source]¶
Attempt every key the plan selected.
- Parameters:
plan (
RedemptionPlan) – The plan to execute.reveal (
Callable[[KeyRecord],str|None] |None) – Called to reveal a key that Humble has not released yet. When omitted, unrevealed keys are skipped.on_result (
Callable[[KeyRecord,RedemptionAttempt],None] |None) – Called after each attempt, for progress reporting.limit (
int) – Maximum attempts;0means no limit.
- Return type:
- Returns:
A
RunSummaryof the run.
- class humble_steam_key_redeemer.core.engine.RedemptionPlan(entries=<factory>)[source]¶
Bases:
objectThe set of decisions for a run.
- class humble_steam_key_redeemer.core.engine.RunSummary(redeemed=0, already_owned=0, failed=0, skipped=0, rate_limited=False)[source]¶
Bases:
objectOutcome counts for a completed run.
Matching¶
Match Humble titles against an owned Steam library.
Humble and Steam rarely spell a title identically — “Game GOTY” against “Game: Game of the Year Edition”, trademark symbols, differing punctuation — so ownership is decided by fuzzy comparison rather than equality.
Getting this wrong is expensive in both directions. A missed match spends one of Steam’s ten hourly failed activations on a game the account already owns; a false match silently skips a key that could have been redeemed.
- class humble_steam_key_redeemer.core.matching.MatchDecision(app_id, app_name, score, confident)[source]¶
Bases:
objectThe outcome of comparing one title against the owned library.
- Variables:
app_id – Steam application id of the best candidate, if any.
app_name – Name of that application.
score – Similarity score from 0 to 100.
confident – Whether the score clears the confirmation threshold.
- Parameters:
- class humble_steam_key_redeemer.core.matching.OwnershipMatcher(owned, *, threshold=70, confirm_threshold=95)[source]¶
Bases:
objectDecides whether a Humble title is already owned on Steam.
- Parameters:
- match(title, *, steam_app_id=None)[source]¶
Find the best owned application matching
title.A Steam app id reported by Humble is authoritative and short-circuits the fuzzy comparison entirely.
- Parameters:
- Return type:
- Returns:
The best
MatchDecision; unmatched when nothing clears the threshold.
Models¶
Database models for keys and redemption attempts.
Replaces the append-only CSV files the tool previously used. CSV was doing
double duty as both report and state, which made it the source of two
problems: fields containing commas, quotes, or newlines corrupted the file,
and values beginning with =, +, - or @ were interpreted as
formulas when the file was opened in a spreadsheet.
A database keeps state; CSV export remains available as a report, where values are escaped on the way out.
- class humble_steam_key_redeemer.core.models.KeyRecord(*, id=None, gamekey, machine_name, human_name, key_type=None, steam_app_id=None, redeemed_key_val=None, key_index=None, is_gift=False, is_expired=False, state=KeyState.UNREVEALED, matched_app_id=None, matched_app_name=None, match_score=None, created_at=<factory>, updated_at=<factory>)[source]¶
Bases:
SQLModelA key entry from a Humble order.
- Variables:
gamekey – Humble order identifier.
machine_name – Humble’s stable internal name for the entry.
human_name – Display title as shown by Humble.
key_type – Platform the key targets (
steam,gog, …).steam_app_id – Steam application id, when Humble reports one.
redeemed_key_val – The revealed key, once Humble has released it.
state – Current lifecycle state.
- Parameters:
id (int | None)
gamekey (str)
machine_name (str)
human_name (str)
key_type (str | None)
steam_app_id (int | None)
redeemed_key_val (str | None)
key_index (int | None)
is_gift (bool)
is_expired (bool)
state (KeyState)
matched_app_id (int | None)
matched_app_name (str | None)
match_score (int | None)
created_at (datetime)
updated_at (datetime)
- created_at: datetime¶
- updated_at: datetime¶
- model_config = {'from_attributes': True, 'read_from_attributes': True, 'read_with_orm_mode': True, 'registry': PydanticUndefined, 'table': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
- class humble_steam_key_redeemer.core.models.KeyState(*values)[source]¶
Bases:
StrEnumLifecycle state of a Humble key.
- UNREVEALED = 'unrevealed'¶
- REVEALED = 'revealed'¶
- ATTEMPTED = 'attempted'¶
- REDEEMED = 'redeemed'¶
- ALREADY_OWNED = 'already_owned'¶
- FAILED = 'failed'¶
- SKIPPED = 'skipped'¶
- class humble_steam_key_redeemer.core.models.RedemptionAttempt(*, id=None, key_id, result_code, result_name, detail, succeeded, granted_items=None, attempted_at=<factory>)[source]¶
Bases:
SQLModelOne attempt to redeem a key on Steam.
Recording every attempt, rather than only the final state, means a rerun can tell “not tried yet” apart from “tried and rejected”, which is what keeps the tool from spending its scarce failure budget twice on the same dead key.
- Parameters:
- attempted_at: datetime¶
- model_config = {'from_attributes': True, 'read_from_attributes': True, 'read_with_orm_mode': True, 'registry': PydanticUndefined, 'table': True}¶
Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].
Store¶
Persistent store for keys and redemption attempts.
The database is the tool’s memory across runs. It answers the question that decides whether a key is worth an activation attempt: has this key already been tried, and what did Steam say?
- class humble_steam_key_redeemer.core.store.RedeemerStore(path)[source]¶
Bases:
objectSQLite-backed store of keys and redemption attempts.
- Parameters:
path (
Path) – Database file path. Parent directories are created.
- session()[source]¶
Open a new database session.
expire_on_commitis disabled so records stay readable after the session closes. Otherwise every attribute access on a returned record tries to reload from a session that no longer exists.- Return type:
Session
- upsert_keys(records)[source]¶
Insert or update key records, keyed by Humble’s identifiers.
Humble’s
(gamekey, machine_name)pair is stable across runs, so re-importing an order updates existing rows instead of duplicating them and losing their redemption history.
- pending_keys()[source]¶
Return Steam keys that are still worth attempting.
Excludes keys already redeemed, already owned, or previously attempted and rejected, so a rerun does not spend Steam’s failure budget on outcomes already known.
- record_attempt(attempt)[source]¶
Store one redemption attempt.
- Parameters:
attempt (
RedemptionAttempt) – The attempt to record.- Return type:
- Returns:
The stored attempt, with its assigned id.
- attempts_for(key_id)[source]¶
Return every recorded attempt for a key.
- Parameters:
key_id (
int) – The key’s database id.- Return type:
- humble_steam_key_redeemer.core.store.csv_safe(value)[source]¶
Neutralize a value for CSV export.
Titles come from Humble and are not trusted input. A cell beginning with
=,+,-or@is executed as a formula by Excel, LibreOffice and Sheets, so such values are prefixed with an apostrophe to force text.