Files
2026-08-09 16:51:01 -05:00

103 lines
4.4 KiB
Markdown

# LZT Market Sniper — C++ rewrite
A from-scratch C++ port of the original single-file `app.py` (Flask +
Selenium + a mobile web dashboard). This version is a native background
service with no browser/webpage at all — you control it entirely from
Discord with slash commands.
## What changed vs. the Python version
| Python (`app.py`) | C++ rewrite |
|---|---|
| Flask web server + mobile HTML/CSS/JS dashboard | Gone. Control is via Discord slash commands instead. |
| Selenium driving a real (headless) Chrome profile | `libcurl` HTTP GET of the search page's raw HTML (no JS execution — see **Scraping caveat** below). |
| Discord **webhook** only | Bot posts through its own REST API (`/channels/{id}/messages`) using the bot token; a legacy webhook URL is still supported as a fallback. |
| In-memory `state` dict, SSE stream to browser | Thread-safe `SniperState`, no streaming needed since Discord is the UI. |
| One 1185-line file | Split into `Config`, `State`, `Logger`, `HttpClient`, `Scraper`, `DiscordNotifier`, `SniperEngine`, `DiscordBot`, `main`. |
## Layout
```
include/ headers (one per component)
Config.hpp JSON config load/save (bot token, links, filters, interval)
State.hpp thread-safe shared state (running flag, last offers, history)
Logger.hpp timestamped console logger
HttpClient.hpp libcurl GET/POST wrapper
Scraper.hpp page fetch + offer-card text extraction + price/keyword filter
DiscordNotifier.hpp builds/sends the stock-update embed
SniperEngine.hpp the watch loop + all mutating operations (start/stop/add/remove/pause/scan)
DiscordBot.hpp D++ slash-command bot wired into SniperEngine
src/ same files, implementations
CMakeLists.txt
config.example.json
```
## Building
Dependencies: a C++17 compiler, CMake, `libcurl`, `nlohmann-json`, and
[D++](https://dpp.dev/) (the Discord library).
```bash
# Debian/Ubuntu
sudo apt install build-essential cmake libcurl4-openssl-dev nlohmann-json3-dev
mkdir build && cd build
cmake ..
make -j$(nproc)
```
CMake first looks for an installed D++ (`find_package(DPP)`); if it's not
found it downloads and builds D++ v10.0.35 from source automatically via
`FetchContent` the first time you configure (this takes a few minutes — D++
is a fairly large library). If you'd rather build D++ once and reuse it,
follow the install instructions at https://dpp.dev/installing.html and
CMake will pick up the installed copy instead.
## Configuring
```bash
cp config.example.json config.json
```
Fill in:
- `discord_bot_token` — from the Discord Developer Portal, with the
`applications.commands` and `bot` scopes and the "Send Messages" permission.
- `discord_channel_id` — the channel the bot should post stock-change alerts to.
- `command_guild_id` *(optional)* — set this to your server's ID while
testing so slash commands sync instantly; leave blank for global commands
(Discord can take up to an hour to propagate those).
- `links` — your initial watch list (you can also manage this entirely from
Discord with `/addlink`, `/removelink`, `/pause` once the bot is running).
Run it:
```bash
./build/lzt_sniper config.json
```
## Discord commands
- `/start` — begin watching all configured links
- `/stop` — stop watching
- `/status` — running state, total scans/changes, current stock per link
- `/links` — list watched links with pause state and filters
- `/addlink url [label] [min_price] [max_price] [keywords]`
- `/removelink url`
- `/pause url` — toggle pause on one link
- `/scan url` — force an immediate one-off scan
- `/interval seconds` — change the polling interval
- `/history` — last 15 scan results
## Scraping caveat
The Python version drove a real headless Chrome browser through a
logged-in profile, so it saw the fully JS-rendered, authenticated page. This
C++ version fetches the raw page HTML over HTTP instead — no JavaScript is
executed. LZT Market's search results are server-rendered, so public
listings work fine; if a page ever needs an authenticated session, copy your
browser's `Cookie:` header value into `cookie_header` in `config.json`. The
offer-card extraction (`Scraper::extractOfferBlocks`) is a heuristic text
parser tuned to reproduce the original's "any div/li/article whose text
mentions 'followers'" logic — inspect it and adjust the tag/keyword filters
in `src/Scraper.cpp` if LZT Market's markup changes.