mattermost-community-enterp.../public/pluginapi/post.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

338 lines
10 KiB
Go

package pluginapi
import (
"slices"
"github.com/pkg/errors"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/plugin"
)
// PostService exposes methods to manipulate posts.
type PostService struct {
api plugin.API
}
// CreatePost creates a post.
//
// Minimum server version: 5.2
func (p *PostService) CreatePost(post *model.Post) error {
createdPost, appErr := p.api.CreatePost(post)
if appErr != nil {
return normalizeAppErr(appErr)
}
err := createdPost.ShallowCopy(post)
if err != nil {
return err
}
return nil
}
// DM sends a post as a direct message
//
// Minimum server version: 5.2
func (p *PostService) DM(senderUserID, receiverUserID string, post *model.Post) error {
channel, appErr := p.api.GetDirectChannel(senderUserID, receiverUserID)
if appErr != nil {
return normalizeAppErr(appErr)
}
post.ChannelId = channel.Id
post.UserId = senderUserID
return p.CreatePost(post)
}
// GetPost gets a post.
//
// Minimum server version: 5.2
func (p *PostService) GetPost(postID string) (*model.Post, error) {
post, appErr := p.api.GetPost(postID)
return post, normalizeAppErr(appErr)
}
// UpdatePost updates a post.
//
// Minimum server version: 5.2
func (p *PostService) UpdatePost(post *model.Post) error {
updatedPost, appErr := p.api.UpdatePost(post)
if appErr != nil {
return normalizeAppErr(appErr)
}
err := updatedPost.ShallowCopy(post)
if err != nil {
return err
}
return nil
}
// DeletePost deletes a post.
//
// Minimum server version: 5.2
func (p *PostService) DeletePost(postID string) error {
return normalizeAppErr(p.api.DeletePost(postID))
}
// SendEphemeralPost creates an ephemeral post.
//
// Minimum server version: 5.2
func (p *PostService) SendEphemeralPost(userID string, post *model.Post) {
*post = *p.api.SendEphemeralPost(userID, post)
}
// UpdateEphemeralPost updates an ephemeral message previously sent to the user.
// EXPERIMENTAL: This API is experimental and can be changed without advance notice.
//
// Minimum server version: 5.2
func (p *PostService) UpdateEphemeralPost(userID string, post *model.Post) {
*post = *p.api.UpdateEphemeralPost(userID, post)
}
// DeleteEphemeralPost deletes an ephemeral message previously sent to the user.
// EXPERIMENTAL: This API is experimental and can be changed without advance notice.
//
// Minimum server version: 5.2
func (p *PostService) DeleteEphemeralPost(userID, postID string) {
p.api.DeleteEphemeralPost(userID, postID)
}
// GetPostThread gets a post with all the other posts in the same thread.
//
// Minimum server version: 5.6
func (p *PostService) GetPostThread(postID string) (*model.PostList, error) {
postList, appErr := p.api.GetPostThread(postID)
return postList, normalizeAppErr(appErr)
}
// GetPostsSince gets posts created after a specified time as Unix time in milliseconds.
//
// Minimum server version: 5.6
func (p *PostService) GetPostsSince(channelID string, time int64) (*model.PostList, error) {
postList, appErr := p.api.GetPostsSince(channelID, time)
return postList, normalizeAppErr(appErr)
}
// GetPostsAfter gets a page of posts that were posted after the post provided.
//
// Minimum server version: 5.6
func (p *PostService) GetPostsAfter(channelID, postID string, page, perPage int) (*model.PostList, error) {
postList, appErr := p.api.GetPostsAfter(channelID, postID, page, perPage)
return postList, normalizeAppErr(appErr)
}
// GetPostsBefore gets a page of posts that were posted before the post provided.
//
// Minimum server version: 5.6
func (p *PostService) GetPostsBefore(channelID, postID string, page, perPage int) (*model.PostList, error) {
postList, appErr := p.api.GetPostsBefore(channelID, postID, page, perPage)
return postList, normalizeAppErr(appErr)
}
// GetPostsForChannel gets a list of posts for a channel.
//
// Minimum server version: 5.6
func (p *PostService) GetPostsForChannel(channelID string, page, perPage int) (*model.PostList, error) {
postList, appErr := p.api.GetPostsForChannel(channelID, page, perPage)
return postList, normalizeAppErr(appErr)
}
// SearchPostsInTeam returns a list of posts in a specific team that match the given params.
//
// Minimum server version: 5.10
func (p *PostService) SearchPostsInTeam(teamID string, paramsList []*model.SearchParams) ([]*model.Post, error) {
postList, appErr := p.api.SearchPostsInTeam(teamID, paramsList)
return postList, normalizeAppErr(appErr)
}
// AddReaction add a reaction to a post.
//
// Minimum server version: 5.3
func (p *PostService) AddReaction(reaction *model.Reaction) error {
addedReaction, appErr := p.api.AddReaction(reaction)
if appErr != nil {
return normalizeAppErr(appErr)
}
*reaction = *addedReaction
return nil
}
// GetReactions get the reactions of a post.
//
// Minimum server version: 5.3
func (p *PostService) GetReactions(postID string) ([]*model.Reaction, error) {
reactions, appErr := p.api.GetReactions(postID)
return reactions, normalizeAppErr(appErr)
}
// RemoveReaction remove a reaction from a post.
//
// Minimum server version: 5.3
func (p *PostService) RemoveReaction(reaction *model.Reaction) error {
return normalizeAppErr(p.api.RemoveReaction(reaction))
}
type ShouldProcessMessageOption func(*shouldProcessMessageOptions)
type shouldProcessMessageOptions struct {
AllowSystemMessages bool
AllowBots bool
AllowWebhook bool
FilterChannelIDs []string
FilterUserIDs []string
OnlyBotDMs bool
BotID string
}
// AllowSystemMessages configures a call to ShouldProcessMessage to return true for system messages.
//
// As it is typically desirable only to consume messages from users of the system, ShouldProcessMessage ignores system messages by default.
func AllowSystemMessages() ShouldProcessMessageOption {
return func(options *shouldProcessMessageOptions) {
options.AllowSystemMessages = true
}
}
// AllowBots configures a call to ShouldProcessMessage to return true for bot posts.
//
// As it is typically desirable only to consume messages from human users of the system, ShouldProcessMessage ignores bot messages by default.
// When allowing bots, take care to avoid a loop where two plugins respond to each others posts repeatedly.
func AllowBots() ShouldProcessMessageOption {
return func(options *shouldProcessMessageOptions) {
options.AllowBots = true
}
}
// AllowWebhook configures a call to ShouldProcessMessage to return true for posts from webhook.
//
// As it is typically desirable only to consume messages from human users of the system, ShouldProcessMessage ignores webhook messages by default.
func AllowWebhook() ShouldProcessMessageOption {
return func(options *shouldProcessMessageOptions) {
options.AllowWebhook = true
}
}
// FilterChannelIDs configures a call to ShouldProcessMessage to return true only for the given channels.
//
// By default, posts from all channels are allowed to be processed.
func FilterChannelIDs(filterChannelIDs []string) ShouldProcessMessageOption {
return func(options *shouldProcessMessageOptions) {
options.FilterChannelIDs = filterChannelIDs
}
}
// FilterUserIDs configures a call to ShouldProcessMessage to return true only for the given users.
//
// By default, posts from all non-bot users are allowed.
func FilterUserIDs(filterUserIDs []string) ShouldProcessMessageOption {
return func(options *shouldProcessMessageOptions) {
options.FilterUserIDs = filterUserIDs
}
}
// OnlyBotDMs configures a call to ShouldProcessMessage to return true only for direct messages sent to the bot created by EnsureBot.
//
// By default, posts from all channels are allowed.
func OnlyBotDMs() ShouldProcessMessageOption {
return func(options *shouldProcessMessageOptions) {
options.OnlyBotDMs = true
}
}
// If provided, BotID configures ShouldProcessMessage to skip its retrieval from the store.
//
// By default, posts from all non-bot users are allowed.
func BotID(botID string) ShouldProcessMessageOption {
return func(options *shouldProcessMessageOptions) {
options.BotID = botID
}
}
// ShouldProcessMessage returns if the message should be processed by a message hook.
//
// Use this method to avoid processing unnecessary messages in a MessageHasBeenPosted
// or MessageWillBePosted hook, and indeed in some cases avoid an infinite loop between
// two automated bots or plugins.
//
// The behavior is customizable using the given options, since plugin needs may vary.
// By default, system messages and messages from bots will be skipped.
//
// Minimum server version: 5.2
func (p *PostService) ShouldProcessMessage(post *model.Post, options ...ShouldProcessMessageOption) (bool, error) {
messageProcessOptions := &shouldProcessMessageOptions{}
for _, option := range options {
option(messageProcessOptions)
}
var botIDBytes []byte
var kvGetErr *model.AppError
if messageProcessOptions.BotID != "" {
botIDBytes = []byte(messageProcessOptions.BotID)
} else {
botIDBytes, kvGetErr = p.api.KVGet(botUserKey)
if kvGetErr != nil {
return false, errors.Wrap(kvGetErr, "failed to get bot")
}
}
if botIDBytes != nil {
if post.UserId == string(botIDBytes) {
return false, nil
}
}
if post.IsSystemMessage() && !messageProcessOptions.AllowSystemMessages {
return false, nil
}
if !messageProcessOptions.AllowWebhook && post.GetProp(model.PostPropsFromWebhook) == "true" {
return false, nil
}
if !messageProcessOptions.AllowBots {
user, appErr := p.api.GetUser(post.UserId)
if appErr != nil {
return false, errors.Wrap(appErr, "unable to get user")
}
if user.IsBot {
return false, nil
}
}
if len(messageProcessOptions.FilterChannelIDs) != 0 && !slices.Contains(messageProcessOptions.FilterChannelIDs, post.ChannelId) {
return false, nil
}
if len(messageProcessOptions.FilterUserIDs) != 0 && !slices.Contains(messageProcessOptions.FilterUserIDs, post.UserId) {
return false, nil
}
if botIDBytes != nil && messageProcessOptions.OnlyBotDMs {
channel, appErr := p.api.GetChannel(post.ChannelId)
if appErr != nil {
return false, errors.Wrap(appErr, "unable to get channel")
}
if !model.IsBotDMChannel(channel, string(botIDBytes)) {
return false, nil
}
}
return true, nil
}