CI / Build & Quality Checks (push) Skipped
CI / Secret scan (gitleaks) (push) Skipped
CI / Playwright smoke (e2e) (push) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 10s
CI / Build & Quality Checks (pull_request) Successful in 2m44s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Playwright smoke (e2e) (pull_request) Successful in 17m26s
People who come in from Discord through mautrix-discord now carry a Discord icon next to their name in the timeline, member list and profile, and a room bridged to a Discord channel shows a "Discord" chip in its header that names the channel and server and opens it in Discord. Ghosts are recognised by mautrix-discord's @discord_<snowflake> user IDs; rooms by their m.bridge / uk.half-shot.bridge state (protocol discordgo). Only http(s) channel links are made clickable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
87 lines
3.2 KiB
TypeScript
87 lines
3.2 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { MatrixEvent, Room } from 'matrix-js-sdk';
|
|
import {
|
|
describeRoomBridge,
|
|
getBridgedUserNetwork,
|
|
getRoomBridges,
|
|
parseBridgeEvent,
|
|
} from './bridge';
|
|
|
|
const bridgeEvent = (type: string, stateKey: string, content: Record<string, unknown>) =>
|
|
new MatrixEvent({ type, state_key: stateKey, content, room_id: '!r:x', sender: '@discordbot:x' });
|
|
|
|
// Shape of what mautrix-discord 0.7.7 sends for a guild channel.
|
|
const discordContent = {
|
|
bridgebot: '@discordbot:matrix.lotusguild.org',
|
|
protocol: { id: 'discordgo', displayname: 'Discord' },
|
|
channel: {
|
|
id: '1565159372122882070',
|
|
displayname: '#general',
|
|
external_url: 'https://discord.com/channels/1/1565159372122882070',
|
|
},
|
|
network: { id: '1', displayname: 'Lotus Guild' },
|
|
};
|
|
|
|
const roomWithState = (events: MatrixEvent[]): Room =>
|
|
({
|
|
getLiveTimeline: () => ({
|
|
getState: () => ({
|
|
getStateEvents: (type: string) => events.filter((e) => e.getType() === type),
|
|
}),
|
|
}),
|
|
}) as unknown as Room;
|
|
|
|
test('Discord ghosts are recognised; the bridge bot and lookalikes are not', () => {
|
|
assert.equal(
|
|
getBridgedUserNetwork('@discord_123456789012345678:matrix.lotusguild.org'),
|
|
'discord',
|
|
);
|
|
assert.equal(getBridgedUserNetwork('@discordbot:matrix.lotusguild.org'), undefined);
|
|
assert.equal(getBridgedUserNetwork('@discord_fan:matrix.lotusguild.org'), undefined);
|
|
assert.equal(getBridgedUserNetwork('@discord-relay:matrix.lotusguild.org'), undefined);
|
|
assert.equal(getBridgedUserNetwork('@jared:matrix.lotusguild.org'), undefined);
|
|
});
|
|
|
|
test('parseBridgeEvent reads a mautrix-discord m.bridge event', () => {
|
|
assert.deepEqual(parseBridgeEvent(bridgeEvent('m.bridge', 'k', discordContent)), {
|
|
network: 'discord',
|
|
channelName: '#general',
|
|
networkName: 'Lotus Guild',
|
|
externalUrl: 'https://discord.com/channels/1/1565159372122882070',
|
|
});
|
|
});
|
|
|
|
test('parseBridgeEvent ignores unknown protocols and empty content', () => {
|
|
assert.equal(
|
|
parseBridgeEvent(bridgeEvent('m.bridge', 'k', { protocol: { id: 'telegram' } })),
|
|
undefined,
|
|
);
|
|
assert.equal(parseBridgeEvent(bridgeEvent('m.bridge', 'k', {})), undefined);
|
|
});
|
|
|
|
test('only http(s) channel links are kept', () => {
|
|
const content = {
|
|
...discordContent,
|
|
channel: { displayname: '#x', external_url: ['javascript', 'alert(1)'].join(':') },
|
|
};
|
|
assert.equal(parseBridgeEvent(bridgeEvent('m.bridge', 'k', content))?.externalUrl, undefined);
|
|
});
|
|
|
|
test('getRoomBridges merges m.bridge with its unstable copy, one entry per channel', () => {
|
|
const room = roomWithState([
|
|
bridgeEvent('m.bridge', 'fi.mau.discord://discord/1/2', discordContent),
|
|
bridgeEvent('uk.half-shot.bridge', 'fi.mau.discord://discord/1/2', discordContent),
|
|
]);
|
|
assert.equal(getRoomBridges(room).length, 1);
|
|
assert.equal(getRoomBridges(roomWithState([])).length, 0);
|
|
});
|
|
|
|
test('describeRoomBridge degrades gracefully without names', () => {
|
|
assert.equal(
|
|
describeRoomBridge({ network: 'discord', channelName: '#general', networkName: 'Lotus Guild' }),
|
|
'Bridged to #general in Lotus Guild on Discord',
|
|
);
|
|
assert.equal(describeRoomBridge({ network: 'discord' }), 'Bridged to Discord');
|
|
});
|