Files
sm-bm-plugin/chat.go
Jakub Zych 64601f6817 feat: host the BM chat context
When golem15.chat is active, Boot registers the context alias bm (id 1)
on its registry. Membership is the user group jz.bm.chat.group (default
studenci); CanOverseeChats is always false, so nobody reads other
people's DMs or edits their messages; the broadcast channel is
chat:context:1, never empty.

GET /_bm/api/v1/chat answers the caller's membership behind jwt.auth and
the per-user bucket jz.bm.chat (240 a minute). The chat config block
(group, moderator_group, max_upload_bytes, max_embeds) is added. jz.bm
still Requires only golem15.user; all chat wiring is gated by HasPlugin.
2026-10-26 15:40:44 +01:00

91 lines
2.8 KiB
Go

package bm
import (
"context"
"errors"
chatcontracts "git.golem15.com/golem15/sm-chat-plugin/contracts"
"git.golem15.com/golem15/summercms/modules/backpack"
"git.golem15.com/jakub/sm-bm-plugin/classes"
"gorm.io/gorm"
)
// The chat config keys and their fallbacks (config/config.yaml).
const (
configChatGroup = "jz.bm.chat.group"
configChatModeratorGroup = "jz.bm.chat.moderator_group"
configChatMaxUpload = "jz.bm.chat.max_upload_bytes"
configChatMaxEmbeds = "jz.bm.chat.max_embeds"
defaultChatGroup = "studenci"
defaultChatModeratorGroup = "moderatorzy"
defaultChatMaxUpload = 62914560
defaultChatMaxEmbeds = 3
)
// registerChat makes jz.bm the host of the BM chat: it registers the bm
// context alias on golem15.chat's registry. Without golem15.chat it does
// nothing, so jz.bm keeps working (and its tests keep activating) without
// the chat plugins; jz.bm does not Require them.
func (p *Plugin) registerChat(app *backpack.App) error {
if app == nil || !app.HasPlugin("golem15.chat") {
return nil
}
reg, ok := app.Lookup[*chatcontracts.Registry]()
if !ok || reg == nil {
return errors.New("jz.bm: golem15.chat must be listed before jz.bm in summer.yaml")
}
return reg.Add(p.ID(), chatcontracts.Registration{Alias: classes.ChatContextType, Loader: p.loadChatContext})
}
// loadChatContext is the loader of the bm alias. The only context is id 1;
// any other id is unknown, which golem15.chat answers with its not-a-member
// error.
func (p *Plugin) loadChatContext(_ context.Context, db *gorm.DB, id uint) (chatcontracts.Context, error) {
if id != classes.ChatContextID {
return nil, nil
}
return classes.NewChatContext(db, p.chatGroup(), p.chatModeratorGroup()), nil
}
// chatGroup is the code of the user group whose members are in the chat.
func (p *Plugin) chatGroup() string {
return p.configString(configChatGroup, defaultChatGroup)
}
// chatModeratorGroup is the code of the user group whose members moderate
// the chat's channels.
func (p *Plugin) chatModeratorGroup() string {
return p.configString(configChatModeratorGroup, defaultChatModeratorGroup)
}
// chatMaxUploadBytes is the total attachment size of one message.
func (p *Plugin) chatMaxUploadBytes() int {
return p.configInt(configChatMaxUpload, defaultChatMaxUpload)
}
// chatMaxEmbeds is the number of entity embeds one message may carry.
func (p *Plugin) chatMaxEmbeds() int {
return p.configInt(configChatMaxEmbeds, defaultChatMaxEmbeds)
}
func (p *Plugin) configString(key, fallback string) string {
if p.app == nil || p.app.Config == nil {
return fallback
}
if v := p.app.Config.String(key); v != "" {
return v
}
return fallback
}
func (p *Plugin) configInt(key string, fallback int) int {
if p.app == nil || p.app.Config == nil {
return fallback
}
if v := p.app.Config.Int(key); v > 0 {
return v
}
return fallback
}