brain/scripts/lib/person-matcher.js

153 lines
4.2 KiB
JavaScript

const { readFileSync, writeFileSync, readdirSync, existsSync } = require('node:fs');
const { resolve } = require('node:path');
const VAULT_ROOT = resolve(__dirname, '..', '..');
const PERSONS_DIR = resolve(VAULT_ROOT, '00 Kontext/Personen');
const COMPANIES_DIR = resolve(VAULT_ROOT, '00 Kontext/Firmen');
function parseFrontmatter(content) {
const match = content.match(/^---\n([\s\S]*?)\n---/);
if (!match) return {};
const fm = {};
for (const line of match[1].split('\n')) {
const colonIdx = line.indexOf(':');
if (colonIdx === -1) continue;
const key = line.substring(0, colonIdx).trim();
let value = line.substring(colonIdx + 1).trim();
if ((value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))) {
value = value.slice(1, -1);
}
if (value.startsWith('[') || value.startsWith('{')) continue;
fm[key] = value;
}
return fm;
}
function loadPersons() {
if (!existsSync(PERSONS_DIR)) return [];
return readdirSync(PERSONS_DIR)
.filter(f => f.endsWith('.md'))
.map(file => {
const content = readFileSync(resolve(PERSONS_DIR, file), 'utf-8');
return { file, fm: parseFrontmatter(content) };
});
}
function loadCompanies() {
if (!existsSync(COMPANIES_DIR)) return [];
return readdirSync(COMPANIES_DIR)
.filter(f => f.endsWith('.md'))
.map(file => {
const content = readFileSync(resolve(COMPANIES_DIR, file), 'utf-8');
return { file, fm: parseFrontmatter(content) };
});
}
function matchAttendeeToPersons(attendee, persons) {
const emailMatch = persons.find(p =>
p.fm.email && p.fm.email.toLowerCase() === attendee.email.toLowerCase()
);
if (emailMatch) {
return { matched: true, file: emailMatch.file, matchType: 'email' };
}
const nameMatch = persons.find(p => {
const fullName = `${p.fm.vorname || ''} ${p.fm.nachname || ''}`.trim().toLowerCase();
return fullName && fullName === attendee.name.toLowerCase();
});
if (nameMatch) {
return { matched: true, file: nameMatch.file, matchType: 'name' };
}
return { matched: false };
}
function resolveCompanyFromDomain(email, companies) {
const domain = email.split('@')[1]?.toLowerCase();
if (!domain) return '';
const match = companies.find(c =>
c.fm.domain && c.fm.domain.toLowerCase() === domain
);
if (match) {
const name = match.file.replace('.md', '');
return `[[00 Kontext/Firmen/${name}]]`;
}
return domain;
}
function splitName(displayName) {
const parts = displayName.trim().split(/\s+/);
if (parts.length === 1) {
return { vorname: parts[0], nachname: '' };
}
const nachname = parts.pop();
return { vorname: parts.join(' '), nachname };
}
function buildNewPersonNote({ name, email, firma }) {
const { vorname, nachname } = splitName(name);
return `---
tags: [person]
vorname: ${vorname}
nachname: ${nachname ? nachname : '""'}
email: ${email}
kategorie: Extern
firma: "${firma}"
status: ungeprüft
---
# ${name}
## Zur Person
- **E-Mail:** ${email}
- **Firma:** ${firma}
## Notizen
`;
}
function createPersonNote({ name, email }, companies) {
const firma = resolveCompanyFromDomain(email, companies);
const content = buildNewPersonNote({ name, email, firma });
const { vorname, nachname } = splitName(name);
const fileName = nachname ? `${vorname} ${nachname}.md` : `${vorname}.md`;
const filePath = resolve(PERSONS_DIR, fileName);
if (!existsSync(filePath)) {
writeFileSync(filePath, content, 'utf-8');
}
return fileName;
}
function resolveAttendees(attendees) {
const persons = loadPersons();
const companies = loadCompanies();
return attendees.map(attendee => {
const match = matchAttendeeToPersons(attendee, persons);
if (match.matched) {
const personName = match.file.replace('.md', '');
return { wikilink: `[[${personName}]]`, name: attendee.name, isNew: false };
}
const fileName = createPersonNote(attendee, companies);
const personName = fileName.replace('.md', '');
return { wikilink: `[[${personName}]]`, name: attendee.name, isNew: true };
});
}
module.exports = {
parseFrontmatter, loadPersons, loadCompanies,
matchAttendeeToPersons, resolveCompanyFromDomain,
buildNewPersonNote, createPersonNote, resolveAttendees
};