openvibers/openvibe.chat Commits

Search engines find Vibe: /vibe in the sitemap and llms.txt, the WebApplication keywords name an AI assistant, and robots.txt keeps crawlers off a person's own Vibe chats and memory (/vibe/) (#189)

OP OpenVibers committed 19 days ago · 48414606f5 · 2 files, +10 −7

Changes

ddiff --git a/server/web/discovery.js b/server/web/discovery.js
iindex 39428af..3d391bf 100644
---- a/server/web/discovery.js
++++ b/server/web/discovery.js
@@@ -28,6 +28,7 @@ async function publicPages() {
const pages = (await rooms.list(null, { limit: 100 })).public;
const out = [
{ path: '/', changefreq: 'hourly', priority: 1.0, lastmod: async () => await newestGlobalMessage() },
++ { path: '/vibe', changefreq: 'weekly', priority: 0.9 },
{ path: '/rooms', changefreq: 'daily', priority: 0.8, lastmod: () => newestRoomActivity(pages) },
{ path: '/servers', changefreq: 'daily', priority: 0.8 },
{ path: '/updates', changefreq: 'daily', priority: 0.5 },
@@@ -68,7 +69,7 @@ function homeJsonLd(config) {
seo.jsonLd.website({ name, url: site, description }),
seo.jsonLd.softwareApp({
name, url: site, description, category: 'SocialNetworkingApplication',
-- keywords: 'chat, chat rooms, community servers, voice channels, video calls, direct messages, AI chat members, openvibe',
++ keywords: 'AI assistant, chat, chat rooms, community servers, voice channels, video calls, direct messages, AI chat members, openvibe',
}),
seo.jsonLd.webPage({ name: 'Global chat', url: `${site}/`, description, siteUrl: site }),
];
@@@ -84,18 +85,20 @@ function createDiscoveryRoutes({ config }) {
// the sitemap. Every previous Disallow is kept.
res.type('text/plain').set('Cache-Control', cache.htmlHeaders({ maxAge: 3600 })).send(seo.robotsTxt({
sitemaps: [abs('/sitemap.xml')],
-- allow: ['/', '/updates', '/developers', '/rooms', '/r/', '/servers', '/s/'],
-- disallow: ['/r/*/settings', '/s/*/settings', '/join/', '/messages', '/settings', '/auth/', '/api/'],
++ allow: ['/', '/vibe', '/updates', '/developers', '/rooms', '/r/', '/servers', '/s/'],
++ // A person's own Vibe chats and memory (/vibe/…) are theirs: never crawled. /vibe itself is the app's front.
++ disallow: ['/r/*/settings', '/s/*/settings', '/join/', '/messages', '/settings', '/auth/', '/api/', '/vibe/'],
}));
});
router.get('/llms.txt', (req, res) => {
res.type('text/plain').set('Cache-Control', cache.htmlHeaders({ maxAge: 3600 })).send(seo.llmsTxt({
name: 'OpenVibe.Chat',
-- summary: 'OpenVibe.Chat: global chat for the whole OpenVibe network, the chat of every stream on OpenVibe.Live, servers with text and voice channels, rooms, direct messages, calls and AI members.',
++ summary: 'OpenVibe.Chat: Vibe, OpenVibe\'s free assistant, global chat for the whole OpenVibe network, the chat of every stream on OpenVibe.Live, servers with text and voice channels, rooms, direct messages, calls and AI members.',
details: 'One OpenVibe account works here and on every other OpenVibe site. Anyone can read the global chat and the public rooms without an account; posting needs a signed-in person. The global chat and the public room pages are readable as plain HTML without JavaScript and update live over the site\'s own WebSocket. Direct messages, room settings and the API are per-person and are never listed here.',
sections: [
{ title: 'Start here', links: [
++ { title: 'Vibe', url: abs('/vibe'), note: 'OpenVibe\'s assistant: searches the web and OpenVibe, reads pages and pictures, remembers what it is asked to, sets up recurring work; chats are private to the signed-in person' },
{ title: 'Global chat', url: abs('/'), note: 'one room for everyone on OpenVibe, newest messages at the end' },
{ title: 'Rooms', url: abs('/rooms'), note: 'every public room, with its topic and activity' },
{ title: 'Servers', url: abs('/servers'), note: 'public servers: groups with text and voice channels, roles and invites' },
ddiff --git a/test/discovery.test.js b/test/discovery.test.js
iindex c56defa..b5f444b 100644
---- a/test/discovery.test.js
++++ b/test/discovery.test.js
@@@ -49,10 +49,10 @@ t('/robots.txt keeps every Disallow and welcomes the search and AI crawlers', as
const r = await req('/robots.txt');
assert.strictEqual(r.status, 200);
assert.match(r.type, /^text\/plain/);
-- for (const d of ['/r/*/settings', '/messages', '/settings', '/auth/', '/api/']) {
++ for (const d of ['/r/*/settings', '/messages', '/settings', '/auth/', '/api/', '/vibe/']) {
assert.ok(r.text.includes(`Disallow: ${d}`), `still disallows ${d}`);
}
-- for (const a of ['/', '/updates', '/rooms', '/r/']) assert.ok(r.text.includes(`Allow: ${a}`), `allows ${a}`);
++ for (const a of ['/', '/vibe', '/updates', '/rooms', '/r/']) assert.ok(r.text.includes(`Allow: ${a}`), `allows ${a}`);
assert.match(r.text, /Sitemap: https:\/\/openvibe\.chat\/sitemap\.xml/);
assert.match(r.text, /User-agent: GPTBot/);
assert.match(r.text, /User-agent: ClaudeBot/);
@@@ -63,7 +63,7 @@ t('/sitemap.xml is the public pages with a real lastmod, never today', async ()
assert.strictEqual(r.status, 200);
assert.match(r.type, /^application\/xml/);
assert.match(r.text, /^<\?xml version="1\.0" encoding="UTF-8"\?>/);
-- for (const p of ['/', '/rooms', '/updates', '/r/night-owls']) {
++ for (const p of ['/', '/vibe', '/rooms', '/updates', '/r/night-owls']) {
assert.ok(r.text.includes(`<loc>${SITE}${p === '/' ? '/' : p}</loc>`), `lists ${p}`);
}
assert.ok(!r.text.includes('private-room'), 'a private room is not in the sitemap');