Merge pull request #2132 from deltachat/add_chat_audit_log_view

add flag to get only info/system messages of chat
This commit is contained in:
Simon Laux
2021-01-18 21:44:16 +01:00
committed by GitHub
5 changed files with 84 additions and 5 deletions

View File

@@ -1000,6 +1000,7 @@ dc_msg_t* dc_get_draft (dc_context_t* context, uint32_t ch
#define DC_GCM_ADDDAYMARKER 0x01
#define DC_GCM_INFO_ONLY 0x02
/**
@@ -1018,6 +1019,7 @@ dc_msg_t* dc_get_draft (dc_context_t* context, uint32_t ch
* @param flags If set to DC_GCM_ADDDAYMARKER, the marker DC_MSG_ID_DAYMARKER will
* be added before each day (regarding the local timezone). Set this to 0 if you do not want this behaviour.
* To get the concrete time of the marker, use dc_array_get_timestamp().
* If set to DC_GCM_INFO_ONLY, only system messages will be returned, can be combined with DC_GCM_ADDDAYMARKER.
* @param marker1before An optional message ID. If set, the id DC_MSG_ID_MARKER1 will be added just
* before the given ID in the returned array. Set this to 0 if you do not want this behaviour.
* @return Array of message IDs, must be dc_array_unref()'d when no longer used.

View File

@@ -145,6 +145,7 @@ def extract_defines(flags):
| DC_STR
| DC_CONTACT_ID
| DC_GCL
| DC_GCM
| DC_SOCKET
| DC_CHAT
| DC_PROVIDER

View File

@@ -8,6 +8,8 @@ from deltachat import const, Account
from deltachat.message import Message
from deltachat.tracker import ImexTracker
from deltachat.hookspec import account_hookimpl
from deltachat.capi import ffi, lib
from deltachat.cutil import iter_array
from datetime import datetime, timedelta
@@ -595,6 +597,28 @@ class TestOfflineChat:
assert in_list[1][1] == chat
assert in_list[1][2] == contacts[3]
def test_audit_log_view_without_daymarker(self, ac1, lp):
lp.sec("ac1: test audit log (show only system messages)")
chat = ac1.create_group_chat(name="audit log sample data")
# promote chat
chat.send_text("hello")
assert chat.is_promoted()
lp.sec("create test data")
chat.add_contact(ac1.create_contact("some-1@example.org"))
chat.set_name("audit log test group")
chat.send_text("a message in between")
lp.sec("check message count of all messages")
assert len(chat.get_messages()) == 4
lp.sec("check message count of only system messages (without daymarkers)")
dc_array = ffi.gc(
lib.dc_get_chat_msgs(ac1._dc_context, chat.id, const.DC_GCM_INFO_ONLY, 0),
lib.dc_array_unref
)
assert len(list(iter_array(dc_array, lambda x: x))) == 2
def test_basic_imap_api(acfactory, tmpdir):
ac1, ac2 = acfactory.get_two_online_accounts()

View File

@@ -2,6 +2,7 @@
use deltachat_derive::{FromSql, ToSql};
use std::convert::TryFrom;
use std::str::FromStr;
use std::time::{Duration, SystemTime};
use anyhow::Context as _;
@@ -17,7 +18,7 @@ use crate::constants::{
Blocked, Chattype, ShowEmails, Viewtype, DC_CHAT_ID_ALLDONE_HINT, DC_CHAT_ID_ARCHIVED_LINK,
DC_CHAT_ID_DEADDROP, DC_CHAT_ID_LAST_SPECIAL, DC_CHAT_ID_TRASH, DC_CONTACT_ID_DEVICE,
DC_CONTACT_ID_INFO, DC_CONTACT_ID_LAST_SPECIAL, DC_CONTACT_ID_SELF, DC_GCM_ADDDAYMARKER,
DC_RESEND_USER_AVATAR_DAYS,
DC_GCM_INFO_ONLY, DC_RESEND_USER_AVATAR_DAYS,
};
use crate::contact::{addr_cmp, Contact, Origin, VerifiedStatus};
use crate::context::Context;
@@ -1764,14 +1765,42 @@ pub async fn get_chat_msgs(
}
}
let process_row =
|row: &rusqlite::Row| Ok((row.get::<_, MsgId>("id")?, row.get::<_, i64>("timestamp")?));
let process_row = if (flags & DC_GCM_INFO_ONLY) != 0 {
|row: &rusqlite::Row| {
// is_info logic taken from Message.is_info()
let params = row.get::<_, String>("param")?;
let (from_id, to_id) = (row.get::<_, u32>("from_id")?, row.get::<_, u32>("to_id")?);
let is_info_msg: bool = from_id == DC_CONTACT_ID_INFO as u32
|| to_id == DC_CONTACT_ID_INFO as u32
|| match Params::from_str(&params) {
Ok(p) => {
let cmd = p.get_cmd();
cmd != SystemMessage::Unknown && cmd != SystemMessage::AutocryptSetupMessage
}
_ => false,
};
Ok((
row.get::<_, MsgId>("id")?,
row.get::<_, i64>("timestamp")?,
!is_info_msg,
))
}
} else {
|row: &rusqlite::Row| {
Ok((
row.get::<_, MsgId>("id")?,
row.get::<_, i64>("timestamp")?,
false,
))
}
};
let process_rows = |rows: rusqlite::MappedRows<_>| {
let mut ret = Vec::new();
let mut last_day = 0;
let cnv_to_local = dc_gm2local_offset();
for row in rows {
let (curr_id, ts) = row?;
let (curr_id, ts, exclude_message): (MsgId, i64, bool) = row?;
if let Some(marker_id) = marker1before {
if curr_id == marker_id {
ret.push(ChatItem::Marker1);
@@ -1787,7 +1816,9 @@ pub async fn get_chat_msgs(
last_day = curr_day;
}
}
ret.push(ChatItem::Message { msg_id: curr_id });
if !exclude_message {
ret.push(ChatItem::Message { msg_id: curr_id });
}
}
Ok(ret)
};
@@ -1815,6 +1846,26 @@ pub async fn get_chat_msgs(
process_rows,
)
.await
} else if (flags & DC_GCM_INFO_ONLY) != 0 {
context
.sql
.query_map(
// GLOB is used here instead of LIKE becase it is case-sensitive
"SELECT m.id AS id, m.timestamp AS timestamp, m.param AS param, m.from_id AS from_id, m.to_id AS to_id
FROM msgs m
WHERE m.chat_id=?
AND m.hidden=0
AND (
m.param GLOB \"*S=*\"
OR m.from_id == ?
OR m.to_id == ?
)
ORDER BY m.timestamp, m.id;",
paramsv![chat_id, DC_CONTACT_ID_INFO, DC_CONTACT_ID_INFO],
process_row,
process_rows,
)
.await
} else {
context
.sql

View File

@@ -99,6 +99,7 @@ pub const DC_GCL_ADD_ALLDONE_HINT: usize = 0x04;
pub const DC_GCL_FOR_FORWARDING: usize = 0x08;
pub const DC_GCM_ADDDAYMARKER: u32 = 0x01;
pub const DC_GCM_INFO_ONLY: u32 = 0x02;
pub const DC_GCL_VERIFIED_ONLY: usize = 0x01;
pub const DC_GCL_ADD_SELF: usize = 0x02;