diff --git a/deltachat-ffi/deltachat.h b/deltachat-ffi/deltachat.h index 632600d6d..7e2d7c5b9 100644 --- a/deltachat-ffi/deltachat.h +++ b/deltachat-ffi/deltachat.h @@ -4505,7 +4505,7 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); /** - * This event is sent out to the inviter when a joiner successfully joined a group. + * (DEPRECATED) * * @param data1 (int) chat_id * @param data2 (int) contact_id @@ -4513,6 +4513,14 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot); */ #define DC_EVENT_SECUREJOIN_MEMBER_ADDED 2062 +/** + * This event is sent for each member that gets added to a (verified or unverified) chat. + * + * @param data1 (int) chat_id + * @param data2 (int) contact_id + * @return 0 + */ +#define DC_EVENT_MEMBER_ADDED 2063 /** * @} diff --git a/deltachat-ffi/src/lib.rs b/deltachat-ffi/src/lib.rs index 74773f18e..10cdfaccf 100644 --- a/deltachat-ffi/src/lib.rs +++ b/deltachat-ffi/src/lib.rs @@ -199,6 +199,10 @@ impl ContextWrapper { Event::SecurejoinMemberAdded { chat_id, contact_id, + } + | Event::MemberAdded { + chat_id, + contact_id, } => { ffi_cb( self, diff --git a/python/CHANGELOG b/python/CHANGELOG index 9b8ec1de8..82b6402cb 100644 --- a/python/CHANGELOG +++ b/python/CHANGELOG @@ -5,6 +5,8 @@ - introduced PerAccount and Global hooks that plugins can implement +- introduced `member_added()` plugin event. + 0.800.0 ------- diff --git a/python/doc/plugins.rst b/python/doc/plugins.rst index 281669991..6b37d19ea 100644 --- a/python/doc/plugins.rst +++ b/python/doc/plugins.rst @@ -7,6 +7,9 @@ for managing global and per-account plugin registration, and performing hook calls. +Registering a plugin +-------------------- + .. autoclass:: deltachat.register_global_plugin .. autoclass:: deltachat.account.Account.add_account_plugin diff --git a/python/src/deltachat/__init__.py b/python/src/deltachat/__init__.py index 43c100700..4630c7ea6 100644 --- a/python/src/deltachat/__init__.py +++ b/python/src/deltachat/__init__.py @@ -86,4 +86,9 @@ def register_global_plugin(plugin): gm.check_pending() +def unregister_global_plugin(plugin): + gm = hookspec.Global._get_plugin_manager() + gm.unregister(plugin) + + register_global_plugin(eventlogger) diff --git a/python/src/deltachat/account.py b/python/src/deltachat/account.py index d9a14d077..70f2eac8e 100644 --- a/python/src/deltachat/account.py +++ b/python/src/deltachat/account.py @@ -76,6 +76,10 @@ class Account(object): elif name == "DC_EVENT_MSG_DELIVERED": msg = self.get_message_by_id(ffi_event.data2) self._pm.hook.process_message_delivered(message=msg) + elif name == "DC_EVENT_MEMBER_ADDED": + chat = self.get_chat_by_id(ffi_event.data1) + contact = self.get_contact_by_id(ffi_event.data2) + self._pm.hook.member_added(chat=chat, contact=contact) # def __del__(self): # self.shutdown() @@ -342,6 +346,13 @@ class Account(object): """ return Message.from_db(self, msg_id) + def get_contact_by_id(self, contact_id): + """ return Contact instance or None. + :param contact_id: integer id of this contact. + :returns: None or :class:`deltachat.contact.Contact` instance. + """ + return Contact(self._dc_context, contact_id) + def get_chat_by_id(self, chat_id): """ return Chat instance. :param chat_id: integer id of this chat. diff --git a/python/src/deltachat/const.py b/python/src/deltachat/const.py index 7135b4905..09229ff75 100644 --- a/python/src/deltachat/const.py +++ b/python/src/deltachat/const.py @@ -99,6 +99,7 @@ DC_EVENT_IMEX_FILE_WRITTEN = 2052 DC_EVENT_SECUREJOIN_INVITER_PROGRESS = 2060 DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061 DC_EVENT_SECUREJOIN_MEMBER_ADDED = 2062 +DC_EVENT_MEMBER_ADDED = 2063 DC_EVENT_FILE_COPIED = 2055 DC_EVENT_IS_OFFLINE = 2081 DC_EVENT_GET_STRING = 2091 diff --git a/python/tests/conftest.py b/python/tests/conftest.py index 5e0129cab..eb83d76b0 100644 --- a/python/tests/conftest.py +++ b/python/tests/conftest.py @@ -4,12 +4,13 @@ import sys import py import pytest import requests +from contextlib import contextmanager import time from deltachat import Account from deltachat.tracker import ConfigureTracker from deltachat import const from deltachat.capi import lib -from deltachat.hookspec import PerAccount +from deltachat.hookspec import account_hookimpl from deltachat.eventlogger import FFIEventLogger from _pytest.monkeypatch import MonkeyPatch from ffi_event import FFIEventTracker @@ -293,14 +294,25 @@ def lp(): @pytest.fixture def make_plugin_recorder(): + @contextmanager def make_plugin_recorder(account): class HookImpl: def __init__(self): self.calls_member_added = [] @account_hookimpl - def member_added(self, chat, member): - self.calls_member_added.append(dict(chat=chat, member=member)) + def member_added(self, chat, contact): + self.calls_member_added.append(dict(chat=chat, contact=contact)) + + def get_first(self, name): + val = getattr(self, "calls_" + name, None) + if val is not None: + return val.pop(0) + + with account.temp_plugin(HookImpl()) as plugin: + yield plugin + + return make_plugin_recorder def wait_configuration_progress(account, min_target, max_target=1001): diff --git a/python/tests/test_account.py b/python/tests/test_account.py index c1a665cc4..c625b94c9 100644 --- a/python/tests/test_account.py +++ b/python/tests/test_account.py @@ -167,16 +167,17 @@ class TestOfflineChat: else: pytest.fail("could not find chat") - def test_add_member_event(self, ac1): - contact1 = ac1.create_contact("some1@hello.com", name="some1") - contact2 = ac1.create_contact("some2@hello.com", name="some2") + def test_add_member_event(self, ac1, make_plugin_recorder): chat = ac1.create_group_chat(name="title1") + # promote the chat + chat.send_text("hello") + contact1 = ac1.create_contact("some1@hello.com", name="some1") with make_plugin_recorder(ac1) as rec: - chat.add_contact(contact2) + chat.add_contact(contact1) kwargs = rec.get_first("member_added") assert kwargs["chat"] == chat - assert kwargs["member"] == contact2 + assert kwargs["contact"] == contact1 def test_group_chat_creation(self, ac1): contact1 = ac1.create_contact("some1@hello.com", name="some1") @@ -1129,7 +1130,7 @@ class TestOnlineAccount: ac1._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED") ac2._evtracker.get_matching("DC_EVENT_IMAP_MESSAGE_DELETED") wait_securejoin_inviter_progress(ac1, 1000) - ac1._evtracker.get_matching("DC_EVENT_SECUREJOIN_MEMBER_ADDED") + ac1._evtracker.get_matching("DC_EVENT_MEMBER_ADDED") def test_qr_verified_group_and_chatting(self, acfactory, lp): ac1, ac2 = acfactory.get_two_online_accounts() @@ -1141,7 +1142,7 @@ class TestOnlineAccount: chat2 = ac2.qr_join_chat(qr) assert chat2.id >= 10 wait_securejoin_inviter_progress(ac1, 1000) - ac1._evtracker.get_matching("DC_EVENT_SECUREJOIN_MEMBER_ADDED") + ac1._evtracker.get_matching("DC_EVENT_MEMBER_ADDED") lp.sec("ac2: read member added message") msg = ac2._evtracker.wait_next_incoming_message() diff --git a/src/chat.rs b/src/chat.rs index 1c24635a3..367007279 100644 --- a/src/chat.rs +++ b/src/chat.rs @@ -1958,6 +1958,10 @@ pub(crate) fn add_contact_to_chat_ex( chat_id, msg_id: msg.id, }); + context.call_cb(Event::MemberAdded { + chat_id, + contact_id: contact.id, + }); } context.call_cb(Event::MsgsChanged { chat_id, diff --git a/src/events.rs b/src/events.rs index 37a0a4bc4..3d1eb140f 100644 --- a/src/events.rs +++ b/src/events.rs @@ -207,4 +207,10 @@ pub enum Event { /// @param data2 (int) contact_id #[strum(props(id = "2062"))] SecurejoinMemberAdded { chat_id: ChatId, contact_id: u32 }, + + /// This event is sent for each contact added to a chat. + /// @param data1 (int) chat_id + /// @param data2 (int) contact_id + #[strum(props(id = "2063"))] + MemberAdded { chat_id: ChatId, contact_id: u32 }, } diff --git a/src/securejoin.rs b/src/securejoin.rs index e50953e72..2964aba98 100644 --- a/src/securejoin.rs +++ b/src/securejoin.rs @@ -750,7 +750,7 @@ pub(crate) fn handle_securejoin_handshake( group: field_grpid.to_string(), } })?; - context.call_cb(Event::SecurejoinMemberAdded { + context.call_cb(Event::MemberAdded { chat_id: group_chat_id, contact_id, });