This commit is contained in:
Henrik Hüttemann 2023-05-17 18:42:26 +02:00
commit 0c6ca6f410
No known key found for this signature in database
GPG Key ID: 9F7BD10E0A8A111E
5 changed files with 404 additions and 0 deletions

136
.gitignore vendored Normal file
View File

@ -0,0 +1,136 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
# for this project
files/
message_example.json
rocketchat_message.json
rocketchat_room.json

27
README.md Normal file
View File

@ -0,0 +1,27 @@
# Rocket.Chat to Matrix Migration Script
Drafts and more
## Exporting RC data
Currently manually via mongodb. Run the following on the server:
```shell
mongoexport --collection=rocketchat_message --db=rocketchat --out=rocketchat_message.json
mongoexport --collection=rocketchat_room --db=rocketchat --out=rocketchat_room.json
mongoexport --collection=users --db=rocketchat --out=users.json
```
## Running the Matrix Dev Server
```shell
docker-compose up -d
# Register a admin user
docker-compose exec -it synapse register_new_matrix_user http://localhost:8008 -c /data/homeserver.yaml
```
## Design Decisions
- Getting data from Rocket.Chat via (currently) manual mongodb export
- Room to Channel conversion:
- Read-only attributes of 2 verdigado channels not converted to power levels due to complexity

50
docker-compose.yml Normal file
View File

@ -0,0 +1,50 @@
version: '3'
services:
synapse:
image: docker.io/matrixdotorg/synapse:latest
# Since synapse does not retry to connect to the database, restart upon
# failure
restart: unless-stopped
# See the readme for a full documentation of the environment settings
# NOTE: You must edit homeserver.yaml to use postgres, it defaults to sqlite
environment:
- SYNAPSE_CONFIG_PATH=/data/homeserver.yaml
volumes:
# You may either store all the files in a local folder
- ./files:/data
# .. or you may split this between different storage points
# - ./files:/data
# - /path/to/ssd:/data/uploads
# - /path/to/large_hdd:/data/media
# depends_on:
# - db
# In order to expose Synapse, remove one of the following, you might for
# instance expose the TLS port directly:
ports:
- 8008:8008/tcp
# ... or use a reverse proxy, here is an example for traefik:
# db:
# image: docker.io/postgres:12-alpine
# # Change that password, of course!
# environment:
# - POSTGRES_USER=synapse
# - POSTGRES_PASSWORD=changeme
# # ensure the database gets created correctly
# # https://matrix-org.github.io/synapse/latest/postgres.html#set-up-database
# - POSTGRES_INITDB_ARGS=--encoding=UTF-8 --lc-collate=C --lc-ctype=C
# volumes:
# # You may store the database tables in a local folder..
# - ./schemas:/var/lib/postgresql/data
# # .. or store them on some high performance storage for better results
# # - /path/to/ssd/storage:/var/lib/postgresql/data
synapse-admin:
container_name: synapse-admin
hostname: synapse-admin
image: awesometechnologies/synapse-admin:latest
ports:
- "8080:80"
restart: unless-stopped

98
draft.drawio Normal file
View File

