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
/**
* 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 {
chat_id,
contact_id,
}
| Event::MemberRemoved {
chat_id,
contact_id,
} => {
ffi_cb(
self,

View File

@@ -3,6 +3,7 @@
from __future__ import print_function
import atexit
from contextlib import contextmanager
import queue
from threading import Event
import os
from array import array
@@ -15,7 +16,6 @@ from .message import Message
from .contact import Contact
from .tracker import ImexTracker
from . import hookspec, iothreads
from queue import Queue
class MissingCredentials(ValueError):
@@ -49,7 +49,7 @@ class Account(object):
hook.account_init(account=self, db_path=db_path)
self._threads = iothreads.IOThreads(self)
self._hook_event_queue = Queue()
self._hook_event_queue = queue.Queue()
self._in_use_iter_events = False
self._shutdown_event = Event()
@@ -578,6 +578,16 @@ class Account(object):
hook = hookspec.Global._get_plugin_manager().hook
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):
""" yield hook events until shutdown.
@@ -614,6 +624,10 @@ class Account(object):
chat = self.get_chat_by_id(ffi_event.data1)
contact = self.get_contact_by_id(ffi_event.data2)
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, {}

View File

@@ -100,6 +100,7 @@ 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_MEMBER_REMOVED = 2064
DC_EVENT_FILE_COPIED = 2055
DC_EVENT_IS_OFFLINE = 2081
DC_EVENT_GET_STRING = 2091

View File

@@ -56,6 +56,10 @@ class PerAccount:
def member_added(self, chat, contact):
""" 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:
""" 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):
lp.sec("ac1: creating group chat with 10 other members")
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 = []
for i in range(10):
lp.sec("create contact")
contact = ac1.create_contact("some{}@example.org".format(i))
contacts.append(contact)
lp.sec("add contact")
chat.add_contact(contact)
num_contacts = len(chat.get_contacts())
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")
chat.remove_contact(contacts[9])
chat.remove_contact(contacts[3])
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:
def get_chat(self, ac1, ac2, both_created=False):
@@ -1390,7 +1426,9 @@ class TestGroupStressTests:
lp.sec("ac2: removing one contact")
to_remove = contacts[-1]
msg.chat.remove_contact(to_remove)
ac2._evtracker.get_matching("DC_EVENT_MEMBER_REMOVED")
lp.sec("ac1: receiving system message about contact removal")
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(Param::Arg, contact.get_addr());
msg.param.set_int(Param::Arg2, from_handshake.into());
msg.id = send_msg(context, chat_id, &mut msg)?;
context.call_cb(Event::MsgsChanged {
chat_id,
msg_id: msg.id,
});
// send_msg sends MsgsChanged event
// so we only send an explicit MemberAdded one
context.call_cb(Event::MemberAdded {
chat_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));
Ok(true)
}
@@ -2171,6 +2173,10 @@ pub fn remove_contact_from_chat(
msg.param.set_cmd(SystemMessage::MemberRemovedFromGroup);
msg.param.set(Param::Arg, contact.get_addr());
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 {
chat_id,
msg_id: msg.id,

View File

@@ -213,4 +213,10 @@ pub enum Event {
/// @param data2 (int) contact_id
#[strum(props(id = "2063"))]
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 },
}