owpengram-server/internal/domain/system.go
2026-09-09 02:49:30 +03:00

426 lines
16 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package domain
import (
"strings"
"telesrv/internal/branding"
)
const (
// OfficialSystemUserID 是 Telegram 兼容客户端识别的官方系统账号。
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"
// OfficialSystemUserPhotoID/AccessHash 是该账号头像 photo 的固定 id
// 与 files.Service.SeedOfficialSystemAvatar 种子写入的行保持一致,
// 确保跨重启后 OfficialSystemUser() 引用的 photo id 稳定不变。
OfficialSystemUserPhotoID int64 = 7770000001
OfficialSystemUserPhotoAccessHash int64 = 5837219004471160321
// BotFatherUserID 是内置 BotFather 账号,与官方 @BotFather 同 ID。
BotFatherUserID int64 = 93372553
// BotFatherAccessHash 固定不变;与迁移 0090 的种子行双写,必须保持一致。
BotFatherAccessHash int64 = 7421896403922962293
// BotFatherUserPhotoID/AccessHash 是 BotFather 头像 photo 的固定 id
// 与 files.Service.SeedBotFatherAvatar 种子写入的行保持一致。
BotFatherUserPhotoID int64 = 933725530001
BotFatherUserPhotoAccessHash int64 = 3198475620194837201
// StickersBotUserID 是内置 @Stickers 账号。它是 server 内置 service bot
// 不走外部 Bot API 进程。
StickersBotUserID int64 = 1063110917
// StickersBotAccessHash 固定不变;与 postgres 种子行双写,必须保持一致。
StickersBotAccessHash int64 = 5213187021149032991
// StickersBotUserPhotoID/AccessHash 是 @Stickers 头像 photo 的固定 id
// 与 files.Service.SeedStickersBotAvatar 种子写入的行保持一致。
StickersBotUserPhotoID int64 = 10631109170001
StickersBotUserPhotoAccessHash int64 = 4636293356791048892
// ChatBotUserID 是内置 @ChatBot 账号。它把私聊文本转给 server AI provider 链。
ChatBotUserID int64 = 1250000007
// ChatBotAccessHash 固定不变;与 postgres 种子行双写,必须保持一致。
ChatBotAccessHash int64 = 6332902371644871201
// ChatBotUserPhotoID/AccessHash 是 @ChatBot 头像 photo 的固定 id
// 与 files.Service.SeedChatBotAvatar 种子写入的行保持一致。
ChatBotUserPhotoID int64 = 12500000070001
ChatBotUserPhotoAccessHash int64 = 8748578814399338333
// 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
// VerifyBotUserPhotoID/AccessHash are the fixed id of @verifybot's avatar
// photo, matching the row files.Service.SeedVerifyBotAvatar seeds.
VerifyBotUserPhotoID int64 = 12500000110001
VerifyBotUserPhotoAccessHash int64 = 975869468725402752
// VerifierBotUserID is the built-in @marksbot: 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. Named "Marks Bot"
// rather than anything containing "Verif*" specifically so it can never be
// misread as a second copy of @verifybot -- the two front doors must stay
// visually distinct at a glance, not just distinct in the underlying mechanism.
VerifierBotUserID int64 = 1250000013
// VerifierBotAccessHash is fixed and double-written with the seed row in
// migration 0156; the two must never drift.
VerifierBotAccessHash int64 = 6913402578811563729
// VerifierBotDefaultIconDocumentID is the custom-emoji document (a ✅ from the
// default "Topics" emoji set, data/sticker-seed/telegram_emoji_export) reused
// as @marksbot's out-of-the-box icon, so the reference verifier has something
// to grant immediately after first boot instead of an empty catalogue. It is
// bundled media that the ordinary sticker-seed import already writes on every
// startup -- not a document minted specifically for this feature -- so it
// resolves the same way any other seeded custom emoji does.
VerifierBotDefaultIconDocumentID int64 = 5237699328843200968
// GifBotUserID is the built-in @gif inline bot: it answers
// messages.getInlineBotResults synchronously (no MTProto session, no Bot API
// process -- see rpc.ServiceBotInlineResults) with the admin-curated GIF
// catalog, the same role Telegram's own @gif plays for the client's GIF
// picker "trending"/search panel. The id is reserved and stable, so a
// restart never re-creates the account under a different identity.
GifBotUserID int64 = 1250000015
// GifBotAccessHash is fixed and double-written with the seed row in this
// feature's migration; the two must never drift.
GifBotAccessHash int64 = 7233282977235616768
)
// officialSystemUserPhotoDCID/Stripped 由 files.Service.SeedOfficialSystemAvatar
// 在启动时通过 SetOfficialSystemUserAvatar 写入一次;写入前 OfficialSystemUser()
// 不带头像PhotoID==0与其它未设置头像的账号行为一致。
var (
officialSystemUserPhotoDCID int
officialSystemUserPhotoStripped []byte
)
// SetOfficialSystemUserAvatar 记录官方系统账号头像所在的 DC 与内联缩略图字节。
// 只应在启动阶段、头像 seed 完成后调用一次。
func SetOfficialSystemUserAvatar(dcID int, stripped []byte) {
officialSystemUserPhotoDCID = dcID
officialSystemUserPhotoStripped = stripped
}
// officialSystemUserDisplayName overrides OfficialSystemUser's FirstName --
// empty means "use branding.ProductName()" (the compile-time default), set
// once at startup from the operator's Server Settings -> Server identity
// name, if any. Deliberately only the display name, not Username: the
// account's @username is a stable, addressable identifier other things may
// already reference, unlike the display name shown in chat headers.
var officialSystemUserDisplayName string
// SetOfficialSystemUserDisplayName records the operator's custom server
// name for the official system account (777000), read once at startup from
// Server Settings -> Server identity. Pass "" to fall back to
// branding.ProductName() -- the same "unset -> default" contract the avatar
// override above uses.
func SetOfficialSystemUserDisplayName(name string) {
officialSystemUserDisplayName = strings.TrimSpace(name)
}
// officialSystemDisplayName returns the official system account's current
// effective display name: the operator's custom override if set via
// SetOfficialSystemUserDisplayName, else branding.ProductName(). Shared by
// OfficialSystemUser (777000's FirstName) and the login-welcome-message
// {{server_name}} placeholder (see login_welcome_template.go) so both stay
// consistent with each other.
func officialSystemDisplayName() string {
if officialSystemUserDisplayName != "" {
return officialSystemUserDisplayName
}
return branding.ProductName()
}
// botFatherPhotoDCID/Stripped 由 files.Service.SeedBotFatherAvatar 在启动时
// 通过 SetBotFatherAvatar 写入一次;写入前 BotFatherUser() 不带头像PhotoID==0
var (
botFatherPhotoDCID int
botFatherPhotoStripped []byte
)
// SetBotFatherAvatar 记录 BotFather 头像所在的 DC 与内联缩略图字节。
// 只应在启动阶段、头像 seed 完成后调用一次。
func SetBotFatherAvatar(dcID int, stripped []byte) {
botFatherPhotoDCID = dcID
botFatherPhotoStripped = stripped
}
// stickersBotPhotoDCID/Stripped 由 files.Service.SeedStickersBotAvatar 在启动时
// 通过 SetStickersBotAvatar 写入一次;写入前 StickersBotUser() 不带头像PhotoID==0
var (
stickersBotPhotoDCID int
stickersBotPhotoStripped []byte
)
// SetStickersBotAvatar 记录 @Stickers 头像所在的 DC 与内联缩略图字节。
// 只应在启动阶段、头像 seed 完成后调用一次。
func SetStickersBotAvatar(dcID int, stripped []byte) {
stickersBotPhotoDCID = dcID
stickersBotPhotoStripped = stripped
}
// chatBotPhotoDCID/Stripped 由 files.Service.SeedChatBotAvatar 在启动时
// 通过 SetChatBotAvatar 写入一次;写入前 ChatBotUser() 不带头像PhotoID==0
var (
chatBotPhotoDCID int
chatBotPhotoStripped []byte
)
// SetChatBotAvatar 记录 @ChatBot 头像所在的 DC 与内联缩略图字节。
// 只应在启动阶段、头像 seed 完成后调用一次。
func SetChatBotAvatar(dcID int, stripped []byte) {
chatBotPhotoDCID = dcID
chatBotPhotoStripped = stripped
}
// verifyBotPhotoDCID/Stripped are written once at startup by
// files.Service.SeedVerifyBotAvatar via SetVerifyBotAvatar; before that,
// VerifyBotUser() carries no photo (PhotoID==0), same as every other
// not-yet-seeded built-in account.
var (
verifyBotPhotoDCID int
verifyBotPhotoStripped []byte
)
// SetVerifyBotAvatar records the DC and inline thumbnail bytes for
// @verifybot's avatar. Should only be called once, at startup, after the
// avatar seed completes.
func SetVerifyBotAvatar(dcID int, stripped []byte) {
verifyBotPhotoDCID = dcID
verifyBotPhotoStripped = stripped
}
// OfficialSystemUser 返回第一阶段内置的官方系统账号。
// No Username: the real Telegram service account (777000) isn't
// @-addressable either, and reserving one here would need it re-blocked in
// config.ReservedUsernames (which it now is, by default, precisely because
// nothing keeps another account from claiming it once this one has none).
func OfficialSystemUser() User {
u := User{
ID: OfficialSystemUserID,
AccessHash: 6599886787491911851,
Phone: "42777",
FirstName: officialSystemDisplayName(),
Verified: true,
Support: true,
}
if officialSystemUserPhotoDCID != 0 {
u.PhotoID = OfficialSystemUserPhotoID
u.PhotoDCID = officialSystemUserPhotoDCID
u.PhotoStripped = officialSystemUserPhotoStripped
}
return u
}
// 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 返回内置 BotFather 账号。username 不以 bot 结尾属种子例外(与官方一致)。
func BotFatherUser() User {
u := User{
ID: BotFatherUserID,
AccessHash: BotFatherAccessHash,
FirstName: "BotFather",
Username: "BotFather",
Verified: true,
Bot: true,
BotInfoVersion: 1,
}
if botFatherPhotoDCID != 0 {
u.PhotoID = BotFatherUserPhotoID
u.PhotoDCID = botFatherPhotoDCID
u.PhotoStripped = botFatherPhotoStripped
}
return u
}
// StickersBotUser 返回内置 @Stickers 账号。username 不以 bot 结尾属种子例外(与官方一致)。
func StickersBotUser() User {
u := User{
ID: StickersBotUserID,
AccessHash: StickersBotAccessHash,
FirstName: "Stickers",
Username: "Stickers",
About: StickersBotDescription(),
Verified: true,
Bot: true,
BotInfoVersion: 2,
}
if stickersBotPhotoDCID != 0 {
u.PhotoID = StickersBotUserPhotoID
u.PhotoDCID = stickersBotPhotoDCID
u.PhotoStripped = stickersBotPhotoStripped
}
return u
}
// ChatBotUser 返回内置 @ChatBot 账号。
func ChatBotUser() User {
u := User{
ID: ChatBotUserID,
AccessHash: ChatBotAccessHash,
FirstName: "ChatBot",
Username: "ChatBot",
About: ChatBotDescription(),
Verified: true,
Bot: true,
BotInfoVersion: 1,
}
if chatBotPhotoDCID != 0 {
u.PhotoID = ChatBotUserPhotoID
u.PhotoDCID = chatBotPhotoDCID
u.PhotoStripped = chatBotPhotoStripped
}
return u
}
// 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 {
u := User{
ID: VerifyBotUserID,
AccessHash: VerifyBotAccessHash,
FirstName: "Verify Bot",
Username: "verifybot",
Verified: true,
Bot: true,
BotInfoVersion: 1,
}
if verifyBotPhotoDCID != 0 {
u.PhotoID = VerifyBotUserPhotoID
u.PhotoDCID = verifyBotPhotoDCID
u.PhotoStripped = verifyBotPhotoStripped
}
return u
}
// VerifierBotUser returns the built-in @marksbot account.
//
// Verified is true: this deployment carries the platform checkmark on its own
// service bots (see e.g. VerifyBotUser, BotFatherUser), and @marksbot is one of
// them -- a legitimate first-party account, just one that happens to also grant
// a *different*, third-party mark to other peers. The checkmark here says
// "this account is who it claims to be", not "this account's grants are
// official"; that distinction is what @marksbot's own messages explain to every
// applicant, and it does not depend on this bot's own badge being off. What
// makes the account a verifier at all is the operator-granted
// BotVerifierSettings row, not this seed -- the two remain fully independent.
func VerifierBotUser() User {
return User{
ID: VerifierBotUserID,
AccessHash: VerifierBotAccessHash,
FirstName: "Marks Bot",
Username: "marksbot",
Verified: true,
Bot: true,
BotInfoVersion: 1,
}
}
// GifBotUser returns the built-in @gif account.
func GifBotUser() User {
return User{
ID: GifBotUserID,
AccessHash: GifBotAccessHash,
FirstName: "GIFs",
Username: "gif",
Verified: true,
Bot: true,
BotInfoVersion: 1,
}
}
// SystemUserByID 返回内置系统账号;非系统账号返回 ok=false。
// 所有对 777000 的硬编码注入点统一经此函数,新增内置账号只改这里。
func SystemUserByID(id int64) (User, bool) {
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
}
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,
GifBotUserID,
}
}
// SystemUserByUsername resolves a case-insensitive exact username match
// against every built-in account, e.g. for username-lookup paths that
// otherwise enforce a minimum length shorter than some reserved system
// handles need (@gif is 3 characters -- shorter than
// MinCollectibleUsernameLength's 4-character floor for an ordinary lookup).
func SystemUserByUsername(username string) (User, bool) {
username = NormalizeUsername(username)
if username == "" {
return User{}, false
}
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
}