diff --git a/fbchat/_graphql.py b/fbchat/_graphql.py index 54e35a4..76ec99e 100644 --- a/fbchat/_graphql.py +++ b/fbchat/_graphql.py @@ -106,6 +106,7 @@ QueryFragment Group: MessageThread { all_participants { nodes { messaging_actor { + __typename, id } } diff --git a/fbchat/_group.py b/fbchat/_group.py index d548b89..419e16d 100644 --- a/fbchat/_group.py +++ b/fbchat/_group.py @@ -182,11 +182,8 @@ class GroupData(Group): return cls( session=session, id=data["thread_key"]["thread_fbid"], - participants=set( - [ - node["messaging_actor"]["id"] - for node in data["all_participants"]["nodes"] - ] + participants=list( + cls._parse_participants(session, data["all_participants"]) ), nicknames=c_info.get("nicknames"), color=c_info["color"], diff --git a/fbchat/_thread.py b/fbchat/_thread.py index 9b9698a..217b620 100644 --- a/fbchat/_thread.py +++ b/fbchat/_thread.py @@ -3,7 +3,7 @@ import attr import collections import datetime import enum -from ._core import attrs_default, Image +from ._core import log, attrs_default, Image from . import _util, _exception, _session, _graphql, _attachment, _file, _plan from typing import MutableMapping, Mapping, Any, Iterable, Tuple, Optional @@ -605,6 +605,27 @@ class ThreadABC(metaclass=abc.ABCMeta): rtn["own_nickname"] = pc[1].get("nickname") return rtn + @staticmethod + def _parse_participants(session, data) -> Iterable["ThreadABC"]: + from . import _user, _group, _page + + for node in data["nodes"]: + actor = node["messaging_actor"] + typename = actor["__typename"] + thread_id = actor["id"] + if typename == "User": + yield _user.User(session=session, id=thread_id) + elif typename == "MessageThread": + # MessageThread => Group thread + yield _group.Group(session=session, id=thread_id) + elif typename == "Page": + yield _page.Page(session=session, id=thread_id) + elif typename == "Group": + # We don't handle Facebook "Groups" + pass + else: + log.warning("Unknown type %r in %s", typename, data) + @attrs_default class Thread(ThreadABC): diff --git a/tests/test_group.py b/tests/test_group.py index ebdfed9..4e25a1c 100644 --- a/tests/test_group.py +++ b/tests/test_group.py @@ -1,4 +1,4 @@ -from fbchat._group import GroupData +from fbchat import GroupData, User def test_group_from_graphql(session): @@ -9,9 +9,9 @@ def test_group_from_graphql(session): "is_group_thread": True, "all_participants": { "nodes": [ - {"messaging_actor": {"id": "1234"}}, - {"messaging_actor": {"id": "2345"}}, - {"messaging_actor": {"id": "3456"}}, + {"messaging_actor": {"__typename": "User", "id": "1234"}}, + {"messaging_actor": {"__typename": "User", "id": "2345"}}, + {"messaging_actor": {"__typename": "User", "id": "3456"}}, ] }, "customization_info": { @@ -33,7 +33,11 @@ def test_group_from_graphql(session): last_active=None, message_count=None, plan=None, - participants={"1234", "2345", "3456"}, + participants=[ + User(session=session, id="1234"), + User(session=session, id="2345"), + User(session=session, id="3456"), + ], nicknames={}, color="#0084ff", emoji="😀", diff --git a/tests/test_thread.py b/tests/test_thread.py index 344b42e..af045ca 100644 --- a/tests/test_thread.py +++ b/tests/test_thread.py @@ -1,6 +1,6 @@ import pytest import fbchat -from fbchat import ThreadABC, Thread +from fbchat import ThreadABC, Thread, User, Group, Page def test_parse_color(): @@ -58,6 +58,22 @@ def test_thread_parse_customization_info_user(): assert expected == ThreadABC._parse_customization_info(data) +def test_thread_parse_participants(session): + nodes = [ + {"messaging_actor": {"__typename": "User", "id": "1234"}}, + {"messaging_actor": {"__typename": "User", "id": "2345"}}, + {"messaging_actor": {"__typename": "Page", "id": "3456"}}, + {"messaging_actor": {"__typename": "MessageThread", "id": "4567"}}, + {"messaging_actor": {"__typename": "UnavailableMessagingActor", "id": "5678"}}, + ] + assert [ + User(session=session, id="1234"), + User(session=session, id="2345"), + Page(session=session, id="3456"), + Group(session=session, id="4567"), + ] == list(ThreadABC._parse_participants(session, {"nodes": nodes})) + + def test_thread_create_and_implements_thread_abc(session): thread = Thread(session=session, id="123") assert thread._parse_customization_info