|
@@ -249,6 +249,8 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
|
|
|
|
|
# Member directory cache: user_id -> user info dict
|
|
# Member directory cache: user_id -> user info dict
|
|
|
self._user_cache: Dict[str, User] = {}
|
|
self._user_cache: Dict[str, User] = {}
|
|
|
|
|
+ # Handle -> does a user hold it. Cached both ways; see _mentions_someone_else.
|
|
|
|
|
+ self._known_handles: Dict[str, bool] = {}
|
|
|
|
|
|
|
|
# Chattolib client cache and lock for async access.
|
|
# Chattolib client cache and lock for async access.
|
|
|
self._chatto_client: Optional[ChattoClient] = None
|
|
self._chatto_client: Optional[ChattoClient] = None
|
|
@@ -502,18 +504,44 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
return True
|
|
return True
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
- def _mentions_someone_else(self, body: str) -> bool:
|
|
|
|
|
|
|
+ async def _handle_belongs_to_a_user(self, handle: str) -> bool:
|
|
|
|
|
+ """Whether ``handle`` is the login of a real Chatto user.
|
|
|
|
|
+
|
|
|
|
|
+ The API carries no mention entities — ``mention_confirmation_token`` is
|
|
|
|
|
+ reserved in the message descriptor — so an @-token is only a candidate
|
|
|
|
|
+ until the directory confirms it. Results are cached both ways, since
|
|
|
|
|
+ the same handles recur and a miss is as reusable as a hit.
|
|
|
|
|
+ """
|
|
|
|
|
+ known = self._known_handles.get(handle)
|
|
|
|
|
+ if known is not None:
|
|
|
|
|
+ return known
|
|
|
|
|
+ try:
|
|
|
|
|
+ client = await self._require_client()
|
|
|
|
|
+ member = await client.get_user(login=handle)
|
|
|
|
|
+ except Exception as exc:
|
|
|
|
|
+ # Unresolved means "not confirmed", so the message goes through.
|
|
|
|
|
+ logger.debug("Chatto: could not resolve handle @%s: %s", handle, exc)
|
|
|
|
|
+ return False
|
|
|
|
|
+ exists = member is not None and member.user is not None
|
|
|
|
|
+ self._known_handles[handle] = exists
|
|
|
|
|
+ return exists
|
|
|
|
|
+
|
|
|
|
|
+ async def _mentions_someone_else(self, body: str) -> bool:
|
|
|
"""Whether the message @-mentions a person who is not this bot.
|
|
"""Whether the message @-mentions a person who is not this bot.
|
|
|
|
|
|
|
|
Broadcast handles are not a person — they address everyone present,
|
|
Broadcast handles are not a person — they address everyone present,
|
|
|
- the bot included, so they do not count as someone else.
|
|
|
|
|
|
|
+ the bot included, so they do not count as someone else. A handle no
|
|
|
|
|
+ user holds is not a mention at all: someone writing *about* mentioning
|
|
|
|
|
+ ("per @-mention", "@nonexistent") is talking to us, and staying silent
|
|
|
|
|
+ on a false positive is worse than answering one.
|
|
|
"""
|
|
"""
|
|
|
for handle in ChattoConstants.MENTION_RE.findall(body):
|
|
for handle in ChattoConstants.MENTION_RE.findall(body):
|
|
|
if handle.lower() in ChattoConstants.BROADCAST_MENTIONS:
|
|
if handle.lower() in ChattoConstants.BROADCAST_MENTIONS:
|
|
|
continue
|
|
continue
|
|
|
if self.me and handle in (self.me.login, self.me.display_name):
|
|
if self.me and handle in (self.me.login, self.me.display_name):
|
|
|
continue
|
|
continue
|
|
|
- return True
|
|
|
|
|
|
|
+ if await self._handle_belongs_to_a_user(handle):
|
|
|
|
|
+ return True
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
def _check_auth(self, user: User) -> bool:
|
|
def _check_auth(self, user: User) -> bool:
|
|
@@ -706,7 +734,7 @@ class ChattoAdapter(BasePlatformAdapter):
|
|
|
room_kind == RoomKind.CHANNEL
|
|
room_kind == RoomKind.CHANNEL
|
|
|
and not self.chatto_config.require_mention.value
|
|
and not self.chatto_config.require_mention.value
|
|
|
and not self._mentions_me(message_body)
|
|
and not self._mentions_me(message_body)
|
|
|
- and self._mentions_someone_else(message_body)
|
|
|
|
|
|
|
+ and await self._mentions_someone_else(message_body)
|
|
|
):
|
|
):
|
|
|
logger.info("Chatto: message addresses someone else, acknowledging only")
|
|
logger.info("Chatto: message addresses someone else, acknowledging only")
|
|
|
if self.chatto_config.reactions.value:
|
|
if self.chatto_config.reactions.value:
|