236 lines
6.0 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 plugin
import (
"fmt"
"strings"
"github.com/mattermost/mattermost/server/public/model"
)
func (p *Plugin) commandLink(args *model.CommandArgs, split []string) (*model.CommandResponse, *model.AppError) {
if len(split) < 3 {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "❌ Usage: /sentry link <project_slug>",
}, nil
}
projectSlug := split[2]
channelID := args.ChannelId
hooks := HookSettings{
EventAlert: true,
MetricAlert: true,
Issue: true,
Comment: true,
}
project, err := p.linkProjectToChannel(projectSlug, channelID, hooks)
if err != nil {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "❌ " + err.Error(),
}, nil
}
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: fmt.Sprintf(
"✅ Linked **%s** (`%s`) to this channel",
project.Name,
project.Slug,
),
}, nil
}
func (p *Plugin) commandUnlink(args *model.CommandArgs, split []string) (*model.CommandResponse, *model.AppError) {
if len(split) < 3 {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "❌ Usage: /sentry unlink <project_slug>",
}, nil
}
slug := split[2]
projects, err := p.getAllProjects()
if err != nil {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: " No linked projects",
}, nil
}
var removed *LinkedProject
for _, project := range projects {
if project.Slug == slug {
removed = project
if err := p.deleteProject(project.ID); err != nil {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "❌ Failed to unlink project: " + err.Error(),
}, nil
}
break
}
}
if removed == nil {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "❌ Project not found",
}, nil
}
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: fmt.Sprintf(
"✅ Unlinked **%s** (`%s`)",
removed.Name,
removed.Slug,
),
}, nil
}
func (p *Plugin) commandList(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
projects, err := p.getAllProjects()
if err != nil || len(projects) == 0 {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "_No linked Sentry projects_",
}, nil
}
var lines []string
for _, project := range projects {
channelName := project.ChannelID
if ch, err := p.API.GetChannel(project.ChannelID); err == nil {
channelName = "~" + ch.Name
}
lines = append(
lines,
fmt.Sprintf(
"• **%s** (`%s`) → %s",
project.Name,
project.Slug,
channelName,
),
)
}
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "🔗 **Linked Sentry projects:**\n\n" + strings.Join(lines, "\n"),
}, nil
}
func (p *Plugin) commandHelp() *model.CommandResponse {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: `**Sentry plugin commands**
• /sentry link <project_slug> — link project to channel
• /sentry help — show help`,
}
}
func (p *Plugin) commandSetup(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
if args.TriggerId == "" {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "This command must be run from Mattermost UI",
}, nil
}
// Получаем каналы пользователя
channels, appErr := p.API.GetChannelsForTeamForUser(args.TeamId, args.UserId, false)
if appErr != nil {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "❌ Failed to load channels",
}, nil
}
options := []*model.PostActionOptions{}
for _, ch := range channels {
options = append(options, &model.PostActionOptions{
Text: ch.DisplayName,
Value: ch.Id,
})
}
modal := &model.Dialog{
Title: "Sentry Setup",
CallbackId: "sentry_setup",
SubmitLabel: "Save",
Elements: []model.DialogElement{
// ───── General ─────
{
DisplayName: "Default channel",
Name: "default_channel_id",
Type: "select",
Options: options,
Default: args.ChannelId,
},
{
DisplayName: "Sentry project slug",
Name: "project_slug",
Type: "text",
Placeholder: "frontend-app",
},
// ───── Webhook types ─────
{DisplayName: "Event alerts", Name: "hook_event_alert", Type: "bool", Default: "true"},
{DisplayName: "Metric alerts", Name: "hook_metric_alert", Type: "bool", Default: "true"},
{DisplayName: "Issues", Name: "hook_issue", Type: "bool", Default: "true"},
{DisplayName: "Comments", Name: "hook_comment", Type: "bool", Default: "true"},
},
}
req := model.OpenDialogRequest{
TriggerId: args.TriggerId,
URL: "/plugins/ru.loop.plugin.sentry/dialog/submit",
Dialog: *modal,
}
if appErr := p.API.OpenInteractiveDialog(req); appErr != nil {
return &model.CommandResponse{
ResponseType: model.CommandResponseTypeEphemeral,
Text: "❌ Failed to open setup dialog: " + appErr.Error(),
}, nil
}
return &model.CommandResponse{}, nil
}
func (p *Plugin) commandSetupHook(
args *model.CommandArgs,
hook string,
) (*model.CommandResponse, *model.AppError) {
project, err := p.getProjectByChannel(args.ChannelId)
if err != nil {
return ephemeral("❌ " + err.Error()), nil
}
hookType := HookType(hook)
if !isHookEnabled(project, hookType) {
return ephemeral("⚠️ This webhook is disabled in setup"), nil
}
switch hookType {
case HookEventAlert:
return p.openEventAlertSetup(args, project)
case HookMetricAlert:
return p.openMetricAlertSetup(args, project)
case HookIssue:
return p.openIssueSetup(args, project)
case HookComment:
return p.openCommentSetup(args, project)
default:
return ephemeral("❌ Unknown webhook type"), nil
}
}