Skip to content

Commit

Permalink
Switch to private message-based workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
pomo-mondreganto committed Nov 13, 2024
1 parent fe069c3 commit 74e5dfd
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 33 deletions.
32 changes: 11 additions & 21 deletions internal/api/service.go
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
package api

import (
"context"
"errors"
"fmt"
"net/http"

"github.com/C4T-BuT-S4D/shpaga/internal/authutil"
"github.com/C4T-BuT-S4D/shpaga/internal/config"
"github.com/C4T-BuT-S4D/shpaga/internal/models"
"github.com/C4T-BuT-S4D/shpaga/internal/storage"
"github.com/go-resty/resty/v2"
"github.com/labstack/echo/v4"
Expand Down Expand Up @@ -56,6 +53,12 @@ func (s *Service) HandleOAuthCallback() echo.HandlerFunc {
"user_id": state.UserID,
})

user, err := s.storage.GetUser(c.Request().Context(), state.UserID)
if err != nil {
logger.WithError(err).Error("failed to get user")
return c.JSON(http.StatusInternalServerError, echo.Map{"error": "failed to get user"})
}

logger.Info("received oauth callback")

token, err := s.getOAuthToken(code)
Expand All @@ -82,8 +85,11 @@ func (s *Service) HandleOAuthCallback() echo.HandlerFunc {

logger.Info("successfully set oauth token")

if err := s.removeGreetings(c.Request().Context(), state); err != nil {
logger.WithError(err).Error("failed to remove greetings")
if _, err := s.bot.Send(
&telebot.User{ID: user.TelegramID},
"Successfully logged in, you can use the chat now.",
); err != nil {
logger.WithError(err).Error("failed to send success message")
}

return c.String(http.StatusOK, "Successfully authorized, you can close this page.")
Expand Down Expand Up @@ -135,19 +141,3 @@ func (s *Service) getUser(token string) (int64, error) {

return resp.Result().(*oauthUserResponse).ID, nil
}

func (s *Service) removeGreetings(ctx context.Context, state *authutil.State) error {
msgs, err := s.storage.GetMessagesForUser(ctx, state.UserID, state.ChatID, models.MessageTypeGreeting)
if err != nil {
return fmt.Errorf("getting messages: %w", err)
}

var finalErr error
for _, msg := range msgs {
if err := s.bot.Delete(msg); err != nil {
finalErr = errors.Join(finalErr, fmt.Errorf("removing message %v", msg))
}
}

return nil
}
98 changes: 86 additions & 12 deletions internal/monitor/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package monitor
import (
"context"
"fmt"
"slices"
"strconv"
"strings"
"time"

"github.com/C4T-BuT-S4D/shpaga/internal/authutil"
Expand All @@ -14,6 +16,12 @@ import (
"gopkg.in/telebot.v4"
)

var allowedChatTypes = []telebot.ChatType{
telebot.ChatGroup,
telebot.ChatSuperGroup,
telebot.ChatPrivate,
}

type Monitor struct {
config *config.Config
storage *storage.Storage
Expand Down Expand Up @@ -45,17 +53,21 @@ func (m *Monitor) HandleAnyUpdate(c telebot.Context) error {
uc.L().Errorf("failed to update last update: %v", err)
}

if c.Chat().Type != telebot.ChatGroup && c.Chat().Type != telebot.ChatSuperGroup {
uc.L().Debugf("ignoring update from non-group chat %d", c.Chat().ID)
if c.Message() == nil {
uc.L().Debugf("ignoring update without message")
return nil
}

if c.Message() == nil {
uc.L().Debugf("ignoring update without message")
if !slices.Contains(allowedChatTypes, c.Chat().Type) {
uc.L().Debugf("ignoring update from chat %d (type %v)", c.Chat().ID, c.Chat().Type)
return nil
}

switch {
case c.Chat().Type == telebot.ChatPrivate:
if err := m.HandlePrivateMessage(uc); err != nil {
uc.L().Errorf("failed to handle private message: %v", err)
}
case c.Message().UserJoined != nil:
if err := m.HandleUserJoined(uc); err != nil {
uc.L().Errorf("failed to handle user joined: %v", err)
Expand All @@ -65,15 +77,15 @@ func (m *Monitor) HandleAnyUpdate(c telebot.Context) error {
uc.L().Errorf("failed to handle chat left: %v", err)
}
default:
if err := m.HandleMessage(uc); err != nil {
if err := m.HandleChatMessage(uc); err != nil {
uc.L().Errorf("failed to handle message: %v", err)
}
}

return nil
}

func (m *Monitor) HandleMessage(uc *UpdateContext) error {
func (m *Monitor) HandleChatMessage(uc *UpdateContext) error {
user, err := m.storage.GetOrCreateUser(uc, uc.TC().Chat().ID, uc.TC().Sender().ID, models.UserStatusActive)
if err != nil {
return fmt.Errorf("failed to get or create user: %w", err)
Expand Down Expand Up @@ -120,21 +132,20 @@ func (m *Monitor) HandleUserJoined(uc *UpdateContext) error {
case models.UserStatusJustJoined:
uc.L().Infof("User %d is just joined, sending welcome message", user.TelegramID)

url, err := authutil.GetCTFTimeOAuthURL(user.ID, uc.Chat().ID, m.config)
if err != nil {
return fmt.Errorf("failed to get oauth url: %w", err)
}

name := ""
if uc.Sender().FirstName != "" || uc.Sender().LastName != "" {
name = fmt.Sprintf("%s %s", uc.Sender().FirstName, uc.Sender().LastName)
} else {
name = uc.Sender().Username
}

botName := uc.Bot().(*telebot.Bot).Me.Username
url := fmt.Sprintf("t.me/%s?start=%d", botName, user.ChatID)

greeting := fmt.Sprintf(
`Welcome to the chat, [%s](tg://user?id=%d)\! `+
`Please, press the button below to log in with [CTFTime](https://ctftime.org)\. `+
`Please, press the button below, start the bot and follow the instructions `+
`to log in with [CTFTime](https://ctftime.org)\. `+
`You won't be able to send messages until you do so\. `+
`The bot will kick you in %d minutes if you don't login\.`,
name,
Expand Down Expand Up @@ -179,6 +190,69 @@ func (m *Monitor) HandleChatLeft(uc *UpdateContext) error {
return nil
}

func (m *Monitor) HandlePrivateMessage(uc *UpdateContext) error {
uc.L().Infof(
"User %s (%d) sent private message %v",
uc.Sender().Username,
uc.Sender().ID,
uc.Message().Text,
)

tokens := strings.Fields(uc.Message().Text)
if len(tokens) < 2 || tokens[0] != "/start" {
uc.L().Infof("Ignoring non-start message %v", uc.Message().Text)
return nil
}

chatID, err := strconv.ParseInt(tokens[1], 10, 64)
if err != nil {
uc.L().Errorf("failed to parse chat id: %v", err)
if err := uc.TC().Send("Invalid chat id"); err != nil {
uc.L().Errorf("failed to send message: %v", err)
}
return nil
}

user, err := m.storage.GetChatUser(uc, chatID, uc.Sender().ID)
if err != nil {
return fmt.Errorf("failed to get user: %w", err)
}

if user.Status != models.UserStatusJustJoined {
uc.L().Warnf("User status is %v, ignoring", user.Status)
if err := uc.TC().Send(fmt.Sprintf("You have an unexpected status %v", user.Status)); err != nil {
uc.L().Errorf("failed to send message: %v", err)
}
return nil
}

url, err := authutil.GetCTFTimeOAuthURL(user.ID, user.ChatID, m.config)
if err != nil {
return fmt.Errorf("getting oauth url: %w", err)
}

text := "Follow the link below to log in with CTFTime"
markup := &telebot.ReplyMarkup{}
markup.Inline(markup.Row(markup.URL("Log in with CTFTime", url)))

if err := uc.TC().Send(text, markup); err != nil {
return fmt.Errorf("sending login message: %w", err)
}

greetings, err := m.storage.GetMessagesForUser(uc, user.ID, user.ChatID, models.MessageTypeGreeting)
if err != nil {
return fmt.Errorf("getting greetings: %w", err)
}

for _, msg := range greetings {
if err := m.bot.Delete(msg); err != nil {
uc.L().Errorf("failed to delete greeting message %v: %v", msg, err)
}
}

return nil
}

func (m *Monitor) RunCleaner(ctx context.Context) {
t := time.NewTicker(time.Minute)
defer t.Stop()
Expand Down
12 changes: 12 additions & 0 deletions internal/storage/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,18 @@ func (s *Storage) GetUser(ctx context.Context, userID string) (*models.User, err
return &user, nil
}

func (s *Storage) GetChatUser(ctx context.Context, chatID, telegramID int64) (*models.User, error) {
var user models.User
if err := s.
getDB(ctx).
Where("chat_id = ? AND telegram_id = ?", chatID, telegramID).
First(&user).
Error; err != nil {
return nil, fmt.Errorf("getting user: %w", err)
}
return &user, nil
}

func (s *Storage) GetOrCreateUser(ctx context.Context, chatID, telegramID int64, defaultStatus models.UserStatus) (*models.User, error) {
userToCreate := &models.User{
ID: uuid.New().String(),
Expand Down

0 comments on commit 74e5dfd

Please sign in to comment.