mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 09:26:29 +03:00
route all flexible contact add/remove through account.as_contact(obj)
This commit is contained in:
@@ -218,20 +218,36 @@ class Account(object):
|
|||||||
with that e-mail address, it is unblocked and its display
|
with that e-mail address, it is unblocked and its display
|
||||||
name is updated.
|
name is updated.
|
||||||
|
|
||||||
:param addr: email-address (text type) or Contact (from other account).
|
:param addr: email-address, Account or Contact instance.
|
||||||
:param name: display name for this contact (optional)
|
:param name: display name for this contact (optional)
|
||||||
:returns: :class:`deltachat.contact.Contact` instance.
|
:returns: :class:`deltachat.contact.Contact` instance.
|
||||||
"""
|
"""
|
||||||
if isinstance(addr, Contact):
|
if not isinstance(addr, (Account, Contact, str)):
|
||||||
# might come from another account
|
raise TypeError(str(addr))
|
||||||
name = addr.name
|
return self.as_contact(addr, name=name)
|
||||||
addr = addr.addr
|
|
||||||
|
def as_contact(self, obj, name=None):
|
||||||
|
""" Create a contact from an Account, Contact or e-mail address. """
|
||||||
|
if isinstance(obj, Account):
|
||||||
|
if not obj.is_configured():
|
||||||
|
raise ValueError("can only add addresses from configured accounts")
|
||||||
|
addr, displayname = obj.get_config("addr"), obj.get_config("displayname")
|
||||||
|
elif isinstance(obj, Contact):
|
||||||
|
if obj.account != self:
|
||||||
|
raise ValueError("account mismatch {}".format(obj))
|
||||||
|
addr, displayname = obj.addr, obj.name
|
||||||
|
elif isinstance(obj, str):
|
||||||
|
displayname, addr = parseaddr(obj)
|
||||||
else:
|
else:
|
||||||
parse_name, addr = parseaddr(addr)
|
raise TypeError("don't know how to create chat for %r" % (obj, ))
|
||||||
if not name and parse_name:
|
|
||||||
name = parse_name
|
if name is None and displayname:
|
||||||
name = as_dc_charpointer(name)
|
name = displayname
|
||||||
|
return self._create_contact(addr, name)
|
||||||
|
|
||||||
|
def _create_contact(self, addr, name):
|
||||||
addr = as_dc_charpointer(addr)
|
addr = as_dc_charpointer(addr)
|
||||||
|
name = as_dc_charpointer(name)
|
||||||
contact_id = lib.dc_create_contact(self._dc_context, name, addr)
|
contact_id = lib.dc_create_contact(self._dc_context, name, addr)
|
||||||
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL, contact_id
|
assert contact_id > const.DC_CHAT_ID_LAST_SPECIAL, contact_id
|
||||||
return Contact(self, contact_id)
|
return Contact(self, contact_id)
|
||||||
@@ -262,12 +278,6 @@ class Account(object):
|
|||||||
"""
|
"""
|
||||||
return Contact(self, contact_id)
|
return Contact(self, contact_id)
|
||||||
|
|
||||||
def _port_contact(self, contact):
|
|
||||||
assert isinstance(contact, Contact)
|
|
||||||
if self != contact.account:
|
|
||||||
return self.create_contact(addr=contact.addr, name=contact.name)
|
|
||||||
return contact
|
|
||||||
|
|
||||||
def get_contacts(self, query=None, with_self=False, only_verified=False):
|
def get_contacts(self, query=None, with_self=False, only_verified=False):
|
||||||
""" get a (filtered) list of contacts.
|
""" get a (filtered) list of contacts.
|
||||||
|
|
||||||
@@ -298,35 +308,28 @@ class Account(object):
|
|||||||
yield from iter_array(dc_array, lambda x: Message.from_db(self, x))
|
yield from iter_array(dc_array, lambda x: Message.from_db(self, x))
|
||||||
|
|
||||||
def create_chat(self, obj):
|
def create_chat(self, obj):
|
||||||
""" Create a 1:1 chat with Account or e-mail addresse. """
|
""" Create a 1:1 chat with Account, Contact or e-mail address. """
|
||||||
if isinstance(obj, Account):
|
return self.as_contact(obj).create_chat()
|
||||||
if not obj.is_configured():
|
|
||||||
raise ValueError("can only add addresses from a configured account")
|
|
||||||
addr, name = obj.get_config("addr"), obj.get_config("displayname")
|
|
||||||
contact = self.create_contact(addr, name)
|
|
||||||
elif isinstance(obj, Contact):
|
|
||||||
contact = self._port_contact(obj)
|
|
||||||
elif isinstance(obj, str):
|
|
||||||
name, addr = parseaddr(obj)
|
|
||||||
contact = self.create_contact(addr, name)
|
|
||||||
else:
|
|
||||||
raise TypeError("don't know how to create chat for %r" % (obj, ))
|
|
||||||
return contact.create_chat()
|
|
||||||
|
|
||||||
def _create_chat_by_message_id(self, msg_id):
|
def _create_chat_by_message_id(self, msg_id):
|
||||||
return Chat(self, lib.dc_create_chat_by_msg_id(self._dc_context, msg_id))
|
return Chat(self, lib.dc_create_chat_by_msg_id(self._dc_context, msg_id))
|
||||||
|
|
||||||
def create_group_chat(self, name, verified=False):
|
def create_group_chat(self, name, contacts=None, verified=False):
|
||||||
""" create a new group chat object.
|
""" create a new group chat object.
|
||||||
|
|
||||||
Chats are unpromoted until the first message is sent.
|
Chats are unpromoted until the first message is sent.
|
||||||
|
|
||||||
|
:param contacts: list of contacts to add
|
||||||
:param verified: if true only verified contacts can be added.
|
:param verified: if true only verified contacts can be added.
|
||||||
:returns: a :class:`deltachat.chat.Chat` object.
|
:returns: a :class:`deltachat.chat.Chat` object.
|
||||||
"""
|
"""
|
||||||
bytes_name = name.encode("utf8")
|
bytes_name = name.encode("utf8")
|
||||||
chat_id = lib.dc_create_group_chat(self._dc_context, int(verified), bytes_name)
|
chat_id = lib.dc_create_group_chat(self._dc_context, int(verified), bytes_name)
|
||||||
return Chat(self, chat_id)
|
chat = Chat(self, chat_id)
|
||||||
|
if contacts is not None:
|
||||||
|
for contact in contacts:
|
||||||
|
chat.add_contact(contact)
|
||||||
|
return chat
|
||||||
|
|
||||||
def get_chats(self):
|
def get_chats(self):
|
||||||
""" return list of chats.
|
""" return list of chats.
|
||||||
|
|||||||
@@ -330,39 +330,34 @@ class Chat(object):
|
|||||||
|
|
||||||
# ------ group management API ------------------------------
|
# ------ group management API ------------------------------
|
||||||
|
|
||||||
def add_contact(self, contact):
|
def add_contact(self, obj):
|
||||||
""" add a contact to this chat.
|
""" add a contact to this chat.
|
||||||
|
|
||||||
If the contact is from another account create a new
|
:params obj: Contact, Account or e-mail address.
|
||||||
contact and add it to the group.
|
|
||||||
|
|
||||||
:params: contact object.
|
|
||||||
:raises ValueError: if contact could not be added
|
:raises ValueError: if contact could not be added
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
contact = self.account._port_contact(contact)
|
contact = self.account.as_contact(obj)
|
||||||
ret = lib.dc_add_contact_to_chat(self.account._dc_context, self.id, contact.id)
|
ret = lib.dc_add_contact_to_chat(self.account._dc_context, self.id, contact.id)
|
||||||
if ret != 1:
|
if ret != 1:
|
||||||
raise ValueError("could not add contact {!r} to chat".format(contact))
|
raise ValueError("could not add contact {!r} to chat".format(contact))
|
||||||
return contact
|
return contact
|
||||||
|
|
||||||
def remove_contact(self, contact):
|
def remove_contact(self, obj):
|
||||||
""" remove a contact from this chat.
|
""" remove a contact from this chat.
|
||||||
|
|
||||||
:params: contact object.
|
:params obj: Contact, Account or e-mail address.
|
||||||
:raises ValueError: if contact could not be removed
|
:raises ValueError: if contact could not be removed
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
contact = self.account._port_contact(contact)
|
contact = self.account.as_contact(obj)
|
||||||
ret = lib.dc_remove_contact_from_chat(self.account._dc_context, self.id, contact.id)
|
ret = lib.dc_remove_contact_from_chat(self.account._dc_context, self.id, contact.id)
|
||||||
if ret != 1:
|
if ret != 1:
|
||||||
raise ValueError("could not remove contact {!r} from chat".format(contact))
|
raise ValueError("could not remove contact {!r} from chat".format(contact))
|
||||||
|
|
||||||
def get_contacts(self):
|
def get_contacts(self):
|
||||||
""" get all contacts for this chat.
|
""" get all contacts for this chat.
|
||||||
:params: contact object.
|
|
||||||
:returns: list of :class:`deltachat.contact.Contact` objects for this chat
|
:returns: list of :class:`deltachat.contact.Contact` objects for this chat
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from .contact import Contact
|
from .contact import Contact
|
||||||
dc_array = ffi.gc(
|
dc_array = ffi.gc(
|
||||||
|
|||||||
@@ -396,6 +396,14 @@ def acfactory(pytestconfig, tmpdir, request, session_liveconfig, data):
|
|||||||
ac2.create_chat(ac1)
|
ac2.create_chat(ac1)
|
||||||
return ac1.create_chat(ac2)
|
return ac1.create_chat(ac2)
|
||||||
|
|
||||||
|
def accept_each_other(self, accounts, sending=False):
|
||||||
|
for i, acc in enumerate(accounts):
|
||||||
|
for acc2 in accounts[i + 1:]:
|
||||||
|
chat = self.get_accepted_chat(acc, acc2)
|
||||||
|
if sending:
|
||||||
|
chat.send_text("hi")
|
||||||
|
acc2._evtracker.wait_next_incoming_message()
|
||||||
|
|
||||||
am = AccountMaker()
|
am = AccountMaker()
|
||||||
request.addfinalizer(am.finalize)
|
request.addfinalizer(am.finalize)
|
||||||
yield am
|
yield am
|
||||||
|
|||||||
@@ -143,10 +143,8 @@ class TestOfflineContact:
|
|||||||
ac1 = acfactory.get_configured_offline_account()
|
ac1 = acfactory.get_configured_offline_account()
|
||||||
ac2 = acfactory.get_configured_offline_account()
|
ac2 = acfactory.get_configured_offline_account()
|
||||||
chat1 = ac1.create_chat(ac2)
|
chat1 = ac1.create_chat(ac2)
|
||||||
contact = ac1.create_contact(ac2.get_self_contact())
|
chat2 = ac1.create_chat(ac2.get_self_contact().addr)
|
||||||
chat2 = ac1.create_chat(contact)
|
assert chat1 == chat2
|
||||||
chat3 = ac1.create_chat(ac2.get_self_contact().addr)
|
|
||||||
assert chat1 == chat2 and chat2 == chat3
|
|
||||||
ac3 = acfactory.get_unconfigured_account()
|
ac3 = acfactory.get_unconfigured_account()
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError):
|
||||||
ac1.create_chat(ac3)
|
ac1.create_chat(ac3)
|
||||||
@@ -192,10 +190,13 @@ class TestOfflineChat:
|
|||||||
ac1 = acfactory.get_configured_offline_account()
|
ac1 = acfactory.get_configured_offline_account()
|
||||||
ac2 = acfactory.get_configured_offline_account()
|
ac2 = acfactory.get_configured_offline_account()
|
||||||
chat = ac1.create_group_chat(name="title1")
|
chat = ac1.create_group_chat(name="title1")
|
||||||
ac2_contact = ac2.get_self_contact()
|
with pytest.raises(ValueError):
|
||||||
contact = chat.add_contact(ac2_contact)
|
chat.add_contact(ac2.get_self_contact())
|
||||||
assert contact != ac2_contact
|
contact = chat.add_contact(ac2)
|
||||||
|
assert contact.addr == ac2.get_config("addr")
|
||||||
|
assert contact.name == ac2.get_config("displayname")
|
||||||
assert contact.account == ac1
|
assert contact.account == ac1
|
||||||
|
chat.remove_contact(ac2)
|
||||||
|
|
||||||
def test_group_chat_creation(self, ac1):
|
def test_group_chat_creation(self, ac1):
|
||||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||||
@@ -966,7 +967,7 @@ class TestOnlineAccount:
|
|||||||
|
|
||||||
lp.sec("create group chat with two members, one of which has no encrypt state")
|
lp.sec("create group chat with two members, one of which has no encrypt state")
|
||||||
chat = ac1.create_group_chat("encryption test")
|
chat = ac1.create_group_chat("encryption test")
|
||||||
chat.add_contact(ac2.get_self_contact())
|
chat.add_contact(ac2)
|
||||||
chat.add_contact(ac1.create_contact("notexisting@testrun.org"))
|
chat.add_contact(ac1.create_contact("notexisting@testrun.org"))
|
||||||
msg = chat.send_text("test not encrypt")
|
msg = chat.send_text("test not encrypt")
|
||||||
assert not msg.is_encrypted()
|
assert not msg.is_encrypted()
|
||||||
@@ -1146,6 +1147,7 @@ class TestOnlineAccount:
|
|||||||
lp.sec("create some chat content")
|
lp.sec("create some chat content")
|
||||||
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
contact1 = ac1.create_contact("some1@hello.com", name="some1")
|
||||||
contact1.create_chat().send_text("msg1")
|
contact1.create_chat().send_text("msg1")
|
||||||
|
assert len(ac1.get_contacts(query="some1")) == 1
|
||||||
backupdir = tmpdir.mkdir("backup")
|
backupdir = tmpdir.mkdir("backup")
|
||||||
|
|
||||||
lp.sec("export all to {}".format(backupdir))
|
lp.sec("export all to {}".format(backupdir))
|
||||||
@@ -1596,41 +1598,16 @@ class TestOnlineAccount:
|
|||||||
|
|
||||||
class TestGroupStressTests:
|
class TestGroupStressTests:
|
||||||
def test_group_many_members_add_leave_remove(self, acfactory, lp):
|
def test_group_many_members_add_leave_remove(self, acfactory, lp):
|
||||||
lp.sec("creating and configuring five accounts")
|
|
||||||
accounts = acfactory.get_many_online_accounts(5)
|
accounts = acfactory.get_many_online_accounts(5)
|
||||||
ac1 = accounts.pop()
|
acfactory.accept_each_other(accounts, sending=True)
|
||||||
|
ac1, ac5 = accounts.pop(), accounts.pop()
|
||||||
lp.sec("ac1: setting up contacts with 4 other members")
|
|
||||||
contacts = []
|
|
||||||
for acc, name in zip(accounts, list("äöüsr")):
|
|
||||||
contact = ac1.create_contact(acc.get_config("addr"), name=name)
|
|
||||||
contacts.append(contact)
|
|
||||||
|
|
||||||
# make sure we accept the "hi" message
|
|
||||||
contact.create_chat()
|
|
||||||
|
|
||||||
# make sure the other side accepts our messages
|
|
||||||
acc.create_chat(ac1).send_text("hi")
|
|
||||||
|
|
||||||
# send a message to get the contact key via autocrypt header
|
|
||||||
msg = ac1._evtracker.wait_next_incoming_message()
|
|
||||||
assert msg.text == "hi"
|
|
||||||
|
|
||||||
# Save fifth account for later
|
|
||||||
ac5 = accounts.pop()
|
|
||||||
contact5 = contacts.pop()
|
|
||||||
|
|
||||||
lp.sec("ac1: creating group chat with 3 other members")
|
lp.sec("ac1: creating group chat with 3 other members")
|
||||||
chat = ac1.create_group_chat("title1")
|
chat = ac1.create_group_chat("title1", contacts=accounts)
|
||||||
for contact in contacts:
|
|
||||||
chat.add_contact(contact)
|
|
||||||
assert not chat.is_promoted()
|
|
||||||
|
|
||||||
lp.sec("ac1: send message to new group chat")
|
lp.sec("ac1: send message to new group chat")
|
||||||
msg = chat.send_text("hello")
|
msg1 = chat.send_text("hello")
|
||||||
assert chat.is_promoted()
|
assert msg1.is_encrypted()
|
||||||
assert msg.is_encrypted()
|
|
||||||
|
|
||||||
gossiped_timestamp = chat.get_summary()["gossiped_timestamp"]
|
gossiped_timestamp = chat.get_summary()["gossiped_timestamp"]
|
||||||
assert gossiped_timestamp > 0
|
assert gossiped_timestamp > 0
|
||||||
|
|
||||||
@@ -1639,24 +1616,23 @@ class TestGroupStressTests:
|
|||||||
|
|
||||||
lp.sec("ac2: checking that the chat arrived correctly")
|
lp.sec("ac2: checking that the chat arrived correctly")
|
||||||
ac2 = accounts[0]
|
ac2 = accounts[0]
|
||||||
msg = ac2._evtracker.wait_next_incoming_message()
|
msg2 = ac2._evtracker.wait_next_incoming_message()
|
||||||
assert msg.text == "hello"
|
assert msg2.text == "hello"
|
||||||
print("chat is", msg.chat)
|
print("chat is", msg2.chat)
|
||||||
assert len(msg.chat.get_contacts()) == 4
|
assert len(msg2.chat.get_contacts()) == 4
|
||||||
|
|
||||||
lp.sec("ac3: checking that 'ac4' is a known contact")
|
lp.sec("ac3: checking that 'ac4' is a known contact")
|
||||||
ac3 = accounts[1]
|
ac3 = accounts[1]
|
||||||
msg3 = ac3._evtracker.wait_next_incoming_message()
|
msg3 = ac3._evtracker.wait_next_incoming_message()
|
||||||
assert msg3.text == "hello"
|
assert msg3.text == "hello"
|
||||||
ac3_contacts = ac3.get_contacts()
|
ac3_contacts = ac3.get_contacts()
|
||||||
assert len(ac3_contacts) == 3
|
assert len(ac3_contacts) == 4
|
||||||
ac4_contacts = ac3.get_contacts(query=accounts[2].get_config("addr"))
|
ac4_contacts = ac3.get_contacts(query=accounts[2].get_config("addr"))
|
||||||
assert len(ac4_contacts) == 1
|
assert len(ac4_contacts) == 1
|
||||||
|
|
||||||
lp.sec("ac2: removing one contact")
|
lp.sec("ac2: removing one contact")
|
||||||
to_remove = contacts[-1]
|
to_remove = ac2.create_contact(accounts[-1])
|
||||||
|
msg2.chat.remove_contact(to_remove)
|
||||||
msg.chat.remove_contact(to_remove)
|
|
||||||
|
|
||||||
lp.sec("ac1: receiving system message about contact removal")
|
lp.sec("ac1: receiving system message about contact removal")
|
||||||
sysmsg = ac1._evtracker.wait_next_incoming_message()
|
sysmsg = ac1._evtracker.wait_next_incoming_message()
|
||||||
@@ -1673,13 +1649,13 @@ class TestGroupStressTests:
|
|||||||
assert chat.get_summary()["gossiped_timestamp"] == gossiped_timestamp
|
assert chat.get_summary()["gossiped_timestamp"] == gossiped_timestamp
|
||||||
|
|
||||||
lp.sec("ac1: adding fifth member to the chat")
|
lp.sec("ac1: adding fifth member to the chat")
|
||||||
chat.add_contact(contact5)
|
chat.add_contact(ac5)
|
||||||
# Additng contact to chat resets gossiped_timestamp
|
# Adding contact to chat resets gossiped_timestamp
|
||||||
assert chat.get_summary()["gossiped_timestamp"] >= gossiped_timestamp
|
assert chat.get_summary()["gossiped_timestamp"] >= gossiped_timestamp
|
||||||
|
|
||||||
lp.sec("ac2: receiving system message about contact addition")
|
lp.sec("ac2: receiving system message about contact addition")
|
||||||
sysmsg = ac2._evtracker.wait_next_incoming_message()
|
sysmsg = ac2._evtracker.wait_next_incoming_message()
|
||||||
assert contact5.addr in sysmsg.text
|
assert ac5.addr in sysmsg.text
|
||||||
assert len(sysmsg.chat.get_contacts()) == 4
|
assert len(sysmsg.chat.get_contacts()) == 4
|
||||||
|
|
||||||
lp.sec("ac5: waiting for message about addition to the chat")
|
lp.sec("ac5: waiting for message about addition to the chat")
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ class TestOnlineInCreation:
|
|||||||
|
|
||||||
lp.sec("forward the message while still in creation")
|
lp.sec("forward the message while still in creation")
|
||||||
chat2 = ac1.create_group_chat("newgroup")
|
chat2 = ac1.create_group_chat("newgroup")
|
||||||
chat2.add_contact(ac2.get_self_contact())
|
chat2.add_contact(ac2)
|
||||||
wait_msgs_changed(ac1, [(0, 0)]) # why not chat id?
|
wait_msgs_changed(ac1, [(0, 0)]) # why not chat id?
|
||||||
ac1.forward_messages([prepared_original], chat2)
|
ac1.forward_messages([prepared_original], chat2)
|
||||||
# XXX there might be two EVENT_MSGS_CHANGED and only one of them
|
# XXX there might be two EVENT_MSGS_CHANGED and only one of them
|
||||||
|
|||||||
Reference in New Issue
Block a user