Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[NEW] Alpha Matrix Federation #23688

Merged
merged 22 commits into from
Apr 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
89ee547
POC - initial commit
alansikora Mar 30, 2022
2bc0237
POC - initial commit
alansikora Mar 30, 2022
d9f46ac
adding settings/better names
alansikora Apr 1, 2022
63e6965
a few patches to get it working
geekgonecrazy Apr 4, 2022
1cc4219
importing settings
alansikora Apr 6, 2022
ffd9fce
fixing automatic generation for yaml file
alansikora Apr 6, 2022
ea8d9a1
Some changes to the user creation flow to better support remote and
geekgonecrazy Apr 6, 2022
0eedbea
fixing config
alansikora Apr 7, 2022
2fc10e1
fix: fix wrong reference
MarcosSpessatto Apr 7, 2022
cb9801b
fix: fix prettier and lint
MarcosSpessatto Apr 7, 2022
5ee790b
better bridge setup
alansikora Apr 12, 2022
5d0a0ec
Merge branch 'develop' into feature/federation-v2
geekgonecrazy Apr 20, 2022
872f07e
clarify comment
geekgonecrazy Apr 20, 2022
4210300
revised yarn lock with the matrix-bridge dependency
geekgonecrazy Apr 21, 2022
49feb37
Fix typescript issues. Rename settings. Move to Federation section. A…
geekgonecrazy Apr 21, 2022
ee82669
Fix remaining linting errors
geekgonecrazy Apr 21, 2022
5aabad2
fix slashcommand definition for ts typescript check
geekgonecrazy Apr 21, 2022
8d07ef7
Merge branch 'develop' into feature/federation-v2
geekgonecrazy Apr 21, 2022
302e176
add migration to put settings into the Rocket.Chat Federation section
geekgonecrazy Apr 21, 2022
dbab8ab
remove migration. Its not needed
geekgonecrazy Apr 21, 2022
bad68f1
Remove unneeded Meteor.startup wrappers for settings registration
rodrigok Apr 21, 2022
109b443
Merge branch 'feature/federation-v2' of https://github.com/RocketChat…
rodrigok Apr 21, 2022
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
63 changes: 63 additions & 0 deletions apps/meteor/app/federation-v2/server/bridge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { Bridge, AppServiceRegistration } from 'matrix-appservice-bridge';

import { IMatrixEvent } from './definitions/IMatrixEvent';
import { MatrixEventType } from './definitions/MatrixEventType';
import { addToQueue } from './queue';
import { config } from './config';

/* eslint-disable @typescript-eslint/camelcase */
const registrationConfig = AppServiceRegistration.fromObject({
id: config.id,
hs_token: config.hsToken,
as_token: config.asToken,
url: config.bridgeUrl,
sender_localpart: config.bridgeLocalpart,
namespaces: {
users: [
{
exclusive: false,
// Reserve these MXID's (usernames)
regex: `.*`,
},
],
aliases: [
{
exclusive: false,
// Reserve these room aliases
regex: `.*`,
},
],
rooms: [
{
exclusive: false,
// This regex is used to define which rooms we listen to with the bridge.
// This does not reserve the rooms like the other namespaces.
regex: '.*',
},
],
},
rate_limited: false,
protocols: null,
});
/* eslint-enable @typescript-eslint/camelcase */

export const matrixBridge = new Bridge({
homeserverUrl: config.homeserverUrl,
domain: config.homeserverDomain,
registration: registrationConfig,
disableStores: true,
controller: {
onAliasQuery: (alias, matrixRoomId): void => {
console.log('onAliasQuery', alias, matrixRoomId);
},
onEvent: async (request /* , context*/): Promise<void> => {
// Get the event
const event = request.getData() as unknown as IMatrixEvent<MatrixEventType>;

addToQueue(event);
},
onLog: async (line, isError): Promise<void> => {
console.log(line, isError);
},
},
});
32 changes: 32 additions & 0 deletions apps/meteor/app/federation-v2/server/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { settings } from '../../settings/server';

type bridgeUrlString = `${string}://${string}:${string}`;
export type bridgeUrlTuple = [string, string, number];

interface IBridgeConfig {
id: string;
hsToken: string;
asToken: string;
homeserverUrl: string;
homeserverDomain: string;
bridgeUrl: bridgeUrlString;
bridgeLocalpart: string;
}

function _getConfig(): IBridgeConfig {
return {
id: settings.get('Federation_Matrix_id') as string,
hsToken: settings.get('Federation_Matrix_hs_token') as string,
asToken: settings.get('Federation_Matrix_as_token') as string,
homeserverUrl: settings.get('Federation_Matrix_homeserver_url') as string,
homeserverDomain: settings.get('Federation_Matrix_homeserver_domain') as string,
bridgeUrl: settings.get('Federation_Matrix_bridge_url') as bridgeUrlString,
bridgeLocalpart: settings.get('Federation_Matrix_bridge_localpart') as string,
} as IBridgeConfig;
}

export let config: IBridgeConfig = _getConfig();

export function getConfig() {
config = _getConfig();
}
9 changes: 9 additions & 0 deletions apps/meteor/app/federation-v2/server/data-interface/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as message from './message';
import * as room from './room';
import * as user from './user';

export const dataInterface = {
message: message.normalize,
room: room.normalize,
user: user.normalize,
};
17 changes: 17 additions & 0 deletions apps/meteor/app/federation-v2/server/data-interface/message.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { IMessage, IUser } from '@rocket.chat/core-typings';

import { dataInterface } from '.';

interface INormalizedMessage extends IMessage {
u: Required<Pick<IUser, '_id' | 'username' | 'name'>>;
}

export const normalize = async (message: IMessage): Promise<INormalizedMessage> => {
// TODO: normalize the entire payload (if needed)
const normalizedMessage: INormalizedMessage = message as INormalizedMessage;

// Normalize the user
normalizedMessage.u = (await dataInterface.user(message.u._id)) as Required<Pick<IUser, '_id' | 'username' | 'name'>>;

return normalizedMessage;
};
8 changes: 8 additions & 0 deletions apps/meteor/app/federation-v2/server/data-interface/room.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IRoom } from '@rocket.chat/core-typings';

import { Rooms } from '../../../models/server';

export const normalize = async (roomId: string): Promise<IRoom> => {
// Normalize the user
return Rooms.findOneById(roomId);
};
8 changes: 8 additions & 0 deletions apps/meteor/app/federation-v2/server/data-interface/user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { IUser } from '@rocket.chat/core-typings';

import { Users } from '../../../models/server';

export const normalize = async (userId: string): Promise<IUser> => {
// Normalize the user
return Users.findOneById(userId);
};
16 changes: 16 additions & 0 deletions apps/meteor/app/federation-v2/server/definitions/IMatrixEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { MatrixEventType } from './MatrixEventType';
import { EventContent } from './IMatrixEventContent';

