Improve Telegram keyboards and fix admin user menu navigation
Unified inline menus, user callbacks for all users, Home button to exit admin panel. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+104
-106
@@ -76,30 +76,25 @@ func (h *Handler) HandleUpdate(update tgbotapi.Update) {
|
|||||||
if h.isAdmin(userID) && h.handleWizardMessage(chatID, userID, text) {
|
if h.isAdmin(userID) && h.handleWizardMessage(chatID, userID, text) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if text == "📲 Получить конфиг (1 день)" || strings.HasPrefix(text, "📲 Получить конфиг") {
|
switch text {
|
||||||
|
case userHomeLabel(), "/menu":
|
||||||
|
h.sendUserMenu(chatID, userID, update.Message.From.FirstName, update.Message.From.UserName)
|
||||||
|
return
|
||||||
|
case adminPanelLabel(), "🛠 Админ-меню":
|
||||||
|
if h.isAdmin(userID) {
|
||||||
|
h.sendAdminMenu(chatID)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if h.isUserConfigButtonText(text) {
|
||||||
h.handleUserConfig(chatID, userID)
|
h.handleUserConfig(chatID, userID)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if h.isAdmin(userID) {
|
// Старые подписи reply-клавиатуры (если остались у пользователя)
|
||||||
switch text {
|
if h.isAdmin(userID) && h.handleLegacyAdminReply(chatID, userID, text) {
|
||||||
case "📋 Конфиг панели":
|
|
||||||
h.sendPanelConfig(chatID)
|
|
||||||
return
|
|
||||||
case "🔌 Проверить панель":
|
|
||||||
h.sendPanelCheck(chatID)
|
|
||||||
return
|
|
||||||
case "👤 Создать пользователя":
|
|
||||||
h.startUserWizard(chatID, userID)
|
|
||||||
return
|
|
||||||
case "📡 Сквады":
|
|
||||||
h.sendSquadsList(chatID)
|
|
||||||
return
|
|
||||||
case "◀️ Выйти из админки":
|
|
||||||
h.sendText(chatID, "Админ-меню закрыто. /admin — снова открыть.")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
h.sendText(chatID, "Напишите /start или нажмите 🏠 Главная в меню.")
|
||||||
h.sendText(chatID, "Напишите /start, чтобы начать.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,33 +128,54 @@ func (h *Handler) handleCallback(cq *tgbotapi.CallbackQuery) {
|
|||||||
log.Printf("callback answer: %v", err)
|
log.Printf("callback answer: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !h.isAdmin(cq.From.ID) {
|
chatID := cq.Message.Chat.ID
|
||||||
h.editOrSend(cq.Message.Chat.ID, cq.Message.MessageID, "Нет доступа.")
|
userID := cq.From.ID
|
||||||
|
|
||||||
|
switch cq.Data {
|
||||||
|
case cbUserConfig:
|
||||||
|
h.handleUserConfig(chatID, userID)
|
||||||
|
return
|
||||||
|
case cbUserHome:
|
||||||
|
h.sendUserMenu(chatID, userID, cq.From.FirstName, cq.From.UserName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if cq.Data == "user:config" {
|
if strings.HasPrefix(cq.Data, "wz:") {
|
||||||
h.handleUserConfig(cq.Message.Chat.ID, cq.From.ID)
|
if !h.isAdmin(userID) {
|
||||||
|
h.callbackDenied(cq)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if h.handleWizardCallback(cq) {
|
if h.handleWizardCallback(cq) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !h.isAdmin(userID) {
|
||||||
|
h.callbackDenied(cq)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch cq.Data {
|
switch cq.Data {
|
||||||
case "admin:user":
|
case cbAdminUser:
|
||||||
h.startUserWizard(cq.Message.Chat.ID, cq.From.ID)
|
h.startUserWizard(chatID, userID)
|
||||||
case "admin:squads":
|
case cbAdminSquads:
|
||||||
h.sendSquadsList(cq.Message.Chat.ID)
|
h.sendSquadsList(chatID)
|
||||||
case "admin:config":
|
case cbAdminConfig:
|
||||||
h.sendPanelConfig(cq.Message.Chat.ID)
|
h.sendPanelConfig(chatID)
|
||||||
case "admin:check":
|
case cbAdminCheck:
|
||||||
h.sendPanelCheck(cq.Message.Chat.ID)
|
h.sendPanelCheck(chatID)
|
||||||
case "admin:menu":
|
case cbAdminMenu:
|
||||||
h.sendAdminMenu(cq.Message.Chat.ID)
|
h.sendAdminMenu(chatID)
|
||||||
default:
|
default:
|
||||||
h.editOrSend(cq.Message.Chat.ID, cq.Message.MessageID, "Неизвестное действие.")
|
h.editOrSend(chatID, cq.Message.MessageID, "Неизвестное действие.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) callbackDenied(cq *tgbotapi.CallbackQuery) {
|
||||||
|
cb := tgbotapi.NewCallback(cq.ID, "Нет доступа")
|
||||||
|
cb.ShowAlert = true
|
||||||
|
if _, err := h.api.Request(cb); err != nil {
|
||||||
|
log.Printf("callback alert: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,64 +184,82 @@ func (h *Handler) isAdmin(userID int64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) sendStart(chatID, userID int64, firstName, tgUsername string) {
|
func (h *Handler) sendStart(chatID, userID int64, firstName, tgUsername string) {
|
||||||
|
h.sendUserMenu(chatID, userID, firstName, tgUsername)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) sendUserMenu(chatID, userID int64, firstName, tgUsername string) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
_ = h.database.UpsertTelegramUser(ctx, userID, tgUsername, firstName)
|
_ = h.database.UpsertTelegramUser(ctx, userID, tgUsername, firstName)
|
||||||
|
|
||||||
|
h.dismissReplyKeyboard(chatID)
|
||||||
|
|
||||||
name := firstName
|
name := firstName
|
||||||
if name == "" {
|
if name == "" {
|
||||||
name = "друг"
|
name = "друг"
|
||||||
}
|
}
|
||||||
days := h.cfg.TrialUserDays
|
days := trialDays(h.cfg)
|
||||||
if days <= 0 {
|
text := fmt.Sprintf(
|
||||||
days = 1
|
"👋 Привет, %s!\n\n"+
|
||||||
}
|
"🔐 Trial VPN — %d дн.\n"+
|
||||||
text := fmt.Sprintf("Привет, %s!\n\nЯ VPN-бот. Нажмите кнопку ниже — получите конфиг на %d дн.\nИли команда /config", name, days)
|
"Нажмите кнопку ниже или /config\n\n"+
|
||||||
|
"Импорт: V2rayNG, Hiddify, Streisand и др.",
|
||||||
|
name, days,
|
||||||
|
)
|
||||||
if h.isAdmin(userID) {
|
if h.isAdmin(userID) {
|
||||||
text += "\n\n/admin — админ-меню"
|
text += "\n\nВы администратор: кнопка «🛠 Админ-панель» или /admin"
|
||||||
}
|
}
|
||||||
msg := tgbotapi.NewMessage(chatID, text)
|
msg := tgbotapi.NewMessage(chatID, text)
|
||||||
msg.ReplyMarkup = h.startInlineKeyboard(userID)
|
msg.ReplyMarkup = userMenuKeyboard(h.cfg, userID, h.admin)
|
||||||
h.send(msg)
|
h.send(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) startInlineKeyboard(userID int64) tgbotapi.InlineKeyboardMarkup {
|
func (h *Handler) dismissReplyKeyboard(chatID int64) {
|
||||||
rows := [][]tgbotapi.InlineKeyboardButton{
|
rm := tgbotapi.NewMessage(chatID, "\u200b")
|
||||||
tgbotapi.NewInlineKeyboardRow(
|
rm.ReplyMarkup = tgbotapi.NewRemoveKeyboard(true)
|
||||||
tgbotapi.NewInlineKeyboardButtonData(h.userConfigButtonLabel(), "user:config"),
|
rm.DisableNotification = true
|
||||||
),
|
if _, err := h.api.Send(rm); err != nil {
|
||||||
|
log.Printf("remove reply keyboard: %v", err)
|
||||||
}
|
}
|
||||||
if h.isAdmin(userID) {
|
|
||||||
rows = append(rows, tgbotapi.NewInlineKeyboardRow(
|
|
||||||
tgbotapi.NewInlineKeyboardButtonData("🛠 Админ-меню", "admin:menu"),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
return tgbotapi.NewInlineKeyboardMarkup(rows...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) userConfigButtonLabel() string {
|
func (h *Handler) isUserConfigButtonText(text string) bool {
|
||||||
days := h.cfg.TrialUserDays
|
return text == userConfigLabel(h.cfg) ||
|
||||||
if days <= 0 {
|
strings.HasPrefix(text, "🔐 ") ||
|
||||||
days = 1
|
strings.HasPrefix(text, "📲 Получить конфиг")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *Handler) handleLegacyAdminReply(chatID, userID int64, text string) bool {
|
||||||
|
switch text {
|
||||||
|
case "📋 Конфиг панели", "⚙️ Настройки":
|
||||||
|
h.sendPanelConfig(chatID)
|
||||||
|
case "🔌 Проверить панель", "🔌 Проверка API":
|
||||||
|
h.sendPanelCheck(chatID)
|
||||||
|
case "👤 Создать пользователя", "👤 Новый пользователь":
|
||||||
|
h.startUserWizard(chatID, userID)
|
||||||
|
case "📡 Сквады":
|
||||||
|
h.sendSquadsList(chatID)
|
||||||
|
case "◀️ Выйти из админки":
|
||||||
|
h.sendUserMenu(chatID, userID, "", "")
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("📲 Получить конфиг (%d дн.)", days)
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) sendAdminMenu(chatID int64) {
|
func (h *Handler) sendAdminMenu(chatID int64) {
|
||||||
text := fmt.Sprintf(
|
text := fmt.Sprintf(
|
||||||
"🛠 *Админ-меню* — %s\n\n"+
|
"🛠 *Админ-панель* — %s\n\n"+
|
||||||
"Команды:\n"+
|
"• /admin check — проверка API\n"+
|
||||||
"• /admin — это меню\n"+
|
"• /admin user — новый пользователь\n"+
|
||||||
"• /admin check — проверка панели, API и подписки\n"+
|
"• /admin squads — сквады\n"+
|
||||||
"• /admin config — конфиг панели\n"+
|
"• /admin assign — назначить сквады\n\n"+
|
||||||
"• /admin user — создать пользователя\n"+
|
"🏠 Главная — меню пользователя",
|
||||||
"• /admin squads — список сквадов\n"+
|
|
||||||
"• /admin assign <логин> — назначить сквады\n\n"+
|
|
||||||
"Или кнопки ниже.",
|
|
||||||
escapeMarkdown(h.cfg.RemnawaveName),
|
escapeMarkdown(h.cfg.RemnawaveName),
|
||||||
)
|
)
|
||||||
msg := tgbotapi.NewMessage(chatID, text)
|
msg := tgbotapi.NewMessage(chatID, text)
|
||||||
msg.ParseMode = "Markdown"
|
msg.ParseMode = "Markdown"
|
||||||
msg.ReplyMarkup = adminInlineKeyboard()
|
msg.ReplyMarkup = adminMenuKeyboard()
|
||||||
h.send(msg)
|
h.send(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +296,7 @@ func (h *Handler) sendPanelConfig(chatID int64) {
|
|||||||
"https://docs.rw/docs/install/subscription-page/bundled",
|
"https://docs.rw/docs/install/subscription-page/bundled",
|
||||||
)
|
)
|
||||||
msg := tgbotapi.NewMessage(chatID, text)
|
msg := tgbotapi.NewMessage(chatID, text)
|
||||||
msg.ReplyMarkup = adminInlineKeyboard()
|
msg.ReplyMarkup = adminContextKeyboard()
|
||||||
h.send(msg)
|
h.send(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -276,46 +310,10 @@ func (h *Handler) sendPanelCheck(chatID int64) {
|
|||||||
text := remnawave.FormatReport(report, h.cfg.RemnawaveName)
|
text := remnawave.FormatReport(report, h.cfg.RemnawaveName)
|
||||||
|
|
||||||
msg := tgbotapi.NewMessage(chatID, text)
|
msg := tgbotapi.NewMessage(chatID, text)
|
||||||
msg.ReplyMarkup = adminInlineKeyboard()
|
msg.ReplyMarkup = adminContextKeyboard()
|
||||||
h.send(msg)
|
h.send(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func adminInlineKeyboard() tgbotapi.InlineKeyboardMarkup {
|
|
||||||
return tgbotapi.NewInlineKeyboardMarkup(
|
|
||||||
tgbotapi.NewInlineKeyboardRow(
|
|
||||||
tgbotapi.NewInlineKeyboardButtonData("👤 Создать пользователя", "admin:user"),
|
|
||||||
tgbotapi.NewInlineKeyboardButtonData("📡 Сквады", "admin:squads"),
|
|
||||||
),
|
|
||||||
tgbotapi.NewInlineKeyboardRow(
|
|
||||||
tgbotapi.NewInlineKeyboardButtonData("🔌 Проверить", "admin:check"),
|
|
||||||
tgbotapi.NewInlineKeyboardButtonData("📋 Конфиг", "admin:config"),
|
|
||||||
),
|
|
||||||
tgbotapi.NewInlineKeyboardRow(
|
|
||||||
tgbotapi.NewInlineKeyboardButtonURL("📖 Документация", docsURL),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func adminReplyKeyboard() tgbotapi.ReplyKeyboardMarkup {
|
|
||||||
return tgbotapi.ReplyKeyboardMarkup{
|
|
||||||
Keyboard: [][]tgbotapi.KeyboardButton{
|
|
||||||
{
|
|
||||||
tgbotapi.NewKeyboardButton("👤 Создать пользователя"),
|
|
||||||
tgbotapi.NewKeyboardButton("📡 Сквады"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tgbotapi.NewKeyboardButton("🔌 Проверить панель"),
|
|
||||||
tgbotapi.NewKeyboardButton("📋 Конфиг панели"),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
tgbotapi.NewKeyboardButton("◀️ Выйти из админки"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ResizeKeyboard: true,
|
|
||||||
OneTimeKeyboard: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) sendText(chatID int64, text string) {
|
func (h *Handler) sendText(chatID int64, text string) {
|
||||||
h.send(tgbotapi.NewMessage(chatID, text))
|
h.send(tgbotapi.NewMessage(chatID, text))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package bot
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"telegramvpn/internal/config"
|
||||||
|
|
||||||
|
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cbUserConfig = "user:config"
|
||||||
|
cbUserHome = "user:home"
|
||||||
|
cbAdminMenu = "admin:menu"
|
||||||
|
cbAdminUser = "admin:user"
|
||||||
|
cbAdminSquads = "admin:squads"
|
||||||
|
cbAdminCheck = "admin:check"
|
||||||
|
cbAdminConfig = "admin:config"
|
||||||
|
)
|
||||||
|
|
||||||
|
func trialDays(cfg *config.Config) int {
|
||||||
|
d := cfg.TrialUserDays
|
||||||
|
if d <= 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func userConfigLabel(cfg *config.Config) string {
|
||||||
|
return fmt.Sprintf("🔐 Подключить VPN · %d дн.", trialDays(cfg))
|
||||||
|
}
|
||||||
|
|
||||||
|
func userHomeLabel() string {
|
||||||
|
return "🏠 Главная"
|
||||||
|
}
|
||||||
|
|
||||||
|
func adminPanelLabel() string {
|
||||||
|
return "🛠 Админ-панель"
|
||||||
|
}
|
||||||
|
|
||||||
|
// userMenuKeyboard — главное меню пользователя (и выход из админки).
|
||||||
|
func userMenuKeyboard(cfg *config.Config, userID, adminID int64) tgbotapi.InlineKeyboardMarkup {
|
||||||
|
rows := [][]tgbotapi.InlineKeyboardButton{
|
||||||
|
tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData(userConfigLabel(cfg), cbUserConfig),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
if userID == adminID {
|
||||||
|
rows = append(rows, tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData(adminPanelLabel(), cbAdminMenu),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return tgbotapi.NewInlineKeyboardMarkup(rows...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// configResultKeyboard — после выдачи конфига: ссылка + возврат в меню.
|
||||||
|
func configResultKeyboard(cfg *config.Config, userID, adminID int64, subscriptionURL string) tgbotapi.InlineKeyboardMarkup {
|
||||||
|
var rows [][]tgbotapi.InlineKeyboardButton
|
||||||
|
if subscriptionURL != "" {
|
||||||
|
rows = append(rows, tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonURL("🔗 Открыть подписку", subscriptionURL),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
rows = append(rows,
|
||||||
|
tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData(userConfigLabel(cfg), cbUserConfig),
|
||||||
|
),
|
||||||
|
tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData(userHomeLabel(), cbUserHome),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if userID == adminID {
|
||||||
|
rows = append(rows, tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData(adminPanelLabel(), cbAdminMenu),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return tgbotapi.NewInlineKeyboardMarkup(rows...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// adminMenuKeyboard — панель администратора.
|
||||||
|
func adminMenuKeyboard() tgbotapi.InlineKeyboardMarkup {
|
||||||
|
return tgbotapi.NewInlineKeyboardMarkup(
|
||||||
|
tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData("👤 Новый пользователь", cbAdminUser),
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData("📡 Сквады", cbAdminSquads),
|
||||||
|
),
|
||||||
|
tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData("🔌 Проверка API", cbAdminCheck),
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData("⚙️ Настройки", cbAdminConfig),
|
||||||
|
),
|
||||||
|
tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonURL("📖 Remnawave Docs", docsURL),
|
||||||
|
),
|
||||||
|
tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData(userHomeLabel(), cbUserHome),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// adminContextKeyboard — под ответами админки (проверка, конфиг и т.д.).
|
||||||
|
func adminContextKeyboard() tgbotapi.InlineKeyboardMarkup {
|
||||||
|
return tgbotapi.NewInlineKeyboardMarkup(
|
||||||
|
tgbotapi.NewInlineKeyboardRow(
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData("🛠 В админ-панель", cbAdminMenu),
|
||||||
|
tgbotapi.NewInlineKeyboardButtonData(userHomeLabel(), cbUserHome),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
+14
-11
@@ -8,6 +8,8 @@ import (
|
|||||||
|
|
||||||
"telegramvpn/internal/db"
|
"telegramvpn/internal/db"
|
||||||
"telegramvpn/internal/remnawave"
|
"telegramvpn/internal/remnawave"
|
||||||
|
|
||||||
|
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (h *Handler) handleUserConfig(chatID, telegramID int64) {
|
func (h *Handler) handleUserConfig(chatID, telegramID int64) {
|
||||||
@@ -26,7 +28,7 @@ func (h *Handler) handleUserConfig(chatID, telegramID int64) {
|
|||||||
}
|
}
|
||||||
if existing != nil && existing.ExpireAt != nil && existing.ExpireAt.After(time.Now()) {
|
if existing != nil && existing.ExpireAt != nil && existing.ExpireAt.After(time.Now()) {
|
||||||
link := h.resolveSubscriptionLink(ctx, existing.RemnawaveUsername, telegramID)
|
link := h.resolveSubscriptionLink(ctx, existing.RemnawaveUsername, telegramID)
|
||||||
h.sendConfigMessage(chatID, days, existing.RemnawaveUsername, *existing.ExpireAt, link)
|
h.sendConfigMessage(chatID, telegramID, days, existing.RemnawaveUsername, *existing.ExpireAt, link)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +39,7 @@ func (h *Handler) handleUserConfig(chatID, telegramID int64) {
|
|||||||
link = h.subscriptionLink(panelUser.ShortUUID)
|
link = h.subscriptionLink(panelUser.ShortUUID)
|
||||||
}
|
}
|
||||||
_ = h.saveVPNFromPanel(ctx, telegramID, panelUser)
|
_ = h.saveVPNFromPanel(ctx, telegramID, panelUser)
|
||||||
h.sendConfigMessage(chatID, days, panelUser.Username, panelUser.ExpireAt, link)
|
h.sendConfigMessage(chatID, telegramID, days, panelUser.Username, panelUser.ExpireAt, link)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,7 +73,7 @@ func (h *Handler) handleUserConfig(chatID, telegramID int64) {
|
|||||||
if link == "" {
|
if link == "" {
|
||||||
link = h.subscriptionLink(u.ShortUUID)
|
link = h.subscriptionLink(u.ShortUUID)
|
||||||
}
|
}
|
||||||
h.sendConfigMessage(chatID, days, u.Username, u.ExpireAt, link)
|
h.sendConfigMessage(chatID, telegramID, days, u.Username, u.ExpireAt, link)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) saveVPNFromPanel(ctx context.Context, telegramID int64, u *remnawave.PanelUser) error {
|
func (h *Handler) saveVPNFromPanel(ctx context.Context, telegramID int64, u *remnawave.PanelUser) error {
|
||||||
@@ -116,21 +118,22 @@ func (h *Handler) subscriptionLink(shortUUID string) string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) sendConfigMessage(chatID int64, days int, username string, expireAt time.Time, link string) {
|
func (h *Handler) sendConfigMessage(chatID, telegramID int64, days int, username string, expireAt time.Time, link string) {
|
||||||
text := fmt.Sprintf(
|
text := fmt.Sprintf(
|
||||||
"✅ Ваш VPN-конфиг готов\n\n"+
|
"✅ VPN готов\n\n"+
|
||||||
"Срок: %d дн. (до %s)\n"+
|
"⏱ Срок: %d дн. (до %s)\n"+
|
||||||
"Логин: %s\n",
|
"👤 Логин: %s\n",
|
||||||
days,
|
days,
|
||||||
expireAt.Local().Format("02.01.2006 15:04"),
|
expireAt.Local().Format("02.01.2006 15:04"),
|
||||||
username,
|
username,
|
||||||
)
|
)
|
||||||
if link != "" {
|
if link != "" {
|
||||||
text += "\n🔗 Ссылка подписки (добавьте в приложение):\n" + link
|
text += "\n🔗 Ссылка подписки — кнопка ниже или:\n" + link
|
||||||
} else {
|
} else {
|
||||||
text += "\n⚠️ Ссылка подписки не настроена. Администратору нужно указать REMNAWAVE_SUBSCRIPTION_URL в .env"
|
text += "\n\n⚠️ Ссылка не настроена — REMNAWAVE_SUBSCRIPTION_URL в .env"
|
||||||
}
|
}
|
||||||
text += "\n\nИмпортируйте ссылку в V2rayNG, Hiddify, Streisand и др."
|
msg := tgbotapi.NewMessage(chatID, text)
|
||||||
h.sendText(chatID, text)
|
msg.ReplyMarkup = configResultKeyboard(h.cfg, telegramID, h.admin, link)
|
||||||
|
h.send(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user