146 lines
5 KiB
TypeScript
146 lines
5 KiB
TypeScript
|
|
import PocketBase from 'pocketbase';
|
|||
|
|
|
|||
|
|
const PB_URL = 'http://127.0.0.1:8090';
|
|||
|
|
const pb = new PocketBase(PB_URL);
|
|||
|
|
|
|||
|
|
interface Post {
|
|||
|
|
id: string;
|
|||
|
|
title: string;
|
|||
|
|
content: string;
|
|||
|
|
slug: string;
|
|||
|
|
category: string;
|
|||
|
|
draft: boolean;
|
|||
|
|
description: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function getFirstParagraph(content: string): string {
|
|||
|
|
// Strip HTML tags and get first paragraph
|
|||
|
|
const text = content.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
|
|||
|
|
const paragraphs = text.split(/\n\n/).filter(p => p.trim().length > 20);
|
|||
|
|
return paragraphs[0] || '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function getH2Headings(content: string): string[] {
|
|||
|
|
const h2Matches = content.match(/<h2[^>]*>([^<]+)<\/h2>/gi) || [];
|
|||
|
|
return h2Matches.map(h2 => h2.replace(/<[^>]+>/g, '').trim());
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function analyzePostDetailed(post: Post) {
|
|||
|
|
const firstParagraph = await getFirstParagraph(post.content);
|
|||
|
|
const h2Headings = await getH2Headings(post.content);
|
|||
|
|
|
|||
|
|
// Check for Claim patterns at start
|
|||
|
|
const claimPatterns = [
|
|||
|
|
{ pattern: /^\d+%/i, name: 'Процент' },
|
|||
|
|
{ pattern: /^В\s+\d+\s+/i, name: 'Конкретное число' },
|
|||
|
|
{ pattern: /^\d+\s+(лет|месяцев|дней)/i, name: 'Срок' },
|
|||
|
|
{ pattern: /^Вы можете/i, name: 'Обращение к читателю' },
|
|||
|
|
{ pattern: /^За\s+\d+/i, name: 'За X' },
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
const foundClaim = claimPatterns.find(c => c.pattern.test(firstParagraph.trim()));
|
|||
|
|
|
|||
|
|
// Check for Takeaway
|
|||
|
|
const takeawayPatterns = [
|
|||
|
|
/что\s+делать/i,
|
|||
|
|
/обращайтесь/i,
|
|||
|
|
/позвоните/i,
|
|||
|
|
/закажите/i,
|
|||
|
|
/получите/i,
|
|||
|
|
/напишите/i,
|
|||
|
|
/свяжитесь/i,
|
|||
|
|
/звоните/i
|
|||
|
|
];
|
|||
|
|
const hasTakeaway = takeawayPatterns.some(p => p.test(post.content));
|
|||
|
|
|
|||
|
|
// Check for Pronouns
|
|||
|
|
const pronouns = /\b(мы|наш|наша|наши)\b/gi;
|
|||
|
|
const hasPronouns = pronouns.test(post.content);
|
|||
|
|
|
|||
|
|
// Check for Proof (numbers)
|
|||
|
|
const numbers = post.content.match(/\d+/g) || [];
|
|||
|
|
const hasProof = numbers.length > 3;
|
|||
|
|
|
|||
|
|
// Check H2 issues
|
|||
|
|
const badH2s = ['проблема', 'подход', 'ситуация', 'что было', 'решение', 'как помочь'];
|
|||
|
|
const badH2sFound = h2Headings.filter(h2 =>
|
|||
|
|
badH2s.some(b => h2.toLowerCase().includes(b))
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
id: post.id,
|
|||
|
|
title: post.title,
|
|||
|
|
slug: post.slug,
|
|||
|
|
category: post.category,
|
|||
|
|
firstParagraph: firstParagraph.substring(0, 150) + '...',
|
|||
|
|
h2Headings,
|
|||
|
|
hasClaim: !!foundClaim,
|
|||
|
|
claimType: foundClaim?.name || null,
|
|||
|
|
hasTakeaway,
|
|||
|
|
hasPronouns,
|
|||
|
|
hasProof,
|
|||
|
|
proofCount: numbers.length,
|
|||
|
|
badH2sFound,
|
|||
|
|
recommendations: []
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function main() {
|
|||
|
|
console.log('\n📋 ДЕТАЛЬНЫЙ АНАЛИЗ ПОСТОВ\n');
|
|||
|
|
console.log('='.repeat(70));
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const postsResult = await pb.collection('posts').getList(1, 500);
|
|||
|
|
const posts = postsResult.items as unknown as Post[];
|
|||
|
|
const activePosts = posts.filter(p => !p.draft);
|
|||
|
|
|
|||
|
|
console.log(`Найдено постов: ${activePosts.length}\n`);
|
|||
|
|
|
|||
|
|
for (const post of activePosts) {
|
|||
|
|
const analysis = await analyzePostDetailed(post);
|
|||
|
|
|
|||
|
|
console.log(`\n${'='.repeat(70)}`);
|
|||
|
|
console.log(`📝 ${post.title}`);
|
|||
|
|
console.log(` slug: ${post.slug}`);
|
|||
|
|
console.log(` category: ${post.category}`);
|
|||
|
|
console.log('-'.repeat(70));
|
|||
|
|
|
|||
|
|
console.log(`\n📄 ПЕРВЫЙ АБЗАЦ:`);
|
|||
|
|
console.log(` "${analysis.firstParagraph}"`);
|
|||
|
|
|
|||
|
|
console.log(`\n🔍 CHECKLIST:`);
|
|||
|
|
console.log(` Claim: ${analysis.hasClaim ? '✓' : '✗'} ${analysis.claimType ? `(${analysis.claimType})` : '(нужно добавить факт/число)'}`);
|
|||
|
|
console.log(` Takeaway: ${analysis.hasTakeaway ? '✓' : '✗'}`);
|
|||
|
|
console.log(` Pronouns: ${analysis.hasPronouns ? '✗' : '✓'}`);
|
|||
|
|
console.log(` Proof: ${analysis.hasProof ? '✓' : '✗'} (${analysis.proofCount} чисел)`);
|
|||
|
|
|
|||
|
|
if (analysis.badH2sFound.length > 0) {
|
|||
|
|
console.log(` H2 issues: ${analysis.badH2sFound.join(', ')}`);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Recommendations
|
|||
|
|
console.log(`\n⚠️ РЕКОМЕНДАЦИИ:`);
|
|||
|
|
if (!analysis.hasClaim) {
|
|||
|
|
console.log(` 1. Добавить Claim в первый абзац — начать с цифры или факта`);
|
|||
|
|
console.log(` Пример: "За 2025 год в Сургуте суд вернул права в 73% дел..."`);
|
|||
|
|
}
|
|||
|
|
if (!analysis.hasTakeaway) {
|
|||
|
|
console.log(` 2. Добавить Takeaway — призыв к действию в конце статьи`);
|
|||
|
|
}
|
|||
|
|
if (analysis.hasPronouns) {
|
|||
|
|
console.log(` 3. Убрать местоимения "мы", "наш"`);
|
|||
|
|
}
|
|||
|
|
if (!analysis.hasProof) {
|
|||
|
|
Console.log(` 4. Добавить конкретные цифры и факты`);
|
|||
|
|
}
|
|||
|
|
if (analysis.badH2sFound.length > 0) {
|
|||
|
|
console.log(` 5. Переименовать H2: ${analysis.badH2sFound.join(', ')}`);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Ошибка:', error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
main();
|