package classes import ( "context" chatcontracts "git.golem15.com/golem15/sm-chat-plugin/contracts" userclasses "git.golem15.com/golem15/sm-user-plugin/classes" "gorm.io/gorm" ) var _ chatcontracts.Context = (*ChatContext)(nil) // The BM chat context: one context of type bm with id 1, whose members are // the members of one user group. const ( // ChatContextType is the context alias (context_type) jz.bm registers on // golem15.chat. ChatContextType = "bm" // ChatContextID is the id of the one BM chat context. ChatContextID uint = 1 // ChatGeneralName is the name of the group conversation and its channel. ChatGeneralName = "ogólny" // ChatGeneralSlug is the slug of the adopted channel. ChatGeneralSlug = "ogolny" // ChatBroadcastChannel is the host channel golem15.chat publishes created // events to. It must never be empty: an empty host channel silences the // conversation channels too. No client can subscribe to it, because the // chat authorizer only admits chat:conversation:{id}. ChatBroadcastChannel = "chat:context:1" ) // ChatContext is the golem15.chat context of the BM chat. Membership is the // user group named by group; nobody oversees other people's chats, so a // moderator reads no DM and edits no foreign message. type ChatContext struct { db *gorm.DB group string moderatorGroup string } // NewChatContext returns the BM chat context over db for the chat group // code group and the moderator group code moderatorGroup. func NewChatContext(db *gorm.DB, group, moderatorGroup string) *ChatContext { return &ChatContext{db: db, group: group, moderatorGroup: moderatorGroup} } func (c *ChatContext) ID() uint { return ChatContextID } func (c *ChatContext) Type() string { return ChatContextType } func (c *ChatContext) ChatEnabled() bool { return true } func (c *ChatContext) GroupName() string { return ChatGeneralName } func (c *ChatContext) BroadcastChannel() string { return ChatBroadcastChannel } // CanActFor is the default: a user acts only for themself. func (c *ChatContext) CanActFor(ctx context.Context, actorID, memberID uint) bool { return chatcontracts.DefaultCanActFor(ctx, actorID, memberID) } // CanOverseeChats is always false. golem15.chat grants an overseer read // access to every conversation and edits of other people's messages, which // no BM user gets; moderators delete through a narrower hook. func (c *ChatContext) CanOverseeChats(context.Context, uint) (bool, error) { return false, nil } // HasMember reports whether userID is in the chat group. func (c *ChatContext) HasMember(ctx context.Context, userID uint) (bool, error) { if userID == 0 || c.group == "" { return false, nil } return userclasses.HasGroupCode(ctx, c.db, userID, c.group) } // Members lists the ids of the chat group's users that are not deleted. func (c *ChatContext) Members(ctx context.Context) ([]uint, error) { ids := []uint{} if c.group == "" { return ids, nil } err := c.db.WithContext(ctx). Table("users_groups"). Joins("JOIN user_groups ON user_groups.id = users_groups.user_group_id"). Joins("JOIN users ON users.id = users_groups.user_id"). Where("user_groups.code = ? AND users.deleted_at IS NULL", c.group). Distinct(). Order("users_groups.user_id"). Pluck("users_groups.user_id", &ids).Error if err != nil { return nil, err } return ids, nil }