export interface IMatrixEvent<T extends MatrixEventType> {
age: number;
content: EventContent[T];
invite_room_state?: IMatrixEvent<MatrixEventType>[];
event_id: string;
origin_server_ts: number;
room_id: string;
sender: string;
state_key: string;
type: T;
unsigned: { age: number };
user_id: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export enum AddMemberToRoomMembership {
JOIN = 'join',
INVITE = 'invite',
LEAVE = 'leave',
}

export interface IMatrixEventContentAddMemberToRoom {
displayname: string;
membership: AddMemberToRoomMembership;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface IMatrixEventContentCreateRoom {
creator: string;
room_version: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export enum MatrixSendMessageType {
'm.text',
}

export interface IMatrixEventContentSendMessage {
body: string;
msgtype: MatrixSendMessageType;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export enum SetRoomJoinRules {
JOIN = 'public',
INVITE = 'invite',
}

export interface IMatrixEventContentSetRoomJoinRules {
join_rule: SetRoomJoinRules;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface IMatrixEventContentSetRoomName {
name: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface IMatrixEventContentSetRoomTopic {
topic: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { MatrixEventType } from '../MatrixEventType';
import { IMatrixEventContentCreateRoom } from './IMatrixEventContentCreateRoom';
import { IMatrixEventContentAddMemberToRoom } from './IMatrixEventContentAddMemberToRoom';
import { IMatrixEventContentSendMessage } from './IMatrixEventContentSendMessage';
import { IMatrixEventContentSetRoomJoinRules } from './IMatrixEventContentSetRoomJoinRules';
import { IMatrixEventContentSetRoomName } from './IMatrixEventContentSetRoomName';
import { IMatrixEventContentSetRoomTopic } from './IMatrixEventContentSetRoomTopic';

export type EventContent = {
[MatrixEventType.CREATE_ROOM]: IMatrixEventContentCreateRoom;
[MatrixEventType.ROOM_MEMBERSHIP]: IMatrixEventContentAddMemberToRoom;
[MatrixEventType.SET_ROOM_JOIN_RULES]: IMatrixEventContentSetRoomJoinRules;
[MatrixEventType.SET_ROOM_NAME]: IMatrixEventContentSetRoomName;
[MatrixEventType.SET_ROOM_TOPIC]: IMatrixEventContentSetRoomTopic;
[MatrixEventType.SEND_MESSAGE]: IMatrixEventContentSendMessage;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export enum MatrixEventType {
CREATE_ROOM = 'm.room.create',
ROOM_MEMBERSHIP = 'm.room.member',
// SET_ROOM_POWER_LEVELS = 'm.room.power_levels',
// SET_ROOM_CANONICAL_ALIAS = 'm.room.canonical_alias',
SET_ROOM_JOIN_RULES = 'm.room.join_rules',
// SET_ROOM_HISTORY_VISIBILITY = 'm.room.history_visibility',
// SET_ROOM_GUEST_ACCESS = 'm.room.guest_access',
SET_ROOM_NAME = 'm.room.name',
SET_ROOM_TOPIC = 'm.room.topic',
SEND_MESSAGE = 'm.room.message',
}
50 changes: 50 additions & 0 deletions apps/meteor/app/federation-v2/server/eventHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { IMatrixEvent } from './definitions/IMatrixEvent';
import { MatrixEventType } from './definitions/MatrixEventType';
import { handleRoomMembership, handleCreateRoom, handleSendMessage, setRoomJoinRules, setRoomName, setRoomTopic } from './events';

export const eventHandler = async (event: IMatrixEvent<MatrixEventType>): Promise<void> => {
console.log(`Processing ${event.type}...`, JSON.stringify(event, null, 2));

switch (event.type) {
case MatrixEventType.CREATE_ROOM: {
await handleCreateRoom(event as IMatrixEvent<MatrixEventType.CREATE_ROOM>);

break;
}
case MatrixEventType.ROOM_MEMBERSHIP: {
await handleRoomMembership(event as IMatrixEvent<MatrixEventType.ROOM_MEMBERSHIP>);

break;
}
case MatrixEventType.SET_ROOM_JOIN_RULES: {
await setRoomJoinRules(event as IMatrixEvent<MatrixEventType.SET_ROOM_JOIN_RULES>);

break;
}
case MatrixEventType.SET_ROOM_NAME: {
await setRoomName(event as IMatrixEvent<MatrixEventType.SET_ROOM_NAME>);

break;
}
case MatrixEventType.SET_ROOM_TOPIC: {
await setRoomTopic(event as IMatrixEvent<MatrixEventType.SET_ROOM_TOPIC>);

break;
}
case MatrixEventType.SEND_MESSAGE: {
await handleSendMessage(event as IMatrixEvent<MatrixEventType.SEND_MESSAGE>);

break;
}
// case MatrixEventType.SET_ROOM_POWER_LEVELS:
// case MatrixEventType.SET_ROOM_CANONICAL_ALIAS:
// case MatrixEventType.SET_ROOM_HISTORY_VISIBILITY:
// case MatrixEventType.SET_ROOM_GUEST_ACCESS: {
// console.log(`Ignoring ${event.type}`);
//
// break;
// }
default:
console.log(`Could not find handler for ${event.type}`, event);
}
};
44 changes: 44 additions & 0 deletions apps/meteor/app/federation-v2/server/events/createRoom.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { MatrixBridgedRoom, MatrixBridgedUser, Users } from '../../../models/server';
import { createRoom } from '../../../lib/server';
import { IMatrixEvent } from '../definitions/IMatrixEvent';
import { MatrixEventType } from '../definitions/MatrixEventType';
import { checkBridgedRoomExists } from '../methods/checkBridgedRoomExists';
import { matrixClient } from '../matrix-client';

export const handleCreateRoom = async (event: IMatrixEvent<MatrixEventType.CREATE_ROOM>): Promise<void> => {
const { room_id: matrixRoomId, sender } = event;

return new Promise((resolve) => {
setTimeout(async () => {
// Check if the room already exists and if so, ignore
const roomExists = await checkBridgedRoomExists(matrixRoomId);

if (roomExists) {
return resolve();
}

// Find the bridged user id
const bridgedUserId = await MatrixBridgedUser.getId(sender);
let user;

// Create the user if necessary
if (!bridgedUserId) {
const { uid } = await matrixClient.user.createLocal(sender);

user = Users.findOneById(uid);
} else {
user = await Users.findOneById(bridgedUserId);
}

// Create temp room name
const roomName = `Federation-${matrixRoomId.split(':')[0].replace('!', '')}`;

// @ts-ignore TODO: typing of legacy functions
const { rid: roomId } = createRoom('c', roomName, user.username);

MatrixBridgedRoom.insert({ rid: roomId, mri: matrixRoomId });

resolve();
}, 500);
});
};
6 changes: 6 additions & 0 deletions apps/meteor/app/federation-v2/server/events/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export * from './createRoom';
export * from './roomMembership';
export * from './sendMessage';
export * from './setRoomJoinRules';
export * from './setRoomName';
export * from './setRoomTopic';
Loading