vinted-bot 2.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vinted_bot-2.1.0/.dockerignore +14 -0
- vinted_bot-2.1.0/.env.example +126 -0
- vinted_bot-2.1.0/.gitignore +23 -0
- vinted_bot-2.1.0/.python-version +1 -0
- vinted_bot-2.1.0/CHANGELOG.md +182 -0
- vinted_bot-2.1.0/CONTRIBUTING.md +148 -0
- vinted_bot-2.1.0/Dockerfile +76 -0
- vinted_bot-2.1.0/LICENSE +21 -0
- vinted_bot-2.1.0/PKG-INFO +282 -0
- vinted_bot-2.1.0/README.fr.md +238 -0
- vinted_bot-2.1.0/README.md +234 -0
- vinted_bot-2.1.0/SECURITY.md +56 -0
- vinted_bot-2.1.0/docker/entrypoint.sh +51 -0
- vinted_bot-2.1.0/docker-compose.yml +69 -0
- vinted_bot-2.1.0/docs/REVIEW.md +101 -0
- vinted_bot-2.1.0/docs/architecture.md +198 -0
- vinted_bot-2.1.0/docs/configuration.md +319 -0
- vinted_bot-2.1.0/docs/legal.md +82 -0
- vinted_bot-2.1.0/docs/self-hosting.md +263 -0
- vinted_bot-2.1.0/docs/troubleshooting.md +254 -0
- vinted_bot-2.1.0/install.ps1 +380 -0
- vinted_bot-2.1.0/install.sh +515 -0
- vinted_bot-2.1.0/llms.txt +168 -0
- vinted_bot-2.1.0/pyproject.toml +173 -0
- vinted_bot-2.1.0/src/vinted_bot/__init__.py +3 -0
- vinted_bot-2.1.0/src/vinted_bot/__main__.py +3 -0
- vinted_bot-2.1.0/src/vinted_bot/app.py +400 -0
- vinted_bot-2.1.0/src/vinted_bot/botctl/__init__.py +1 -0
- vinted_bot-2.1.0/src/vinted_bot/botctl/telegram_bot.py +582 -0
- vinted_bot-2.1.0/src/vinted_bot/cli.py +490 -0
- vinted_bot-2.1.0/src/vinted_bot/config.py +288 -0
- vinted_bot-2.1.0/src/vinted_bot/db/__init__.py +6 -0
- vinted_bot-2.1.0/src/vinted_bot/db/connection.py +126 -0
- vinted_bot-2.1.0/src/vinted_bot/db/export.py +98 -0
- vinted_bot-2.1.0/src/vinted_bot/db/migrations/0001_init.sql +124 -0
- vinted_bot-2.1.0/src/vinted_bot/db/migrations/0002_seller_details.sql +6 -0
- vinted_bot-2.1.0/src/vinted_bot/db/migrations/0003_listing_gallery.sql +7 -0
- vinted_bot-2.1.0/src/vinted_bot/db/migrations/0004_richer_searches.sql +277 -0
- vinted_bot-2.1.0/src/vinted_bot/db/migrations/0005_resale_and_mute.sql +17 -0
- vinted_bot-2.1.0/src/vinted_bot/db/migrations/0006_dedup_that_survives.sql +66 -0
- vinted_bot-2.1.0/src/vinted_bot/db/migrations.py +100 -0
- vinted_bot-2.1.0/src/vinted_bot/db/repo.py +2145 -0
- vinted_bot-2.1.0/src/vinted_bot/deals.py +85 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/__init__.py +15 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/apprise.py +136 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/base.py +88 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/discord.py +819 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/dispatcher.py +359 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/labels.py +452 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/ntfy.py +208 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/preview.py +218 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/ratelimit.py +80 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/render.py +390 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/telegram.py +551 -0
- vinted_bot-2.1.0/src/vinted_bot/deliver/webhook.py +186 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/__init__.py +6 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/dedup.py +135 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/filters.py +249 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/health.py +204 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/poller.py +484 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/quiet.py +37 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/self_update.py +621 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/updates.py +81 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/validation.py +293 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/watchdog.py +142 -0
- vinted_bot-2.1.0/src/vinted_bot/engine/watcher.py +215 -0
- vinted_bot-2.1.0/src/vinted_bot/log.py +52 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/__init__.py +34 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/agents.json +78 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/canary.py +100 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/client.py +420 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/errors.py +73 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/headers.py +239 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/item_page.py +228 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/models.py +373 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/proxies.py +94 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/session.py +289 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/taxonomy.py +343 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/transport.py +382 -0
- vinted_bot-2.1.0/src/vinted_bot/vinted/urls.py +329 -0
- vinted_bot-2.1.0/src/vinted_bot/web/__init__.py +1 -0
- vinted_bot-2.1.0/src/vinted_bot/web/core.py +245 -0
- vinted_bot-2.1.0/src/vinted_bot/web/data.py +156 -0
- vinted_bot-2.1.0/src/vinted_bot/web/destinations.py +207 -0
- vinted_bot-2.1.0/src/vinted_bot/web/forms.py +366 -0
- vinted_bot-2.1.0/src/vinted_bot/web/preview.py +97 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/__init__.py +1 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/alerts.py +208 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/api.py +293 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/auth.py +85 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/finds.py +308 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/searches.py +471 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/settings_page.py +185 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/stats.py +157 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/updating.py +37 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/watchlist.py +147 -0
- vinted_bot-2.1.0/src/vinted_bot/web/routes/welcome.py +394 -0
- vinted_bot-2.1.0/src/vinted_bot/web/security.py +236 -0
- vinted_bot-2.1.0/src/vinted_bot/web/server.py +308 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/LICENSE-lucide.txt +15 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/css/app.css +1211 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/css/tokens.css +276 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/fonts/Figtree-latin-ext.woff2 +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/fonts/Figtree-latin.woff2 +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/fonts/Gabarito-latin-ext.woff2 +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/fonts/Gabarito-latin.woff2 +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/fonts/OFL-Figtree.txt +93 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/fonts/OFL-Gabarito.txt +93 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/demo/corduroy-trousers.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/demo/denim-jacket.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/demo/straight-leg-jeans.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/demo/wool-coat.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/icons/apple-touch-icon.png +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/icons/favicon.ico +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/icons/favicon.svg +13 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/icons/icon-192.png +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/icons/icon-512.png +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/icons/icon-maskable-512.png +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-alert.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-cheer.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-find.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-head.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-parcel.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-point.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-sleep.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-think.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-watch.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-wave.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/img/mags/mags-worried.webp +0 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/js/app.js +976 -0
- vinted_bot-2.1.0/src/vinted_bot/web/static/js/qrcode.js +2297 -0
- vinted_bot-2.1.0/src/vinted_bot/web/support.py +73 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/alerts.html +131 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/bare.html +14 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/base.html +110 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/error.html +12 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/index.html +184 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/login.html +32 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/ob_base.html +54 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/partials/banners.html +57 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/partials/card.html +1 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/partials/dest_forms.html +109 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/partials/head.html +14 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/partials/icons.svg +109 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/partials/macros.html +87 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/partials/ob_back.html +1 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/partials/support.html +94 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/search-detail.html +103 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/search-edit.html +319 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/searches.html +158 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/settings.html +183 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/stats.html +118 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/watchlist.html +80 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/welcome-1.html +36 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/welcome-2.html +48 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/welcome-3.html +85 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/welcome-4.html +51 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/welcome-5.html +48 -0
- vinted_bot-2.1.0/src/vinted_bot/web/templates/welcome-6.html +37 -0
- vinted_bot-2.1.0/src/vinted_bot/web/views.py +615 -0
- vinted_bot-2.1.0/tests/__init__.py +0 -0
- vinted_bot-2.1.0/tests/conftest.py +149 -0
- vinted_bot-2.1.0/tests/fixtures/vinted/catalogue_fr.json +982 -0
- vinted_bot-2.1.0/tests/fixtures/vinted/item_available_fr.html +4 -0
- vinted_bot-2.1.0/tests/fixtures/vinted/item_sold_nl.html +22 -0
- vinted_bot-2.1.0/tests/fixtures/vinted/user_fr.json +23 -0
- vinted_bot-2.1.0/tests/fixtures/vinted/wardrobe_fr.json +754 -0
- vinted_bot-2.1.0/tests/integration/__init__.py +0 -0
- vinted_bot-2.1.0/tests/integration/test_alert_tools.py +386 -0
- vinted_bot-2.1.0/tests/integration/test_app_end_to_end.py +245 -0
- vinted_bot-2.1.0/tests/integration/test_cli.py +116 -0
- vinted_bot-2.1.0/tests/integration/test_dashboard_usability.py +399 -0
- vinted_bot-2.1.0/tests/integration/test_delivery.py +315 -0
- vinted_bot-2.1.0/tests/integration/test_heartbeat_and_updates.py +118 -0
- vinted_bot-2.1.0/tests/integration/test_poller.py +402 -0
- vinted_bot-2.1.0/tests/integration/test_poller_rules.py +471 -0
- vinted_bot-2.1.0/tests/integration/test_repo.py +658 -0
- vinted_bot-2.1.0/tests/integration/test_self_update.py +535 -0
- vinted_bot-2.1.0/tests/integration/test_telegram_bot.py +553 -0
- vinted_bot-2.1.0/tests/integration/test_watchdog_and_storage.py +266 -0
- vinted_bot-2.1.0/tests/integration/test_watcher.py +228 -0
- vinted_bot-2.1.0/tests/integration/test_web.py +1350 -0
- vinted_bot-2.1.0/tests/integration/test_web_updates.py +198 -0
- vinted_bot-2.1.0/tests/integration/test_what_counts_as_new.py +343 -0
- vinted_bot-2.1.0/tests/unit/__init__.py +0 -0
- vinted_bot-2.1.0/tests/unit/sending.py +258 -0
- vinted_bot-2.1.0/tests/unit/test_canary.py +63 -0
- vinted_bot-2.1.0/tests/unit/test_client.py +325 -0
- vinted_bot-2.1.0/tests/unit/test_config.py +109 -0
- vinted_bot-2.1.0/tests/unit/test_deals.py +60 -0
- vinted_bot-2.1.0/tests/unit/test_dedup_and_filters.py +208 -0
- vinted_bot-2.1.0/tests/unit/test_discord.py +667 -0
- vinted_bot-2.1.0/tests/unit/test_docs_match_code.py +68 -0
- vinted_bot-2.1.0/tests/unit/test_filters.py +232 -0
- vinted_bot-2.1.0/tests/unit/test_install_detection.py +195 -0
- vinted_bot-2.1.0/tests/unit/test_item_page.py +116 -0
- vinted_bot-2.1.0/tests/unit/test_migrations.py +202 -0
- vinted_bot-2.1.0/tests/unit/test_models.py +277 -0
- vinted_bot-2.1.0/tests/unit/test_other_senders.py +436 -0
- vinted_bot-2.1.0/tests/unit/test_proxies.py +125 -0
- vinted_bot-2.1.0/tests/unit/test_quiet.py +36 -0
- vinted_bot-2.1.0/tests/unit/test_ratelimit.py +39 -0
- vinted_bot-2.1.0/tests/unit/test_render.py +301 -0
- vinted_bot-2.1.0/tests/unit/test_session.py +74 -0
- vinted_bot-2.1.0/tests/unit/test_support.py +55 -0
- vinted_bot-2.1.0/tests/unit/test_taxonomy.py +298 -0
- vinted_bot-2.1.0/tests/unit/test_telegram.py +468 -0
- vinted_bot-2.1.0/tests/unit/test_transport.py +108 -0
- vinted_bot-2.1.0/tests/unit/test_urls.py +228 -0
- vinted_bot-2.1.0/tests/unit/test_validation.py +118 -0
- vinted_bot-2.1.0/uv.lock +2050 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# You don't have to change anything in this file to get started.
|
|
2
|
+
# Searches and Discord webhooks are set up in the dashboard at http://localhost:8000
|
|
3
|
+
# (or with the CLI) — not here. Every line below is optional and shows its default.
|
|
4
|
+
# Full reference: docs/configuration.md
|
|
5
|
+
|
|
6
|
+
# --- Telegram (only if you want alerts there) ----------------------------------------
|
|
7
|
+
# Talk to @BotFather on Telegram to create a bot; it gives you a token like
|
|
8
|
+
# 123456:AAxxxx. Setting this turns on Telegram delivery and the pairing bot.
|
|
9
|
+
# VINTED_BOT_TELEGRAM_BOT_TOKEN=
|
|
10
|
+
|
|
11
|
+
# --- Web dashboard -------------------------------------------------------------------
|
|
12
|
+
# On by default at http://localhost:8000, no password. That is safe because only your
|
|
13
|
+
# own machine can reach it. Set a token before exposing it any further: anyone who can
|
|
14
|
+
# open the dashboard can add, remove and reroute your alerts.
|
|
15
|
+
# Generate one with: openssl rand -hex 32
|
|
16
|
+
# VINTED_BOT_WEB_AUTH_TOKEN=
|
|
17
|
+
|
|
18
|
+
# Set to false if you only use the CLI.
|
|
19
|
+
# VINTED_BOT_WEB_ENABLED=true
|
|
20
|
+
|
|
21
|
+
# Localhost only. The container sets 0.0.0.0 internally; the compose file is what limits
|
|
22
|
+
# who can reach the published port.
|
|
23
|
+
# VINTED_BOT_WEB_HOST=127.0.0.1
|
|
24
|
+
# VINTED_BOT_WEB_PORT=8000
|
|
25
|
+
|
|
26
|
+
# The address the dashboard is reachable at from wherever you read your alerts — set it
|
|
27
|
+
# when the dashboard sits behind a reverse proxy or a tunnel. It becomes the Dashboard
|
|
28
|
+
# link in Discord messages; unset, that link points at http://<host>:<port> above.
|
|
29
|
+
# VINTED_BOT_WEB_PUBLIC_URL=
|
|
30
|
+
|
|
31
|
+
# Extra host names the dashboard answers to, comma-separated (e.g. nas.local). It always
|
|
32
|
+
# answers to localhost, any IP address and the host of WEB_PUBLIC_URL; other names get a
|
|
33
|
+
# 400, which stops other websites using it through your browser (DNS rebinding).
|
|
34
|
+
# VINTED_BOT_WEB_ALLOWED_HOSTS=
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ======================================================================================
|
|
38
|
+
# Everything below is tuning. The defaults are deliberate; skip this on a first read.
|
|
39
|
+
# ======================================================================================
|
|
40
|
+
|
|
41
|
+
# --- How often to check --------------------------------------------------------------
|
|
42
|
+
# Seconds between checks for a new search. Per-search overrides live in the app itself.
|
|
43
|
+
# Below 10 is ignored: Vinted's own catalog lags further behind than that, so checking
|
|
44
|
+
# faster finds nothing sooner and does get you blocked.
|
|
45
|
+
# VINTED_BOT_POLL_DEFAULT_INTERVAL_S=60
|
|
46
|
+
|
|
47
|
+
# How old a listing may be and still count as new, in minutes. Listings the app can tell
|
|
48
|
+
# were already on Vinted longer ago are skipped, and after a gap in checking longer than
|
|
49
|
+
# this (the app off, the computer asleep) the first check records what it missed without
|
|
50
|
+
# announcing it, so a restart never replays yesterday's catalog.
|
|
51
|
+
# VINTED_BOT_FRESHNESS_WINDOW_MIN=20
|
|
52
|
+
|
|
53
|
+
# What a brand-new search does on its first check:
|
|
54
|
+
# silent notify nothing, just remember what is already there (default)
|
|
55
|
+
# newest send exactly one listing, so you can confirm delivery works
|
|
56
|
+
# VINTED_BOT_FIRST_RUN_MODE=silent
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# --- Staying unblocked ---------------------------------------------------------------
|
|
60
|
+
# Start a fresh session after this many minutes. Blocks track how long a session has been
|
|
61
|
+
# alive more than how fast it is used, so replacing them on a timer helps.
|
|
62
|
+
# VINTED_BOT_SESSION_ROTATE_MINUTES=60
|
|
63
|
+
|
|
64
|
+
# Present a real browser's TLS fingerprint instead of Python's. Only worth turning on if
|
|
65
|
+
# you are getting blocked while the same search loads fine in your browser. Needs the
|
|
66
|
+
# image built with the 'impersonate' extra.
|
|
67
|
+
# VINTED_BOT_HTTP_IMPERSONATE=false
|
|
68
|
+
|
|
69
|
+
# A text file of proxy URLs, one per line. Off by default and rarely needed.
|
|
70
|
+
# VINTED_BOT_PROXY_FILE=
|
|
71
|
+
|
|
72
|
+
# How long to wait for Vinted before giving up on a single request.
|
|
73
|
+
# VINTED_BOT_REQUEST_TIMEOUT_S=15
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# --- Noticing when something is wrong -------------------------------------------------
|
|
77
|
+
# How many checks with no new listing before a search is treated as stuck. It is only
|
|
78
|
+
# treated as stuck if other searches on the same site are still finding things.
|
|
79
|
+
# VINTED_BOT_WATCHDOG_STALE_CYCLES=10
|
|
80
|
+
|
|
81
|
+
# What to do about it: warn, or also start a fresh session (default).
|
|
82
|
+
# VINTED_BOT_WATCHDOG_ACTION=rotate
|
|
83
|
+
|
|
84
|
+
# Give up on an undelivered notification after this long. An alert about a listing from
|
|
85
|
+
# two hours ago is not worth sending.
|
|
86
|
+
# VINTED_BOT_OUTBOX_EXPIRY_MINUTES=60
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# --- Where things are stored ----------------------------------------------------------
|
|
90
|
+
# Inside Docker this is already /data/app.db; leave it alone unless you know why.
|
|
91
|
+
# Unset outside Docker: ./data/app.db if that file already exists (so upgrades keep your
|
|
92
|
+
# searches), otherwise your user data folder, e.g. ~/.local/share/vinted-bot/app.db.
|
|
93
|
+
# VINTED_BOT_DB_PATH=./data/app.db
|
|
94
|
+
|
|
95
|
+
# Delete stored listings after this many days. Does not cause anything to be re-sent.
|
|
96
|
+
# VINTED_BOT_ITEM_RETENTION_DAYS=30
|
|
97
|
+
|
|
98
|
+
# Keep each listing's raw API payload. Useful when debugging, but it stores more seller
|
|
99
|
+
# data than notifications need, so it is off by default.
|
|
100
|
+
# VINTED_BOT_KEEP_RAW_JSON=false
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# --- Time ------------------------------------------------------------------------------
|
|
104
|
+
# The time zone quiet hours and the stats page use. TZ is used if this is unset; the
|
|
105
|
+
# dashboard's settings page can change it later.
|
|
106
|
+
# VINTED_BOT_TIMEZONE=UTC
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# --- Updating --------------------------------------------------------------------------
|
|
110
|
+
# Set by the installers' start-at-login entries (launchd, systemd or windows-launcher) to
|
|
111
|
+
# say something restarts the app after it exits, so updating from the dashboard can
|
|
112
|
+
# restart it too. Only set it for your own service if that service restarts after any exit.
|
|
113
|
+
# VINTED_BOT_SUPERVISED=
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
# --- Logging ---------------------------------------------------------------------------
|
|
117
|
+
# VINTED_BOT_LOG_LEVEL=INFO
|
|
118
|
+
# console while you are watching, json when something else is collecting it.
|
|
119
|
+
# VINTED_BOT_LOG_FORMAT=console
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# --- Working on the code ----------------------------------------------------------------
|
|
123
|
+
# 'mock' replays recorded responses from disk instead of calling Vinted, so you can
|
|
124
|
+
# develop and demo offline. Requires a scenario directory.
|
|
125
|
+
# VINTED_BOT_FETCH_MODE=live
|
|
126
|
+
# VINTED_BOT_MOCK_SCENARIO_DIR=
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.venv/
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
.mypy_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
.pytest_cache/
|
|
7
|
+
.coverage
|
|
8
|
+
htmlcov/
|
|
9
|
+
dist/
|
|
10
|
+
build/
|
|
11
|
+
*.egg-info/
|
|
12
|
+
|
|
13
|
+
# Runtime data and secrets — never commit these.
|
|
14
|
+
.env
|
|
15
|
+
data/
|
|
16
|
+
*.db
|
|
17
|
+
*.db-wal
|
|
18
|
+
*.db-shm
|
|
19
|
+
|
|
20
|
+
.DS_Store
|
|
21
|
+
|
|
22
|
+
# Landing site build output (python3 site/build.py)
|
|
23
|
+
site/dist/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.13
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Every release, newest first. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
4
|
+
and versions follow [Semantic Versioning](https://semver.org/). Each version's section here
|
|
5
|
+
becomes the text of its GitHub release, so write it for the people running the app.
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [2.1.0] - 2026-10-30
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
|
|
13
|
+
- **Renamed to vinted-bot** (from vinted-sniper), the name people search for. Everything
|
|
14
|
+
uses it now: the `vinted-bot` command, the PyPI package `vinted-bot`, the Docker image
|
|
15
|
+
`ghcr.io/jasp-nerd/vinted-bot`, the repository github.com/jasp-nerd/vinted-bot (old links
|
|
16
|
+
redirect), the website vinted-bot.jaspnerd.dev, settings (`VINTED_BOT_...`), the data folder
|
|
17
|
+
(`vinted-bot`), the installers' start-at-login entries, the Docker volume
|
|
18
|
+
(`vinted-bot-data`), the default Discord name and the webhook headers
|
|
19
|
+
(`X-Vinted-Bot-Signature`, `X-Vinted-Bot-Timestamp`). The old names are not read any more,
|
|
20
|
+
so this is a fresh install: install vinted-bot with the commands in the README, uninstall
|
|
21
|
+
the old copy, and rename any settings in your `.env`. To keep your searches, copy the old
|
|
22
|
+
data folder's `app.db` into the new one before the first start.
|
|
23
|
+
- The installers' start-at-login entries now tell the app they restart it
|
|
24
|
+
(`VINTED_BOT_SUPERVISED`), and the Windows launcher stays running in the background so it
|
|
25
|
+
can install an update while the app is closed.
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **Update from the dashboard.** Where the installer set vinted-bot up to start by itself,
|
|
30
|
+
"Update now" installs the new version and restarts into it, and "Install updates by
|
|
31
|
+
itself" (off unless you turn it on) does that between 4 and 5 at night, never while an
|
|
32
|
+
alert is on its way out, then says so in your status alerts. Installed with uv or pipx but
|
|
33
|
+
started by hand, the button installs it and tells you how to restart. Docker and source
|
|
34
|
+
installs are shown the command to run. Updates only ever come from PyPI through uv or
|
|
35
|
+
pipx, and only when GitHub has a newer release; a failed update leaves the running version
|
|
36
|
+
alone and says what went wrong.
|
|
37
|
+
|
|
38
|
+
### Removed
|
|
39
|
+
|
|
40
|
+
- The empty `web` extra, kept in 2.0 only so 1.x install commands still worked. The
|
|
41
|
+
dashboard is part of every install.
|
|
42
|
+
|
|
43
|
+
## [2.0.0] - 2026-10-30
|
|
44
|
+
|
|
45
|
+
Vinted moved its search to a new address in September 2026, and every earlier version stopped
|
|
46
|
+
finding listings. This release works again, and comes with a rebuilt dashboard, many more
|
|
47
|
+
filters, new-look alerts and one-line installers for Windows, macOS and Linux. Your searches
|
|
48
|
+
and destinations carry over: the database upgrades itself on the first start.
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- Searches work again after Vinted's September 2026 move to a new search address
|
|
53
|
+
([#9](https://github.com/jasp-nerd/vinted-bot/issues/9),
|
|
54
|
+
[#12](https://github.com/jasp-nerd/vinted-bot/issues/12),
|
|
55
|
+
[#14](https://github.com/jasp-nerd/vinted-bot/issues/14)). Filters saved before the move
|
|
56
|
+
keep working, and brand, size and condition show up in alerts again.
|
|
57
|
+
- The search builder's filter options load again.
|
|
58
|
+
- A listing that turns up in the results after a newer one was announced is no longer
|
|
59
|
+
skipped forever. New listings are now recognised by their id, since Vinted's search stopped
|
|
60
|
+
saying when a photo was taken.
|
|
61
|
+
- Excluding a word only matches whole words, ignoring accents and capitals: excluding "art"
|
|
62
|
+
no longer drops "smartphone".
|
|
63
|
+
- ntfy alerts with accents or a euro sign in the title no longer fail to send.
|
|
64
|
+
- A listing title containing brackets or HTML can no longer turn into a link in ntfy, or load
|
|
65
|
+
an image in an Apprise email.
|
|
66
|
+
- Prices read back as "15.00" instead of "15.0", and "12,50" means the same thing in the
|
|
67
|
+
dashboard, the command line and the Telegram bot.
|
|
68
|
+
- A database upgrade that fails part-way no longer leaves the database half changed.
|
|
69
|
+
- A locked database no longer stops the whole app from its heartbeat.
|
|
70
|
+
- A search that has failed since it was added shows as failing, not as starting.
|
|
71
|
+
- Restarting no longer fires every search in the same second.
|
|
72
|
+
- Starting again after the app was off, the computer asleep or Vinted refusing for a while no
|
|
73
|
+
longer announces a whole page of old listings as new. The first check back records what it
|
|
74
|
+
missed quietly. The same goes for the first check after upgrading from 1.x, which now
|
|
75
|
+
carries on from the newest listing 1.x had seen.
|
|
76
|
+
- "Ignore listings older than" (and `watch --max-age`, and `FRESHNESS_WINDOW_MIN`) works
|
|
77
|
+
again: it skips listings the app can tell were already on Vinted that long ago, such as an
|
|
78
|
+
old listing whose price was just cut into range.
|
|
79
|
+
- Changing a search's link or sites starts it over quietly, instead of announcing everything
|
|
80
|
+
above where the old search had got to, and its typical price starts again too.
|
|
81
|
+
- A listing still for sale after the retention period (30 days) is no longer announced again,
|
|
82
|
+
by a search or by a seller watch.
|
|
83
|
+
- A listing two searches match is judged by each search's own typical price, so a narrow
|
|
84
|
+
search's steal is no longer reported as overpriced because a broader search found it
|
|
85
|
+
first.
|
|
86
|
+
- When Vinted refuses the homepage itself, a search backs off like for any other refusal
|
|
87
|
+
instead of retrying every 15 seconds. A search that crashes is restarted with growing
|
|
88
|
+
pauses, and tokens that keep being rejected are no longer retried every five seconds.
|
|
89
|
+
- When Vinted refuses or rate-limits seller lookups, the app stops asking for a while
|
|
90
|
+
(honouring `Retry-After`), and the listings waiting on one are listed in "Not sent" as
|
|
91
|
+
"seller unknown".
|
|
92
|
+
- Alerts a destination did not want (for example a normal listing for a steals-only
|
|
93
|
+
channel) no longer count as failed deliveries on the alerts and stats pages.
|
|
94
|
+
- An open dashboard tab no longer misses finds after a quiet month emptied the stored
|
|
95
|
+
listings.
|
|
96
|
+
- Watch alerts sent to a webhook are posted one watch at a time, so `search` names the right
|
|
97
|
+
watch.
|
|
98
|
+
- **Security:** the dashboard only answers to localhost, IP addresses, the `WEB_PUBLIC_URL`
|
|
99
|
+
host and the new `WEB_ALLOWED_HOSTS`, however it is bound. Before, the Docker image's
|
|
100
|
+
`0.0.0.0` bind switched that check off, which let a web page use a password-less
|
|
101
|
+
dashboard through your browser (DNS rebinding). If you reach the dashboard by a name, add
|
|
102
|
+
it to `VINTED_SNIPER_WEB_ALLOWED_HOSTS`; the error page says so too.
|
|
103
|
+
- **Security:** a search name can no longer run script in the stats page's chart tooltips.
|
|
104
|
+
- **Security:** in a CSV export, a cell that would start a spreadsheet formula (a title such
|
|
105
|
+
as `=HYPERLINK(...)`) is prefixed with an apostrophe so it shows as text.
|
|
106
|
+
- **Security:** signing in to the dashboard gives each browser its own random session, which
|
|
107
|
+
signing out revokes, and changing the password signs everyone out. After five wrong
|
|
108
|
+
passwords from one address, sign-in waits 15 minutes.
|
|
109
|
+
|
|
110
|
+
### Added
|
|
111
|
+
|
|
112
|
+
- **A rebuilt dashboard**: a guided setup on first run that ends with a test alert, a live
|
|
113
|
+
feed of finds laid out like Vinted's own grid (save, hide, block the seller), a plain-words
|
|
114
|
+
status for every search, a "Not sent" list showing why each skipped listing was skipped, a
|
|
115
|
+
watchlist, stats, settings, dark mode and a phone layout.
|
|
116
|
+
- **More filters**: words the title must contain (with alternatives such as `501|505`), a title
|
|
117
|
+
pattern, a minimum price, seller rating, number of reviews and country, and a seller
|
|
118
|
+
blocklist.
|
|
119
|
+
- **Deals**: each search learns its typical price, alerts show how far under it a listing is,
|
|
120
|
+
and a search or a destination can be set to deals only.
|
|
121
|
+
- **Quiet hours** per search, in your time zone: hold alerts until the end, drop them, or send
|
|
122
|
+
them without a sound.
|
|
123
|
+
- One search can cover several country sites.
|
|
124
|
+
- **Watches**: hear when a listing's price drops or it is reserved, sold or removed, and when
|
|
125
|
+
a seller uploads something new.
|
|
126
|
+
- **Discord alerts** as cards with a photo gallery, the total price, a deal badge and Buy,
|
|
127
|
+
Message and Open buttons, in the search's colour. Up to three share a message, and bigger bursts become a
|
|
128
|
+
compact digest. Mentions are opt-in.
|
|
129
|
+
- **Telegram alerts** as photo cards with buttons, an optional gallery style, and a digest in
|
|
130
|
+
busy groups. A paired chat can `/list`, `/pause`, `/resume`, `/add` and `/remove` searches
|
|
131
|
+
and ask for `/status`.
|
|
132
|
+
- Alerts are written in the language of the listing's site (English, French, German, Dutch,
|
|
133
|
+
Spanish, Italian, Polish, Lithuanian and Czech) with prices formatted to match.
|
|
134
|
+
- ntfy alerts gain the photo, action buttons and a higher priority for deals.
|
|
135
|
+
- **Apprise**, an optional extra, for email, Slack, Pushover, Gotify, Matrix and many more.
|
|
136
|
+
- Webhook payloads can be signed with a shared secret (HMAC-SHA256).
|
|
137
|
+
- A test button for every destination, and a preview of what its alerts will look like.
|
|
138
|
+
- Export found listings as CSV or JSON, from the dashboard or `vinted-sniper export`.
|
|
139
|
+
- A notice in the dashboard when a newer version is out (one request to GitHub a day, can be
|
|
140
|
+
turned off), and an optional uptime ping to a service such as healthchecks.io.
|
|
141
|
+
- `vinted-sniper run --open` opens the dashboard once it is ready.
|
|
142
|
+
- One-line installers for Windows (`install.ps1`) and macOS and Linux (`install.sh`), with
|
|
143
|
+
optional start at login, no Docker and no admin rights.
|
|
144
|
+
- Docker: `PUID` and `PGID` to choose who owns the data folder, so NAS shares and cloud
|
|
145
|
+
volumes owned by root work without manual fixes, a `TZ` setting, and an opt-in automatic
|
|
146
|
+
updater.
|
|
147
|
+
- Templates for Unraid, Umbrel, CasaOS, Render and Coolify in `deploy/`.
|
|
148
|
+
- `llms.txt`, a setup guide an AI assistant can follow to walk someone through installing,
|
|
149
|
+
and a ready-made prompt in the README.
|
|
150
|
+
- Issue forms that ask for the version, the status output and the log.
|
|
151
|
+
|
|
152
|
+
### Changed
|
|
153
|
+
|
|
154
|
+
- The `latest` image tag now means the newest release. Builds of the main branch are tagged
|
|
155
|
+
`edge`.
|
|
156
|
+
- The dashboard is part of every install. `vinted-sniper[web]` still works but is no longer
|
|
157
|
+
needed.
|
|
158
|
+
- Outside a container, the database lives in your user data folder (unless `./data/app.db`
|
|
159
|
+
already exists, which keeps being used).
|
|
160
|
+
- A search you haven't routed anywhere goes to every active destination, including ones added
|
|
161
|
+
later.
|
|
162
|
+
- The same Vinted link can be added twice with different filters, for example to send a
|
|
163
|
+
stricter copy somewhere else.
|
|
164
|
+
- The webhook payload is version 2: every version 1 field is still there, with the alert
|
|
165
|
+
kind, deal, photos and seller details added beside them. Each POST holds one search's
|
|
166
|
+
listings, or one watch's. The fields and the signature headers are documented in
|
|
167
|
+
docs/configuration.md.
|
|
168
|
+
- **RSS feeds of a password-protected dashboard need the new feed key** instead of the
|
|
169
|
+
password: `?key=<your password>` no longer works, because feed addresses end up in reader
|
|
170
|
+
settings and proxy logs. Copy the new address from the "RSS feed of this search" link on
|
|
171
|
+
the search's page.
|
|
172
|
+
|
|
173
|
+
## [0.1.0] - 2026-08-16
|
|
174
|
+
|
|
175
|
+
First version, never tagged as a release: polling with a stale-catalog watchdog, filters on
|
|
176
|
+
the total price including buyer protection, Discord, Telegram, ntfy, RSS and webhook
|
|
177
|
+
delivery through a reliable outbox, and a web dashboard.
|
|
178
|
+
|
|
179
|
+
[Unreleased]: https://github.com/jasp-nerd/vinted-bot/compare/v2.1.0...HEAD
|
|
180
|
+
[2.1.0]: https://github.com/jasp-nerd/vinted-bot/compare/v2.0.0...v2.1.0
|
|
181
|
+
[2.0.0]: https://github.com/jasp-nerd/vinted-bot/compare/v0.1.0...v2.0.0
|
|
182
|
+
[0.1.0]: https://github.com/jasp-nerd/vinted-bot/commits/v0.1.0
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Bug reports with logs attached are the most useful thing anyone can send. For code, read on.
|
|
4
|
+
|
|
5
|
+
## Setting up
|
|
6
|
+
|
|
7
|
+
You need [uv](https://docs.astral.sh/uv/). It installs the right Python by itself.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/jasp-nerd/vinted-bot && cd vinted-bot
|
|
11
|
+
uv sync --all-extras
|
|
12
|
+
uv run vinted-bot run --open
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The test suite never contacts Vinted, so it runs anywhere.
|
|
16
|
+
[docs/architecture.md](docs/architecture.md) explains how the pieces fit together.
|
|
17
|
+
|
|
18
|
+
### A dashboard with something in it
|
|
19
|
+
|
|
20
|
+
For screenshots, the public demo or working on the dashboard, `scripts/seed_demo.py` fills
|
|
21
|
+
a fresh database with invented searches, a month of finds, destinations and delivery
|
|
22
|
+
history. Its photos are the AI-generated ones hosted with the project site
|
|
23
|
+
(`scripts/demo/catalog.json` lists them), so nothing points at a real listing.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
uv run python scripts/seed_demo.py /tmp/demo.db
|
|
27
|
+
VINTED_BOT_DB_PATH=/tmp/demo.db VINTED_BOT_FETCH_MODE=mock \
|
|
28
|
+
VINTED_BOT_MOCK_SCENARIO_DIR=scripts/demo/mock uv run vinted-bot run --open
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The mock scenario answers every search with an empty page, so this never talks to Vinted
|
|
32
|
+
and never sends an alert. Leave out the two mock variables and the demo searches check
|
|
33
|
+
Vinted for real, adding real finds next to the invented ones. Even then nothing already
|
|
34
|
+
on Vinted is announced, and the demo's places for alerts reach nobody (a random ntfy
|
|
35
|
+
name, a switched-off Discord webhook, a Telegram chat that doesn't exist). Run it from a
|
|
36
|
+
folder without your own `.env`, or it picks up your real bot token and settings.
|
|
37
|
+
|
|
38
|
+
The dashboard's templates live in `src/vinted_bot/web/templates` and its CSS and
|
|
39
|
+
script in `web/static`; they need no build step. Static files are served with a content
|
|
40
|
+
hash in the URL, so a reload always picks up a change.
|
|
41
|
+
|
|
42
|
+
## Before you push
|
|
43
|
+
|
|
44
|
+
CI runs exactly these, and a pull request needs all of them green:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv run pytest
|
|
48
|
+
uv run ruff check .
|
|
49
|
+
uv run ruff format --check .
|
|
50
|
+
uv run mypy
|
|
51
|
+
uv run deptry src
|
|
52
|
+
uv run vulture
|
|
53
|
+
uv run --with zizmor zizmor .github/workflows # only if you touched a workflow
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
If you changed `install.sh` or `docker/entrypoint.sh`, also run
|
|
57
|
+
`uvx --from shellcheck-py shellcheck -s sh install.sh docker/entrypoint.sh`. If you changed
|
|
58
|
+
the Dockerfile, `docker build .` and start the result once.
|
|
59
|
+
|
|
60
|
+
## What a change is held to
|
|
61
|
+
|
|
62
|
+
[docs/REVIEW.md](docs/REVIEW.md) is the checklist every change is reviewed against, yours and
|
|
63
|
+
the maintainer's alike. The short version:
|
|
64
|
+
|
|
65
|
+
- Every new behaviour gets a test that would fail without it. Don't loosen a test to make it
|
|
66
|
+
pass.
|
|
67
|
+
- Match the code around you: docstrings that explain *why*, plain asyncio, SQL only in
|
|
68
|
+
`db/repo.py`, structlog events named like `poller.started`.
|
|
69
|
+
- No retry or fallback whose only job is to make a symptom go away.
|
|
70
|
+
- The README and docs never claim something the code doesn't do.
|
|
71
|
+
- Never log in to Vinted, and no features that need a Vinted account (buying, messaging,
|
|
72
|
+
offers). That is a deliberate line, not a missing feature.
|
|
73
|
+
|
|
74
|
+
## Commit messages
|
|
75
|
+
|
|
76
|
+
One plain sentence in the imperative, saying what the change does for someone using or
|
|
77
|
+
reading the code:
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
Retry Telegram sends that hit a rate limit after the delay Telegram asks for
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
No prefixes (`feat:`, `fix(web):`), no ticket numbers in the subject, no trailers crediting
|
|
84
|
+
tools. Add a body when the *why* isn't obvious from the subject.
|
|
85
|
+
|
|
86
|
+
## Pull requests
|
|
87
|
+
|
|
88
|
+
Keep each one about one thing. Say what changed and how you checked it. A new abstraction
|
|
89
|
+
(a base class, a registry, a new layer) needs one line explaining why the simpler version
|
|
90
|
+
wasn't enough.
|
|
91
|
+
|
|
92
|
+
User-facing changes get a line in `CHANGELOG.md` under `[Unreleased]`, written for the
|
|
93
|
+
people running the app.
|
|
94
|
+
|
|
95
|
+
## Cutting a release
|
|
96
|
+
|
|
97
|
+
For the maintainer. A release is one button; the workflows do the rest.
|
|
98
|
+
|
|
99
|
+
1. In a pull request: in `CHANGELOG.md`, rename `## [Unreleased]` to `## [X.Y.Z] - YYYY-MM-DD`,
|
|
100
|
+
add a fresh empty `## [Unreleased]` above it, and update the compare links at the bottom.
|
|
101
|
+
Set `version = "X.Y.Z"` in `pyproject.toml` and `__version__` in
|
|
102
|
+
`src/vinted_bot/__init__.py`, run `uv lock`, and merge it to main.
|
|
103
|
+
2. On GitHub, open **Actions**, choose **Release** on the left, click **Run workflow**, leave
|
|
104
|
+
the branch on `main`, and click the green **Run workflow** button. There is nothing to
|
|
105
|
+
fill in.
|
|
106
|
+
|
|
107
|
+
The Release workflow then:
|
|
108
|
+
|
|
109
|
+
- reads the version from `pyproject.toml`, and stops without changing anything if
|
|
110
|
+
`CHANGELOG.md` has no `## [X.Y.Z]` section, if the tag `vX.Y.Z` already exists, or if it
|
|
111
|
+
was started on a branch other than main;
|
|
112
|
+
- creates the tag `vX.Y.Z` on that commit;
|
|
113
|
+
- builds the package and publishes it to PyPI;
|
|
114
|
+
- builds the Docker image for amd64 and arm64 and tags it `X.Y.Z`, `X.Y`, `X` and `latest`
|
|
115
|
+
(by calling **Publish image**);
|
|
116
|
+
- last, creates the GitHub release with this version's changelog section as its text and the
|
|
117
|
+
package files attached. The dashboard's update notice reads that release, so it only
|
|
118
|
+
appears once the package and the image are out.
|
|
119
|
+
|
|
120
|
+
If a check fails before the tag is made, fix it and click Run workflow again. If a later job
|
|
121
|
+
fails (PyPI or the registry having a bad moment), open that run and use **Re-run failed
|
|
122
|
+
jobs**: a new run would refuse, because the tag exists by then. If the fix needs a code
|
|
123
|
+
change, release it as the next version instead.
|
|
124
|
+
|
|
125
|
+
Pushing a tag by hand (`git tag -a vX.Y.Z -m vX.Y.Z && git push origin vX.Y.Z`) still works
|
|
126
|
+
and does the same, with the image built by **Publish image** on that push. Builds of main
|
|
127
|
+
keep going out as the `edge` image in between.
|
|
128
|
+
|
|
129
|
+
### One-time PyPI setup
|
|
130
|
+
|
|
131
|
+
The Release workflow publishes with PyPI trusted publishing, so no token is stored anywhere.
|
|
132
|
+
Before the first release:
|
|
133
|
+
|
|
134
|
+
1. On pypi.org, open Your account, **Publishing**, and under "Add a new pending publisher"
|
|
135
|
+
choose GitHub and fill in:
|
|
136
|
+
|
|
137
|
+
| Field | Value |
|
|
138
|
+
|---|---|
|
|
139
|
+
| PyPI Project Name | `vinted-bot` |
|
|
140
|
+
| Owner | `jasp-nerd` |
|
|
141
|
+
| Repository name | `vinted-bot` |
|
|
142
|
+
| Workflow name | `release.yml` |
|
|
143
|
+
| Environment name | `pypi` |
|
|
144
|
+
|
|
145
|
+
The first release creates the project and turns the pending publisher into a normal one.
|
|
146
|
+
2. In the GitHub repository's **Settings**, **Environments**, create an environment named
|
|
147
|
+
`pypi`. Adding yourself as a required reviewer there means each PyPI upload waits for a
|
|
148
|
+
click.
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Build stage: resolve and install dependencies into a self-contained virtualenv.
|
|
2
|
+
# Both base images are pinned by digest so a rebuild of the same commit gets the same bytes;
|
|
3
|
+
# Dependabot moves the digests forward weekly.
|
|
4
|
+
FROM ghcr.io/astral-sh/uv:python3.13-trixie-slim@sha256:e586c1c65b7071aec78f02c5587c76c5011159cd76b3d6f1255f40e48e850abc AS build
|
|
5
|
+
|
|
6
|
+
ENV UV_COMPILE_BYTECODE=1 \
|
|
7
|
+
UV_LINK_MODE=copy \
|
|
8
|
+
UV_PYTHON_DOWNLOADS=never
|
|
9
|
+
|
|
10
|
+
WORKDIR /app
|
|
11
|
+
|
|
12
|
+
# Dependencies change far less often than the code, so they get their own layer and their
|
|
13
|
+
# own cache mount. Editing a source file then costs a rebuild of the last layer only.
|
|
14
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
15
|
+
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
|
|
16
|
+
--mount=type=bind,source=uv.lock,target=uv.lock \
|
|
17
|
+
--mount=type=bind,source=README.md,target=README.md \
|
|
18
|
+
uv sync --locked --no-install-project --no-dev
|
|
19
|
+
|
|
20
|
+
COPY . /app
|
|
21
|
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
|
22
|
+
uv sync --locked --no-editable --no-dev
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Runtime stage: the virtualenv and the code, nothing else.
|
|
26
|
+
FROM python:3.13-slim-trixie@sha256:2d44738ebf4ce5f63b9d9ea871e9c7cfdc0426f7b0c97cac915fd88ded1d86bb
|
|
27
|
+
|
|
28
|
+
# Static so a local build carries them too; the publish workflow adds version and revision.
|
|
29
|
+
LABEL org.opencontainers.image.title="vinted-bot" \
|
|
30
|
+
org.opencontainers.image.description="Watch Vinted searches and get a message when a new listing matches." \
|
|
31
|
+
org.opencontainers.image.source="https://github.com/jasp-nerd/vinted-bot" \
|
|
32
|
+
org.opencontainers.image.url="https://github.com/jasp-nerd/vinted-bot" \
|
|
33
|
+
org.opencontainers.image.documentation="https://github.com/jasp-nerd/vinted-bot/blob/main/docs/self-hosting.md" \
|
|
34
|
+
org.opencontainers.image.licenses="MIT"
|
|
35
|
+
|
|
36
|
+
# curl is here for the health check and for the connection test in the troubleshooting
|
|
37
|
+
# guide, which is the first thing anyone reaches for when listings stop arriving.
|
|
38
|
+
RUN apt-get update \
|
|
39
|
+
&& apt-get install -y --no-install-recommends curl \
|
|
40
|
+
&& rm -rf /var/lib/apt/lists/* \
|
|
41
|
+
&& groupadd --system --gid 10001 vinted \
|
|
42
|
+
&& useradd --system --create-home --uid 10001 --gid 10001 vinted
|
|
43
|
+
|
|
44
|
+
WORKDIR /app
|
|
45
|
+
COPY --from=build --chown=vinted:vinted /app/.venv /app/.venv
|
|
46
|
+
COPY --from=build --chown=vinted:vinted /app/src /app/src
|
|
47
|
+
|
|
48
|
+
# The privilege-dropping wrapper from docker/entrypoint.sh. It sits ahead of the real
|
|
49
|
+
# command on PATH, so the health check and `docker compose exec ... vinted-bot status`
|
|
50
|
+
# also run as the app's user rather than as root.
|
|
51
|
+
COPY --chmod=755 docker/entrypoint.sh /app/bin/vinted-bot
|
|
52
|
+
|
|
53
|
+
# The web host is 0.0.0.0 *inside the container* so the published port can reach it;
|
|
54
|
+
# docker-compose.yml maps it to the host's localhost only.
|
|
55
|
+
ENV PATH="/app/bin:/app/.venv/bin:$PATH" \
|
|
56
|
+
PYTHONUNBUFFERED=1 \
|
|
57
|
+
VINTED_BOT_DB_PATH=/data/app.db \
|
|
58
|
+
VINTED_BOT_LOG_FORMAT=json \
|
|
59
|
+
VINTED_BOT_WEB_HOST=0.0.0.0
|
|
60
|
+
|
|
61
|
+
RUN mkdir -p /data && chown vinted:vinted /data
|
|
62
|
+
VOLUME ["/data"]
|
|
63
|
+
|
|
64
|
+
# No USER line on purpose: the container starts as root only long enough for the wrapper
|
|
65
|
+
# to hand /data to PUID:PGID (default 10001, the `vinted` user), then the app runs as that
|
|
66
|
+
# user. Anyone who wants no root at all can set `user: "10001:10001"` instead.
|
|
67
|
+
|
|
68
|
+
EXPOSE 8000
|
|
69
|
+
|
|
70
|
+
# Checks that the app came round the loop recently, not merely that a process exists —
|
|
71
|
+
# a poller can be alive and stuck, which is the failure worth catching.
|
|
72
|
+
HEALTHCHECK --interval=60s --timeout=10s --start-period=45s --retries=3 \
|
|
73
|
+
CMD ["vinted-bot", "heartbeat"]
|
|
74
|
+
|
|
75
|
+
ENTRYPOINT ["vinted-bot"]
|
|
76
|
+
CMD ["run"]
|
vinted_bot-2.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 vinted-bot contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|