teldrive/pkg/services/user.go

329 lines
8.8 KiB
Go
Raw Normal View History

2023-08-17 19:32:27 +08:00
package services
import (
"context"
2023-08-28 14:30:04 +08:00
"errors"
2023-08-17 19:32:27 +08:00
"fmt"
"net/http"
"strconv"
2023-09-20 03:20:44 +08:00
"sync"
2023-08-17 19:32:27 +08:00
2023-12-03 03:47:23 +08:00
"github.com/divyam234/teldrive/internal/cache"
"github.com/divyam234/teldrive/internal/config"
"github.com/divyam234/teldrive/internal/kv"
2023-12-03 03:47:23 +08:00
"github.com/divyam234/teldrive/internal/tgc"
"github.com/divyam234/teldrive/pkg/logging"
2023-12-03 03:47:23 +08:00
"github.com/divyam234/teldrive/pkg/models"
"github.com/divyam234/teldrive/pkg/schemas"
"github.com/divyam234/teldrive/pkg/types"
2023-08-17 19:32:27 +08:00
"github.com/gotd/td/telegram"
2023-09-20 03:20:44 +08:00
"github.com/gotd/td/telegram/message/peer"
"github.com/gotd/td/telegram/query"
2023-08-17 19:32:27 +08:00
"github.com/gotd/td/tg"
2023-09-20 03:20:44 +08:00
"github.com/thoas/go-funk"
"go.uber.org/zap"
2023-08-17 19:32:27 +08:00
"github.com/gin-gonic/gin"
2023-09-20 03:20:44 +08:00
"gorm.io/gorm"
"gorm.io/gorm/clause"
2023-08-17 19:32:27 +08:00
)
type UserService struct {
db *gorm.DB
cnf *config.Config
kv kv.KV
2023-08-17 19:32:27 +08:00
}
func NewUserService(db *gorm.DB, cnf *config.Config, kv kv.KV) *UserService {
return &UserService{db: db, cnf: cnf, kv: kv}
2023-09-20 03:20:44 +08:00
}
func (us *UserService) GetProfilePhoto(c *gin.Context) {
_, session := GetUserAuth(c)
2023-08-17 19:32:27 +08:00
2024-02-13 00:02:55 +08:00
client, err := tgc.AuthClient(c, &us.cnf.TG, session)
2023-08-17 19:32:27 +08:00
if err != nil {
2023-09-20 03:20:44 +08:00
c.AbortWithError(http.StatusInternalServerError, err)
return
2023-08-17 19:32:27 +08:00
}
2023-08-24 02:40:40 +08:00
err = tgc.RunWithAuth(c, client, "", func(ctx context.Context) error {
2023-08-24 02:40:40 +08:00
self, err := client.Self(c)
if err != nil {
return err
}
peer := self.AsInputPeer()
if self.Photo == nil {
return nil
}
2023-08-28 14:30:04 +08:00
photo, ok := self.Photo.AsNotEmpty()
if !ok {
return errors.New("profile not found")
2023-08-27 14:28:39 +08:00
}
2023-08-24 02:40:40 +08:00
location := &tg.InputPeerPhotoFileLocation{Big: false, Peer: peer, PhotoID: photo.PhotoID}
buff, err := iterContent(c, client, location)
if err != nil {
return err
}
content := buff.Bytes()
c.Writer.Header().Set("Content-Type", "image/jpeg")
c.Writer.Header().Set("Cache-Control", "public, max-age=86400")
c.Writer.Header().Set("Content-Length", strconv.Itoa(len(content)))
c.Writer.Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", "profile.jpeg"))
c.Writer.Write(content)
return nil
})
2023-08-17 19:32:27 +08:00
if err != nil {
2023-08-28 14:30:04 +08:00
c.AbortWithError(http.StatusNotFound, err)
2023-08-17 19:32:27 +08:00
return
}
}
2023-09-20 03:20:44 +08:00
2023-12-03 05:46:53 +08:00
func (us *UserService) GetStats(c *gin.Context) (*schemas.AccountStats, *types.AppError) {
userId, _ := GetUserAuth(c)
2023-09-20 03:20:44 +08:00
var res []schemas.AccountStats
if err := us.db.Raw("select * from teldrive.account_stats(?);", userId).Scan(&res).Error; err != nil {
2023-12-04 03:21:30 +08:00
return nil, &types.AppError{Error: err, Code: http.StatusInternalServerError}
2023-09-20 03:20:44 +08:00
}
return &res[0], nil
}
func (us *UserService) GetBots(c *gin.Context) ([]string, *types.AppError) {
userID, _ := GetUserAuth(c)
2023-11-16 23:21:35 +08:00
var (
channelId int64
err error
)
if c.Param("channelId") != "" {
channelId, _ = strconv.ParseInt(c.Param("channelId"), 10, 64)
} else {
channelId, err = GetDefaultChannel(c, us.db, userID)
2023-11-16 23:21:35 +08:00
if err != nil {
return nil, &types.AppError{Error: err, Code: http.StatusInternalServerError}
}
}
tokens, err := getBotsToken(c, us.db, userID, channelId)
2023-09-20 03:20:44 +08:00
if err != nil {
return nil, &types.AppError{Error: err, Code: http.StatusInternalServerError}
}
return tokens, nil
}
func (us *UserService) UpdateChannel(c *gin.Context) (*schemas.Message, *types.AppError) {
cache := cache.FromContext(c)
userId, _ := GetUserAuth(c)
2023-09-20 03:20:44 +08:00
var payload schemas.Channel
if err := c.ShouldBindJSON(&payload); err != nil {
2023-12-04 03:21:30 +08:00
return nil, &types.AppError{Error: err, Code: http.StatusBadRequest}
2023-09-20 03:20:44 +08:00
}
channel := &models.Channel{ChannelID: payload.ChannelID, ChannelName: payload.ChannelName, UserID: userId,
Selected: true}
if err := us.db.Clauses(clause.OnConflict{
2023-09-20 03:20:44 +08:00
Columns: []clause.Column{{Name: "channel_id"}},
DoUpdates: clause.Assignments(map[string]interface{}{"selected": true}),
}).Create(channel).Error; err != nil {
return nil, &types.AppError{Error: errors.New("failed to update channel"),
Code: http.StatusInternalServerError}
}
us.db.Model(&models.Channel{}).Where("channel_id != ?", payload.ChannelID).
2023-09-20 03:20:44 +08:00
Where("user_id = ?", userId).Update("selected", false)
2023-11-02 21:51:30 +08:00
key := fmt.Sprintf("users:channel:%d", userId)
cache.Set(key, payload.ChannelID, 0)
2023-12-03 03:47:23 +08:00
return &schemas.Message{Message: "channel updated"}, nil
2023-09-20 03:20:44 +08:00
}
func (us *UserService) ListChannels(c *gin.Context) (interface{}, *types.AppError) {
_, session := GetUserAuth(c)
2024-02-13 00:02:55 +08:00
client, _ := tgc.AuthClient(c, &us.cnf.TG, session)
2023-09-20 03:20:44 +08:00
channels := make(map[int64]*schemas.Channel)
client.Run(c, func(ctx context.Context) error {
dialogs, _ := query.GetDialogs(client.API()).BatchSize(100).Collect(ctx)
for _, dialog := range dialogs {
if !dialog.Deleted() {
for _, channel := range dialog.Entities.Channels() {
_, exists := channels[channel.ID]
2023-09-27 17:36:12 +08:00
if !exists && channel.AdminRights.AddAdmins {
2023-09-20 03:20:44 +08:00
channels[channel.ID] = &schemas.Channel{ChannelID: channel.ID, ChannelName: channel.Title}
}
}
}
}
return nil
})
return funk.Values(channels), nil
}
func (us *UserService) AddBots(c *gin.Context) (*schemas.Message, *types.AppError) {
userId, session := GetUserAuth(c)
2024-02-13 00:02:55 +08:00
client, _ := tgc.AuthClient(c, &us.cnf.TG, session)
2023-09-20 03:20:44 +08:00
var botsTokens []string
if err := c.ShouldBindJSON(&botsTokens); err != nil {
2023-12-04 03:21:30 +08:00
return nil, &types.AppError{Error: err, Code: http.StatusBadRequest}
2023-09-20 03:20:44 +08:00
}
if len(botsTokens) == 0 {
2023-12-03 03:47:23 +08:00
return &schemas.Message{Message: "no bots to add"}, nil
2023-09-20 03:20:44 +08:00
}
channelId, err := GetDefaultChannel(c, us.db, userId)
2023-09-20 03:20:44 +08:00
if err != nil {
return nil, &types.AppError{Error: err, Code: http.StatusInternalServerError}
}
return us.addBots(c, client, userId, channelId, botsTokens)
}
func (us *UserService) RemoveBots(c *gin.Context) (*schemas.Message, *types.AppError) {
cache := cache.FromContext(c)
userID, _ := GetUserAuth(c)
channelId, err := GetDefaultChannel(c, us.db, userID)
if err != nil {
return nil, &types.AppError{Error: err, Code: http.StatusInternalServerError}
}
if err := us.db.Where("user_id = ?", userID).Where("channel_id = ?", channelId).
Delete(&models.Bot{}).Error; err != nil {
2023-12-04 03:21:30 +08:00
return nil, &types.AppError{Error: err, Code: http.StatusInternalServerError}
}
cache.Delete(fmt.Sprintf("users:bots:%d:%d", userID, channelId))
2023-12-03 03:47:23 +08:00
return &schemas.Message{Message: "bots deleted"}, nil
}
2023-09-20 03:20:44 +08:00
func (us *UserService) addBots(c context.Context, client *telegram.Client, userId int64, channelId int64, botsTokens []string) (*schemas.Message, *types.AppError) {
2023-12-03 05:46:53 +08:00
botInfo := []types.BotInfo{}
2023-09-20 03:20:44 +08:00
var wg sync.WaitGroup
logger := logging.FromContext(c)
cache := cache.FromContext(c)
err := tgc.RunWithAuth(c, client, "", func(ctx context.Context) error {
2023-09-20 03:20:44 +08:00
channel, err := GetChannelById(ctx, client, channelId, strconv.FormatInt(userId, 10))
2023-12-25 06:06:14 +08:00
2023-09-20 03:20:44 +08:00
if err != nil {
logger.Error("error", zap.Error(err))
2023-09-20 03:20:44 +08:00
return err
}
2023-12-03 05:46:53 +08:00
botInfoChannel := make(chan *types.BotInfo, len(botsTokens))
2023-09-20 03:20:44 +08:00
waitChan := make(chan struct{}, 6)
for _, token := range botsTokens {
waitChan <- struct{}{}
wg.Add(1)
go func(t string) {
2024-02-13 00:02:55 +08:00
info, err := getBotInfo(c, us.kv, &us.cnf.TG, t)
2023-09-20 03:20:44 +08:00
if err != nil {
return
}
botPeerClass, err := peer.DefaultResolver(client.API()).ResolveDomain(ctx, info.UserName)
if err != nil {
logger.Error("error", zap.Error(err))
2023-09-20 03:20:44 +08:00
return
}
botPeer := botPeerClass.(*tg.InputPeerUser)
info.AccessHash = botPeer.AccessHash
defer func() {
<-waitChan
wg.Done()
}()
2024-03-20 01:01:56 +08:00
botInfoChannel <- info
2023-09-20 03:20:44 +08:00
}(token)
}
wg.Wait()
close(botInfoChannel)
for result := range botInfoChannel {
botInfo = append(botInfo, *result)
}
if len(botsTokens) == len(botInfo) {
2023-12-03 05:46:53 +08:00
users := funk.Map(botInfo, func(info types.BotInfo) tg.InputUser {
2023-09-20 03:20:44 +08:00
return tg.InputUser{UserID: info.Id, AccessHash: info.AccessHash}
})
botsToAdd := users.([]tg.InputUser)
for _, user := range botsToAdd {
payload := &tg.ChannelsEditAdminRequest{
Channel: channel,
UserID: tg.InputUserClass(&user),
AdminRights: tg.ChatAdminRights{
ChangeInfo: true,
PostMessages: true,
EditMessages: true,
DeleteMessages: true,
BanUsers: true,
InviteUsers: true,
PinMessages: true,
ManageCall: true,
Other: true,
ManageTopics: true,
},
Rank: "bot",
}
2023-12-25 06:06:14 +08:00
_, err := client.API().ChannelsEditAdmin(ctx, payload)
if err != nil {
logger.Error("error", zap.Error(err))
2023-12-25 06:06:14 +08:00
return err
}
2023-09-20 03:20:44 +08:00
}
} else {
return errors.New("failed to fetch bots")
}
return nil
})
if err != nil {
return nil, &types.AppError{Error: err, Code: http.StatusInternalServerError}
}
payload := []models.Bot{}
for _, info := range botInfo {
payload = append(payload, models.Bot{UserID: userId, Token: info.Token, BotID: info.Id,
BotUserName: info.UserName, ChannelID: channelId,
2023-09-20 03:20:44 +08:00
})
}
cache.Delete(fmt.Sprintf("users:bots:%d:%d", userId, channelId))
2023-09-20 03:20:44 +08:00
if err := us.db.Clauses(clause.OnConflict{DoNothing: true}).Create(&payload).Error; err != nil {
2023-12-04 03:21:30 +08:00
return nil, &types.AppError{Error: err, Code: http.StatusInternalServerError}
2023-09-20 03:20:44 +08:00
}
2023-12-03 03:47:23 +08:00
return &schemas.Message{Message: "bots added"}, nil
2023-09-20 03:20:44 +08:00
}