Initial open source release
This commit is contained in:
commit
74992e893f
377 changed files with 118084 additions and 0 deletions
292
internal/rpc/account.go
Normal file
292
internal/rpc/account.go
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/gotd/td/tg"
|
||||
|
||||
"telesrv/internal/compat/tdesktop"
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
// registerAccount 注册 account.* RPC handler。
|
||||
func (r *Router) registerAccount(d *tg.ServerDispatcher) {
|
||||
d.OnAccountCheckUsername(r.onAccountCheckUsername)
|
||||
d.OnAccountUpdateProfile(r.onAccountUpdateProfile)
|
||||
d.OnAccountUpdateUsername(r.onAccountUpdateUsername)
|
||||
d.OnAccountGetPassword(func(ctx context.Context) (*tg.AccountPassword, error) {
|
||||
if r.deps.Account == nil {
|
||||
return tgPassword(domain.PasswordSettings{SecureRandom: []byte("telesrv-tdesktop-dev-secure-rand")}), nil
|
||||
}
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
settings, err := r.deps.Account.GetPassword(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
return tgPassword(settings), nil
|
||||
})
|
||||
d.OnAccountGetNotifySettings(func(ctx context.Context, peer tg.InputNotifyPeerClass) (*tg.PeerNotifySettings, error) {
|
||||
return tdesktop.NotifySettings(), nil
|
||||
})
|
||||
d.OnAccountUpdateNotifySettings(func(ctx context.Context, req *tg.AccountUpdateNotifySettingsRequest) (bool, error) {
|
||||
return true, nil
|
||||
})
|
||||
d.OnAccountGetPrivacy(func(ctx context.Context, key tg.InputPrivacyKeyClass) (*tg.AccountPrivacyRules, error) {
|
||||
return tdesktop.PrivacyRules(key), nil
|
||||
})
|
||||
d.OnAccountGetAuthorizations(func(ctx context.Context) (*tg.AccountAuthorizations, error) {
|
||||
return tdesktop.Authorizations(), nil
|
||||
})
|
||||
d.OnAccountGetDefaultEmojiStatuses(func(ctx context.Context, hash int64) (tg.AccountEmojiStatusesClass, error) {
|
||||
return tdesktop.DefaultEmojiStatuses(), nil
|
||||
})
|
||||
d.OnAccountGetCollectibleEmojiStatuses(func(ctx context.Context, hash int64) (tg.AccountEmojiStatusesClass, error) {
|
||||
return tdesktop.CollectibleEmojiStatuses(), nil
|
||||
})
|
||||
d.OnAccountGetDefaultGroupPhotoEmojis(func(ctx context.Context, hash int64) (tg.EmojiListClass, error) {
|
||||
return tdesktop.DefaultGroupPhotoEmojis(), nil
|
||||
})
|
||||
d.OnAccountGetConnectedBots(func(ctx context.Context) (*tg.AccountConnectedBots, error) {
|
||||
return tdesktop.ConnectedBots(), nil
|
||||
})
|
||||
d.OnAccountGetReactionsNotifySettings(r.onAccountGetReactionsNotifySettings)
|
||||
d.OnAccountSetReactionsNotifySettings(r.onAccountSetReactionsNotifySettings)
|
||||
d.OnAccountGetContactSignUpNotification(func(ctx context.Context) (bool, error) {
|
||||
return false, nil
|
||||
})
|
||||
d.OnAccountGetThemes(func(ctx context.Context, req *tg.AccountGetThemesRequest) (tg.AccountThemesClass, error) {
|
||||
return tdesktop.AccountThemes(), nil
|
||||
})
|
||||
d.OnAccountGetContentSettings(func(ctx context.Context) (*tg.AccountContentSettings, error) {
|
||||
return tdesktop.ContentSettings(), nil
|
||||
})
|
||||
d.OnAccountGetGlobalPrivacySettings(func(ctx context.Context) (*tg.GlobalPrivacySettings, error) {
|
||||
return tdesktop.GlobalPrivacySettings(), nil
|
||||
})
|
||||
d.OnAccountGetPasskeys(func(ctx context.Context) (*tg.AccountPasskeys, error) {
|
||||
return tdesktop.Passkeys(), nil
|
||||
})
|
||||
d.OnAccountGetSavedMusicIDs(func(ctx context.Context, hash int64) (tg.AccountSavedMusicIDsClass, error) {
|
||||
if _, _, err := r.currentUserID(ctx); err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
return &tg.AccountSavedMusicIDs{IDs: []int64{}}, nil
|
||||
})
|
||||
d.OnAccountGetAccountTTL(r.onAccountGetAccountTTL)
|
||||
d.OnAccountUpdateStatus(r.onAccountUpdateStatus)
|
||||
}
|
||||
|
||||
func (r *Router) onAccountGetAccountTTL(ctx context.Context) (*tg.AccountDaysTTL, error) {
|
||||
if _, _, err := r.currentUserID(ctx); err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
return &tg.AccountDaysTTL{Days: 365}, nil
|
||||
}
|
||||
|
||||
func (r *Router) onAccountUpdateStatus(ctx context.Context, offline bool) (bool, error) {
|
||||
userID, authorized, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return false, internalErr()
|
||||
}
|
||||
if !authorized || userID == 0 {
|
||||
return true, nil
|
||||
}
|
||||
status := r.setPresenceFromContext(ctx, userID, offline)
|
||||
r.pushUserStatus(ctx, userID, status)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
type accountReactionSettingsService interface {
|
||||
GetReactionSettings(ctx context.Context, userID int64) (domain.AccountReactionSettings, error)
|
||||
SetReactionsNotifySettings(ctx context.Context, userID int64, settings domain.ReactionsNotifySettings) (domain.AccountReactionSettings, error)
|
||||
}
|
||||
|
||||
func (r *Router) onAccountGetReactionsNotifySettings(ctx context.Context) (*tg.ReactionsNotifySettings, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
if svc, ok := r.deps.Account.(accountReactionSettingsService); ok {
|
||||
settings, err := svc.GetReactionSettings(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
return tgReactionsNotifySettings(settings.Notify), nil
|
||||
}
|
||||
return tgReactionsNotifySettings(domain.DefaultAccountReactionSettings().Notify), nil
|
||||
}
|
||||
|
||||
func (r *Router) onAccountSetReactionsNotifySettings(ctx context.Context, settings tg.ReactionsNotifySettings) (*tg.ReactionsNotifySettings, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
notify := domainReactionsNotifySettings(settings)
|
||||
if svc, ok := r.deps.Account.(accountReactionSettingsService); ok {
|
||||
next, err := svc.SetReactionsNotifySettings(ctx, userID, notify)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
return tgReactionsNotifySettings(next.Notify), nil
|
||||
}
|
||||
return tgReactionsNotifySettings(notify), nil
|
||||
}
|
||||
|
||||
func domainReactionsNotifySettings(settings tg.ReactionsNotifySettings) domain.ReactionsNotifySettings {
|
||||
return domain.ReactionsNotifySettings{
|
||||
MessagesFrom: domainReactionNotifyFrom(settings.GetMessagesNotifyFrom),
|
||||
StoriesFrom: domainReactionNotifyFrom(settings.GetStoriesNotifyFrom),
|
||||
PollVotesFrom: domainReactionNotifyFrom(settings.GetPollVotesNotifyFrom),
|
||||
ShowPreviews: settings.ShowPreviews,
|
||||
}
|
||||
}
|
||||
|
||||
func domainReactionNotifyFrom(get func() (tg.ReactionNotificationsFromClass, bool)) domain.ReactionNotifyFrom {
|
||||
if get == nil {
|
||||
return domain.ReactionNotifyFromNone
|
||||
}
|
||||
value, ok := get()
|
||||
if !ok || value == nil {
|
||||
return domain.ReactionNotifyFromNone
|
||||
}
|
||||
switch value.(type) {
|
||||
case *tg.ReactionNotificationsFromAll:
|
||||
return domain.ReactionNotifyFromAll
|
||||
case *tg.ReactionNotificationsFromContacts:
|
||||
return domain.ReactionNotifyFromContacts
|
||||
default:
|
||||
return domain.ReactionNotifyFromNone
|
||||
}
|
||||
}
|
||||
|
||||
func tgReactionsNotifySettings(settings domain.ReactionsNotifySettings) *tg.ReactionsNotifySettings {
|
||||
out := &tg.ReactionsNotifySettings{
|
||||
Sound: &tg.NotificationSoundDefault{},
|
||||
ShowPreviews: settings.ShowPreviews,
|
||||
}
|
||||
if value := tgReactionNotifyFrom(settings.MessagesFrom); value != nil {
|
||||
out.SetMessagesNotifyFrom(value)
|
||||
}
|
||||
if value := tgReactionNotifyFrom(settings.StoriesFrom); value != nil {
|
||||
out.SetStoriesNotifyFrom(value)
|
||||
}
|
||||
if value := tgReactionNotifyFrom(settings.PollVotesFrom); value != nil {
|
||||
out.SetPollVotesNotifyFrom(value)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func tgReactionNotifyFrom(value domain.ReactionNotifyFrom) tg.ReactionNotificationsFromClass {
|
||||
switch value {
|
||||
case domain.ReactionNotifyFromAll:
|
||||
return &tg.ReactionNotificationsFromAll{}
|
||||
case domain.ReactionNotifyFromContacts:
|
||||
return &tg.ReactionNotificationsFromContacts{}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Router) onAccountUpdateProfile(ctx context.Context, req *tg.AccountUpdateProfileRequest) (tg.UserClass, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
svc, ok := r.deps.Users.(UserIdentityService)
|
||||
if !ok {
|
||||
return nil, internalErr()
|
||||
}
|
||||
firstName, hasFirstName := req.GetFirstName()
|
||||
lastName, hasLastName := req.GetLastName()
|
||||
about, hasAbout := req.GetAbout()
|
||||
u, err := svc.UpdateProfile(ctx, userID, domain.UserProfileUpdate{
|
||||
FirstName: firstName,
|
||||
HasFirstName: hasFirstName,
|
||||
LastName: lastName,
|
||||
HasLastName: hasLastName,
|
||||
About: about,
|
||||
HasAbout: hasAbout,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, profileErr(err)
|
||||
}
|
||||
r.pushUsernameUpdate(ctx, u)
|
||||
return r.tgSelfUser(u), nil
|
||||
}
|
||||
|
||||
func (r *Router) onAccountCheckUsername(ctx context.Context, username string) (bool, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return false, internalErr()
|
||||
}
|
||||
svc, ok := r.deps.Users.(UserIdentityService)
|
||||
if !ok {
|
||||
return false, internalErr()
|
||||
}
|
||||
okUsername, err := svc.CheckUsername(ctx, userID, username)
|
||||
if err != nil {
|
||||
return false, usernameErr(err)
|
||||
}
|
||||
return okUsername, nil
|
||||
}
|
||||
|
||||
func (r *Router) onAccountUpdateUsername(ctx context.Context, username string) (tg.UserClass, error) {
|
||||
userID, _, err := r.currentUserID(ctx)
|
||||
if err != nil {
|
||||
return nil, internalErr()
|
||||
}
|
||||
svc, ok := r.deps.Users.(UserIdentityService)
|
||||
if !ok {
|
||||
return nil, internalErr()
|
||||
}
|
||||
u, err := svc.UpdateUsername(ctx, userID, username)
|
||||
if err != nil {
|
||||
return nil, usernameErr(err)
|
||||
}
|
||||
r.pushUsernameUpdate(ctx, u)
|
||||
return r.tgSelfUser(u), nil
|
||||
}
|
||||
|
||||
func (r *Router) pushUsernameUpdate(ctx context.Context, u domain.User) {
|
||||
if u.ID == 0 {
|
||||
return
|
||||
}
|
||||
r.pushUserUpdates(ctx, u.ID, &tg.Updates{
|
||||
Updates: []tg.UpdateClass{&tg.UpdateUserName{
|
||||
UserID: u.ID,
|
||||
FirstName: u.FirstName,
|
||||
LastName: u.LastName,
|
||||
Usernames: tgUsernames(u.Username),
|
||||
}},
|
||||
Users: []tg.UserClass{r.tgSelfUser(u)},
|
||||
Date: int(r.clock.Now().Unix()),
|
||||
})
|
||||
}
|
||||
|
||||
func usernameErr(err error) error {
|
||||
switch {
|
||||
case errors.Is(err, domain.ErrUsernameInvalid):
|
||||
return usernameInvalidErr()
|
||||
case errors.Is(err, domain.ErrUsernameOccupied):
|
||||
return usernameOccupiedErr()
|
||||
case errors.Is(err, domain.ErrUsernameNotOccupied):
|
||||
return usernameNotOccupiedErr()
|
||||
default:
|
||||
return internalErr()
|
||||
}
|
||||
}
|
||||
|
||||
func profileErr(err error) error {
|
||||
switch {
|
||||
case errors.Is(err, domain.ErrFirstNameInvalid):
|
||||
return firstNameInvalidErr()
|
||||
case errors.Is(err, domain.ErrAboutTooLong):
|
||||
return aboutTooLongErr()
|
||||
default:
|
||||
return internalErr()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue