mautrix-slack/commands.go

201 lines
5.6 KiB
Go

// mautrix-slack - A Matrix-Slack puppeting bridge.
// Copyright (C) 2022 Tulir Asokan
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package main
import (
"fmt"
"net/url"
"strings"
"maunium.net/go/mautrix/bridge/commands"
)
type WrappedCommandEvent struct {
*commands.Event
Bridge *SlackBridge
User *User
Portal *Portal
}
func (br *SlackBridge) RegisterCommands() {
proc := br.CommandProcessor.(*commands.Processor)
proc.AddHandlers(
cmdPing,
cmdLoginPassword,
cmdLoginToken,
cmdLogout,
cmdSyncTeams,
cmdDeletePortal,
)
}
func wrapCommand(handler func(*WrappedCommandEvent)) func(*commands.Event) {
return func(ce *commands.Event) {
user := ce.User.(*User)
var portal *Portal
if ce.Portal != nil {
portal = ce.Portal.(*Portal)
}
br := ce.Bridge.Child.(*SlackBridge)
handler(&WrappedCommandEvent{ce, br, user, portal})
}
}
var cmdPing = &commands.FullHandler{
Func: wrapCommand(fnPing),
Name: "ping",
Help: commands.HelpMeta{
Section: commands.HelpSectionAuth,
Description: "Check which teams you're currently signed into",
},
}
func fnPing(ce *WrappedCommandEvent) {
if len(ce.User.Teams) == 0 {
ce.Reply("You are not signed in to any Slack teams.")
return
}
var text strings.Builder
text.WriteString("You are signed in to the following Slack teams:\n")
for _, team := range ce.User.Teams {
teamInfo := ce.Bridge.DB.TeamInfo.GetBySlackTeam(team.Key.TeamID)
text.WriteString(fmt.Sprintf("%s - %s - %s.slack.com", teamInfo.TeamID, teamInfo.TeamName, teamInfo.TeamDomain))
if team.RTM == nil {
text.WriteString(" (Error: not connected to Slack)")
}
text.WriteRune('\n')
}
ce.Reply(text.String())
}
var cmdLoginPassword = &commands.FullHandler{
Func: wrapCommand(fnLoginPassword),
Name: "login-password",
Help: commands.HelpMeta{
Section: commands.HelpSectionAuth,
Description: "Link the bridge to a Slack account (legacy password login)",
Args: "<email> <domain> <password>",
},
}
func fnLoginPassword(ce *WrappedCommandEvent) {
if len(ce.Args) != 3 {
ce.Reply("**Usage**: $cmdprefix login-password <email> <domain> <password>")
return
}
if ce.User.IsLoggedInTeam(ce.Args[0], ce.Args[1]) {
ce.Reply("%s is already logged in to team %s", ce.Args[0], ce.Args[1])
return
}
user := ce.Bridge.GetUserByMXID(ce.User.MXID)
err := user.LoginTeam(ce.Args[0], ce.Args[1], ce.Args[2])
if err != nil {
ce.Reply("Failed to log in as %s for team %s: %v", ce.Args[0], ce.Args[1], err)
return
}
ce.Reply("Successfully logged into %s for team %s", ce.Args[0], ce.Args[1])
ce.Reply("Note: with legacy password login, your conversations will only be bridged once messages arrive in them through Slack. Use the `login-token` command if you want your joined conversations to be immediately bridged (you don't need to logout first).")
}
var cmdLoginToken = &commands.FullHandler{
Func: wrapCommand(fnLoginToken),
Name: "login-token",
Help: commands.HelpMeta{
Section: commands.HelpSectionAuth,
Description: "Link the bridge to a Slack account",
Args: "<token> <cookieToken>",
},
}
func fnLoginToken(ce *WrappedCommandEvent) {
if len(ce.Args) != 2 {
ce.Reply("**Usage**: $cmdprefix login-token <token> <cookieToken>")
return
}
cookieToken, _ := url.PathUnescape(ce.Args[1])
user := ce.Bridge.GetUserByMXID(ce.User.MXID)
info, err := user.TokenLogin(ce.Args[0], cookieToken)
if err != nil {
ce.Reply("Failed to log in with token: %v", err)
} else {
ce.Reply("Successfully logged into %s for team %s", info.UserEmail, info.TeamName)
}
}
var cmdLogout = &commands.FullHandler{
Func: wrapCommand(fnLogout),
Name: "logout",
Help: commands.HelpMeta{
Section: commands.HelpSectionAuth,
Description: "Unlink the bridge from your Slack account.",
Args: "<email> <domain>",
},
RequiresLogin: true,
}
func fnLogout(ce *WrappedCommandEvent) {
if len(ce.Args) != 2 {
ce.Reply("**Usage**: $cmdprefix logout <email> <domain>")
return
}
domain := strings.TrimSuffix(ce.Args[1], ".slack.com")
userTeam := ce.User.bridge.DB.UserTeam.GetBySlackDomain(ce.User.MXID, ce.Args[0], domain)
err := ce.User.LogoutUserTeam(userTeam)
if err != nil {
ce.Reply("Error logging out: %v", err)
} else {
ce.Reply("Logged out successfully.")
}
}
var cmdSyncTeams = &commands.FullHandler{
Func: wrapCommand(fnSyncTeams),
Name: "sync-teams",
Help: commands.HelpMeta{
Section: commands.HelpSectionGeneral,
Description: "Synchronize team information and channels from Slack into Matrix",
},
RequiresLogin: true,
}
func fnSyncTeams(ce *WrappedCommandEvent) {
for _, team := range ce.User.Teams {
ce.User.UpdateTeam(team, true)
}
ce.Reply("Done syncing teams.")
}
var cmdDeletePortal = &commands.FullHandler{
Func: wrapCommand(fnDeletePortal),
Name: "delete-portal",
RequiresPortal: true,
}
func fnDeletePortal(ce *WrappedCommandEvent) {
ce.Portal.delete()
ce.Bridge.cleanupRoom(ce.Portal.MainIntent(), ce.Portal.MXID, false, ce.Log)
ce.Log.Infofln("Deleted portal")
}