Files
pulse-api/internal/bot/handlers.go

387 lines
9.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package bot
import (
"fmt"
"strconv"
"strings"
"time"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
"github.com/daniil/homelab-api/internal/model"
"github.com/daniil/homelab-api/internal/repository"
)
func (b *Bot) handleMessage(msg *tgbotapi.Message) {
if !msg.IsCommand() {
return
}
cmd := msg.Command()
// Handle commands like /done_123 or /check_123
if strings.HasPrefix(cmd, "done_") {
b.handleDoneByID(msg, strings.TrimPrefix(cmd, "done_"))
return
}
if strings.HasPrefix(cmd, "check_") {
b.handleCheckByID(msg, strings.TrimPrefix(cmd, "check_"))
return
}
switch cmd {
case "start":
b.handleStart(msg)
case "tasks":
b.handleTasks(msg)
case "habits":
b.handleHabits(msg)
case "done":
b.handleDone(msg)
case "check":
b.handleCheck(msg)
case "help":
b.handleHelp(msg)
}
}
func (b *Bot) handleCallback(callback *tgbotapi.CallbackQuery) {
data := callback.Data
chatID := callback.Message.Chat.ID
messageID := callback.Message.MessageID
user, err := b.userRepo.GetByTelegramChatID(chatID)
if err != nil {
b.answerCallback(callback.ID, "❌ Аккаунт не найден")
return
}
parts := strings.Split(data, "_")
if len(parts) < 2 {
return
}
action := parts[0]
id, _ := strconv.ParseInt(parts[1], 10, 64)
switch action {
case "donetask":
err = b.taskRepo.Complete(id, user.ID)
if err != nil {
b.answerCallback(callback.ID, "❌ Ошибка")
return
}
b.answerCallback(callback.ID, "✅ Задача выполнена!")
b.refreshTasksMessage(chatID, messageID, user.ID)
case "deltask":
err = b.taskRepo.Delete(id, user.ID)
if err != nil {
b.answerCallback(callback.ID, "❌ Ошибка")
return
}
b.answerCallback(callback.ID, "🗑 Задача удалена")
b.refreshTasksMessage(chatID, messageID, user.ID)
case "checkhabit":
log := &model.HabitLog{
HabitID: id,
UserID: user.ID,
Date: time.Now(),
Count: 1,
}
err = b.habitRepo.CreateLog(log)
if err != nil {
b.answerCallback(callback.ID, "❌ Ошибка")
return
}
b.answerCallback(callback.ID, "✅ Привычка отмечена!")
b.refreshHabitsMessage(chatID, messageID, user.ID)
}
}
func (b *Bot) answerCallback(callbackID, text string) {
callback := tgbotapi.NewCallback(callbackID, text)
b.api.Request(callback)
}
func (b *Bot) refreshTasksMessage(chatID int64, messageID int, userID int64) {
tasks, err := b.taskRepo.GetTodayTasks(userID)
if err != nil {
return
}
text, keyboard := b.buildTasksMessage(tasks)
edit := tgbotapi.NewEditMessageText(chatID, messageID, text)
edit.ParseMode = "HTML"
if keyboard != nil {
edit.ReplyMarkup = keyboard
}
b.api.Send(edit)
}
func (b *Bot) refreshHabitsMessage(chatID int64, messageID int, userID int64) {
habits, _ := b.habitRepo.ListByUser(userID, false)
text, keyboard := b.buildHabitsMessage(habits, userID)
edit := tgbotapi.NewEditMessageText(chatID, messageID, text)
edit.ParseMode = "HTML"
if keyboard != nil {
edit.ReplyMarkup = keyboard
}
b.api.Send(edit)
}
func (b *Bot) buildTasksMessage(tasks []model.Task) (string, *tgbotapi.InlineKeyboardMarkup) {
if len(tasks) == 0 {
return "✨ На сегодня задач нет!", nil
}
text := "📋 <b>Задачи на сегодня:</b>\n\n"
var rows [][]tgbotapi.InlineKeyboardButton
for _, task := range tasks {
priority := ""
switch task.Priority {
case 3:
priority = "🔴 "
case 2:
priority = "🟡 "
case 1:
priority = "🔵 "
}
status := "⬜"
if task.CompletedAt.Valid {
status = "✅"
}
text += fmt.Sprintf("%s %s%s <b>%s</b>\n", status, priority, task.Icon, task.Title)
if task.Description != "" {
text += fmt.Sprintf(" <i>%s</i>\n", task.Description)
}
text += "\n"
// Add buttons only for incomplete tasks
if !task.CompletedAt.Valid {
row := []tgbotapi.InlineKeyboardButton{
tgbotapi.NewInlineKeyboardButtonData("✅ Выполнить", fmt.Sprintf("donetask_%d", task.ID)),
tgbotapi.NewInlineKeyboardButtonData("🗑 Удалить", fmt.Sprintf("deltask_%d", task.ID)),
}
rows = append(rows, row)
}
}
if len(rows) == 0 {
return text, nil
}
keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...)
return text, &keyboard
}
func (b *Bot) buildHabitsMessage(habits []model.Habit, userID int64) (string, *tgbotapi.InlineKeyboardMarkup) {
today := int(time.Now().Weekday())
var todayHabits []model.Habit
for _, habit := range habits {
if habit.Frequency == "daily" {
todayHabits = append(todayHabits, habit)
} else {
for _, day := range habit.TargetDays {
if day == today {
todayHabits = append(todayHabits, habit)
break
}
}
}
}
if len(todayHabits) == 0 {
return "✨ На сегодня привычек нет!", nil
}
text := "🎯 <b>Привычки на сегодня:</b>\n\n"
var rows [][]tgbotapi.InlineKeyboardButton
for _, habit := range todayHabits {
completed, _ := b.habitRepo.IsHabitCompletedToday(habit.ID, userID)
status := "⬜"
if completed {
status = "✅"
}
text += fmt.Sprintf("%s %s <b>%s</b>\n", status, habit.Icon, habit.Name)
if habit.Description != "" {
text += fmt.Sprintf(" <i>%s</i>\n", habit.Description)
}
text += "\n"
if !completed {
row := []tgbotapi.InlineKeyboardButton{
tgbotapi.NewInlineKeyboardButtonData(fmt.Sprintf("✅ %s", habit.Name), fmt.Sprintf("checkhabit_%d", habit.ID)),
}
rows = append(rows, row)
}
}
if len(rows) == 0 {
return text, nil
}
keyboard := tgbotapi.NewInlineKeyboardMarkup(rows...)
return text, &keyboard
}
func (b *Bot) handleStart(msg *tgbotapi.Message) {
text := fmt.Sprintf(`👋 Привет! Я бот Pulse.
Твой Chat ID: <code>%d</code>
Скопируй его и вставь в настройках Pulse для получения уведомлений.
Доступные команды:
/tasks — задачи на сегодня
/habits — привычки на сегодня
/help — справка`, msg.Chat.ID)
b.SendMessage(msg.Chat.ID, text)
}
func (b *Bot) handleHelp(msg *tgbotapi.Message) {
text := `📚 <b>Справка по командам:</b>
/start — получить твой Chat ID
/tasks — список задач на сегодня
/habits — список привычек
💡 Чтобы получать уведомления, добавь свой Chat ID в настройках Pulse.`
b.SendMessage(msg.Chat.ID, text)
}
func (b *Bot) handleTasks(msg *tgbotapi.Message) {
user, err := b.userRepo.GetByTelegramChatID(msg.Chat.ID)
if err != nil {
if err == repository.ErrUserNotFound {
b.SendMessage(msg.Chat.ID, "❌ Аккаунт не найден. Добавь свой Chat ID в настройках Pulse.")
return
}
b.SendMessage(msg.Chat.ID, "❌ Ошибка при получении данных")
return
}
tasks, err := b.taskRepo.GetTodayTasks(user.ID)
if err != nil {
b.SendMessage(msg.Chat.ID, "❌ Ошибка при получении задач")
return
}
text, keyboard := b.buildTasksMessage(tasks)
b.SendMessageWithKeyboard(msg.Chat.ID, text, keyboard)
}
func (b *Bot) handleHabits(msg *tgbotapi.Message) {
user, err := b.userRepo.GetByTelegramChatID(msg.Chat.ID)
if err != nil {
if err == repository.ErrUserNotFound {
b.SendMessage(msg.Chat.ID, "❌ Аккаунт не найден. Добавь свой Chat ID в настройках Pulse.")
return
}
b.SendMessage(msg.Chat.ID, "❌ Ошибка при получении данных")
return
}
habits, err := b.habitRepo.ListByUser(user.ID, false)
if err != nil {
b.SendMessage(msg.Chat.ID, "❌ Ошибка при получении привычек")
return
}
text, keyboard := b.buildHabitsMessage(habits, user.ID)
b.SendMessageWithKeyboard(msg.Chat.ID, text, keyboard)
}
func (b *Bot) handleDone(msg *tgbotapi.Message) {
args := msg.CommandArguments()
if args == "" {
b.SendMessage(msg.Chat.ID, "❌ Укажи ID задачи: /done <id>")
return
}
b.handleDoneByID(msg, args)
}
func (b *Bot) handleDoneByID(msg *tgbotapi.Message, idStr string) {
user, err := b.userRepo.GetByTelegramChatID(msg.Chat.ID)
if err != nil {
b.SendMessage(msg.Chat.ID, "❌ Аккаунт не найден")
return
}
taskID, _ := strconv.ParseInt(idStr, 10, 64)
if taskID == 0 {
b.SendMessage(msg.Chat.ID, "❌ Неверный ID задачи")
return
}
err = b.taskRepo.Complete(taskID, user.ID)
if err != nil {
if err == repository.ErrTaskNotFound {
b.SendMessage(msg.Chat.ID, "❌ Задача не найдена")
return
}
b.SendMessage(msg.Chat.ID, "❌ Ошибка при выполнении задачи")
return
}
b.SendMessage(msg.Chat.ID, "✅ Задача выполнена!")
}
func (b *Bot) handleCheck(msg *tgbotapi.Message) {
args := msg.CommandArguments()
if args == "" {
b.SendMessage(msg.Chat.ID, "❌ Укажи ID привычки: /check <id>")
return
}
b.handleCheckByID(msg, args)
}
func (b *Bot) handleCheckByID(msg *tgbotapi.Message, idStr string) {
user, err := b.userRepo.GetByTelegramChatID(msg.Chat.ID)
if err != nil {
b.SendMessage(msg.Chat.ID, "❌ Аккаунт не найден")
return
}
habitID, _ := strconv.ParseInt(idStr, 10, 64)
if habitID == 0 {
b.SendMessage(msg.Chat.ID, "❌ Неверный ID привычки")
return
}
_, err = b.habitRepo.GetByID(habitID, user.ID)
if err != nil {
if err == repository.ErrHabitNotFound {
b.SendMessage(msg.Chat.ID, "❌ Привычка не найдена")
return
}
b.SendMessage(msg.Chat.ID, "❌ Ошибка при получении привычки")
return
}
log := &model.HabitLog{
HabitID: habitID,
UserID: user.ID,
Date: time.Now(),
Count: 1,
}
err = b.habitRepo.CreateLog(log)
if err != nil {
b.SendMessage(msg.Chat.ID, "❌ Ошибка при отметке привычки")
return
}
b.SendMessage(msg.Chat.ID, "✅ Привычка отмечена!")
}