Ctrl K
ComunidadGuías…

internal/domain/system.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package domain

import (
	"strings"
	"sync/atomic"

	"telesrv/internal/branding"
)

const (
	// OfficialSystemUserID is the official system account recognized by Telegram-compatible clients.
	OfficialSystemUserID int64 = 777000
	// OfficialSystemPhone is a reserved service identity, not an ordinary E.164
	// login number. Auth must recognize and reject it before account lookup.
	OfficialSystemPhone = "42777"

	// BotFatherUserID is the built-in BotFather account, with the same ID as the official @BotFather.
	BotFatherUserID int64 = 93372553
	// BotFatherAccessHash is fixed; it is double-written with the seed row in migration 0090 and the two must stay consistent.
	BotFatherAccessHash int64 = 7421896403922962293

	// StickersBotUserID is the built-in @Stickers account. It is a server-internal service bot
	// that does not go through an external Bot API process.
	StickersBotUserID int64 = 1063110917
	// StickersBotAccessHash is fixed; it is double-written with the postgres seed row and the two must stay consistent.
	StickersBotAccessHash int64 = 5213187021149032991

	// ChatBotUserID is the built-in @ChatBot account. It forwards private-chat text to the server AI provider chain.
	ChatBotUserID int64 = 1250000007
	// ChatBotAccessHash is fixed; it is double-written with the postgres seed row and the two must stay consistent.
	ChatBotAccessHash int64 = 6332902371644871201

	// VerifyBotUserID is the built-in @verifybot: it collects official platform
	// verification applications and reports decisions back to the applicant. The
	// id is reserved and stable, so a restart never re-creates the account under a
	// different identity.
	VerifyBotUserID int64 = 1250000011
	// VerifyBotAccessHash is fixed and double-written with the seed row in
	// migration 0153; the two must never drift.
	VerifyBotAccessHash int64 = 7802113947355620887

	// VerifierBotUserID is the built-in @verifierbot: the first THIRD-PARTY
	// verifier of a deployment (core.telegram.org/api/bots/verification). It
	// collects applications for its own icon+description mark and reports the
	// operator's decision back to the applicant. The id is reserved and stable, so
	// a restart never re-creates the account under a different identity.
	//
	// It is not a second route to the platform checkmark: that badge is granted by
	// the operator alone and collected by VerifyBotUserID above.
	VerifierBotUserID int64 = 1250000013
	// VerifierBotAccessHash is fixed and double-written with the seed row in
	// migration 0156; the two must never drift.
	VerifierBotAccessHash int64 = 6913402578811563729

	// PremiumBotUserID is the stable identity of the built-in @premiumbot.
	PremiumBotUserID     int64 = 1250000015
	PremiumBotAccessHash int64 = 5841763291047652381

	// GifBotUserID is intentionally distinct from PremiumBotUserID. The
	// upstream dev implementation reused 1250000015; that would overwrite the
	// retained Premium storefront in this project.
	GifBotUserID     int64 = 1250000017
	GifBotAccessHash int64 = 7233282977235616768

	// GiftRelayerUserID is the stable @relayer profile used to host an exported
	// collectible until its current TON owner claims it to a Gramsrv profile.
	GiftRelayerUserID     int64 = 1250000019
	GiftRelayerAccessHash int64 = 6184953271048627319

	// GiftClaimBotUserID is the stable @claim service bot and Mini App owner.
	GiftClaimBotUserID     int64 = 1250000021
	GiftClaimBotAccessHash int64 = 8462917350861429053

	// GiftClaimTestBotUserID is the stable @claimtest service bot and Mini App owner.
	GiftClaimTestBotUserID     int64 = 1250000022
	GiftClaimTestBotAccessHash int64 = 3284157906273045387
)

var configuredPremiumBotUserID atomic.Int64
var configuredPremiumBotUsername atomic.Value

// ConfigurePremiumBotUserID installs the deployment's reserved Premium bot ID
// before stores and RPC services are constructed.
func ConfigurePremiumBotUserID(id int64) bool {
	if !ValidPremiumBotUserID(id) {
		return false
	}
	configuredPremiumBotUserID.Store(id)
	return true
}

func ConfigurePremiumBotUsername(username string) bool {
	username = strings.TrimPrefix(strings.TrimSpace(username), "@")
	if !ValidBotUsername(username) {
		return false
	}
	configuredPremiumBotUsername.Store(username)
	return true
}

func ValidPremiumBotUserID(id int64) bool {
	if id <= 0 {
		return false
	}
	switch id {
	case OfficialSystemUserID, BotFatherUserID, StickersBotUserID, ChatBotUserID,
		VerifyBotUserID, VerifierBotUserID, GifBotUserID:
		return false
	}
	return true
}

