diff --git a/python/setup.py b/python/setup.py index 013e0900..ec921db3 100644 --- a/python/setup.py +++ b/python/setup.py @@ -9,7 +9,7 @@ def main(): version='0.1', description='Python bindings for deltachat-core using CFFI', long_description = long_description, - author='Delta Chat contributors', + author='holger krekel and contributors', setup_requires=['cffi>=1.0.0'], install_requires=['cffi>=1.0.0', 'requests', 'attr'], packages=setuptools.find_packages('src'), diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index 6da00903..d11dd71a 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -2,6 +2,7 @@ from __future__ import print_function import threading import re import requests +from array import array try: from queue import Queue except ImportError: @@ -88,7 +89,8 @@ class Contact(object): return capi.lib.dc_get_contact(self.dc_context, self.id) def __del__(self, dc_contact_unref=capi.lib.dc_contact_unref): - dc_contact_unref(self.dc_contact_t) + if self._property_cache: + dc_contact_unref(self.dc_contact_t) @property def addr(self): @@ -98,12 +100,12 @@ class Contact(object): def display_name(self): return ffi_unicode(capi.lib.dc_contact_get_display_name(self.dc_contact_t)) - @property def is_blocked(self): + """ Return True if the contact is blocked. """ return capi.lib.dc_contact_is_blocked(self.dc_contact_t) - @property def is_verified(self): + """ Return True if the contact is verified. """ return capi.lib.dc_contact_is_verified(self.dc_contact_t) @@ -117,7 +119,12 @@ class Chat(object): return capi.lib.dc_get_chat(self.dc_context, self.id) def __del__(self): - capi.lib.dc_chat_unref(self.dc_chat_t) + if self._property_cache: + capi.lib.dc_chat_unref(self.dc_chat_t) + + def is_deaddrop(self): + """ return true if this chat is a deaddrop chat. """ + return self.id == lib.DC_CHAT_ID_DEADDROP def send_text_message(self, msg): """ send a text message and return the resulting Message instance. """ @@ -131,6 +138,17 @@ class Chat(object): 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))) + def count_fresh_messages(self): + """ return number of fresh messages in this chat. """ + return lib.dc_get_fresh_msg_cnt(self.dc_context, self.id) + + def mark_noticed(self): + """ mark all messages in this chat as noticed. + + Noticed messages are no longer fresh. + """ + return lib.dc_marknoticed_chat(self.dc_context, self.id) + @attr.s class Message(object): @@ -142,7 +160,8 @@ class Message(object): return capi.lib.dc_get_msg(self.dc_context, self.id) def __del__(self, dc_msg_unref=capi.lib.dc_msg_unref): - dc_msg_unref(self.dc_msg_t) + if self._property_cache: + dc_msg_unref(self.dc_msg_t) @property def text(self): @@ -224,9 +243,31 @@ class Account(object): self.dc_context, contact_id) return Chat(self.dc_context, chat_id) + def create_chat_by_message(self, message): + """ return a Chat object for the given message. + + @param message: messsage id or message instance. + """ + msg_id = getattr(message, "id", message) + assert isinstance(msg_id, int) + chat_id = capi.lib.dc_create_chat_by_msg_id(self.dc_context, msg_id) + return Chat(self.dc_context, chat_id) + def get_message_by_id(self, msg_id): return Message(self.dc_context, msg_id) + def mark_seen_messages(self, messages): + """ mark the given set of messages as seen. + + :messages: a list of message ids or Message instances. + """ + arr = array("i") + for msg in messages: + msg = getattr(msg, "id", msg) + arr.append(msg) + msg_ids = ffi.cast("uint32_t*", ffi.from_buffer(arr)) + lib.dc_markseen_msgs(self.dc_context, msg_ids, len(messages)) + def start(self): deltachat.set_context_callback(self.dc_context, self._process_event) capi.lib.dc_configure(self.dc_context) diff --git a/python/tests/test_account.py b/python/tests/test_account.py index bb777832..4a195f2d 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -21,8 +21,8 @@ class TestOfflineAccount: assert contact1.id assert contact1.addr == "some1@hello.com" assert contact1.display_name == "some1" - assert not contact1.is_blocked - assert not contact1.is_verified + assert not contact1.is_blocked() + assert not contact1.is_verified() def test_contact_get_contacts(self, acfactory): ac1 = acfactory.get_offline_account() @@ -91,7 +91,7 @@ class TestOnlineAccount: assert ac1.get_config("mail_pw") assert ac1.is_configured() - def test_send_message(self, acfactory): + def test_send_and_receive_message(self, acfactory): ac1 = acfactory.get_live_account() ac2 = acfactory.get_live_account() c2 = ac1.create_contact(email=ac2.get_config("addr")) @@ -107,9 +107,31 @@ class TestOnlineAccount: evt_name, data1, data2 = ev assert data1 == chat.id assert data2 == msg.id + + # wait for other account to receive ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED") assert ev[2] == msg.id - msg = ac2.get_message_by_id(msg.id) - assert msg.text == "msg1" - messages = msg.chat.get_messages() - assert msg in messages, (msg, messages) + msg2 = ac2.get_message_by_id(msg.id) + assert msg2.text == "msg1" + + # check the message arrived in contact-requets/deaddrop + chat2 = msg2.chat + assert msg2 in chat2.get_messages() + assert chat2.is_deaddrop() + assert chat2.count_fresh_messages() == 0 + + # create new chat with contact and verify it's proper + chat2b = ac2.create_chat_by_message(msg2) + assert not chat2b.is_deaddrop() + assert chat2b.count_fresh_messages() == 1 + + # mark chat as noticed + chat2b.mark_noticed() + assert chat2b.count_fresh_messages() == 0 + + # mark messages as seen and check ac1 sees the MDN + ac2.mark_seen_messages([msg2]) + while 1: + ev = ac1._evlogger.get_matching("DC_EVENT_INFO") + if "Marking message" in ev[2]: + break