Skip to content

Add DeckySales plugin to Plugin Store - #1124

Open
zarif98 wants to merge 1 commit into
SteamDeckHomebrew:mainfrom
zarif98:add/deckysales
Open

zarif98 wants to merge 1 commit into
SteamDeckHomebrew:mainfrom
zarif98:add/deckysales

Conversation

@zarif98

@zarif98 zarif98 commented Sep 11, 2026 •

Copy link
Copy Markdown

Add DeckySales to Plugin Store

DeckySales shows you where a game is cheapest anywhere, not just on Steam! It sends you notifications if a game is on sale and shows you the cheapest game available. I built this off of Deckdeals, but it has been disbanded. I've already credited the user as well.

A picture is worth a 1000 words, so here are some images of what I have built.
Wishlist deals
Best price now

It adds a panel with Steam's current price next to the lowest price across 30 stores. Prices are also converted between currencies. Wishlist alerts run in the background, and tapping on the notification opens a list of game deals found.

It differs from IsThereAnyDeal for Deck by adding background wishlist alerts across every store, a deals list, a direct "cheapest right now vs Steam" comparison, sale prediction, and currency conversion. I use the IsThereAnyDeal API to pull the latest prices. Just to forewarn, this is a fork of Deckdeals, which was never on the store.

Task Checklist

Developer

  • I am the original author or an authorized maintainer of this plugin.
  • I have abided by the licenses of the libraries I am utilizing, including attaching license notices where appropriate.
  • Generative AI was NOT used to write a majority of the code I am submitting.

Plugin

  • I have verified that my plugin works properly on the Stable and Beta update channels of SteamOS.
  • I have verified my plugin is unique or provides more/alternative functionality to a plugin already on the store.

Backend

  • No: I am using a custom backend other than Python.
  • No: I am using a tool or software from a 3rd party FOSS project that does not have it's dependencies statically linked.
  • No: I am using a custom binary that has all of it's dependencies statically linked.

Community

  • I have tested and left feedback on two other pull requests for new or updating plugins.
  • I have commented links to my testing report in this PR.

Testing

  • Tested by a third party on SteamOS Stable or Beta update channel.

@zarif98
zarif98 requested a review from a team as a code owner September 11, 2026 04:07
@github-actions github-actions Bot added the not-plugin Not related to plugins themselves label Sep 11, 2026
@github-actions

Copy link
Copy Markdown

Issues Found

No issues with your PR description were found.

Next Steps

  1. If we found any issues above, please edit your pull request description to resolve them and leave a comment saying you've done so.
  2. For the quickest review, please see the Community section of the pull request template for how you can help other developers.
  3. Once your description is correct, a maintainer will review your pull request as soon as possible.

Thank you for your contribution! If you need any help, please reach out on our Discord server. ❤️

@zarif98 zarif98 changed the title Add DeckySales plugin Add DeckySales plugin to Plugin Store Sep 11, 2026
@github-actions github-actions Bot added the plugin-addition Adding a plugin to the Plugin Store label Sep 11, 2026
@datbird

datbird commented Sep 13, 2026

Copy link
Copy Markdown

Testing report

Built from source at the PR pin 362e24b (v1.2.2) and used it on hardware. It works as advertised and I really like it. No notes from an end user point of view. The store page panel is clear, the cheapest price next to Steam's is genuinely useful, and settings do what they say. Everything below is code level, none of it blocking.

Environment: Steam Deck OLED, SteamOS 3.8.16 Stable, Decky Loader v3.2.8. Built on-device with node 22.23.0 and pnpm 9.15.9. Install, typecheck, 163 tests and build all clean, and the bundle hashed identical to my desktop build. Installed root owned, loader logged Loaded DeckySales, no traceback.

What I exercised: the store page panel on a few games, the settings panel in the QAM, and the data path itself from the Deck: credentials endpoint, ITAD lookup, live prices across stores, and the currency API. All returned what the plugin expects. Pulled the panel out of the DOM on Baldur's Gate 3 to be sure: it sits inside .game_area_purchase_game and read CURRENT 59.99 USD Steam, BEST NOW 59.99 USD Steam, All-time low 41.99 USD 7/30/2026, which matches ITAD.

One innerHTML write skips the escaping

