281 lines
6.9 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"encoding/json"
"fmt"
"strings"
"github.com/larkox/mattermost-plugin-badges/badgesmodel"
"github.com/mattermost/mattermost-server/v5/model"
)
func areRolesAllowed(userRoles []string, allowedRoles map[string]bool) bool {
for ar, b := range allowedRoles {
if !b {
continue
}
for _, ur := range userRoles {
if ar == ur {
return true
}
}
}
return false
}
func canGrantBadge(user *model.User, badgeAdminIDs map[string]bool, badge *badgesmodel.Badge, badgeType *badgesmodel.BadgeTypeDefinition) bool {
if badgeAdminIDs[user.Id] {
return true
}
if user.IsSystemAdmin() {
return true
}
if badgeType.CreatedBy == user.Id {
return true
}
if badge.CreatedBy == user.Id {
return true
}
blocked := badgeType.CanGrant.BlockList[user.Id]
if blocked {
return false
}
if areRolesAllowed(user.GetRoles(), badgeType.CanGrant.Roles) {
return true
}
allowed := badgeType.CanGrant.AllowList[user.Id]
if allowed {
return true
}
return badgeType.CanGrant.Everyone
}
func canCreateBadge(user *model.User, badgeAdminIDs map[string]bool, badgeType *badgesmodel.BadgeTypeDefinition) bool {
if badgeAdminIDs[user.Id] {
return true
}
if user.IsSystemAdmin() {
return true
}
if badgeType.CreatedBy == user.Id {
return true
}
blocked := badgeType.CanCreate.BlockList[user.Id]
if blocked {
return false
}
if areRolesAllowed(user.GetRoles(), badgeType.CanCreate.Roles) {
return true
}
allowed := badgeType.CanCreate.AllowList[user.Id]
if allowed {
return true
}
return badgeType.CanCreate.Everyone
}
func canEditType(user *model.User, badgeAdminIDs map[string]bool, badgeType *badgesmodel.BadgeTypeDefinition) bool {
if badgeAdminIDs[user.Id] {
return true
}
return user.IsSystemAdmin()
}
func canEditBadge(user *model.User, badgeAdminIDs map[string]bool, badge *badgesmodel.Badge, badgeType *badgesmodel.BadgeTypeDefinition) bool {
if badgeAdminIDs[user.Id] {
return true
}
if user.IsSystemAdmin() {
return true
}
if badgeType != nil && canCreateBadge(user, badgeAdminIDs, badgeType) {
return true
}
return false
}
func canCreateType(user *model.User, badgeAdminIDs map[string]bool, isPlugin bool) bool {
if isPlugin {
return true
}
if badgeAdminIDs[user.Id] {
return true
}
return user.IsSystemAdmin()
}
func canCreateSubscription(user *model.User, badgeAdminIDs map[string]bool, channelID string) bool {
if badgeAdminIDs[user.Id] {
return true
}
return user.IsSystemAdmin()
}
func dumpObject(o interface{}) {
b, err := json.MarshalIndent(o, "", " ")
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(string(b))
}
const fallbackEmoji = "question"
// sanitizeBadgeEmoji checks if a badge's custom emoji still exists.
// If the emoji was deleted, it replaces it with a fallback and persists the change.
func (p *Plugin) sanitizeBadgeEmoji(badge *badgesmodel.Badge) {
if badge.ImageType != badgesmodel.ImageTypeEmoji {
return
}
if _, ok := model.SystemEmojis[badge.Image]; ok {
return
}
_, appErr := p.API.GetEmojiByName(badge.Image)
if appErr == nil {
return
}
badge.Image = fallbackEmoji
if err := p.store.UpdateBadge(badge); err != nil {
p.mm.Log.Warn("Failed to replace deleted emoji on badge", "badge_id", badge.ID, "error", err.Error())
}
}
func (p *Plugin) notifyGrant(badgeID badgesmodel.BadgeID, granter string, granted *model.User, inChannel bool, channelID string, reason string) {
b, errBadge := p.store.GetBadgeDetails(badgeID)
granterUser, errUser := p.mm.User.Get(granter)
if errBadge != nil {
p.mm.Log.Debug("badge error", "err", errBadge)
}
if errUser != nil {
p.mm.Log.Debug("user error", "err", errUser)
}
subs, _ := p.store.GetTypeSubscriptions(b.Type)
if errBadge == nil && errUser == nil {
image := ""
switch b.ImageType {
case badgesmodel.ImageTypeEmoji:
image = fmt.Sprintf(":%s: ", b.Image)
case badgesmodel.ImageTypeAbsoluteURL:
image = fmt.Sprintf("![icon](%s) ", b.Image)
}
// DM to the granted user — use their locale
Tdm := p.getT(granted.Locale)
dmPost := &model.Post{}
dmText := Tdm("badges.notify.dm_text", "@%s выдал вам значок %s`%s`.", granterUser.Username, image, b.Name)
if reason != "" {
dmText += Tdm("badges.notify.dm_reason", "\nПочему? ") + reason
}
dmAttachment := model.SlackAttachment{
Title: Tdm("badges.notify.title", "%sзначок выдан!", image),
Text: dmText,
}
model.ParseSlackAttachment(dmPost, []*model.SlackAttachment{&dmAttachment})
err := p.mm.Post.DM(p.BotUserID, granted.Id, dmPost)
if err != nil {
p.mm.Log.Debug("dm error", "err", err)
}
// Channel/subscription notifications — use granter's locale
Tch := p.getT(granterUser.Locale)
basePost := model.Post{
UserId: p.BotUserID,
ChannelId: channelID,
}
text := Tch("badges.notify.channel_text", "@%s выдал @%s значок %s`%s`.", granterUser.Username, granted.Username, image, b.Name)
if reason != "" {
text += Tch("badges.notify.dm_reason", "\nПочему? ") + reason
}
attachment := model.SlackAttachment{
Title: Tch("badges.notify.title", "%sзначок выдан!", image),
Text: text,
}
model.ParseSlackAttachment(&basePost, []*model.SlackAttachment{&attachment})
for _, sub := range subs {
post := basePost.Clone()
post.ChannelId = sub
err := p.mm.Post.CreatePost(post)
if err != nil {
p.mm.Log.Debug("notify subscription error", "err", err)
}
}
if inChannel {
if !p.API.HasPermissionToChannel(granter, channelID, model.PERMISSION_CREATE_POST) {
Tg := p.getT(granterUser.Locale)
p.mm.Post.SendEphemeralPost(granter, &model.Post{Message: Tg("badges.notify.no_permission_channel", "У вас нет прав на отправку уведомления о выдаче в этот канал."), ChannelId: channelID})
} else {
post := basePost.Clone()
post.ChannelId = channelID
err := p.mm.Post.CreatePost(post)
if err != nil {
p.mm.Log.Debug("notify here error", "err", err)
}
}
}
}
}
// resolveUsernameList parses a comma-separated list of usernames and returns a map of user IDs.
func (p *Plugin) resolveUsernameList(csv string) (map[string]bool, error) {
result := map[string]bool{}
usernames := strings.Split(csv, ",")
for _, username := range usernames {
username = strings.TrimSpace(username)
if username == "" {
continue
}
user, err := p.mm.User.GetByUsername(username)
if err != nil {
return nil, fmt.Errorf("user not found: %s", username)
}
result[user.Id] = true
}
return result, nil
}
// resolveUserIDList converts a map of user IDs to a comma-separated list of usernames.
func (p *Plugin) resolveUserIDList(ids map[string]bool) string {
var names []string
for id, allowed := range ids {
if !allowed {
continue
}
user, err := p.mm.User.Get(id)
if err != nil {
continue
}
names = append(names, user.Username)
}
return strings.Join(names, ", ")
}
func getBooleanString(in bool) string {
if in {
return TrueString
}
return FalseString
}