166 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			166 lines
		
	
	
		
			4.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package crons
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"database/sql"
 | |
| 	"errors"
 | |
| 	"gralias/broker"
 | |
| 	"gralias/models"
 | |
| 	"gralias/repos"
 | |
| 	"log/slog"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| type CronManager struct {
 | |
| 	repo repos.AllRepos
 | |
| 	log  *slog.Logger
 | |
| }
 | |
| 
 | |
| func NewCronManager(repo repos.AllRepos, log *slog.Logger) *CronManager {
 | |
| 	return &CronManager{
 | |
| 		repo: repo,
 | |
| 		log:  log,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (cm *CronManager) Start() {
 | |
| 	ticker := time.NewTicker(30 * time.Second)
 | |
| 	go func() {
 | |
| 		for range ticker.C {
 | |
| 			cm.CleanupRooms()
 | |
| 			cm.CleanupActions()
 | |
| 			cm.CleanupPlayersRoom()
 | |
| 			ticker.Reset(30 * time.Second)
 | |
| 		}
 | |
| 	}()
 | |
| }
 | |
| 
 | |
| func (cm *CronManager) CleanupRooms() {
 | |
| 	ctx, tx, err := cm.repo.InitTx(context.Background())
 | |
| 	if err != nil {
 | |
| 		cm.log.Error("failed to init transaction", "err", err)
 | |
| 		return
 | |
| 	}
 | |
| 	defer func() {
 | |
| 		if r := recover(); r != nil {
 | |
| 			if err := tx.Rollback(); err != nil {
 | |
| 				cm.log.Error("failed to rollback transaction", "err", err)
 | |
| 			}
 | |
| 			panic(r)
 | |
| 		}
 | |
| 	}()
 | |
| 	rooms, err := cm.repo.RoomList(ctx)
 | |
| 	if err != nil {
 | |
| 		cm.log.Error("failed to get rooms list", "err", err)
 | |
| 		if err := tx.Rollback(); err != nil {
 | |
| 			cm.log.Error("failed to rollback transaction", "err", err)
 | |
| 		}
 | |
| 		return
 | |
| 	}
 | |
| 	roomListChange := false
 | |
| 	for _, room := range rooms {
 | |
| 		players, err := cm.repo.PlayerListByRoom(ctx, room.ID)
 | |
| 		if err != nil {
 | |
| 			cm.log.Error("failed to get players for room", "room_id", room.ID, "err", err)
 | |
| 			continue
 | |
| 		}
 | |
| 		if len(players) == 0 {
 | |
| 			cm.log.Info("deleting empty room", "room_id", room.ID)
 | |
| 			if err := cm.repo.RoomDeleteByID(ctx, room.ID); err != nil {
 | |
| 				cm.log.Error("failed to delete empty room", "room_id", room.ID, "err", err)
 | |
| 			}
 | |
| 			if err := cm.repo.SettingsDeleteByRoomID(ctx, room.ID); err != nil {
 | |
| 				cm.log.Error("failed to delete settings for empty room", "room_id", room.ID, "err", err)
 | |
| 			}
 | |
| 			roomListChange = true
 | |
| 			continue
 | |
| 		}
 | |
| 		creatorInRoom := false
 | |
| 		for _, player := range players {
 | |
| 			if player.Username == room.CreatorName {
 | |
| 				creatorInRoom = true
 | |
| 				break
 | |
| 			}
 | |
| 		}
 | |
| 		isInactive := false
 | |
| 		// If the creator is in the room and the room is more than one hour old, check for inactivity
 | |
| 		if creatorInRoom && time.Since(room.CreatedAt) > time.Hour {
 | |
| 			lastActionTime, err := cm.repo.ActionGetLastTimeByRoomID(ctx, room.ID)
 | |
| 			if err != nil && !errors.Is(err, sql.ErrNoRows) {
 | |
| 				cm.log.Error("failed to get last action time for room", "room_id", room.ID, "err", err)
 | |
| 				// Skip setting isInactive and proceed
 | |
| 			} else {
 | |
| 				// If there are no actions, lastActionTime is the zero value (or from sql.ErrNoRows we get zero as well)
 | |
| 				if lastActionTime.IsZero() {
 | |
| 					isInactive = true
 | |
| 				} else if time.Since(lastActionTime) > time.Hour {
 | |
| 					isInactive = true
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		// If the creator is not in the room or the room is inactive, it's time to delete
 | |
| 		if !creatorInRoom || isInactive {
 | |
| 			reason := "creator left"
 | |
| 			if isInactive {
 | |
| 				reason = "inactive"
 | |
| 			}
 | |
| 			cm.log.Info("deleting room", "room_id", room.ID, "reason", reason)
 | |
| 			for _, player := range players {
 | |
| 				if player.IsBot {
 | |
| 					if err := cm.repo.PlayerDelete(ctx, room.ID); err != nil {
 | |
| 						cm.log.Error("failed to delete bot player", "room_id", room.ID, "username", player.Username, "err", err)
 | |
| 					}
 | |
| 				} else {
 | |
| 					if err := cm.repo.PlayerExitRoom(ctx, player.Username); err != nil {
 | |
| 						cm.log.Error("failed to update player room", "room_id", room.ID, "username", player.Username, "err", err)
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			if err := cm.repo.RoomDeleteByID(ctx, room.ID); err != nil {
 | |
| 				cm.log.Error("failed to delete room", "room_id", room.ID, "reason", reason, "err", err)
 | |
| 			}
 | |
| 			if err := cm.repo.SettingsDeleteByRoomID(ctx, room.ID); err != nil {
 | |
| 				cm.log.Error("failed to delete settings for room", "room_id", room.ID, "reason", reason, "err", err)
 | |
| 			}
 | |
| 			roomListChange = true
 | |
| 			// Move to the next room
 | |
| 			continue
 | |
| 		}
 | |
| 	}
 | |
| 	if err := tx.Commit(); err != nil {
 | |
| 		cm.log.Error("failed to commit transaction", "err", err)
 | |
| 	}
 | |
| 	if roomListChange {
 | |
| 		broker.Notifier.Notifier <- broker.NotificationEvent{
 | |
| 			EventName: models.NotifyRoomListUpdate,
 | |
| 			Payload:   "",
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (cm *CronManager) CleanupActions() {
 | |
| 	ctx, tx, err := cm.repo.InitTx(context.Background())
 | |
| 	if err != nil {
 | |
| 		cm.log.Error("failed to init transaction for actions cleanup", "err", err)
 | |
| 		return
 | |
| 	}
 | |
| 	defer func() {
 | |
| 		if r := recover(); r != nil {
 | |
| 			if err := tx.Rollback(); err != nil {
 | |
| 				cm.log.Error("failed to rollback transaction for actions cleanup", "err", err)
 | |
| 			}
 | |
| 			panic(r)
 | |
| 		}
 | |
| 	}()
 | |
| 	if err := cm.repo.ActionDeleteOrphaned(ctx); err != nil {
 | |
| 		cm.log.Error("failed to delete orphaned actions", "err", err)
 | |
| 		if err := tx.Rollback(); err != nil {
 | |
| 			cm.log.Error("failed to rollback transaction for actions cleanup", "err", err)
 | |
| 		}
 | |
| 		return
 | |
| 	}
 | |
| 	if err := tx.Commit(); err != nil {
 | |
| 		cm.log.Error("failed to commit transaction for actions cleanup", "err", err)
 | |
| 	}
 | |
| }
 | 
