chore: refresh gramsrv public release
This commit is contained in:
parent
75cebe8dbf
commit
70b6820474
1274 changed files with 378751 additions and 59919 deletions
308
internal/rpc/convert_users.go
Normal file
308
internal/rpc/convert_users.go
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/gotd/td/tg"
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
// tgSelfUser 把 domain.User 转为 self 标记的 tg.User(optional 字段由 Encode 自动 SetFlags)。
|
||||
func tgSelfUser(u domain.User) *tg.User {
|
||||
out := &tg.User{
|
||||
ID: u.ID,
|
||||
AccessHash: u.AccessHash,
|
||||
FirstName: u.FirstName,
|
||||
LastName: u.LastName,
|
||||
Username: u.Username,
|
||||
Phone: u.Phone,
|
||||
Self: true,
|
||||
Verified: u.Verified,
|
||||
Support: u.Support,
|
||||
Contact: u.Contact,
|
||||
MutualContact: u.Mutual,
|
||||
CloseFriend: u.CloseFriend,
|
||||
Usernames: tgUsernames(u.Username),
|
||||
}
|
||||
applyTgUserBotFields(out, u)
|
||||
applyTgUserPremiumFields(out, u)
|
||||
applyTgUserColorFields(out, u)
|
||||
if photo := tgUserProfilePhoto(u); photo != nil {
|
||||
out.Photo = photo
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func tgUser(u domain.User) *tg.User {
|
||||
out := &tg.User{
|
||||
ID: u.ID,
|
||||
AccessHash: u.AccessHash,
|
||||
FirstName: u.FirstName,
|
||||
LastName: u.LastName,
|
||||
Username: u.Username,
|
||||
Phone: u.Phone,
|
||||
Verified: u.Verified,
|
||||
Support: u.Support,
|
||||
Contact: u.Contact,
|
||||
MutualContact: u.Mutual,
|
||||
CloseFriend: u.CloseFriend,
|
||||
Usernames: tgUsernames(u.Username),
|
||||
}
|
||||
applyTgUserBotFields(out, u)
|
||||
applyTgUserPremiumFields(out, u)
|
||||
applyTgUserColorFields(out, u)
|
||||
if photo := tgUserProfilePhoto(u); photo != nil {
|
||||
out.Photo = photo
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// applyTgUserPremiumFields 由到期时间即时派生 premium flag(bit28,独立位)与
|
||||
// emoji status。判断用真实时钟:premium 的权威来源是 premium_expires_at 本身,
|
||||
// 到期即停发,正确性不依赖后台 sweeper(它只负责清理与 updateUser 通知);
|
||||
// bot 永不带 premium(PremiumActiveAt 内排除)。
|
||||
func applyTgUserPremiumFields(out *tg.User, u domain.User) {
|
||||
now := time.Now().Unix()
|
||||
if !u.PremiumActiveAt(now) {
|
||||
return
|
||||
}
|
||||
out.Premium = true
|
||||
if u.EmojiStatusActiveAt(now) {
|
||||
out.SetEmojiStatus(tgUserEmojiStatus(u, now))
|
||||
}
|
||||
}
|
||||
|
||||
// tgUserEmojiStatus 把用户 emoji status 转为 TL:未设置/已失效返回
|
||||
// emojiStatusEmpty(updateUserEmojiStatus 清除语义用)。user TL 内联输出仍由
|
||||
// applyTgUserPremiumFields 控制(无状态时直接省略字段,对齐官方 user 编码)。
|
||||
func tgUserEmojiStatus(u domain.User, now int64) tg.EmojiStatusClass {
|
||||
if !u.EmojiStatusActiveAt(now) {
|
||||
return &tg.EmojiStatusEmpty{}
|
||||
}
|
||||
status := &tg.EmojiStatus{DocumentID: u.EmojiStatusDocumentID}
|
||||
if u.EmojiStatusUntil > 0 {
|
||||
status.SetUntil(u.EmojiStatusUntil)
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func applyTgUserColorFields(out *tg.User, u domain.User) {
|
||||
if color := tgUserPeerColor(u.Color); color != nil {
|
||||
out.SetColor(color)
|
||||
}
|
||||
if color := tgUserPeerColor(u.ProfileColor); color != nil {
|
||||
out.SetProfileColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
func tgUserPeerColor(color domain.PeerColor) tg.PeerColorClass {
|
||||
if color.Empty() {
|
||||
return nil
|
||||
}
|
||||
out := &tg.PeerColor{}
|
||||
if color.HasColor {
|
||||
out.SetColor(color.Color)
|
||||
}
|
||||
if color.BackgroundEmojiID != 0 {
|
||||
out.SetBackgroundEmojiID(color.BackgroundEmojiID)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// applyTgUserBotFields 应用 bot/普通用户的互斥输出:
|
||||
// - bot:必须同时置 bot flag 与 bot_info_version(Layer 225 两者共用 flags
|
||||
// bit14,TDesktop 只认 bot_info_version 字段是否存在);无 phone、无 status
|
||||
// (客户端显示 "bot" 而非 last seen)。
|
||||
// - 普通用户:照常输出 presence status。
|
||||
func applyTgUserBotFields(out *tg.User, u domain.User) {
|
||||
if !u.Bot {
|
||||
out.Status = tgUserStatus(u.Status)
|
||||
return
|
||||
}
|
||||
out.Bot = true
|
||||
version := u.BotInfoVersion
|
||||
if version < 1 {
|
||||
version = 1
|
||||
}
|
||||
out.SetBotInfoVersion(version)
|
||||
if u.ID != domain.BotFatherUserID {
|
||||
out.SetBotBusiness(true)
|
||||
}
|
||||
out.Phone = ""
|
||||
}
|
||||
|
||||
// isSystemUserID 报告 id 是否为内置系统账号(777000 / BotFather)。
|
||||
func isSystemUserID(id int64) bool {
|
||||
_, ok := domain.SystemUserByID(id)
|
||||
return ok
|
||||
}
|
||||
|
||||
// tgUserProfilePhoto 由 domain.User 反范式头像字段构造 UserProfilePhoto;无头像返回 nil(Encode 时为 empty)。
|
||||
func tgUserProfilePhoto(u domain.User) tg.UserProfilePhotoClass {
|
||||
if u.PhotoID == 0 {
|
||||
return nil
|
||||
}
|
||||
photo := &tg.UserProfilePhoto{PhotoID: u.PhotoID, DCID: u.PhotoDCID, Personal: u.PhotoPersonal}
|
||||
if u.PhotoHasVideo {
|
||||
photo.SetHasVideo(true)
|
||||
}
|
||||
if len(u.PhotoStripped) > 0 {
|
||||
photo.SetStrippedThumb(u.PhotoStripped)
|
||||
}
|
||||
return photo
|
||||
}
|
||||
|
||||
func tgUserStatus(status domain.UserStatus) tg.UserStatusClass {
|
||||
switch status.Kind {
|
||||
case domain.UserStatusOnline:
|
||||
if status.Expires > 0 {
|
||||
return &tg.UserStatusOnline{Expires: status.Expires}
|
||||
}
|
||||
case domain.UserStatusOffline:
|
||||
if status.WasOnline > 0 {
|
||||
return &tg.UserStatusOffline{WasOnline: status.WasOnline}
|
||||
}
|
||||
case domain.UserStatusLastWeek:
|
||||
return &tg.UserStatusLastWeek{}
|
||||
case domain.UserStatusLastMonth:
|
||||
return &tg.UserStatusLastMonth{}
|
||||
case domain.UserStatusEmpty:
|
||||
return &tg.UserStatusEmpty{}
|
||||
case domain.UserStatusRecently, domain.UserStatusUnknown:
|
||||
return &tg.UserStatusRecently{}
|
||||
}
|
||||
return &tg.UserStatusRecently{}
|
||||
}
|
||||
|
||||
func tgUsernames(username string) []tg.Username {
|
||||
if username == "" {
|
||||
return nil
|
||||
}
|
||||
return []tg.Username{{Editable: true, Active: true, Username: username}}
|
||||
}
|
||||
|
||||
func tgContacts(list domain.ContactList) tg.ContactsContactsClass {
|
||||
out := &tg.ContactsContacts{
|
||||
Contacts: make([]tg.Contact, 0, len(list.Contacts)),
|
||||
Users: make([]tg.UserClass, 0, len(list.Contacts)),
|
||||
SavedCount: len(list.Contacts),
|
||||
}
|
||||
for _, c := range list.Contacts {
|
||||
out.Contacts = append(out.Contacts, tg.Contact{UserID: c.User.ID, Mutual: c.Mutual})
|
||||
out.Users = append(out.Users, tgUser(c.User))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func tgContactsFound(viewerUserID int64, res domain.UserSearchResult) *tg.ContactsFound {
|
||||
out := &tg.ContactsFound{
|
||||
MyResults: make([]tg.PeerClass, 0, len(res.MyResults)+len(res.MyChannelResults)),
|
||||
Results: make([]tg.PeerClass, 0, len(res.Results)+len(res.ChannelResults)),
|
||||
Chats: make([]tg.ChatClass, 0, len(res.MyChannelResults)+len(res.ChannelResults)),
|
||||
Users: make([]tg.UserClass, 0, len(res.MyResults)+len(res.Results)),
|
||||
}
|
||||
seen := make(map[int64]struct{}, len(res.MyResults)+len(res.Results))
|
||||
appendUser := func(u domain.User) {
|
||||
if u.ID == 0 {
|
||||
return
|
||||
}
|
||||
if _, ok := seen[u.ID]; ok {
|
||||
return
|
||||
}
|
||||
seen[u.ID] = struct{}{}
|
||||
out.Users = append(out.Users, tgUser(u))
|
||||
}
|
||||
seenChannels := make(map[int64]struct{}, len(res.MyChannelResults)+len(res.ChannelResults))
|
||||
appendChannel := func(ch domain.Channel, self *domain.ChannelMember) {
|
||||
if ch.ID == 0 {
|
||||
return
|
||||
}
|
||||
if _, ok := seenChannels[ch.ID]; ok {
|
||||
return
|
||||
}
|
||||
seenChannels[ch.ID] = struct{}{}
|
||||
out.Chats = append(out.Chats, tgChannelChat(viewerUserID, ch, self))
|
||||
}
|
||||
for _, u := range res.MyResults {
|
||||
out.MyResults = append(out.MyResults, &tg.PeerUser{UserID: u.ID})
|
||||
appendUser(u)
|
||||
}
|
||||
for _, ch := range res.MyChannelResults {
|
||||
out.MyResults = append(out.MyResults, &tg.PeerChannel{ChannelID: ch.ID})
|
||||
appendChannel(ch, nil)
|
||||
}
|
||||
for _, u := range res.Results {
|
||||
out.Results = append(out.Results, &tg.PeerUser{UserID: u.ID})
|
||||
appendUser(u)
|
||||
}
|
||||
for _, ch := range res.ChannelResults {
|
||||
out.Results = append(out.Results, &tg.PeerChannel{ChannelID: ch.ID})
|
||||
appendChannel(ch, &domain.ChannelMember{ChannelID: ch.ID, UserID: viewerUserID, Status: domain.ChannelMemberLeft})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func tgUsers(users []domain.User) []tg.UserClass {
|
||||
out := make([]tg.UserClass, 0, len(users))
|
||||
for _, u := range users {
|
||||
out = append(out, tgUser(u))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func tgUsersForViewer(viewerUserID int64, users []domain.User) []tg.UserClass {
|
||||
out := make([]tg.UserClass, 0, len(users))
|
||||
for _, u := range users {
|
||||
if viewerUserID != 0 && u.ID == viewerUserID {
|
||||
out = append(out, tgSelfUser(u))
|
||||
continue
|
||||
}
|
||||
out = append(out, tgUser(u))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func addUsers(out *tg.UpdatesDifference, seen map[int64]struct{}, viewerUserID int64, users []domain.User) {
|
||||
for _, u := range users {
|
||||
if u.ID == 0 {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[u.ID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[u.ID] = struct{}{}
|
||||
// difference 里自己的 user 必须带 self 标志:客户端会用该对象
|
||||
// 持久覆盖当前账号资料,self=false 会破坏 Saved Messages 语义。
|
||||
if viewerUserID != 0 && u.ID == viewerUserID {
|
||||
out.Users = append(out.Users, tgSelfUser(u))
|
||||
continue
|
||||
}
|
||||
out.Users = append(out.Users, tgUser(u))
|
||||
}
|
||||
}
|
||||
|
||||
func addMessageUsers(out *tg.UpdatesDifference, seen map[int64]struct{}, msg domain.Message) {
|
||||
for _, peer := range []domain.Peer{msg.From, msg.Peer, domain.Peer{Type: domain.PeerTypeUser, ID: msg.ViaBotID}} {
|
||||
if peer.Type != domain.PeerTypeUser {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[peer.ID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[peer.ID] = struct{}{}
|
||||
if u, ok := domain.SystemUserByID(peer.ID); ok {
|
||||
out.Users = append(out.Users, tgUser(u))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func tgChannelEmojiStatus(status domain.ChannelEmojiStatus) tg.EmojiStatusClass {
|
||||
if status.Empty() {
|
||||
return nil
|
||||
}
|
||||
out := &tg.EmojiStatus{DocumentID: status.DocumentID}
|
||||
if status.Until > 0 {
|
||||
out.SetUntil(status.Until)
|
||||
}
|
||||
return out
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue