Skip to content

Commit 96bc40d

Browse files
authored
Merge pull request #142 from turt2live/travis/e2ee
Add basic encryption support for simple bots
2 parents 4054706 + e07b0f4 commit 96bc40d

38 files changed

+9881
-156
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
.idea/
22
lib/
3+
examples/storage/
34

45
# Logs
56
logs

examples/encryption_bot.ts

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
import {
2+
EncryptionAlgorithm,
3+
FileMessageEventContent,
4+
LogLevel,
5+
LogService,
6+
MatrixClient, MessageEvent,
7+
RichConsoleLogger,
8+
SimpleFsStorageProvider,
9+
} from "../src";
10+
import { SqliteCryptoStorageProvider } from "../src/storage/SqliteCryptoStorageProvider";
11+
import * as fs from "fs";
12+
13+
LogService.setLogger(new RichConsoleLogger());
14+
LogService.setLevel(LogLevel.TRACE);
15+
LogService.muteModule("Metrics");
16+
LogService.trace = LogService.debug;
17+
18+
let creds = null;
19+
try {
20+
creds = require("../../examples/storage/encryption_bot.creds.json");
21+
} catch (e) {
22+
// ignore
23+
}
24+
25+
const dmTarget = creds?.['dmTarget'] ?? "@admin:localhost";
26+
const homeserverUrl = creds?.['homeserverUrl'] ?? "http://localhost:8008";
27+
const accessToken = creds?.['accessToken'] ?? 'YOUR_TOKEN';
28+
const storage = new SimpleFsStorageProvider("./examples/storage/encryption_bot.json");
29+
const crypto = new SqliteCryptoStorageProvider("./examples/storage/encryption_bot.db");
30+
const worksImage = fs.readFileSync("./examples/static/it-works.png");
31+
32+
const client = new MatrixClient(homeserverUrl, accessToken, storage, crypto);
33+
34+
(async function() {
35+
let encryptedRoomId: string;
36+
const joinedRooms = await client.getJoinedRooms();
37+
await client.crypto.prepare(joinedRooms); // init crypto because we're doing things before the client is started
38+
for (const roomId of joinedRooms) {
39+
if (await client.crypto.isRoomEncrypted(roomId)) {
40+
encryptedRoomId = roomId;
41+
break;
42+
}
43+
}
44+
if (!encryptedRoomId) {
45+
encryptedRoomId = await client.createRoom({
46+
invite: [dmTarget],
47+
is_direct: true,
48+
visibility: "private",
49+
preset: "trusted_private_chat",
50+
initial_state: [
51+
{type: "m.room.encryption", state_key: "", content: {algorithm: EncryptionAlgorithm.MegolmV1AesSha2}},
52+
{type: "m.room.guest_access", state_key: "", content: {guest_access: "can_join"}},
53+
],
54+
});
55+
}
56+
57+
client.on("room.failed_decryption", async (roomId: string, event: any, e: Error) => {
58+
LogService.error("index", `Failed to decrypt ${roomId} ${event['event_id']} because `, e);
59+
});
60+
61+
client.on("room.message", async (roomId: string, event: any) => {
62+
if (roomId !== encryptedRoomId) return;
63+
64+
const message = new MessageEvent(event);
65+
66+
if (message.sender === (await client.getUserId()) && message.messageType === "m.notice") {
67+
// yay, we decrypted our own message. Communicate that back for testing purposes.
68+
const encrypted = await client.crypto.encryptMedia(Buffer.from(worksImage));
69+
const mxc = await client.uploadContent(encrypted.buffer);
70+
await client.sendMessage(roomId, {
71+
msgtype: "m.image",
72+
body: "it-works.png",
73+
info: {
74+
// XXX: We know these details, so have hardcoded them.
75+
w: 256,
76+
h: 256,
77+
mimetype: "image/png",
78+
size: worksImage.length,
79+
},
80+
file: {
81+
url: mxc,
82+
...encrypted.file,
83+
},
84+
});
85+
return;
86+
}
87+
88+
if (message.messageType === "m.image") {
89+
const fileEvent = new MessageEvent<FileMessageEventContent>(message.raw);
90+
const decrypted = await client.crypto.decryptMedia(fileEvent.content.file);
91+
fs.writeFileSync('./examples/storage/decrypted.png', decrypted);
92+
await client.unstableApis.addReactionToEvent(roomId, fileEvent.eventId, 'Decrypted');
93+
return;
94+
}
95+
96+
if (message.messageType !== "m.text") return;
97+
if (message.textBody.startsWith("!ping")) {
98+
await client.replyNotice(roomId, event, "Pong");
99+
}
100+
});
101+
102+
LogService.info("index", "Starting bot...");
103+
await client.start();
104+
})();

examples/static/it-works.png

17.1 KB
Loading

package.json

+10-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
"test": "ts-mocha --project ./tsconfig.json test/*Test.ts test/**/*.ts",
2121
"build:examples": "tsc -p tsconfig-examples.json",
2222
"example:appservice": "yarn build:examples && node lib/examples/appservice.js",
23-
"example:login_register": "yarn build:examples && node lib/examples/login_register.js"
23+
"example:login_register": "yarn build:examples && node lib/examples/login_register.js",
24+
"example:encryption_bot": "yarn build:examples && node lib/examples/encryption_bot.js"
2425
},
2526
"main": "./lib/index.js",
2627
"typings": "./lib/index.d.ts",
@@ -47,8 +48,13 @@
4748
"scripts/*",
4849
"tsconfig.json"
4950
],
51+
"optionalDependencies": {
52+
"better-sqlite3": "^7.4.3"
53+
},
5054
"dependencies": {
55+
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.4.tgz",
5156
"@types/express": "^4.17.7",
57+
"another-json": "^0.2.0",
5258
"chalk": "^4.1.0",
5359
"express": "^4.17.1",
5460
"glob-to-regexp": "^0.4.1",
@@ -64,9 +70,11 @@
6470
"sanitize-html": "^2.3.2"
6571
},
6672
"devDependencies": {
73+
"@types/better-sqlite3": "^5.4.3",
6774
"@types/expect": "^24.3.0",
6875
"@types/mocha": "^8.0.1",
6976
"@types/node": "10",
77+
"@types/simple-mock": "^0.8.2",
7078
"@typescript-eslint/eslint-plugin": "^3.8.0",
7179
"@typescript-eslint/eslint-plugin-tslint": "^3.8.0",
7280
"@typescript-eslint/parser": "^3.8.0",
@@ -81,6 +89,6 @@
8189
"tmp": "^0.2.1",
8290
"ts-mocha": "^7.0.0",
8391
"tslint": "^6.1.3",
84-
"typescript": "^3.9.7"
92+
"typescript": "^4.3.5"
8593
}
8694
}

0 commit comments

Comments
 (0)