Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 21 additions & 18 deletions bot/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"go.mau.fi/util/exerrors"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/appservice"
"maunium.net/go/mautrix/continuwuityadmin"
"maunium.net/go/mautrix/crypto"
"maunium.net/go/mautrix/crypto/cryptohelper"
"maunium.net/go/mautrix/id"
Expand All @@ -23,14 +24,15 @@ type Bot struct {
Meta *database.Bot
Log zerolog.Logger
*mautrix.Client
Intent *appservice.IntentAPI
SynapseAdmin *synapseadmin.Client
ServerName string
CryptoStore *crypto.SQLCryptoStore
CryptoHelper *cryptohelper.CryptoHelper
Mach *crypto.OlmMachine
eventProcessor *appservice.EventProcessor
mainDB *database.Database
Intent *appservice.IntentAPI
SynapseAdmin *synapseadmin.Client
ContinuwuityAdmin *continuwuityadmin.Client
ServerName string
CryptoStore *crypto.SQLCryptoStore
CryptoHelper *cryptohelper.CryptoHelper
Mach *crypto.OlmMachine
eventProcessor *appservice.EventProcessor
mainDB *database.Database
}

func NewBot(
Expand Down Expand Up @@ -75,16 +77,17 @@ func NewBot(
}
}
return &Bot{
Meta: bot,
Client: client,
Intent: intent,
Log: log,
SynapseAdmin: adminClient,
ServerName: client.UserID.Homeserver(),
CryptoStore: cryptoStore,
CryptoHelper: helper,
eventProcessor: ep,
mainDB: db,
Meta: bot,
Client: client,
Intent: intent,
Log: log,
SynapseAdmin: adminClient,
ContinuwuityAdmin: &continuwuityadmin.Client{Client: adminClient.Client},
ServerName: client.UserID.Homeserver(),
CryptoStore: cryptoStore,
CryptoHelper: helper,
eventProcessor: ep,
mainDB: db,
}
}