func PremiumBotConfiguredUserID() int64 {
	if id := configuredPremiumBotUserID.Load(); id > 0 {
		return id
	}
	return PremiumBotUserID
}

func PremiumBotConfiguredUsername() string {
	if username, ok := configuredPremiumBotUsername.Load().(string); ok && username != "" {
		return username
	}
	return "premiumbot"
}

// officialSystemUserDisplayName overrides OfficialSystemUser's FirstName —
// empty means "use branding.ProductName()" (the compile-time default). The name an operator
// configures in Server Settings → Server identity is written both at startup and at runtime
// when the identity watcher detects a change (the main server and the admin panel are two processes sharing
// only identity.json), so it is held in an atomic.Value to avoid a data race between the projection path and the refresh path.
// It deliberately overrides only the display name, not Username: @username is a stable, addressable identifier
// that other things may already reference, whereas the display name appears only in the chat header.
var officialSystemUserDisplayName atomic.Value

// SetOfficialSystemUserDisplayName records the custom server name an operator
// configured for the official system account (777000), called at startup and on runtime identity refresh.
// Passing "" means fall back to branding.ProductName() — the same
// "unset → default" contract as the avatar override.
func SetOfficialSystemUserDisplayName(name string) {
	officialSystemUserDisplayName.Store(strings.TrimSpace(name))
}

// officialSystemDisplayName returns the official system account's currently effective display name: the operator's
// custom value set via SetOfficialSystemUserDisplayName if present, otherwise
// branding.ProductName(). It is shared by OfficialSystemUser (777000's FirstName) and the login
// welcome message's {{server_name}} placeholder (see login_welcome_template.go),
// keeping the two consistent.
func officialSystemDisplayName() string {
	if name, ok := officialSystemUserDisplayName.Load().(string); ok && name != "" {
		return name
	}
	return branding.ProductName()
}

// OfficialSystemUser returns the official system account built in for phase one.
func OfficialSystemUser() User {
	return User{
		ID:         OfficialSystemUserID,
		AccessHash: 6599886787491911851,
		Phone:      OfficialSystemPhone,
		FirstName:  officialSystemDisplayName(),
		Username:   branding.ProductUsername(),
		Verified:   true,
		Support:    true,
	}
}

// ChatBotDescription and StickersBotDescription are the shared branded seed
// text for both user.about and bots.description. PostgreSQL migrations contain
// only the default snapshot; startup reconciliation and the memory backend use
// these helpers so custom deployments do not expose stale "telesrv" text.
func ChatBotDescription() string {
	return "Chat with the configured " + branding.ProductName() + " AI provider."
}

func StickersBotDescription() string {
	return "Create custom sticker and emoji packs for " + branding.ProductName() + "."
}

// BotFatherUser returns the built-in BotFather account. Its username not ending in bot is a seed exception (matching the official behavior).
func BotFatherUser() User {
	return User{
		ID:             BotFatherUserID,
		AccessHash:     BotFatherAccessHash,
		FirstName:      "BotFather",
		Username:       "BotFather",
		Verified:       true,
		Bot:            true,
		BotInfoVersion: 1,
	}
}

// StickersBotUser returns the built-in @Stickers account. Its username not ending in bot is a seed exception (matching the official behavior).
func StickersBotUser() User {
	return User{
		ID:             StickersBotUserID,
		AccessHash:     StickersBotAccessHash,
		FirstName:      "Stickers",
		Username:       "Stickers",
		About:          StickersBotDescription(),
		Verified:       true,
		Bot:            true,
		BotInfoVersion: 2,
	}
}

// ChatBotUser returns the built-in @ChatBot account.
func ChatBotUser() User {
	return User{
		ID:             ChatBotUserID,
		AccessHash:     ChatBotAccessHash,
		FirstName:      "ChatBot",
		Username:       "ChatBot",
		About:          ChatBotDescription(),
		Verified:       true,
		Bot:            true,
		BotInfoVersion: 1,
	}
}

// VerifyBotUser returns the built-in @verifybot account. It is verified itself,
// so the applicant sees the same badge on the account that grants it.
func VerifyBotUser() User {
	return User{
		ID:             VerifyBotUserID,
		AccessHash:     VerifyBotAccessHash,
		FirstName:      "Verify Bot",
		Username:       "verifybot",
		Verified:       true,
		Bot:            true,
		BotInfoVersion: 1,
	}
}

