diff --git a/lib/src/event.dart b/lib/src/event.dart
index d369534..7807f88 100644
--- a/lib/src/event.dart
+++ b/lib/src/event.dart
@@ -762,4 +762,49 @@ class Event extends MatrixEvent {
}
return this;
}
+
+ /// returns if a message is a rich message
+ bool get isRichMessage =>
+ content['format'] == 'org.matrix.custom.html' &&
+ content['formatted_body'] is String;
+
+ // regexes to fetch the number of emotes, including emoji, and if the message consists of only those
+ // to match an emoji we can use the following regex:
+ // \x{00a9}|\x{00ae}|[\x{2000}-\x{3300}]|\x{d83c}[\x{d000}-\x{dfff}|\x{d83d}[\x{d000}-\x{dfff}]|\x{d83e}[\x{d000}-\x{dfff}]
+ // we need to replace \x{0000} with \u0000, the comment is left in the other format to be able to paste into regex101.com
+ // to see if there is a custom emote, we use the following regex: ]+data-mx-(?:emote|emoticon)(?==|>|\s)[^>]*>
+ // now we combind the two to have four regexes:
+ // 1. are there only emoji, or whitespace
+ // 2. are there only emoji, emotes, or whitespace
+ // 3. count number of emoji
+ // 4- count number of emoji or emotes
+ static final RegExp _onlyEmojiRegex = RegExp(
+ r'^(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]|\s)*$',
+ caseSensitive: false,
+ multiLine: true);
+ static final RegExp _onlyEmojiEmoteRegex = RegExp(
+ r'^(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]|]+data-mx-(?:emote|emoticon)(?==|>|\s)[^>]*>|\s)*$',
+ caseSensitive: false,
+ multiLine: true);
+ static final RegExp _countEmojiRegex = RegExp(
+ r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])',
+ caseSensitive: false,
+ multiLine: true);
+ static final RegExp _countEmojiEmoteRegex = RegExp(
+ r'(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]|]+data-mx-(?:emote|emoticon)(?==|>|\s)[^>]*>)',
+ caseSensitive: false,
+ multiLine: true);
+
+ /// Returns if a given event only has emotes, emojis or whitespace as content.
+ /// This is useful to determine if stand-alone emotes should be displayed bigger.
+ bool get onlyEmotes => isRichMessage
+ ? _onlyEmojiEmoteRegex.hasMatch(content['formatted_body'])
+ : _onlyEmojiRegex.hasMatch(content['body'] ?? '');
+
+ /// Gets the number of emotes in a given message. This is useful to determine if
+ /// emotes should be displayed bigger. WARNING: This does **not** test if there are
+ /// only emotes. Use `event.onlyEmotes` for that!
+ int get numberEmotes => isRichMessage
+ ? _countEmojiEmoteRegex.allMatches(content['formatted_body']).length
+ : _countEmojiRegex.allMatches(content['body'] ?? '').length;
}
diff --git a/test/event_test.dart b/test/event_test.dart
index 9649b7c..3dabdd4 100644
--- a/test/event_test.dart
+++ b/test/event_test.dart
@@ -1159,5 +1159,103 @@ void main() {
await room.client.dispose(closeDatabase: true);
});
+ test('emote detection', () async {
+ var event = Event.fromJson({
+ 'type': EventTypes.Message,
+ 'content': {
+ 'msgtype': 'm.text',
+ 'body': 'normal message',
+ },
+ 'event_id': '\$edit2',
+ 'sender': '@alice:example.org',
+ }, null);
+ expect(event.onlyEmotes, false);
+ expect(event.numberEmotes, 0);
+ event = Event.fromJson({
+ 'type': EventTypes.Message,
+ 'content': {
+ 'msgtype': 'm.text',
+ 'body': 'normal message with emoji 🦊',
+ },
+ 'event_id': '\$edit2',
+ 'sender': '@alice:example.org',
+ }, null);
+ expect(event.onlyEmotes, false);
+ expect(event.numberEmotes, 1);
+ event = Event.fromJson({
+ 'type': EventTypes.Message,
+ 'content': {
+ 'msgtype': 'm.text',
+ 'body': '🦊',
+ },
+ 'event_id': '\$edit2',
+ 'sender': '@alice:example.org',
+ }, null);
+ expect(event.onlyEmotes, true);
+ expect(event.numberEmotes, 1);
+ event = Event.fromJson({
+ 'type': EventTypes.Message,
+ 'content': {
+ 'msgtype': 'm.text',
+ 'body': '🦊🦊 🦊\n🦊🦊',
+ },
+ 'event_id': '\$edit2',
+ 'sender': '@alice:example.org',
+ }, null);
+ expect(event.onlyEmotes, true);
+ expect(event.numberEmotes, 5);
+ event = Event.fromJson({
+ 'type': EventTypes.Message,
+ 'content': {
+ 'msgtype': 'm.text',
+ 'body': 'rich message',
+ 'format': 'org.matrix.custom.html',
+ 'formatted_body': 'rich message'
+ },
+ 'event_id': '\$edit2',
+ 'sender': '@alice:example.org',
+ }, null);
+ expect(event.onlyEmotes, false);
+ expect(event.numberEmotes, 0);
+ event = Event.fromJson({
+ 'type': EventTypes.Message,
+ 'content': {
+ 'msgtype': 'm.text',
+ 'body': '🦊',
+ 'format': 'org.matrix.custom.html',
+ 'formatted_body': '🦊'
+ },
+ 'event_id': '\$edit2',
+ 'sender': '@alice:example.org',
+ }, null);
+ expect(event.onlyEmotes, true);
+ expect(event.numberEmotes, 1);
+ event = Event.fromJson({
+ 'type': EventTypes.Message,
+ 'content': {
+ 'msgtype': 'm.text',
+ 'body': ':blah:',
+ 'format': 'org.matrix.custom.html',
+ 'formatted_body': ''
+ },
+ 'event_id': '\$edit2',
+ 'sender': '@alice:example.org',
+ }, null);
+ expect(event.onlyEmotes, true);
+ expect(event.numberEmotes, 1);
+ event = Event.fromJson({
+ 'type': EventTypes.Message,
+ 'content': {
+ 'msgtype': 'm.text',
+ 'body': '🦊 :blah:',
+ 'format': 'org.matrix.custom.html',
+ 'formatted_body': '🦊 '
+ },
+ 'event_id': '\$edit2',
+ 'sender': '@alice:example.org',
+ }, null);
+ expect(event.onlyEmotes, true);
+ expect(event.numberEmotes, 2);
+ });
});
}