owpengram-server/internal/rpc/convert_theme.go

220 lines
5.8 KiB
Go

package rpc
import (
"context"
"sort"
"github.com/iamxvbaba/td/tg"
"telesrv/internal/domain"
)
// themesListHash 计算一组主题的稳定哈希(服务端权威,客户端原样回传)。对 id 升序折叠,
// 与返回顺序无关;主题集合变化(用户新建/安装/卸载)即变,驱动客户端重取。
func themesListHash(themes []tg.Theme) int64 {
ids := make([]int64, 0, len(themes))
for _, t := range themes {
ids = append(ids, t.ID)
}
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
var h uint64 = 0xcbf29ce484222325 // FNV-1a 64 offset basis
for _, id := range ids {
h ^= uint64(id)
h *= 0x100000001b3
}
return int64(h & 0x7fffffffffffffff)
}
// themeRefFromInput 把 tg.InputThemeClass(inputTheme / inputThemeSlug)转成 domain.ThemeRef。
func themeRefFromInput(in tg.InputThemeClass) (domain.ThemeRef, bool) {
switch v := in.(type) {
case *tg.InputTheme:
if v.ID == 0 {
return domain.ThemeRef{}, false
}
return domain.ThemeRef{ID: v.ID, AccessHash: v.AccessHash}, true
case *tg.InputThemeSlug:
if v.Slug == "" {
return domain.ThemeRef{}, false
}
return domain.ThemeRef{Slug: v.Slug}, true
default:
return domain.ThemeRef{}, false
}
}
func domainBaseThemeFromInput(in tg.BaseThemeClass) domain.ThemeBaseKind {
switch in.(type) {
case *tg.BaseThemeClassic:
return domain.ThemeBaseClassic
case *tg.BaseThemeDay:
return domain.ThemeBaseDay
case *tg.BaseThemeNight:
return domain.ThemeBaseNight
case *tg.BaseThemeTinted:
return domain.ThemeBaseTinted
case *tg.BaseThemeArctic:
return domain.ThemeBaseArctic
default:
return domain.ThemeBaseClassic
}
}
func tgBaseTheme(kind domain.ThemeBaseKind) tg.BaseThemeClass {
switch kind {
case domain.ThemeBaseDay:
return &tg.BaseThemeDay{}
case domain.ThemeBaseNight:
return &tg.BaseThemeNight{}
case domain.ThemeBaseTinted:
return &tg.BaseThemeTinted{}
case domain.ThemeBaseArctic:
return &tg.BaseThemeArctic{}
default:
return &tg.BaseThemeClassic{}
}
}
// domainThemeSettingsFromInput 解析 createTheme/updateTheme 传入的 settings 向量。
func domainThemeSettingsFromInput(in []tg.InputThemeSettings) []domain.ThemeSettingsSpec {
if len(in) == 0 {
return nil
}
out := make([]domain.ThemeSettingsSpec, 0, len(in))
for _, s := range in {
spec := domain.ThemeSettingsSpec{
BaseTheme: domainBaseThemeFromInput(s.BaseTheme),
AccentColor: s.GetAccentColor(),
MessageColorsAnimated: s.GetMessageColorsAnimated(),
}
if v, ok := s.GetOutboxAccentColor(); ok {
spec.OutboxAccentColor = v
spec.HasOutboxAccent = true
}
if v, ok := s.GetMessageColors(); ok {
spec.MessageColors = append([]int(nil), v...)
}
// flag bit1 同时门控 wallpaper 与 wallpaper_settings;渐变色在 wallpaper_settings 里。
if wp, ok := s.GetWallpaperSettings(); ok {
spec.Wallpaper = domainThemeWallpaperFromInput(wp)
}
out = append(out, spec)
}
return out
}
func domainThemeWallpaperFromInput(wp tg.WallPaperSettings) *domain.ThemeWallpaperSpec {
out := &domain.ThemeWallpaperSpec{Blur: wp.Blur, Motion: wp.Motion}
colors := make([]int, 0, 4)
if v, ok := wp.GetBackgroundColor(); ok {
colors = append(colors, v)
}
if v, ok := wp.GetSecondBackgroundColor(); ok {
colors = append(colors, v)
}
if v, ok := wp.GetThirdBackgroundColor(); ok {
colors = append(colors, v)
}
if v, ok := wp.GetFourthBackgroundColor(); ok {
colors = append(colors, v)
}
if len(colors) > 0 {
out.BackgroundColors = colors
}
if v, ok := wp.GetIntensity(); ok {
out.Intensity = v
}
if v, ok := wp.GetRotation(); ok {
out.Rotation = v
}
if v, ok := wp.GetEmoticon(); ok {
out.Emoticon = v
}
return out
}
// tgTheme 把 domain.Theme 投影为 tg.Theme。viewerUserID 决定 creator 标志(决定客户端
// 下次上传走 update 而非 create)。完整自定义主题靠 DocumentID 解析出可下载 Document。
func (r *Router) tgTheme(ctx context.Context, t domain.Theme, viewerUserID int64) *tg.Theme {
out := &tg.Theme{
ID: t.ID,
AccessHash: t.AccessHash,
Slug: t.Slug,
Title: t.Title,
}
if t.IsCreator(viewerUserID) {
out.SetCreator(true)
}
if t.ForChat {
out.SetForChat(true)
}
if t.Emoticon != "" {
out.SetEmoticon(t.Emoticon)
}
out.SetInstallsCount(t.InstallsCount)
if len(t.Settings) > 0 {
out.SetSettings(tgThemeSettingsList(t.Settings))
}
if t.DocumentID != 0 && r.deps.Files != nil {
if doc, ok, err := r.deps.Files.GetDocument(ctx, t.DocumentID); err == nil && ok {
out.SetDocument(tgDocument(doc))
}
}
return out
}
func tgThemeSettingsList(in []domain.ThemeSettingsSpec) []tg.ThemeSettings {
out := make([]tg.ThemeSettings, 0, len(in))
for _, s := range in {
ts := tg.ThemeSettings{
BaseTheme: tgBaseTheme(s.BaseTheme),
AccentColor: s.AccentColor,
}
if s.HasOutboxAccent {
ts.SetOutboxAccentColor(s.OutboxAccentColor)
}
if len(s.MessageColors) > 0 {
ts.SetMessageColors(append([]int(nil), s.MessageColors...))
}
if s.MessageColorsAnimated {
ts.SetMessageColorsAnimated(true)
}
if s.Wallpaper != nil {
ts.SetWallpaper(tgThemeWallpaper(s.Wallpaper))
}
out = append(out, ts)
}
return out
}
func tgThemeWallpaper(wp *domain.ThemeWallpaperSpec) tg.WallPaperClass {
settings := tg.WallPaperSettings{Blur: wp.Blur, Motion: wp.Motion}
colors := wp.BackgroundColors
if len(colors) > 0 {
settings.SetBackgroundColor(colors[0])
}
if len(colors) > 1 {
settings.SetSecondBackgroundColor(colors[1])
}
if len(colors) > 2 {
settings.SetThirdBackgroundColor(colors[2])
}
if len(colors) > 3 {
settings.SetFourthBackgroundColor(colors[3])
}
if wp.Intensity != 0 {
settings.SetIntensity(wp.Intensity)
}
if wp.Rotation != 0 {
settings.SetRotation(wp.Rotation)
}
if wp.Emoticon != "" {
settings.SetEmoticon(wp.Emoticon)
}
out := &tg.WallPaperNoFile{}
if wp.Dark {
out.SetDark(true)
}
out.SetSettings(settings)
return out
}