Expand Down
20 changes: 19 additions & 1 deletion cmd/meowlnir/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
flag "maunium.net/go/mauflag"
"maunium.net/go/mautrix"
"maunium.net/go/mautrix/appservice"
"maunium.net/go/mautrix/continuwuityadmin"
cryptoupgrade "maunium.net/go/mautrix/crypto/sql_store_upgrade"
"maunium.net/go/mautrix/federation"
"maunium.net/go/mautrix/id"
Expand Down Expand Up @@ -397,7 +398,24 @@ func (m *Meowlnir) Run(ctx context.Context) {

func (m *Meowlnir) LoadAllRoomHashes(ctx context.Context) {
if m.SynapseDB == nil {
m.Log.Warn().Msg("Synapse database not configured, can't load all room hashes")
m.Log.Debug().Msg("No Synapse database configured, loading room IDs from Continuwuity admin API")
c10yClient := continuwuityadmin.Client{Client: m.AS.Intent(m.Config.Meowlnir.ContinuwuityAdminUser).Client}
start := time.Now()
resp, err := c10yClient.ListRooms(ctx)
if err != nil {
if errors.Is(err, mautrix.MUnrecognized) {
err = nil
}
m.Log.Warn().Err(err).Msg("Synapse database not configured, can't load all room hashes")
return
}
for _, room := range resp.Rooms {
m.RoomHashes.Put(room)
}
m.Log.Info().
Dur("duration", time.Since(start)).
Int("count", len(resp.Rooms)).
Msg("Read all existing room IDs from Continuwuity admin API")
return
}
start := time.Now()
Expand Down
3 changes: 2 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ type MeowlnirConfig struct {
HackyRuleFilter []string `yaml:"hacky_rule_filter"`
HackyRedactPatterns []string `yaml:"hacky_redact_patterns"`

AdminTokens map[id.UserID]string `yaml:"admin_tokens"`
AdminTokens map[id.UserID]string `yaml:"admin_tokens"`
ContinuwuityAdminUser id.UserID `yaml:"continuwuity_admin_user"`
}

type Meowlnir4AllConfig struct {
Expand Down
4 changes: 4 additions & 0 deletions config/example-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ meowlnir:
admin_tokens:
"@abuse:example.com": admin_token

# The user ID that is in the Continuwuity admin room. Used to load room hashes and takedowns.
# Irrelevant unless you use Continuwuity.
continuwuity_admin_user: "@abuse:example.com"

# Settings for provisioning new bots using the !provision command.
# None of this is relevant unless you offer moderation bots to other users.
meowlnir4all:
Expand Down
1 change: 1 addition & 0 deletions config/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func upgradeConfig(helper up.Helper) {
helper.Copy(up.List, "meowlnir", "hacky_rule_filter")
helper.Copy(up.List, "meowlnir", "hacky_redact_patterns")
helper.Copy(up.Map, "meowlnir", "admin_tokens")
helper.Copy(up.Str, "meowlnir", "continuwuity_admin_user")

helper.Copy(up.Str|up.Null, "meowlnir4all", "admin_room")
helper.Copy(up.Str, "meowlnir4all", "localpart_template")
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
golang.org/x/sync v0.19.0
gopkg.in/yaml.v3 v3.0.1
maunium.net/go/mauflag v1.0.0
maunium.net/go/mautrix v0.26.2
maunium.net/go/mautrix v0.26.3-0.20260119025107-d8b3f64a2d0f
)

require (
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
maunium.net/go/mauflag v1.0.0 h1:YiaRc0tEI3toYtJMRIfjP+jklH45uDHtT80nUamyD4M=
maunium.net/go/mauflag v1.0.0/go.mod h1:nLivPOpTpHnpzEh8jEdSL9UqO9+/KBJFmNRlwKfkPeA=
maunium.net/go/mautrix v0.26.2 h1:rLiZLQoSKCJDZ+mF1gBQS4p74h3jZXs83g8D4W6Te8g=
maunium.net/go/mautrix v0.26.2/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo=
maunium.net/go/mautrix v0.26.3-0.20260119025107-d8b3f64a2d0f h1:tRnBU1JAj0hQlS/TfNhoIx2Xjyc6HcBwSVesWAiVZbU=
maunium.net/go/mautrix v0.26.3-0.20260119025107-d8b3f64a2d0f/go.mod h1:CUxSZcjPtQNxsZLRQqETAxg2hiz7bjWT+L1HCYoMMKo=
47 changes: 34 additions & 13 deletions policyeval/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"

"fmt"
"regexp"
"slices"
Expand Down Expand Up @@ -926,27 +928,27 @@ var cmdRoomInfo = &CommandHandler{
}),
}

func formatDeleteResult(resp synapseadmin.RespDeleteRoomResult) string {
func formatDeleteResult(kicked, failed []id.UserID, aliases []id.RoomAlias) string {
var parts []string
if len(resp.KickedUsers) > 10 {
parts = append(parts, fmt.Sprintf("* Kicked %d users", len(resp.KickedUsers)))
if len(kicked) > 10 {
parts = append(parts, fmt.Sprintf("* Kicked %d users", len(kicked)))
} else {
kickedUsers := make([]string, len(resp.KickedUsers))
for i, user := range resp.KickedUsers {
kickedUsers := make([]string, len(kicked))
for i, user := range kicked {
kickedUsers[i] = format.MarkdownMention(user)
}
parts = append(parts, fmt.Sprintf("* Kicked users: %s", strings.Join(kickedUsers, ", ")))
}
if len(resp.FailedToKickUsers) > 0 {
failedUsers := make([]string, len(resp.FailedToKickUsers))
for i, user := range resp.FailedToKickUsers {
if len(failed) > 0 {
failedUsers := make([]string, len(failed))
for i, user := range failed {
failedUsers[i] = format.MarkdownMention(user)
}
parts = append(parts, fmt.Sprintf("* Failed to kick users: %s", strings.Join(failedUsers, ", ")))
}
if len(resp.LocalAliases) > 0 {
localAliases := make([]string, len(resp.LocalAliases))
for i, alias := range resp.LocalAliases {
if len(aliases) > 0 {
localAliases := make([]string, len(aliases))
for i, alias := range aliases {
localAliases[i] = format.SafeMarkdownCode(alias)
}
parts = append(parts, fmt.Sprintf("* Deleted local aliases: %s", strings.Join(localAliases, ", ")))
Expand All @@ -970,7 +972,9 @@ var cmdRoomDeleteStatus = &CommandHandler{
if err != nil {
ce.Reply("Failed to get delete status for %s: %v", format.SafeMarkdownCode(args.DeleteID), err)
} else if resp.Status == "complete" {
ce.Reply("Deletion is complete:\n\n%s", formatDeleteResult(resp.ShutdownRoom))
ce.Reply("Deletion is complete:\n\n%s", formatDeleteResult(
resp.ShutdownRoom.KickedUsers, resp.ShutdownRoom.FailedToKickUsers, resp.ShutdownRoom.LocalAliases,
))
} else if resp.Status == "failed" {
ce.Reply("Deletion failed: %s", resp.Error)
} else {
Expand Down Expand Up @@ -1060,9 +1064,26 @@ var cmdRoomDelete = &CommandHandler{
resp, err := ce.Meta.Bot.SynapseAdmin.DeleteRoomSync(ce.Ctx, roomID, req)
_, _ = ce.Meta.Bot.RedactEvent(ce.Ctx, ce.RoomID, reactionID)
if err != nil {
if errors.Is(err, mautrix.MUnrecognized) {
resp2, err := ce.Meta.Bot.ContinuwuityAdmin.BanRoom(ce.Ctx, roomID)
if err != nil {
ce.Reply("Failed to ban room %s: %v", format.SafeMarkdownCode(roomID), err)
return
}
ce.Reply(
"Successfully banned room %s\n\n%s",
format.SafeMarkdownCode(roomID),
formatDeleteResult(resp2.KickedUsers, resp2.FailedKickedUsers, resp2.LocalAliases),
)
return
}
ce.Reply("Failed to delete room %s: %v", format.SafeMarkdownCode(roomID), err)
} else {
ce.Reply("Successfully deleted room %s\n\n%s", format.SafeMarkdownCode(roomID), formatDeleteResult(resp))
ce.Reply(
"Successfully deleted room %s\n\n%s",
format.SafeMarkdownCode(roomID),
formatDeleteResult(resp.KickedUsers, resp.FailedToKickUsers, resp.LocalAliases),
)
}
}
}),
Expand Down