Skip to content
This repository has been archived by the owner on May 22, 2024. It is now read-only.

feat: ✨ trigger the bot by mentioning it #81

Draft
wants to merge 17 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion simplematrixbotlib/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems to be required to reliably load self.room.own_user_id and self.room.users which may be empty otherwise from testing. I hope there is a better way to do it than just syncing everything.

Copy link

@ghost ghost Nov 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does self.room.own_user_id follow the structure of @username:homeserver ? If so, it would not be neccesary to do anything with self.room.users to obtain it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.room is a Dict[str, MatrixUser]. MatrixUser contains display_name and disambiguated_name which we need for mention() matches

Copy link
Contributor Author

@HarHarLinks HarHarLinks Nov 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this https://matrix-nio.readthedocs.io/en/latest/nio.html#nio.rooms.MatrixRoom.user_name is good enough instead? I don't think so as it does the same: if room members haven't been synced yet, it just fails.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we need the user_id, then whoami should solve that.

Copy link

@ghost ghost Nov 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lines 57-63 of api.py

async with aiohttp.ClientSession() as session:
            async with session.get(f'{self.creds.homeserver}/_matrix/client/r0/account/whoami?access_token={self.creds.access_token}') as response:
                device_id = ast.literal_eval((await response.text()).replace(":false,", ":\"false\","))['device_id']
                user_id = ast.literal_eval((await response.text()).replace(":false,", ":\"false\","))['user_id']
            
            self.async_client.device_id, self.creds.device_id = device_id, device_id
            self.async_client.user_id, self.creds.user_id = user_id, user_id

Copy link

@ghost ghost Nov 25, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would the user id not be stored in bot.async_client.user_id ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would. the issue is more about getting the displayname though

Copy link
Contributor Author

@HarHarLinks HarHarLinks Nov 28, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think doing a full sync is actually ok if we enable storage in this PR (store is needed for #79)

https://github.com/poljar/matrix-nio/blob/a4fb83fd515568e269646d2111dc68e17cc251c6/nio/client/async_client.py#L368-L380

then only the very first time would be a "big" sync

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is acceptable.


if isinstance(resp, SyncResponse):
print(f"Connected to {self.async_client.homeserver} as {self.async_client.user_id} ({self.async_client.device_id})")
Expand Down
60 changes: 50 additions & 10 deletions simplematrixbotlib/match.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ def __init__(self, room, event, bot, prefix="") -> None:
"""
super().__init__(room, event, bot)
self._prefix = prefix
bot_user = self.room.users[self.room.own_user_id]
self._display_name = bot_user.display_name
self._disambiguated_name = bot_user.disambiguated_name
self._pill = f'<a href="https://matrix.to/#/{self.room.own_user_id}">'
self._body_without_prefix = None

def command(self, command=None):
"""
Expand All @@ -99,18 +104,37 @@ 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 []
# we cache this part
if not self._body_without_prefix:
if self._prefix == self.event.body[0:len(self._prefix)]:
self._body_without_prefix = self.event.body[len(self._prefix):]
elif self.mention():
HarHarLinks marked this conversation as resolved.
Show resolved Hide resolved
# the order is important here!
# note: we assume that body and formatted_body, if present, match as a workaraound of cleaning html
id_matched = False
for id in (self._disambiguated_name, self._display_name, self.room.own_user_id):
This conversation was marked as resolved.
Show resolved Hide resolved
if self.event.body.startswith(id) and not id_matched:
self._body_without_prefix = self.event.body[len(id):]
id_matched = True
HarHarLinks marked this conversation as resolved.
Show resolved Hide resolved

if self.event.formatted_body.startswith(self._pill) and not id_matched:
HarHarLinks marked this conversation as resolved.
Show resolved Hide resolved
name = self.event.formatted_body[len():self.event.formatted_body.index('</a>')]
self._body_without_prefix = self.event.body[len(name):]

# mentioning may include a : (colon) as inserted by Element when clicking on a user
if self._body_without_prefix.startswith(':'):
self._body_without_prefix = self._body_without_prefix[1:]

# trim leading whitespace after the mention
self._body_without_prefix = self._body_without_prefix.strip()

else:
self._body_without_prefix = self.event.body

This conversation was marked as resolved.
Show resolved Hide resolved
if command:
return body_without_prefix.split()[0] == command
return self._body_without_prefix.split()[0] == command
else:
return body_without_prefix.split()[0]
return self._body_without_prefix.split()[0]
HarHarLinks marked this conversation as resolved.
Show resolved Hide resolved

def prefix(self):
"""
Expand All @@ -123,6 +147,22 @@ def prefix(self):

return self.event.body.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 body in (self.event.formatted_body, self.event.body):
for id in [self._display_name, self._disambiguated_name, self.room.own_user_id, self._pill]:
if body.startswith(id):
return True

HarHarLinks marked this conversation as resolved.
Show resolved Hide resolved
return False

def args(self):
"""

Expand All @@ -132,7 +172,7 @@ def args(self):
Returns a list of strings that are the "words" of the message, except for the first "word", which would be the command.
"""

return self.event.body.split()[1:]
return self._body_without_prefix.split()[1:]

def contains(self, string):
"""
Expand Down