mirror of
https://github.com/serty2005/rmser.git
synced 2026-02-04 19:02:33 -06:00
Настройки работают
Иерархия групп работает Полностью завязано на пользователя и серверы
This commit is contained in:
@@ -141,6 +141,9 @@ func (bot *Bot) initHandlers() {
|
||||
// Actions Callbacks
|
||||
bot.b.Handle(&tele.Btn{Unique: "act_add_server"}, bot.startAddServerFlow)
|
||||
bot.b.Handle(&tele.Btn{Unique: "act_sync"}, bot.triggerSync)
|
||||
bot.b.Handle(&tele.Btn{Unique: "act_del_server_menu"}, bot.renderDeleteServerMenu)
|
||||
bot.b.Handle(&tele.Btn{Unique: "confirm_name_yes"}, bot.handleConfirmNameYes)
|
||||
bot.b.Handle(&tele.Btn{Unique: "confirm_name_no"}, bot.handleConfirmNameNo)
|
||||
bot.b.Handle(&tele.Btn{Unique: "act_deposit"}, func(c tele.Context) error {
|
||||
return c.Respond(&tele.CallbackResponse{Text: "Функция пополнения в разработке 🛠"})
|
||||
})
|
||||
@@ -151,6 +154,7 @@ func (bot *Bot) initHandlers() {
|
||||
// Input Handlers
|
||||
bot.b.Handle(tele.OnText, bot.handleText)
|
||||
bot.b.Handle(tele.OnPhoto, bot.handlePhoto)
|
||||
|
||||
}
|
||||
|
||||
func (bot *Bot) Start() {
|
||||
@@ -205,9 +209,10 @@ func (bot *Bot) renderServersMenu(c tele.Context) error {
|
||||
}
|
||||
|
||||
btnAdd := menu.Data("➕ Добавить сервер", "act_add_server")
|
||||
btnDel := menu.Data("🗑 Удалить", "act_del_server_menu")
|
||||
btnBack := menu.Data("🔙 Назад", "nav_main")
|
||||
|
||||
rows = append(rows, menu.Row(btnAdd))
|
||||
rows = append(rows, menu.Row(btnAdd, btnDel))
|
||||
rows = append(rows, menu.Row(btnBack))
|
||||
|
||||
menu.Inline(rows...)
|
||||
@@ -264,18 +269,21 @@ func (bot *Bot) renderBalanceMenu(c tele.Context) error {
|
||||
func (bot *Bot) handleCallback(c tele.Context) error {
|
||||
data := c.Callback().Data
|
||||
|
||||
// FIX: Telebot v3 добавляет префикс '\f' к Unique ID кнопки.
|
||||
// Нам нужно удалить его, чтобы корректно парсить строку.
|
||||
if len(data) > 0 && data[0] == '\f' {
|
||||
data = data[1:]
|
||||
}
|
||||
|
||||
// Обработка выбора сервера "set_server_..."
|
||||
if strings.HasPrefix(data, "set_server_") {
|
||||
serverIDStr := strings.TrimPrefix(data, "set_server_")
|
||||
// Удаляем лишние пробелы/символы, которые telebot иногда добавляет (уникальный префикс \f)
|
||||
serverIDStr = strings.TrimSpace(serverIDStr)
|
||||
// Telebot v3: Callback data is prefixed with \f followed by unique id.
|
||||
// But here we use 'data' which is the payload.
|
||||
// NOTE: data variable contains what we passed in .Data() second arg.
|
||||
|
||||
// Split by | just in case middleware adds something, but usually raw string is fine.
|
||||
parts := strings.Split(serverIDStr, "|") // Защита от старых форматов
|
||||
serverIDStr = parts[0]
|
||||
// Защита от старых форматов с разделителем |
|
||||
if idx := strings.Index(serverIDStr, "|"); idx != -1 {
|
||||
serverIDStr = serverIDStr[:idx]
|
||||
}
|
||||
|
||||
userDB, _ := bot.accountRepo.GetUserByTelegramID(c.Sender().ID)
|
||||
|
||||
@@ -290,30 +298,74 @@ func (bot *Bot) handleCallback(c tele.Context) error {
|
||||
}
|
||||
|
||||
if !found {
|
||||
logger.Log.Warn("User tried to select unknown server",
|
||||
zap.Int64("user_tg_id", c.Sender().ID),
|
||||
zap.String("server_id_req", serverIDStr))
|
||||
return c.Respond(&tele.CallbackResponse{Text: "Сервер не найден или доступ запрещен"})
|
||||
}
|
||||
|
||||
// 2. Делаем активным
|
||||
// Важно: нужно спарсить UUID
|
||||
// Telebot sometimes sends garbage if Unique is not handled properly.
|
||||
// But we handle OnCallback generally.
|
||||
|
||||
// Fix: В Telebot 3 Data() возвращает payload как есть.
|
||||
// Но лучше быть аккуратным.
|
||||
|
||||
if err := bot.accountRepo.SetActiveServer(userDB.ID, parseUUID(serverIDStr)); err != nil {
|
||||
targetID := parseUUID(serverIDStr)
|
||||
if err := bot.accountRepo.SetActiveServer(userDB.ID, targetID); err != nil {
|
||||
logger.Log.Error("Failed to set active server", zap.Error(err))
|
||||
return c.Respond(&tele.CallbackResponse{Text: "Ошибка смены сервера"})
|
||||
}
|
||||
|
||||
// 3. Сбрасываем кэш фабрики клиентов (чтобы при следующем запросе создался клиент с новыми кредами, если бы они поменялись,
|
||||
// но тут меняется сам сервер, так что Factory.GetClientForUser просто возьмет другой сервер)
|
||||
// Для надежности можно ничего не делать, Factory сама разберется.
|
||||
|
||||
// 3. Успех
|
||||
c.Respond(&tele.CallbackResponse{Text: "✅ Сервер выбран"})
|
||||
return bot.renderServersMenu(c) // Перерисовываем меню
|
||||
}
|
||||
|
||||
// --- ЛОГИКА УДАЛЕНИЯ (новая) ---
|
||||
if strings.HasPrefix(data, "do_del_server_") {
|
||||
serverIDStr := strings.TrimPrefix(data, "do_del_server_")
|
||||
serverIDStr = strings.TrimSpace(serverIDStr)
|
||||
|
||||
// Очистка от мусора
|
||||
if idx := strings.Index(serverIDStr, "|"); idx != -1 {
|
||||
serverIDStr = serverIDStr[:idx]
|
||||
}
|
||||
|
||||
targetID := parseUUID(serverIDStr)
|
||||
if targetID == uuid.Nil {
|
||||
return c.Respond(&tele.CallbackResponse{Text: "Некорректный ID"})
|
||||
}
|
||||
|
||||
// 1. Проверяем, активен ли он сейчас
|
||||
// Нам нужно знать это ДО удаления, чтобы переключить активность
|
||||
// Но проще удалить, а потом проверить, остался ли активный сервер
|
||||
|
||||
// Удаляем
|
||||
if err := bot.accountRepo.DeleteServer(targetID); err != nil {
|
||||
logger.Log.Error("Failed to delete server", zap.Error(err))
|
||||
return c.Respond(&tele.CallbackResponse{Text: "Ошибка удаления"})
|
||||
}
|
||||
|
||||
// Сбрасываем кэш клиента в фабрике
|
||||
bot.rmsFactory.ClearCache(targetID)
|
||||
|
||||
// 2. Проверяем, есть ли активный сервер у пользователя
|
||||
userDB, _ := bot.accountRepo.GetUserByTelegramID(c.Sender().ID)
|
||||
active, err := bot.accountRepo.GetActiveServer(userDB.ID)
|
||||
|
||||
// Если активного нет (мы удалили активный) или ошибка - назначаем новый
|
||||
if active == nil || err != nil {
|
||||
all, _ := bot.accountRepo.GetAllServers(userDB.ID)
|
||||
if len(all) > 0 {
|
||||
// Делаем активным первый попавшийся
|
||||
_ = bot.accountRepo.SetActiveServer(userDB.ID, all[0].ID)
|
||||
c.Respond(&tele.CallbackResponse{Text: "Сервер удален. Активным назначен " + all[0].Name})
|
||||
} else {
|
||||
c.Respond(&tele.CallbackResponse{Text: "Сервер удален. Список пуст."})
|
||||
}
|
||||
} else {
|
||||
c.Respond(&tele.CallbackResponse{Text: "Сервер удален"})
|
||||
}
|
||||
|
||||
// Возвращаемся в меню удаления (обновляем список)
|
||||
return bot.renderDeleteServerMenu(c)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -366,55 +418,65 @@ func (bot *Bot) handleText(c tele.Context) error {
|
||||
ctx.TempURL = strings.TrimRight(text, "/")
|
||||
ctx.State = StateAddServerLogin
|
||||
})
|
||||
return c.Send("👤 Введите <b>логин</b> пользователя iiko:")
|
||||
return c.Send("👤 Введите логин пользователя iiko:")
|
||||
|
||||
case StateAddServerLogin:
|
||||
bot.fsm.UpdateContext(userID, func(ctx *UserContext) {
|
||||
ctx.TempLogin = text
|
||||
ctx.State = StateAddServerPassword
|
||||
})
|
||||
return c.Send("🔑 Введите <b>пароль</b>:")
|
||||
return c.Send("🔑 Введите пароль:")
|
||||
|
||||
case StateAddServerPassword:
|
||||
password := text
|
||||
ctx := bot.fsm.GetContext(userID)
|
||||
msg, _ := bot.b.Send(c.Sender(), "⏳ Проверяю подключение...")
|
||||
|
||||
// Check connection
|
||||
// 1. Проверяем авторизацию (креды)
|
||||
tempClient := bot.rmsFactory.CreateClientFromRawCredentials(ctx.TempURL, ctx.TempLogin, password)
|
||||
if err := tempClient.Auth(); err != nil {
|
||||
bot.b.Delete(msg)
|
||||
return c.Send(fmt.Sprintf("❌ Ошибка: %v\nПопробуйте ввести пароль снова или начните сначала /add_server", err))
|
||||
return c.Send(fmt.Sprintf("❌ Ошибка авторизации: %v\nПроверьте логин/пароль.", err))
|
||||
}
|
||||
|
||||
// Save
|
||||
encPass, _ := bot.cryptoManager.Encrypt(password)
|
||||
userDB, _ := bot.accountRepo.GetOrCreateUser(userID, c.Sender().Username, "", "")
|
||||
|
||||
newServer := &account.RMSServer{
|
||||
UserID: userDB.ID,
|
||||
Name: "iiko Server " + time.Now().Format("15:04"), // Генерируем имя, чтобы не спрашивать лишнего
|
||||
BaseURL: ctx.TempURL,
|
||||
Login: ctx.TempLogin,
|
||||
EncryptedPassword: encPass,
|
||||
IsActive: true, // Сразу делаем активным
|
||||
// 2. Пробуем узнать имя сервера
|
||||
var detectedName string
|
||||
info, err := rms.GetServerInfo(ctx.TempURL)
|
||||
if err == nil && info.ServerName != "" {
|
||||
detectedName = info.ServerName
|
||||
}
|
||||
|
||||
// Сначала сохраняем, потом делаем активным (через репо сохранения)
|
||||
if err := bot.accountRepo.SaveServer(newServer); err != nil {
|
||||
return c.Send("Ошибка БД: " + err.Error())
|
||||
}
|
||||
// Устанавливаем активным (сбрасывая другие)
|
||||
bot.accountRepo.SetActiveServer(userDB.ID, newServer.ID)
|
||||
|
||||
bot.fsm.Reset(userID)
|
||||
bot.b.Delete(msg)
|
||||
c.Send("✅ <b>Сервер добавлен и выбран активным!</b>", tele.ModeHTML)
|
||||
|
||||
// Auto-sync
|
||||
go bot.syncService.SyncAllData(userDB.ID)
|
||||
// Сохраняем пароль во временный контекст, он нам пригодится при финальном сохранении
|
||||
bot.fsm.UpdateContext(userID, func(uCtx *UserContext) {
|
||||
uCtx.TempPassword = password
|
||||
uCtx.TempServerName = detectedName
|
||||
})
|
||||
|
||||
return bot.renderMainMenu(c)
|
||||
// Если имя нашли - предлагаем выбор
|
||||
if detectedName != "" {
|
||||
bot.fsm.SetState(userID, StateAddServerConfirmName)
|
||||
|
||||
menu := &tele.ReplyMarkup{}
|
||||
btnYes := menu.Data("✅ Да, использовать это имя", "confirm_name_yes")
|
||||
btnNo := menu.Data("✏️ Ввести другое", "confirm_name_no")
|
||||
menu.Inline(menu.Row(btnYes), menu.Row(btnNo))
|
||||
|
||||
return c.Send(fmt.Sprintf("🔎 Обнаружено имя сервера: <b>%s</b>.\nИспользовать его?", detectedName), menu, tele.ModeHTML)
|
||||
}
|
||||
|
||||
// Если имя не нашли - просим ввести вручную
|
||||
bot.fsm.SetState(userID, StateAddServerInputName)
|
||||
return c.Send("🏷 Введите <b>название</b> для этого сервера (для вашего удобства):")
|
||||
|
||||
case StateAddServerInputName:
|
||||
// Пользователь ввел свое название
|
||||
name := text
|
||||
if len(name) < 3 {
|
||||
return c.Send("⚠️ Название слишком короткое.")
|
||||
}
|
||||
return bot.saveServerFinal(c, userID, name)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -488,3 +550,78 @@ func parseUUID(s string) uuid.UUID {
|
||||
id, _ := uuid.Parse(s)
|
||||
return id
|
||||
}
|
||||
|
||||
func (bot *Bot) handleConfirmNameYes(c tele.Context) error {
|
||||
userID := c.Sender().ID
|
||||
ctx := bot.fsm.GetContext(userID)
|
||||
if ctx.State != StateAddServerConfirmName {
|
||||
return c.Respond()
|
||||
}
|
||||
return bot.saveServerFinal(c, userID, ctx.TempServerName)
|
||||
}
|
||||
|
||||
func (bot *Bot) handleConfirmNameNo(c tele.Context) error {
|
||||
userID := c.Sender().ID
|
||||
bot.fsm.SetState(userID, StateAddServerInputName)
|
||||
return c.EditOrSend("🏷 Хорошо, введите желаемое <b>название</b>:")
|
||||
}
|
||||
|
||||
// saveServerFinal - общая логика сохранения в БД
|
||||
func (bot *Bot) saveServerFinal(c tele.Context, userID int64, serverName string) error {
|
||||
ctx := bot.fsm.GetContext(userID)
|
||||
|
||||
encPass, _ := bot.cryptoManager.Encrypt(ctx.TempPassword)
|
||||
userDB, _ := bot.accountRepo.GetOrCreateUser(userID, c.Sender().Username, "", "")
|
||||
|
||||
newServer := &account.RMSServer{
|
||||
UserID: userDB.ID,
|
||||
Name: serverName,
|
||||
BaseURL: ctx.TempURL,
|
||||
Login: ctx.TempLogin,
|
||||
EncryptedPassword: encPass,
|
||||
IsActive: true,
|
||||
}
|
||||
|
||||
if err := bot.accountRepo.SaveServer(newServer); err != nil {
|
||||
return c.Send("Ошибка сохранения в БД: " + err.Error())
|
||||
}
|
||||
|
||||
bot.accountRepo.SetActiveServer(userDB.ID, newServer.ID)
|
||||
bot.fsm.Reset(userID)
|
||||
|
||||
c.Send(fmt.Sprintf("✅ Сервер <b>%s</b> успешно добавлен!", serverName), tele.ModeHTML)
|
||||
|
||||
// Auto-sync
|
||||
go bot.syncService.SyncAllData(userDB.ID)
|
||||
|
||||
return bot.renderMainMenu(c)
|
||||
}
|
||||
|
||||
func (bot *Bot) renderDeleteServerMenu(c tele.Context) error {
|
||||
userDB, _ := bot.accountRepo.GetUserByTelegramID(c.Sender().ID)
|
||||
servers, err := bot.accountRepo.GetAllServers(userDB.ID)
|
||||
if err != nil {
|
||||
return c.Send("Ошибка БД: " + err.Error())
|
||||
}
|
||||
|
||||
if len(servers) == 0 {
|
||||
return c.Respond(&tele.CallbackResponse{Text: "Список серверов пуст"})
|
||||
}
|
||||
|
||||
menu := &tele.ReplyMarkup{}
|
||||
var rows []tele.Row
|
||||
|
||||
for _, s := range servers {
|
||||
// Кнопка удаления для каждого сервера
|
||||
// Префикс do_del_server_
|
||||
btn := menu.Data(fmt.Sprintf("❌ %s", s.Name), "do_del_server_"+s.ID.String())
|
||||
rows = append(rows, menu.Row(btn))
|
||||
}
|
||||
|
||||
btnBack := menu.Data("🔙 Назад к списку", "nav_servers")
|
||||
rows = append(rows, menu.Row(btnBack))
|
||||
|
||||
menu.Inline(rows...)
|
||||
|
||||
return c.EditOrSend("🗑 <b>Удаление сервера</b>\n\nНажмите на сервер, который хотите удалить.\nЭто действие нельзя отменить.", menu, tele.ModeHTML)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user