This is a bridge between Matrix and Mattermost using the Application Services API. This bridge creates a Matrix user for each Mattermost user, and a Mattermost user for each Matrix user. It uses an actual user instead of a bot to provide a superior user experience (e.g. consecutive bot messages aren't grouped).
This is currently in beta, but is sufficiently usable that it is used in a production system (with understanding users) by the author.
-
Mattermost 5.26.0 and above.
- This uses the
POST /users/{user_id}/email/verify/member
endpoint to verify the emails of puppet users.
- This uses the
-
Node 10.16.0
- The dependency
matrix-js-sdk
usesEventEmitter.once
, which was introduced in 10.16.0.
- The dependency
-
Matrix
- A matrix server supporting the Application Services API is needed. No attempt has been made to track the minimum supported API version, but it should work with any reasonably modern server. It is assumed to be synapse in this document.
-
Clone this repository to a directory.
git clone https://github.com/dalcde/matrix-appservice-mattermost
-
Install dependencies and build
npm ci npm run build
-
Copy
config.sample.yaml
toconfig.yaml
and edit accordingly -
Generate registration file
node build/index.js -c config.yaml -f registration.yaml -r
You should regenerate the registration file every time you update the bridge or change your configuration file.
-
Add the path to the registration file to the
app_service_config_files
variable in the synapse configuration file. Then restart synapse. -
Start the bridge by
node build/index.js -c config.yaml -f registration.yaml
After building, only the contents of build/
are needed for the bridge to run.
Of course, the dependencies are also needed. ONe can use system dependencies,
or one can vendor the dependencies by moving the node_modules
folder into
build/
after npm ci --proudction
. All runtime dependencies are portable and
do not require platform-specific building.
The bridge attempts to notify systemd
when it has initialized.
This ensures systemctl start matrix-appservice-mattermost
will not return
until the bridge is initialized. To configure this, add the following lines to
the Service
section of the systemd service file:
Type=notify
NotifyAccess=all
The second line is necessary since we spawn systemd-notify
to perform the
notification; node doesn't natively support this.
Note: The bridge is considered initialized when all mattermost and matrix messages in the bridged channels from that point on will be received by the bridge (barring, e.g. connection issues). Specifically, the bridge is considered initialized after the mattermost websocket is connected and the bridge has joined all channels to be bridged.
-
Mattermost -> Matrix:
- Markdown -> HTML
- Join/leave
- Attachments
- Username Substitutions
- /me
- Edits
- Replies
- Redaction
- Room substitutions (#9)
- PMs (#1)
- Presence (#8)
- Avatars (#17)
- Reactions (#13)
- Attachment thumbnails (#10)
- Correctly indicate remover when removing from channel (#7)
- Typing notification
-
Matrix -> Mattermost:
- HTML -> Markdown
- Join/leave
- Attachments
- Username Substitutions
- /me
- Edits
- Replies
- Redaction
- Room substitutions (#9)
- PMs (#1)
- Presence (#8)
- Avatars (#17)
- Reactions (#13)
- Correctly indicate remover when removing from channel (#7)
- Customize bridged username (#12)
- Typing notification (#11)
M_UNKNOWN_TOKEN: Invalid macaroon passed
: Service not registered with matrix. Make sure you followed the last three steps of the set up instructions carefully. In particular, remember to restart synapse.
There is an admin endpoint that lets users interact with the bridge. This is accessed in the same way as the appservice.
GET /bridge/status
The possible replies are
initializing
- The bridge is initializingrunning
- The bridge is running
Of course, if the request is made too early in the initialization stage, there would be no response at all.
POST /bridge/rename/:oldName/:newName?access_token=<hs_token>
This renames the mattermost puppet with username :oldName
to :newName
. The
hs_token
is the token specified in the registration file.
By design, every user in a team must join the Town Square room. If a matrix user joins a matrix room bridged to a mattermost channel, the puppet user would automatically join Town Square of the corresponding team.
When the user leaves all channels of a team (i.e. all matrix rooms bridged to such channels), the puppet user would leave the team, hence leave Town Square.
Mattermost and matrix "group" posts in different ways. For example, when deleting the root post of a thread in Mattermost, the entire thread is deleted. Similarly, attachments in Mattermost are part of a text message, whereas in Matrix they are separate events.
Our implementation is based on the following two principles:
-
From the point of view of a single platform, the presence of a bridge should not affect what happens when a post is deleted.
-
When a post is deleted on a platform, the contents must not be visible on the other platform.
In practice, this means if we delete a message on Matrix, there might be more posts deleted on Mattermost. These Mattermost deletions will not be reflected on the matrix side, so there will be more messages on Matrix than on Mattermost.
Also, Mattermost does not remember who performed the deletion. Thus, on the matrix side, it is always displayed as the bot user deleting the message.