@ -0,0 +1,98 @@
<mxfile host="65bd71144e">
<diagram id="C5RBs43oDa-KdzZeNtuy" name="Page-1">
<mxGraphModel dx="1013" dy="535" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-0"/>
<mxCell id="WIyWlLk6GJQsqaUBKTNV-1" parent="WIyWlLk6GJQsqaUBKTNV-0"/>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-1" value="Map Entity" style="swimlane;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="440" y="40" width="360" height="440" as="geometry">
<mxRectangle x="440" y="40" width="140" height="30" as="alternateBounds"/>
</mxGeometry>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-3" value="&lt;div&gt;Lookup RC entity&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;glass=0;strokeWidth=1;shadow=0;" parent="lQxHJICs_7MlIo1Uhlzd-1" vertex="1">
<mxGeometry x="40" y="100" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-6" value="&lt;div&gt;Mapping&lt;/div&gt;&lt;div&gt;exists?&lt;/div&gt;" style="rhombus;whiteSpace=wrap;html=1;shadow=0;fontFamily=Helvetica;fontSize=12;align=center;strokeWidth=1;spacing=6;spacingTop=-4;" parent="lQxHJICs_7MlIo1Uhlzd-1" vertex="1">
<mxGeometry x="50" y="170" width="100" height="80" as="geometry"/>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-2" value="" style="rounded=0;html=1;jettySize=auto;orthogonalLoop=1;fontSize=11;endArrow=block;endFill=0;endSize=8;strokeWidth=1;shadow=0;labelBackgroundColor=none;edgeStyle=orthogonalEdgeStyle;" parent="lQxHJICs_7MlIo1Uhlzd-1" source="lQxHJICs_7MlIo1Uhlzd-3" target="lQxHJICs_7MlIo1Uhlzd-6" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-13" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="lQxHJICs_7MlIo1Uhlzd-1" source="lQxHJICs_7MlIo1Uhlzd-7" target="lQxHJICs_7MlIo1Uhlzd-11" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-7" value="Create entity, write to table" style="rounded=1;whiteSpace=wrap;html=1;fontSize=12;glass=0;strokeWidth=1;shadow=0;" parent="lQxHJICs_7MlIo1Uhlzd-1" vertex="1">
<mxGeometry x="200" y="190" width="130" height="40" as="geometry"/>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-5" value="No" style="edgeStyle=orthogonalEdgeStyle;rounded=0;html=1;jettySize=auto;orthogonalLoop=1;fontSize=11;endArrow=block;endFill=0;endSize=8;strokeWidth=1;shadow=0;labelBackgroundColor=none;" parent="lQxHJICs_7MlIo1Uhlzd-1" source="lQxHJICs_7MlIo1Uhlzd-6" target="lQxHJICs_7MlIo1Uhlzd-7" edge="1">
<mxGeometry y="10" relative="1" as="geometry">
<mxPoint as="offset"/>
</mxGeometry>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="lQxHJICs_7MlIo1Uhlzd-1" source="lQxHJICs_7MlIo1Uhlzd-8" target="lQxHJICs_7MlIo1Uhlzd-11" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-8" value="Load from table" style="rounded=1;whiteSpace=wrap;html=1;" parent="lQxHJICs_7MlIo1Uhlzd-1" vertex="1">
<mxGeometry x="40" y="290" width="120" height="60" as="geometry"/>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-4" value="Yes" style="rounded=0;html=1;jettySize=auto;orthogonalLoop=1;fontSize=11;endArrow=block;endFill=0;endSize=8;strokeWidth=1;shadow=0;labelBackgroundColor=none;edgeStyle=orthogonalEdgeStyle;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="lQxHJICs_7MlIo1Uhlzd-1" source="lQxHJICs_7MlIo1Uhlzd-6" target="lQxHJICs_7MlIo1Uhlzd-8" edge="1">
<mxGeometry y="20" relative="1" as="geometry">
<mxPoint as="offset"/>
<mxPoint x="100" y="300" as="targetPoint"/>
</mxGeometry>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="lQxHJICs_7MlIo1Uhlzd-1" source="lQxHJICs_7MlIo1Uhlzd-9" target="lQxHJICs_7MlIo1Uhlzd-3" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-9" value="&lt;div&gt;RC ID&lt;/div&gt;" style="text;html=1;strokeColor=default;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="lQxHJICs_7MlIo1Uhlzd-1" vertex="1">
<mxGeometry x="70" y="50" width="60" height="30" as="geometry"/>
</mxCell>
<mxCell id="lQxHJICs_7MlIo1Uhlzd-11" value="Matrix ID" style="rounded=0;whiteSpace=wrap;html=1;" parent="lQxHJICs_7MlIo1Uhlzd-1" vertex="1">
<mxGeometry x="40" y="380" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="17" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="19" target="22" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="18" style="edgeStyle=none;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="19" target="28" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="19" value="Map Users" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="190" y="70" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="20" value="" style="edgeStyle=none;html=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="22" target="26" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="21" style="edgeStyle=none;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="22" target="23" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="22" value="Map Channels" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="190" y="150" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="23" value="Add Users to Channels" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="110" y="230" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="24" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;endArrow=classic;endFill=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="26" target="29" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="25" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;endArrow=classic;endFill=1;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="26" target="30" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="26" value="Map Messages" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="270" y="230" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="27" style="edgeStyle=none;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;endArrow=none;endFill=0;" parent="WIyWlLk6GJQsqaUBKTNV-1" source="28" target="23" edge="1">
<mxGeometry relative="1" as="geometry"/>
</mxCell>
<mxCell id="28" value="yields memberships" style="rounded=1;whiteSpace=wrap;html=1;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="40" y="110" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="29" value="Set read status for all?" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="110" y="350" width="120" height="40" as="geometry"/>
</mxCell>
<mxCell id="30" value="Map reactions" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;" parent="WIyWlLk6GJQsqaUBKTNV-1" vertex="1">
<mxGeometry x="270" y="350" width="120" height="40" as="geometry"/>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

93
functions.js Normal file
View File

@ -0,0 +1,93 @@
postToMatrix (endpoint, payload) {}
mapUserId (id) {}
mapChannelId (id) {}
mapMessageId (id) {}
generateHmac(user) {}
mapRoom (rcRoom) {
const room = {
creation_content: {
'm.federate': false
},
name: rcRoom.name,
room_alias_name: rcRoom.name,
topic: rcRoom.description,
// TODO: Invite users (Rate Limit?)
// POST /_matrix/client/v3/rooms/{roomId}/invite
// {
// "reason": "Welcome to the team!",
// "user_id": "@cheeky_monkey:matrix.org"
// }
}
switch (rcRoom.t) {
case 'd':
room.is_direct = true
break;
case 'c':
room.preset = 'public_chat'
break;
case 'p':
room.preset = 'private_chat'
break;
default:
// log; 'l' for livechat, anything else is undefined
break;
}
// POST /_matrix/client/v3/createRoom
}
mapUser (rcUser) {
return {
'nonce': '',
'username': rcUser.username,
'displayname': rcUser.name,
'password': '',
'admin': rcUser.roles.includes('admin'),
'mac': '',
}
}
getUserRegisterNonce () {} // GET /_synapse/admin/v1/register
createUser (rcUser) {
const user = mapUser(rcUser)
user.nonce = getUserRegisterNonce()
user.mac = generateHmac(user)
const mUser = postToMatrix('/_synapse/admin/v1/register', user) // POST /_synapse/admin/v1/register
// rcUser.__rooms.map(mapChannelId)
return mUser
}
mapMessage (rcMessage) {
const message = {
'content': {
'body': rc.msg,
// 'format': 'org.matrix.custom.html',
// 'formatted_body': '<b>This is an example text message</b>',
'msgtype': 'm.text',
},
'event_id': '$143273582443PhrSn:example.org', // TODO: ??
'origin_server_ts': new Date(rc.t.$date).valueOf(),
'room_id': mapChannelId(rcMessage.rid),
'sender': mapUserId(rc.u._id),
'type': 'm.room.message',
'unsigned': {
'age': 1234, // TODO: ??
},
}
// TODO: Other media types
if (rc.tmid) { // If it is a thread reply
message.content['m.relates_to'] = {
rel_type: 'm.thread',
event_id: mapMessageId(rc.tmid),
}
}
return message
}