- added get_close_notification_button helper and CLOSE_BUTTON_ID constant - added close button as a selectable option in broadcast button picker - renamed get_goto_buttons to get_broadcast_buttons - fixed webhook endpoint: moved gateway.build_webhook_response out of finally block - re-enabled YooKassa webhook verification - fixed FreeKassa nonce: switched to time.time_ns() for strict monotonicity - fixed FreeKassa currency: use .upper() instead of .value - fixed system stats getter: use stats.memory.used instead of active, cpu.cores instead of physical_cores - fixed node traffic_limit: pass None instead of 0 to i18n_format_bytes_to_unit - added as_payload to UserDevicesUpdatedEvent and SubscriptionRevokedEvent with user keyboard - added session.close() in UnitOfWork __aexit__ - added python-multipart dependency - added DOCS constant, updated update keyboard links to docs instead of GitHub README - bumped version 0.7.5
#TODO: add custom
Banners
The banners folder contains all banner images.
Banner configuration
You can configure how banners are displayed in the bot using an environment variable:
BOT_USE_BANNERS: Set to true to enable banners, or false to disable them.
Locale support
The banner system supports localized versions. A banner corresponding to the user's locale will be loaded for each user. Available locales are defined by the APP_LOCALES environment variable.
How it works:
When loading a banner, the system searches in the following order:
banners/{user_locale}/{page}— page-specific banner for the user’s localebanners/{user_locale}/default— default banner for the user’s localebanners/{default_locale}/{page}— page-specific banner for the default locale (APP_DEFAULT_LOCALE)banners/{default_locale}/default— default banner for the default localebanners/default— global fallback
Steps 3–4 handle the case where a user’s locale is not supported — the system falls back to the default locale’s banners before using the global default.
This means:
- To use one image everywhere — place a single
banners/default.jpg. - To use one image per locale — place
banners/{locale}/default.jpgfor each locale. - To use per-page images — place
banners/{locale}/{page}.jpgfor each page and locale.
This ensures that even if a specific banner or locale is not found, some banner will always be displayed, preventing empty or missing images.
Supported formats
The following file formats are supported, as defined in /remnashop/src/core/enums.py as BannerFormat:
- JPG
- JPEG
- PNG
- GIF
- WEBP
Banner names
Banner filenames must correspond to the following predefined names, specified in /remnashop/src/core/enums.py as BannerName:
DEFAULT: The default banner, used when a specific banner is not found.MENU: The main menu banner.DASHBOARD: The dashboard banner.SUBSCRIPTION: The subscription banner.REFERRAL: The referral banner.
Example file structure
banners/
├── default.jpg ← global default (used for all pages and locales as last fallback)
├── ru/
│ ├── default.jpg ← default for all pages in ru locale
│ ├── menu.jpg ← page-specific banner for ru locale
│ └── subscription.jpg
└── en/
├── default.jpg ← default for all pages in en locale
└── menu.jpg
Translations
The translations folder contains all localization text files.
Translation configuration
Supported locales are defined in environment variables:
APP_LOCALES: A list of supported locales. A full list of available locales can be found inremnashop/src/core/enums.pyasLocale.APP_DEFAULT_LOCALE: The default locale to be used if a user's language preference is not specified or not supported.
Key naming convention
All translation keys must follow a unified structure:
{category}-{scope}-{entity}-{action-or-state}
Components
| Part | Description | Example |
|---|---|---|
{category} |
Top-level type of text | btn, msg, ntf |
{scope} |
Logical group or subsystem | user, plan, broadcast, gateway, subscription, access, error, event |
{entity} |
Specific object or sub-entity | content, payment, link, node |
{action-or-state} |
Action or state, in lowercase | created, deleted, empty, invalid, failed, not-found |
Naming rules
- Use lowercase with hyphens (-) — no underscores or spaces.
- Follow the order:
category → scope → entity → action/state-
✅ ntf-broadcast-content-empty
-
✅ btn-user-create
-
✅ msg-plan-deleted-success
-
❌ ntf-content-empty-broadcast
-
❌ btn-create-user
-
❌ msg-plan-success-deleted
-
- Actions — past tense verbs (created, updated, deleted, canceled, failed).
- States — adjectives (empty, invalid, not-found, expired, not-available).
- Limit to 5 segments maximum.
Examples keys
| Purpose | Key |
|---|---|
| Notification: user expired | ntf-user-expired |
| Notification: broadcast empty content | ntf-broadcast-content-empty |
| Button: confirm deletion | btn-plan-confirm-delete |
| Message: plan created successfully | msg-plan-created-success |
| Notification: gateway test failed | ntf-gateway-test-payment-failed |
QR Code Logo
You can customize the appearance of the generated invitation QR code by adding your logo to the center of the code.
- Path:
assets/logo.png - Purpose: If this file exists, the system will use it as a logo, overlaying it in the center of the generated QR code image for branding purposes.
- Format: The logo must be a
PNGfile, preferably with a transparent background.