Skip to content

Commit 96bbdda

Browse files
authored
Merge pull request #84 from lequocbinh04/fix-stable-ephemeral-key
Read ephemeral events from the stable transaction key
2 parents d9ce715 + 38e3014 commit 96bbdda

3 files changed

Lines changed: 61 additions & 1 deletion

File tree

changelog.d/84.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Read ephemeral events from the stable `ephemeral` transaction key, falling back to the unstable MSC2409 key. Fixes ephemeral events (typing, receipts, presence) being dropped from spec-compliant homeservers.

src/app-service.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,10 @@ export class AppService extends EventEmitter {
285285
}
286286

287287
const events = req.body.events || [];
288-
const ephemeral = req.body["de.sorunome.msc2409.ephemeral"] || [];
288+
// Ephemeral events are delivered under the stable `ephemeral` key by
289+
// spec-compliant homeservers; fall back to the unstable MSC2409 key for
290+
// older ones. See https://spec.matrix.org/v1.13/application-service-api/#put_matrixappv1transactionstxnid
291+
const ephemeral = req.body.ephemeral || req.body["de.sorunome.msc2409.ephemeral"] || [];
289292

290293
if (this.lastProcessedTxnId === txnId) {
291294
res.send({}); // duplicate

test/test_app-service.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { AppService } from "../src/app-service";
2+
import { expect } from "chai";
3+
import { Request, Response } from "express";
4+
5+
const HS_TOKEN = "hstoken";
6+
7+
function mockReq(txnId: string, body: unknown): Request {
8+
return {
9+
params: { txnId },
10+
query: { access_token: HS_TOKEN },
11+
headers: {},
12+
body,
13+
} as unknown as Request;
14+
}
15+
16+
function mockRes(): Response {
17+
return {
18+
status() { return this; },
19+
send() { return this; },
20+
} as unknown as Response;
21+
}
22+
23+
describe("AppService", () => {
24+
describe("onTransaction ephemeral events", () => {
25+
function receive(body: unknown): Record<string, unknown>[] {
26+
const appservice = new AppService({ homeserverToken: HS_TOKEN });
27+
const received: Record<string, unknown>[] = [];
28+
appservice.on("ephemeral", (ev) => received.push(ev));
29+
// onTransaction is private; exercise it directly with a valid token
30+
(appservice as unknown as { onTransaction: (req: Request, res: Response) => void })
31+
.onTransaction(mockReq("1", body), mockRes());
32+
return received;
33+
}
34+
35+
function mockReceipt() {
36+
return { type: "m.receipt", content: { "!e:example.org": { "m.read": { "@u:example.org": { ts: Date.now() } } } } };
37+
}
38+
39+
it("emits ephemeral events sent under the stable `ephemeral` key", () => {
40+
const receipt = mockReceipt();
41+
expect(receive({ ephemeral: [receipt] })).to.deep.equal([receipt]);
42+
});
43+
44+
it("still emits ephemeral events under the unstable MSC2409 key", () => {
45+
const receipt = mockReceipt();
46+
expect(receive({ "de.sorunome.msc2409.ephemeral": [receipt] })).to.deep.equal([receipt]);
47+
});
48+
49+
it("prefers the stable key when both are present", () => {
50+
const stable = { type: "m.receipt", content: { stable: true } };
51+
const unstable = { type: "m.receipt", content: { stable: false } };
52+
expect(receive({ ephemeral: [stable], "de.sorunome.msc2409.ephemeral": [unstable] }))
53+
.to.deep.equal([stable]);
54+
});
55+
});
56+
});

0 commit comments

Comments
 (0)