@@ -42,6 +42,8 @@ pub struct SyncOptions {
4242 pub concurrency : usize ,
4343 /// If set, only sync this specific chat
4444 pub chat_filter : Option < i64 > ,
45+ /// After sync, prune messages keeping only the N most recent per chat
46+ pub prune_after : Option < usize > ,
4547}
4648
4749/// Get media type string and file extension from grammers Media enum
@@ -134,6 +136,7 @@ pub struct TopicSyncSummary {
134136 pub topic_id : i32 ,
135137 pub topic_name : String ,
136138 pub messages_synced : u64 ,
139+ pub unread_count : i32 ,
137140}
138141
139142/// Summary of messages synced for a single chat
@@ -145,6 +148,8 @@ pub struct ChatSyncSummary {
145148 /// For forum chats, breakdown by topic
146149 #[ serde( skip_serializing_if = "Vec::is_empty" ) ]
147150 pub topics : Vec < TopicSyncSummary > ,
151+ /// Unread message count for this chat at sync time
152+ pub unread_count : i32 ,
148153}
149154
150155pub struct SyncResult {
@@ -998,18 +1003,22 @@ impl App {
9981003 if result. is_forum && !result. topic_counts . is_empty ( ) {
9991004 let mut topic_summaries = Vec :: new ( ) ;
10001005 for ( tid, msg_count) in & result. topic_counts {
1001- let topic_name = self
1006+ let topic = self
10021007 . store
10031008 . get_topic ( result. chat_id , * tid)
10041009 . await
10051010 . ok ( )
1006- . flatten ( )
1011+ . flatten ( ) ;
1012+ let topic_name = topic
1013+ . as_ref ( )
10071014 . map ( |t| t. name . clone ( ) )
10081015 . unwrap_or_else ( || format ! ( "Topic {}" , tid) ) ;
1016+ let unread_count = topic. map ( |t| t. unread_count ) . unwrap_or ( 0 ) ;
10091017 topic_summaries. push ( TopicSyncSummary {
10101018 topic_id : * tid,
10111019 topic_name,
10121020 messages_synced : * msg_count,
1021+ unread_count,
10131022 } ) ;
10141023 }
10151024 topic_summaries
@@ -1028,6 +1037,7 @@ impl App {
10281037 . find ( |t| t. topic_id == new_topic. topic_id )
10291038 {
10301039 existing_topic. messages_synced += new_topic. messages_synced ;
1040+ existing_topic. unread_count = new_topic. unread_count ;
10311041 } else {
10321042 existing. topics . push ( new_topic. clone ( ) ) ;
10331043 }
@@ -1038,6 +1048,7 @@ impl App {
10381048 chat_name : result. chat_name . clone ( ) ,
10391049 messages_synced : result. messages . len ( ) as u64 ,
10401050 topics : new_topics,
1051+ unread_count : 0 , // Not available in msgs-only sync
10411052 } ) ;
10421053 }
10431054 }
@@ -1047,6 +1058,29 @@ impl App {
10471058 chats_processed, messages_stored, concurrency
10481059 ) ;
10491060
1061+ // Prune old messages if --prune-after is set
1062+ if let Some ( keep_count) = opts. prune_after {
1063+ if show_progress {
1064+ eprint ! ( "Pruning old messages (keeping {} per chat)..." , keep_count) ;
1065+ }
1066+ match self . store . prune_all_chats ( keep_count) . await {
1067+ Ok ( deleted) => {
1068+ if show_progress {
1069+ eprint ! ( "\r \x1b [K" ) ;
1070+ }
1071+ if deleted > 0 {
1072+ eprintln ! ( "Pruned {} old messages" , deleted) ;
1073+ }
1074+ }
1075+ Err ( e) => {
1076+ if show_progress {
1077+ eprint ! ( "\r \x1b [K" ) ;
1078+ }
1079+ log:: warn!( "Failed to prune messages: {}" , e) ;
1080+ }
1081+ }
1082+ }
1083+
10501084 // Convert HashMap to Vec and sort topics by message count descending
10511085 let per_chat: Vec < ChatSyncSummary > = per_chat_map
10521086 . into_values ( )
@@ -1154,6 +1188,9 @@ impl App {
11541188 . await ?;
11551189 }
11561190
1191+ // Track unread_count for filtering output later
1192+ let unread_count = extract_unread_count ( & dialog. raw ) ;
1193+
11571194 // Fetch messages for this chat
11581195 let peer_ref = PeerRef :: from ( peer) ;
11591196 let mut message_iter = client. iter_messages ( peer_ref) ;
@@ -1347,18 +1384,17 @@ impl App {
13471384 let new_topics: Vec < TopicSyncSummary > = if is_forum && !topic_counts. is_empty ( ) {
13481385 let mut topic_summaries = Vec :: new ( ) ;
13491386 for ( tid, msg_count) in & topic_counts {
1350- let topic_name = self
1351- . store
1352- . get_topic ( id, * tid)
1353- . await
1354- . ok ( )
1355- . flatten ( )
1387+ let topic = self . store . get_topic ( id, * tid) . await . ok ( ) . flatten ( ) ;
1388+ let topic_name = topic
1389+ . as_ref ( )
13561390 . map ( |t| t. name . clone ( ) )
13571391 . unwrap_or_else ( || format ! ( "Topic {}" , tid) ) ;
1392+ let unread_count = topic. map ( |t| t. unread_count ) . unwrap_or ( 0 ) ;
13581393 topic_summaries. push ( TopicSyncSummary {
13591394 topic_id : * tid,
13601395 topic_name,
13611396 messages_synced : * msg_count,
1397+ unread_count,
13621398 } ) ;
13631399 }
13641400 topic_summaries
@@ -1371,14 +1407,16 @@ impl App {
13711407 . entry ( id)
13721408 . and_modify ( |existing| {
13731409 existing. messages_synced += count as u64 ;
1374- // Merge topics by topic_id
1410+ existing. unread_count = unread_count; // Update with latest unread count
1411+ // Merge topics by topic_id
13751412 for new_topic in & new_topics {
13761413 if let Some ( existing_topic) = existing
13771414 . topics
13781415 . iter_mut ( )
13791416 . find ( |t| t. topic_id == new_topic. topic_id )
13801417 {
13811418 existing_topic. messages_synced += new_topic. messages_synced ;
1419+ existing_topic. unread_count = new_topic. unread_count ;
13821420 } else {
13831421 existing. topics . push ( new_topic. clone ( ) ) ;
13841422 }
@@ -1389,6 +1427,7 @@ impl App {
13891427 chat_name : name. clone ( ) ,
13901428 messages_synced : count as u64 ,
13911429 topics : new_topics,
1430+ unread_count,
13921431 } ) ;
13931432 }
13941433 }
@@ -1608,26 +1647,25 @@ impl App {
16081647 let new_topics: Vec < TopicSyncSummary > = if is_forum && !topic_counts. is_empty ( ) {
16091648 let mut topic_summaries = Vec :: new ( ) ;
16101649 for ( tid, msg_count) in & topic_counts {
1611- let topic_name = self
1612- . store
1613- . get_topic ( id, * tid)
1614- . await
1615- . ok ( )
1616- . flatten ( )
1650+ let topic = self . store . get_topic ( id, * tid) . await . ok ( ) . flatten ( ) ;
1651+ let topic_name = topic
1652+ . as_ref ( )
16171653 . map ( |t| t. name . clone ( ) )
16181654 . unwrap_or_else ( || format ! ( "Topic {}" , tid) ) ;
1655+ let unread_count = topic. map ( |t| t. unread_count ) . unwrap_or ( 0 ) ;
16191656 topic_summaries. push ( TopicSyncSummary {
16201657 topic_id : * tid,
16211658 topic_name,
16221659 messages_synced : * msg_count,
1660+ unread_count,
16231661 } ) ;
16241662 }
16251663 topic_summaries
16261664 } else {
16271665 Vec :: new ( )
16281666 } ;
16291667
1630- // Aggregate into per_chat_map
1668+ // Aggregate into per_chat_map (archived chats don't have unread info)
16311669 per_chat_map
16321670 . entry ( id)
16331671 . and_modify ( |existing| {
@@ -1640,6 +1678,7 @@ impl App {
16401678 . find ( |t| t. topic_id == new_topic. topic_id )
16411679 {
16421680 existing_topic. messages_synced += new_topic. messages_synced ;
1681+ existing_topic. unread_count = new_topic. unread_count ;
16431682 } else {
16441683 existing. topics . push ( new_topic. clone ( ) ) ;
16451684 }
@@ -1650,6 +1689,7 @@ impl App {
16501689 chat_name : name. clone ( ) ,
16511690 messages_synced : count as u64 ,
16521691 topics : new_topics,
1692+ unread_count : 0 , // Archived chats don't have unread info
16531693 } ) ;
16541694 }
16551695 }
@@ -1672,6 +1712,29 @@ impl App {
16721712 ) ;
16731713 }
16741714
1715+ // Prune old messages if --prune-after is set
1716+ if let Some ( keep_count) = opts. prune_after {
1717+ if opts. show_progress {
1718+ eprint ! ( "Pruning old messages (keeping {} per chat)..." , keep_count) ;
1719+ }
1720+ match self . store . prune_all_chats ( keep_count) . await {
1721+ Ok ( deleted) => {
1722+ if opts. show_progress {
1723+ eprint ! ( "\r \x1b [K" ) ;
1724+ }
1725+ if deleted > 0 {
1726+ eprintln ! ( "Pruned {} old messages" , deleted) ;
1727+ }
1728+ }
1729+ Err ( e) => {
1730+ if opts. show_progress {
1731+ eprint ! ( "\r \x1b [K" ) ;
1732+ }
1733+ log:: warn!( "Failed to prune messages: {}" , e) ;
1734+ }
1735+ }
1736+ }
1737+
16751738 // Convert HashMap to Vec and sort topics by message count descending
16761739 let per_chat: Vec < ChatSyncSummary > = per_chat_map
16771740 . into_values ( )
@@ -2006,6 +2069,14 @@ async fn download_message_media_static(
20062069 }
20072070}
20082071
2072+ /// Extract unread_count from a raw Dialog enum
2073+ fn extract_unread_count ( raw : & tl:: enums:: Dialog ) -> i32 {
2074+ match raw {
2075+ tl:: enums:: Dialog :: Dialog ( d) => d. unread_count ,
2076+ tl:: enums:: Dialog :: Folder ( _) => 0 ,
2077+ }
2078+ }
2079+
20092080/// Returns (kind, name, username, is_forum, access_hash)
20102081fn peer_info ( peer : & Peer ) -> ( String , String , Option < String > , bool , Option < i64 > ) {
20112082 match peer {
0 commit comments