Skip to content

Commit 355b4c6

Browse files
fix(mattermost): land #30891 route private channels as group (@BlueBirdBack)
Landed from contributor PR #30891 by @BlueBirdBack. Co-authored-by: BlueBirdBack <[email protected]>
1 parent 6bea38b commit 355b4c6

File tree

3 files changed

+31
-3
lines changed

3 files changed

+31
-3
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ Docs: https://docs.openclaw.ai
112112

113113
### Fixes
114114

115+
- Mattermost/Private channel policy routing: map Mattermost private channel type `P` to group chat type so `groupPolicy`/`groupAllowFrom` gates apply correctly instead of being treated as open public channels. Landed from contributor PR #30891 by @BlueBirdBack. Thanks @BlueBirdBack.
115116
- Models/Custom provider keys: trim custom provider map keys during normalization so image-capable models remain discoverable when provider keys are configured with leading/trailing whitespace. Landed from contributor PR #31202 by @stakeswky. Thanks @stakeswky.
116117
- Discord/Agent component interactions: accept Components v2 `cid` payloads alongside legacy `componentId`, and safely decode percent-encoded IDs without throwing on malformed `%` sequences. Landed from contributor PR #29013 by @Jacky1n7. Thanks @Jacky1n7.
117118
- Matrix/Directory room IDs: preserve original room-ID casing for direct `!roomId` group lookups (without `:server`) so allowlist checks do not fail on case-sensitive IDs. Landed from contributor PR #31201 by @williamos-dev. Thanks @williamos-dev.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, expect, it } from "vitest";
2+
import { mapMattermostChannelTypeToChatType } from "./monitor.js";
3+
4+
describe("mapMattermostChannelTypeToChatType", () => {
5+
it("maps direct and group dm channel types", () => {
6+
expect(mapMattermostChannelTypeToChatType("D")).toBe("direct");
7+
expect(mapMattermostChannelTypeToChatType("g")).toBe("group");
8+
});
9+
10+
it("maps private channels to group", () => {
11+
expect(mapMattermostChannelTypeToChatType("P")).toBe("group");
12+
expect(mapMattermostChannelTypeToChatType(" p ")).toBe("group");
13+
});
14+
15+
it("keeps public channels and unknown values as channel", () => {
16+
expect(mapMattermostChannelTypeToChatType("O")).toBe("channel");
17+
expect(mapMattermostChannelTypeToChatType("x")).toBe("channel");
18+
expect(mapMattermostChannelTypeToChatType(undefined)).toBe("channel");
19+
});
20+
});

extensions/mattermost/src/mattermost/monitor.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,17 +110,24 @@ function isSystemPost(post: MattermostPost): boolean {
110110
return Boolean(type);
111111
}
112112

113-
function channelKind(channelType?: string | null): ChatType {
113+
export function mapMattermostChannelTypeToChatType(channelType?: string | null): ChatType {
114114
if (!channelType) {
115115
return "channel";
116116
}
117+
// Mattermost channel types: D=direct, G=group DM, O=public channel, P=private channel.
117118
const normalized = channelType.trim().toUpperCase();
118119
if (normalized === "D") {
119120
return "direct";
120121
}
121122
if (normalized === "G") {
122123
return "group";
123124
}
125+
if (normalized === "P") {
126+
// Private channels are invitation-restricted spaces; route as "group" so
127+
// groupPolicy / groupAllowFrom can gate access separately from open public
128+
// channels (type "O"), and the From prefix becomes mattermost:group:<id>.
129+
return "group";
130+
}
124131
return "channel";
125132
}
126133

@@ -352,7 +359,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
352359

353360
const channelInfo = await resolveChannelInfo(channelId);
354361
const channelType = payload.data?.channel_type ?? channelInfo?.type ?? undefined;
355-
const kind = channelKind(channelType);
362+
const kind = mapMattermostChannelTypeToChatType(channelType);
356363
const chatType = channelChatType(kind);
357364

358365
const senderName =
@@ -863,7 +870,7 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
863870
logVerboseMessage(`mattermost: drop reaction (cannot resolve channel type for ${channelId})`);
864871
return;
865872
}
866-
const kind = channelKind(channelInfo.type);
873+
const kind = mapMattermostChannelTypeToChatType(channelInfo.type);
867874

868875
// Enforce DM/group policy and allowlist checks (same as normal messages)
869876
const dmPolicy = account.config.dmPolicy ?? "pairing";

0 commit comments

Comments
 (0)