diff --git a/doc/manual/match.md b/doc/manual/match.md
index 5a3f660..e948089 100644
--- a/doc/manual/match.md
+++ b/doc/manual/match.md
@@ -70,6 +70,17 @@ async def example(room, message):
#Respond to help command
```
+It is also possible to match by mention of the bot's username, matrix ID, etc.
+In the next example, we can use the prefix or mention the bot to show its help message.
+
+```python
+bot.listener.on_message_event
+async def help(room, message):
+ match = botlib.MessageMatch(room, message, bot, "!")
+ if match.command("help") and (match.prefix() or match.mention()):
+ #Respond to help command
+```
+
A list of methods for the Match class is shown below. [Methods from the Match class](#match-methods) can also be used with the MessageMatch class.
#### List of Methods:
@@ -78,5 +89,6 @@ A list of methods for the Match class is shown below. [Methods from the Match cl
| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MessageMatch.command()` or `MessageMatch.command(command)` | The "command" is the beginning of messages that are intended to be commands, but after the prefix; e.g. "help". Returns the command if the command argument is empty. Returns True if the command argument is equivalent to the command. |
| `MessageMatch.prefix()` | Returns True if the message begins with the prefix specified during the initialization of the instance of the MessageMatch class. Returns True if no prefix was specified during the initialization. |
+| `MessageMatch.mention()` | Returns True if the message begins with the bot's display name, disambiguated display name, matrix ID, or pill (HTML link to the bot via matrix.to) if formatted_body is present. |
| `MessageMatch.args()` | Returns a list of strings; each string is part of the message separated by a space, with the exception of the part of the message before the first space (the prefix and command). Returns an empty list if it is a single-word command. |
-| `MessageMatch.contains(string)` | Returns True if the message contains the value specified in the string argument. |
\ No newline at end of file
+| `MessageMatch.contains(string)` | Returns True if the message contains the value specified in the string argument. |
diff --git a/simplematrixbotlib/bot.py b/simplematrixbotlib/bot.py
index 969e04e..20177c9 100644
--- a/simplematrixbotlib/bot.py
+++ b/simplematrixbotlib/bot.py
@@ -39,7 +39,7 @@ async def main(self):
resp = await self.async_client.sync(timeout=65536,
- full_state=False) #Ignore prior messages
+ full_state=True) #Ignore prior messages
if isinstance(resp, SyncResponse):
print(f"Connected to {self.async_client.homeserver} as {self.async_client.user_id} ({self.async_client.device_id})")
diff --git a/simplematrixbotlib/match.py b/simplematrixbotlib/match.py
index 8d98516..bc39539 100644
--- a/simplematrixbotlib/match.py
+++ b/simplematrixbotlib/match.py
@@ -1,3 +1,5 @@
+from typing import Union, Optional
+
class Match:
"""
Class with methods to filter events
@@ -83,7 +85,27 @@ def __init__(self, room, event, bot, prefix="") -> None:
super().__init__(room, event, bot)
self._prefix = prefix
- def command(self, command=None):
+ """Forms of identification"""
+ self._own_user_id = room.own_user_id
+ self._own_nio_user = self.room.users[self._own_user_id]
+ self._own_disambiguated_name = self._own_nio_user.disambiguated_name
+ self._own_display_name = self._own_nio_user.display_name
+ self._own_display_name_colon = f"{self._own_display_name}:"
+ self._own_pill = f""
+
+ self.mention() # Set self._mention_id_length
+ self._body_without_prefix = self.event.body[len(self._prefix):]
+ self._body_without_mention = self.event.body[self._mention_id_length:]
+
+ if self.mention():
+ body = self._body_without_mention
+ elif self.prefix():
+ body = self._body_without_prefix
+ else:
+ body = self.event.body
+ self._split_body = body.split()
+
+ def command(self, command: Optional[str] = None) -> Union[bool, str]:
"""
Parameters
----------
@@ -99,18 +121,19 @@ def command(self, command=None):
Returns the string after the prefix and before the first space if no arg is passed to this method.
"""
- if self._prefix == self.event.body[0:len(self._prefix)]:
- body_without_prefix = self.event.body[len(self._prefix):]
- else:
- body_without_prefix = self.event.body
-
- if not body_without_prefix:
- return []
+ if not (self._body_without_prefix and self._body_without_mention):
+ """Body is empty after removing prefix or mention"""
+ if command is None:
+ return ""
+ elif not command:
+ return True
+ else:
+ return False
- if command:
- return body_without_prefix.split()[0] == command
+ if command is not None:
+ return self._split_body[0] == command
else:
- return body_without_prefix.split()[0]
+ return self._split_body[0]
def prefix(self):
"""
@@ -121,7 +144,24 @@ def prefix(self):
Returns True if the message begins with the prefix, and False otherwise. If there is no prefix specified during the creation of this MessageMatch object, then return True.
"""
- return self.event.body.startswith(self._prefix)
+ return self.event.body[self._mention_id_length:].startswith(self._prefix)
+
+ def mention(self):
+ """
+
+ Returns
+ -------
+ boolean
+ Returns True if the message begins with the bot's username, MXID, or pill targeting the MXID, and False otherwise.
+ """
+
+ for id in [self._own_disambiguated_name, self._own_display_name, self._own_user_id, self._own_display_name_colon]:
+ if self.event.body.startswith(id):
+ self._mention_id_length = len(id)+1
+ return True
+ self._mention_id_length = 0
+
+ return False
def args(self):
"""
@@ -129,10 +169,10 @@ def args(self):
Returns
-------
list
- Returns a list of strings that are the "words" of the message, except for the first "word", which would be the command.
+ Returns a list of strings that are the "words" of the message, except for the first "word", which would be the prefix/mention + command.
"""
- return self.event.body.split()[1:]
+ return self._split_body[1:]
def contains(self, string):
"""
diff --git a/tests/match/test_messagematch.py b/tests/match/test_messagematch.py
index 162d19f..9367dbc 100644
--- a/tests/match/test_messagematch.py
+++ b/tests/match/test_messagematch.py
@@ -2,6 +2,18 @@
from unittest import mock
mock_room = mock.MagicMock()
+mock_room.own_user_id = "@bot:matrix.org"
+mock_user = mock.MagicMock()
+mock_user.display_name = "bot"
+mock_user.disambiguated_name = f"{mock_user.display_name} ({mock_room.own_user_id})"
+mock_room.users = {mock_room.own_user_id: mock_user}
+
+mock_room2 = mock.MagicMock()
+mock_room2.own_user_id = "@bot:matrix.org"
+mock_user2 = mock.MagicMock()
+mock_user2.display_name = "bot"
+mock_user2.disambiguated_name = f"{mock_user2.display_name} ({mock_room2.own_user_id})"
+mock_room2.users = {mock_room2.own_user_id: mock_user2}
mock_event = mock.MagicMock()
mock_event.body = "p!help example"
@@ -9,15 +21,49 @@
mock_event2 = mock.MagicMock()
mock_event2.body = "p!help"
+mock_event3 = mock.MagicMock()
+mock_event3.body = "bot help"
+mock_event3.formatted_body = None
+mock_event4 = mock.MagicMock()
+mock_event4.body = "bot: help"
+mock_event4.formatted_body = None
+mock_event5 = mock.MagicMock()
+mock_event5.body = f"{mock_room2.own_user_id} help"
+mock_event5.formatted_body = None
+mock_event6 = mock.MagicMock()
+mock_event6.body = f"bot ({mock_room2.own_user_id}) help"
+mock_event6.formatted_body = None
+mock_event7 = mock.MagicMock()
+mock_event7.body = "something else"
+mock_event7.formatted_body = "bot help"
+mock_event8 = mock.MagicMock()
+mock_event8.body = "bottom help"
+mock_event8.formatted_body = None
+
+mock_event9 = mock.MagicMock()
+mock_event9.body = "p!"
+
mock_bot = mock.MagicMock()
+mock_bot.creds.username = "bot"
+mock_bot.creds.homeserver = "https://matrix.org"
prefix = "p!"
prefix2 = "!!"
-match = MessageMatch(mock_room, mock_event, mock_bot, prefix)
-match2 = MessageMatch(mock_room, mock_event, mock_bot)
-match3 = MessageMatch(mock_room, mock_event, mock_bot, prefix2)
-match4 = MessageMatch(mock_room, mock_event2, mock_bot, prefix)
+match = MessageMatch(mock_room, mock_event, mock_bot, prefix) # prefix match
+match2 = MessageMatch(mock_room, mock_event, mock_bot) # no prefix given
+match3 = MessageMatch(mock_room, mock_event, mock_bot, prefix2) # wrong prefix given
+match4 = MessageMatch(mock_room, mock_event2, mock_bot, prefix) # no arguments given
+
+match5 = MessageMatch(mock_room2, mock_event3, mock_bot, prefix) # mention with display name
+match6 = MessageMatch(mock_room2, mock_event4, mock_bot, prefix) # mention with colon
+match7 = MessageMatch(mock_room2, mock_event5, mock_bot, prefix) # mention with user id
+match8 = MessageMatch(mock_room2, mock_event6, mock_bot, prefix) # mention with disambiguated name
+match9 = MessageMatch(mock_room2, mock_event7, mock_bot, prefix) # mention with pill
+match10 = MessageMatch(mock_room2, mock_event8, mock_bot, prefix) # mention someone else
+match11 = MessageMatch(mock_room2, mock_event3, mock_bot) # mention without prefix
+
+match12 = MessageMatch(mock_room, mock_event9, mock_bot, prefix) # prefix match with empty command
def test_init():
assert issubclass(MessageMatch, Match)
@@ -30,11 +76,38 @@ def test_command():
assert match2.command() == "p!help"
assert match2.command("p!help") == True
+ assert match12.command() == ""
+ assert match12.command("") == True
+
+def test_mention():
+ assert match5.command() == "help"
+ assert match5.mention() == True
+
+ assert match6.command() == "help"
+ assert match6.mention() == True
+
+ assert match7.command() == "help"
+ assert match7.mention() == True
+
+ assert match8.command() == "help"
+ assert match8.mention() == True
+
+ assert match9.command() == "help"
+ assert match9.mention() == True
+
+ assert match10.command() == "bottom"
+ assert match10.mention() == False
+
+ assert match11.command() == "help"
+ assert match11.mention() == True
+
def test_prefix():
assert match.prefix() == True
assert match3.prefix() == False
- #assert match2.prefix() == True
+ assert match2.prefix() == True
+
+ assert match12.prefix() == True
def test_args():
assert match.args() == ["example"]