Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cmd/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,9 @@ func initializeRoutes(engine *gin.Engine) {
engine.GET("/v2/ranking/queue/:id", handlers.CreateHandler(handlers.GetRankingQueueMapset))
engine.POST("/v2/ranking/queue/:id/submit", middleware.RequireAuth, handlers.CreateHandler(handlers.SubmitMapsetToRankingQueue))
engine.POST("/v2/ranking/queue/:id/remove", middleware.RequireAuth, handlers.CreateHandler(handlers.RemoveFromRankingQueue))
engine.GET("/v2/ranking/queue/:id/comments", handlers.CreateHandler(handlers.GetRankingQueueComments))
engine.GET("/v2/ranking/queue/:id/comments", middleware.AllowAuth, handlers.CreateHandler(handlers.GetRankingQueueComments))
engine.POST("/v2/ranking/queue/:id/comment", middleware.RequireAuth, handlers.CreateHandler(handlers.AddRankingQueueComment))
engine.POST("/v2/ranking/queue/:id/private-comment", middleware.RequireAuth, handlers.CreateHandler(handlers.AddPrivateRankingQueueComment))
engine.POST("/v2/ranking/queue/comment/:id/edit", middleware.RequireAuth, handlers.CreateHandler(handlers.EditRankingQueueComment))
engine.POST("/v2/ranking/queue/:id/vote", middleware.RequireAuth, handlers.CreateHandler(handlers.VoteForRankingQueueMapset))
engine.POST("/v2/ranking/queue/:id/deny", middleware.RequireAuth, handlers.CreateHandler(handlers.DenyRankingQueueMapset))
Expand Down
5 changes: 5 additions & 0 deletions cmd/database/migrations/29_private_ranking_messages.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ALTER TABLE mapset_ranking_queue_comments
DROP COLUMN is_anonymous;

ALTER TABLE mapset_ranking_queue_comments
DROP COLUMN is_private;
5 changes: 5 additions & 0 deletions cmd/database/migrations/29_private_ranking_messages.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
ALTER TABLE mapset_ranking_queue_comments
ADD COLUMN is_anonymous TINYINT(1) NOT NULL DEFAULT 0 AFTER game_mode;

ALTER TABLE mapset_ranking_queue_comments
ADD COLUMN is_private TINYINT(1) NOT NULL DEFAULT 0 AFTER game_mode;
10 changes: 7 additions & 3 deletions db/mapset_ranking_queue_comments.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package db

import (
"time"

"github.com/Quaver/api2/enums"
"gorm.io/gorm"
"time"
)

type RankingQueueAction int8
Expand All @@ -29,7 +30,10 @@ type MapsetRankingQueueComment struct {
DateLastUpdated int64 `gorm:"date_last_updated" json:"-"`
DateLastUpdatedJSON time.Time `gorm:"-:all" json:"date_last_updated"`
GameMode *enums.GameMode `gorm:"column:game_mode" json:"game_mode"`
IsPrivate bool `gorm:"column:is_private" json:"is_private"`
IsAnonymous bool `gorm:"column:is_anonymous" json:"is_anonymous"`
User *User `gorm:"foreignKey:UserId; references:Id" json:"user,omitempty"`
AnonymousAuthor *User `gorm:"-" json:"anonymous_author,omitempty"`
}

func (*MapsetRankingQueueComment) TableName() string {
Expand Down Expand Up @@ -73,12 +77,12 @@ func (c *MapsetRankingQueueComment) Edit(comment string) error {
}

// GetRankingQueueComments Retrieves the ranking queue comments for a given mapset
func GetRankingQueueComments(mapsetId int) ([]*MapsetRankingQueueComment, error) {
func GetRankingQueueComments(mapsetId int, includePrivate bool) ([]*MapsetRankingQueueComment, error) {
var comments = make([]*MapsetRankingQueueComment, 0)

result := SQL.
Joins("User").
Where("mapset_id = ?", mapsetId).
Where("mapset_id = ? AND is_private = ?", mapsetId, includePrivate).
Order("id DESC").
Find(&comments)

Expand Down
7 changes: 6 additions & 1 deletion db/user_notifications.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,13 @@ func NewMapsetRankedNotification(mapset *Mapset) *UserNotification {

// Returns a new mapset ranking queue action notification
func NewMapsetActionNotification(mapset *Mapset, comment *MapsetRankingQueueComment) *UserNotification {
senderId := comment.UserId
if comment.IsAnonymous {
senderId = QuaverBotId
}

notif := &UserNotification{
SenderId: comment.UserId,
SenderId: senderId,
ReceiverId: mapset.CreatorID,
Type: NotificationMapsetAction,
Category: NotificationCategoryRankingQueue,
Expand Down
5 changes: 5 additions & 0 deletions handlers/ranking.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ func GetRankingQueue(c *gin.Context) *APIError {
return APIErrorServerError("Error retrieving ranking queue count", err)
}

for _, mapset := range rankingQueue {
mapset.Votes = prepareRankingQueueCommentsForResponse(mapset.Votes, false)
mapset.Denies = prepareRankingQueueCommentsForResponse(mapset.Denies, false)
}

c.JSON(http.StatusOK, gin.H{
"count": count,
"ranking_queue": rankingQueue,
Expand Down
82 changes: 51 additions & 31 deletions handlers/ranking_actions.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type rankingQueueRequestData struct {
QueueMapset *db.RankingQueueMapset
Comment string
GameMode enums.GameMode
Anonymous bool
}

// Validates and returns common data used for ranking queue action requests
Expand All @@ -36,8 +37,9 @@ func validateRankingQueueRequest(c *gin.Context) (*rankingQueueRequestData, *API
}

body := struct {
Comment string `form:"comment" json:"comment" binding:"required"`
GameMode enums.GameMode `form:"game_mode" json:"game_mode"`
Comment string `form:"comment" json:"comment" binding:"required"`
GameMode enums.GameMode `form:"game_mode" json:"game_mode"`
Anonymous bool `form:"anonymous" json:"anonymous"`
}{}

if err := c.ShouldBind(&body); err != nil {
Expand All @@ -52,6 +54,10 @@ func validateRankingQueueRequest(c *gin.Context) (*rankingQueueRequestData, *API
return nil, APIErrorForbidden("You do not have permission to perform this action.")
}

if body.Anonymous && !canUserAccessSupervisorRoute(c) {
return nil, APIErrorForbidden("Only ranking supervisors can take ranking actions anonymously.")
}

queueMapset, err := db.GetRankingQueueMapset(id)

if err != nil && err != gorm.ErrRecordNotFound {
Expand All @@ -68,6 +74,7 @@ func validateRankingQueueRequest(c *gin.Context) (*rankingQueueRequestData, *API
QueueMapset: queueMapset,
Comment: body.Comment,
GameMode: body.GameMode,
Anonymous: body.Anonymous,
}, nil
}

Expand Down Expand Up @@ -111,13 +118,14 @@ func VoteForRankingQueueMapset(c *gin.Context) *APIError {
}

newVoteAction := &db.MapsetRankingQueueComment{
UserId: data.User.Id,
User: data.User,
MapsetId: data.MapsetId,
ActionType: db.RankingQueueActionVote,
IsActive: true,
Comment: data.Comment,
GameMode: &data.GameMode,
UserId: data.User.Id,
User: data.User,
MapsetId: data.MapsetId,
ActionType: db.RankingQueueActionVote,
IsActive: true,
Comment: data.Comment,
GameMode: &data.GameMode,
IsAnonymous: data.Anonymous,
}

existingVotes = append(existingVotes, newVoteAction)
Expand Down Expand Up @@ -168,7 +176,7 @@ func VoteForRankingQueueMapset(c *gin.Context) *APIError {
return APIErrorServerError("Error updating vote count for queue mapset", err)
}

_ = webhooks.SendQueueWebhook(data.User, queueMapset.Mapset, db.RankingQueueActionVote)
sendRankingQueueActionWebhook(data, db.RankingQueueActionVote)
c.JSON(http.StatusOK, gin.H{"message": "You have successfully added a vote to this mapset."})
return nil
}
Expand Down Expand Up @@ -209,12 +217,13 @@ func DenyRankingQueueMapset(c *gin.Context) *APIError {
}

denyAction := &db.MapsetRankingQueueComment{
UserId: data.User.Id,
MapsetId: data.MapsetId,
ActionType: db.RankingQueueActionDeny,
IsActive: true,
Comment: data.Comment,
GameMode: &data.GameMode,
UserId: data.User.Id,
MapsetId: data.MapsetId,
ActionType: db.RankingQueueActionDeny,
IsActive: true,
Comment: data.Comment,
GameMode: &data.GameMode,
IsAnonymous: data.Anonymous,
}

if err := denyAction.Insert(); err != nil {
Expand All @@ -240,7 +249,7 @@ func DenyRankingQueueMapset(c *gin.Context) *APIError {
}
}

_ = webhooks.SendQueueWebhook(data.User, queueMapset.Mapset, db.RankingQueueActionDeny)
sendRankingQueueActionWebhook(data, db.RankingQueueActionDeny)
c.JSON(http.StatusOK, gin.H{"message": "You have successfully added a deny to this mapset."})
return nil
}
Expand All @@ -265,12 +274,13 @@ func BlacklistRankingQueueMapset(c *gin.Context) *APIError {
}

blacklistAction := &db.MapsetRankingQueueComment{
UserId: data.User.Id,
MapsetId: data.MapsetId,
ActionType: db.RankingQueueActionBlacklist,
IsActive: true,
Comment: data.Comment,
GameMode: &data.GameMode,
UserId: data.User.Id,
MapsetId: data.MapsetId,
ActionType: db.RankingQueueActionBlacklist,
IsActive: true,
Comment: data.Comment,
GameMode: &data.GameMode,
IsAnonymous: data.Anonymous,
}

if err := blacklistAction.Insert(); err != nil {
Expand All @@ -289,7 +299,7 @@ func BlacklistRankingQueueMapset(c *gin.Context) *APIError {
return APIErrorServerError("Error updating vote count for queue mapset", err)
}

_ = webhooks.SendQueueWebhook(data.User, queueMapset.Mapset, db.RankingQueueActionBlacklist)
sendRankingQueueActionWebhook(data, db.RankingQueueActionBlacklist)
c.JSON(http.StatusOK, gin.H{"message": "You have successfully blacklisted this mapset."})
return nil
}
Expand Down Expand Up @@ -318,12 +328,13 @@ func OnHoldRankingQueueMapset(c *gin.Context) *APIError {
}

onHoldAction := &db.MapsetRankingQueueComment{
UserId: data.User.Id,
MapsetId: data.MapsetId,
ActionType: db.RankingQueueActionOnHold,
IsActive: true,
Comment: data.Comment,
GameMode: &data.GameMode,
UserId: data.User.Id,
MapsetId: data.MapsetId,
ActionType: db.RankingQueueActionOnHold,
IsActive: true,
Comment: data.Comment,
GameMode: &data.GameMode,
IsAnonymous: data.Anonymous,
}

if err := onHoldAction.Insert(); err != nil {
Expand All @@ -342,7 +353,16 @@ func OnHoldRankingQueueMapset(c *gin.Context) *APIError {
return APIErrorServerError("Error updating vote count for queue mapset", err)
}

_ = webhooks.SendQueueWebhook(data.User, queueMapset.Mapset, db.RankingQueueActionOnHold)
sendRankingQueueActionWebhook(data, db.RankingQueueActionOnHold)
c.JSON(http.StatusOK, gin.H{"message": "You have successfully placed this mapset on hold."})
return nil
}

func sendRankingQueueActionWebhook(data *rankingQueueRequestData, action db.RankingQueueAction) {
if data.Anonymous {
_ = webhooks.SendAnonymousQueueWebhook(data.QueueMapset.Mapset, action)
return
}

_ = webhooks.SendQueueWebhook(data.User, data.QueueMapset.Mapset, action)
}
87 changes: 74 additions & 13 deletions handlers/ranking_comments.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package handlers

import (
"net/http"
"strconv"

"github.com/Quaver/api2/db"
"github.com/Quaver/api2/enums"
"github.com/Quaver/api2/webhooks"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"net/http"
"strconv"
)

// GetRankingQueueComments Returns all of the comments for a mapset in the ranking queue
Expand All @@ -19,19 +20,69 @@ func GetRankingQueueComments(c *gin.Context) *APIError {
return APIErrorBadRequest("You must provide a valid mapset id")
}

comments, err := db.GetRankingQueueComments(id)
canViewPrivate := canUserAccessSupervisorRoute(c)
comments, err := db.GetRankingQueueComments(id, canViewPrivate)

if err != nil {
return APIErrorServerError("Error getting ranking queue comments", err)
}

c.JSON(http.StatusOK, gin.H{"comments": comments})
c.JSON(http.StatusOK, gin.H{"comments": prepareRankingQueueCommentsForResponse(comments, canViewPrivate)})
return nil
}

func prepareRankingQueueCommentsForResponse(comments []*db.MapsetRankingQueueComment, canViewPrivate bool) []*db.MapsetRankingQueueComment {
prepared := make([]*db.MapsetRankingQueueComment, 0, len(comments))

for _, comment := range comments {
if !canViewPrivate {
continue
}

if comment.IsAnonymous {
avatarUrl := webhooks.QuaverLogo
redacted := *comment
redacted.User = &db.User{
Id: db.QuaverBotId,
Username: "QuaverBot",
UserGroups: enums.UserGroupBot,
AvatarUrl: &avatarUrl,
}

if canViewPrivate {
redacted.AnonymousAuthor = comment.User
} else {
redacted.UserId = db.QuaverBotId
}

prepared = append(prepared, &redacted)
continue
}

prepared = append(prepared, comment)
}

return prepared
}

// AddRankingQueueComment Inserts a ranking queue comment to the database
// Endpoint: POST /v2/ranking/queue/:id/comment
func AddRankingQueueComment(c *gin.Context) *APIError {
return addRankingQueueComment(c, db.RankingQueueActionComment, false)
}

// AddPrivateRankingQueueComment inserts an attributed comment only ranking supervisors can retrieve.
// Endpoint: POST /v2/ranking/queue/:id/private-comment
func AddPrivateRankingQueueComment(c *gin.Context) *APIError {
canUsePrivate := canUserAccessSupervisorRoute(c)
if !canUsePrivate {
return APIErrorForbidden("Only ranking supervisors can add a private ranking comment.")
}

return addRankingQueueComment(c, db.RankingQueueActionComment, true)
}

func addRankingQueueComment(c *gin.Context, action db.RankingQueueAction, isPrivate bool) *APIError {
id, err := strconv.Atoi(c.Param("id"))

if err != nil {
Expand Down Expand Up @@ -72,23 +123,33 @@ func AddRankingQueueComment(c *gin.Context) *APIError {
}

comment := &db.MapsetRankingQueueComment{
UserId: user.Id,
MapsetId: queueMapset.MapsetId,
Comment: body.Comment,
GameMode: &body.GameMode,
IsActive: true,
UserId: user.Id,
MapsetId: queueMapset.MapsetId,
ActionType: action,
Comment: body.Comment,
GameMode: &body.GameMode,
IsPrivate: isPrivate,
IsActive: true,
}

if err := comment.Insert(); err != nil {
return APIErrorServerError("Error inserting comment into DB", err)
}

if err := db.NewMapsetActionNotification(queueMapset.Mapset, comment).Insert(); err != nil {
return APIErrorServerError("Error inserting comment notification", err)
if action == db.RankingQueueActionComment {
if err := db.NewMapsetActionNotification(queueMapset.Mapset, comment).Insert(); err != nil {
return APIErrorServerError("Error inserting comment notification", err)
}

_ = webhooks.SendQueueWebhook(user, queueMapset.Mapset, db.RankingQueueActionComment)
}

message := "Your comment has been successfully added."
if isPrivate {
message = "Your private ranking comment has been successfully added."
}

_ = webhooks.SendQueueWebhook(user, queueMapset.Mapset, db.RankingQueueActionComment)
c.JSON(http.StatusOK, gin.H{"message": "Your comment has been successfully added."})
c.JSON(http.StatusOK, gin.H{"message": message})
return nil
}

Expand Down
Loading
Loading