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"
|
2024-06-23 00:57:42 +08:00
|
|
|
"sort"
|
2023-08-17 19:32:27 +08:00
|
|
|
"strconv"
|
2024-06-11 00:16:41 +08:00
|
|
|
"strings"
|
2023-09-20 03:20:44 +08:00
|
|
|
"sync"
|
2024-06-11 00:16:41 +08:00
|
|
|
"time"
|
2023-08-17 19:32:27 +08:00
|
|
|
|
2024-06-22 20:29:59 +08:00
|
|
|
"github.com/divyam234/teldrive/internal/auth"
|
2023-12-03 03:47:23 +08:00
|
|
|
"github.com/divyam234/teldrive/internal/cache"
|
2024-02-12 04:52:41 +08:00
|
|
|
"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/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"
|
2024-06-11 00:16:41 +08:00
|
|
|
"github.com/gotd/td/tgerr"
|
2024-06-23 00:57:42 +08:00
|
|
|
"golang.org/x/sync/errgroup"
|
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 {
|
2024-02-12 04:52:41 +08:00
|
|
|
db *gorm.DB
|
|
|
|
cnf *config.Config
|
|
|
|
kv kv.KV
|
2023-08-17 19:32:27 +08:00
|
|
|
}
|
|
|
|
|
2024-02-12 04:52:41 +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) {
|
2024-06-22 20:29:59 +08:00
|
|
|
_, session := auth.GetUser(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
|
|
|
|
2024-02-12 04:52:41 +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()
|
2023-08-29 10:33:46 +08:00
|
|
|
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}
|
2024-06-22 20:29:59 +08:00
|
|
|
buff, err := tgc.GetMediaContent(c, client.API(), location)
|
2023-08-24 02:40:40 +08:00
|
|
|
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) {
|
2024-06-22 20:29:59 +08:00
|
|
|
userID, _ := auth.GetUser(c)
|
2023-11-16 23:21:35 +08:00
|
|
|
var (
|
|
|
|
channelId int64
|
|
|
|
err error
|
|
|
|
)
|
2024-04-19 14:13:30 +08:00
|
|
|
|
2024-06-22 20:29:59 +08:00
|
|
|
channelId, _ = getDefaultChannel(c, us.db, userID)
|
2023-11-16 23:21:35 +08:00
|
|
|
|
2024-02-12 04:52:41 +08:00
|
|
|
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}
|
|
|
|
}
|
2024-04-19 04:46:47 +08:00
|
|
|
return &schemas.AccountStats{Bots: tokens, ChannelID: channelId}, nil
|
2023-09-20 03:20:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (us *UserService) UpdateChannel(c *gin.Context) (*schemas.Message, *types.AppError) {
|
2024-02-12 04:52:41 +08:00
|
|
|
|
|
|
|
cache := cache.FromContext(c)
|
|
|
|
|
2024-06-22 20:29:59 +08:00
|
|
|
userId, _ := auth.GetUser(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}
|
|
|
|
|
2024-02-12 04:52:41 +08:00
|
|
|
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}
|
|
|
|
}
|
2024-02-12 04:52:41 +08:00
|
|
|
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)
|
2024-02-12 04:52:41 +08:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2024-06-11 00:16:41 +08:00
|
|
|
func (us *UserService) ListSessions(c *gin.Context) ([]schemas.SessionOut, *types.AppError) {
|
2024-06-22 20:29:59 +08:00
|
|
|
userId, userSession := auth.GetUser(c)
|
2024-06-11 00:16:41 +08:00
|
|
|
|
|
|
|
client, _ := tgc.AuthClient(c, &us.cnf.TG, userSession)
|
|
|
|
|
|
|
|
var (
|
|
|
|
auth *tg.AccountAuthorizations
|
|
|
|
err error
|
|
|
|
)
|
|
|
|
|
|
|
|
err = client.Run(c, func(ctx context.Context) error {
|
|
|
|
auth, err = client.API().AccountGetAuthorizations(c)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
if err != nil && !tgerr.Is(err, "AUTH_KEY_UNREGISTERED") {
|
|
|
|
return nil, &types.AppError{Error: err}
|
|
|
|
}
|
|
|
|
|
|
|
|
dbSessions := []models.Session{}
|
|
|
|
|
2024-06-12 15:35:09 +08:00
|
|
|
if err = us.db.Where("user_id = ?", userId).Order("created_at DESC").Find(&dbSessions).Error; err != nil {
|
2024-06-11 00:16:41 +08:00
|
|
|
return nil, &types.AppError{Error: err}
|
|
|
|
}
|
|
|
|
|
|
|
|
sessionsOut := []schemas.SessionOut{}
|
|
|
|
|
|
|
|
for _, session := range dbSessions {
|
|
|
|
|
|
|
|
s := schemas.SessionOut{Hash: session.Hash,
|
|
|
|
CreatedAt: session.CreatedAt.UTC().Format(time.RFC3339),
|
|
|
|
Current: session.Session == userSession}
|
|
|
|
|
|
|
|
if auth != nil {
|
2024-06-12 15:35:09 +08:00
|
|
|
for _, a := range auth.Authorizations {
|
|
|
|
if session.SessionDate == a.DateCreated {
|
|
|
|
s.AppName = strings.Trim(strings.Replace(a.AppName, "Telegram", "", -1), " ")
|
|
|
|
s.Location = a.Country
|
|
|
|
s.OfficialApp = a.OfficialApp
|
2024-06-11 00:16:41 +08:00
|
|
|
s.Valid = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sessionsOut = append(sessionsOut, s)
|
|
|
|
}
|
|
|
|
|
|
|
|
return sessionsOut, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (us *UserService) RemoveSession(c *gin.Context) (*schemas.Message, *types.AppError) {
|
|
|
|
|
2024-06-22 20:29:59 +08:00
|
|
|
userId, _ := auth.GetUser(c)
|
2024-06-11 00:16:41 +08:00
|
|
|
|
|
|
|
session := &models.Session{}
|
|
|
|
|
|
|
|
if err := us.db.Where("user_id = ?", userId).Where("hash = ?", c.Param("id")).First(session).Error; err != nil {
|
|
|
|
return nil, &types.AppError{Error: err}
|
|
|
|
}
|
|
|
|
|
|
|
|
client, _ := tgc.AuthClient(c, &us.cnf.TG, session.Session)
|
|
|
|
|
|
|
|
client.Run(c, func(ctx context.Context) error {
|
|
|
|
_, err := client.API().AuthLogOut(c)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
|
|
|
us.db.Where("user_id = ?", userId).Where("hash = ?", session.Hash).Delete(&models.Session{})
|
|
|
|
|
|
|
|
return &schemas.Message{Message: "session deleted"}, nil
|
|
|
|
}
|
|
|
|
|
2024-06-23 00:57:42 +08:00
|
|
|
func (us *UserService) ListChannels(c *gin.Context) ([]schemas.Channel, *types.AppError) {
|
2024-06-22 20:29:59 +08:00
|
|
|
_, session := auth.GetUser(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
|
|
|
|
|
|
|
|
})
|
2024-06-23 00:57:42 +08:00
|
|
|
res := []schemas.Channel{}
|
2023-09-20 03:20:44 +08:00
|
|
|
|
2024-06-23 00:57:42 +08:00
|
|
|
for _, channel := range channels {
|
|
|
|
res = append(res, *channel)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Slice(res, func(i, j int) bool {
|
|
|
|
return res[i].ChannelName < res[j].ChannelName
|
|
|
|
})
|
|
|
|
return res, nil
|
2023-09-20 03:20:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
func (us *UserService) AddBots(c *gin.Context) (*schemas.Message, *types.AppError) {
|
2024-06-22 20:29:59 +08:00
|
|
|
userId, session := auth.GetUser(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
|
|
|
}
|
|
|
|
|
2024-06-22 20:29:59 +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)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2023-09-27 19:38:33 +08:00
|
|
|
func (us *UserService) RemoveBots(c *gin.Context) (*schemas.Message, *types.AppError) {
|
|
|
|
|
2024-02-12 04:52:41 +08:00
|
|
|
cache := cache.FromContext(c)
|
|
|
|
|
2024-06-22 20:29:59 +08:00
|
|
|
userID, _ := auth.GetUser(c)
|
2024-02-12 04:52:41 +08:00
|
|
|
|
2024-06-22 20:29:59 +08:00
|
|
|
channelId, err := getDefaultChannel(c, us.db, userID)
|
2023-09-27 19:38:33 +08:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, &types.AppError{Error: err, Code: http.StatusInternalServerError}
|
|
|
|
}
|
|
|
|
|
2024-02-12 04:52:41 +08:00
|
|
|
if err := us.db.Where("user_id = ?", userID).Where("channel_id = ?", channelId).
|
2023-09-27 19:38:33 +08:00
|
|
|
Delete(&models.Bot{}).Error; err != nil {
|
2023-12-04 03:21:30 +08:00
|
|
|
return nil, &types.AppError{Error: err, Code: http.StatusInternalServerError}
|
2023-09-27 19:38:33 +08:00
|
|
|
}
|
|
|
|
|
2024-02-12 04:52:41 +08:00
|
|
|
cache.Delete(fmt.Sprintf("users:bots:%d:%d", userID, channelId))
|
2023-09-27 19:38:33 +08:00
|
|
|
|
2023-12-03 03:47:23 +08:00
|
|
|
return &schemas.Message{Message: "bots deleted"}, nil
|
2023-09-27 19:38:33 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
|
2024-02-12 04:52:41 +08:00
|
|
|
cache := cache.FromContext(c)
|
|
|
|
|
2024-06-23 00:57:42 +08:00
|
|
|
botInfoMap := make(map[string]*types.BotInfo)
|
|
|
|
|
2024-02-12 04:52:41 +08:00
|
|
|
err := tgc.RunWithAuth(c, client, "", func(ctx context.Context) error {
|
2023-09-20 03:20:44 +08:00
|
|
|
|
2024-06-22 20:29:59 +08:00
|
|
|
channel, err := tgc.GetChannelById(ctx, client.API(), channelId)
|
2023-12-25 06:06:14 +08:00
|
|
|
|
2023-09-20 03:20:44 +08:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2024-06-23 00:57:42 +08:00
|
|
|
g, _ := errgroup.WithContext(ctx)
|
2023-09-20 03:20:44 +08:00
|
|
|
|
2024-06-23 00:57:42 +08:00
|
|
|
g.SetLimit(8)
|
|
|
|
|
|
|
|
mapMu := sync.Mutex{}
|
2023-09-20 03:20:44 +08:00
|
|
|
|
|
|
|
for _, token := range botsTokens {
|
2024-06-23 00:57:42 +08:00
|
|
|
g.Go(func() error {
|
|
|
|
info, err := tgc.GetBotInfo(c, us.kv, &us.cnf.TG, token)
|
2023-09-20 03:20:44 +08:00
|
|
|
if err != nil {
|
2024-06-23 00:57:42 +08:00
|
|
|
return err
|
2023-09-20 03:20:44 +08:00
|
|
|
}
|
|
|
|
botPeerClass, err := peer.DefaultResolver(client.API()).ResolveDomain(ctx, info.UserName)
|
|
|
|
if err != nil {
|
2024-06-23 00:57:42 +08:00
|
|
|
return err
|
2023-09-20 03:20:44 +08:00
|
|
|
}
|
|
|
|
botPeer := botPeerClass.(*tg.InputPeerUser)
|
|
|
|
info.AccessHash = botPeer.AccessHash
|
2024-06-23 00:57:42 +08:00
|
|
|
mapMu.Lock()
|
|
|
|
botInfoMap[token] = info
|
|
|
|
mapMu.Unlock()
|
|
|
|
return nil
|
|
|
|
})
|
2024-03-20 01:01:56 +08:00
|
|
|
|
2023-09-20 03:20:44 +08:00
|
|
|
}
|
2024-06-23 00:57:42 +08:00
|
|
|
if err = g.Wait(); err != nil {
|
|
|
|
return err
|
2023-09-20 03:20:44 +08:00
|
|
|
}
|
2024-06-23 00:57:42 +08:00
|
|
|
if len(botsTokens) == len(botInfoMap) {
|
|
|
|
users := []tg.InputUser{}
|
|
|
|
for _, info := range botInfoMap {
|
|
|
|
users = append(users, tg.InputUser{UserID: info.Id, AccessHash: info.AccessHash})
|
|
|
|
}
|
|
|
|
for _, user := range users {
|
2023-09-20 03:20:44 +08:00
|
|
|
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 {
|
|
|
|
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{}
|
|
|
|
|
2024-06-23 00:57:42 +08:00
|
|
|
for _, info := range botInfoMap {
|
2023-09-20 03:20:44 +08:00
|
|
|
payload = append(payload, models.Bot{UserID: userId, Token: info.Token, BotID: info.Id,
|
2023-09-27 19:38:33 +08:00
|
|
|
BotUserName: info.UserName, ChannelID: channelId,
|
2023-09-20 03:20:44 +08:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-02-12 04:52:41 +08:00
|
|
|
cache.Delete(fmt.Sprintf("users:bots:%d:%d", userId, channelId))
|
2023-09-20 03:20:44 +08:00
|
|
|
|
2024-02-12 04:52:41 +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
|
|
|
|
|
|
|
}
|