@@ -396,6 +396,13 @@ async fn handle_message(
396396 handle_location ( & msg, account_id, reply_to, meta, chat_jid, state) . await ;
397397 } ,
398398 ChannelMessageKind :: Other => {
399+ info ! (
400+ account_id = %state. account_id,
401+ chat = %chat_jid,
402+ "unhandled WhatsApp message type — replying with error. \
403+ Message fields present: {}",
404+ describe_message_fields( & msg) ,
405+ ) ;
399406 let reply_msg = wa:: Message {
400407 conversation : Some (
401408 "Sorry, I can't understand that message type. Check logs for details." . into ( ) ,
@@ -717,6 +724,42 @@ fn is_owner_user(jid: &Jid, own_pn: Option<&Jid>, own_lid: Option<&Jid>) -> bool
717724 || own_lid. is_some_and ( |lid| lid. is_same_user_as ( jid) )
718725}
719726
727+ /// List which `Option` fields on `wa::Message` are `Some`, giving operators a
728+ /// concrete clue about the unhandled message type (e.g. "sticker_message, reaction_message").
729+ ///
730+ /// Only checks fields that are NOT already handled by `classify_message` to avoid
731+ /// misleading output — if a field appears here it genuinely was not dispatched.
732+ fn describe_message_fields ( msg : & wa:: Message ) -> String {
733+ let mut present = Vec :: new ( ) ;
734+ macro_rules! check {
735+ ( $( $field: ident) ,+ $( , ) ?) => {
736+ $( if msg. $field. is_some( ) { present. push( stringify!( $field) ) ; } ) +
737+ } ;
738+ }
739+ // Omit fields already handled by classify_message:
740+ // conversation, extended_text_message, image_message, audio_message,
741+ // video_message, document_message, location_message, live_location_message
742+ check ! (
743+ sender_key_distribution_message,
744+ contact_message,
745+ call,
746+ protocol_message,
747+ contacts_array_message,
748+ sticker_message,
749+ reaction_message,
750+ poll_creation_message,
751+ poll_update_message,
752+ interactive_message,
753+ edited_message,
754+ event_message,
755+ ) ;
756+ if present. is_empty ( ) {
757+ "none" . to_owned ( )
758+ } else {
759+ present. join ( ", " )
760+ }
761+ }
762+
720763/// Classify the inbound message kind based on its content.
721764///
722765/// Media types take priority over text — an image with a caption is still `Photo`,
@@ -949,4 +992,39 @@ mod tests {
949992 assert ! ( is_self_chat) ;
950993 assert ! ( !sender_is_owner) ;
951994 }
995+
996+ #[ test]
997+ fn describe_message_fields_reports_present_fields ( ) {
998+ let msg = wa:: Message {
999+ sticker_message : Some ( Default :: default ( ) ) ,
1000+ reaction_message : Some ( Default :: default ( ) ) ,
1001+ ..Default :: default ( )
1002+ } ;
1003+ let desc = describe_message_fields ( & msg) ;
1004+ assert ! (
1005+ desc. contains( "sticker_message" ) ,
1006+ "expected sticker_message in: {desc}"
1007+ ) ;
1008+ assert ! (
1009+ desc. contains( "reaction_message" ) ,
1010+ "expected reaction_message in: {desc}"
1011+ ) ;
1012+ }
1013+
1014+ #[ test]
1015+ fn describe_message_fields_empty_message_returns_none ( ) {
1016+ let msg = wa:: Message :: default ( ) ;
1017+ assert_eq ! ( describe_message_fields( & msg) , "none" ) ;
1018+ }
1019+
1020+ #[ test]
1021+ fn describe_message_fields_excludes_handled_types ( ) {
1022+ // image_message is handled by classify_message — should NOT appear
1023+ let msg = wa:: Message {
1024+ image_message : Some ( Default :: default ( ) ) ,
1025+ ..Default :: default ( )
1026+ } ;
1027+ let desc = describe_message_fields ( & msg) ;
1028+ assert_eq ! ( desc, "none" , "handled fields should not appear: {desc}" ) ;
1029+ }
9521030}
0 commit comments