diff --git a/lib/src/room.dart b/lib/src/room.dart
index dd7c6825..e6e92327 100644
--- a/lib/src/room.dart
+++ b/lib/src/room.dart
@@ -710,6 +710,7 @@ class Room {
String? threadRootEventId,
String? threadLastEventId,
StringBuffer? commandStdout,
+ bool addMentions = true,
}) {
if (parseCommands) {
return client.parseAndRunCommand(
@@ -727,6 +728,41 @@ class Room {
'msgtype': msgtype,
'body': message,
};
+
+ if (addMentions) {
+ var potentialMentions = message
+ .split('@')
+ .map(
+ (text) => text.startsWith('[')
+ ? '@${text.split(']').first}]'
+ : '@${text.split(RegExp(r'\s+')).first}',
+ )
+ .toList()
+ ..removeAt(0);
+
+ final hasRoomMention = potentialMentions.remove('@room');
+
+ potentialMentions = potentialMentions
+ .map(
+ (mention) =>
+ mention.isValidMatrixId ? mention : getMention(mention),
+ )
+ .nonNulls
+ .toSet() // Deduplicate
+ .toList()
+ ..remove(client.userID); // We should never mention ourself.
+
+ // https://spec.matrix.org/v1.7/client-server-api/#mentioning-the-replied-to-user
+ if (inReplyTo != null) potentialMentions.add(inReplyTo.senderId);
+
+ if (hasRoomMention || potentialMentions.isNotEmpty) {
+ event['m.mentions'] = {
+ if (hasRoomMention) 'room': true,
+ if (potentialMentions.isNotEmpty) 'user_ids': potentialMentions,
+ };
+ }
+ }
+
if (parseMarkdown) {
final html = markdown(
event['body'],
diff --git a/test/commands_test.dart b/test/commands_test.dart
index 238c00cb..6efc34e6 100644
--- a/test/commands_test.dart
+++ b/test/commands_test.dart
@@ -243,6 +243,9 @@ void main() {
expect(sent, {
'msgtype': 'm.text',
'body': '> <@test:fakeServer.notExisting> reply\n\nreply',
+ 'm.mentions': {
+ 'user_ids': ['@test:fakeServer.notExisting'],
+ },
'format': 'org.matrix.custom.html',
'formatted_body':
'In reply to @test:fakeServer.notExisting
reply
reply',
diff --git a/test/event_test.dart b/test/event_test.dart
index a5166ce9..e3edae90 100644
--- a/test/event_test.dart
+++ b/test/event_test.dart
@@ -2960,7 +2960,7 @@ void main() async {
'm.mentions': {
'user_ids': ['@alice:matrix.org'],
'room': false,
- }
+ },
},
'event_id': '\$143273582443PhrSn:example.org',
'origin_server_ts': 1432735824653,
diff --git a/test/room_test.dart b/test/room_test.dart
index 01fe4d20..175b3459 100644
--- a/test/room_test.dart
+++ b/test/room_test.dart
@@ -1038,6 +1038,36 @@ void main() {
});
});
+ test('sendEvent with room mention', () async {
+ FakeMatrixApi.calledEndpoints.clear();
+ final resp = await room.sendTextEvent(
+ 'Hello world @room',
+ txid: 'testtxid',
+ addMentions: true,
+ );
+ expect(resp?.startsWith('\$event'), true);
+ final entry = FakeMatrixApi.calledEndpoints.entries
+ .firstWhere((p) => p.key.contains('/send/m.room.message/'));
+ final content = json.decode(entry.value.first);
+ expect(content['m.mentions'], {'room': true});
+ });
+
+ test('sendEvent with user mention', () async {
+ FakeMatrixApi.calledEndpoints.clear();
+ final resp = await room.sendTextEvent(
+ 'Hello world @[Alice Margatroid]',
+ addMentions: true,
+ txid: 'testtxid',
+ );
+ expect(resp?.startsWith('\$event'), true);
+ final entry = FakeMatrixApi.calledEndpoints.entries
+ .firstWhere((p) => p.key.contains('/send/m.room.message/'));
+ final content = json.decode(entry.value.first);
+ expect(content['m.mentions'], {
+ 'user_ids': ['@alice:matrix.org'],
+ });
+ });
+
test('send edit', () async {
FakeMatrixApi.calledEndpoints.clear();
final dynamic resp = await room.sendTextEvent(
@@ -1089,6 +1119,9 @@ void main() {
expect(content, {
'body': '> <@alice:example.org> Blah\n\nHello world',
'msgtype': 'm.text',
+ 'm.mentions': {
+ 'user_ids': ['@alice:example.org'],
+ },
'format': 'org.matrix.custom.html',
'formatted_body':
'In reply to @alice:example.org
Blah
Hello world',
@@ -1125,6 +1158,9 @@ void main() {
'body':
'> <@alice:example.org> Blah\n> beep\n\nHello world\nfox',
'msgtype': 'm.text',
+ 'm.mentions': {
+ 'user_ids': ['@alice:example.org'],
+ },
'format': 'org.matrix.custom.html',
'formatted_body':
'In reply to @alice:example.org
<b>Blah</b>
beep
Hello world
fox',
@@ -1162,6 +1198,9 @@ void main() {
expect(content, {
'body': '> <@alice:example.org> plaintext meow\n\nHello world',
'msgtype': 'm.text',
+ 'm.mentions': {
+ 'user_ids': ['@alice:example.org'],
+ },
'format': 'org.matrix.custom.html',
'formatted_body':
'In reply to @alice:example.org
meow
Hello world',
@@ -1197,6 +1236,9 @@ void main() {
expect(content, {
'body': '> <@alice:example.org> Hey @\u{200b}room\n\nHello world',
'msgtype': 'm.text',
+ 'm.mentions': {
+ 'user_ids': ['@alice:example.org'],
+ },
'format': 'org.matrix.custom.html',
'formatted_body':
'In reply to @alice:example.org
Hey @room
Hello world',
@@ -1214,6 +1256,9 @@ void main() {
'content': {
'body': '> <@alice:example.org> Hey\n\nHello world',
'msgtype': 'm.text',
+ 'm.mentions': {
+ 'user_ids': ['@alice:example.org'],
+ },
'format': 'org.matrix.custom.html',
'formatted_body':
'In reply to @alice:example.org
Hey
Hello world',
@@ -1238,6 +1283,9 @@ void main() {
expect(content, {
'body': '> <@alice:example.org> Hello world\n\nFox',
'msgtype': 'm.text',
+ 'm.mentions': {
+ 'user_ids': ['@alice:example.org'],
+ },
'format': 'org.matrix.custom.html',
'formatted_body':
'In reply to @alice:example.org
Hello world
Fox',
@@ -1296,7 +1344,7 @@ void main() {
test('sendFileEvent', () async {
final testFile = MatrixFile(bytes: Uint8List(0), name: 'file.jpeg');
final resp = await room.sendFileEvent(testFile, txid: 'testtxid');
- expect(resp.toString(), '\$event10');
+ expect(resp.toString(), '\$event12');
});
test('pushRuleState', () async {