feat: страница /kontakty/, robots.txt, ai.txt, llms.txt, sitemap.txt
- /kontakty/ — email, Telegram, ВКонтакте + сетка игровых форумов - Меню обновлено: добавлен пункт «Контакты» - public/robots.txt — статика, разрешено всё; Host для Yandex; перечислены AI-краулеры (GPTBot, ClaudeBot, Google-Extended, CCBot, PerplexityBot, anthropic-ai); ссылки на оба sitemap (XML + TXT) - public/ai.txt — Train/Cite/Quote: yes, контактная почта - public/llms.txt — структура llmstxt.org с описанием проекта, ключевыми страницами, форумами, RSS-фидами, контактами - src/pages/sitemap.txt.ts — endpoint, генерит plain-text список 66 URL (главная + статика + страницы + посты + категории)
This commit is contained in:
13
public/ai.txt
Normal file
13
public/ai.txt
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# AI / LLM training and crawling directives for anotherreflections.ru
|
||||||
|
# Format: see https://github.com/AnthonyJSquires/ai.txt-spec
|
||||||
|
|
||||||
|
User-Agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
# Permission for AI use of public site content
|
||||||
|
Train: yes
|
||||||
|
Cite: yes
|
||||||
|
Quote: yes
|
||||||
|
|
||||||
|
# Contact for questions, takedown requests, partnerships
|
||||||
|
Contact: info@anotherreflections.ru
|
||||||
41
public/llms.txt
Normal file
41
public/llms.txt
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
# Иные Отражения
|
||||||
|
|
||||||
|
> Ролевой проект «Иные Отражения» — независимое сообщество текстовых ролевых игр по мотивам современной фантастики. Восемь игровых вселенных, главные — Дозоры (С. Лукьяненко), Амбер (Р. Желязны), Киндрет (А. Пехов). Онлайн с 2006 года.
|
||||||
|
|
||||||
|
## Что это
|
||||||
|
|
||||||
|
Сайт-портал проекта: новости администрации (50+ публикаций с 2009 года), карточки игровых миров со ссылками на форумы, страницы «О нас» и контактов. Игра идёт на отдельных форумах, портал — точка входа.
|
||||||
|
|
||||||
|
## Ключевые страницы
|
||||||
|
|
||||||
|
- [Главная — лента новостей](https://anotherreflections.ru/)
|
||||||
|
- [О нас](https://anotherreflections.ru/o-nas/)
|
||||||
|
- [Миры — 8 игровых вселенных](https://anotherreflections.ru/miry/)
|
||||||
|
- [Наши друзья](https://anotherreflections.ru/nashi-druzya/)
|
||||||
|
- [Контакты](https://anotherreflections.ru/kontakty/)
|
||||||
|
|
||||||
|
## Игровые форумы
|
||||||
|
|
||||||
|
- [Главный форум](https://forum.anotherreflections.ru/) — общая площадка
|
||||||
|
- [Сумерки Дозоров](https://sumerki.anotherreflections.ru/) — Дозоры (С. Лукьяненко)
|
||||||
|
- [Амбер](https://amber.anotherreflections.ru/) — Янтарное Королевство (Р. Желязны)
|
||||||
|
- [Киндрет](https://kindret.anotherreflections.ru/) — Кровные братья (А. Пехов)
|
||||||
|
- [Ренессанс](https://renessans.anotherreflections.ru/) — историческая фантастика с допущениями мира Сумрака
|
||||||
|
- [Над бездной](https://bezdna.anotherreflections.ru/) — тёмная мистика (в разработке)
|
||||||
|
- [Глубина](https://deep.anotherreflections.ru/) — Дозоры · Глубина (в разработке)
|
||||||
|
- [Warhammer 40k](https://warhammer40k.anotherreflections.ru/) — Сорок первое тысячелетие
|
||||||
|
|
||||||
|
## RSS-фиды
|
||||||
|
|
||||||
|
- [Общий фид](https://anotherreflections.ru/feed.xml) — все новости портала
|
||||||
|
- Фиды по категориям: `https://anotherreflections.ru/category/<slug>/feed.xml`
|
||||||
|
|
||||||
|
## Контакты
|
||||||
|
|
||||||
|
- Электронная почта: info@anotherreflections.ru
|
||||||
|
- Telegram: https://t.me/anotherreflections
|
||||||
|
- ВКонтакте: https://vk.com/anotherreflections
|
||||||
|
|
||||||
|
## Использование
|
||||||
|
|
||||||
|
Контент создан администрацией и участниками проекта. Цитирование и обучение моделей разрешено (см. ai.txt). По вопросам сотрудничества — на электронную почту.
|
||||||
34
public/robots.txt
Normal file
34
public/robots.txt
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# robots.txt for anotherreflections.ru
|
||||||
|
# Static site (Astro), no admin area, no parametric URLs.
|
||||||
|
|
||||||
|
User-agent: *
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: Yandex
|
||||||
|
Allow: /
|
||||||
|
Host: https://anotherreflections.ru
|
||||||
|
|
||||||
|
# AI/LLM crawlers — индексировать разрешено,
|
||||||
|
# для уточнения политики обучения см. ai.txt
|
||||||
|
User-agent: GPTBot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: ClaudeBot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: Google-Extended
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: CCBot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: PerplexityBot
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
User-agent: anthropic-ai
|
||||||
|
Allow: /
|
||||||
|
|
||||||
|
Sitemap: https://anotherreflections.ru/sitemap-index.xml
|
||||||
|
Sitemap: https://anotherreflections.ru/sitemap.txt
|
||||||
|
|
||||||
|
Crawl-delay: 2
|
||||||
@@ -3,6 +3,8 @@ export const SOCIAL = {
|
|||||||
vk: { url: 'https://vk.com/anotherreflections', label: 'ВКонтакте' },
|
vk: { url: 'https://vk.com/anotherreflections', label: 'ВКонтакте' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const CONTACT_EMAIL = 'info@anotherreflections.ru';
|
||||||
|
|
||||||
/** Русская плюрализация: [1, 2-4, 5+]. Пример: plural(8, ['мир', 'мира', 'миров']) → 'миров' */
|
/** Русская плюрализация: [1, 2-4, 5+]. Пример: plural(8, ['мир', 'мира', 'миров']) → 'миров' */
|
||||||
export function plural(n: number, forms: [string, string, string]): string {
|
export function plural(n: number, forms: [string, string, string]): string {
|
||||||
const a = Math.abs(n) % 100;
|
const a = Math.abs(n) % 100;
|
||||||
@@ -127,4 +129,5 @@ export const MAIN_NAV = [
|
|||||||
{ label: 'О нас', href: '/o-nas/' },
|
{ label: 'О нас', href: '/o-nas/' },
|
||||||
{ label: 'Миры', href: '/miry/' },
|
{ label: 'Миры', href: '/miry/' },
|
||||||
{ label: 'Друзья', href: '/nashi-druzya/' },
|
{ label: 'Друзья', href: '/nashi-druzya/' },
|
||||||
|
{ label: 'Контакты', href: '/kontakty/' },
|
||||||
];
|
];
|
||||||
|
|||||||
50
src/pages/kontakty.astro
Normal file
50
src/pages/kontakty.astro
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
---
|
||||||
|
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||||
|
import { CONTACT_EMAIL, SOCIAL, WORLDS } from '../consts';
|
||||||
|
---
|
||||||
|
<BaseLayout
|
||||||
|
title="Контакты"
|
||||||
|
description={`Связаться с проектом «Иные Отражения»: ${CONTACT_EMAIL}, Telegram, ВКонтакте.`}
|
||||||
|
>
|
||||||
|
<section class="hero" style="padding: 4rem 1rem 2rem;">
|
||||||
|
<span class="hero-eyebrow">На связи</span>
|
||||||
|
<h1>Контакты</h1>
|
||||||
|
<p class="hero-tagline">
|
||||||
|
Пишите нам по любым вопросам проекта: предложить идею мира, рассказать о баге форума, договориться о сотрудничестве.
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="contacts">
|
||||||
|
<a class="contact-card" href={`mailto:${CONTACT_EMAIL}`}>
|
||||||
|
<span class="contact-label">Электронная почта</span>
|
||||||
|
<span class="contact-value">{CONTACT_EMAIL}</span>
|
||||||
|
<span class="contact-hint">Основной канал — отвечаем в течение нескольких дней</span>
|
||||||
|
</a>
|
||||||
|
<a class="contact-card" href={SOCIAL.telegram.url} target="_blank" rel="noopener">
|
||||||
|
<span class="contact-label">Telegram-канал</span>
|
||||||
|
<span class="contact-value">@anotherreflections</span>
|
||||||
|
<span class="contact-hint">Анонсы, новости, объявления администрации</span>
|
||||||
|
</a>
|
||||||
|
<a class="contact-card" href={SOCIAL.vk.url} target="_blank" rel="noopener">
|
||||||
|
<span class="contact-label">ВКонтакте</span>
|
||||||
|
<span class="contact-value">vk.com/anotherreflections</span>
|
||||||
|
<span class="contact-hint">Сообщество — здесь же открытое обсуждение</span>
|
||||||
|
</a>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section style="margin-top: 3rem;">
|
||||||
|
<h2 style="font-family: var(--font-serif); font-size: 1.6rem; margin-bottom: 1rem;">Игровые форумы</h2>
|
||||||
|
<p style="color: var(--fg-muted);">Хотите включиться в игру? Заглядывайте на соответствующий форум — там модераторы каждого мира.</p>
|
||||||
|
<ul class="worlds-grid">
|
||||||
|
{WORLDS.map((w) => (
|
||||||
|
<li>
|
||||||
|
<a class="world-card" href={w.url} target="_blank" rel="noopener" style={`--cat-color: ${w.color}`}>
|
||||||
|
<span class="world-tag">{w.tag}</span>
|
||||||
|
<span class="world-name">{w.name}</span>
|
||||||
|
<span class="world-desc">{w.desc}</span>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</BaseLayout>
|
||||||
29
src/pages/sitemap.txt.ts
Normal file
29
src/pages/sitemap.txt.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import type { APIContext } from 'astro';
|
||||||
|
import { getCollection } from 'astro:content';
|
||||||
|
import { SITE_URL } from '../consts';
|
||||||
|
|
||||||
|
export async function GET(_context: APIContext) {
|
||||||
|
const posts = await getCollection('posts');
|
||||||
|
const pages = await getCollection('pages');
|
||||||
|
|
||||||
|
const staticPaths = [
|
||||||
|
'/',
|
||||||
|
'/miry/',
|
||||||
|
'/kontakty/',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Категории — собираем из постов
|
||||||
|
const categorySlugs = new Set<string>();
|
||||||
|
posts.forEach((p) => p.data.categorySlugs.forEach((s) => categorySlugs.add(s)));
|
||||||
|
|
||||||
|
const urls = [
|
||||||
|
...staticPaths.map((p) => new URL(p, SITE_URL).toString()),
|
||||||
|
...pages.map((pg) => new URL(`/${pg.data.slug}/`, SITE_URL).toString()),
|
||||||
|
...posts.map((p) => new URL(`/${p.data.slug}/`, SITE_URL).toString()),
|
||||||
|
...Array.from(categorySlugs).map((s) => new URL(`/category/${s}/`, SITE_URL).toString()),
|
||||||
|
];
|
||||||
|
|
||||||
|
return new Response(urls.join('\n') + '\n', {
|
||||||
|
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -519,6 +519,50 @@ pre {
|
|||||||
background: var(--bg-elev-2);
|
background: var(--bg-elev-2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================== CONTACTS ============================== */
|
||||||
|
.contacts {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 1rem;
|
||||||
|
margin: 1rem 0 0;
|
||||||
|
}
|
||||||
|
.contact-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: .4rem;
|
||||||
|
padding: 1.5rem 1.6rem;
|
||||||
|
background: var(--bg-elev);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--fg);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
transition: border-color .25s, transform .25s, background .25s;
|
||||||
|
}
|
||||||
|
.contact-card:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
background: var(--bg-elev-2);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
border-bottom-color: var(--accent);
|
||||||
|
}
|
||||||
|
.contact-card .contact-label {
|
||||||
|
font-size: .72rem;
|
||||||
|
letter-spacing: 0.18em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--fg-dim);
|
||||||
|
}
|
||||||
|
.contact-card .contact-value {
|
||||||
|
font-family: var(--font-serif);
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #ebe3ff;
|
||||||
|
}
|
||||||
|
.contact-card:hover .contact-value { color: var(--accent); }
|
||||||
|
.contact-card .contact-hint {
|
||||||
|
font-size: .88rem;
|
||||||
|
color: var(--fg-muted);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================== SOCIAL RAIL ============================== */
|
/* ============================== SOCIAL RAIL ============================== */
|
||||||
.social-rail {
|
.social-rail {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|||||||
Reference in New Issue
Block a user