mirror of
https://github.com/MarekWo/mc-webui.git
synced 2026-03-28 17:42:45 +01:00
refactor(dm): Use native SENT_MSG from meshcore-cli 1.3.12+ instead of custom log
Remove workaround for meshcore-cli 1.3.11 bug where SENT_MSG contained
sender name instead of recipient. Now using native SENT_MSG entries from
.msgs file with correct recipient and sender fields (requires meshcore-cli >= 1.3.12).
Changes:
- Add _parse_sent_msg() to parse SENT_MSG from .msgs file
- Update read_dm_messages() to process both PRIV and SENT_MSG from .msgs
- Remove save_sent_dm(), _read_sent_dm_log(), _parse_sent_dm_entry()
- Remove dm_sent_log_path property from config
- Add _cleanup_old_dm_sent_log() to remove obsolete log file
- Update comments and documentation
Benefits:
- Single source of truth (.msgs file only)
- Simpler codebase (-95 lines)
- No custom workarounds
- Better data consistency
Related: meshcore-cli update to 1.3.12 (commit ad4a7b3)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -42,11 +42,6 @@ class Config:
|
||||
"""Get the full path to archive directory"""
|
||||
return Path(self.MC_ARCHIVE_DIR)
|
||||
|
||||
@property
|
||||
def dm_sent_log_path(self) -> Path:
|
||||
"""Get the full path to our sent DM log file (workaround for meshcore-cli bug)"""
|
||||
return Path(self.MC_CONFIG_DIR) / f"{self.MC_DEVICE_NAME}_dm_sent.jsonl"
|
||||
|
||||
def __repr__(self):
|
||||
return (
|
||||
f"Config(device={self.MC_DEVICE_NAME}, "
|
||||
|
||||
@@ -314,71 +314,38 @@ def delete_channel_messages(channel_idx: int) -> bool:
|
||||
# Direct Messages (DM) Parsing
|
||||
# =============================================================================
|
||||
#
|
||||
# Note: meshcore-cli has a bug where SENT_MSG entries contain the sender's
|
||||
# device name instead of the recipient's name. To work around this, we maintain
|
||||
# our own sent DM log file with correct recipient information.
|
||||
# See: https://github.com/liamcottle/meshcore-cli/issues/XXX
|
||||
# Requires meshcore-cli >= 1.3.12 for correct SENT_MSG format with recipient field.
|
||||
#
|
||||
# Message types:
|
||||
# - PRIV: Incoming private messages (from others to us)
|
||||
# - SENT_MSG: Outgoing private messages (from us to others) - txt_type=0 for DM
|
||||
# =============================================================================
|
||||
|
||||
def save_sent_dm(recipient: str, text: str) -> bool:
|
||||
# Global flag to track if cleanup has been performed
|
||||
_dm_cleanup_done = False
|
||||
|
||||
|
||||
def _cleanup_old_dm_sent_log() -> None:
|
||||
"""
|
||||
Save a sent DM to our own log file (workaround for meshcore-cli bug).
|
||||
Clean up the old _dm_sent.jsonl file that was used as a workaround
|
||||
for meshcore-cli 1.3.11 bug. This file is no longer needed with 1.3.12+.
|
||||
|
||||
Args:
|
||||
recipient: Contact name the message was sent to
|
||||
text: Message content
|
||||
|
||||
Returns:
|
||||
True if saved successfully, False otherwise
|
||||
This function is called once at the first read_dm_messages() invocation.
|
||||
"""
|
||||
dm_log_file = config.dm_sent_log_path
|
||||
global _dm_cleanup_done
|
||||
|
||||
entry = {
|
||||
'timestamp': int(time.time()),
|
||||
'recipient': recipient,
|
||||
'text': text,
|
||||
'status': 'pending'
|
||||
}
|
||||
if _dm_cleanup_done:
|
||||
return
|
||||
|
||||
try:
|
||||
with open(dm_log_file, 'a', encoding='utf-8') as f:
|
||||
f.write(json.dumps(entry, ensure_ascii=False) + '\n')
|
||||
logger.info(f"Saved sent DM to {recipient}")
|
||||
return True
|
||||
dm_log_file = Path(config.MC_CONFIG_DIR) / f"{config.MC_DEVICE_NAME}_dm_sent.jsonl"
|
||||
if dm_log_file.exists():
|
||||
dm_log_file.unlink()
|
||||
logger.info(f"Cleaned up old DM sent log: {dm_log_file}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving sent DM: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def _read_sent_dm_log() -> List[Dict]:
|
||||
"""
|
||||
Read sent DMs from our own log file.
|
||||
|
||||
Returns:
|
||||
List of sent DM entries
|
||||
"""
|
||||
dm_log_file = config.dm_sent_log_path
|
||||
|
||||
if not dm_log_file.exists():
|
||||
return []
|
||||
|
||||
entries = []
|
||||
try:
|
||||
with open(dm_log_file, 'r', encoding='utf-8') as f:
|
||||
for line_num, line in enumerate(f, 1):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
try:
|
||||
data = json.loads(line)
|
||||
entries.append(data)
|
||||
except json.JSONDecodeError as e:
|
||||
logger.warning(f"Invalid JSON in DM log at line {line_num}: {e}")
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f"Error reading sent DM log: {e}")
|
||||
|
||||
return entries
|
||||
logger.warning(f"Could not clean up old DM sent log: {e}")
|
||||
finally:
|
||||
_dm_cleanup_done = True
|
||||
|
||||
|
||||
def _parse_priv_message(line: Dict) -> Optional[Dict]:
|
||||
@@ -428,22 +395,32 @@ def _parse_priv_message(line: Dict) -> Optional[Dict]:
|
||||
}
|
||||
|
||||
|
||||
def _parse_sent_dm_entry(entry: Dict) -> Optional[Dict]:
|
||||
def _parse_sent_msg(line: Dict) -> Optional[Dict]:
|
||||
"""
|
||||
Parse a sent DM entry from our own log file.
|
||||
Parse outgoing private message (SENT_MSG type) from meshcore-cli 1.3.12+.
|
||||
|
||||
This function parses SENT_MSG entries from the .msgs file. As of meshcore-cli 1.3.12,
|
||||
these entries now correctly include both 'recipient' and 'sender' fields.
|
||||
|
||||
Args:
|
||||
entry: Entry from our dm_sent.jsonl file
|
||||
line: Raw JSON object from .msgs file with type='SENT_MSG'
|
||||
|
||||
Returns:
|
||||
Parsed DM dict or None if invalid
|
||||
Parsed DM dict or None if invalid or not a private message
|
||||
"""
|
||||
text = entry.get('text', '').strip()
|
||||
text = line.get('text', '').strip()
|
||||
if not text:
|
||||
return None
|
||||
|
||||
timestamp = entry.get('timestamp', 0)
|
||||
recipient = entry.get('recipient', 'Unknown')
|
||||
# Check txt_type - only process private messages (0), not channel messages (1)
|
||||
txt_type = line.get('txt_type', 0)
|
||||
if txt_type != 0:
|
||||
return None
|
||||
|
||||
timestamp = line.get('timestamp', 0)
|
||||
# Use 'recipient' field (added in meshcore-cli 1.3.12), fallback to 'name'
|
||||
recipient = line.get('recipient', line.get('name', 'Unknown'))
|
||||
sender = line.get('sender', config.MC_DEVICE_NAME)
|
||||
|
||||
# Generate conversation ID from recipient name
|
||||
conversation_id = f"name_{recipient}"
|
||||
@@ -452,18 +429,16 @@ def _parse_sent_dm_entry(entry: Dict) -> Optional[Dict]:
|
||||
text_hash = hash(text[:50]) & 0xFFFFFFFF
|
||||
dedup_key = f"sent_{timestamp}_{text_hash}"
|
||||
|
||||
# Keep the status from log file (pending by default, no ACK tracking available)
|
||||
status = entry.get('status', 'pending')
|
||||
|
||||
return {
|
||||
'type': 'dm',
|
||||
'direction': 'outgoing',
|
||||
'recipient': recipient,
|
||||
'sender': sender,
|
||||
'content': text,
|
||||
'timestamp': timestamp,
|
||||
'datetime': datetime.fromtimestamp(timestamp).isoformat() if timestamp > 0 else None,
|
||||
'is_own': True,
|
||||
'status': status,
|
||||
'txt_type': txt_type,
|
||||
'conversation_id': conversation_id,
|
||||
'dedup_key': dedup_key
|
||||
}
|
||||
@@ -475,10 +450,9 @@ def read_dm_messages(
|
||||
days: Optional[int] = 7
|
||||
) -> Tuple[List[Dict], Dict[str, str]]:
|
||||
"""
|
||||
Read and parse DM messages from .msgs file (incoming) and our sent DM log (outgoing).
|
||||
Read and parse DM messages from .msgs file (both incoming PRIV and outgoing SENT_MSG).
|
||||
|
||||
Note: We ignore SENT_MSG entries from .msgs because they have the wrong recipient
|
||||
due to a bug in meshcore-cli.
|
||||
Requires meshcore-cli >= 1.3.12 for correct SENT_MSG format with recipient field.
|
||||
|
||||
Args:
|
||||
limit: Maximum messages to return (None = all)
|
||||
@@ -493,7 +467,10 @@ def read_dm_messages(
|
||||
seen_dedup_keys = set()
|
||||
pubkey_to_name = {} # Map pubkey_prefix -> most recent name
|
||||
|
||||
# --- Read incoming messages (PRIV) from .msgs file ---
|
||||
# Clean up old DM sent log file (once per session)
|
||||
_cleanup_old_dm_sent_log()
|
||||
|
||||
# --- Read DM messages from .msgs file ---
|
||||
msgs_file = config.msgs_file_path
|
||||
if msgs_file.exists():
|
||||
try:
|
||||
@@ -505,17 +482,21 @@ def read_dm_messages(
|
||||
|
||||
try:
|
||||
data = json.loads(line)
|
||||
msg_type = data.get('type')
|
||||
|
||||
# Only process PRIV messages (incoming DMs)
|
||||
if data.get('type') != 'PRIV':
|
||||
continue
|
||||
# Process PRIV (incoming) and SENT_MSG (outgoing) messages
|
||||
if msg_type == 'PRIV':
|
||||
parsed = _parse_priv_message(data)
|
||||
elif msg_type == 'SENT_MSG':
|
||||
parsed = _parse_sent_msg(data)
|
||||
else:
|
||||
continue # Ignore other message types
|
||||
|
||||
parsed = _parse_priv_message(data)
|
||||
if not parsed:
|
||||
continue
|
||||
|
||||
# Update pubkey->name mapping
|
||||
if parsed.get('pubkey_prefix'):
|
||||
# Update pubkey->name mapping (only for PRIV messages)
|
||||
if msg_type == 'PRIV' and parsed.get('pubkey_prefix'):
|
||||
pubkey_to_name[parsed['pubkey_prefix']] = parsed['sender']
|
||||
|
||||
# Deduplicate
|
||||
@@ -529,26 +510,12 @@ def read_dm_messages(
|
||||
logger.warning(f"Invalid JSON at line {line_num}: {e}")
|
||||
continue
|
||||
except Exception as e:
|
||||
logger.error(f"Error parsing DM at line {line_num}: {e}")
|
||||
logger.error(f"Error parsing message at line {line_num}: {e}")
|
||||
continue
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error reading messages file: {e}")
|
||||
|
||||
# --- Read sent DMs from our own log file ---
|
||||
sent_entries = _read_sent_dm_log()
|
||||
for entry in sent_entries:
|
||||
parsed = _parse_sent_dm_entry(entry)
|
||||
if not parsed:
|
||||
continue
|
||||
|
||||
# Deduplicate
|
||||
if parsed['dedup_key'] in seen_dedup_keys:
|
||||
continue
|
||||
seen_dedup_keys.add(parsed['dedup_key'])
|
||||
|
||||
messages.append(parsed)
|
||||
|
||||
# --- Filter by conversation if specified ---
|
||||
if conversation_id:
|
||||
filtered_messages = []
|
||||
|
||||
@@ -1069,9 +1069,6 @@ def send_dm_message():
|
||||
success, message = cli.send_dm(recipient, text)
|
||||
|
||||
if success:
|
||||
# Save to our own sent DM log (workaround for meshcore-cli bug)
|
||||
parser.save_sent_dm(recipient, text)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'DM sent',
|
||||
|
||||
Reference in New Issue
Block a user