""" OPML 2.0 feed list generation for StarPunk Generates OPML 2.0 subscription lists that include all available feed formats (RSS, ATOM, JSON Feed). OPML files allow feed readers to easily subscribe to all feeds from a site. Per v1.1.2 Phase 3: - OPML 2.0 compliant - Lists all three feed formats - Public access (no authentication required per CQ8) - Includes feed discovery link Specification: http://opml.org/spec2.opml """ from datetime import datetime from xml.sax.saxutils import escape def generate_opml(site_url: str, site_name: str) -> str: """ Generate OPML 2.0 feed subscription list. Creates an OPML document listing all available feed formats for the site. Feed readers can import this file to subscribe to all feeds at once. Args: site_url: Base URL of the site (e.g., "https://example.com") site_name: Name of the site (e.g., "My Blog") Returns: OPML 2.0 XML document as string Example: >>> opml = generate_opml("https://example.com", "My Blog") >>> print(opml[:38]) OPML Structure: - version: 2.0 - head: Contains title and creation date - body: Contains outline elements for each feed format - outline attributes: - type: "rss" (used for all syndication formats) - text: Human-readable feed description - xmlUrl: URL to the feed Standards: - OPML 2.0: http://opml.org/spec2.opml - RSS type used for all formats (standard convention) """ # Ensure site_url doesn't have trailing slash site_url = site_url.rstrip('/') # Escape XML special characters in site name safe_site_name = escape(site_name) # RFC 822 date format (required by OPML spec) creation_date = datetime.utcnow().strftime('%a, %d %b %Y %H:%M:%S GMT') # Build OPML document opml_lines = [ '', '', ' ', f' {safe_site_name} Feeds', f' {creation_date}', ' ', ' ', f' ', f' ', f' ', ' ', '', ] return '\n'.join(opml_lines)