diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 833a42fc..40d3d9b2 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -14,7 +14,7 @@ from attr import validators as v import deltachat from .capi import ffi, lib -from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array_and_unref +from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array from .chatting import Contact, Chat, Message @@ -117,8 +117,11 @@ class Account(object): flags |= lib.DC_GCL_VERIFIED_ONLY if with_self: flags |= lib.DC_GCL_ADD_SELF - dc_array_t = lib.dc_get_contacts(self._dc_context, flags, query) - return list(iter_array_and_unref(dc_array_t, lambda x: Contact(self._dc_context, x))) + dc_array = ffi.gc( + lib.dc_get_contacts(self._dc_context, flags, query), + lib.dc_array_unref + ) + return list(iter_array(dc_array, lambda x: Contact(self._dc_context, x))) def create_chat_by_contact(self, contact): """ create or get an existing 1:1 chat object for the specified contact. @@ -144,6 +147,18 @@ class Account(object): chat_id = lib.dc_create_chat_by_msg_id(self._dc_context, msg_id) return Chat(self._dc_context, chat_id) + def create_group_chat(self, name, verified=False): + """ create a new group chat object. + + Chats are unpromoted until the first message is sent. + + :param verified: if true only verified contacts can be added. + :returns: a :class:`Chat` object. + """ + bytes_name = name.encode("utf8") + chat_id = lib.dc_create_group_chat(self._dc_context, verified, bytes_name) + return Chat(self._dc_context, chat_id) + def get_chats(self): """ return list of chats. diff --git a/python/src/deltachat/chatting.py b/python/src/deltachat/chatting.py index dd2f46b1..c2e0be20 100644 --- a/python/src/deltachat/chatting.py +++ b/python/src/deltachat/chatting.py @@ -1,6 +1,6 @@ """ chatting related objects: Contact, Chat, Message. """ -from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array_and_unref +from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array from .capi import lib, ffi from .types import cached_property, property_with_doc import attr @@ -58,10 +58,21 @@ class Chat(object): lib.dc_chat_unref ) + # ------ chat status API ------------------------------ + def is_deaddrop(self): """ return true if this chat is a deaddrop chat. """ return self.id == lib.DC_CHAT_ID_DEADDROP + def is_promoted(self): + """ return True if this chat is promoted, i.e. + the member contacts are aware of their membership, + have been sent messages. + """ + return not lib.dc_chat_is_unpromoted(self._dc_chat) + + # ------ chat messaging API ------------------------------ + def send_text_message(self, msg): """ send a text message and return the resulting Message instance. @@ -77,8 +88,11 @@ class Chat(object): :returns: list of :class:`Message` objects for this chat. """ - dc_array_t = lib.dc_get_chat_msgs(self._dc_context, self.id, 0, 0) - return list(iter_array_and_unref(dc_array_t, lambda x: Message(self._dc_context, x))) + dc_array = ffi.gc( + lib.dc_get_chat_msgs(self._dc_context, self.id, 0, 0), + lib.dc_array_unref + ) + return list(iter_array(dc_array, lambda x: Message(self._dc_context, x))) def count_fresh_messages(self): """ return number of fresh messages in this chat. @@ -94,6 +108,34 @@ class Chat(object): """ return lib.dc_marknoticed_chat(self._dc_context, self.id) + # ------ group management API ------------------------------ + + def add_contact(self, contact): + """ add a contact to this chat. + + :params: contact object. + :exception: ValueError if contact could not be added + :returns: None + """ + ret = lib.dc_add_contact_to_chat(self._dc_context, self.id, contact.id) + if ret != 1: + raise ValueError("could not add contact {!r} to chat".format(contact)) + + def get_contacts(self): + """ get all contacts for this chat. + + :params: contact object. + :exception: ValueError if contact could not be added + :returns: None + """ + dc_array = ffi.gc( + lib.dc_get_contacts(self._dc_context, 0, ffi.NULL), + lib.dc_array_unref + ) + return list(iter_array( + dc_array, lambda id: Contact(self._dc_context, id)) + ) + @attr.s class Message(object): diff --git a/python/src/deltachat/cutil.py b/python/src/deltachat/cutil.py index 5249b7a4..80c1f3ab 100644 --- a/python/src/deltachat/cutil.py +++ b/python/src/deltachat/cutil.py @@ -10,12 +10,9 @@ def as_dc_charpointer(obj): return obj -def iter_array_and_unref(dc_array_t, constructor): - try: - for i in range(0, lib.dc_array_get_cnt(dc_array_t)): - yield constructor(lib.dc_array_get_id(dc_array_t, i)) - finally: - lib.dc_array_unref(dc_array_t) +def iter_array(dc_array_t, constructor): + for i in range(0, lib.dc_array_get_cnt(dc_array_t)): + yield constructor(lib.dc_array_get_id(dc_array_t, i)) def from_dc_charpointer(obj): diff --git a/python/tests/test_account.py b/python/tests/test_account.py index 51b5af01..5cb83f85 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -60,6 +60,17 @@ class TestOfflineAccount: else: pytest.fail("could not find chat") + def test_group_chat(self, acfactory): + ac1 = acfactory.get_offline_account() + contact1 = ac1.create_contact("some1@hello.com", name="some1") + contact2 = ac1.create_contact("some2@hello.com", name="some2") + chat = ac1.create_group_chat(name="chat name") + chat.add_contact(contact1) + chat.add_contact(contact2) + assert contact1 in chat.get_contacts() + assert contact2 in chat.get_contacts() + assert not chat.is_promoted() + def test_message(self, acfactory): ac1 = acfactory.get_offline_account() contact1 = ac1.create_contact("some1@hello.com", name="some1")