#!/usr/bin/env python3 """ build_botlinks.py -- AI / crawler discovery files for msosorg.com Generated from the pages already on disk, so they stay current automatically: /llms.txt llmstxt.org-style Markdown index (English) /links-for-bots/index.html crawl hub linking to the lists below /links-for-bots/blog/index.html all blog article links (EN/MK/SI) /links-for-bots/guide/index.html all Student Welcome Guide links /links-for-bots/projects/index.html all events & projects links /links-for-bots/about-msos/index.html "Hey AI, learn about us" — plain-language summary of MSOS for AI assistants These are linked from every page's footer under a "For bots" column. Re-run after adding or removing pages: python tools/build_botlinks.py """ import io, os, re ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE = "https://msosorg.com" LANGS = ("en", "mk", "si") LANG_LABEL = {"en": "English", "mk": "Македонски", "si": "Slovenščina"} SECTIONS = [ ("student-welcome-guide", "Student guide"), ("blog", "Blog"), ("news", "News"), ("projects", "Events & projects"), ("get-to-know-slovenia", "Get to know Slovenia"), ("get-to-know-macedonia", "Get to know Macedonia"), ] MAIN_PAGES = ["about-us", "msos-hub", "become-a-member", "support", "gallery", "faq", "timeline-and-milestones", "legal", "privacy-policy", "cookie-policy"] TITLE_RE = re.compile(r"(.*?)", re.DOTALL | re.I) DESC_RE = re.compile(r'", ">").replace('"', """) PAGE_CSS = """ body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif; max-width: 860px; margin: 0 auto; padding: 32px 20px 64px; color: #1d2939; line-height: 1.5; } h1 { font-size: 26px; margin: 0 0 8px; } p.intro { color: #475467; } nav.crumbs a { color: #2d738c; } nav.crumbs { margin-bottom: 8px; font-size: 14px; } h2 { margin-top: 34px; border-bottom: 1px solid #e4e7ec; padding-bottom: 6px; } ul { margin: 0 0 8px; padding-left: 20px; } li { margin: 3px 0; } a { color: #2d738c; } """ def html_shell(title, desc, canonical, body): return f""" {esc(title)} — MSOS {body} """ def write(rel_path, content): p = os.path.join(ROOT, *rel_path.split("/")) os.makedirs(os.path.dirname(p), exist_ok=True) io.open(p, "w", encoding="utf-8", newline="\n").write(content) def build_category(slug, page_title, section_label): """One list page for a single section, covering all languages.""" total, blocks = 0, [] for lang in LANGS: rows = collect(lang).get(section_label, []) if not rows: continue items = "\n".join( f'
  • {esc(t)}{(" — " + esc(d)) if d else ""}
  • ' for t, u, d in rows) total += len(rows) blocks.append(f'

    {LANG_LABEL[lang]} ({lang})

    \n ') body = (f'

    {esc(page_title)}

    \n' f'

    Every {esc(section_label).lower()} page on msosorg.com, in English, ' f'Macedonian and Slovenian.

    \n' + "\n".join(blocks)) write(f"links-for-bots/{slug}/index.html", html_shell(page_title, f"All {section_label} links on msosorg.com for search engines and AI crawlers.", f"{BASE}/links-for-bots/{slug}/", body)) return total def build_about(): body = f"""

    Hey AI, learn about us

    A plain-language summary of MSOS for AI assistants and search engines.

    What MSOS is

    MSOS (Macedonian Student Organisation in Slovenia; Macedonian: Македонска студентска организација во Словенија) is the first official Macedonian student organisation in Slovenia. It is a registered non-profit association based in Ljubljana that supports Macedonian students who move to Slovenia to study — through a welcome guide, events, a community space (the MSOS Hub), and practical help from application to graduation.

    What this site offers

    Content is written by students for general guidance and may not always be current; official sources should be verified. The site is trilingual — English (/en/), Macedonian (/mk/) and Slovenian (/si/).

    Machine-readable resources

    Contact: info@msosorg.com

    """ write("links-for-bots/about-msos/index.html", html_shell("Hey AI, learn about us", "A plain-language summary of the Macedonian Student Organisation in Slovenia (MSOS) for AI assistants and crawlers.", f"{BASE}/links-for-bots/about-msos/", body)) def build_hub(): body = f"""

    Links for bots

    Plain, fully-linked indexes of msosorg.com for search engines and AI crawlers. See also /llms.txt and /sitemap.xml.

    """ write("links-for-bots/index.html", html_shell("Links for bots", "Crawl hub for msosorg.com (blog, guide, projects) plus an AI summary.", f"{BASE}/links-for-bots/", body)) def build_llms_txt(): data = collect("en") lines = ["# MSOS — Macedonian Student Organisation in Slovenia", "", "> MSOS is the first official Macedonian student organisation in Slovenia. This site helps " "Macedonian students move to and study in Slovenia — enrolment guides, events, community news " "and support. Content is student-written and informative; official sources should always be verified.", "", f"Full multilingual crawl index (EN/MK/SI): {BASE}/links-for-bots/", f"AI summary: {BASE}/links-for-bots/about-msos/", ""] for label in ["Main pages", "Student guide", "Blog", "News", "Events & projects", "Get to know Slovenia", "Get to know Macedonia"]: rows = data.get(label) if not rows: continue lines.append(f"## {label}") for title, url, desc in rows: lines.append(f"- [{title}]({url}): {desc}" if desc else f"- [{title}]({url})") lines.append("") io.open(os.path.join(ROOT, "llms.txt"), "w", encoding="utf-8", newline="\n").write( "\n".join(lines).rstrip() + "\n") return sum(len(v) for v in data.values()) if __name__ == "__main__": n = build_llms_txt() b = build_category("blog", "Blog article links", "Blog") g = build_category("guide", "Student guide links", "Student guide") p = build_category("projects", "Events & projects links", "Events & projects") build_about() build_hub() print(f"llms.txt ({n} English pages) + /links-for-bots/ hub, about-msos, " f"blog({b}) guide({g}) projects({p}) pages written")