fix #164 add MEMBER_REMOVED event and member_removed plugin python hook

This commit is contained in:
holger krekel
2020-03-04 14:34:26 +01:00
parent 36b50436d7
commit d66829702f
8 changed files with 92 additions and 10 deletions

View File

@@ -4522,6 +4522,15 @@ int64_t dc_lot_get_timestamp (const dc_lot_t* lot);
*/ */
#define DC_EVENT_MEMBER_ADDED 2063 #define DC_EVENT_MEMBER_ADDED 2063
/**
* This event is sent for each member that gets removed from a (verified or unverified) chat.
*
* @param data1 (int) chat_id
* @param data2 (int) contact_id
* @return 0
*/
#define DC_EVENT_MEMBER_REMOVED 2064
/** /**
* @} * @}
*/ */

View File

@@ -203,6 +203,10 @@ impl ContextWrapper {
| Event::MemberAdded { | Event::MemberAdded {
chat_id, chat_id,
contact_id, contact_id,
}
| Event::MemberRemoved {
chat_id,
contact_id,
} => { } => {
ffi_cb( ffi_cb(
self, self,

View File

@@ -3,6 +3,7 @@
from __future__ import print_function from __future__ import print_function
import atexit import atexit
from contextlib import contextmanager from contextlib import contextmanager
import queue
from threading import Event from threading import Event
import os import os
from array import array from array import array
@@ -15,7 +16,6 @@ from .message import Message
from .contact import Contact from .contact import Contact
from .tracker import ImexTracker from .tracker import ImexTracker
from . import hookspec, iothreads from . import hookspec, iothreads
from queue import Queue
class MissingCredentials(ValueError): class MissingCredentials(ValueError):
@@ -49,7 +49,7 @@ class Account(object):
hook.account_init(account=self, db_path=db_path) hook.account_init(account=self, db_path=db_path)
self._threads = iothreads.IOThreads(self) self._threads = iothreads.IOThreads(self)
self._hook_event_queue = Queue() self._hook_event_queue = queue.Queue()
self._in_use_iter_events = False self._in_use_iter_events = False
self._shutdown_event = Event() self._shutdown_event = Event()
@@ -578,6 +578,16 @@ class Account(object):
hook = hookspec.Global._get_plugin_manager().hook hook = hookspec.Global._get_plugin_manager().hook
hook.account_after_shutdown(account=self, dc_context=dc_context) hook.account_after_shutdown(account=self, dc_context=dc_context)
def _handle_current_events(self):
""" handle all currently queued events and then return. """
while 1:
try:
event = self._hook_event_queue.get(block=False)
except queue.Empty:
break
else:
event.call_hook()
def iter_events(self, timeout=None): def iter_events(self, timeout=None):
""" yield hook events until shutdown. """ yield hook events until shutdown.
@@ -614,6 +624,10 @@ class Account(object):
chat = self.get_chat_by_id(ffi_event.data1) chat = self.get_chat_by_id(ffi_event.data1)
contact = self.get_contact_by_id(ffi_event.data2) contact = self.get_contact_by_id(ffi_event.data2)
return "member_added", dict(chat=chat, contact=contact) return "member_added", dict(chat=chat, contact=contact)
elif name == "DC_EVENT_MEMBER_REMOVED":
chat = self.get_chat_by_id(ffi_event.data1)
contact = self.get_contact_by_id(ffi_event.data2)
return "member_removed", dict(chat=chat, contact=contact)
return None, {} return None, {}

View File

@@ -100,6 +100,7 @@ DC_EVENT_SECUREJOIN_INVITER_PROGRESS = 2060
DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061 DC_EVENT_SECUREJOIN_JOINER_PROGRESS = 2061
DC_EVENT_SECUREJOIN_MEMBER_ADDED = 2062 DC_EVENT_SECUREJOIN_MEMBER_ADDED = 2062
DC_EVENT_MEMBER_ADDED = 2063 DC_EVENT_MEMBER_ADDED = 2063
DC_EVENT_MEMBER_REMOVED = 2064
DC_EVENT_FILE_COPIED = 2055 DC_EVENT_FILE_COPIED = 2055
DC_EVENT_IS_OFFLINE = 2081 DC_EVENT_IS_OFFLINE = 2081
DC_EVENT_GET_STRING = 2091 DC_EVENT_GET_STRING = 2091

View File

@@ -56,6 +56,10 @@ class PerAccount:
def member_added(self, chat, contact): def member_added(self, chat, contact):
""" Called for each contact added to a chat. """ """ Called for each contact added to a chat. """
@account_hookspec
def member_removed(self, chat, contact):
""" Called for each contact removed from a chat. """
class Global: class Global:
""" global hook specifications using a per-process singleton """ global hook specifications using a per-process singleton

View File

@@ -438,20 +438,56 @@ class TestOfflineChat:
def test_group_chat_many_members_add_remove(self, ac1, lp): def test_group_chat_many_members_add_remove(self, ac1, lp):
lp.sec("ac1: creating group chat with 10 other members") lp.sec("ac1: creating group chat with 10 other members")
chat = ac1.create_group_chat(name="title1") chat = ac1.create_group_chat(name="title1")
# promote chat
chat.send_text("hello")
assert chat.is_promoted()
# activate local plugin
in_list = []
class InPlugin:
@account_hookimpl
def member_added(self, chat, contact):
in_list.append(("added", chat, contact))
@account_hookimpl
def member_removed(self, chat, contact):
in_list.append(("removed", chat, contact))
ac1.add_account_plugin(InPlugin())
# perform add contact many times
contacts = [] contacts = []
for i in range(10): for i in range(10):
lp.sec("create contact")
contact = ac1.create_contact("some{}@example.org".format(i)) contact = ac1.create_contact("some{}@example.org".format(i))
contacts.append(contact) contacts.append(contact)
lp.sec("add contact")
chat.add_contact(contact) chat.add_contact(contact)
num_contacts = len(chat.get_contacts()) num_contacts = len(chat.get_contacts())
assert num_contacts == 11 assert num_contacts == 11
# perform plugin hooks
ac1._handle_current_events()
assert len(in_list) == 10
for in_cmd, in_chat, in_contact in in_list:
assert in_cmd == "added"
assert in_chat == chat
assert in_contact in contacts
lp.sec("ac1: removing two contacts and checking things are right") lp.sec("ac1: removing two contacts and checking things are right")
chat.remove_contact(contacts[9]) chat.remove_contact(contacts[9])
chat.remove_contact(contacts[3]) chat.remove_contact(contacts[3])
assert len(chat.get_contacts()) == 9 assert len(chat.get_contacts()) == 9
ac1._handle_current_events()
assert len(in_list) == 12
assert in_list[-2][0] == "removed"
assert in_list[-2][1] == chat
assert in_list[-2][2] == contacts[9]
class TestOnlineAccount: class TestOnlineAccount:
def get_chat(self, ac1, ac2, both_created=False): def get_chat(self, ac1, ac2, both_created=False):
@@ -1390,7 +1426,9 @@ class TestGroupStressTests:
lp.sec("ac2: removing one contact") lp.sec("ac2: removing one contact")
to_remove = contacts[-1] to_remove = contacts[-1]
msg.chat.remove_contact(to_remove) msg.chat.remove_contact(to_remove)
ac2._evtracker.get_matching("DC_EVENT_MEMBER_REMOVED")
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()

View File

@@ -1953,20 +1953,22 @@ pub(crate) fn add_contact_to_chat_ex(
msg.param.set_cmd(SystemMessage::MemberAddedToGroup); msg.param.set_cmd(SystemMessage::MemberAddedToGroup);
msg.param.set(Param::Arg, contact.get_addr()); msg.param.set(Param::Arg, contact.get_addr());
msg.param.set_int(Param::Arg2, from_handshake.into()); msg.param.set_int(Param::Arg2, from_handshake.into());
msg.id = send_msg(context, chat_id, &mut msg)?; msg.id = send_msg(context, chat_id, &mut msg)?;
context.call_cb(Event::MsgsChanged { // send_msg sends MsgsChanged event
chat_id, // so we only send an explicit MemberAdded one
msg_id: msg.id,
});
context.call_cb(Event::MemberAdded { context.call_cb(Event::MemberAdded {
chat_id, chat_id,
contact_id: contact.id, contact_id: contact.id,
}); });
} else {
// send an event for unpromoted groups
// XXX probably not neccessary because ChatModified should suffice
context.call_cb(Event::MsgsChanged {
chat_id,
msg_id: MsgId::new(0),
});
} }
context.call_cb(Event::MsgsChanged {
chat_id,
msg_id: MsgId::new(0),
});
context.call_cb(Event::ChatModified(chat_id)); context.call_cb(Event::ChatModified(chat_id));
Ok(true) Ok(true)
} }
@@ -2171,6 +2173,10 @@ pub fn remove_contact_from_chat(
msg.param.set_cmd(SystemMessage::MemberRemovedFromGroup); msg.param.set_cmd(SystemMessage::MemberRemovedFromGroup);
msg.param.set(Param::Arg, contact.get_addr()); msg.param.set(Param::Arg, contact.get_addr());
msg.id = send_msg(context, chat_id, &mut msg)?; msg.id = send_msg(context, chat_id, &mut msg)?;
context.call_cb(Event::MemberRemoved {
chat_id,
contact_id: contact.id,
});
context.call_cb(Event::MsgsChanged { context.call_cb(Event::MsgsChanged {
chat_id, chat_id,
msg_id: msg.id, msg_id: msg.id,

View File

@@ -213,4 +213,10 @@ pub enum Event {
/// @param data2 (int) contact_id /// @param data2 (int) contact_id
#[strum(props(id = "2063"))] #[strum(props(id = "2063"))]
MemberAdded { chat_id: ChatId, contact_id: u32 }, MemberAdded { chat_id: ChatId, contact_id: u32 },
/// This event is sent for each contact removed from a chat.
/// @param data1 (int) chat_id
/// @param data2 (int) contact_id
#[strum(props(id = "2064"))]
MemberRemoved { chat_id: ChatId, contact_id: u32 },
} }