146 lines
No EOL
5 KiB
TypeScript
146 lines
No EOL
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(); |