387 lines
9.9 KiB
Go
387 lines
9.9 KiB
Go
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, "✅ Привычка отмечена!")
|
||
}
|