StoreInjector.ts:965, the graph tooltip, writes price, date and store straight into innerHTML. Every neighbouring write goes through escapeHtml first, and the comment at line 311 says provider values are escaped. This one isn't. Same fix as line 792, just wrap the three values. Low severity since the names come from ITAD, but the file already set the standard.

The ITAD key rides along in the debug object

getLowestPrice returns debug: { lookupUrl, historyUrl } and both URLs carry key=. Nothing logs it today, so it's one console.log(result) away from landing in a bug report. Strip the key before it goes in debug.

Settings writes aren't atomic

settings.py truncates settings.json then writes into it. Interrupt that (power loss, suspend) and read() swallows the decode error and starts from {}. That silently drops a user's own ITAD key and resets the wishlist baseline. Write to a temp file and os.replace() it.

I saw the quiet half of this on my Deck. After first load settings.json was 0 bytes (not sure what wrote it, the loader doesn't do that for my other plugins), and the "Failed to read settings" error never showed up in the plugin log or the journal, because read() runs at import before logging is wired up. So a bad file is both fatal to settings and invisible.

Smaller notes

main.py's _migration is still the template boilerplate. Harmless, but reviewers usually ask for it to go, and a Deckdeals user gets no settings carried over.

It loads as a legacy plugin and the console warns findModuleChild is deprecated and will be removed soon. Works fine on 3.2.8, just a deadline to keep an eye on.

MAX_WISHLIST_APPS = 500 truncates silently. The wishlist status line could say "checked 500 of 830".

Happy to re-test once you push fixes.

@datbird datbird mentioned this pull request Sep 13, 2026
7 of 8 tasks
@zarif98
zarif98 deployed to testing_env September 13, 2026 15:06 — with GitHub Actions Active
@github-actions

Copy link
Copy Markdown

This pull request is stale as no updates or testing reports have been posted within the last 7 days. Please ensure you are actively recruiting testers or resolving the issues discussed. If you do not remove the stale label or comment, this will be closed in 7 days. Please close this pull request if you need more time to resolve issues so we can keep our repo healthy.

@github-actions github-actions Bot added the stale label Sep 21, 2026
@zarif98

zarif98 commented Sep 21, 2026

Copy link
Copy Markdown
Author

Not stale still waiting for review

@AAGaming00 AAGaming00 removed the not-plugin Not related to plugins themselves label Sep 21, 2026
@github-actions github-actions Bot removed the stale label Sep 22, 2026
@steam3d

steam3d commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Plugin Testing Report

Installed Plugins

MagicBlack - 2.0.2 (Not Decky Store version)
MagicBlack - 2.0.20 (Not Decky Store version)
DeckySales - 1.2.3-27791ad
ProtonDB Badges - 1.3.4-809751c
Exaple Plugin - 0.0.1 (disabled)
Bluetooth - 2.3.1-7b6d030

Specifications

OS Version 3.8.27 (Preview)
Steam Version 1790036264 (Preview)
Decky 3.2.9 (Preview)

Issues

Has the following major blocking issue(s): None
Has the following minor non-blocking issue(s): None

Summary

It does not load anything.

  1. Testing an empty wishlist

    The "Check Now" button shows the error "Wishlist check failed. It will retry automatically." When I open "View Deal List," it keeps loading indefinitely.

  2. Testing with one game in the wishlist

    The same behavior occurs as in the first test.

I assumed it was an internet connection issue, so I tried a different internet connection and got the same result.

20260922120242_1 20260922120257_1

@steam3d steam3d mentioned this pull request Sep 22, 2026
7 of 8 tasks
@zarif98

zarif98 commented Sep 22, 2026

Copy link
Copy Markdown
Author

@steam3d Hey, could you share the logs please? I think it's in ~/homebrew/logs/DeckySales/ or Decky->Settings-> Developer->logs. There should be a traceback for me to read. Also is your wishlist private or public?

@datbird

datbird commented Sep 22, 2026 •

Copy link
Copy Markdown

@steam3d @zarif98

I reproduced this on my Deck (Decky 3.2.8 Stable). My earlier test done using v1.2.2 (362e24b) and it worked. The v1.2.3 version (bb3dcbd) breaks.

The Python backend crashes on start, so every call from the UI fails:

main.py, line 27, in _migration
AttributeError: 'SettingsManager' object has no attribute 'settings_file'

from settings import SettingsManager gets Decky's own SettingsManager, not yours. Decky aliases its internal decky_loader.* modules into sys.modules, and only py_modules/ is on the plugin's path, not the plugin root. v1.2.2 only used methods Decky's class also has, so it worked by luck.

The fix I tested on the my deck: move settings.py to py_modules/deckysales_settings.py and change the import to match. The backend starts, settings load, and the wishlist fetch returns 200.

Small one: an empty wishlist returns {"response":{}}, which currently shows the "private" message.

@zarif98

zarif98 commented Sep 23, 2026 •

Copy link
Copy Markdown
Author

@datbird, thank you so much for debugging. I think I genuinely got lucky with the first release working, and the newer release was just an oversight. Fixed the issue, and it should now work. Also added a URL link similar to how you have it on your repo:
image

@zarif98

zarif98 commented Sep 23, 2026 •

Copy link
Copy Markdown
Author

@steam3d Your plugin looks awesome, but unfortunately I don't have AirPods :( I think it might be time for me to get some tho! And I've updated my plugin as well if you have some time to take a look. Also added a plugin address similar to how @datbird does it:
image

@datbird

datbird commented Sep 23, 2026

Copy link
Copy Markdown

@datbird, thank you so much for debugging. I think I genuinely got lucky with the first release working, and the newer release was just an oversight. Fixed the issue, and it should now work. Also added a URL link similar to how you have it on your repo: image

Retest of v1.2.4 (bab0891)

The crash is fixed. I built from the pin on my Deck (SteamOS 3.8.16 Stable, Decky 3.2.8). Install, typecheck, 179 tests and build are clean, and the 17 Python tests pass. The loader logs Loaded DeckySales (v1.2.4), settings calls answer in about 1 ms, and View Deals List opens right away instead of spinning.

One thing I got wrong yesterday, and it matters now. I said an empty wishlist returns {"response":{}}. It does, but a private one returns the exact same thing, with a 200. I checked a private profile and a friends only profile, and both came back 200 with {"response":{}}. So the new 401/403 check never fires.

My own account shows the problem. My wishlist has 632 games, but the public API returns {"response":{}} for it. My profile is public, so it comes down to the Game details privacy setting. Check Now now says "No new deals right now", which looks like a clean run when it actually saw zero games. Before, it at least said private. Sorry for steering you there.

Two ways out:

  • The plugin runs inside the Steam client, so fetch("https://store.steampowered.com/dynamicstore/userdata/", {credentials: "include"}) returns rgWishlist for the signed in user, whatever the privacy setting. On my Deck it returned all 632.
  • Or, when the public API returns no items, show one message that covers both cases: "No games found. If your wishlist has games, set Game details to Public in your Steam privacy settings."

Small one: an empty result returns before WISHLIST_LAST_CHECK is saved, so the panel still says "Not checked yet" after a check.

@zarif98

zarif98 commented Sep 23, 2026

Copy link
Copy Markdown
Author

@datbird sorry pinned the wrong branch. Let me get home today and I'll fix

@zarif98

zarif98 commented Sep 24, 2026

Copy link
Copy Markdown
Author

@datbird fixed! Went on a rabbit hole with the change you suggested but was able to find and test once more.

@datbird

datbird commented Sep 24, 2026

Copy link
Copy Markdown

Retested v1.2.5 (88e92d1) on my Deck, SteamOS Stable. Works now!

  • Wishlist reads from the client session: 632 games, where the public API gave {}.
  • My first run saved a baseline of 264 deals as well as the last check time. I observed that 500 of my 632 got checked, which I believe matches your cap, therefore "working as intended".
  • All tests pasted and build appears clean.

DeckySales shows the cheapest price live across ~30 stores on any Steam
store page, and notifies when a wishlisted game goes on sale at any store,
not just Steam. Forked from Deckdeals by ebdevag.

Pinned to v1.2.5 (88e92d1), retested on hardware by datbird.

This branch is waiting to be deployed

1 waiting deployment
testing_env — e4c5ef4a Waiting Sep 28, 2026 by zarif98 via Build plugin (DeckySales, true) #4038
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

plugin-addition Adding a plugin to the Plugin Store

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants