filter system message on starting the chat #3
5 changed files with 37 additions and 55 deletions
|
|
@ -156,15 +156,6 @@ class MattermostClient:
|
||||||
|
|
||||||
return channel_id
|
return channel_id
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _is_system_post(post: dict) -> bool:
|
|
||||||
"""
|
|
||||||
Mattermost auto-generates posts like "user-x added to the channel"
|
|
||||||
(type e.g. "system_add_to_channel") whenever channel membership
|
|
||||||
changes. Real user-sent posts have an empty type.
|
|
||||||
"""
|
|
||||||
return post.get("type", "").startswith("system_")
|
|
||||||
|
|
||||||
def post_message(self, channel_id: str, message: str) -> str:
|
def post_message(self, channel_id: str, message: str) -> str:
|
||||||
"""
|
"""
|
||||||
Post a message to the given channel using the admin token.
|
Post a message to the given channel using the admin token.
|
||||||
|
|
@ -190,8 +181,8 @@ class MattermostClient:
|
||||||
) -> list[dict]:
|
) -> list[dict]:
|
||||||
"""
|
"""
|
||||||
Fetch posts from a channel and return them in chronological order.
|
Fetch posts from a channel and return them in chronological order.
|
||||||
System posts (channel membership changes, etc.) are excluded — only
|
Includes Mattermost system posts (channel membership changes, etc.)
|
||||||
real user-sent posts are returned.
|
as-is, with their raw `type` intact — callers filter if they need to.
|
||||||
|
|
||||||
`since` is a Unix timestamp in **milliseconds**. When provided,
|
`since` is a Unix timestamp in **milliseconds**. When provided,
|
||||||
`page` is ignored and all posts after that timestamp are returned.
|
`page` is ignored and all posts after that timestamp are returned.
|
||||||
|
|
@ -211,30 +202,19 @@ class MattermostClient:
|
||||||
posts_map: dict = data.get("posts", {})
|
posts_map: dict = data.get("posts", {})
|
||||||
|
|
||||||
# Mattermost returns newest-first; reverse to get chronological order.
|
# Mattermost returns newest-first; reverse to get chronological order.
|
||||||
return [
|
return [posts_map[pid] for pid in reversed(order) if pid in posts_map]
|
||||||
posts_map[pid]
|
|
||||||
for pid in reversed(order)
|
|
||||||
if pid in posts_map and not self._is_system_post(posts_map[pid])
|
|
||||||
]
|
|
||||||
|
|
||||||
def get_latest_post_id(self, channel_id: str) -> str | None:
|
def get_latest_post_id(self, channel_id: str) -> str | None:
|
||||||
"""
|
"""
|
||||||
Return the ID of the most recent real (non-system) post in the
|
Return the ID of the most recent post in the channel, or None if empty.
|
||||||
channel, or None if there are none yet.
|
|
||||||
Used to derive `has_unread` against a stored last_read_mattermost_post_id.
|
Used to derive `has_unread` against a stored last_read_mattermost_post_id.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
data = self._driver.posts.get_posts_for_channel(
|
data = self._driver.posts.get_posts_for_channel(
|
||||||
channel_id, params={"page": 0, "per_page": 20}
|
channel_id, params={"page": 0, "per_page": 1}
|
||||||
)
|
)
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
raise MattermostError(f"Fetching latest post failed: {exc}") from exc
|
raise MattermostError(f"Fetching latest post failed: {exc}") from exc
|
||||||
|
|
||||||
order: list[str] = data.get("order", [])
|
order: list[str] = data.get("order", [])
|
||||||
posts_map: dict = data.get("posts", {})
|
return order[0] if order else None
|
||||||
|
|
||||||
for pid in order: # newest-first
|
|
||||||
post = posts_map.get(pid)
|
|
||||||
if post and not self._is_system_post(post):
|
|
||||||
return pid
|
|
||||||
return None
|
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ class MessageSerializer(serializers.Serializer):
|
||||||
post_id = serializers.CharField()
|
post_id = serializers.CharField()
|
||||||
sender_id = serializers.UUIDField(allow_null=True)
|
sender_id = serializers.UUIDField(allow_null=True)
|
||||||
message_type = serializers.CharField(default="text")
|
message_type = serializers.CharField(default="text")
|
||||||
|
mattermost_message_type = serializers.CharField(default="", allow_blank=True)
|
||||||
text = serializers.CharField(allow_null=True, required=False)
|
text = serializers.CharField(allow_null=True, required=False)
|
||||||
url = serializers.CharField(allow_null=True, required=False)
|
url = serializers.CharField(allow_null=True, required=False)
|
||||||
created_at = serializers.DateTimeField(allow_null=True, required=False)
|
created_at = serializers.DateTimeField(allow_null=True, required=False)
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,7 @@ class MessageService:
|
||||||
"post_id": post_id,
|
"post_id": post_id,
|
||||||
"sender_id": sender_id,
|
"sender_id": sender_id,
|
||||||
"message_type": message_type,
|
"message_type": message_type,
|
||||||
|
"mattermost_message_type": "",
|
||||||
"text": text,
|
"text": text,
|
||||||
"url": file_url,
|
"url": file_url,
|
||||||
"created_at": None,
|
"created_at": None,
|
||||||
|
|
@ -118,6 +119,7 @@ class MessageService:
|
||||||
"post_id": post.get("id"),
|
"post_id": post.get("id"),
|
||||||
"sender_id": sender_id,
|
"sender_id": sender_id,
|
||||||
"message_type": "file" if is_url else "text",
|
"message_type": "file" if is_url else "text",
|
||||||
|
"mattermost_message_type": post.get("type", ""),
|
||||||
"text": None if is_url else msg,
|
"text": None if is_url else msg,
|
||||||
"url": msg if is_url else None,
|
"url": msg if is_url else None,
|
||||||
"created_at": created_at,
|
"created_at": created_at,
|
||||||
|
|
|
||||||
|
|
@ -21,20 +21,8 @@ def _post(post_id, msg="hi", post_type="", user_id="u1"):
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_get_posts_excludes_system_posts():
|
def test_get_posts_keeps_chronological_order():
|
||||||
# Mattermost order is newest-first.
|
# Mattermost order is newest-first.
|
||||||
posts_map = {
|
|
||||||
"p1": _post("p1", "real message"),
|
|
||||||
"sys1": _post("sys1", "user-x added to the channel", post_type="system_add_to_channel"),
|
|
||||||
}
|
|
||||||
client = _make_client({"order": ["sys1", "p1"], "posts": posts_map})
|
|
||||||
|
|
||||||
result = client.get_posts("chan-1")
|
|
||||||
|
|
||||||
assert [p["id"] for p in result] == ["p1"]
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_posts_keeps_chronological_order_with_no_system_posts():
|
|
||||||
posts_map = {
|
posts_map = {
|
||||||
"p1": _post("p1", "first"),
|
"p1": _post("p1", "first"),
|
||||||
"p2": _post("p2", "second"),
|
"p2": _post("p2", "second"),
|
||||||
|
|
@ -46,28 +34,20 @@ def test_get_posts_keeps_chronological_order_with_no_system_posts():
|
||||||
assert [p["id"] for p in result] == ["p1", "p2"]
|
assert [p["id"] for p in result] == ["p1", "p2"]
|
||||||
|
|
||||||
|
|
||||||
def test_get_latest_post_id_skips_leading_system_post():
|
def test_get_posts_includes_system_posts_with_raw_type():
|
||||||
# sys1 is newest (e.g. someone was just added), p1 is the latest real message.
|
|
||||||
posts_map = {
|
posts_map = {
|
||||||
"sys1": _post("sys1", "user-y added to the channel", post_type="system_add_to_channel"),
|
|
||||||
"p1": _post("p1", "real message"),
|
"p1": _post("p1", "real message"),
|
||||||
|
"sys1": _post("sys1", "user-x added to the channel", post_type="system_add_to_channel"),
|
||||||
}
|
}
|
||||||
client = _make_client({"order": ["sys1", "p1"], "posts": posts_map})
|
client = _make_client({"order": ["sys1", "p1"], "posts": posts_map})
|
||||||
|
|
||||||
assert client.get_latest_post_id("chan-1") == "p1"
|
result = client.get_posts("chan-1")
|
||||||
|
|
||||||
|
assert [p["id"] for p in result] == ["p1", "sys1"]
|
||||||
|
assert result[1]["type"] == "system_add_to_channel"
|
||||||
|
|
||||||
|
|
||||||
def test_get_latest_post_id_returns_none_when_only_system_posts_exist():
|
def test_get_latest_post_id_returns_newest_post_id():
|
||||||
posts_map = {
|
|
||||||
"sys1": _post("sys1", "user-x added to the channel", post_type="system_add_to_channel"),
|
|
||||||
"sys2": _post("sys2", "user-y added to the channel", post_type="system_add_to_channel"),
|
|
||||||
}
|
|
||||||
client = _make_client({"order": ["sys2", "sys1"], "posts": posts_map})
|
|
||||||
|
|
||||||
assert client.get_latest_post_id("chan-1") is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_get_latest_post_id_returns_newest_real_post_when_no_system_posts():
|
|
||||||
posts_map = {
|
posts_map = {
|
||||||
"p1": _post("p1", "first"),
|
"p1": _post("p1", "first"),
|
||||||
"p2": _post("p2", "second"),
|
"p2": _post("p2", "second"),
|
||||||
|
|
@ -75,3 +55,9 @@ def test_get_latest_post_id_returns_newest_real_post_when_no_system_posts():
|
||||||
client = _make_client({"order": ["p2", "p1"], "posts": posts_map})
|
client = _make_client({"order": ["p2", "p1"], "posts": posts_map})
|
||||||
|
|
||||||
assert client.get_latest_post_id("chan-1") == "p2"
|
assert client.get_latest_post_id("chan-1") == "p2"
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_latest_post_id_returns_none_when_empty():
|
||||||
|
client = _make_client({"order": [], "posts": {}})
|
||||||
|
|
||||||
|
assert client.get_latest_post_id("chan-1") is None
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,13 @@ def test_list_messages_normalized():
|
||||||
"message": "http://minio.local/chat/file.pdf",
|
"message": "http://minio.local/chat/file.pdf",
|
||||||
"create_at": 1700000001000,
|
"create_at": 1700000001000,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "sys1",
|
||||||
|
"type": "system_add_to_channel",
|
||||||
|
"user_id": mm_user_id,
|
||||||
|
"message": "user-x added to the channel",
|
||||||
|
"create_at": 1700000002000,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
mm_client = Mock()
|
mm_client = Mock()
|
||||||
|
|
@ -99,12 +106,13 @@ def test_list_messages_normalized():
|
||||||
|
|
||||||
messages = svc.list_messages(conv.id, page=0, per_page=20)
|
messages = svc.list_messages(conv.id, page=0, per_page=20)
|
||||||
|
|
||||||
assert len(messages) == 2
|
assert len(messages) == 3
|
||||||
|
|
||||||
text_msg = messages[0]
|
text_msg = messages[0]
|
||||||
assert text_msg["post_id"] == "p1"
|
assert text_msg["post_id"] == "p1"
|
||||||
assert text_msg["sender_id"] == sender_uuid
|
assert text_msg["sender_id"] == sender_uuid
|
||||||
assert text_msg["message_type"] == "text"
|
assert text_msg["message_type"] == "text"
|
||||||
|
assert text_msg["mattermost_message_type"] == ""
|
||||||
assert text_msg["text"] == "First message"
|
assert text_msg["text"] == "First message"
|
||||||
assert text_msg["url"] is None
|
assert text_msg["url"] is None
|
||||||
|
|
||||||
|
|
@ -112,5 +120,10 @@ def test_list_messages_normalized():
|
||||||
assert file_msg["post_id"] == "p2"
|
assert file_msg["post_id"] == "p2"
|
||||||
assert file_msg["sender_id"] == sender_uuid
|
assert file_msg["sender_id"] == sender_uuid
|
||||||
assert file_msg["message_type"] == "file"
|
assert file_msg["message_type"] == "file"
|
||||||
|
assert file_msg["mattermost_message_type"] == ""
|
||||||
assert file_msg["url"] == "http://minio.local/chat/file.pdf"
|
assert file_msg["url"] == "http://minio.local/chat/file.pdf"
|
||||||
assert file_msg["text"] is None
|
assert file_msg["text"] is None
|
||||||
|
|
||||||
|
system_msg = messages[2]
|
||||||
|
assert system_msg["post_id"] == "sys1"
|
||||||
|
assert system_msg["mattermost_message_type"] == "system_add_to_channel"
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue