owpengram-server/internal/rpc/convert_dialogs.go

339 lines
9.9 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 rpc
import (
"github.com/iamxvbaba/td/tg"
"telesrv/internal/domain"
)
func tgMessagesDialogs(viewerUserID int64, list domain.DialogList) tg.MessagesDialogsClass {
dialogs := make([]tg.DialogClass, 0, len(list.Dialogs)+1)
// dialogFolder 条目排在最前TDesktop 据它发现 archive folder 并渲染
// 归档行的未读徽章Folder::applyDialog / MainList::updateCloudUnread
if folder := tgDialogFolder(list.ArchiveSummary); folder != nil {
dialogs = append(dialogs, folder)
}
for _, d := range list.Dialogs {
if dialog := tgDialog(d); dialog != nil {
dialogs = append(dialogs, dialog)
}
}
messages := make([]tg.MessageClass, 0, len(list.Messages))
for _, msg := range list.Messages {
if item := tgMessage(msg); item != nil {
messages = append(messages, item)
}
}
for _, msg := range list.ChannelMessages {
if item := tgChannelMessage(viewerUserID, msg); item != nil {
messages = append(messages, item)
}
}
users := tgUsersForViewer(viewerUserID, list.Users)
chats := tgChannelsForDialogs(viewerUserID, list.Channels, list.Dialogs)
if list.Count > len(dialogs) {
return &tg.MessagesDialogsSlice{
Count: list.Count,
Dialogs: dialogs,
Messages: messages,
Chats: chats,
Users: users,
}
}
return &tg.MessagesDialogs{
Dialogs: dialogs,
Messages: messages,
Chats: chats,
Users: users,
}
}
func tgPeerDialogs(viewerUserID int64, list domain.DialogList, st domain.UpdateState) *tg.MessagesPeerDialogs {
out := &tg.MessagesPeerDialogs{
Dialogs: make([]tg.DialogClass, 0, len(list.Dialogs)+1),
Messages: make([]tg.MessageClass, 0, len(list.Messages)+len(list.ChannelMessages)),
Users: make([]tg.UserClass, 0, len(list.Users)),
Chats: make([]tg.ChatClass, 0, len(list.Channels)),
State: tgUpdateState(st),
}
// getPinnedDialogs(folder_id=0) 必须带 dialogFolder 条目DrKLO 主列表
// getDialogs 一律 exclude_pinnedarchive 行只能从 pinned 响应发现
// fetchFolderInLoadedPinnedDialogs且要求 top_message/peer 非零)。
if folder := tgDialogFolder(list.ArchiveSummary); folder != nil {
out.Dialogs = append(out.Dialogs, folder)
}
for _, d := range list.Dialogs {
if dialog := tgDialog(d); dialog != nil {
out.Dialogs = append(out.Dialogs, dialog)
}
}
for _, msg := range list.Messages {
if item := tgMessage(msg); item != nil {
out.Messages = append(out.Messages, item)
}
}
for _, msg := range list.ChannelMessages {
if item := tgChannelMessage(viewerUserID, msg); item != nil {
out.Messages = append(out.Messages, item)
}
}
for _, u := range list.Users {
if viewerUserID != 0 && u.ID == viewerUserID {
out.Users = append(out.Users, tgSelfUser(u))
} else {
out.Users = append(out.Users, tgUser(u))
}
}
out.Chats = append(out.Chats, tgChannelsForDialogs(viewerUserID, list.Channels, list.Dialogs)...)
return out
}
// tgDialogFolder 把归档摘要转成 dialogFolder#71bd134c 条目。当前未接
// per-peer mute 状态,未读计数全部归入 unmuted 桶TDesktop 用 unmuted
// 桶渲染亮色徽章muted 桶渲染灰色,全归 unmuted 只影响徽章颜色不丢计数)。
func tgDialogFolder(summary *domain.DialogArchiveSummary) tg.DialogClass {
if summary == nil {
return nil
}
peer := tgPeer(summary.TopPeer)
if peer == nil {
return nil
}
return &tg.DialogFolder{
Pinned: summary.Pinned,
Folder: tg.Folder{
ID: domain.DialogArchiveFolderID,
Title: "Archived Chats",
},
Peer: peer,
TopMessage: summary.TopMessage,
UnreadUnmutedPeersCount: summary.UnreadPeersCount,
UnreadUnmutedMessagesCount: summary.UnreadMessagesCount,
}
}
func tgDialog(d domain.Dialog) *tg.Dialog {
peer := tgPeer(d.Peer)
if peer == nil {
return nil
}
out := &tg.Dialog{
Pinned: d.Pinned,
UnreadMark: d.UnreadMark,
ViewForumAsMessages: d.ViewForumAsMessages,
Peer: peer,
TopMessage: d.TopMessage,
ReadInboxMaxID: d.ReadInboxMaxID,
ReadOutboxMaxID: d.ReadOutboxMaxID,
UnreadCount: d.UnreadCount,
UnreadMentionsCount: d.UnreadMentions,
UnreadReactionsCount: d.UnreadReactions,
NotifySettings: *tgPeerNotifySettings(d.NotifySettings),
}
if d.FolderID != domain.DialogMainFolderID {
out.SetFolderID(d.FolderID)
}
if d.TTLPeriod > 0 {
out.SetTTLPeriod(d.TTLPeriod)
}
if d.Peer.Type == domain.PeerTypeChannel && d.Pts > 0 {
// 客户端用 dialog.pts 初始化 channel 本地序列;缺失会让
// getChannelDifference 起点失效(冷启动 gap 不被发现)。
out.SetPts(d.Pts)
}
if d.Draft != nil {
out.SetDraft(tgDialogDraft(*d.Draft))
}
return out
}
func tgDialogDraft(d domain.DialogDraft) tg.DraftMessageClass {
if d.Empty() {
out := &tg.DraftMessageEmpty{}
out.SetDate(d.Date)
return out
}
out := &tg.DraftMessage{
NoWebpage: d.NoWebpage,
InvertMedia: d.InvertMedia,
ReplyTo: tgDraftReplyTo(d),
Message: d.Message,
Entities: tgMessageEntities(d.Entities),
Media: tgDraftWebPage(d.WebPage),
Date: d.Date,
Effect: d.Effect,
}
if rich := mustTGRichMessage(d.RichMessage); rich != nil {
out.SetRichMessage(*rich)
}
return out
}
func tgDraftReplyTo(d domain.DialogDraft) tg.InputReplyToClass {
if d.ReplyTo == nil {
return nil
}
reply := &tg.InputReplyToMessage{ReplyToMsgID: d.ReplyTo.MessageID}
if d.ReplyTo.TopMessageID > 0 {
reply.SetTopMsgID(d.ReplyTo.TopMessageID)
}
if d.ReplyTo.QuoteText != "" {
reply.SetQuoteText(d.ReplyTo.QuoteText)
}
if len(d.ReplyTo.QuoteEntities) > 0 {
reply.SetQuoteEntities(tgMessageEntities(d.ReplyTo.QuoteEntities))
}
if d.ReplyTo.QuoteOffset > 0 {
reply.SetQuoteOffset(d.ReplyTo.QuoteOffset)
}
return reply
}
func tgDraftWebPage(webpage *domain.DialogDraftWebPage) tg.InputMediaClass {
if webpage == nil || webpage.URL == "" {
return nil
}
return &tg.InputMediaWebPage{
ForceLargeMedia: webpage.ForceLargeMedia,
ForceSmallMedia: webpage.ForceSmallMedia,
Optional: webpage.Optional,
URL: webpage.URL,
}
}
func tgDialogPeer(p domain.Peer) tg.DialogPeerClass {
peer := tgPeer(p)
if peer == nil {
return nil
}
return &tg.DialogPeer{Peer: peer}
}
func tgDialogPeers(peers []domain.Peer) []tg.DialogPeerClass {
out := make([]tg.DialogPeerClass, 0, len(peers))
for _, peer := range peers {
item := tgDialogPeer(peer)
if item != nil {
out = append(out, item)
}
}
return out
}
func tgDialogFilters(list domain.DialogFolderList) *tg.MessagesDialogFilters {
filters := make([]tg.DialogFilterClass, 0, len(list.Folders)+1)
filters = append(filters, &tg.DialogFilterDefault{})
for _, folder := range list.Folders {
if item := tgDialogFilter(folder); item != nil {
filters = append(filters, item)
}
}
return &tg.MessagesDialogFilters{TagsEnabled: list.TagsEnabled, Filters: filters}
}
func tgDialogFilter(folder domain.DialogFolder) tg.DialogFilterClass {
title := tg.TextWithEntities{Text: folder.Title, Entities: tgMessageEntities(folder.TitleEntities)}
if folder.IsChatlist {
out := &tg.DialogFilterChatlist{
HasMyInvites: folder.HasMyInvites,
TitleNoanimate: folder.TitleNoanimate,
ID: folder.ID,
Title: title,
PinnedPeers: tgDialogFolderInputPeers(folder.PinnedPeers),
IncludePeers: tgDialogFolderInputPeers(folder.IncludePeers),
}
if folder.HasEmoticon {
out.SetEmoticon(folder.Emoticon)
}
if folder.HasColor {
out.SetColor(folder.Color)
}
return out
}
out := &tg.DialogFilter{
Contacts: folder.Contacts,
NonContacts: folder.NonContacts,
Groups: folder.Groups,
Broadcasts: folder.Broadcasts,
Bots: folder.Bots,
ExcludeMuted: folder.ExcludeMuted,
ExcludeRead: folder.ExcludeRead,
ExcludeArchived: folder.ExcludeArchived,
TitleNoanimate: folder.TitleNoanimate,
ID: folder.ID,
Title: title,
PinnedPeers: tgDialogFolderInputPeers(folder.PinnedPeers),
IncludePeers: tgDialogFolderInputPeers(folder.IncludePeers),
ExcludePeers: tgDialogFolderInputPeers(folder.ExcludePeers),
}
if folder.HasEmoticon {
out.SetEmoticon(folder.Emoticon)
}
if folder.HasColor {
out.SetColor(folder.Color)
}
return out
}
func tgDialogFolderInputPeers(peers []domain.DialogFolderPeer) []tg.InputPeerClass {
if len(peers) == 0 {
return nil
}
out := make([]tg.InputPeerClass, 0, len(peers))
for _, item := range peers {
switch item.Peer.Type {
case domain.PeerTypeUser:
out = append(out, &tg.InputPeerUser{UserID: item.Peer.ID, AccessHash: item.AccessHash})
case domain.PeerTypeChannel:
out = append(out, &tg.InputPeerChannel{ChannelID: item.Peer.ID, AccessHash: item.AccessHash})
}
}
return out
}
func tgFolderPeers(peers []domain.FolderPeerUpdate) []tg.FolderPeer {
out := make([]tg.FolderPeer, 0, len(peers))
for _, item := range peers {
peer := tgPeer(item.Peer)
if peer == nil {
continue
}
out = append(out, tg.FolderPeer{Peer: peer, FolderID: item.FolderID})
}
return out
}
func tgChannelsForDialogs(viewerUserID int64, channels []domain.Channel, dialogs []domain.Dialog) []tg.ChatClass {
left := make(map[int64]struct{})
for _, dialog := range dialogs {
if dialog.Peer.Type == domain.PeerTypeChannel && dialog.Peer.ID != 0 && dialog.ChannelLeft {
left[dialog.Peer.ID] = struct{}{}
}
}
out := make([]tg.ChatClass, 0, len(channels))
seen := make(map[int64]struct{}, len(channels))
for _, ch := range channels {
if ch.ID == 0 {
continue
}
if _, ok := seen[ch.ID]; ok {
continue
}
seen[ch.ID] = struct{}{}
var self *domain.ChannelMember
if _, ok := left[ch.ID]; ok {
self = &domain.ChannelMember{
ChannelID: ch.ID,
UserID: viewerUserID,
Status: domain.ChannelMemberLeft,
}
} else {
self = &domain.ChannelMember{
ChannelID: ch.ID,
UserID: viewerUserID,
Status: domain.ChannelMemberActive,
}
}
out = append(out, tgChannelChat(viewerUserID, ch, self))
}
return out
}