@@ -3906,8 +3906,22 @@ class MeshtasticManager {
39063906 if ( isDirectMessage ) {
39073907 channelIndex = - 1 ;
39083908 } else if ( context ?. decryptedBy === 'server' && context ?. decryptedChannelId !== undefined ) {
3909- // Use Channel Database ID + offset for server-decrypted messages
3910- channelIndex = CHANNEL_DB_OFFSET + context . decryptedChannelId ;
3909+ // Check if the database channel's PSK matches a device channel — if so, prefer the device channel
3910+ // This prevents database channels from "shadowing" device channels with the same key (#2375, #2413)
3911+ const dbChannel = await databaseService . channelDatabase . getByIdAsync ( context . decryptedChannelId ) ;
3912+ const deviceChannels = await databaseService . channels . getAllChannels ( ) ;
3913+ const matchingDeviceChannel = dbChannel ?. psk
3914+ ? deviceChannels . find ( dc => dc . psk === dbChannel . psk && dc . role !== 0 )
3915+ : null ;
3916+
3917+ if ( matchingDeviceChannel ) {
3918+ // Device channel has the same PSK — use device channel slot instead of database channel
3919+ channelIndex = matchingDeviceChannel . id ;
3920+ logger . debug ( `📡 Server-decrypted message matches device channel ${ matchingDeviceChannel . id } ("${ matchingDeviceChannel . name } ") — using device channel instead of database channel` ) ;
3921+ } else {
3922+ // No matching device channel — use Channel Database ID + offset
3923+ channelIndex = CHANNEL_DB_OFFSET + context . decryptedChannelId ;
3924+ }
39113925 } else {
39123926 channelIndex = meshPacket . channel !== undefined ? meshPacket . channel : 0 ;
39133927 }
@@ -3974,22 +3988,39 @@ class MeshtasticManager {
39743988 logger . debug ( `💾 Saved channel message from ${ message . fromNodeId } on channel ${ channelIndex } : "${ messageText . substring ( 0 , 30 ) } ..." (replyId: ${ message . replyId } )` ) ;
39753989 }
39763990
3977- // Dual-channel insertion: if server-decrypted and the packet has a radio channel,
3978- // also insert a copy in the radio channel so both views show the message (#2375)
3979- if ( ! isDirectMessage && context ?. decryptedBy === 'server' && meshPacket . channel !== undefined ) {
3980- const radioChannelIndex = meshPacket . channel ;
3981- const radioChannel = await databaseService . channels . getChannelById ( radioChannelIndex ) ;
3982- if ( radioChannel ) {
3983- const radioCopy : TextMessage = {
3991+ // Dual-channel insertion for server-decrypted messages (#2375, #2413)
3992+ // Messages should appear in BOTH the device channel and database channel views
3993+ if ( ! isDirectMessage && context ?. decryptedBy === 'server' && context ?. decryptedChannelId !== undefined ) {
3994+ if ( channelIndex < CHANNEL_DB_OFFSET ) {
3995+ // Primary went to device channel — also insert into database channel
3996+ const dbChannelIndex = CHANNEL_DB_OFFSET + context . decryptedChannelId ;
3997+ const dbCopy : TextMessage = {
39843998 ...message ,
3985- id : `${ message . id } _radio ` ,
3986- channel : radioChannelIndex ,
3999+ id : `${ message . id } _dbchan ` ,
4000+ channel : dbChannelIndex ,
39874001 decryptedBy : 'server' ,
39884002 } ;
3989- const radioInserted = await databaseService . messages . insertMessage ( radioCopy ) ;
3990- if ( radioInserted ) {
3991- dataEventEmitter . emitNewMessage ( radioCopy as any ) ;
3992- logger . debug ( `💾 Also saved to radio channel ${ radioChannelIndex } ("${ radioChannel . name } ")` ) ;
4003+ const dbInserted = await databaseService . messages . insertMessage ( dbCopy ) ;
4004+ if ( dbInserted ) {
4005+ dataEventEmitter . emitNewMessage ( dbCopy as any ) ;
4006+ logger . debug ( `💾 Also saved to database channel ${ dbChannelIndex } ` ) ;
4007+ }
4008+ } else if ( meshPacket . channel !== undefined ) {
4009+ // Primary went to database channel — also insert into radio channel if it exists
4010+ const radioChannelIndex = meshPacket . channel ;
4011+ const radioChannel = await databaseService . channels . getChannelById ( radioChannelIndex ) ;
4012+ if ( radioChannel ) {
4013+ const radioCopy : TextMessage = {
4014+ ...message ,
4015+ id : `${ message . id } _radio` ,
4016+ channel : radioChannelIndex ,
4017+ decryptedBy : 'server' ,
4018+ } ;
4019+ const radioInserted = await databaseService . messages . insertMessage ( radioCopy ) ;
4020+ if ( radioInserted ) {
4021+ dataEventEmitter . emitNewMessage ( radioCopy as any ) ;
4022+ logger . debug ( `💾 Also saved to radio channel ${ radioChannelIndex } ("${ radioChannel . name } ")` ) ;
4023+ }
39934024 }
39944025 }
39954026 }
0 commit comments