// VerifierBotUser returns the built-in @verifierbot account.
//
// Verified is false on purpose: the official checkmark is the platform's own
// mechanism, and a third-party verifier wearing it would blur exactly the
// distinction this bot has to explain to every applicant. What makes the account a
// verifier is the operator-granted BotVerifierSettings row, not this seed.
func VerifierBotUser() User {
	return User{
		ID:             VerifierBotUserID,
		AccessHash:     VerifierBotAccessHash,
		FirstName:      "Verifier Bot",
		Username:       "verifierbot",
		Verified:       false,
		Bot:            true,
		BotInfoVersion: 1,
	}
}

// PremiumBotUser returns the built-in Premium storefront and payment peer.
func PremiumBotUser() User {
	return User{
		ID:             PremiumBotConfiguredUserID(),
		AccessHash:     PremiumBotAccessHash,
		FirstName:      "Premium Bot",
		Username:       PremiumBotConfiguredUsername(),
		Verified:       true,
		Bot:            true,
		BotInfoVersion: 1,
	}
}

// GifBotUser returns the built-in inline-only @gif catalog bot.
func GifBotUser() User {
	return User{
		ID:             GifBotUserID,
		AccessHash:     GifBotAccessHash,
		FirstName:      "GIF",
		Username:       "gif",
		Verified:       true,
		Bot:            true,
		BotInfoVersion: 1,
	}
}

func GiftRelayerUser() User {
	return User{
		ID: GiftRelayerUserID, AccessHash: GiftRelayerAccessHash,
		FirstName: "InvGram Gift Relayer", Username: "relayer", Verified: true,
	}
}

func GiftClaimBotUser() User {
	return User{
		ID: GiftClaimBotUserID, AccessHash: GiftClaimBotAccessHash,
		FirstName: "InvGram Gift Claim", Username: "claim", Verified: true,
		Bot: true, BotInfoVersion: 1,
	}
}

func GiftClaimTestBotUser() User {
	return User{
		ID: GiftClaimTestBotUserID, AccessHash: GiftClaimTestBotAccessHash,
		FirstName: "InvGram Gift Claim (test)", Username: "claimtest", Verified: true,
		Bot: true, BotInfoVersion: 1,
	}
}

// SystemUserByID returns a built-in system account; ok=false for a non-system account.
// Every hard-coded injection point for 777000 goes through this function, so adding a built-in account only touches here.
func SystemUserByID(id int64) (User, bool) {
	if id == PremiumBotConfiguredUserID() {
		return PremiumBotUser(), true
	}
	switch id {
	case OfficialSystemUserID:
		return OfficialSystemUser(), true
	case BotFatherUserID:
		return BotFatherUser(), true
	case StickersBotUserID:
		return StickersBotUser(), true
	case ChatBotUserID:
		return ChatBotUser(), true
	case VerifyBotUserID:
		return VerifyBotUser(), true
	case VerifierBotUserID:
		return VerifierBotUser(), true
	case GifBotUserID:
		return GifBotUser(), true
	case GiftRelayerUserID:
		return GiftRelayerUser(), true
	case GiftClaimBotUserID:
		return GiftClaimBotUser(), true
	case GiftClaimTestBotUserID:
		return GiftClaimTestBotUser(), true
	}
	return User{}, false
}

func IsSystemUserID(id int64) bool {
	_, ok := SystemUserByID(id)
	return ok
}

// SystemUserIDs returns every built-in account id, in a stable order.
//
// It is the one list to extend when a service account is added, so a caller that
// has to enumerate them -- a SQL predicate excluding infrastructure, say -- cannot
// silently miss one the way an inline literal would.
func SystemUserIDs() []int64 {
	return []int64{
		OfficialSystemUserID,
		BotFatherUserID,
		StickersBotUserID,
		ChatBotUserID,
		VerifyBotUserID,
		VerifierBotUserID,
		PremiumBotConfiguredUserID(),
		GifBotUserID,
		GiftRelayerUserID,
		GiftClaimBotUserID,
		GiftClaimTestBotUserID,
	}
}

// SystemUserByUsername resolves reserved short handles such as @gif that do
// not satisfy the ordinary 4/5-character account creation rules.
func SystemUserByUsername(username string) (User, bool) {
	username = strings.TrimPrefix(strings.TrimSpace(username), "@")
	for _, id := range SystemUserIDs() {
		u, ok := SystemUserByID(id)
		if ok && strings.EqualFold(u.Username, username) {
			return u, true
		}
	}
	return User{}, false
}

func SystemUserByPhone(phone string) (User, bool) {
	phone = NormalizePhone(phone)
	for _, id := range SystemUserIDs() {
		u, ok := SystemUserByID(id)
		if !ok || u.Phone == "" {
			continue
		}
		if NormalizePhone(u.Phone) == phone {
			return u, true
		}
	}
	return User{}, false
}