lib/dm.js
Rama main · réplica de lectura del repositorio original.
var fs = require("fs");
var path = require("path");
var config = require("./config");
var DM_DIR = path.join(config.CONFIG_DIR, "dm");
// Ensure dm directory exists
function ensureDmDir() {
fs.mkdirSync(DM_DIR, { recursive: true });
config.chmodSafe(DM_DIR, 0o700);
}
// Generate deterministic DM key from two user IDs (sorted, order-independent)
function dmKey(userId1, userId2) {
return [userId1, userId2].sort().join(":");
}
// File path for a DM conversation
function dmFilePath(key) {
// Replace : with _ for safe filename
return path.join(DM_DIR, key.replace(/:/g, "_") + ".jsonl");
}
// Load DM history from JSONL file
function loadHistory(key) {
var filePath = dmFilePath(key);
if (!fs.existsSync(filePath)) return [];
try {
var content = fs.readFileSync(filePath, "utf8").trim();
if (!content) return [];
var lines = content.split("\n");
var messages = [];
for (var i = 0; i < lines.length; i++) {
if (!lines[i].trim()) continue;
try {
messages.push(JSON.parse(lines[i]));
} catch (e) {
// skip malformed lines
}
}
return messages;
} catch (e) {
return [];
}
}
// Append a message to DM JSONL file
function appendMessage(key, message) {
ensureDmDir();
var filePath = dmFilePath(key);
var line = JSON.stringify(message) + "\n";
fs.appendFileSync(filePath, line);
}
// Open a DM conversation (find or create)
function openDm(userId1, userId2) {
var key = dmKey(userId1, userId2);
var history = loadHistory(key);
return { dmKey: key, messages: history };
}
// Send a DM message
function sendMessage(key, fromUserId, text) {
var message = {
type: "dm_message",
ts: Date.now(),
from: fromUserId,
text: text,
};
appendMessage(key, message);
return message;
}
// Get list of all DM conversations for a user
// Returns: [{ dmKey, otherUserId, lastMessage, lastTs }]
function getDmList(userId) {
ensureDmDir();
var files;
try {
files = fs.readdirSync(DM_DIR).filter(function (f) {
return f.endsWith(".jsonl");
});
} catch (e) {
return [];
}
var dms = [];
for (var i = 0; i < files.length; i++) {
// Reconstruct dmKey from filename (replace _ back to :)
var key = files[i].replace(".jsonl", "").replace(/_/g, ":");
var parts = key.split(":");
if (parts.length !== 2) continue;
// Check if this user is a participant
var idx = parts.indexOf(userId);
if (idx === -1) continue;
var otherUserId = parts[idx === 0 ? 1 : 0];
// Get last message
var messages = loadHistory(key);
var lastMessage = messages.length > 0 ? messages[messages.length - 1] : null;
dms.push({
dmKey: key,
otherUserId: otherUserId,
lastMessage: lastMessage ? lastMessage.text : null,
lastTs: lastMessage ? lastMessage.ts : 0,
messageCount: messages.length,
});
}
// Sort by most recent activity
dms.sort(function (a, b) {
return b.lastTs - a.lastTs;
});
return dms;
}
// Check if a user ID looks like a mate (AI persona) by format only
function isMate(userId) {
var mates = require("./mates");
return mates.isMateIdFormat(userId);
}
module.exports = {
dmKey: dmKey,
openDm: openDm,
sendMessage: sendMessage,
getDmList: getDmList,
loadHistory: loadHistory,
isMate: isMate,
DM_DIR: DM_DIR,
};