package utils
import (
"dkforest/pkg/config"
"dkforest/pkg/database"
"dkforest/pkg/managers"
"dkforest/pkg/utils"
"errors"
"fmt"
"github.com/labstack/echo"
"github.com/sirupsen/logrus"
)
func GetZeroUser(db *database.DkfDB) database.User {
zeroUser, err := db.GetUserByUsername(config.NullUsername)
if err != nil {
logrus.Fatal(err)
}
return zeroUser
}
func ZeroSendMsg(db *database.DkfDB, recipientID database.UserID, msg string) {
zeroUser := GetZeroUser(db)
_, _ = db.CreateMsg(msg, msg, "", config.GeneralRoomID, zeroUser.ID, &recipientID)
}
func RootAdminNotify(db *database.DkfDB, msg string) {
rootAdminID := database.UserID(config.RootAdminID)
ZeroSendMsg(db, rootAdminID, msg)
}
func SendNewChessGameMessages(db *database.DkfDB, key, roomKey string, roomID database.RoomID, zeroUser, player1, player2 database.User) {
// Send game link to players
getPlayerMsg := func(opponent database.User) (raw string, msg string) {
raw = `Chess game against ` + string(opponent.Username)
msg = `Chess game against ` + string(opponent.Username) + ``
return
}
raw, msg := getPlayerMsg(player2)
_, _ = db.CreateMsg(raw, msg, roomKey, roomID, zeroUser.ID, &player1.ID)
raw, msg = getPlayerMsg(player1)
_, _ = db.CreateMsg(raw, msg, roomKey, roomID, zeroUser.ID, &player2.ID)
// Send notifications to chess games subscribers
raw = `Chess game: ` + string(player1.Username) + ` VS ` + string(player2.Username)
msg = `Chess game: ` + string(player1.Username) + ` VS ` + string(player2.Username) + ``
activeUsers := managers.ActiveUsers.GetActiveUsers()
activeUsersIDs := make([]database.UserID, len(activeUsers))
for idx, activeUser := range activeUsers {
activeUsersIDs[idx] = activeUser.UserID
}
users, _ := db.GetOnlineChessSubscribers(activeUsersIDs)
for _, user := range users {
if user.ID == player1.ID || user.ID == player2.ID {
continue
}
// Make a copy of user ID, otherwise next iteration will overwrite the pointer
// and change data that was sent previously in the pubsub later on
userID := user.ID
_, _ = db.CreateMsg(raw, msg, roomKey, roomID, zeroUser.ID, &userID)
}
}
func DoParseUsernamePtr(v string) *database.Username {
if v == "" {
return nil
}
username := database.Username(v)
return &username
}
func GetUserIDFromUsername(db *database.DkfDB, u string) *database.UserID {
username := DoParseUsernamePtr(u)
if username == nil {
return nil
}
userID, err := db.GetUserIDByUsername(*username)
if err != nil {
return nil
}
return &userID
}
func DoParsePmDisplayMode(v string) database.PmDisplayMode {
p, err := utils.ParseInt64(v)
if err != nil {
return database.PmNoFilter
}
switch p {
case 1:
return database.PmOnly
case 2:
return database.PmNone
default:
return database.PmNoFilter
}
}
func Parse[T ~int64](v string) (out T, err error) {
p, err := utils.ParseInt64(v)
if err != nil {
return out, err
}
return T(p), nil
}
func DoParse[T ~int64](v string) (out T) {
out, _ = Parse[T](v)
return
}
func ParseUserID(v string) (database.UserID, error) {
return Parse[database.UserID](v)
}
func DoParseUserID(v string) (out database.UserID) {
return DoParse[database.UserID](v)
}
func ParseRoomID(v string) (database.RoomID, error) {
return Parse[database.RoomID](v)
}
func DoParseRoomID(v string) (out database.RoomID) {
return DoParse[database.RoomID](v)
}
func SelfHellBan(db *database.DkfDB, user *database.User) {
db.NewAudit(*user, fmt.Sprintf("hellban %s #%d", user.Username, user.ID))
user.HellBan(db)
managers.ActiveUsers.UpdateUserHBInRooms(managers.NewUserInfo(user))
}
func Kick(db *database.DkfDB, kicked, kickedBy database.User, purge, silent bool) error {
if kicked.IsHellbanned {
silent = true
}
return kick(db, kicked, kickedBy, silent, purge)
}
func SilentKick(db *database.DkfDB, kicked, kickedBy database.User) error {
return kick(db, kicked, kickedBy, true, true)
}
func SelfKick(db *database.DkfDB, kicked database.User, silent bool) error {
return kick(db, kicked, kicked, silent, true)
}
func kick(db *database.DkfDB, kicked, kickedBy database.User, silent, purge bool) error {
if !kicked.Verified {
return errors.New("user already kicked")
}
// Can't kick a vetted user (unless admin)
if !kickedBy.IsAdmin && kicked.Vetted {
return errors.New("cannot kick a vetted user")
}
// Can't kick another moderator (unless admin)
if !kickedBy.IsAdmin && kicked.IsModerator() {
return errors.New("cannot kick another moderator")
}
// Can't kick yourself as a moderator/admin
if (kicked.IsAdmin || kicked.IsModerator()) && kickedBy.ID == kicked.ID {
return errors.New("cannot kick yourself")
}
db.NewAudit(kickedBy, fmt.Sprintf("kick %s #%d", kicked.Username, kicked.ID))
kicked.SetVerified(db, false)
// Remove user from the user cache
managers.ActiveUsers.RemoveUser(kicked.ID)
if purge {
// Purge user messages
if err := db.DeleteUserChatMessages(kicked.ID); err != nil {
logrus.Error(err)
}
database.MsgPubSub.Pub(database.RefreshTopic, database.ChatMessageType{Typ: database.ForceRefresh})
} else {
database.MsgPubSub.Pub("refresh_"+string(kicked.Username), database.ChatMessageType{Typ: database.ForceRefresh})
}
// If user is HB, do not display system message
if !silent {
// Display kick message
db.CreateKickMsg(kicked, kickedBy)
}
return nil
}
func GetRoomAndKey(db *database.DkfDB, c echo.Context, roomName string) (database.ChatRoom, string, error) {
roomKey := ""
room, err := db.GetChatRoomByName(roomName)
if err != nil {
return room, roomKey, errors.New("room not found")
}
hasAccess, roomKey := room.HasAccess(c)
if !hasAccess {
return room, roomKey, errors.New("forbidden")
}
return room, roomKey, nil
}
var ErrPMDenied = errors.New("you cannot pm/inbox this user")
var Err20Msgs = errors.New("you need 20 public messages to unlock PMs/Inbox; or be whitelisted")
var ErrOther20Msgs = errors.New("dest user must be whitelisted or have 20 public messages")
func CanUserPmOther(db *database.DkfDB, user, other database.User, roomIsPrivate bool) (skipInbox bool, err error) {
errPMDenied := ErrPMDenied
if user.ID == other.ID {
return false, errors.New("cannot /pm yourself")
}
if db.IsUserPmWhitelisted(user.ID, other.ID) {
return false, nil
}
switch other.PmMode {
case database.PmModeWhitelist:
// We are in whitelist mode, and user is not whitelisted
return false, errPMDenied
case database.PmModeStandard:
if !user.CanSendPM() {
// In private rooms, can send PM but inboxes will be skipped if not enough public messages
if roomIsPrivate {
return true, nil
}
// Need at least 20 public messages to send PM in a public room
return false, Err20Msgs
}
// User on blacklist cannot PM/Inbox
if db.IsUserPmBlacklisted(user.ID, other.ID) {
return false, errPMDenied
}
// Other doesn't want PM from new users
if !user.AccountOldEnough() && other.BlockNewUsersPm {
return false, errPMDenied
}
if !other.CanSendPM() {
if db.IsUserPmWhitelisted(other.ID, user.ID) {
return true, nil
}
// In private rooms, can send PM but inboxes will be skipped if not enough public messages
if roomIsPrivate {
return true, nil
}
return false, ErrOther20Msgs
}
return false, nil
}
// Should never go here
return false, nil
}
// VerifyMsgAuth returns either or not authUser is allowed to see msg
func VerifyMsgAuth(db *database.DkfDB, msg *database.ChatMessage, authUserID database.UserID, isModerator bool) bool {
// Verify moderators channel authorization
if msg.Moderators && !isModerator {
return false
}
// Verify group authorization
if msg.GroupID != nil {
userGroupsIDs, _ := db.GetUserRoomGroupsIDs(authUserID, msg.RoomID)
if !utils.InArr(*msg.GroupID, userGroupsIDs) {
return false
}
}
// verify PM authorization
if msg.IsPm() {
if msg.UserID != authUserID && *msg.ToUserID != authUserID {
return false
}
}
return true
}