Skip to content
14 changes: 8 additions & 6 deletions src/handlers/notification.rs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -849,7 +849,8 @@ async fn handle_business_notification(client: &Arc<Client>, node: &NodeRef<'_>)
let event = Event::BusinessStatusUpdate(BusinessStatusUpdate {
jid: notification.from.clone(),
update_type,
timestamp: notification.timestamp,
timestamp: chrono::DateTime::from_timestamp(notification.timestamp, 0)
.unwrap_or_else(wacore::time::now_utc),
target_jid: notification.jid.clone(),
hash: notification.hash.clone(),
verified_name,
Expand Down Expand Up @@ -1046,7 +1047,7 @@ fn notification_timestamp(node: &NodeRef<'_>) -> chrono::DateTime<chrono::Utc> {
.optional_u64("t")
.and_then(|t| i64::try_from(t).ok())
.and_then(|t| chrono::DateTime::from_timestamp(t, 0))
.unwrap_or_else(chrono::Utc::now)
.unwrap_or_else(wacore::time::now_utc)
}

/// Learn LID-PN mappings from a contacts modify notification.
Expand Down Expand Up @@ -1221,7 +1222,7 @@ async fn handle_group_notification(client: &Arc<Client>, node: Arc<OwnedNodeRef>
let timestamp = i64::try_from(notification.timestamp)
.ok()
.and_then(|t| chrono::DateTime::from_timestamp(t, 0))
.unwrap_or_else(chrono::Utc::now);
.unwrap_or_else(wacore::time::now_utc);

for action in notification.actions {
// Granularly patch group cache instead of invalidating — matches WA Web's
Expand Down Expand Up @@ -1396,7 +1397,8 @@ fn handle_disappearing_mode_notification(client: &Arc<Client>, node: &NodeRef<'_
// WA Web: `t.attrTime("t")` — required, no default.
let Some(setting_timestamp) = dm_attrs
.optional_string("t")
.and_then(|s| s.parse::<u64>().ok())
.and_then(|s| s.parse::<i64>().ok())
.and_then(|t| chrono::DateTime::from_timestamp(t, 0))
else {
warn!(
"disappearing_mode notification missing or invalid 't' attribute: {}",
Expand Down Expand Up @@ -1698,7 +1700,7 @@ mod tests {
/// Helper: parse a disappearing_mode notification node the same way
/// the handler does, returning `(duration, setting_timestamp)` or `None`
/// on validation failure.
fn parse_disappearing_mode(node: &Node) -> Option<(u32, u64)> {
fn parse_disappearing_mode(node: &Node) -> Option<(u32, i64)> {
let dm_node = node.get_optional_child("disappearing_mode")?;
let mut dm_attrs = dm_node.attrs();
let duration = dm_attrs
Expand All @@ -1707,7 +1709,7 @@ mod tests {
.unwrap_or(0);
let setting_timestamp = dm_attrs
.optional_string("t")
.and_then(|s| s.parse::<u64>().ok())?;
.and_then(|s| s.parse::<i64>().ok())?;
Some((duration, setting_timestamp))
}

Expand Down
4 changes: 2 additions & 2 deletions src/pdo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -414,9 +414,9 @@ impl Client {
let timestamp = web_msg
.message_timestamp
.map(|ts| {
chrono::DateTime::from_timestamp(ts as i64, 0).unwrap_or_else(chrono::Utc::now)
chrono::DateTime::from_timestamp(ts as i64, 0).unwrap_or_else(wacore::time::now_utc)
})
.unwrap_or_else(chrono::Utc::now);
.unwrap_or_else(wacore::time::now_utc);

Ok(MessageInfo {
id: key.id.clone().unwrap_or_default(),
Expand Down
2 changes: 1 addition & 1 deletion wacore/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ pub fn parse_message_info(
.map(|s| s.to_string())
.unwrap_or_default(),
timestamp: chrono::DateTime::from_timestamp(attrs.unix_time("t"), 0)
.unwrap_or_else(chrono::Utc::now),
.unwrap_or_else(crate::time::now_utc),
category,
edit: attrs
.optional_string("edit")
Expand Down
83 changes: 78 additions & 5 deletions wacore/src/types/events.rs
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,11 @@ impl From<crate::stanza::business::BusinessNotificationType> for BusinessUpdateT
/// Business status update notification.
#[derive(Debug, Clone, Serialize)]
pub struct BusinessStatusUpdate {
/// The business account whose status changed.
pub jid: Jid,
pub update_type: BusinessUpdateType,
pub timestamp: i64,
#[serde(with = "chrono::serde::ts_seconds")]
pub timestamp: DateTime<Utc>,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
#[serde(skip_serializing_if = "Option::is_none")]
pub target_jid: Option<Jid>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand All @@ -344,9 +346,10 @@ pub struct DisappearingModeChanged {
pub from: Jid,
/// New duration in seconds (0 = disabled, 86400 = 24h, etc.).
pub duration: u32,
/// Unix timestamp (seconds) when the setting was changed.
/// Consumers should only apply this if it's newer than their stored timestamp.
pub setting_timestamp: u64,
/// When the setting was changed.
/// Consumers should only apply this if it's newer than their stored value.
#[serde(with = "chrono::serde::ts_seconds")]
pub setting_timestamp: DateTime<Utc>,
}

#[derive(Debug, Clone, Serialize)]
Expand Down Expand Up @@ -445,12 +448,69 @@ impl Event {
let (msg, _) = self.as_message()?;
msg.conversation.as_deref()
}

/// Returns the primary JID associated with this event, if any.
///
/// Useful for routing events to the right chat without exhaustively matching every variant.
/// Returns `None` for connection-lifecycle and sync events that have no associated chat,
/// and for `JoinedGroup` (the JID is embedded inside the serialized `LazyConversation`
/// bytes and would require proto decoding to extract).
pub fn chat_jid(&self) -> Option<&Jid> {
match self {
Event::Message(_, info) => Some(&info.source.chat),
Event::Receipt(r) => Some(&r.source.chat),
Event::UndecryptableMessage(u) => Some(&u.info.source.chat),
Event::ChatPresence(c) => Some(&c.source.chat),
Event::Presence(p) => Some(&p.from),
Event::PictureUpdate(p) => Some(&p.jid),
Event::UserAboutUpdate(u) => Some(&u.jid),
Event::ContactUpdated(c) => Some(&c.jid),
Event::ContactNumberChanged(c) => Some(&c.new_jid),
Event::GroupUpdate(g) => Some(&g.group_jid),
Event::JoinedGroup(_) => None,
Event::ContactUpdate(c) => Some(&c.jid),
Event::PushNameUpdate(p) => Some(&p.jid),
Event::PinUpdate(p) => Some(&p.jid),
Event::MuteUpdate(m) => Some(&m.jid),
Event::ArchiveUpdate(a) => Some(&a.jid),
Event::StarUpdate(s) => Some(&s.chat_jid),
Event::MarkChatAsReadUpdate(m) => Some(&m.jid),
Event::DeleteChatUpdate(d) => Some(&d.jid),
Event::DeleteMessageForMeUpdate(d) => Some(&d.chat_jid),
Event::BusinessStatusUpdate(b) => Some(&b.jid),
Event::DisappearingModeChanged(d) => Some(&d.from),
Event::NewsletterLiveUpdate(n) => Some(&n.newsletter_jid),
Event::DeviceListUpdate(d) => Some(&d.user),
Event::IdentityChange(i) => Some(&i.user),
Event::Connected(_)
| Event::Disconnected(_)
| Event::PairSuccess(_)
| Event::PairError(_)
| Event::LoggedOut(_)
| Event::PairingQrCode { .. }
| Event::PairingCode { .. }
| Event::QrScannedWithoutMultidevice(_)
| Event::ClientOutdated(_)
| Event::SelfPushNameUpdated(_)
| Event::HistorySync(_)
| Event::OfflineSyncPreview(_)
| Event::OfflineSyncCompleted(_)
| Event::StreamReplaced(_)
| Event::TemporaryBan(_)
| Event::ConnectFailure(_)
| Event::StreamError(_)
| Event::ContactSyncRequested(_)
| Event::Notification(_)
| Event::RawNode(_) => None,
}
}
}

/// A newsletter live update notification, typically containing updated
/// reaction counts for one or more messages.
#[derive(Debug, Clone, Serialize)]
pub struct NewsletterLiveUpdate {
/// The newsletter channel this update belongs to.
pub newsletter_jid: Jid,
pub messages: Vec<NewsletterLiveUpdateMessage>,
}
Expand Down Expand Up @@ -706,6 +766,7 @@ pub struct ChatPresenceUpdate {

#[derive(Debug, Clone, Serialize)]
pub struct PresenceUpdate {
/// The contact whose presence changed.
pub from: Jid,
pub unavailable: bool,
pub last_seen: Option<DateTime<Utc>>,
Expand All @@ -729,6 +790,7 @@ pub struct PictureUpdate {

#[derive(Debug, Clone, Serialize)]
pub struct UserAboutUpdate {
/// The contact whose about text changed.
pub jid: Jid,
pub status: String,
pub timestamp: DateTime<Utc>,
Expand All @@ -744,6 +806,7 @@ pub struct UserAboutUpdate {
/// sync mutations (different source, different payload).
#[derive(Debug, Clone, Serialize)]
pub struct ContactUpdated {
/// The contact whose profile was updated.
pub jid: Jid,
pub timestamp: DateTime<Utc>,
}
Expand Down Expand Up @@ -804,6 +867,7 @@ pub struct GroupUpdate {

#[derive(Debug, Clone, Serialize)]
pub struct ContactUpdate {
/// The chat/contact this sync action applies to.
pub jid: Jid,
pub timestamp: DateTime<Utc>,
pub action: Box<wa::sync_action_value::ContactAction>,
Expand All @@ -812,14 +876,17 @@ pub struct ContactUpdate {

#[derive(Debug, Clone, Serialize)]
pub struct PushNameUpdate {
/// The contact who changed their push name.
pub jid: Jid,
pub message: Box<MessageInfo>,
pub message: Arc<MessageInfo>,
pub old_push_name: String,
pub new_push_name: String,
pub from_full_sync: bool,
}

#[derive(Debug, Clone, Serialize)]
pub struct PinUpdate {
/// The chat being pinned or unpinned.
pub jid: Jid,
pub timestamp: DateTime<Utc>,
pub action: Box<wa::sync_action_value::PinAction>,
Expand All @@ -828,6 +895,7 @@ pub struct PinUpdate {

#[derive(Debug, Clone, Serialize)]
pub struct MuteUpdate {
/// The chat being muted or unmuted.
pub jid: Jid,
pub timestamp: DateTime<Utc>,
pub action: Box<wa::sync_action_value::MuteAction>,
Expand All @@ -836,6 +904,7 @@ pub struct MuteUpdate {

#[derive(Debug, Clone, Serialize)]
pub struct ArchiveUpdate {
/// The chat being archived or unarchived.
pub jid: Jid,
pub timestamp: DateTime<Utc>,
pub action: Box<wa::sync_action_value::ArchiveChatAction>,
Expand All @@ -844,6 +913,7 @@ pub struct ArchiveUpdate {

#[derive(Debug, Clone, Serialize)]
pub struct StarUpdate {
/// The chat containing the starred or unstarred message.
pub chat_jid: Jid,
/// The participant who sent the message. `Some` for group messages from
/// others, `None` for self-authored or 1-on-1 messages (wire value `"0"`).
Expand All @@ -857,6 +927,7 @@ pub struct StarUpdate {

#[derive(Debug, Clone, Serialize)]
pub struct MarkChatAsReadUpdate {
/// The chat being marked as read or unread.
pub jid: Jid,
pub timestamp: DateTime<Utc>,
pub action: Box<wa::sync_action_value::MarkChatAsReadAction>,
Expand All @@ -865,6 +936,7 @@ pub struct MarkChatAsReadUpdate {

#[derive(Debug, Clone, Serialize)]
pub struct DeleteChatUpdate {
/// The chat being deleted.
pub jid: Jid,
/// From the index, not the proto — DeleteChatAction only has messageRange.
pub delete_media: bool,
Expand All @@ -875,6 +947,7 @@ pub struct DeleteChatUpdate {

#[derive(Debug, Clone, Serialize)]
pub struct DeleteMessageForMeUpdate {
/// The chat containing the deleted message.
pub chat_jid: Jid,
pub participant_jid: Option<Jid>,
pub message_id: String,
Expand Down
Loading