owpengram-server/internal/rpc/deps.go
2026-09-01 12:06:31 +03:00

1307 lines
105 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 (
"context"
"time"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"github.com/iamxvbaba/td/tlprofile"
"telesrv/internal/domain"
"telesrv/internal/sfu"
"telesrv/internal/store"
"telesrv/internal/turnsrv"
"telesrv/internal/updatecdn"
)
// 本文件按「消费者定义接口」惯例,在 rpc 包定义 Router 依赖的业务服务接口。
// app/* 的 Service 实现它们;微服务化时 gRPC client 同样可实现rpc 层无需改动。
// 接口方法只用 domain 类型与基本类型,不依赖 app 具体包——这是 rpc↔业务的契约边界。
// AuthService 抽象登录/注册业务。
type AuthService interface {
BindTempAuthKey(ctx context.Context, sessionID int64, binding domain.TempAuthKeyBinding) (domain.TempAuthKeyBindingResult, error)
ResolveAuthKey(ctx context.Context, authKeyID [8]byte) ([8]byte, bool, error)
UserID(ctx context.Context, authKeyID [8]byte) (int64, bool, error)
PendingPasswordUserID(ctx context.Context, authKeyID [8]byte) (int64, bool, error)
CompletePasswordSignIn(ctx context.Context, authKeyID [8]byte, expectedUserID int64) error
SendCode(ctx context.Context, phone string) (string, error)
CodeDelivery(ctx context.Context, phoneCodeHash string) (domain.AuthCodeDelivery, bool, error)
ResendCode(ctx context.Context, phone, phoneCodeHash string) (string, error)
CancelCode(ctx context.Context, phone, phoneCodeHash string) error
SignIn(ctx context.Context, a domain.Authorization, phone, phoneCodeHash, code string) (domain.User, domain.Message, bool, error)
// SignInWithEmail 处理带 email_verification 的 auth.signIn登录邮箱路径
SignInWithEmail(ctx context.Context, a domain.Authorization, phone, phoneCodeHash, code string) (domain.User, domain.Message, bool, error)
SignUp(ctx context.Context, a domain.Authorization, phone, phoneCodeHash, firstName, lastName string) (domain.User, domain.Message, error)
AcceptLoginToken(ctx context.Context, a domain.Authorization, userID int64) (domain.Authorization, error)
// BindVerifiedLogin 绑定一个已由外部强因子(passkey)验证身份的用户,直接完成授权。
BindVerifiedLogin(ctx context.Context, a domain.Authorization, userID int64) (domain.User, error)
// SignInBot 校验 bot token 并绑定授权auth.importBotAuthorization
// 校验失败返回 domain.ErrBotTokenInvalid。
SignInBot(ctx context.Context, a domain.Authorization, token string) (domain.User, error)
LogOut(ctx context.Context, authKeyID [8]byte) error
Authorization(ctx context.Context, authKeyID [8]byte) (domain.Authorization, bool, error)
AuthKeyClientInfo(ctx context.Context, authKeyID [8]byte) (domain.AuthKeyClientInfo, bool, error)
UpdateAuthKeyClientInfo(ctx context.Context, authKeyID [8]byte, info domain.AuthKeyClientInfo) error
ListAuthorizations(ctx context.Context, userID int64) ([]domain.Authorization, error)
ResetAuthorization(ctx context.Context, userID, hash int64) (domain.Authorization, bool, error)
ResetAuthorizations(ctx context.Context, userID int64, keepAuthKeyID [8]byte) ([]domain.Authorization, error)
}
type AuthDeliveryReportService interface {
ReportMissingCode(ctx context.Context, req domain.AuthMissingCodeReportRequest) (domain.AuthDeliveryReport, bool, error)
}
type ClientTelemetryService interface {
Record(ctx context.Context, userID int64, kind domain.ClientTelemetryKind, peer domain.Peer, subjectIDs []int64, payload any, createdAt time.Time) (domain.ClientTelemetryEvent, bool, error)
}
// SessionBinder 抽象登录后 session 与 user 的在线绑定。
//
// MTProto session 的完整身份是 raw auth_key_id + session_id。所有定位单个 session
// 的方法都必须携带这两个值;禁止退回只按 session_id 查询,否则不同 auth key 复用
// 同一随机 session_id 时会产生跨账号绑定、排除或推送歧义。
type SessionBinder interface {
BindAuthKeyForSession(rawAuthKeyID [8]byte, sessionID int64, authKeyID [8]byte)
AuthKeyIDForSession(rawAuthKeyID [8]byte, sessionID int64) ([8]byte, bool)
BindUserForAuthKey(rawAuthKeyID [8]byte, sessionID, userID int64)
UserIDResolvedForAuthKey(rawAuthKeyID [8]byte, sessionID int64) (userID int64, resolved bool)
UnbindAuthKey(authKeyID [8]byte) int
SetReceivesUpdatesForAuthKey(rawAuthKeyID [8]byte, sessionID int64, receives bool)
PushToSessionForAuthKey(ctx context.Context, rawAuthKeyID [8]byte, sessionID int64, t proto.MessageType, msg tg.UpdatesClass) error
// excludeAuthKeyID/excludeSessionID 必须同时为零(不排除)或同时非零(精确排除)。
PushToUserExceptAuthKeySession(ctx context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg tg.UpdatesClass) (int, error)
}
// RawAuthKeySessionBinder 在 auth.bindTempAuthKey 成功后,把同一 raw temporary key
// 已建立的所有 session 一次性切到 canonical permanent identity。只更新当前 session
// 会让并发启动的其它连接永久粘在 raw identity。
type RawAuthKeySessionBinder interface {
BindAuthKeyForRawAuthKey(rawAuthKeyID [8]byte, authKeyID [8]byte) int
}
// RawAuthKeyMetadataProvider 暴露握手时确定的 raw key protocol expiry。0 表示
// permanent key正值表示 temporary key。Router 只用它判断 cached==raw 是否可作为
// permanent 快路径,协议过期的实际拒绝仍由 mtprotoedge 完成。
type RawAuthKeyMetadataProvider interface {
AuthKeyExpiresAtForSession(rawAuthKeyID [8]byte, sessionID int64) (expiresAt int, found bool)
}
// ImmediateSessionPusher 是可选的登录前信号直推能力。
// 它绕过登录后 updates-ready 队列,只能用于会解锁登录流程本身的握手消息,
// 例如 updateLoginToken。
type ImmediateSessionPusher interface {
PushToSessionForAuthKeyImmediate(ctx context.Context, rawAuthKeyID [8]byte, sessionID int64, t proto.MessageType, msg tg.UpdatesClass) error
}
// SessionUpdatesStateProvider 暴露连接当前的 updates 接收状态(可选能力)。
// 用于按 RPC 置位 receivesUpdates 时的幂等短路;不实现时每次都走完整置位(幂等,仅多余开销)。
type SessionUpdatesStateProvider interface {
ReceivesUpdatesForAuthKey(rawAuthKeyID [8]byte, sessionID int64) bool
}
// SessionUpdatesActivationProvider serializes the expensive transition from an
// authenticated physical session to updates-ready. A successful claim belongs
// to exactly one physical connection generation; the token prevents a delayed
// callback from an old connection clearing a replacement's claim.
//
// This capability gates only membership synchronization and readiness. Each
// updates.getState/getDifference delivery keeps its own cursor commit and must
// never be coalesced with another RPC.
type SessionUpdatesActivationProvider interface {
BeginSessionUpdatesActivation(rawAuthKeyID [8]byte, sessionID int64) (token uint64, ok bool)
EndSessionUpdatesActivation(rawAuthKeyID [8]byte, sessionID int64, token uint64)
}
// SessionBootstrapProbeProvider makes the durable bootstrap-job readiness
// lookup a one-shot per physical connection generation. A successful probe
// includes an authoritative zero-row result. Failed delivery work releases the
// claim so a later delivered baseline can retry; replacement connections own
// independent state and reject completion from an older generation.
type SessionBootstrapProbeProvider interface {
BeginSessionBootstrapProbe(rawAuthKeyID [8]byte, sessionID int64) (token uint64, ok bool)
EndSessionBootstrapProbe(rawAuthKeyID [8]byte, sessionID int64, token uint64, success bool)
}
// ClientLayerBinder 把协商 TL layer 即时下推到连接(可选能力)。
// invokeWithLayer 在 Dispatch 入口被观测到时立即调用,使同一请求 handler 执行期间
// 触发的 pending flush / 并发 push 就已按正确 layer 降级;不实现时连接层只能靠
// Dispatch 返回后的兜底刷新,重连老客户端首条 RPC 期间的推送会漏降级。
type ClientLayerBinder interface {
SetClientLayerForAuthKey(rawAuthKeyID [8]byte, sessionID int64, layer int)
}
// AuthKeyLayerBinder seeds an inherited auth-key default into every live
// session that still has no explicit invokeWithLayer observation. Implementors
// must not overwrite an explicit per-session profile; the returned count is
// diagnostic only.
//
// Router uses this optional capability after auth.bindTempAuthKey normalizes a
// raw temporary key to its permanent identity. Keeping it separate from
// ClientLayerBinder makes the source distinction explicit: inherited defaults
// are mutable initialization state, while an explicit session observation is a
// correction for exactly one logical session.
type AuthKeyLayerBinder interface {
SeedInheritedLayerForRawAuthKey(rawAuthKeyID [8]byte, layer int) int
}
// BusinessAuthKeyLayerBinder seeds unknown live sessions across every raw PFS
// key currently normalized to the same permanent/business auth key.
type BusinessAuthKeyLayerBinder interface {
SeedInheritedLayerForBusinessAuthKey(authKeyID [8]byte, layer int) int
}
// AuthKeyLayerRefresher is the identity-normalization variant used after
// auth.bindTempAuthKey. It may replace an inherited raw-key shadow with the
// permanent key's default, but must still preserve every explicit session
// profile.
type AuthKeyLayerRefresher interface {
RefreshInheritedLayerForRawAuthKey(rawAuthKeyID [8]byte, layer int) int
}
// AuthKeyInheritedLayerClearer removes only the mutable inherited default for
// a physical/raw key. Explicit per-session invokeWithLayer profiles are wire
// evidence and must survive. Router uses this when a temp key binds to a
// permanent key whose durable Layer is authoritative but unsupported by this
// binary; retaining the raw key's older inherited default would silently
// downgrade naked RPCs instead of asking the client to correct explicitly.
type AuthKeyInheritedLayerClearer interface {
ClearInheritedLayerForRawAuthKey(rawAuthKeyID [8]byte) int
}
// ActiveSessionLayerEvidenceProvider exposes the live connection's explicit
// profile when auth.bindTempAuthKey runs after the Router's bounded exact-
// profile retention window. It is deliberately session-scoped and must never
// report an inherited profile as explicit evidence.
type ActiveSessionLayerEvidenceProvider interface {
ExplicitLayerEvidenceForAuthKey(rawAuthKeyID [8]byte, sessionID int64) (layer int, msgID int64, ok bool)
}
// SessionTerminator 暴露按业务 auth_key 强制断开活跃连接的能力(可选)。
// 授权撤销(被踢设备)必须断开连接:出站推送用连接持有的密钥加密、不回查授权,
// perm-key 连接的授权缓存也只有断开重连才会重新回查授权表。
type SessionTerminator interface {
CloseSessionsForBusinessAuthKey(authKeyID [8]byte) int
}
// RawSessionTerminator 暴露按连接实际 raw auth_key 强制断开的能力(可选)。
// temp auth key 被撤销时Router 会先从 temp→perm 缓存里找出同一授权的 raw temp key
// 再用这个接口踢掉仍未完全解析到业务 key 的活跃连接,避免等缓存 TTL 或下一帧才失效。
type RawSessionTerminator interface {
CloseSessionsForRawAuthKeyExcept(authKeyID [8]byte, exceptSessionID int64) int
}
// BestEffortSessionBinder 是带 raw auth_key_id 精确排除当前设备的短超时推送接口;
// 不用于 RPC result/ack。
type BestEffortSessionBinder interface {
PushToUserExceptAuthKeySessionBestEffort(ctx context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg tg.UpdatesClass, timeout time.Duration) (int, error)
}
// PushSessionRegistrar 登记/撤销 account.registerDevice(token_type=7MTProto 内部
// 推送通道) 的 session。登记后该 session 在推送 fan-out 中被视为永久就绪,绕过
// receivesUpdates 门槛——它只发 ping、永远不会调 updates.getState但在账号后台/
// 未选中(客户端把主连接 setAppPaused 挂起)时是唯一还连着服务器的连接,是官方
// Telegram 在无 FCM/APNs 场景(大陆、去 Google 化设备)下仍能收到来电、消息等实时
// 推送的机制。SessionManager 实现;未装配时 registerDevice 静默跳过登记(只影响
// 后台唤醒不影响功能正确性。See memory: call-inactive-account-network-pause。
type PushSessionRegistrar interface {
MarkPushSession(rawAuthKeyID [8]byte, sessionID int64)
UnmarkPushSession(rawAuthKeyID [8]byte, sessionID int64)
}
// TransientSessionBinder 推送短命、不写 durable log 的 updatetyping / presence
// 与普通推送的关键区别:目标 session 未就绪时直接跳过、不进 pending——transient 数据
// getDifference 无法补,就绪后由 getState 快照/下次状态变化重建,囤积过期 transient 无意义。
type TransientSessionBinder interface {
PushToUserTransientExceptAuthKeySession(ctx context.Context, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64, t proto.MessageType, msg tg.UpdatesClass, timeout time.Duration) (int, error)
}
// AuthKeyTargetedSessionBinder 把 update 定向投递给某用户【绑定到具体 business auth_key
// 这台设备】的就绪连接密聊设备级投递。SessionManager 实现;密聊启用时必须装配,
// 缺失时 fail-closed严禁回退账号级推送。未就绪连接跳过、不进 pending离线靠 difference 补)。
type AuthKeyTargetedSessionBinder interface {
PushToUserAuthKey(ctx context.Context, userID int64, businessAuthKeyID [8]byte, t proto.MessageType, msg tg.UpdatesClass) (int, error)
PushToUserAuthKeyTransient(ctx context.Context, userID int64, businessAuthKeyID [8]byte, t proto.MessageType, msg tg.UpdatesClass, timeout time.Duration) (int, error)
PushToUserExceptBusinessAuthKey(ctx context.Context, userID int64, excludeBusinessAuthKeyID [8]byte, t proto.MessageType, msg tg.UpdatesClass, timeout time.Duration) (int, error)
}
// SemanticTransientSessionBinder filters by generated exact-profile metadata
// instead of a hard-coded minimum layer. A newly generated profile therefore
// becomes eligible automatically when it has a wire constructor for semantic.
type SemanticTransientSessionBinder interface {
PushToUserTransientCompatible(ctx context.Context, userID int64, semantic tlprofile.SemanticID, t proto.MessageType, msg tg.UpdatesClass, timeout time.Duration) (int, error)
PushToUserAuthKeyTransientCompatible(ctx context.Context, userID int64, businessAuthKeyID [8]byte, semantic tlprofile.SemanticID, t proto.MessageType, msg tg.UpdatesClass, timeout time.Duration) (int, error)
}
// OnlineUserProvider exposes a bounded runtime snapshot for best-effort fanout.
type OnlineUserProvider interface {
IsUserOnline(userID int64) bool
OnlineUserIDsForCandidates(candidateUserIDs []int64, limit int) []int64
TrackChannelInterest(rawAuthKeyID [8]byte, sessionID, userID int64, channelIDs []int64)
ClearChannelInterest(rawAuthKeyID [8]byte, sessionID, userID int64)
OnlineChannelUserIDs(channelID int64, limit int) []int64
// ChannelMembershipGeneration / SetSessionChannelMemberships 配对使用:调用方在读取
// 持久成员列表前采样修订号,落地时带回;期间发生增量 Add/Remove 时 Set 走合并路径
// 并保持未就绪,由下一条 RPC 重试全量同步(防全量替换覆盖窗口内的增量 join/leave
ChannelMembershipGeneration(rawAuthKeyID [8]byte, sessionID int64) int64
SetSessionChannelMemberships(rawAuthKeyID [8]byte, sessionID, userID int64, channelIDs []int64, expectedGen int64)
AddUserChannelMembership(userID, channelID int64)
RemoveUserChannelMembership(userID, channelID int64)
OnlineChannelMemberUserIDs(channelID int64, limit int) []int64
}
// ChannelSubscriptionProvider is the bounded process-local implementation of
// Telegram's public-channel short-poll subscription. A successful
// updates.getChannelDifference refresh from one session enables passive channel
// updates for the whole user account until the subscription expires.
type ChannelSubscriptionProvider interface {
RefreshChannelSubscription(rawAuthKeyID [8]byte, sessionID, userID, channelID int64, ttl time.Duration)
OnlineChannelSubscriberUserIDs(channelID int64, limit int) []int64
OnlineChannelSubscriberUserIDsExcluding(channelID int64, exclude map[int64]struct{}, limit int) []int64
}
// ChannelNudgeProvider 暴露「频道在线成员中排除已投递集合后的剩余 user id」用于 >cap
// 在线成员的 UpdateChannelTooLong nudgeP0-8。SessionManager 实现;测试/未装配 fake 可不实现
// type-assert 失败时跳过 nudge不影响完整 payload 投递)。
type ChannelNudgeProvider interface {
OnlineChannelMemberUserIDsExcluding(channelID int64, exclude map[int64]struct{}, limit int) []int64
}
// ChannelFanoutRecoverySessionProvider snapshots the process-local online joined-channel index in
// stable channel-id order. It is used only after every keyed fan-out mailbox is saturated. The
// fixed recovery actor accepts the temporary 8*C id slice so one sweep never repeatedly scans the
// SessionManager index or holds its global lock while sorting/database work runs.
type ChannelFanoutRecoverySessionProvider interface {
OnlineChannelIDsSnapshot() []int64
}
// ChannelFanoutRecoveryPtsProvider reloads the authoritative channel pts after in-memory fan-out
// saturation. Production channels.Service implements it through the channel store; keeping this
// separate from ChannelsService avoids burdening lightweight RPC fakes that never run the worker.
type ChannelFanoutRecoveryPtsProvider interface {
MaxChannelPtsBatch(ctx context.Context, channelIDs []int64) (map[int64]int, error)
}
// RateLimiter 抽象 RPC 高频写操作限流。
type RateLimiter interface {
Allow(ctx context.Context, key string, limit int, window time.Duration) (allowed bool, retryAfterSeconds int, err error)
AllowN(ctx context.Context, key string, cost, limit int, window time.Duration) (allowed bool, retryAfterSeconds int, err error)
}
// UsersService 抽象用户查询。
type UsersService interface {
Self(ctx context.Context, userID int64) (domain.User, error)
ByID(ctx context.Context, currentUserID, userID int64) (domain.User, bool, error)
ByIDs(ctx context.Context, currentUserID int64, userIDs []int64) ([]domain.User, error)
}
// BaseUserBotStatusProvider exposes the immutable, viewer-independent bot bit
// without constructing a full user projection. Production users.Service
// implements it through the shared base-user Redis read model.
type BaseUserBotStatusProvider interface {
BotStatus(ctx context.Context, userID int64) (bot bool, found bool, err error)
}
type UserProjectionFactInvalidator interface {
InvalidateAccountFreezeFact(userID int64)
}
// TelegramLoginService is the domain-only boundary shared by the MTProto RPC
// edge and the public OIDC provider. PostgreSQL remains authoritative for all
// consent transitions; the RPC layer only projects domain state to TL.
type TelegramLoginService interface {
ValidateMessageButton(ctx context.Context, botUserID int64, rawURL string) (normalizedURL, domainName string, err error)
AuthorizeMessageButton(ctx context.Context, params domain.TelegramLoginMessageButtonAuthorization) (domain.TelegramLoginMessageButtonResult, error)
RequestByDeepLink(ctx context.Context, deepLink string) (domain.TelegramLoginRequest, error)
RequestByDeepLinkForOrigin(ctx context.Context, deepLink, inAppOrigin string) (domain.TelegramLoginRequest, error)
CheckMatchCode(ctx context.Context, deepLink, selected string) (bool, error)
Approve(ctx context.Context, deepLink string, identity domain.TelegramLoginIdentitySnapshot, writeAllowed, phoneShared bool, matchCode string) (domain.TelegramLoginRequest, domain.TelegramLoginWebAuthorization, error)
FinalizeRedirectByDeepLink(ctx context.Context, deepLink string) (string, error)
FinalizeInAppRedirectByDeepLink(ctx context.Context, deepLink string) (string, error)
Decline(ctx context.Context, deepLink string, userID int64) (domain.TelegramLoginRequest, error)
ListWebAuthorizations(ctx context.Context, userID int64) ([]domain.TelegramLoginWebAuthorization, error)
RevokeWebAuthorization(ctx context.Context, userID, hash int64) error
RevokeAllWebAuthorizations(ctx context.Context, userID int64) (int64, error)
}
// BatchViewerUsersResolver 是 UsersService 的可选能力:跨多个 viewer 一次性投影同一组 user
// fan-out 模板化,把 per-recipient 的 ByIDs(=ForViewer) 折叠成 O(owner) 查询)。结果按 viewer
// 与 ByIDs(viewer, ids) 字节等价,包含 viewer-specific personal photo overlay。
// 声明需要 fan-out 预热的路径必须具备该能力;缺失或失败时在线 fan-out fail-closed
// 不得在同一请求里改走逐 recipient 查询。
type BatchViewerUsersResolver interface {
ByIDsForViewers(ctx context.Context, viewerUserIDs []int64, userIDs []int64) (map[int64][]domain.User, error)
}
// SparseBatchViewerUsersResolver projects only the explicitly supplied
// viewer->user edges. Local Durable Outbox uses this instead of widening one
// claim into viewers x union(users).
type SparseBatchViewerUsersResolver interface {
ByIDsForViewerUserIDs(ctx context.Context, userIDsByViewer map[int64][]int64) (map[int64][]domain.User, error)
}
// BotsService 抽象 bot 元数据查询与管理bots.* RPC + userFull.bot_info hydrate
// 写方法返回 bump 后的 bot_info_version客户端据此重拉
type BotsService interface {
BotInfo(ctx context.Context, botUserID int64) (domain.BotProfile, bool, error)
OwnsBot(ctx context.Context, ownerUserID, botUserID int64) (bool, error)
CheckUsername(ctx context.Context, ownerUserID int64, username string) (bool, error)
CreateBot(ctx context.Context, ownerUserID int64, name, username string) (domain.User, string, error)
ListOwnedBots(ctx context.Context, ownerUserID int64) ([]domain.User, error)
ExportBotToken(ctx context.Context, ownerUserID, botUserID int64, revoke bool) (string, error)
SetBotCommands(ctx context.Context, botUserID int64, commands []domain.BotCommand) (int, error)
GetBotCommands(ctx context.Context, botUserID int64) ([]domain.BotCommand, error)
SetBotInfo(ctx context.Context, botUserID int64, upd domain.BotInfoUpdate) (int, error)
GetBotInfo(ctx context.Context, botUserID int64) (name, about, description string, err error)
SetBotMenuButton(ctx context.Context, botUserID int64, button domain.BotMenuButton) (int, error)
GetBotMenuButton(ctx context.Context, botUserID int64) (domain.BotMenuButton, error)
SetInlinePlaceholder(ctx context.Context, botUserID int64, placeholder string) (int, error)
CanSendMessage(ctx context.Context, userID, botUserID int64) (bool, error)
AllowSendMessage(ctx context.Context, userID, botUserID int64, fromRequest bool) (bool, error)
UpsertBotApp(ctx context.Context, botUserID int64, app domain.BotApp) (domain.BotApp, int, error)
GetBotAppByID(ctx context.Context, appID, accessHash int64) (domain.BotApp, bool, error)
GetBotAppByShortName(ctx context.Context, botUserID int64, shortName string) (domain.BotApp, bool, error)
GetMainBotApp(ctx context.Context, botUserID int64) (domain.BotApp, bool, error)
ListBotApps(ctx context.Context, botUserID int64) ([]domain.BotApp, error)
GetBotAppSettings(ctx context.Context, botUserID int64) (domain.BotAppSettings, bool, error)
UpsertBotAppSettings(ctx context.Context, botUserID int64, settings domain.BotAppSettings) (int, error)
ListBotAppPreviewMedia(ctx context.Context, botUserID, appID int64) ([]domain.BotAppPreviewMedia, error)
UpsertBotAppPreviewMedia(ctx context.Context, media domain.BotAppPreviewMedia) (domain.BotAppPreviewMedia, int, error)
DeleteBotAppPreviewMedia(ctx context.Context, botUserID, appID, mediaID int64) (int, error)
ReorderBotAppPreviewMedia(ctx context.Context, botUserID, appID int64, mediaIDs []int64) (int, error)
UpsertAttachMenuBot(ctx context.Context, botUserID int64, bot domain.BotAttachMenuBot) (int, error)
GetAttachMenuBot(ctx context.Context, botUserID int64) (domain.BotAttachMenuBot, bool, error)
ListAttachMenuBots(ctx context.Context) ([]domain.BotAttachMenuBot, error)
GetAttachMenuState(ctx context.Context, userID, botUserID int64) (domain.BotAttachMenuState, bool, error)
SetAttachMenuState(ctx context.Context, state domain.BotAttachMenuState) (domain.BotAttachMenuState, error)
SaveRequestedWebViewButton(ctx context.Context, button domain.BotRequestedWebViewButton) (domain.BotRequestedWebViewButton, error)
GetRequestedWebViewButton(ctx context.Context, botUserID, userID int64, reqID string) (domain.BotRequestedWebViewButton, bool, error)
DeleteRequestedWebViewButton(ctx context.Context, botUserID, userID int64, reqID string) error
SetBotEmojiStatusPermission(ctx context.Context, botUserID, userID int64, allowed bool) error
BotEmojiStatusPermission(ctx context.Context, botUserID, userID int64) (bool, error)
PutWebViewCustomMethodQuery(ctx context.Context, botUserID, userID int64, method, paramsJSON string) (domain.BotWebViewCustomMethodQuery, error)
}
// ServiceBotCallbacks answers inline-button clicks for the built-in bots that run
// inside this process (@verifybot and friends); app/bots implements it.
//
// It exists because the ordinary callback path cannot serve them: an internal bot
// has no MTProto session to receive updateBotCallbackQuery and no Bot API consumer
// to drain the update queue, so pushing the query at it and waiting could only
// ever end in BOT_RESPONSE_TIMEOUT after the full 25-second window. A bot claimed
// here is answered synchronously instead, by the responder that owns it.
//
// OnCallbackQuery reports handled=false when the bot is not one of the responder's
// own, which the edge treats as invalid callback data. The answer is final:
// nothing is registered in the shared callback registry for it, so no external
// setBotCallbackAnswer can overwrite or spoof it.
//
// A nil Deps.ServiceBotCallbacks keeps the edge behaviour exactly as it was:
// every callback is pushed to the bot's session and waited on.
type ServiceBotCallbacks interface {
HandlesBot(botUserID int64) bool
OnCallbackQuery(ctx context.Context, query domain.BotCallbackQuery) (domain.BotCallbackAnswer, bool, error)
}
// ServiceBotInlineResults answers messages.getInlineBotResults for built-in
// bots that run inside this process (@gif); app/bots implements it.
//
// Same rationale as ServiceBotCallbacks above: an internal bot has no MTProto
// session to receive updateBotInlineQuery and no Bot API consumer to drain the
// queue, so the ordinary push-and-wait-25s path could only ever time out. A bot
// claimed here is answered synchronously by the responder that owns it, and
// nothing is registered in the shared inline-query registry for it.
//
// A nil Deps.ServiceBotInlineResults keeps the edge behaviour exactly as it
// was: every inline query is pushed to the bot's session and waited on.
//
// HandlesInlineBot is deliberately its own method, not a reuse of
// ServiceBotCallbacks'/messages.BotResponder's shared HandlesBot: that one
// method already drives both private-message and callback routing for the
// bots it covers, and @gif should answer inline queries only, not also become
// eligible for private-message/callback dispatch it was never given a case
// for.
type ServiceBotInlineResults interface {
HandlesInlineBot(botUserID int64) bool
OnInlineQuery(ctx context.Context, botUserID, userID int64, query, offset string) (domain.BotInlineResults, bool, error)
}
// UserIdentityService 是 UsersService 的资料扩展能力,用于 username/phone 解析。
type UserIdentityService interface {
CheckUsername(ctx context.Context, userID int64, username string) (bool, error)
UpdateProfile(ctx context.Context, userID int64, update domain.UserProfileUpdate) (domain.User, error)
UpdateUsername(ctx context.Context, userID int64, username string) (domain.User, error)
UpdateBirthday(ctx context.Context, userID int64, birthday domain.Birthday) (domain.User, error)
UpdatePersonalChannel(ctx context.Context, userID int64, channelID int64) (domain.User, error)
ResolveUsername(ctx context.Context, currentUserID int64, username string) (domain.User, bool, error)
ResolvePhone(ctx context.Context, currentUserID int64, phone string) (domain.User, bool, error)
}
// UserPremiumService 是 UsersService 的会员扩展能力:授予/续期、到期清理
// PremiumSweeper与 emoji statuspremium 专属)。设计见 docs/premium-module.md。
type UserPremiumService interface {
GrantPremium(ctx context.Context, userID int64, months int) (domain.User, error)
SweepExpiredPremium(ctx context.Context, now int64, limit int) ([]domain.User, error)
UpdateEmojiStatus(ctx context.Context, userID int64, status domain.UserEmojiStatus) (domain.User, error)
}
// UserEmojiStatusDurableService exposes the aggregate state+event write used
// by account.updateEmojiStatus. The bool is false for lightweight stores that
// require the RPC Updates service to append the event separately.
type UserEmojiStatusDurableService interface {
UpdateEmojiStatusWithEvent(ctx context.Context, userID int64, status domain.UserEmojiStatus, date int, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.User, domain.UpdateEvent, bool, error)
}
// UserColorService 是 UsersService 的个人色板扩展能力。用于 account.updateColor
// 持久化当前账号的消息气泡 accent 或资料页背景色。
type UserColorService interface {
UpdateColor(ctx context.Context, userID int64, forProfile bool, color domain.PeerColor) (domain.User, error)
}
// UserPremiumStatusService 暴露轻量会员判断(基础用户缓存路径,不做 viewer
// 投影供限额双档reaction 上限等)低成本调用。
type UserPremiumStatusService interface {
PremiumActive(ctx context.Context, userID int64) bool
}
// AccountService 抽象账号设置查询。
type AccountService interface {
SendChangePhoneCode(ctx context.Context, userID int64, authKeyID [8]byte, sessionID int64, phone string) (string, domain.AuthCodeDelivery, error)
ChangePhone(ctx context.Context, userID int64, authKeyID, originRawAuthKeyID [8]byte, sessionID int64, phone, phoneCodeHash, code string, date int) (domain.PhoneChangeResult, error)
GetPassword(ctx context.Context, userID int64) (domain.PasswordSettings, error)
GetPasswordSettings(ctx context.Context, userID int64, check domain.PasswordCheck) (domain.PrivatePasswordSettings, error)
UpdatePasswordSettings(ctx context.Context, userID int64, check domain.PasswordCheck, input domain.PasswordInputSettings) error
CheckPassword(ctx context.Context, userID int64, check domain.PasswordCheck) error
RevenueWithdrawalPasswordState(ctx context.Context, userID int64) (domain.RevenueWithdrawalPasswordState, error)
RequestPasswordRecovery(ctx context.Context, userID int64) (string, error)
CheckRecoveryPassword(ctx context.Context, userID int64, code string) error
RecoverPassword(ctx context.Context, userID int64, code string, input *domain.PasswordInputSettings) error
ConfirmPasswordEmail(ctx context.Context, userID int64, code string) error
ResendPasswordEmail(ctx context.Context, userID int64) error
CancelPasswordEmail(ctx context.Context, userID int64) error
// 登录邮箱(独立于 2FA 恢复邮箱authed 走 userID登录流程/重置走 phone。
SendLoginEmailCode(ctx context.Context, userID int64, phone, phoneCodeHash, email string, setup bool) (string, int, error)
VerifyLoginEmail(ctx context.Context, userID int64, phone, phoneCodeHash, code string, setup bool) (string, error)
SetLoginEmail(ctx context.Context, userID int64, email string) error
LoginEmail(ctx context.Context, userID int64) (string, bool, error)
LoginEmailByPhone(ctx context.Context, phone string) (string, bool, error)
ClearLoginEmail(ctx context.Context, userID int64) error
ResetPassword(ctx context.Context, userID int64) (domain.PasswordResetResult, error)
DeclinePasswordReset(ctx context.Context, userID int64) error
SaveMusic(ctx context.Context, userID int64, req domain.SaveMusicRequest) (bool, error)
ListSavedMusicIDs(ctx context.Context, userID int64, limit int) ([]int64, error)
ListSavedMusic(ctx context.Context, userID int64, offset, limit int) (domain.SavedMusicList, error)
GetSavedMusicByIDs(ctx context.Context, userID int64, ids []int64) (domain.SavedMusicList, error)
}
// AccountBusinessAutomationService 是账号业务自动化的可选扩展。
// 只暴露 domain DTO避免 Telegram TL 类型越过 rpc 边界。
type AccountBusinessAutomationService interface {
GetBusinessProfile(ctx context.Context, userID int64) (domain.BusinessProfile, bool, error)
UpdateBusinessWorkHours(ctx context.Context, userID int64, hours *domain.BusinessWorkHours) (domain.BusinessProfile, error)
UpdateBusinessLocation(ctx context.Context, userID int64, location *domain.BusinessLocation) (domain.BusinessProfile, error)
UpdateBusinessIntro(ctx context.Context, userID int64, intro *domain.BusinessIntro) (domain.BusinessProfile, error)
UpdateBusinessGreetingMessage(ctx context.Context, userID int64, greeting *domain.BusinessGreetingMessage) (domain.BusinessProfile, error)
UpdateBusinessAwayMessage(ctx context.Context, userID int64, away *domain.BusinessAwayMessage) (domain.BusinessProfile, error)
ListBusinessChatLinks(ctx context.Context, userID int64) ([]domain.BusinessChatLink, error)
CreateBusinessChatLink(ctx context.Context, userID int64, input domain.BusinessChatLinkInput) (domain.BusinessChatLink, error)
EditBusinessChatLink(ctx context.Context, userID int64, slug string, input domain.BusinessChatLinkInput) (domain.BusinessChatLink, error)
DeleteBusinessChatLink(ctx context.Context, userID int64, slug string) (bool, error)
ResolveBusinessChatLink(ctx context.Context, slug string, bumpViews bool) (domain.BusinessChatLink, bool, error)
ListQuickReplies(ctx context.Context, userID int64) (domain.QuickReplyList, error)
CheckQuickReplyShortcut(ctx context.Context, userID int64, shortcut string) (bool, error)
SaveQuickReplyText(ctx context.Context, userID int64, shortcut string, msg domain.QuickReplyMessage) (domain.QuickReplyMutation, error)
GetQuickReplyMessages(ctx context.Context, userID int64, shortcutID int, ids []int) (domain.QuickReplyMessages, error)
RenameQuickReplyShortcut(ctx context.Context, userID int64, shortcutID int, shortcut string) (domain.QuickReplyMutation, error)
ReorderQuickReplies(ctx context.Context, userID int64, order []int) (domain.QuickReplyMutation, error)
DeleteQuickReplyShortcut(ctx context.Context, userID int64, shortcutID int) (domain.QuickReplyMutation, error)
DeleteQuickReplyMessages(ctx context.Context, userID int64, shortcutID int, ids []int) (domain.QuickReplyMutation, error)
GetConnectedBusinessBot(ctx context.Context, ownerUserID int64) (domain.ConnectedBusinessBot, bool, error)
SaveConnectedBusinessBot(ctx context.Context, ownerUserID int64, bot domain.ConnectedBusinessBot) (domain.ConnectedBusinessBot, error)
DeleteConnectedBusinessBot(ctx context.Context, ownerUserID, botUserID int64) (bool, error)
SetConnectedBusinessBotPaused(ctx context.Context, ownerUserID, peerUserID int64, paused bool) (domain.ConnectedBusinessBotPeerState, error)
DisableConnectedBusinessBotForPeer(ctx context.Context, ownerUserID, peerUserID int64) (domain.ConnectedBusinessBotPeerState, error)
GetConnectedBusinessBotPeerState(ctx context.Context, ownerUserID, peerUserID int64) (domain.ConnectedBusinessBotPeerState, bool, error)
}
// PrivacyService owns account privacy rule storage/evaluation.
type PrivacyService interface {
GetRules(ctx context.Context, ownerUserID int64, key domain.PrivacyKey) (domain.PrivacyRules, error)
SetRules(ctx context.Context, ownerUserID int64, key domain.PrivacyKey, rules []domain.PrivacyRule) (domain.PrivacyRules, error)
AddAllowUser(ctx context.Context, ownerUserID int64, key domain.PrivacyKey, targetUserID int64) (domain.PrivacyRules, bool, error)
CanSee(ctx context.Context, ownerUserID, viewerUserID int64, key domain.PrivacyKey) (bool, error)
// CanSeeBatch evaluates multiple keys for one (owner, viewer) pair with a
// single ListPrivacyRules + GetReverseContacts round trip, equivalent to
// one CanSee call per key. Used where a single projection (e.g.
// getFullUser) otherwise needs several keys checked at once.
CanSeeBatch(ctx context.Context, ownerUserIDs []int64, viewerUserID int64, keys []domain.PrivacyKey) (map[int64]map[domain.PrivacyKey]bool, error)
}
// HelpService 抽象启动配置与国家区号目录。
type HelpService interface {
GetAppConfig(ctx context.Context, userID int64, hash int) (domain.AppConfig, bool, error)
GetCountries(ctx context.Context, langCode string, hash int) (domain.CountriesList, bool, error)
}
// AccountFreezeService exposes the account-level read-only fact used by the
// central RPC mutation gate. It is domain-only and shared with app/help.
type AccountFreezeService interface {
AccountFreeze(ctx context.Context, userID int64) (domain.AccountFreeze, bool, error)
}
// AccountFreezeNotificationService owns the durable non-PTS notification
// queue. It is intentionally separate from AccountFreezeService so hot
// read-only gates can use a versioned fact cache without disabling queue
// consumption.
type AccountFreezeNotificationService interface {
ClaimAccountFreezeNotifications(ctx context.Context, now time.Time, limit int, lease time.Duration) ([]domain.AccountFreezeNotification, error)
CompleteAccountFreezeNotification(ctx context.Context, id, version int64, now time.Time) error
}
// UpdatesService 抽象 update 状态查询。
type UpdatesService interface {
GetState(ctx context.Context, authKeyID [8]byte, userID int64) (domain.UpdateState, error)
CurrentState(ctx context.Context, userID int64) (domain.UpdateState, error)
ConfirmedState(ctx context.Context, authKeyID [8]byte, userID int64) (domain.UpdateState, bool, error)
ObserveDifferenceRequest(ctx context.Context, authKeyID [8]byte, userID int64, from domain.UpdateState) (domain.UpdateState, error)
GetDifference(ctx context.Context, authKeyID [8]byte, userID int64, from domain.UpdateState) (domain.UpdateDifference, error)
CommitDeliveredState(ctx context.Context, authKeyID [8]byte, userID int64, state domain.UpdateState, mode domain.UpdateStateCommitMode) error
ClearAuthKey(ctx context.Context, authKeyID [8]byte) error
RecordNewMessage(ctx context.Context, authKeyID [8]byte, userID int64, msg domain.Message) (domain.UpdateEvent, domain.UpdateState, error)
PublishNewMessage(ctx context.Context, userID int64, msg domain.Message) (domain.UpdateEvent, domain.UpdateState, error)
RecordStory(ctx context.Context, stateAuthKeyID [8]byte, userID int64, story domain.Story, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordStoryFanout(ctx context.Context, userID int64, story domain.Story) (domain.UpdateEvent, domain.UpdateState, error)
RecordReadStories(ctx context.Context, stateAuthKeyID [8]byte, userID int64, read domain.StoryReadResult, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordSentStoryReaction(ctx context.Context, stateAuthKeyID [8]byte, userID int64, reaction domain.StoryReactionResult, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordNewStoryReaction(ctx context.Context, stateAuthKeyID [8]byte, ownerUserID int64, reaction domain.StoryReactionResult, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordQuickReplyMutation(ctx context.Context, stateAuthKeyID [8]byte, userID int64, mutation domain.QuickReplyMutation, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordReadHistory(ctx context.Context, stateAuthKeyID [8]byte, userID int64, read domain.ReadHistoryResult, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordContactsReset(ctx context.Context, stateAuthKeyID [8]byte, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordChannelState(ctx context.Context, stateAuthKeyID [8]byte, userID, channelID int64, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordDialogPinned(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peer domain.Peer, pinned bool, folderID int, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordPinnedDialogs(ctx context.Context, stateAuthKeyID [8]byte, userID int64, folderID int, order []domain.Peer, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordSavedDialogPinned(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peer domain.Peer, pinned bool, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordPinnedSavedDialogs(ctx context.Context, stateAuthKeyID [8]byte, userID int64, order []domain.Peer, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordDialogUnreadMark(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peer domain.Peer, unread bool, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordPeerSettings(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peer domain.Peer, settings domain.PeerSettings, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordPeerStoryBlocked(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peer domain.Peer, blocked bool, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordDialogFilter(ctx context.Context, stateAuthKeyID [8]byte, userID int64, folderID int, folder *domain.DialogFolder, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordDialogFilterOrder(ctx context.Context, stateAuthKeyID [8]byte, userID int64, order []int, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordDialogFiltersReload(ctx context.Context, stateAuthKeyID [8]byte, userID int64, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordFolderPeers(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peers []domain.FolderPeerUpdate, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordChannelViewForumAsMessages(ctx context.Context, stateAuthKeyID [8]byte, userID, channelID int64, enabled bool, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordChannelDiscussionInbox(ctx context.Context, stateAuthKeyID [8]byte, userID, channelID int64, topicID, maxID int, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
RecordDraftMessage(ctx context.Context, stateAuthKeyID [8]byte, userID int64, peer domain.Peer, topMsgID int, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
}
// UserEmojiStatusUpdatesService is the optional durable settings-update
// extension used by account.updateEmojiStatus. Keeping it separate preserves
// lightweight test/service implementations of the core UpdatesService.
type UserEmojiStatusUpdatesService interface {
RecordUserEmojiStatus(ctx context.Context, stateAuthKeyID [8]byte, userID int64, status domain.UserEmojiStatus, excludeAuthKeyID [8]byte, excludeSessionID int64) (domain.UpdateEvent, domain.UpdateState, error)
}
// ContactsService 抽象通讯录查询。
type ContactsService interface {
GetContacts(ctx context.Context, userID int64, hash int64) (domain.ContactList, bool, error)
ContactIDs(ctx context.Context, userID int64, hash int64) ([]int, bool, error)
AddContact(ctx context.Context, userID int64, input domain.ContactInput) (domain.Contact, error)
AcceptContact(ctx context.Context, userID, contactUserID int64) (domain.Contact, error)
ImportContacts(ctx context.Context, userID int64, inputs []domain.ContactInput) (domain.ImportContactsResult, error)
Search(ctx context.Context, userID int64, query string, limit int) (domain.UserSearchResult, error)
DeleteContacts(ctx context.Context, userID int64, contactUserIDs []int64) (int, error)
EditCloseFriends(ctx context.Context, userID int64, contactUserIDs []int64) (domain.CloseFriendsEditResult, error)
UpdateContactNote(ctx context.Context, userID, contactUserID int64, note string, entities []domain.MessageEntity) (domain.Contact, error)
SetPersonalPhoto(ctx context.Context, userID, contactUserID int64, photo domain.Photo, date int) (domain.Contact, error)
ClearPersonalPhoto(ctx context.Context, userID, contactUserID int64, date int) (domain.Contact, error)
PersonalPhotos(ctx context.Context, userID int64, contactUserIDs []int64) (map[int64]domain.ProfilePhotoRef, error)
GetPeerSettings(ctx context.Context, userID int64, peer domain.Peer) (domain.PeerSettings, error)
BlockContact(ctx context.Context, userID, peerUserID int64, date int) (bool, error)
UnblockContact(ctx context.Context, userID, peerUserID int64) (bool, error)
IsBlocked(ctx context.Context, userID, peerUserID int64) (bool, error)
GetBlocked(ctx context.Context, userID int64, offset, limit int) (domain.BlockedContactList, error)
}
// DialogsService 抽象会话列表查询。
type DialogsService interface {
GetDialogsHash(ctx context.Context, userID int64, filter domain.DialogFilter) (domain.DialogHashCheck, error)
GetDialogs(ctx context.Context, userID int64, filter domain.DialogFilter) (domain.DialogList, error)
GetPeerDialogs(ctx context.Context, userID int64, peers []domain.Peer) (domain.DialogList, error)
SaveDraft(ctx context.Context, userID int64, draft domain.DialogDraft) (bool, error)
GetDraft(ctx context.Context, userID int64, peer domain.Peer, topMessageID int) (domain.DialogDraft, bool, error)
DeleteDraft(ctx context.Context, userID int64, peer domain.Peer, topMessageID int) (bool, error)
ListDrafts(ctx context.Context, userID int64, limit int) ([]domain.DialogDraft, error)
ClearDrafts(ctx context.Context, userID int64, limit int) ([]domain.DialogDraft, error)
TogglePinned(ctx context.Context, userID int64, peer domain.Peer, pinned bool) (bool, int, error)
ToggleArchivePinned(ctx context.Context, userID int64, pinned bool) (bool, error)
ReorderPinned(ctx context.Context, userID int64, folderID int, order []domain.Peer, force bool) (bool, error)
MarkUnread(ctx context.Context, userID int64, peer domain.Peer, unread bool) (bool, error)
UnreadMarks(ctx context.Context, userID int64) ([]domain.Peer, error)
HidePeerSettingsBar(ctx context.Context, userID int64, peer domain.Peer) (bool, error)
PeerSettingsBarHidden(ctx context.Context, userID int64, peer domain.Peer) (bool, error)
GetDialogFolders(ctx context.Context, userID int64) (domain.DialogFolderList, error)
SaveDialogFolder(ctx context.Context, userID int64, folder domain.DialogFolder) error
DeleteDialogFolder(ctx context.Context, userID int64, folderID int) error
ReorderDialogFolders(ctx context.Context, userID int64, order []int) error
ToggleDialogFolderTags(ctx context.Context, userID int64, enabled bool) error
EditPeerFolders(ctx context.Context, userID int64, peers []domain.FolderPeerUpdate) error
}
// ChatlistsService 抽象 Telegram shared folders / chatlists 业务。
type ChatlistsService interface {
ExportInvite(ctx context.Context, userID int64, filterID int, title string, peers []domain.DialogFolderPeer, date int) (domain.DialogFolder, domain.ChatlistInvite, error)
ListInvites(ctx context.Context, userID int64, filterID int) ([]domain.ChatlistInvite, error)
EditInvite(ctx context.Context, userID int64, filterID int, slug string, title *string, peers *[]domain.DialogFolderPeer, revoke bool) (domain.ChatlistInvite, error)
DeleteInvite(ctx context.Context, userID int64, filterID int, slug string) (domain.DialogFolder, bool, error)
CheckInvite(ctx context.Context, userID int64, slug string) (domain.ChatlistInvitePreview, error)
JoinInvite(ctx context.Context, userID int64, slug string, peers []domain.DialogFolderPeer, date int) (domain.ChatlistJoinResult, error)
GetUpdates(ctx context.Context, userID int64, localFilterID int) (domain.ChatlistUpdates, error)
JoinUpdates(ctx context.Context, userID int64, localFilterID int, peers []domain.DialogFolderPeer, date int) (domain.ChatlistJoinResult, error)
HideUpdates(ctx context.Context, userID int64, localFilterID int) error
Leave(ctx context.Context, userID int64, localFilterID int, peers []domain.DialogFolderPeer, date int) (domain.ChatlistLeaveResult, error)
LeaveSuggestions(ctx context.Context, userID int64, localFilterID int) ([]domain.DialogFolderPeer, error)
}
// MessagesService 抽象消息历史、搜索与已读。
type MessagesService interface {
SendPrivateText(ctx context.Context, userID int64, req domain.SendPrivateTextRequest) (domain.SendPrivateTextResult, error)
SetChatTheme(ctx context.Context, userID int64, req domain.SetPrivateChatThemeRequest) (domain.SetPrivateChatThemeResult, error)
ForwardPrivateMessages(ctx context.Context, userID int64, req domain.ForwardPrivateMessagesRequest) (domain.ForwardPrivateMessagesResult, error)
GetMessages(ctx context.Context, userID int64, ids []int) (domain.MessageList, error)
GetHistory(ctx context.Context, userID int64, filter domain.MessageFilter) (domain.MessageList, error)
Search(ctx context.Context, userID int64, filter domain.MessageFilter) (domain.MessageList, error)
SearchPrivateMedia(ctx context.Context, userID, peerID int64, req domain.MediaSearchRequest) (domain.MessageList, error)
CountPrivateMediaCategories(ctx context.Context, userID, peerID int64) (domain.MediaCategoryCounts, error)
ReadHistory(ctx context.Context, userID int64, req domain.ReadHistoryRequest) (domain.ReadHistoryResult, error)
ReadMessageContents(ctx context.Context, userID int64, req domain.ReadMessageContentsRequest) (domain.ReadMessageContentsResult, error)
GetOutboxReadDate(ctx context.Context, userID int64, req domain.OutboxReadDateRequest) (int, error)
SetMessageReactions(ctx context.Context, userID int64, req domain.SetPrivateMessageReactionsRequest) (domain.PrivateMessageReactionsResult, error)
GetMessageReactions(ctx context.Context, userID int64, req domain.PrivateMessageReactionsRequest) (domain.PrivateMessageReactionsResult, error)
SavedReactionTags(ctx context.Context, userID int64, savedPeer domain.Peer, limit int) ([]domain.SavedReactionTag, error)
UpdateSavedReactionTag(ctx context.Context, userID int64, tag domain.SavedReactionTag) error
VoteMessagePoll(ctx context.Context, userID int64, req domain.VotePrivateMessagePollRequest) (domain.PrivateMessagePollResult, error)
CloseMessagePoll(ctx context.Context, userID int64, req domain.ClosePrivateMessagePollRequest) (domain.PrivateMessagePollResult, error)
ListUnreadReactionMessages(ctx context.Context, userID int64, peer domain.Peer, limit int) ([]domain.Message, error)
ReadPeerReactions(ctx context.Context, userID int64, peer domain.Peer) (int, error)
EditMessage(ctx context.Context, userID int64, req domain.EditMessageRequest) (domain.EditMessageResult, error)
PinPrivateMessage(ctx context.Context, userID int64, req domain.PinPrivateMessageRequest) (domain.PinPrivateMessageResult, error)
UnpinAllPrivateMessages(ctx context.Context, userID int64, req domain.UnpinAllPrivateMessagesRequest) (domain.PinPrivateMessageResult, error)
DeleteMessages(ctx context.Context, userID int64, req domain.DeleteMessagesRequest) (domain.DeleteMessagesResult, error)
DeleteHistory(ctx context.Context, userID int64, req domain.DeleteHistoryRequest) (domain.DeleteMessagesResult, error)
GetSavedDialogs(ctx context.Context, userID int64, filter domain.SavedDialogsFilter) (domain.SavedDialogList, error)
GetPinnedSavedDialogs(ctx context.Context, userID int64) (domain.SavedDialogList, error)
GetSavedDialogsByPeers(ctx context.Context, userID int64, peers []domain.Peer) (domain.SavedDialogList, error)
ToggleSavedDialogPin(ctx context.Context, userID int64, peer domain.Peer, pinned bool) (bool, error)
ReorderPinnedSavedDialogs(ctx context.Context, userID int64, order []domain.Peer, force bool) error
DeleteSavedHistory(ctx context.Context, userID int64, req domain.DeleteSavedHistoryRequest) (domain.DeleteSavedHistoryResult, error)
}
// PrivateNoForwardsService is an optional messages capability used by the
// private-user branch of messages.toggleNoForwards and userFull projection.
type PrivateNoForwardsService interface {
GetPrivateNoForwards(ctx context.Context, userID, peerUserID int64) (domain.PrivateNoForwardsState, error)
TogglePrivateNoForwards(ctx context.Context, userID int64, req domain.TogglePrivateNoForwardsRequest) (domain.TogglePrivateNoForwardsResult, error)
}
// TranslationService owns read-only translation and the durable per-account
// peer preference. It only exposes domain values to the RPC edge.
type TranslationService interface {
Translate(ctx context.Context, req domain.TranslationRequest) (domain.TranslationResult, error)
SetPeerDisabled(ctx context.Context, userID int64, peer domain.Peer, disabled bool) (bool, error)
PeerDisabled(ctx context.Context, userID int64, peer domain.Peer) (bool, error)
}
// AlbumGroupService 是 MessagesService 的可选、生产必备能力sendMultiMedia 在
// 解析任何媒体或落第一条消息前,持久预留整批 random_id 的 grouped_id。
// 单独定义可避免让不触发 sendMultiMedia 的轻量测试替身实现无关方法。
type AlbumGroupService interface {
ReserveAlbumGroup(ctx context.Context, userID int64, req domain.AlbumGroupReservationRequest) (int64, error)
}
// StoriesService 抽象 story 读取、已读、观看与 reaction 状态。
type StoriesService interface {
CreateStory(ctx context.Context, userID int64, req domain.StoryCreateRequest) (domain.StoryCreateResult, error)
GetAllStories(ctx context.Context, viewerUserID int64, hidden bool, now, limit int) (domain.StoryList, error)
GetAllStoriesPage(ctx context.Context, viewerUserID int64, hidden bool, now int, cursor domain.StoryListCursor, limit int) (domain.StoryList, error)
GetAllStoriesDigest(ctx context.Context, viewerUserID int64, hidden bool, now int) (domain.StoryListDigest, error)
ListOwnerActiveStories(ctx context.Context, userID int64, owner domain.Peer, now, limit int) (domain.StoryList, error)
GetPeerStories(ctx context.Context, viewerUserID int64, peer domain.Peer, now int) (domain.PeerStories, error)
GetStoriesByID(ctx context.Context, viewerUserID int64, peer domain.Peer, ids []int, now int) (domain.StoryList, error)
GetStoriesArchive(ctx context.Context, viewerUserID int64, peer domain.Peer, offsetID, limit, now int) (domain.StoryList, error)
GetPinnedStories(ctx context.Context, viewerUserID int64, peer domain.Peer, offsetID, limit, now int) (domain.StoryList, error)
HasPinnedStories(ctx context.Context, viewerUserID int64, peer domain.Peer, now int) (bool, error)
ListReadStates(ctx context.Context, viewerUserID int64) ([]domain.StoryReadState, error)
GetPeerMaxIDs(ctx context.Context, viewerUserID int64, peers []domain.Peer, now int) ([]domain.RecentStory, error)
GetPeerHiddenStates(ctx context.Context, viewerUserID int64, peers []domain.Peer) (map[domain.Peer]bool, error)
GetPeerStoryProjections(ctx context.Context, viewerUserID int64, peers []domain.Peer, now int) ([]domain.PeerStoryProjection, error)
ReadStories(ctx context.Context, viewerUserID int64, peer domain.Peer, maxID, date int) (domain.StoryReadResult, error)
IncrementViews(ctx context.Context, viewerUserID int64, peer domain.Peer, ids []int, date int) (int, error)
SendReaction(ctx context.Context, viewerUserID int64, peer domain.Peer, storyID int, reaction *domain.MessageReaction, date int) (domain.StoryReactionResult, error)
GetStoryViewsList(ctx context.Context, viewerUserID int64, req domain.StoryViewListRequest) (domain.StoryViewList, error)
GetStoryReactionsList(ctx context.Context, viewerUserID int64, req domain.StoryReactionListRequest) (domain.StoryReactionList, error)
GetStoryPublicForwards(ctx context.Context, viewerUserID int64, req domain.StoryPublicForwardListRequest) (domain.StoryPublicForwardList, error)
CanViewStoryStats(ctx context.Context, userID int64, peer domain.Peer) error
ListStoryViewerIDs(ctx context.Context, userID int64, owner domain.Peer, storyID, limit int) ([]int64, error)
EditStory(ctx context.Context, userID int64, req domain.StoryEditRequest) (domain.StoryEditResult, error)
DeleteStories(ctx context.Context, userID int64, peer domain.Peer, ids []int, date int) (domain.StoryMutationResult, error)
TogglePinned(ctx context.Context, userID int64, peer domain.Peer, ids []int, pinned bool, date int) (domain.StoryMutationResult, error)
TogglePinnedToTop(ctx context.Context, userID int64, peer domain.Peer, ids []int) error
TogglePeerStoriesHidden(ctx context.Context, viewerUserID int64, peer domain.Peer, hidden bool) error
CanSendStory(ctx context.Context, viewerUserID int64, peer domain.Peer) (int, error)
}
// ChannelsService 抽象超级群/频道业务。
type ChannelsService interface {
CreateMegagroupFromCreateChat(ctx context.Context, userID int64, req domain.CreateChannelRequest) (domain.CreateChannelResult, error)
CreateChannel(ctx context.Context, userID int64, req domain.CreateChannelRequest) (domain.CreateChannelResult, error)
GetChannel(ctx context.Context, userID, channelID int64) (domain.ChannelView, error)
// ResolveChannel 是 GetChannel 的轻量版(仅访问校验 + Channel/Self跳过 dialog/boost
// 供 inputPeerFor 等只需 access_hash / 频道标志的解析路径用。
ResolveChannel(ctx context.Context, userID, channelID int64) (domain.ChannelView, error)
GetChannels(ctx context.Context, userID int64, channelIDs []int64) ([]domain.ChannelView, error)
GetJoinableChannel(ctx context.Context, userID, channelID int64) (domain.Channel, error)
GetParticipants(ctx context.Context, userID, channelID int64, filter domain.ChannelParticipantsFilter, offset, limit int) (domain.ChannelParticipantList, error)
GetParticipant(ctx context.Context, userID, channelID, participantUserID int64) (domain.ChannelMember, error)
FutureCreatorAfterLeave(ctx context.Context, userID, channelID int64) (domain.ChannelMember, error)
InviteToChannel(ctx context.Context, userID, channelID int64, userIDs []int64, date int) (domain.CreateChannelResult, error)
JoinChannel(ctx context.Context, userID, channelID int64, date int) (domain.CreateChannelResult, error)
LeaveChannel(ctx context.Context, userID, channelID int64, date int) (domain.CreateChannelResult, error)
EditTitle(ctx context.Context, userID int64, req domain.EditChannelTitleRequest) (domain.EditChannelTitleResult, error)
SetWallpaper(ctx context.Context, userID int64, req domain.SetChannelWallpaperRequest) (domain.SetChannelWallpaperResult, error)
EditAbout(ctx context.Context, userID int64, req domain.EditChannelAboutRequest) (domain.Channel, error)
EditAdmin(ctx context.Context, userID int64, req domain.EditChannelAdminRequest) (domain.EditChannelAdminResult, error)
TransferOwnership(ctx context.Context, userID int64, req domain.TransferChannelOwnershipRequest) (domain.TransferChannelOwnershipResult, error)
EditMemberRank(ctx context.Context, userID int64, req domain.EditChannelMemberRankRequest) (domain.EditChannelAdminResult, error)
EditBanned(ctx context.Context, userID int64, req domain.EditChannelBannedRequest) (domain.EditChannelBannedResult, error)
EditDefaultBannedRights(ctx context.Context, userID int64, req domain.EditChannelDefaultBannedRightsRequest) (domain.Channel, error)
DeleteChannel(ctx context.Context, userID int64, req domain.DeleteChannelRequest) (domain.DeleteChannelResult, error)
CheckUsername(ctx context.Context, userID, channelID int64, username string) (bool, error)
UpdateUsername(ctx context.Context, userID int64, req domain.UpdateChannelUsernameRequest) (domain.Channel, error)
ListAdminedPublicChannels(ctx context.Context, userID int64) ([]domain.Channel, error)
ListCommunityLinkableChannels(ctx context.Context, userID int64) ([]domain.Channel, error)
ListStoryPostableChannels(ctx context.Context, userID int64) ([]domain.Channel, error)
ListSendAsChannels(ctx context.Context, userID int64) ([]domain.Channel, error)
ResolvePublicUsername(ctx context.Context, userID int64, username string) (domain.Channel, bool, error)
SearchPublicChannels(ctx context.Context, userID int64, query string, limit int) (domain.PublicChannelSearchResult, error)
SetSignatures(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetPhoto(ctx context.Context, userID, channelID int64, photo *domain.Photo, date int) (domain.SetChannelPhotoResult, error)
SetPreHistoryHidden(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetParticipantsHidden(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetForum(ctx context.Context, userID, channelID int64, enabled, tabs bool) (domain.Channel, error)
SetAutotranslation(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetRestrictedSponsored(ctx context.Context, userID, channelID int64, restricted bool) (domain.Channel, error)
SetPaidMessagesPrice(ctx context.Context, userID, channelID int64, stars int64, broadcastMessagesAllowed bool) (domain.ChannelPaidMessagesPriceResult, error)
SetAntiSpam(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetSlowMode(ctx context.Context, userID, channelID int64, seconds int) (domain.Channel, error)
SetBoostsToUnblockRestrictions(ctx context.Context, userID, channelID int64, boosts int) (domain.Channel, error)
SetNoForwards(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetJoinToSend(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetJoinRequest(ctx context.Context, userID, channelID int64, enabled bool) (domain.Channel, error)
SetAvailableReactions(ctx context.Context, userID, channelID int64, policy domain.ChannelReactionPolicy) (domain.Channel, error)
SetColor(ctx context.Context, userID, channelID int64, forProfile bool, color domain.ChannelPeerColor) (domain.Channel, error)
SetEmojiStatus(ctx context.Context, userID, channelID int64, status domain.ChannelEmojiStatus) (domain.Channel, error)
ListAdminLog(ctx context.Context, userID int64, req domain.ChannelAdminLogRequest) (domain.ChannelAdminLogResult, error)
GetChannelForChangeInfo(ctx context.Context, userID, channelID int64) (domain.ChannelView, error)
SaveDefaultSendAs(ctx context.Context, userID int64, req domain.SaveChannelDefaultSendAsRequest) (domain.ChannelView, error)
GetMessageViews(ctx context.Context, userID int64, req domain.ChannelMessageViewsRequest) (domain.ChannelMessageViewsResult, error)
SetMessageReactions(ctx context.Context, userID int64, req domain.SetChannelMessageReactionsRequest) (domain.ChannelMessageReactionsResult, error)
GetMessageReactions(ctx context.Context, userID int64, req domain.ChannelMessageReactionsRequest) (domain.ChannelMessageReactionsResult, error)
VoteMessagePoll(ctx context.Context, userID int64, req domain.VoteChannelMessagePollRequest) (domain.ChannelMessagePollResult, error)
CloseMessagePoll(ctx context.Context, userID int64, req domain.CloseChannelMessagePollRequest) (domain.ChannelMessagePollResult, error)
ListMessageReactions(ctx context.Context, userID int64, req domain.ChannelMessageReactionsListRequest) (domain.ChannelMessageReactionsList, error)
FindMessageReaction(ctx context.Context, userID int64, req domain.ChannelMessageReactionLookupRequest) (domain.ChannelMessageReactionLookup, bool, error)
TopReactions(ctx context.Context, userID int64, limit int) ([]domain.MessageReaction, error)
RecentReactions(ctx context.Context, userID int64, limit int) ([]domain.MessageReaction, error)
ClearRecentReactions(ctx context.Context, userID int64) error
GetPremiumBoostStatus(ctx context.Context, userID, channelID int64, now int) (domain.PremiumBoostStatus, error)
ListPremiumBoosts(ctx context.Context, userID, channelID int64, gifts bool, offset string, limit, now int) (domain.PremiumBoostList, error)
GetPremiumMyBoosts(ctx context.Context, userID int64, now, premiumUntil int) (domain.PremiumMyBoosts, error)
ApplyPremiumBoost(ctx context.Context, userID, channelID int64, slots []int, now, premiumUntil int) (domain.PremiumMyBoosts, error)
GetPremiumUserBoosts(ctx context.Context, userID, channelID, targetUserID int64, now int) (domain.PremiumBoostList, error)
ReadMessageContents(ctx context.Context, userID int64, req domain.ReadChannelMessageContentsRequest) (domain.ReadChannelMessageContentsResult, error)
GetMessageAuthor(ctx context.Context, userID int64, req domain.GetChannelMessageAuthorRequest) (domain.GetChannelMessageAuthorResult, error)
CreateForumTopic(ctx context.Context, userID int64, req domain.CreateChannelForumTopicRequest) (domain.CreateChannelForumTopicResult, error)
EditForumTopic(ctx context.Context, userID int64, req domain.EditChannelForumTopicRequest) (domain.EditChannelForumTopicResult, error)
UpdatePinnedForumTopic(ctx context.Context, userID int64, req domain.UpdateChannelForumTopicPinnedRequest) (domain.UpdateChannelForumTopicPinnedResult, error)
ReorderPinnedForumTopics(ctx context.Context, userID int64, req domain.ReorderChannelPinnedForumTopicsRequest) (domain.ReorderChannelPinnedForumTopicsResult, error)
DeleteForumTopicHistory(ctx context.Context, userID int64, req domain.DeleteChannelForumTopicHistoryRequest) (domain.DeleteChannelHistoryResult, error)
GetForumTopics(ctx context.Context, userID int64, filter domain.ChannelForumTopicFilter) (domain.ChannelForumTopicList, error)
GetForumTopicsByID(ctx context.Context, userID, channelID int64, ids []int) (domain.ChannelForumTopicList, error)
SendMessage(ctx context.Context, userID int64, req domain.SendChannelMessageRequest) (domain.SendChannelMessageResult, error)
EditMessage(ctx context.Context, userID int64, req domain.EditChannelMessageRequest) (domain.EditChannelMessageResult, error)
GetInlineBotMessage(ctx context.Context, botID, channelID int64, id int) (domain.Channel, domain.ChannelMessage, bool, error)
ListStoryMessageForwards(ctx context.Context, userID int64, req domain.StoryMessageForwardListRequest) (domain.StoryMessageForwardList, error)
EditInlineBotMessage(ctx context.Context, botID int64, req domain.EditChannelMessageRequest) (domain.EditChannelMessageResult, error)
DeleteMessages(ctx context.Context, userID int64, req domain.DeleteChannelMessagesRequest) (domain.DeleteChannelMessagesResult, error)
DeleteHistory(ctx context.Context, userID int64, req domain.DeleteChannelHistoryRequest) (domain.DeleteChannelHistoryResult, error)
DeleteParticipantHistory(ctx context.Context, userID int64, req domain.DeleteChannelParticipantHistoryRequest) (domain.DeleteChannelHistoryResult, error)
UpdatePinnedMessage(ctx context.Context, userID int64, req domain.UpdateChannelPinnedMessageRequest) (domain.UpdateChannelPinnedMessageResult, error)
UnpinAllMessages(ctx context.Context, userID int64, req domain.UnpinAllChannelMessagesRequest) (domain.UpdateChannelPinnedMessageResult, error)
ClearDanglingPinnedMessage(ctx context.Context, channelID int64, messageID int) error
ExportInvite(ctx context.Context, userID int64, req domain.ExportChannelInviteRequest) (domain.ExportChannelInviteResult, error)
CheckInvite(ctx context.Context, userID int64, hash string, date int) (domain.CheckChannelInviteResult, error)
ImportInvite(ctx context.Context, userID int64, req domain.ImportChannelInviteRequest) (domain.CreateChannelResult, error)
ListExportedInvites(ctx context.Context, userID int64, req domain.ChannelInviteListRequest) (domain.ChannelInviteList, error)
GetExportedInvite(ctx context.Context, userID int64, req domain.GetChannelInviteRequest) (domain.ChannelInvite, error)
EditExportedInvite(ctx context.Context, userID int64, req domain.EditChannelInviteRequest) (domain.EditChannelInviteResult, error)
DeleteExportedInvite(ctx context.Context, userID int64, req domain.DeleteChannelInviteRequest) error
DeleteRevokedExportedInvites(ctx context.Context, userID int64, req domain.DeleteRevokedChannelInvitesRequest) error
ListAdminsWithInvites(ctx context.Context, userID, channelID int64) ([]domain.ChannelAdminInviteCount, error)
ListInviteImporters(ctx context.Context, userID int64, req domain.ChannelInviteImportersRequest) (domain.ChannelInviteImporterList, error)
PendingJoinRequests(ctx context.Context, channelID int64, limit int) (domain.ChannelPendingJoinRequests, error)
HideChatJoinRequest(ctx context.Context, userID int64, req domain.HideChannelJoinRequestRequest) (domain.CreateChannelResult, error)
HideAllChatJoinRequests(ctx context.Context, userID int64, req domain.HideChannelJoinRequestsRequest) (domain.CreateChannelResult, error)
CommonChannels(ctx context.Context, userID int64, req domain.CommonChannelsRequest) (domain.CommonChannelsResult, error)
LeftChannels(ctx context.Context, userID int64, offset, limit int) (domain.LeftChannelsResult, error)
InactiveChannels(ctx context.Context, userID int64, limit int) (domain.ChannelDialogList, error)
ChannelRecommendations(ctx context.Context, userID int64, req domain.ChannelRecommendationsRequest) (domain.ChannelRecommendationsResult, error)
DiscussionGroups(ctx context.Context, userID int64, limit int) ([]domain.Channel, error)
SetDiscussionGroup(ctx context.Context, userID, broadcastID, groupID int64) (domain.DiscussionGroupUpdateResult, error)
SetViewForumAsMessages(ctx context.Context, userID, channelID int64, enabled bool) (bool, error)
GetHistory(ctx context.Context, userID int64, filter domain.ChannelHistoryFilter) (domain.ChannelHistory, error)
SearchChannelMedia(ctx context.Context, userID, channelID int64, req domain.MediaSearchRequest) (domain.ChannelHistory, error)
CountChannelMediaCategories(ctx context.Context, userID, channelID int64) (domain.MediaCategoryCounts, error)
GetStats(ctx context.Context, userID int64, req domain.ChannelStatsRequest) (domain.ChannelStats, error)
GetMessageStats(ctx context.Context, userID int64, req domain.ChannelMessageStatsRequest) (domain.ChannelMessageStats, error)
ListMessagePublicForwards(ctx context.Context, userID int64, req domain.ChannelMessagePublicForwardListRequest) (domain.ChannelMessagePublicForwardList, error)
SearchPosts(ctx context.Context, userID int64, req domain.ChannelSearchPostsRequest) (domain.ChannelHistory, error)
SearchJoinedMessages(ctx context.Context, userID int64, req domain.ChannelGlobalSearchRequest) (domain.ChannelHistory, error)
GetMessages(ctx context.Context, userID, channelID int64, ids []int) (domain.ChannelHistory, error)
ChannelPollFanoutViews(ctx context.Context, channelID int64, msgID int, viewers []int64, now int) (map[int64]*domain.MessagePoll, error)
GetReplies(ctx context.Context, userID int64, filter domain.ChannelRepliesFilter) (domain.ChannelHistory, error)
GetUnreadMentions(ctx context.Context, userID int64, filter domain.ChannelUnreadMentionsFilter) (domain.ChannelHistory, error)
ReadMentions(ctx context.Context, userID int64, req domain.ReadChannelMentionsRequest) (domain.ReadChannelMentionsResult, error)
GetUnreadReactions(ctx context.Context, userID int64, filter domain.ChannelUnreadReactionsFilter) (domain.ChannelHistory, error)
ReadReactions(ctx context.Context, userID int64, req domain.ReadChannelReactionsRequest) (domain.ReadChannelReactionsResult, error)
GetDiscussionMessage(ctx context.Context, userID, channelID int64, msgID int) (domain.ChannelDiscussionMessage, error)
SendMonoforumMessage(ctx context.Context, req domain.SendMonoforumMessageRequest) (domain.SendChannelMessageResult, error)
ListMonoforumHistory(ctx context.Context, filter domain.MonoforumHistoryFilter) (domain.ChannelHistory, error)
ListMonoforumDialogs(ctx context.Context, filter domain.MonoforumDialogsFilter) (domain.MonoforumDialogList, error)
ResolveMonoforumSend(ctx context.Context, viewerUserID, monoforumID int64) (domain.Channel, bool, error)
ReadHistory(ctx context.Context, userID int64, req domain.ReadChannelHistoryRequest) (domain.ReadChannelHistoryResult, error)
ReadTopicHistory(ctx context.Context, userID int64, req domain.ReadChannelTopicHistoryRequest) (domain.ReadChannelTopicHistoryResult, error)
GeneralForumTopic(ctx context.Context, userID, channelID int64) (domain.ChannelForumTopic, error)
GetMessageReadParticipants(ctx context.Context, userID int64, req domain.ChannelReadParticipantsRequest) (domain.ChannelReadParticipantsResult, error)
GetDifference(ctx context.Context, userID int64, req domain.ChannelDifferenceRequest) (domain.ChannelDifference, error)
ActiveChannelIDsForUser(ctx context.Context, userID, afterChannelID int64, limit int) ([]int64, error)
DirtyActiveChannelsForUser(ctx context.Context, userID int64, sinceDate int, afterChannelID int64, limit int) ([]domain.DirtyChannel, error)
ActiveMemberIDs(ctx context.Context, userID, channelID int64, limit int) ([]int64, error)
// SetActiveCall / AppendCallServiceMessage 是群通话模块的频道侧挂接点。
SetActiveCall(ctx context.Context, channelID, callID, callAccessHash int64, notEmpty bool) (domain.Channel, error)
AppendCallServiceMessage(ctx context.Context, channelID, senderUserID int64, date int, action domain.ChannelMessageAction) (domain.SendChannelMessageResult, error)
InviteAdminMemberIDs(ctx context.Context, channelID int64, limit int) ([]int64, error)
FilterActiveMemberIDs(ctx context.Context, channelID int64, userIDs []int64) ([]int64, error)
}
// ChannelMessageAudienceService is the optional production authorization
// boundary for public short-poll subscribers. Lightweight test/domain adapters
// that only model joined members may omit it and retain member-only behavior.
type ChannelMessageAudienceService interface {
FilterMessageAudienceIDs(ctx context.Context, channelID int64, userIDs []int64) ([]int64, error)
}
// ChannelAuthoritativeProjectionService bypasses long-lived channel read
// models for a durable channel_state refresh emitted by an admin mutation.
type ChannelAuthoritativeProjectionService interface {
GetChannelsAuthoritative(ctx context.Context, userID int64, channelIDs []int64) ([]domain.ChannelView, error)
}
// CommunitiesService abstracts the Layer 228 Community aggregation domain.
// Community containers never expose tg types and never own message/read/pts state.
type CommunitiesService interface {
Create(ctx context.Context, userID int64, req domain.CreateCommunityRequest) (domain.CommunityView, error)
Get(ctx context.Context, userID, communityID int64) (domain.CommunityView, error)
GetMany(ctx context.Context, userID int64, ids []int64) ([]domain.CommunityView, error)
ListJoined(ctx context.Context, userID int64) ([]domain.CommunityView, error)
TogglePeerLink(ctx context.Context, userID int64, req domain.CommunityTogglePeerLinkRequest) (domain.CommunityTogglePeerLinkResult, error)
SetCollapsed(ctx context.Context, userID, communityID int64, collapsed bool) (domain.CommunityView, bool, error)
ListPeerLinkRequests(ctx context.Context, userID, communityID int64, offset string, limit int) (domain.CommunityPeerLinkRequestPage, error)
DecidePeerLinkRequest(ctx context.Context, userID, communityID int64, peer domain.Peer, reject bool, date int) (domain.CommunityTogglePeerLinkResult, error)
DecideAllPeerLinkRequests(ctx context.Context, userID, communityID int64, reject bool, date int) ([]domain.CommunityTogglePeerLinkResult, error)
ToggleParticipantBanned(ctx context.Context, userID, communityID, participantUserID int64, unban bool, date int) (domain.CommunityParticipantBanResult, error)
ParticipantJoinedChats(ctx context.Context, userID, communityID, participantUserID int64) (domain.CommunityParticipantJoinedChats, error)
Participants(ctx context.Context, userID, communityID int64, filter domain.ChannelParticipantsFilter, offset, limit int) (domain.CommunityParticipantList, error)
EditTitle(ctx context.Context, userID, communityID int64, title string) (domain.CommunityView, bool, error)
EditAbout(ctx context.Context, userID, communityID int64, about string) (domain.CommunityView, bool, error)
EditAdmin(ctx context.Context, userID int64, req domain.CommunityEditAdminRequest) (domain.CommunityView, bool, error)
EditDefaultBannedRights(ctx context.Context, userID, communityID int64, rights domain.ChannelBannedRights) (domain.CommunityView, bool, error)
SetPhoto(ctx context.Context, userID, communityID int64, photo *domain.Photo, date int) (domain.CommunityView, bool, error)
Delete(ctx context.Context, userID, communityID int64, date int) (domain.CommunityView, []domain.Peer, error)
SetPinned(ctx context.Context, userID, communityID int64, pinned bool) (bool, error)
ReorderPinned(ctx context.Context, userID int64, order []domain.Peer, force bool) (bool, error)
SearchScope(ctx context.Context, userID, communityID int64) (domain.CommunitySearchScope, error)
}
// FilesService 抽象文件上传分片、下载与媒体document/photo组装。
// 方法只用 domain 类型rpc 层负责 tg.InputFileLocation / InputMedia ↔ domain 转换。
type FilesService interface {
SaveFilePart(ctx context.Context, ownerUserID, fileID int64, part int, bytes []byte) (bool, error)
SaveBigFilePart(ctx context.Context, ownerUserID, fileID int64, part, totalParts int, bytes []byte) (bool, error)
GetFile(ctx context.Context, req domain.FileDownloadRequest) (domain.FileChunk, bool, error)
// CreateEncryptedFileFromUpload 把密聊上传分片组装成盲 blob 并铸造 EncryptedFile 快照P2
CreateEncryptedFileFromUpload(ctx context.Context, file domain.UploadedFileRef, keyFingerprint int) (domain.EncryptedFileRef, error)
// GeoMapTile 渲染 geo 消息地图缩略占位图upload.getWebFile确定性、无外部依赖。
GeoMapTile(lat, long float64, w, h, zoom, scale int) ([]byte, string)
// 资源读取reaction / sticker / document
ListAvailableReactions(ctx context.Context) ([]domain.AvailableReaction, error)
AvailableEffects(ctx context.Context) ([]domain.AvailableEffect, int, error)
GetDocuments(ctx context.Context, ids []int64) ([]domain.Document, error)
ResolveStickerSet(ctx context.Context, ref domain.StickerSetRef) (set domain.StickerSet, documents []domain.Document, found bool, err error)
ListStickerSets(ctx context.Context, kind domain.StickerSetKind) ([]domain.StickerSet, error)
CheckStickerSetShortName(ctx context.Context, shortName string) (bool, error)
SuggestStickerSetShortName(ctx context.Context, title string, userID int64) (string, error)
CreateStickerSet(ctx context.Context, req domain.CreateStickerSetRequest) (domain.StickerSet, []domain.Document, error)
ListCreatedStickerSets(ctx context.Context, userID int64, offsetID int64, limit int) ([]domain.StickerSet, int, error)
AddStickerToSet(ctx context.Context, actorUserID int64, ref domain.StickerSetRef, item domain.StickerSetItemInput) (domain.StickerSet, []domain.Document, error)
RemoveStickerFromSet(ctx context.Context, actorUserID int64, documentID int64, accessHash int64) (domain.StickerSet, []domain.Document, error)
ChangeStickerPosition(ctx context.Context, actorUserID int64, documentID int64, accessHash int64, position int) (domain.StickerSet, []domain.Document, error)
RenameStickerSet(ctx context.Context, actorUserID int64, ref domain.StickerSetRef, title string) (domain.StickerSet, []domain.Document, error)
DeleteStickerSet(ctx context.Context, actorUserID int64, ref domain.StickerSetRef) (domain.StickerSetKind, error)
// 头像profile photo与消息媒体组装。
CreatePhotoFromUpload(ctx context.Context, file domain.UploadedFileRef) (domain.Photo, error)
CreatePhotoFromBytes(ctx context.Context, data []byte) (domain.Photo, error)
// CreatePhotoFromURL / CreateDocumentFromURL 抓取外链媒体inputMediaPhoto/DocumentExternal
// SSRF 安全;未启用返回 ErrExternalMediaDisabled。
CreatePhotoFromURL(ctx context.Context, rawURL string) (domain.Photo, error)
CreateDocumentFromURL(ctx context.Context, rawURL string) (domain.Document, error)
// ResolveWebPage 解析链接预览messages.getWebPagePreview / 发送挂卡片SSRF 安全,
// 经 L1+L3 去重缓存。未启用返回 ErrWebPagePreviewDisabled瞬时失败返回错误调用方降级
ResolveWebPage(ctx context.Context, rawURL string) (domain.MessageWebPage, error)
// WebPagePreviewEnabled 报告是否启用链接预览;未启用时发送不挂 pending 占位(否则会永久 pending
WebPagePreviewEnabled() bool
// LookupWebPage 仅查缓存(不抓取)返回已解析的链接预览。发送时用它:若客户端输入时
// getWebPagePreview 已解析过,则 echo 直接带 done 卡片(与官方一致),不依赖异步换卡。
LookupWebPage(ctx context.Context, rawURL string) (domain.MessageWebPage, bool)
CreateAvatarFromUpload(ctx context.Context, file domain.UploadedFileRef) (domain.Photo, error)
CreateAvatarVideoFromUpload(ctx context.Context, file domain.UploadedFileRef, videoStartTs float64) (domain.Photo, error)
CreateAvatarVideoMarkupFromUpload(ctx context.Context, file domain.UploadedFileRef, videoStartTs float64, markup domain.PhotoSize) (domain.Photo, error)
CreateAvatarMarkup(ctx context.Context, size domain.PhotoSize) (domain.Photo, error)
CreateDocumentFromUpload(ctx context.Context, file domain.UploadedFileRef, spec domain.DocumentSpec) (domain.Document, error)
CreateDocumentFromBytes(ctx context.Context, data []byte, spec domain.DocumentSpec) (domain.Document, error)
GetPhoto(ctx context.Context, id int64) (domain.Photo, bool, error)
GetDocument(ctx context.Context, id int64) (domain.Document, bool, error)
UploadProfilePhoto(ctx context.Context, ownerType domain.PeerType, ownerID int64, file domain.UploadedFileRef, date int) (domain.Photo, error)
UploadProfilePhotoKind(ctx context.Context, ownerType domain.PeerType, ownerID int64, kind domain.ProfilePhotoKind, file domain.UploadedFileRef, date int) (domain.Photo, error)
SetCurrentProfilePhoto(ctx context.Context, ownerType domain.PeerType, ownerID, photoID int64, date int) (domain.Photo, bool, error)
SetCurrentProfilePhotoKind(ctx context.Context, ownerType domain.PeerType, ownerID int64, kind domain.ProfilePhotoKind, photoID int64, date int) (domain.Photo, bool, error)
CurrentProfilePhoto(ctx context.Context, ownerType domain.PeerType, ownerID int64) (domain.Photo, bool, error)
CurrentProfilePhotoKind(ctx context.Context, ownerType domain.PeerType, ownerID int64, kind domain.ProfilePhotoKind) (domain.Photo, bool, error)
GetProfilePhotos(ctx context.Context, ownerType domain.PeerType, ownerID int64, offset, limit int, maxID int64) (photos []domain.Photo, total int, err error)
GetProfilePhotosKind(ctx context.Context, ownerType domain.PeerType, ownerID int64, kind domain.ProfilePhotoKind, offset, limit int, maxID int64) (photos []domain.Photo, total int, err error)
DeleteProfilePhotos(ctx context.Context, ownerType domain.PeerType, ownerID int64, photoIDs []int64) (int, error)
DeleteProfilePhotosKind(ctx context.Context, ownerType domain.PeerType, ownerID int64, kind domain.ProfilePhotoKind, photoIDs []int64) (int, error)
}
// LangPackService 抽象客户端语言包查询。
type LangPackService interface {
GetLangPack(ctx context.Context, langPack, langCode string) (domain.LangPack, error)
GetDifference(ctx context.Context, langPack, langCode string, fromVersion int) (domain.LangPack, error)
GetStrings(ctx context.Context, langPack, langCode string, keys []string) (domain.LangPack, error)
ListLanguages(ctx context.Context, langPack string) ([]domain.LangPackLanguage, error)
}
// AIComposeService 抽象客户端输入框 AI 改写/润色与 aicompose tones 目录。
// 这里只使用 domain DTOrpc 层负责 tg.TextWithEntities/InputAiComposeTone ↔ domain 转换。
type AIComposeService interface {
ListTones(ctx context.Context, userID, hash int64) (domain.AIComposeTones, bool, error)
GetTone(ctx context.Context, userID int64, ref domain.AIComposeToneRef) (domain.AIComposeTones, error)
CreateTone(ctx context.Context, input domain.AIComposeToneInput) (domain.AIComposeTone, error)
UpdateTone(ctx context.Context, update domain.AIComposeToneUpdate) (domain.AIComposeTone, error)
SaveTone(ctx context.Context, userID int64, ref domain.AIComposeToneRef, unsave bool) error
DeleteTone(ctx context.Context, userID int64, ref domain.AIComposeToneRef) error
GetToneExample(ctx context.Context, userID int64, ref domain.AIComposeToneRef, num int) (domain.AIComposeToneExample, error)
Compose(ctx context.Context, req domain.AIComposeRequest) (domain.AIComposeResult, error)
}
// EphemeralService owns Layer 228 short-lived bot/member state. It must never
// write ordinary messages, dialogs, pts/qts/seq logs or durable update outbox.
type EphemeralService interface {
SendFromClient(ctx context.Context, request domain.SendClientEphemeralRequest) (domain.EphemeralMessage, bool, error)
SendFromBot(ctx context.Context, request domain.SendBotEphemeralRequest) (domain.EphemeralMessage, bool, error)
SendFromBotLazy(ctx context.Context, request domain.SendBotEphemeralRequest, build func(context.Context) (domain.EphemeralContent, error)) (domain.EphemeralMessage, bool, error)
EditFromBot(ctx context.Context, botUserID int64, peer domain.Peer, id int, content domain.EphemeralContent) (domain.EphemeralMessage, error)
EditFieldsFromBot(ctx context.Context, botUserID, receiverUserID int64, peer domain.Peer, id int, mode domain.EphemeralEditMode, fields domain.EditEphemeralFields) (domain.EphemeralMessage, error)
EditFieldsFromBotLazy(ctx context.Context, botUserID, receiverUserID int64, peer domain.Peer, id int, mode domain.EphemeralEditMode, build func(context.Context) (domain.EditEphemeralFields, error)) (domain.EphemeralMessage, error)
Delete(ctx context.Context, actorUserID, receiverUserID int64, peer domain.Peer, id int) (domain.EphemeralMessage, bool, error)
DeleteFromDevice(ctx context.Context, actorUserID, receiverUserID int64, device domain.EphemeralDevice, peer domain.Peer, id int) (domain.EphemeralMessage, bool, error)
Callback(ctx context.Context, userID int64, device domain.EphemeralDevice, peer domain.Peer, id int, data []byte) (domain.EphemeralCallback, error)
PutCallbackAction(ctx context.Context, action domain.EphemeralCallbackAction) (bool, error)
ReportTarget(ctx context.Context, userID int64, device domain.EphemeralDevice, peer domain.Peer, id int) (domain.EphemeralMessage, error)
}
// WelcomeMessageService owns the independent durable Layer 229 peer templates.
// It has no transient device, PTS, difference, push or outbox responsibility.
type WelcomeMessageService interface {
Authorize(ctx context.Context, userID int64, peer domain.Peer) error
Create(ctx context.Context, userID int64, peer domain.Peer, randomID int64, content domain.WelcomeMessageContent) (domain.WelcomeMessage, bool, error)
Edit(ctx context.Context, userID int64, peer domain.Peer, id int, fields domain.WelcomeMessageEditFields) (domain.WelcomeMessage, error)
List(ctx context.Context, userID int64, peer domain.Peer, hash int64) (domain.WelcomeMessageList, error)
Delete(ctx context.Context, userID int64, peer domain.Peer, id int) (bool, error)
DeleteAll(ctx context.Context, userID int64, peer domain.Peer) (bool, error)
HasAny(ctx context.Context, peer domain.Peer) (bool, error)
}
// ModerationService accepts only final report choices. Implementations must
// validate and snapshot referenced evidence, then durably commit the immutable
// submission before returning success.
type ModerationService interface {
ReportPeer(ctx context.Context, reporterUserID int64, source domain.ModerationReportSource, target domain.Peer, reason domain.ModerationReason, option, comment string, createdAt time.Time) (domain.ModerationReport, bool, error)
ReportMessages(ctx context.Context, req domain.ModerationMessageReportRequest) (domain.ModerationReport, bool, error)
ReportProfilePhoto(ctx context.Context, req domain.ModerationProfilePhotoReportRequest) (domain.ModerationReport, bool, error)
ReportChannelSpam(ctx context.Context, req domain.ModerationChannelSpamReportRequest) (domain.ModerationReport, bool, error)
ReportReaction(ctx context.Context, req domain.ModerationReactionReportRequest) (domain.ModerationReport, bool, error)
ReportEncryptedSpam(ctx context.Context, reporterUserID int64, chat domain.SecretChat, createdAt time.Time) (domain.ModerationReport, bool, error)
ReportStories(ctx context.Context, req domain.ModerationStoryReportRequest) (domain.ModerationReport, bool, error)
ReportEphemeral(ctx context.Context, reporterUserID int64, target domain.EphemeralMessage, reason domain.ModerationReason, option, comment string, createdAt time.Time) (domain.ModerationReport, bool, error)
SponsoredImpression(ctx context.Context, userID int64, randomID []byte, now time.Time) (domain.SponsoredMessageImpression, error)
ReportSponsored(ctx context.Context, userID int64, randomID []byte, reason domain.ModerationReason, option string, now time.Time) (domain.ModerationReport, bool, error)
ReportAntiSpamFalsePositive(ctx context.Context, reporterUserID, channelID int64, messageID int, now time.Time) (domain.ModerationReport, bool, error)
}
// PremiumPromoService exposes the immutable promo media catalog through a
// domain-only boundary. File bytes remain served by upload.getFile through the
// ordinary Files service.
type PremiumPromoService interface {
PremiumPromo(ctx context.Context) (domain.PremiumPromoCatalog, bool, error)
}
// UsernameRegistryService is the collectible (Fragment-style) username registry
// boundary. It owns the full per-peer username list -- the editable slot the
// client owns through account/channels.updateUsername plus every collectible
// asset attached to the peer -- and the purchase record behind a collectible.
//
// The registry is deliberately optional. Every RPC surface that consults it must
// degrade to the legacy single-editable-username behaviour when the field is nil
// or a call fails, because the scalar users.username / channels.username column
// remains the editable-name persistence slot.
type UsernameRegistryService interface {
// PeerUsernames returns one peer's full username list. Order is irrelevant:
// callers project through domain.SortUsernames.
PeerUsernames(ctx context.Context, peer domain.Peer) ([]domain.Username, error)
// UsernamesBatch is the N+1-free variant used by list projections. Peers with
// no registry row may be omitted from the result map.
UsernamesBatch(ctx context.Context, peers []domain.Peer) (map[domain.Peer][]domain.Username, error)
// ToggleUsername activates/deactivates one collectible username. The bool
// reports whether anything changed; false maps to USERNAME_NOT_MODIFIED.
ToggleUsername(ctx context.Context, peer domain.Peer, username string, active bool) (bool, error)
// ReorderUsernames rewrites the active username display order, including the
// editable slot when present (domain.ValidateUsernameReorder).
ReorderUsernames(ctx context.Context, peer domain.Peer, order []string) (bool, error)
// DeactivateAllUsernames deactivates every collectible username of the peer.
DeactivateAllUsernames(ctx context.Context, peer domain.Peer) (bool, error)
// Collectible returns the asset and owner needed by the RPC edge to enforce
// fragment visibility before projecting fragment.collectibleInfo.
Collectible(ctx context.Context, username string) (domain.CollectibleUsername, error)
}
// BotVerificationService is the third-party bot verification boundary
// (core.telegram.org/api/bots/verification): a verifier bot marking peers with its
// own icon and description, which official clients render as a badge distinct from
// the operator-granted checkmark.
//
// It is the single source for every surface that projects the feature --
// user.bot_verification_icon, channel.bot_verification_icon,
// userFull.bot_verification, channelFull.bot_verification,
// chatInvite.bot_verification and botInfo.verifier_settings -- so no two responses
// can disagree about which mark a peer carries.
//
// Optional like UsernameRegistryService: a nil field, or
// any read error, must leave every flag unset and bots.setCustomVerification
// answering BOT_VERIFIER_FORBIDDEN, which is exactly the pre-feature wire shape.
type BotVerificationService interface {
// PeerVerification returns the peer's single mark, or
// domain.ErrCustomVerificationNotFound.
PeerVerification(ctx context.Context, peer domain.Peer) (domain.CustomVerification, error)
// PeerVerificationBatch is the N+1-free variant used by the response-boundary
// overlay. Peers without a mark may be omitted from the result map.
PeerVerificationBatch(ctx context.Context, peers []domain.Peer) (map[domain.Peer]domain.CustomVerification, error)
// VerifierSettings reads one bot's verifier status, or
// domain.ErrVerifierNotFound.
VerifierSettings(ctx context.Context, botID int64) (domain.BotVerifierSettings, error)
// VerifierSettingsBatch resolves several bots at once for the botInfo
// projection; bots without verifier status may be omitted.
VerifierSettingsBatch(ctx context.Context, botIDs []int64) (map[int64]domain.BotVerifierSettings, error)
// SetCustomVerification applies bots.setCustomVerification. changed controls
// update fan-out only; the RPC returns Bool true for an idempotent success.
SetCustomVerification(ctx context.Context, req domain.SetCustomVerificationRequest) (changed bool, err error)
}
// Deps 按业务域注入服务接口。各域的 handler 注册见对应文件auth.go / users.go / updates.go
type Deps struct {
Auth AuthService
AuthDeliveryReports AuthDeliveryReportService
ClientTelemetry ClientTelemetryService
// AuthKeySessionLayers is the protocol-only durable ordering boundary for
// explicit invokeWithLayer evidence. Production must wire the same auth-key
// store used by the MTProto edge; nil is reserved for isolated router tests.
AuthKeySessionLayers store.AuthKeySessionLayerStore
ReadModelVersions store.ReadModelVersionStore
UserProjectionFacts UserProjectionFactInvalidator
Account AccountService
Privacy PrivacyService
Help HelpService
AppUpdates updatecdn.Resolver
AccountFreeze AccountFreezeService
AccountFreezeNotifications AccountFreezeNotificationService
AICompose AIComposeService
Ephemeral EphemeralService
EphemeralPush store.EphemeralPushBroker
WelcomeMessages WelcomeMessageService
Moderation ModerationService
Users UsersService
Usernames UsernameRegistryService
BotVerifications BotVerificationService
TelegramLogin TelegramLoginService
Updates UpdatesService
BootstrapUpdates store.BootstrapUpdateJobStore
BotAPIUpdates store.BotAPIUpdateStore
BotCallbacks store.BotCallbackRegistryStore
Contacts ContactsService
Dialogs DialogsService
Chatlists ChatlistsService
Messages MessagesService
Translation TranslationService
Stories StoriesService
Channels ChannelsService
Communities CommunitiesService
Files FilesService
PremiumPromo PremiumPromoService
Bots BotsService
ServiceBotCallbacks ServiceBotCallbacks
ServiceBotInlineResults ServiceBotInlineResults
Polls PollsService
Phone PhoneService
GroupCalls GroupCallsService
LiveStreams LiveStreamsService
SFU sfu.Service
TURN turnsrv.Service
LangPack LangPackService
Sessions SessionBinder
Inline store.InlineRegistryStore
Limiter RateLimiter
Metrics Metrics
SecretChats SecretChatService
Passkey PasskeyService
Themes ThemeService
}
// ThemeService 抽象自定义云主题(app/themes):创建/更新/查询主题 + 维护每用户已安装列表。
// 文件上传(uploadTheme)由 rpc 层直接经 Files 完成,不经此接口。只用 domain + 基本类型。
type ThemeService interface {
Create(ctx context.Context, spec domain.ThemeSpec) (domain.Theme, error)
Update(ctx context.Context, userID int64, ref domain.ThemeRef, upd domain.ThemeUpdate) (domain.Theme, error)
Get(ctx context.Context, ref domain.ThemeRef) (domain.Theme, bool, error)
Save(ctx context.Context, userID int64, ref domain.ThemeRef) error
Unsave(ctx context.Context, userID int64, ref domain.ThemeRef) error
Install(ctx context.Context, userID int64, ref domain.ThemeRef, dark bool) error
ListInstalled(ctx context.Context, userID int64) ([]domain.Theme, error)
ListForUser(ctx context.Context, userID int64) ([]domain.Theme, error)
}
// PasskeyService 抽象 passkey(WebAuthn)登录与管理(app/passkey)。挑战选项以 DataJSON
// 字节返回;注册/登录验证收原始字节(credentialID 已 base64url 解码)。FinishLogin 返回
// 已验证用户 id,auth_key 绑定由 rpc 经 Auth.BindVerifiedLogin 完成。
type PasskeyService interface {
InitLogin(ctx context.Context) ([]byte, error)
FinishLogin(ctx context.Context, credentialID, clientDataJSON, authenticatorData, signature []byte, userHandle string) (int64, error)
InitRegistration(ctx context.Context, userID int64, displayName string) ([]byte, error)
Register(ctx context.Context, userID int64, credentialID, clientDataJSON, attestationObject []byte, name string) (domain.PasskeyCredential, error)
List(ctx context.Context, userID int64) ([]domain.PasskeyCredential, error)
Delete(ctx context.Context, userID int64, credentialID []byte) (bool, error)
}
// SecretChatService 抽象私聊端对端加密Secret Chat握手状态机app/secretchat
// 服务端是盲中继;错误集合见 domain.ErrSecretChat* 与 app/secretchat.ErrGAInvalid
// rpc 层经 secretChatErr 映射为 ENCRYPTION_* / CHAT_ID_INVALID / DH_G_A_INVALID
type SecretChatService interface {
RequestEncryption(ctx context.Context, req domain.SecretChatRequest) (domain.SecretChat, error)
AcceptEncryption(ctx context.Context, chatID int, viewerUserID, participantAuthKeyID, accessHash int64, gb []byte, keyFingerprint int64) (domain.SecretChat, error)
DiscardEncryption(ctx context.Context, chatID int, viewerUserID, viewerAuthKeyID int64, deleteHistory bool) (domain.SecretChat, bool, error)
// DiscardForAuthKey 级联 discard 绑定该 perm auth_key 的全部活跃密聊(设备登出/授权撤销),
// 返回实际迁移到 discarded 的密聊供通知对端。
DiscardForAuthKey(ctx context.Context, authKeyID int64) ([]domain.SecretChat, error)
GetSecretChat(ctx context.Context, chatID int) (domain.SecretChat, bool, error)
SendEncrypted(ctx context.Context, chatID int, viewerUserID, viewerAuthKeyID, accessHash int64, delivery domain.SecretMessageDelivery) (domain.SecretChat, domain.SecretChatMessage, error)
ListNewMessages(ctx context.Context, deviceAuthKeyID int64, sinceQts, limit int) ([]domain.SecretChatMessage, error)
DeviceReservedQts(ctx context.Context, deviceAuthKeyID int64) (int, error)
AckQueue(ctx context.Context, deviceAuthKeyID int64, maxQts int) error
RecordEncryptionEvent(ctx context.Context, chatID int, targetUserID, targetAuthKeyID int64, date int) error
RecordReadEvent(ctx context.Context, chatID int, targetUserID, targetAuthKeyID int64, maxDate, date int) error
ListStateEvents(ctx context.Context, userID, deviceAuthKeyID int64, limit int) ([]domain.EncryptedStateEvent, error)
MarkStateEventsDelivered(ctx context.Context, deviceAuthKeyID int64, eventIDs []int64) error
PutEncryptedFile(ctx context.Context, ownerUserID int64, ref domain.EncryptedFileRef) error
GetEncryptedFile(ctx context.Context, id, accessHash int64) (domain.EncryptedFileRef, bool, error)
}
// PhoneService 抽象私聊 1:1 通话信令状态机app/phone。所有返回值都是状态快照
// 错误集合见 app/phone 的 Err*rpc 层经 phoneCallErr 映射为 CALL_* RPC_ERROR
type PhoneService interface {
RequestCall(ctx context.Context, callerID int64, in domain.PhoneCallRequest) (domain.PhoneCall, error)
ReceivedCall(ctx context.Context, userID, callID, accessHash int64) (domain.PhoneCall, bool, error)
AcceptCall(ctx context.Context, userID, callID, accessHash int64, gb []byte, proto domain.PhoneCallProtocol, device domain.SessionRef) (domain.PhoneCall, error)
ConfirmCall(ctx context.Context, userID, callID, accessHash int64, ga []byte, keyFingerprint int64, proto domain.PhoneCallProtocol) (call domain.PhoneCall, forcedDiscard bool, err error)
DiscardCall(ctx context.Context, userID, callID, accessHash int64, reason domain.PhoneCallDiscardReason, duration int) (call domain.PhoneCall, already bool, err error)
DiscardCallWithSlug(ctx context.Context, userID, callID, accessHash int64, reason domain.PhoneCallDiscardReason, reasonSlug string, duration int) (call domain.PhoneCall, already bool, err error)
// Signal 在该通话的信令顺序锁内执行 forwarddrop=true 表示按契约静默吞掉。
// peerDevice 是对端受理设备锚点(可零值/失效),定向推送失败须回退 user 扇出。
Signal(ctx context.Context, userID, callID, accessHash int64, forward func(peerUserID int64, peerDevice domain.SessionRef)) (drop bool, err error)
Lookup(ctx context.Context, callID, accessHash int64) (domain.PhoneCall, bool)
// ExpireDue 由 PhoneExpiryDispatcher 周期调用:迁移超时通话并返回快照。
ExpireDue(ctx context.Context, now time.Time) []domain.PhoneCall
}
// GroupCallsService 抽象超级群语音聊天信令app/groupcalls
// 错误集合见 domain.ErrGroupCall*rpc 层映射为 GROUPCALL_* RPC_ERROR
type GroupCallsService interface {
Create(ctx context.Context, channelID, creatorUserID int64, title string, rtmpStream, joinMuted bool, scheduleDate, now int) (domain.GroupCall, error)
// RtmpStreamKey 取/轮换 channel 的持久 RTMP 推流密钥rotate=true 覆盖旧 key
RtmpStreamKey(ctx context.Context, channelID int64, rotate bool, now int) (string, error)
// StartScheduled / SetScheduleSubscription / ScheduleSubscriberIDs 是定时通话流程。
StartScheduled(ctx context.Context, callID int64) (domain.GroupCall, bool, error)
SetScheduleSubscription(ctx context.Context, callID, userID int64, subscribed bool) error
ScheduleSubscriberIDs(ctx context.Context, callID int64) ([]int64, error)
CreateConference(ctx context.Context, creatorUserID, randomID, migratedFromPhoneCallID int64, now int) (domain.GroupCall, error)
Get(ctx context.Context, callID int64) (domain.GroupCall, bool, error)
GetBySlug(ctx context.Context, slug string) (domain.GroupCall, bool, error)
GetByInviteMessage(ctx context.Context, userID int64, msgID int) (domain.GroupCall, domain.GroupCallInvite, bool, error)
Join(ctx context.Context, req domain.JoinGroupCallRequest) (domain.GroupCallMutation, error)
Leave(ctx context.Context, callID, userID int64, now int) (domain.GroupCallMutation, error)
RemoveConferenceParticipants(ctx context.Context, req domain.RemoveConferenceCallParticipantsRequest) (domain.RemoveConferenceCallParticipantsResult, error)
Discard(ctx context.Context, callID int64, now int) (domain.GroupCall, []domain.GroupCallParticipant, error)
Touch(ctx context.Context, callID, userID int64, now int) (activeSSRCs []int64, joined bool, err error)
Participant(ctx context.Context, callID, userID int64) (domain.GroupCallParticipant, bool, error)
Participants(ctx context.Context, callID int64, offset string, limit int) (domain.GroupCallParticipantPage, error)
UpdateParticipant(ctx context.Context, callID, userID int64, update domain.GroupCallParticipantUpdate) (domain.GroupCallMutation, bool, error)
SetTitle(ctx context.Context, callID int64, title string) (domain.GroupCall, bool, error)
SetJoinMuted(ctx context.Context, callID int64, joinMuted bool) (domain.GroupCall, bool, error)
SetStartedMessageID(ctx context.Context, callID int64, msgID int) error
SweepStale(ctx context.Context, checkOlderThan, now, limit int) ([]domain.GroupCallMutation, error)
ResetAllParticipants(ctx context.Context, now int) ([]domain.GroupCall, error)
NextRaiseHandRating(ctx context.Context, callID int64) (int64, error)
SetParticipantOverride(ctx context.Context, callID, setterUserID, targetUserID int64, override domain.GroupCallParticipantOverride, clear bool) error
ParticipantOverride(ctx context.Context, callID, setterUserID, targetUserID int64) (domain.GroupCallParticipantOverride, bool, error)
CreateConferenceInvite(ctx context.Context, invite domain.GroupCallInvite) (domain.GroupCallInvite, error)
SetConferenceInviteStatus(ctx context.Context, callID, inviteeUserID int64, msgID int, status domain.GroupCallInviteStatus, now int) (domain.GroupCallInvite, bool, error)
ConferenceRecipients(ctx context.Context, callID int64) ([]int64, error)
AppendChainBlock(ctx context.Context, block domain.GroupCallChainBlock) (domain.GroupCallChainBlock, error)
ChainBlocks(ctx context.Context, callID int64, subChainID, offset, limit int) (domain.GroupCallChainBlockPage, error)
}
// LiveStreamsService 抽象直播媒体面app/livestreamRTMP ingest + 切段 ring
// nil = 直播媒体面未启用(信令仍可用,观众停留在"等待推流"占位)。
type LiveStreamsService interface {
// StreamChannels 返回 channel 当前直播时间轴;无活跃推流返回空。
StreamChannels(channelID int64) []domain.LiveStreamChannel
// StreamPart 按 time_ms/scale 取一段打包好的 tgcalls broadcast part。
StreamPart(channelID int64, timeMs int64, scale int) ([]byte, error)
// DropChannel 断开该 channel 的推流会话并清空缓冲discard/revoke
DropChannel(channelID int64)
}
// PollsService 抽象 poll 权威态的发送时创建与投票人列表messages.getPollVotes
type PollsService interface {
CreatePoll(ctx context.Context, def domain.PollDefinition) error
GetPollDefinition(ctx context.Context, pollID int64) (domain.PollDefinition, bool, error)
ListPollVotes(ctx context.Context, req domain.PollVotesListRequest) (domain.PollVotesList, error)
}