fix: harden Mattermost plugin gating (#1428) (thanks @damoahdominic)
This commit is contained in:
@@ -40,17 +40,6 @@ extension ChannelsSettings {
|
||||
return .orange
|
||||
}
|
||||
|
||||
var mattermostTint: Color {
|
||||
guard let status = self.channelStatus("mattermost", as: ChannelsStatusSnapshot.MattermostStatus.self)
|
||||
else { return .secondary }
|
||||
if !status.configured { return .secondary }
|
||||
if status.lastError != nil { return .orange }
|
||||
if status.probe?.ok == false { return .orange }
|
||||
if status.connected == true { return .green }
|
||||
if status.running { return .orange }
|
||||
return .orange
|
||||
}
|
||||
|
||||
var signalTint: Color {
|
||||
guard let status = self.channelStatus("signal", as: ChannelsStatusSnapshot.SignalStatus.self)
|
||||
else { return .secondary }
|
||||
@@ -96,15 +85,6 @@ extension ChannelsSettings {
|
||||
return "Configured"
|
||||
}
|
||||
|
||||
var mattermostSummary: String {
|
||||
guard let status = self.channelStatus("mattermost", as: ChannelsStatusSnapshot.MattermostStatus.self)
|
||||
else { return "Checking…" }
|
||||
if !status.configured { return "Not configured" }
|
||||
if status.connected == true { return "Connected" }
|
||||
if status.running { return "Running" }
|
||||
return "Configured"
|
||||
}
|
||||
|
||||
var signalSummary: String {
|
||||
guard let status = self.channelStatus("signal", as: ChannelsStatusSnapshot.SignalStatus.self)
|
||||
else { return "Checking…" }
|
||||
@@ -213,38 +193,6 @@ extension ChannelsSettings {
|
||||
return lines.isEmpty ? nil : lines.joined(separator: " · ")
|
||||
}
|
||||
|
||||
var mattermostDetails: String? {
|
||||
guard let status = self.channelStatus("mattermost", as: ChannelsStatusSnapshot.MattermostStatus.self)
|
||||
else { return nil }
|
||||
var lines: [String] = []
|
||||
if let source = status.botTokenSource {
|
||||
lines.append("Token source: \(source)")
|
||||
}
|
||||
if let baseUrl = status.baseUrl, !baseUrl.isEmpty {
|
||||
lines.append("Base URL: \(baseUrl)")
|
||||
}
|
||||
if let probe = status.probe {
|
||||
if probe.ok {
|
||||
if let name = probe.bot?.username {
|
||||
lines.append("Bot: @\(name)")
|
||||
}
|
||||
if let elapsed = probe.elapsedMs {
|
||||
lines.append("Probe \(Int(elapsed))ms")
|
||||
}
|
||||
} else {
|
||||
let code = probe.status.map { String($0) } ?? "unknown"
|
||||
lines.append("Probe failed (\(code))")
|
||||
}
|
||||
}
|
||||
if let last = self.date(fromMs: status.lastProbeAt ?? status.lastConnectedAt) {
|
||||
lines.append("Last probe \(relativeAge(from: last))")
|
||||
}
|
||||
if let err = status.lastError, !err.isEmpty {
|
||||
lines.append("Error: \(err)")
|
||||
}
|
||||
return lines.isEmpty ? nil : lines.joined(separator: " · ")
|
||||
}
|
||||
|
||||
var signalDetails: String? {
|
||||
guard let status = self.channelStatus("signal", as: ChannelsStatusSnapshot.SignalStatus.self)
|
||||
else { return nil }
|
||||
@@ -296,7 +244,7 @@ extension ChannelsSettings {
|
||||
}
|
||||
|
||||
var orderedChannels: [ChannelItem] {
|
||||
let fallback = ["whatsapp", "telegram", "discord", "slack", "mattermost", "signal", "imessage"]
|
||||
let fallback = ["whatsapp", "telegram", "discord", "slack", "signal", "imessage"]
|
||||
let order = self.store.snapshot?.channelOrder ?? fallback
|
||||
let channels = order.enumerated().map { index, id in
|
||||
ChannelItem(
|
||||
@@ -359,8 +307,6 @@ extension ChannelsSettings {
|
||||
return self.telegramTint
|
||||
case "discord":
|
||||
return self.discordTint
|
||||
case "mattermost":
|
||||
return self.mattermostTint
|
||||
case "signal":
|
||||
return self.signalTint
|
||||
case "imessage":
|
||||
@@ -380,8 +326,6 @@ extension ChannelsSettings {
|
||||
return self.telegramSummary
|
||||
case "discord":
|
||||
return self.discordSummary
|
||||
case "mattermost":
|
||||
return self.mattermostSummary
|
||||
case "signal":
|
||||
return self.signalSummary
|
||||
case "imessage":
|
||||
@@ -401,8 +345,6 @@ extension ChannelsSettings {
|
||||
return self.telegramDetails
|
||||
case "discord":
|
||||
return self.discordDetails
|
||||
case "mattermost":
|
||||
return self.mattermostDetails
|
||||
case "signal":
|
||||
return self.signalDetails
|
||||
case "imessage":
|
||||
@@ -435,10 +377,6 @@ extension ChannelsSettings {
|
||||
return self
|
||||
.date(fromMs: self.channelStatus("discord", as: ChannelsStatusSnapshot.DiscordStatus.self)?
|
||||
.lastProbeAt)
|
||||
case "mattermost":
|
||||
guard let status = self.channelStatus("mattermost", as: ChannelsStatusSnapshot.MattermostStatus.self)
|
||||
else { return nil }
|
||||
return self.date(fromMs: status.lastProbeAt ?? status.lastConnectedAt)
|
||||
case "signal":
|
||||
return self
|
||||
.date(fromMs: self.channelStatus("signal", as: ChannelsStatusSnapshot.SignalStatus.self)?.lastProbeAt)
|
||||
@@ -473,10 +411,6 @@ extension ChannelsSettings {
|
||||
guard let status = self.channelStatus("discord", as: ChannelsStatusSnapshot.DiscordStatus.self)
|
||||
else { return false }
|
||||
return status.lastError?.isEmpty == false || status.probe?.ok == false
|
||||
case "mattermost":
|
||||
guard let status = self.channelStatus("mattermost", as: ChannelsStatusSnapshot.MattermostStatus.self)
|
||||
else { return false }
|
||||
return status.lastError?.isEmpty == false || status.probe?.ok == false
|
||||
case "signal":
|
||||
guard let status = self.channelStatus("signal", as: ChannelsStatusSnapshot.SignalStatus.self)
|
||||
else { return false }
|
||||
|
||||
@@ -85,40 +85,6 @@ struct ChannelsStatusSnapshot: Codable {
|
||||
let lastProbeAt: Double?
|
||||
}
|
||||
|
||||
struct MattermostBot: Codable {
|
||||
let id: String?
|
||||
let username: String?
|
||||
}
|
||||
|
||||
struct MattermostProbe: Codable {
|
||||
let ok: Bool
|
||||
let status: Int?
|
||||
let error: String?
|
||||
let elapsedMs: Double?
|
||||
let bot: MattermostBot?
|
||||
}
|
||||
|
||||
struct MattermostDisconnect: Codable {
|
||||
let at: Double
|
||||
let status: Int?
|
||||
let error: String?
|
||||
}
|
||||
|
||||
struct MattermostStatus: Codable {
|
||||
let configured: Bool
|
||||
let botTokenSource: String?
|
||||
let running: Bool
|
||||
let connected: Bool?
|
||||
let lastConnectedAt: Double?
|
||||
let lastDisconnect: MattermostDisconnect?
|
||||
let lastStartAt: Double?
|
||||
let lastStopAt: Double?
|
||||
let lastError: String?
|
||||
let baseUrl: String?
|
||||
let probe: MattermostProbe?
|
||||
let lastProbeAt: Double?
|
||||
}
|
||||
|
||||
struct SignalProbe: Codable {
|
||||
let ok: Bool
|
||||
let status: Int?
|
||||
|
||||
@@ -12,7 +12,6 @@ enum GatewayAgentChannel: String, Codable, CaseIterable, Sendable {
|
||||
case telegram
|
||||
case discord
|
||||
case slack
|
||||
case mattermost
|
||||
case signal
|
||||
case imessage
|
||||
case msteams
|
||||
|
||||
@@ -12,11 +12,10 @@ struct ChannelsSettingsSmokeTests {
|
||||
let store = ChannelsStore(isPreview: true)
|
||||
store.snapshot = ChannelsStatusSnapshot(
|
||||
ts: 1_700_000_000_000,
|
||||
channelOrder: ["whatsapp", "telegram", "mattermost", "signal", "imessage"],
|
||||
channelOrder: ["whatsapp", "telegram", "signal", "imessage"],
|
||||
channelLabels: [
|
||||
"whatsapp": "WhatsApp",
|
||||
"telegram": "Telegram",
|
||||
"mattermost": "Mattermost",
|
||||
"signal": "Signal",
|
||||
"imessage": "iMessage",
|
||||
],
|
||||
@@ -58,21 +57,6 @@ struct ChannelsSettingsSmokeTests {
|
||||
],
|
||||
"lastProbeAt": 1_700_000_050_000,
|
||||
]),
|
||||
"mattermost": SnapshotAnyCodable([
|
||||
"configured": true,
|
||||
"botTokenSource": "env",
|
||||
"running": true,
|
||||
"connected": true,
|
||||
"baseUrl": "https://chat.example.com",
|
||||
"lastStartAt": 1_700_000_000_000,
|
||||
"probe": [
|
||||
"ok": true,
|
||||
"status": 200,
|
||||
"elapsedMs": 95,
|
||||
"bot": ["id": "bot-123", "username": "clawdbot"],
|
||||
],
|
||||
"lastProbeAt": 1_700_000_050_000,
|
||||
]),
|
||||
"signal": SnapshotAnyCodable([
|
||||
"configured": true,
|
||||
"baseUrl": "http://127.0.0.1:8080",
|
||||
@@ -98,7 +82,6 @@ struct ChannelsSettingsSmokeTests {
|
||||
channelDefaultAccountId: [
|
||||
"whatsapp": "default",
|
||||
"telegram": "default",
|
||||
"mattermost": "default",
|
||||
"signal": "default",
|
||||
"imessage": "default",
|
||||
])
|
||||
@@ -115,11 +98,10 @@ struct ChannelsSettingsSmokeTests {
|
||||
let store = ChannelsStore(isPreview: true)
|
||||
store.snapshot = ChannelsStatusSnapshot(
|
||||
ts: 1_700_000_000_000,
|
||||
channelOrder: ["whatsapp", "telegram", "mattermost", "signal", "imessage"],
|
||||
channelOrder: ["whatsapp", "telegram", "signal", "imessage"],
|
||||
channelLabels: [
|
||||
"whatsapp": "WhatsApp",
|
||||
"telegram": "Telegram",
|
||||
"mattermost": "Mattermost",
|
||||
"signal": "Signal",
|
||||
"imessage": "iMessage",
|
||||
],
|
||||
@@ -146,19 +128,6 @@ struct ChannelsSettingsSmokeTests {
|
||||
],
|
||||
"lastProbeAt": 1_700_000_100_000,
|
||||
]),
|
||||
"mattermost": SnapshotAnyCodable([
|
||||
"configured": false,
|
||||
"running": false,
|
||||
"lastError": "bot token missing",
|
||||
"baseUrl": "https://chat.example.com",
|
||||
"probe": [
|
||||
"ok": false,
|
||||
"status": 401,
|
||||
"error": "unauthorized",
|
||||
"elapsedMs": 110,
|
||||
],
|
||||
"lastProbeAt": 1_700_000_150_000,
|
||||
]),
|
||||
"signal": SnapshotAnyCodable([
|
||||
"configured": false,
|
||||
"baseUrl": "http://127.0.0.1:8080",
|
||||
@@ -185,7 +154,6 @@ struct ChannelsSettingsSmokeTests {
|
||||
channelDefaultAccountId: [
|
||||
"whatsapp": "default",
|
||||
"telegram": "default",
|
||||
"mattermost": "default",
|
||||
"signal": "default",
|
||||
"imessage": "default",
|
||||
])
|
||||
|
||||
@@ -11,7 +11,6 @@ import Testing
|
||||
#expect(GatewayAgentChannel.last.shouldDeliver(true) == true)
|
||||
#expect(GatewayAgentChannel.whatsapp.shouldDeliver(true) == true)
|
||||
#expect(GatewayAgentChannel.telegram.shouldDeliver(true) == true)
|
||||
#expect(GatewayAgentChannel.mattermost.shouldDeliver(true) == true)
|
||||
#expect(GatewayAgentChannel.bluebubbles.shouldDeliver(true) == true)
|
||||
#expect(GatewayAgentChannel.last.shouldDeliver(false) == false)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user