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 := "📋 Задачи на сегодня:\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 %s\n", status, priority, task.Icon, task.Title)
if task.Description != "" {
text += fmt.Sprintf(" %s\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 := "🎯 Привычки на сегодня:\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 %s\n", status, habit.Icon, habit.Name)
if habit.Description != "" {
text += fmt.Sprintf(" %s\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: %d
Скопируй его и вставь в настройках Pulse для получения уведомлений.
Доступные команды:
/tasks — задачи на сегодня
/habits — привычки на сегодня
/help — справка`, msg.Chat.ID)
b.SendMessage(msg.Chat.ID, text)
}
func (b *Bot) handleHelp(msg *tgbotapi.Message) {
text := `📚 Справка по командам:
/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 ")
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 ")
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, "✅ Привычка отмечена!")
}