topBarHandler.go (9784B)
1 package v1 2 3 import ( 4 "dkforest/pkg/clockwork" 5 "dkforest/pkg/config" 6 "dkforest/pkg/database" 7 dutils "dkforest/pkg/database/utils" 8 "dkforest/pkg/utils" 9 "dkforest/pkg/web/handlers/interceptors" 10 "dkforest/pkg/web/handlers/interceptors/command" 11 "dkforest/pkg/web/handlers/streamModals" 12 "dkforest/pkg/web/handlers/utils/stream" 13 "errors" 14 "fmt" 15 "github.com/dustin/go-humanize" 16 "github.com/labstack/echo" 17 "net/http" 18 "net/url" 19 "strings" 20 "time" 21 ) 22 23 func getDataMessagePrefix(db *database.DkfDB, c echo.Context, roomKey string, room database.ChatRoom, authUser *database.User) (out string, err error) { 24 pm := c.QueryParam(command.RedirectPmQP) 25 edit := c.QueryParam(command.RedirectEditQP) 26 group := c.QueryParam(command.RedirectGroupQP) 27 mod := c.QueryParam(command.RedirectModQP) 28 hbm := c.QueryParam(command.RedirectHbmQP) 29 tag := c.QueryParam(command.RedirectTagQP) 30 htag := c.QueryParam(command.RedirectHTagQP) 31 mtag := c.QueryParam(command.RedirectMTagQP) 32 quote := c.QueryParam(command.RedirectQuoteQP) 33 34 if pm != "" { 35 out = "/pm " + pm + " " 36 } else if hbm != "" { 37 out = "/hbm " 38 } else if mod != "" { 39 out = "/m " 40 } else if group != "" { 41 out = "/g " + group + " " 42 } else if tag != "" { 43 out = "@" + tag + " " 44 } else if htag != "" { 45 out = "/hbm @" + htag + " " 46 } else if mtag != "" { 47 out = "/m @" + mtag + " " 48 } else if edit != "" { 49 out, err = handleGetEdit(db, edit, roomKey, room, authUser) 50 if err != nil { 51 return 52 } 53 } else if quote != "" { 54 out, err = handleGetQuote(db, quote, roomKey, room, authUser) 55 if err != nil { 56 return 57 } 58 } 59 return 60 } 61 62 func buildCommandsList(authUser *database.User, room database.ChatRoom) (commandsList []string) { 63 if !authUser.AutocompleteCommandsEnabled { 64 return 65 } 66 commandsList = append(commandsList, "/pm ") 67 commandsList = append(commandsList, "/pmw ") 68 commandsList = append(commandsList, "/pmb ") 69 if authUser.IsModerator() { 70 commandsList = append(commandsList, "/m ") 71 } 72 commandsList = append(commandsList, "/me ") 73 commandsList = append(commandsList, "/e ") 74 commandsList = append(commandsList, "/chess ") 75 commandsList = append(commandsList, "/ignore ") 76 commandsList = append(commandsList, "/unignore ") 77 commandsList = append(commandsList, "/inbox ") 78 commandsList = append(commandsList, "/toggle-autocomplete") 79 commandsList = append(commandsList, "/d") 80 commandsList = append(commandsList, "/hide") 81 commandsList = append(commandsList, "/unhide") 82 commandsList = append(commandsList, "/pmwhitelist") 83 commandsList = append(commandsList, "/setpmmode whitelist") 84 commandsList = append(commandsList, "/setpmmode standard") 85 commandsList = append(commandsList, "/g ") 86 commandsList = append(commandsList, "/subscribe") 87 commandsList = append(commandsList, "/unsubscribe") 88 commandsList = append(commandsList, "/p ") 89 commandsList = append(commandsList, "/token") 90 commandsList = append(commandsList, "/md5 ") 91 commandsList = append(commandsList, "/sha1 ") 92 commandsList = append(commandsList, "/sha256 ") 93 commandsList = append(commandsList, "/sha512 ") 94 commandsList = append(commandsList, "/dice") 95 commandsList = append(commandsList, "/choice ") 96 if authUser.CanSeeHB() { 97 commandsList = append(commandsList, "/hbm") // CanSeeHB 98 } 99 // Private room 100 if room.IsOwned() { 101 commandsList = append(commandsList, "/mode") 102 commandsList = append(commandsList, "/wl") 103 } 104 // Private room owner 105 if room.IsRoomOwner(authUser.ID) { 106 commandsList = append(commandsList, "/addgroup") 107 commandsList = append(commandsList, "/rmgroup") 108 commandsList = append(commandsList, "/glock") 109 commandsList = append(commandsList, "/gunlock") 110 commandsList = append(commandsList, "/gusers") 111 commandsList = append(commandsList, "/groups") 112 commandsList = append(commandsList, "/gadduser") 113 commandsList = append(commandsList, "/grmuser") 114 commandsList = append(commandsList, "/mode user-whitelist") 115 commandsList = append(commandsList, "/mode standard") 116 commandsList = append(commandsList, "/wl groupName") 117 } 118 // Moderators 119 if authUser.IsModerator() { 120 commandsList = append(commandsList, "/moderators") 121 commandsList = append(commandsList, "/kick ") 122 commandsList = append(commandsList, "/unkick ") 123 commandsList = append(commandsList, "/logout ") 124 commandsList = append(commandsList, "/captcha ") 125 commandsList = append(commandsList, "/rtuto ") 126 commandsList = append(commandsList, "/hellban ") 127 commandsList = append(commandsList, "/unhellban ") 128 } 129 return commandsList 130 } 131 132 func ChatTopBarHandler(c echo.Context) error { 133 authUser := c.Get("authUser").(*database.User) 134 db := c.Get("database").(*database.DkfDB) 135 csrf, _ := c.Get("csrf").(string) 136 var data chatTopBarData 137 data.RoomName = c.Param("roomName") 138 139 redirectPmUsernameQP := command.RedirectPmUsernameQP 140 redirectMultilineQP := command.RedirectMultilineQP 141 queryParams := c.QueryParams() 142 pmUsername := c.QueryParam(redirectPmUsernameQP) 143 origMl := utils.DoParseBool(c.QueryParam(redirectMultilineQP)) 144 data.QueryParams = queryParams.Encode() 145 queryParams.Set(redirectMultilineQP, "1") 146 data.QueryParamsMl = queryParams.Encode() 147 queryParams.Del(redirectMultilineQP) 148 data.QueryParamsNml = queryParams.Encode() 149 150 redirectQP := url.Values{} 151 if authUser.CanUseMultiline { 152 data.Multiline = origMl 153 if data.Multiline { 154 redirectQP.Set(redirectMultilineQP, "1") 155 } 156 } 157 if pmUsername != "" { 158 redirectQP.Set(redirectPmUsernameQP, pmUsername) 159 } 160 161 room, roomKey, err := dutils.GetRoomAndKey(db, c, data.RoomName) 162 if err != nil { 163 return c.NoContent(http.StatusForbidden) 164 } 165 166 // If the tutorial is not completed, just render the chat top-bar, no matter what. 167 if (room.IsOfficialRoom() || (room.IsListed && !room.IsProtected())) && !authUser.TutorialCompleted() { 168 return c.Render(http.StatusOK, "chat-top-bar", data) 169 } 170 171 data.Message, err = getDataMessagePrefix(db, c, roomKey, room, authUser) 172 if err != nil { 173 return c.Redirect(http.StatusFound, "/api/v1/chat/top-bar/"+room.Name) 174 } 175 176 data.CommandsList = buildCommandsList(authUser, room) 177 178 // GET requests stops here 179 if c.Request().Method != http.MethodPost { 180 if authUser.UseStreamTopBar { 181 if streamItem, err := stream.SetStreaming(c, authUser.ID, ""); err == nil { 182 defer streamItem.Cleanup() 183 _, _ = c.Response().Write([]byte(RenderTopBar(csrf, data, authUser))) 184 c.Response().Flush() 185 <-streamItem.Quit 186 } 187 return c.NoContent(http.StatusOK) 188 } 189 return c.Render(http.StatusOK, "chat-top-bar", data) 190 } 191 192 // ------------------------------------------------------------------------ 193 194 if room.Name == config.AnnouncementsRoomName && authUser.ID != config.RootAdminID { 195 data.Error = "read only room" 196 return c.Render(http.StatusOK, "chat-top-bar", data) 197 } 198 199 if c.Request().ContentLength > config.MaxUserFileUploadSize { 200 data.Error = fmt.Sprintf("The maximum file size is %s", humanize.Bytes(config.MaxUserFileUploadSize)) 201 return c.Render(http.StatusOK, "chat-top-bar", data) 202 } 203 204 origMessage := strings.TrimSpace(c.Request().PostFormValue("message")) 205 206 cmd := command.NewCommand(c, origMessage, room, roomKey) 207 cmd.RedirectQP = redirectQP 208 209 interceptorsArr := []interceptors.Interceptor{ 210 interceptors.SnippetInterceptor{}, 211 interceptors.SpamInterceptor{}, 212 interceptors.BattleshipInstance, 213 interceptors.WWInstance, 214 interceptors.BangInterceptor{}, 215 interceptors.UploadInterceptor{}, 216 interceptors.SlashInterceptor{}, 217 streamModals.CodeModal{}, 218 streamModals.PurgeModal{}, 219 interceptors.MsgInterceptor{}, 220 } 221 for _, interceptor := range interceptorsArr { 222 interceptor.InterceptMsg(cmd) 223 data.Message = cmd.DataMessage 224 if cmd.Err != nil { 225 break 226 } 227 } 228 return redirectCmd(c, cmd, data) 229 } 230 231 func redirectCmd(c echo.Context, cmd *command.Command, data chatTopBarData) error { 232 err := cmd.Err 233 redirectURL := cmd.RedirectURL() 234 if err != nil { 235 var successErr *command.ErrSuccess 236 if errors.Is(err, command.ErrRedirect) { 237 return c.Redirect(http.StatusFound, redirectURL) 238 } else if errors.Is(err, command.ErrStop) { 239 return c.Render(http.StatusOK, "chat-top-bar", data) 240 } else if errors.As(err, &successErr) { 241 data.Success = successErr.Error() 242 return c.Render(http.StatusOK, "chat-top-bar", data) 243 } 244 data.Message = cmd.OrigMessage 245 data.Error = err.Error() 246 return c.Render(http.StatusOK, "chat-top-bar", data) 247 } 248 return c.Redirect(http.StatusFound, redirectURL) 249 } 250 251 func handleGetQuote(db *database.DkfDB, msgUUID, roomKey string, room database.ChatRoom, authUser *database.User) (dataMessage string, err error) { 252 quoted, err := db.GetRoomChatMessageByUUID(room.ID, msgUUID) 253 if err != nil { 254 return 255 } 256 257 // Build prefix for /m | /pm | /g | /hbm 258 prefix := "" 259 if quoted.IsPm() { 260 toUsername := utils.Ternary(quoted.OwnMessage(authUser.ID), quoted.ToUser.Username, quoted.User.Username) 261 prefix = fmt.Sprintf(`/pm %s `, toUsername) 262 } else if quoted.GroupID != nil { 263 prefix = fmt.Sprintf(`/g %s `, quoted.Group.Name) 264 } else if quoted.Moderators { 265 prefix = fmt.Sprintf(`/m `) 266 } else if (quoted.IsHellbanned || quoted.User.IsHellbanned) && authUser.IsModerator() { 267 prefix = fmt.Sprintf(`/hbm `) 268 } 269 270 // Append the actual quoted text 271 dataMessage = prefix + dutils.GetQuoteTxt(db, roomKey, quoted) + " " 272 return 273 } 274 275 func handleGetEdit(db *database.DkfDB, hourMinSec, roomKey string, room database.ChatRoom, authUser *database.User) (dataMessage string, err error) { 276 if dt, err := utils.ParsePrevDatetimeAt(hourMinSec, clockwork.NewRealClock()); err == nil { 277 if time.Since(dt) <= config.EditMessageTimeLimit { 278 if msg, err := db.GetRoomChatMessageByDate(room.ID, authUser.ID, dt.UTC()); err == nil { 279 decrypted, err := msg.GetRawMessage(roomKey) 280 if err != nil { 281 return "", err 282 } 283 dataMessage = "/e " + hourMinSec + " " + decrypted 284 } 285 } 286 } 287 return dataMessage, nil 288 }