feat(telegram): promo deep-link seeds English Scrabble for new users
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 50s
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 56s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 50s
The promo bot button carries a configurable variant-seed start-param (default verudit_ru-scrabble_en). The gateway parses start_param from the validated initData and forwards it; the backend, on first contact only, seeds the new account variant_preferences from it (English Scrabble alongside the default Erudit). No schema change (the scrabble_en CHECK is already in the baseline) and the gateway<->backend REST field is additive, so the rolling deploy is safe in either order. TELEGRAM_PROMO_START_PARAM configures the payload (empty forwards the user own /start payload). Covered by account unit tests, a gateway transcode test, and an integration test asserting new-only seeding.
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
package promobot
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"net/url"
|
||||
"strings"
|
||||
@@ -33,6 +34,11 @@ type Config struct {
|
||||
// BotLinkURL is the main bot's Mini App direct link; the button appends
|
||||
// ?startapp=<payload> to it.
|
||||
BotLinkURL string
|
||||
// StartParam is the campaign payload appended as ?startapp=<StartParam> to the launch
|
||||
// button — e.g. a variant-seed deep link ("verudit_ru-scrabble_en") the backend
|
||||
// decodes to seed a brand-new user's variant preferences. Empty falls back to
|
||||
// forwarding the user's own /start payload.
|
||||
StartParam string
|
||||
// SendRatePerSecond caps outbound sends to respect the Bot API flood limits; 0
|
||||
// disables the limiter. The burst equals the per-second rate.
|
||||
SendRatePerSecond int
|
||||
@@ -40,11 +46,12 @@ type Config struct {
|
||||
|
||||
// Bot is the promo bot wrapper around a Telegram Bot API client.
|
||||
type Bot struct {
|
||||
api *tgbot.Bot
|
||||
username string
|
||||
linkURL string
|
||||
log *zap.Logger
|
||||
limiter *rate.Limiter
|
||||
api *tgbot.Bot
|
||||
username string
|
||||
linkURL string
|
||||
startParam string
|
||||
log *zap.Logger
|
||||
limiter *rate.Limiter
|
||||
}
|
||||
|
||||
// New builds the promo bot, registering a /start (and default) handler that replies
|
||||
@@ -53,7 +60,7 @@ func New(cfg Config, log *zap.Logger) (*Bot, error) {
|
||||
if log == nil {
|
||||
log = zap.NewNop()
|
||||
}
|
||||
t := &Bot{username: cfg.BotUsername, linkURL: cfg.BotLinkURL, log: log}
|
||||
t := &Bot{username: cfg.BotUsername, linkURL: cfg.BotLinkURL, startParam: cfg.StartParam, log: log}
|
||||
if cfg.SendRatePerSecond > 0 {
|
||||
t.limiter = rate.NewLimiter(rate.Limit(cfg.SendRatePerSecond), cfg.SendRatePerSecond)
|
||||
}
|
||||
@@ -87,7 +94,8 @@ func (t *Bot) Run(ctx context.Context) {
|
||||
}
|
||||
|
||||
// handleStart replies to any message (typically /start) with the localized promo text
|
||||
// and a button that opens the main bot's Mini App, forwarding any /start payload.
|
||||
// and a button that opens the main bot's Mini App at the configured campaign payload
|
||||
// (falling back to forwarding any /start payload the user arrived with).
|
||||
func (t *Bot) handleStart(ctx context.Context, api *tgbot.Bot, update *models.Update) {
|
||||
if update.Message == nil {
|
||||
return
|
||||
@@ -106,9 +114,11 @@ func (t *Bot) handleStart(ctx context.Context, api *tgbot.Bot, update *models.Up
|
||||
}
|
||||
text, button := promoText(lang, t.username)
|
||||
if _, err := api.SendMessage(ctx, &tgbot.SendMessageParams{
|
||||
ChatID: update.Message.Chat.ID,
|
||||
Text: text,
|
||||
ReplyMarkup: t.launchMarkup(button, startPayload(update.Message.Text)),
|
||||
ChatID: update.Message.Chat.ID,
|
||||
Text: text,
|
||||
// The configured campaign payload (a variant-seed deep link) takes precedence;
|
||||
// absent one, fall back to forwarding any /start payload the user arrived with.
|
||||
ReplyMarkup: t.launchMarkup(button, cmp.Or(t.startParam, startPayload(update.Message.Text))),
|
||||
}); err != nil {
|
||||
t.log.Warn("promo: reply to start failed", zap.Error(err))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user