mattermost-plugin-fawkes/server/plugin.go

277 lines
9.1 KiB
Go

package main
import (
"database/sql"
"fmt"
"strings"
"sync"
sq "github.com/Masterminds/squirrel"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/public/plugin"
pluginapi "github.com/mattermost/mattermost/server/public/pluginapi"
)
// Plugin implements the interface expected by the Mattermost server to communicate between the server and plugin processes.
type Plugin struct {
plugin.MattermostPlugin
// configurationLock synchronizes access to the configuration.
configurationLock sync.RWMutex
// configuration is the active plugin configuration. Consult getConfiguration and
// setConfiguration for usage.
configuration *configuration
apiClient *pluginapi.Client
db *sql.DB
}
// ServeHTTP demonstrates a plugin that handles HTTP requests by greeting the world.
// func (p *Plugin) ServeHTTP(c *plugin.Context, w http.ResponseWriter, r *http.Request) {
// fmt.Fprint(w, "Hello, world!")
// }
func (p *Plugin) Q() sq.StatementBuilderType {
return sq.StatementBuilder.PlaceholderFormat(sq.Dollar).RunWith(p.db)
}
func (p *Plugin) OnActivate() error {
p.apiClient = pluginapi.NewClient(p.API, p.Driver)
db, dbErr := p.apiClient.Store.GetMasterDB()
if dbErr != nil {
return dbErr
}
p.db = db
if err := p.API.UnregisterCommand("", "fawkes"); err != nil {
return err
}
autocompleteData := model.NewAutocompleteData("fawkes", "[command]", "Gets information about the msteams current state")
whitelistedCmd := model.NewAutocompleteData("count-whitelisted", "", "Counts the amount of whitelisted users in msteamssync_whitelisted_users")
// adds the from arg?
autocompleteData.AddCommand(whitelistedCmd)
lastConnectAtCmd := model.NewAutocompleteData("count-lastconnectat", "", "Counts the amount of users with a lastconnectat value != 0 in msteamssync_whitelisted_users")
autocompleteData.AddCommand(lastConnectAtCmd)
lastDisconnectAtCmd := model.NewAutocompleteData("count-lastdisconnectat", "", "Counts the amount of users with a lastdisconnectat value != 0 in msteamssync_whitelisted_users")
autocompleteData.AddCommand(lastDisconnectAtCmd)
userInfoCmd := model.NewAutocompleteData("user-info", "[username|email]", "Returns information about the user state regarding msteams")
autocompleteData.AddCommand(userInfoCmd)
channelInfoCmd := model.NewAutocompleteData("channel-info", "[channelID]", "Returns information about the channel state regarding msteams")
autocompleteData.AddCommand(channelInfoCmd)
listInvitesCmd := model.NewAutocompleteData("list-invites", "", "List the current invites")
autocompleteData.AddCommand(listInvitesCmd)
removeInviteCmd := model.NewAutocompleteData("remove-invite", "[mmUserID]", "Deletes the invite for the user ID")
autocompleteData.AddCommand(removeInviteCmd)
cmd := &model.Command{
Trigger: "fawkes",
AutoComplete: true,
AutoCompleteDesc: "Get insights of the user state",
AutocompleteData: autocompleteData,
}
if err := p.API.RegisterCommand(cmd); err != nil {
return err
}
p.API.LogDebug("plugin started")
return nil
}
func (p *Plugin) ExecuteCommand(_ *plugin.Context, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
split := strings.Fields(args.Command)
command := split[0]
var parameters []string
action := ""
if len(split) > 1 {
action = split[1]
}
if len(split) > 2 {
parameters = split[2:]
}
if command != "/fawkes" {
return &model.CommandResponse{}, nil
}
switch (action) {
case "count-whitelisted":
rows, err := p.Q().Select("count(*)").From("msteamssync_whitelisted_users").Query()
if err != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR: %s", err), args)
}
defer rows.Close()
var result int
rows.Next()
if err := rows.Scan(&result); err != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR: %s", err), args)
}
return p.sendEphemeralAndExit(fmt.Sprintf("There are %d users in the msteamssync_whitelisted_users table", result), args)
case "count-lastconnectat":
rows, err := p.Q().Select("count(*)").From("msteamssync_users").Where(sq.NotEq{"lastconnectat": 0}).Query()
if err != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR: %s", err), args)
}
defer rows.Close()
var result int
rows.Next()
if err := rows.Scan(&result); err != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR: %s", err), args)
}
return p.sendEphemeralAndExit(fmt.Sprintf("There are %d users in the msteamssync_users table with `lastconnectat != 0`", result), args)
case "count-lastdisconnectat":
rows, err := p.Q().Select("count(*)").From("msteamssync_users").Where(sq.NotEq{"lastdisconnectat": 0}).Query()
if err != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR: %s", err), args)
}
defer rows.Close()
var result int
rows.Next()
if err := rows.Scan(&result); err != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR: %s", err), args)
}
return p.sendEphemeralAndExit(fmt.Sprintf("There are %d users in the msteamssync_users table with `lastdisconnectat != 0`", result), args)
case "user-info":
if len(parameters) != 1 {
return p.sendEphemeralAndExit("user-info must receive one \"username\" or \"email\" parameter", args)
}
email := parameters[0]
var user *model.User
var appErr *model.AppError
user, appErr = p.API.GetUserByEmail(email)
if appErr != nil {
user, appErr = p.API.GetUserByUsername(email)
if appErr != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR getting the user by email and username: %s", appErr.Error()), args)
}
}
pref, appErr := p.API.GetPreferenceForUser(user.Id, "pp_com.mattermost.msteams-sync", "platform")
if appErr != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR getting user preferences: %s", appErr.Error()), args)
}
rows, _ := p.Q().
Select("msteamsuserid, token, lastconnectat, lastdisconnectat").
From("msteamssync_users").
Where(sq.Eq{"mmuserid": user.Id}).
Query()
// if err != nil {
// return p.sendEphemeralAndExit(fmt.Sprintf("ERROR querying for msteamssync user: %s", err), args)
// }
defer rows.Close()
var mmTeamsUserID string
var token string
var lastConnectAt int
var lastDisconnectAt int
// if !rows.Next() {
// return p.sendEphemeralAndExit(fmt.Sprintf("User %q not found in the msteamssync_users table", email), args)
// }
if rows.Next() {
if err := rows.Scan(&mmTeamsUserID, &token, &lastConnectAt, &lastDisconnectAt); err != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR scanning rows: %s", err), args)
}
}
var remoteID string
if user.RemoteId != nil {
remoteID = *user.RemoteId
}
hasToken := false
if token != "" {
hasToken = true
}
return p.sendEphemeralAndExit(fmt.Sprintf("User %q has\n - `mmTeamsUserID`: %s\n - `hasToken`: %v\n - `lastConnectAt`: %d\n - `lastDisconnectAt`: %d\n - `remoteID`: %s\n - `primaryPlatform preference`: %s", email, mmTeamsUserID, hasToken, lastConnectAt, lastDisconnectAt, remoteID, pref.Value), args)
case "channel-info":
if len(parameters) != 1 {
return p.sendEphemeralAndExit("channel-info must receive the channel ID", args)
}
channelID := parameters[0]
channel, appErr := p.API.GetChannel(channelID)
if appErr != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR getting the channel: %s", appErr.Error()), args)
}
return p.sendEphemeralAndExit(fmt.Sprintf(" - `Id`: %s\n - `name`: %s\n - `IsShared`: %v", channel.Id, channel.Name, channel.IsShared()), args)
case "list-invites":
rows, err := p.Q().
Select("mmuserid, invitependingsince, invitelastsentat").
From("msteamssync_invited_users").
Query()
if err != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR querying for invited users: %s", err), args)
}
defer rows.Close()
invites := []map[string]any{}
for rows.Next() {
var mmUserID string
var invitePendingSince int
var inviteLastSentAt int
if err := rows.Scan(&mmUserID, &invitePendingSince, &inviteLastSentAt); err != nil {
return p.sendEphemeralAndExit(fmt.Sprintf("ERROR scanning row: %s", err), args)
}
invites = append(invites, map[string]any{
"mmUserID": mmUserID,
"invitePendingSince": invitePendingSince,
"inviteLastSentAt": inviteLastSentAt,
})
}
message := "Invite list:\n"
for _, invite := range invites {
message += fmt.Sprintf(
" - `mmUserID`: %s :: `invitePendingSince`: %d :: `inviteLastSentAt`: %d\n",
invite["mmUserID"],
invite["invitePendingSince"],
invite["inviteLastSentAt"],
)
}
return p.sendEphemeralAndExit(message, args)
case "delete-invite":
return p.sendEphemeralAndExit("Not implemented yet", args)
default:
p.sendEphemeral(fmt.Sprintf("Invalid command %q", action), args)
return &model.CommandResponse{}, nil
}
}
func (p *Plugin) sendEphemeral(message string, args *model.CommandArgs) {
_ = p.API.SendEphemeralPost(args.UserId, &model.Post{
Message: message,
UserId: args.UserId,
ChannelId: args.ChannelId,
})
}
func (p *Plugin) sendEphemeralAndExit(message string, args *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
p.sendEphemeral(message, args)
return &model.CommandResponse{}, nil
}
// See https://developers.mattermost.com/extend/plugins/server/reference/