Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- **WhatsApp** — History sync after pairing is stored again. The library now delivers the backfill one conversation at a time, so the old bulk handler never ran and the pairing dump was dropped (observed: 775 conversations parsed, 4 rows stored). Progress is logged every 250 messages.

- **Archive** — `void archive <id>` now dismisses the whole context group behind the item (Slack thread, Slack 1-hour channel group, Gmail thread) instead of a single row. The inbox shows one row per context, so archiving only the visible id let an older sibling resurface as the next representative. The response gains `archived_count` (rows newly archived by the call, `0` when it was already archived), and Gmail pushes the group in one `batchModify` request.

### Added
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions crates/void-whatsapp/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ chrono = { workspace = true }

[dev-dependencies]
uuid = { workspace = true }
# Encodes protobuf fixtures for the history-sync tests. Must track the prost
# version wa-rs-proto is built against.
prost = "0.14"
48 changes: 47 additions & 1 deletion crates/void-whatsapp/src/connector/connector_trait.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;

use async_trait::async_trait;
Expand All @@ -15,7 +16,7 @@ use void_core::models::*;
use crate::CONNECTOR_ID;

use super::presence::schedule_unavailable;
use super::sync::{handle_history_sync, handle_message, render_qr};
use super::sync::{handle_history_sync, handle_message, render_qr, store_conversation};
use super::WhatsAppConnector;

#[async_trait]
Expand Down Expand Up @@ -93,6 +94,13 @@ impl Connector for WhatsAppConnector {
let config_id = self.config_id.clone();
let client_holder = Arc::clone(&self.client);
let own_identity_holder = Arc::clone(&self.own_identity);
// Cumulative counter of imported history messages, shared across handler
// calls (one per conversation during a backfill).
let history_count = Arc::new(AtomicU64::new(0));
// wa-rs Bot spawns one tokio task per event. Serialize decode+store so
// a hundreds-of-conversations backfill does not decode every payload
// at once while they convoy on Database's mutex.
let history_gate = Arc::new(tokio::sync::Mutex::new(()));

let mut bot = Bot::builder()
.with_backend(backend)
Expand All @@ -103,6 +111,8 @@ impl Connector for WhatsAppConnector {
let config_id = config_id.clone();
let client_holder = Arc::clone(&client_holder);
let own_identity_holder = Arc::clone(&own_identity_holder);
let history_count = Arc::clone(&history_count);
let history_gate = Arc::clone(&history_gate);
async move {
{
let mut holder = client_holder.lock().await;
Expand Down Expand Up @@ -180,6 +190,42 @@ impl Connector for WhatsAppConnector {
"WhatsApp mute update ignored (mute list is managed in config.toml)"
);
}
Event::JoinedGroup(lazy_conv) => {
// wa-rs 0.2 delivers history sync here, one
// conversation per event, not through
// Event::HistorySync (never dispatched).
//
// Use get(), not conversation(): the latter clears
// conv.messages after decoding to save memory, so it
// would hand us metadata with an empty message list.
// get() keeps the messages and returns None when
// decode yields an empty id (empty or undecodable
// payload).
let _guard = history_gate.lock().await;
let own_identity = own_identity_holder.lock().expect("mutex").clone();
if let Some(conv) = lazy_conv.get() {
match store_conversation(&db, &config_id, &own_identity, conv) {
Ok(0) => {}
Ok(n) => {
let hist =
history_count.fetch_add(n, Ordering::Relaxed) + n;
// Cumulative counter rather than one line per
// conversation: a backfill carries hundreds.
if hist % 250 < n {
eprintln!(
"[whatsapp:{config_id}] history sync: {hist} messages imported"
);
}
}
Err(e) => warn!("Failed to store history conversation: {e}"),
}
} else {
warn!(
connection_id = %config_id,
"skipping history conversation with empty or undecodable id"
);
}
}
Event::HistorySync(history) => {
let own_identity = own_identity_holder.lock().expect("mutex").clone();
let sync_type = history.sync_type;
Expand Down
Loading