dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

commit f31832af640dc0a28126a8e7b99c838eeeabb7c4
parent 7e53f66ebd895655618aa7c6e9fab8ee789978fc
Author: n0tr1v <n0tr1v@protonmail.com>
Date:   Fri, 29 Dec 2023 17:20:50 -0500

proof of concept to have private conversation within same page as other chat

Diffstat:
Mpkg/database/database.go | 2+-
Mpkg/database/tableChatMessages.go | 7++++++-
Mpkg/database/utils/utils.go | 9+++++++++
Mpkg/web/handlers/api/v1/chat.go | 23+++++++++++++++++++----
Mpkg/web/handlers/api/v1/handlers.go | 6++++--
Mpkg/web/handlers/api/v1/topBarHandler.go | 4++++
Mpkg/web/handlers/interceptors/command/command.go | 21+++++++++++----------
Mpkg/web/handlers/interceptors/msgInterceptor.go | 18++++++++++++++++++
Mpkg/web/handlers/interceptors/slashInterceptor.go | 8++++++++
9 files changed, 80 insertions(+), 18 deletions(-)

diff --git a/pkg/database/database.go b/pkg/database/database.go @@ -100,7 +100,7 @@ type IDkfDB interface { DoCreateSession(userID UserID, userAgent string, sessionDuration time.Duration) Session GetActiveUserSessions(userID UserID) (out []Session) GetCategories() (out []CategoriesResult, err error) - GetChatMessages(roomID RoomID, roomKey string, username Username, userID UserID, displayPms PmDisplayMode, mentionsOnly, displayHellbanned, displayIgnored, displayModerators, displayIgnoredMessages bool, msgsLimit, minID1 int64) (out ChatMessages, err error) + GetChatMessages(roomID RoomID, roomKey string, username Username, userID UserID, pmUserID *UserID, displayPms PmDisplayMode, mentionsOnly, displayHellbanned, displayIgnored, displayModerators, displayIgnoredMessages bool, msgsLimit, minID1 int64) (out ChatMessages, err error) GetChatRoomByID(roomID RoomID) (out ChatRoom, err error) GetChatRoomByName(roomName string) (out ChatRoom, err error) GetChessSubscribers() (out []User, err error) diff --git a/pkg/database/tableChatMessages.go b/pkg/database/tableChatMessages.go @@ -377,7 +377,7 @@ const ( ) func (d *DkfDB) GetChatMessages(roomID RoomID, roomKey string, username Username, userID UserID, - displayPms PmDisplayMode, mentionsOnly, displayHellbanned, displayIgnored, displayModerators, + pmUserID *UserID, displayPms PmDisplayMode, mentionsOnly, displayHellbanned, displayIgnored, displayModerators, displayIgnoredMessages bool, msgsLimit, minID1 int64) (out ChatMessages, err error) { cmp := func(t, t2 ChatMessage) bool { return t.ID > t2.ID } @@ -408,6 +408,9 @@ func (d *DkfDB) GetChatMessages(roomID RoomID, roomKey string, username Username if mentionsOnly { q = q.Where(`raw_message LIKE ?`, "%@"+username+"%") } + if pmUserID != nil { + q = q.Where(`(to_user_id = ? AND user_id = ?) OR (user_id = ? AND to_user_id = ?)`, userID, pmUserID, userID, pmUserID) + } switch displayPms { case PmNoFilter: // Display all messages q = q.Where(`to_user_id is null OR to_user_id = ? OR user_id = ?`, userID, userID) @@ -681,6 +684,7 @@ const ( ForceRefresh DeleteMsg Wizz + Redirect RefreshTopic string = "refresh" ) @@ -690,6 +694,7 @@ type ChatMessageType struct { Msg ChatMessage IsMod bool ToUserUsername *Username + NewURL string } var MsgPubSub = pubsub.NewPubSub[ChatMessageType]() diff --git a/pkg/database/utils/utils.go b/pkg/database/utils/utils.go @@ -67,6 +67,15 @@ func DoParseUsernamePtr(v string) *database.Username { return &username } +func GetUserIDFromUsername(db *database.DkfDB, u string) *database.UserID { + username := DoParseUsernamePtr(u) + if username == nil { + return nil + } + userID, _ := db.GetUserIDByUsername(*username) + return &userID +} + func DoParsePmDisplayMode(v string) database.PmDisplayMode { p, err := utils.ParseInt64(v) if err != nil { diff --git a/pkg/web/handlers/api/v1/chat.go b/pkg/web/handlers/api/v1/chat.go @@ -8,6 +8,7 @@ import ( "dkforest/pkg/managers" "dkforest/pkg/pubsub" "dkforest/pkg/utils" + "dkforest/pkg/web/handlers/interceptors/command" "dkforest/pkg/web/handlers/poker" "dkforest/pkg/web/handlers/streamModals" "dkforest/pkg/web/handlers/utils/stream" @@ -63,7 +64,15 @@ func manualPreload(db *database.DkfDB, msg *database.ChatMessage, room database. // Return true if the message passes all the user's filter. // false if the message does not and should be discarded. func applyUserFilters(db *database.DkfDB, authUser *database.User, msg *database.ChatMessage, - pmOnlyQuery database.PmDisplayMode, displayHellbanned, mentionsOnlyQuery bool) bool { + pmUserID *database.UserID, pmOnlyQuery database.PmDisplayMode, displayHellbanned, mentionsOnlyQuery bool) bool { + if pmUserID != nil { + if msg.ToUserID == nil { + return false + } + if *msg.ToUserID == *pmUserID || msg.UserID == *pmUserID { + return true + } + } if (pmOnlyQuery == database.PmOnly && msg.ToUser == nil) || (pmOnlyQuery == database.PmNone && msg.ToUser != nil) || !authUser.DisplayModerators && msg.Moderators || @@ -176,9 +185,10 @@ func ChatStreamMessagesHandler(c echo.Context) error { // Get initial messages for the user pmOnlyQuery := dutils.DoParsePmDisplayMode(c.QueryParam("pmonly")) mentionsOnlyQuery := utils.DoParseBool(c.QueryParam("mentionsOnly")) + pmUserID := dutils.GetUserIDFromUsername(db, c.QueryParam(command.RedirectPmUsernameQP)) displayHellbanned := authUser.DisplayHellbanned || authUser.IsHellbanned displayIgnoredMessages := false - msgs, err := db.GetChatMessages(room.ID, roomKey, authUser.Username, authUser.ID, pmOnlyQuery, mentionsOnlyQuery, + msgs, err := db.GetChatMessages(room.ID, roomKey, authUser.Username, authUser.ID, pmUserID, pmOnlyQuery, mentionsOnlyQuery, displayHellbanned, authUser.DisplayIgnored, authUser.DisplayModerators, displayIgnoredMessages, 500, 0) if err != nil { return c.Redirect(http.StatusFound, "/") @@ -305,6 +315,11 @@ Loop: return nil } + if topic == selfRefreshTopic && msgTyp.Typ == database.Redirect { + send(`<meta http-equiv="refresh" content="0; URL='` + msgTyp.NewURL + `'" />`) + return nil + } + if topic == selfRefreshTopic || msgTyp.Typ == database.ForceRefresh { send(metaRefresh) return nil @@ -336,7 +351,7 @@ Loop: if msgTyp.Typ == database.EditMsg { // Get all messages for the user that were created after the edited one (included) - msgs, err := db.GetChatMessages(room.ID, roomKey, authUser.Username, authUser.ID, pmOnlyQuery, + msgs, err := db.GetChatMessages(room.ID, roomKey, authUser.Username, authUser.ID, pmUserID, pmOnlyQuery, mentionsOnlyQuery, displayHellbanned, authUser.DisplayIgnored, authUser.DisplayModerators, displayIgnoredMessages, 150, msgTyp.Msg.ID) if err != nil { @@ -384,7 +399,7 @@ Loop: } if !VerifyMsgAuth(db, authUser, msg) || - !applyUserFilters(db, authUser, msg, pmOnlyQuery, displayHellbanned, mentionsOnlyQuery) { + !applyUserFilters(db, authUser, msg, pmUserID, pmOnlyQuery, displayHellbanned, mentionsOnlyQuery) { continue } diff --git a/pkg/web/handlers/api/v1/handlers.go b/pkg/web/handlers/api/v1/handlers.go @@ -66,6 +66,7 @@ func chatMessages(c echo.Context) (status int, data ChatMessagesData) { pmOnlyQuery := dutils.DoParsePmDisplayMode(c.QueryParam("pmonly")) mentionsOnlyQuery := utils.DoParseBool(c.QueryParam("mentionsOnly")) + pmUserID := dutils.GetUserIDFromUsername(db, c.QueryParam(command.RedirectPmUsernameQP)) room, roomKey, err := dutils.GetRoomAndKey(db, c, roomName) if err != nil { @@ -76,7 +77,7 @@ func chatMessages(c echo.Context) (status int, data ChatMessagesData) { displayHellbanned := authUser.DisplayHellbanned || authUser.IsHellbanned displayIgnoredMessages := false - msgs, err := db.GetChatMessages(room.ID, roomKey, authUser.Username, authUser.ID, pmOnlyQuery, mentionsOnlyQuery, + msgs, err := db.GetChatMessages(room.ID, roomKey, authUser.Username, authUser.ID, pmUserID, pmOnlyQuery, mentionsOnlyQuery, displayHellbanned, authUser.DisplayIgnored, authUser.DisplayModerators, displayIgnoredMessages, 150, 0) if err != nil { return http.StatusInternalServerError, data @@ -180,7 +181,8 @@ func RoomNotifierHandler(c echo.Context) error { displayHellbanned := authUser.DisplayHellbanned || authUser.IsHellbanned mentionsOnly := false displayIgnoredMessages := false - msgs, err := db.GetChatMessages(room.ID, roomKey, authUser.Username, authUser.ID, database.PmNoFilter, mentionsOnly, + var pmUserID *database.UserID + msgs, err := db.GetChatMessages(room.ID, roomKey, authUser.Username, authUser.ID, pmUserID, database.PmNoFilter, mentionsOnly, displayHellbanned, authUser.DisplayIgnored, authUser.DisplayModerators, displayIgnoredMessages, 150, 0) if err != nil { return c.NoContent(http.StatusInternalServerError) diff --git a/pkg/web/handlers/api/v1/topBarHandler.go b/pkg/web/handlers/api/v1/topBarHandler.go @@ -134,6 +134,7 @@ func ChatTopBarHandler(c echo.Context) error { data.RoomName = c.Param("roomName") queryParams := c.QueryParams() + pmUsername := c.QueryParam(command.RedirectPmUsernameQP) origMl := utils.DoParseBool(c.QueryParam(command.RedirectMultilineQP)) data.QueryParams = queryParams.Encode() queryParams.Set(command.RedirectMultilineQP, "1") @@ -148,6 +149,9 @@ func ChatTopBarHandler(c echo.Context) error { redirectQP.Set(command.RedirectMultilineQP, "1") } } + if pmUsername != "" { + redirectQP.Set(command.RedirectPmUsernameQP, pmUsername) + } room, roomKey, err := dutils.GetRoomAndKey(db, c, data.RoomName) if err != nil { diff --git a/pkg/web/handlers/interceptors/command/command.go b/pkg/web/handlers/interceptors/command/command.go @@ -25,16 +25,17 @@ func (e ErrSuccess) Error() string { } const ( - RedirectPmQP = "pm" - RedirectEditQP = "e" - RedirectGroupQP = "g" - RedirectModQP = "m" - RedirectHbmQP = "hbm" - RedirectTagQP = "tag" - RedirectHTagQP = "htag" - RedirectMTagQP = "mtag" - RedirectQuoteQP = "quote" - RedirectMultilineQP = "ml" + RedirectPmQP = "pm" + RedirectEditQP = "e" + RedirectGroupQP = "g" + RedirectModQP = "m" + RedirectHbmQP = "hbm" + RedirectTagQP = "tag" + RedirectHTagQP = "htag" + RedirectMTagQP = "mtag" + RedirectQuoteQP = "quote" + RedirectMultilineQP = "ml" + RedirectPmUsernameQP = "pmusername" ) type Command struct { diff --git a/pkg/web/handlers/interceptors/msgInterceptor.go b/pkg/web/handlers/interceptors/msgInterceptor.go @@ -116,6 +116,24 @@ func (i MsgInterceptor) InterceptMsg(cmd *command.Command) { return } + pmUsername := dutils.DoParseUsernamePtr(cmd.C.QueryParam(command.RedirectPmUsernameQP)) + if pmUsername != nil { + toUser, err := cmd.DB.GetUserByUsername(*pmUsername) + if err != nil { + cmd.Err = command.ErrRedirect + return + } + cmd.SkipInboxes, cmd.Err = dutils.CanUserPmOther(cmd.DB, *cmd.AuthUser, toUser, cmd.Room.IsOwned()) + if cmd.Err != nil { + return + } + cmd.ToUser = &toUser + cmd.HellbanMsg = false + cmd.ModMsg = false + cmd.SystemMsg = false + cmd.GroupID = nil + } + toUserID := database.UserPtrID(cmd.ToUser) msgID, _ := cmd.DB.CreateOrEditMessage(cmd.EditMsg, html, cmd.OrigMessage, cmd.RoomKey, cmd.Room.ID, cmd.AuthUser.ID, toUserID, cmd.Upload, cmd.GroupID, cmd.HellbanMsg, cmd.ModMsg, cmd.SystemMsg) diff --git a/pkg/web/handlers/interceptors/slashInterceptor.go b/pkg/web/handlers/interceptors/slashInterceptor.go @@ -977,6 +977,14 @@ func handlePMCmd(c *command.Command) (handled bool) { username := database.Username(m[1]) newMsg := m[2] + if strings.TrimSpace(newMsg) == "" { + newURL := fmt.Sprintf("/api/v1/chat/messages/%s/stream?pmusername=%s", c.Room.Name, username) + database.MsgPubSub.Pub("refresh_"+string(c.AuthUser.Username), database.ChatMessageType{Typ: database.Redirect, NewURL: newURL}) + c.RedirectQP.Set(command.RedirectPmUsernameQP, username.String()) + c.Err = command.ErrRedirect + return true + } + // Chat helpers if username == config.NullUsername { return handlePm0(c, newMsg)