Telegram Webhookによるボット操作機能を追加(課題の追加・完了・一覧・スヌーズ対応)

This commit is contained in:
2026-06-07 12:04:47 +09:00
parent c7f4c40964
commit bd600c24c9
10 changed files with 812 additions and 20 deletions

View File

@@ -50,40 +50,92 @@ func (s *NotificationService) UpdateUserSettings(userID uint, settings *models.U
settings.ID = existing.ID
return database.GetDB().Save(settings).Error
}
func (s *NotificationService) SendTelegramNotification(chatID, message string) error {
// InlineKeyboardMarkup represents a Telegram inline keyboard attached to a message.
type InlineKeyboardMarkup struct {
InlineKeyboard [][]InlineKeyboardButton `json:"inline_keyboard"`
}
// InlineKeyboardButton is a single button in an inline keyboard row.
type InlineKeyboardButton struct {
Text string `json:"text"`
CallbackData string `json:"callback_data"`
}
func (s *NotificationService) sendTelegramRequest(endpoint string, payload interface{}) error {
if s.telegramBotToken == "" {
return fmt.Errorf("telegram bot token is not configured")
}
if chatID == "" {
return fmt.Errorf("telegram chat ID is empty")
}
apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/sendMessage", s.telegramBotToken)
payload := map[string]string{
"chat_id": chatID,
"text": message,
"parse_mode": "HTML",
}
apiURL := fmt.Sprintf("https://api.telegram.org/bot%s/%s", s.telegramBotToken, endpoint)
jsonPayload, err := json.Marshal(payload)
if err != nil {
return err
}
resp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(jsonPayload))
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("telegram API returned status %d", resp.StatusCode)
return fmt.Errorf("telegram API %s returned status %d", endpoint, resp.StatusCode)
}
return nil
}
func (s *NotificationService) SendTelegramNotification(chatID, message string) error {
if chatID == "" {
return fmt.Errorf("telegram chat ID is empty")
}
return s.SendMessageWithKeyboard(chatID, message, nil)
}
// SendMessageWithKeyboard sends a Telegram message with an optional inline keyboard.
func (s *NotificationService) SendMessageWithKeyboard(chatID, text string, keyboard *InlineKeyboardMarkup) error {
if chatID == "" {
return fmt.Errorf("telegram chat ID is empty")
}
payload := map[string]interface{}{
"chat_id": chatID,
"text": text,
"parse_mode": "HTML",
}
if keyboard != nil {
payload["reply_markup"] = keyboard
}
return s.sendTelegramRequest("sendMessage", payload)
}
// AnswerCallbackQuery acknowledges a Telegram inline button press.
func (s *NotificationService) AnswerCallbackQuery(callbackID, text string) error {
payload := map[string]interface{}{
"callback_query_id": callbackID,
"show_alert": false,
}
if text != "" {
payload["text"] = text
}
return s.sendTelegramRequest("answerCallbackQuery", payload)
}
// EditMessageRemoveKeyboard removes the inline keyboard from an existing message.
func (s *NotificationService) EditMessageRemoveKeyboard(chatID string, messageID int64) error {
payload := map[string]interface{}{
"chat_id": chatID,
"message_id": messageID,
"reply_markup": map[string]interface{}{"inline_keyboard": []interface{}{}},
}
return s.sendTelegramRequest("editMessageReplyMarkup", payload)
}
// FindUserIDByChatID looks up a UserID by Telegram chat ID.
func (s *NotificationService) FindUserIDByChatID(chatID string) (uint, error) {
var settings models.UserNotificationSettings
result := database.GetDB().Where("telegram_chat_id = ? AND telegram_enabled = ?", chatID, true).First(&settings)
if result.Error != nil {
return 0, fmt.Errorf("user not found for chat ID %s", chatID)
}
return settings.UserID, nil
}
func (s *NotificationService) SendAssignmentReminder(userID uint, assignment *models.Assignment) error {
settings, err := s.GetUserSettings(userID)
if err != nil {
@@ -201,10 +253,20 @@ func (s *NotificationService) SendUrgentReminder(userID uint, assignment *models
assignment.DueDate.Format("2006/01/02 15:04"),
)
keyboard := &InlineKeyboardMarkup{
InlineKeyboard: [][]InlineKeyboardButton{
{
{Text: "✅ 完了", CallbackData: fmt.Sprintf("done:%d", assignment.ID)},
{Text: "⏰ 15分後", CallbackData: fmt.Sprintf("snooze15:%d", assignment.ID)},
{Text: "💬 今やってる", CallbackData: fmt.Sprintf("working:%d", assignment.ID)},
},
},
}
var errors []string
if settings.TelegramEnabled && settings.TelegramChatID != "" {
if err := s.SendTelegramNotification(settings.TelegramChatID, message); err != nil {
if err := s.SendMessageWithKeyboard(settings.TelegramChatID, message, keyboard); err != nil {
errors = append(errors, fmt.Sprintf("Telegram: %v", err))
}
}