mirror of
https://github.com/deltachat/deltachat-core.git
synced 2025-10-04 02:09:17 +02:00
add chat.delete(), chat.send_image, msg.filename, msg.filemime and msg.type.is_*
This commit is contained in:
parent
60240ce507
commit
b6355176de
5 changed files with 181 additions and 3 deletions
|
@ -11,6 +11,7 @@ high level API reference
|
||||||
- :class:`deltachat.chatting.Contact`
|
- :class:`deltachat.chatting.Contact`
|
||||||
- :class:`deltachat.chatting.Chat`
|
- :class:`deltachat.chatting.Chat`
|
||||||
- :class:`deltachat.chatting.Message`
|
- :class:`deltachat.chatting.Message`
|
||||||
|
- :class:`deltachat.chatting.MessageType`
|
||||||
- :class:`deltachat.chatting.MessageState`
|
- :class:`deltachat.chatting.MessageState`
|
||||||
|
|
||||||
Account
|
Account
|
||||||
|
@ -38,6 +39,11 @@ Message
|
||||||
.. autoclass:: deltachat.chatting.Message
|
.. autoclass:: deltachat.chatting.Message
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
MessageType
|
||||||
|
------------
|
||||||
|
|
||||||
|
.. autoclass:: deltachat.chatting.MessageType
|
||||||
|
:members:
|
||||||
|
|
||||||
MessageState
|
MessageState
|
||||||
------------
|
------------
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
""" chatting related objects: Contact, Chat, Message. """
|
""" chatting related objects: Contact, Chat, Message. """
|
||||||
|
|
||||||
|
import os
|
||||||
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
|
from .cutil import as_dc_charpointer, from_dc_charpointer, iter_array
|
||||||
from .capi import lib, ffi
|
from .capi import lib, ffi
|
||||||
from .types import property_with_doc
|
from .types import property_with_doc
|
||||||
|
@ -60,6 +61,16 @@ class Chat(object):
|
||||||
lib.dc_chat_unref
|
lib.dc_chat_unref
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
"""Delete this chat and all its messages.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
|
||||||
|
- does not delete messages on server
|
||||||
|
- the chat or contact is not blocked, new message will arrive
|
||||||
|
"""
|
||||||
|
lib.dc_delete_chat(self._dc_context, self.id)
|
||||||
|
|
||||||
# ------ chat status/metadata API ------------------------------
|
# ------ chat status/metadata API ------------------------------
|
||||||
|
|
||||||
def is_deaddrop(self):
|
def is_deaddrop(self):
|
||||||
|
@ -100,10 +111,28 @@ class Chat(object):
|
||||||
""" send a text message and return the resulting Message instance.
|
""" send a text message and return the resulting Message instance.
|
||||||
|
|
||||||
:param msg: unicode text
|
:param msg: unicode text
|
||||||
|
:raises: ValueError if message can not be send/chat does not exist.
|
||||||
:returns: the resulting :class:`deltachat.chatting.Message` instance
|
:returns: the resulting :class:`deltachat.chatting.Message` instance
|
||||||
"""
|
"""
|
||||||
msg = as_dc_charpointer(msg)
|
msg = as_dc_charpointer(msg)
|
||||||
msg_id = lib.dc_send_text_msg(self._dc_context, self.id, msg)
|
msg_id = lib.dc_send_text_msg(self._dc_context, self.id, msg)
|
||||||
|
if msg_id == 0:
|
||||||
|
raise ValueError("message could not be send, does chat exist?")
|
||||||
|
return Message(self._dc_context, msg_id)
|
||||||
|
|
||||||
|
def send_image(self, path):
|
||||||
|
""" send an image message and return the resulting Message instance.
|
||||||
|
|
||||||
|
:param path: path to an image file.
|
||||||
|
:raises: ValueError if message can not be send/chat does not exist.
|
||||||
|
:returns: the resulting :class:`deltachat.chatting.Message` instance
|
||||||
|
"""
|
||||||
|
if not os.path.exists(path):
|
||||||
|
raise ValueError("path does not exist: {!r}".format(path))
|
||||||
|
path = as_dc_charpointer(path)
|
||||||
|
msg_id = lib.dc_send_image_msg(self._dc_context, self.id, path, ffi.NULL, 0, 0)
|
||||||
|
if msg_id == 0:
|
||||||
|
raise ValueError("chat does not exist")
|
||||||
return Message(self._dc_context, msg_id)
|
return Message(self._dc_context, msg_id)
|
||||||
|
|
||||||
def get_messages(self):
|
def get_messages(self):
|
||||||
|
@ -186,9 +215,27 @@ class Message(object):
|
||||||
|
|
||||||
@property_with_doc
|
@property_with_doc
|
||||||
def text(self):
|
def text(self):
|
||||||
"""unicode text of this messages. """
|
"""unicode text of this messages (might be empty if not a text message). """
|
||||||
return from_dc_charpointer(lib.dc_msg_get_text(self._dc_msg))
|
return from_dc_charpointer(lib.dc_msg_get_text(self._dc_msg))
|
||||||
|
|
||||||
|
@property_with_doc
|
||||||
|
def filename(self):
|
||||||
|
"""filename if there was an attachment, otherwise empty string. """
|
||||||
|
return from_dc_charpointer(lib.dc_msg_get_file(self._dc_msg))
|
||||||
|
|
||||||
|
@property_with_doc
|
||||||
|
def filemime(self):
|
||||||
|
"""mime type of the file (if it exists)"""
|
||||||
|
return from_dc_charpointer(lib.dc_msg_get_filemime(self._dc_msg))
|
||||||
|
|
||||||
|
@property_with_doc
|
||||||
|
def type(self):
|
||||||
|
"""the media type of this message.
|
||||||
|
|
||||||
|
:returns: a :class:`deltachat.chatting.MessageType` instance.
|
||||||
|
"""
|
||||||
|
return MessageType(lib.dc_msg_get_type(self._dc_msg))
|
||||||
|
|
||||||
@property_with_doc
|
@property_with_doc
|
||||||
def time_sent(self):
|
def time_sent(self):
|
||||||
"""time when the message was sent.
|
"""time when the message was sent.
|
||||||
|
@ -210,12 +257,55 @@ class Message(object):
|
||||||
def get_sender_contact(self):
|
def get_sender_contact(self):
|
||||||
"""return the contact of who wrote the message.
|
"""return the contact of who wrote the message.
|
||||||
|
|
||||||
:returns: :class:`deltachat.chatting.Contact`` instance
|
:returns: :class:`deltachat.chatting.Contact` instance
|
||||||
"""
|
"""
|
||||||
contact_id = lib.dc_msg_get_from_id(self._dc_msg)
|
contact_id = lib.dc_msg_get_from_id(self._dc_msg)
|
||||||
return Contact(self._dc_context, contact_id)
|
return Contact(self._dc_context, contact_id)
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s
|
||||||
|
class MessageType(object):
|
||||||
|
""" DeltaChat message type, with is_* methods. """
|
||||||
|
_type = attr.ib(validator=v.instance_of(int))
|
||||||
|
_mapping = {
|
||||||
|
const.DC_MSG_TEXT: 'text',
|
||||||
|
const.DC_MSG_IMAGE: 'image',
|
||||||
|
const.DC_MSG_GIF: 'gif',
|
||||||
|
const.DC_MSG_AUDIO: 'audio',
|
||||||
|
const.DC_MSG_VIDEO: 'video',
|
||||||
|
const.DC_MSG_FILE: 'file'
|
||||||
|
}
|
||||||
|
|
||||||
|
@property_with_doc
|
||||||
|
def name(self):
|
||||||
|
""" human readable type name. """
|
||||||
|
return self._mapping[self._type]
|
||||||
|
|
||||||
|
def is_text(self):
|
||||||
|
""" return True if it's a text message. """
|
||||||
|
return self._type == const.DC_MSG_TEXT
|
||||||
|
|
||||||
|
def is_image(self):
|
||||||
|
""" return True if it's an image message. """
|
||||||
|
return self._type == const.DC_MSG_IMAGE
|
||||||
|
|
||||||
|
def is_gif(self):
|
||||||
|
""" return True if it's a gif message. """
|
||||||
|
return self._type == const.DC_MSG_GIF
|
||||||
|
|
||||||
|
def is_audio(self):
|
||||||
|
""" return True if it's an audio message. """
|
||||||
|
return self._type == const.DC_MSG_AUDIO
|
||||||
|
|
||||||
|
def is_video(self):
|
||||||
|
""" return True if it's a video message. """
|
||||||
|
return self._type == const.DC_MSG_VIDEO
|
||||||
|
|
||||||
|
def is_file(self):
|
||||||
|
""" return True if it's a file message. """
|
||||||
|
return self._type == const.DC_MSG_FILE
|
||||||
|
|
||||||
|
|
||||||
@attr.s
|
@attr.s
|
||||||
class MessageState(object):
|
class MessageState(object):
|
||||||
""" Current Message In/Out state, updated on each call of is_* methods.
|
""" Current Message In/Out state, updated on each call of is_* methods.
|
||||||
|
|
|
@ -24,6 +24,9 @@ DC_CHAT_TYPE_UNDEFINED = 0
|
||||||
DC_CHAT_TYPE_SINGLE = 100
|
DC_CHAT_TYPE_SINGLE = 100
|
||||||
DC_CHAT_TYPE_GROUP = 120
|
DC_CHAT_TYPE_GROUP = 120
|
||||||
DC_CHAT_TYPE_VERIFIED_GROUP = 130
|
DC_CHAT_TYPE_VERIFIED_GROUP = 130
|
||||||
|
DC_MSG_ID_MARKER1 = 1
|
||||||
|
DC_MSG_ID_DAYMARKER = 9
|
||||||
|
DC_MSG_ID_LAST_SPECIAL = 9
|
||||||
DC_STATE_UNDEFINED = 0
|
DC_STATE_UNDEFINED = 0
|
||||||
DC_STATE_IN_FRESH = 10
|
DC_STATE_IN_FRESH = 10
|
||||||
DC_STATE_IN_NOTICED = 13
|
DC_STATE_IN_NOTICED = 13
|
||||||
|
@ -35,6 +38,14 @@ DC_STATE_OUT_MDN_RCVD = 28
|
||||||
DC_CONTACT_ID_SELF = 1
|
DC_CONTACT_ID_SELF = 1
|
||||||
DC_CONTACT_ID_DEVICE = 2
|
DC_CONTACT_ID_DEVICE = 2
|
||||||
DC_CONTACT_ID_LAST_SPECIAL = 9
|
DC_CONTACT_ID_LAST_SPECIAL = 9
|
||||||
|
DC_MSG_UNDEFINED = 0
|
||||||
|
DC_MSG_TEXT = 10
|
||||||
|
DC_MSG_IMAGE = 20
|
||||||
|
DC_MSG_GIF = 21
|
||||||
|
DC_MSG_AUDIO = 40
|
||||||
|
DC_MSG_VOICE = 41
|
||||||
|
DC_MSG_VIDEO = 50
|
||||||
|
DC_MSG_FILE = 60
|
||||||
DC_EVENT_INFO = 100
|
DC_EVENT_INFO = 100
|
||||||
DC_EVENT_SMTP_CONNECTED = 101
|
DC_EVENT_SMTP_CONNECTED = 101
|
||||||
DC_EVENT_IMAP_CONNECTED = 102
|
DC_EVENT_IMAP_CONNECTED = 102
|
||||||
|
@ -62,7 +73,7 @@ DC_EVENT_HTTP_GET = 2100
|
||||||
|
|
||||||
|
|
||||||
def read_event_defines(f):
|
def read_event_defines(f):
|
||||||
rex = re.compile(r'#define\s+((?:DC_EVENT_|DC_STATE_|DC_CONTACT_ID_|DC_GCL|DC_CHAT)\S+)\s+([x\d]+).*')
|
rex = re.compile(r'#define\s+((?:DC_EVENT_|DC_MSG|DC_STATE_|DC_CONTACT_ID_|DC_GCL|DC_CHAT)\S+)\s+([x\d]+).*')
|
||||||
for line in f:
|
for line in f:
|
||||||
m = rex.match(line)
|
m = rex.match(line)
|
||||||
if m:
|
if m:
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
from deltachat import Account
|
from deltachat import Account
|
||||||
from deltachat.types import cached_property
|
from deltachat.types import cached_property
|
||||||
|
@ -13,6 +14,19 @@ def pytest_addoption(parser):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def data():
|
||||||
|
class Data:
|
||||||
|
def __init__(self):
|
||||||
|
self.path = os.path.join(os.path.dirname(__file__), "data")
|
||||||
|
|
||||||
|
def get_path(self, bn):
|
||||||
|
fn = os.path.join(self.path, bn)
|
||||||
|
assert os.path.exists(fn)
|
||||||
|
return fn
|
||||||
|
return Data()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def acfactory(pytestconfig, tmpdir, request):
|
def acfactory(pytestconfig, tmpdir, request):
|
||||||
fn = pytestconfig.getoption("--liveconfig")
|
fn = pytestconfig.getoption("--liveconfig")
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import pytest
|
import pytest
|
||||||
|
import os
|
||||||
from deltachat import const
|
from deltachat import const
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from conftest import wait_configuration_progress, wait_successful_IMAP_SMTP_connection
|
from conftest import wait_configuration_progress, wait_successful_IMAP_SMTP_connection
|
||||||
|
@ -82,12 +83,28 @@ class TestOfflineAccount:
|
||||||
chat.set_name("title2")
|
chat.set_name("title2")
|
||||||
assert chat.get_name() == "title2"
|
assert chat.get_name() == "title2"
|
||||||
|
|
||||||
|
def test_delete_and_send_fails(self, acfactory):
|
||||||
|
ac1 = acfactory.get_configured_offline_account()
|
||||||
|
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||||
|
chat = ac1.create_chat_by_contact(contact1)
|
||||||
|
chat.delete()
|
||||||
|
ac1._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
chat.send_text_message("msg1")
|
||||||
|
|
||||||
def test_message(self, acfactory):
|
def test_message(self, acfactory):
|
||||||
ac1 = acfactory.get_configured_offline_account()
|
ac1 = acfactory.get_configured_offline_account()
|
||||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||||
chat = ac1.create_chat_by_contact(contact1)
|
chat = ac1.create_chat_by_contact(contact1)
|
||||||
msg = chat.send_text_message("msg1")
|
msg = chat.send_text_message("msg1")
|
||||||
assert msg
|
assert msg
|
||||||
|
assert msg.type.is_text()
|
||||||
|
assert msg.type.name == "text"
|
||||||
|
assert not msg.type.is_audio()
|
||||||
|
assert not msg.type.is_video()
|
||||||
|
assert not msg.type.is_gif()
|
||||||
|
assert not msg.type.is_file()
|
||||||
|
assert not msg.type.is_image()
|
||||||
msg_state = msg.get_state()
|
msg_state = msg.get_state()
|
||||||
assert not msg_state.is_in_fresh()
|
assert not msg_state.is_in_fresh()
|
||||||
assert not msg_state.is_in_noticed()
|
assert not msg_state.is_in_noticed()
|
||||||
|
@ -97,6 +114,20 @@ class TestOfflineAccount:
|
||||||
assert not msg_state.is_out_delivered()
|
assert not msg_state.is_out_delivered()
|
||||||
assert not msg_state.is_out_mdn_received()
|
assert not msg_state.is_out_mdn_received()
|
||||||
|
|
||||||
|
def test_message_image(self, acfactory, data):
|
||||||
|
ac1 = acfactory.get_configured_offline_account()
|
||||||
|
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||||
|
chat = ac1.create_chat_by_contact(contact1)
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
chat.send_image(path="notexists")
|
||||||
|
fn = data.get_path("d.png")
|
||||||
|
msg = chat.send_image(fn)
|
||||||
|
assert msg.type.name == "image"
|
||||||
|
assert msg
|
||||||
|
assert msg.id > 0
|
||||||
|
assert os.path.exists(msg.filename)
|
||||||
|
assert msg.filemime == "image/png"
|
||||||
|
|
||||||
def test_chat_message_distinctions(self, acfactory):
|
def test_chat_message_distinctions(self, acfactory):
|
||||||
ac1 = acfactory.get_configured_offline_account()
|
ac1 = acfactory.get_configured_offline_account()
|
||||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||||
|
@ -199,3 +230,29 @@ class TestOnlineAccount:
|
||||||
lp.step("2")
|
lp.step("2")
|
||||||
ac1._evlogger.get_info_matching("Message marked as seen")
|
ac1._evlogger.get_info_matching("Message marked as seen")
|
||||||
assert msg_out.get_state().is_out_mdn_received()
|
assert msg_out.get_state().is_out_mdn_received()
|
||||||
|
|
||||||
|
def test_send_and_receive_image(self, acfactory, lp, data):
|
||||||
|
lp.sec("starting accounts, waiting for configuration")
|
||||||
|
ac1 = acfactory.get_online_configuring_account()
|
||||||
|
ac2 = acfactory.get_online_configuring_account()
|
||||||
|
c2 = ac1.create_contact(email=ac2.get_config("addr"))
|
||||||
|
chat = ac1.create_chat_by_contact(c2)
|
||||||
|
|
||||||
|
wait_configuration_progress(ac1, 1000)
|
||||||
|
wait_configuration_progress(ac2, 1000)
|
||||||
|
|
||||||
|
lp.sec("sending image message from ac1 to ac2")
|
||||||
|
path = data.get_path("d.png")
|
||||||
|
msg_out = chat.send_image(path)
|
||||||
|
ev = ac1._evlogger.get_matching("DC_EVENT_MSG_DELIVERED")
|
||||||
|
evt_name, data1, data2 = ev
|
||||||
|
assert data1 == chat.id
|
||||||
|
assert data2 == msg_out.id
|
||||||
|
assert msg_out.get_state().is_out_delivered()
|
||||||
|
|
||||||
|
lp.sec("wait for ac2 to receive message")
|
||||||
|
ev = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||||
|
assert ev[2] == msg_out.id
|
||||||
|
msg_in = ac2.get_message_by_id(msg_out.id)
|
||||||
|
assert os.path.exists(msg_in.filename)
|
||||||
|
assert os.stat(msg_in.filename).st_size == os.stat(path).st_size
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue