"""NIP-30 custom emoji: ``emoji`` tags + ``:shortcode:`` in note text.""" import tempfile from pathlib import Path from imwald.core.database import Database from imwald.core.md_render import markdown_html_fragment, markdown_plain_summary from imwald.core.nip30_emoji import ( nip30_emoji_urls_from_tags, parse_kind30030_a_coordinate, preprocess_nip30_emoji_markdown, ) from imwald.core.nostr_crypto import build_signed_event, pubkey_hex_from_secret def test_urls_from_tags() -> None: tags = [ ["emoji", "100percent", "https://example.com/a.png"], ["emoji", "bad", "ftp://x/y.png"], ["emoji", "", "https://example.com/z.png"], ["t", "nostr"], ] m = nip30_emoji_urls_from_tags(tags) assert m == {"100percent": "https://example.com/a.png"} def test_preprocess_skips_fenced_blocks() -> None: urls = {"x": "https://example.com/x.png"} md = "```\n:x:\n```\nline :x: end" out = preprocess_nip30_emoji_markdown(md, urls) assert ":x:" in out.split("```")[1] assert ' None: tags = [["emoji", "100percent", "https://example.com/e.png"]] html = markdown_html_fragment("Hello :100percent: world", nip30_tags=tags) assert 'class="nip30-emoji"' in html assert 'src="https://example.com/e.png"' in html assert "Hello" in html and "world" in html def test_parse_kind30030_a_coordinate() -> None: pk = "a" * 64 assert parse_kind30030_a_coordinate(f"30030:{pk}:my-pack") == (pk, "my-pack") assert parse_kind30030_a_coordinate("1:xx:yy") is None assert parse_kind30030_a_coordinate("nope") is None def test_author_kind0_inventory_resolves_shortcode_in_markdown() -> None: """Jumble-style: shortcodes in the note resolve from the author's kind 0 ``emoji`` tags.""" sk = bytes.fromhex("3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683") pk = pubkey_hex_from_secret(sk) k0 = build_signed_event( sk, created_at=1, kind=0, tags=[["emoji", "chadyes_sm", "https://example.com/chad.png"]], content="{}", ) with tempfile.TemporaryDirectory() as td: db = Database(Path(td) / "nip30.sqlite") db.connect() db.upsert_event(k0) html = markdown_html_fragment( "Hi :chadyes_sm: bye", db=db, nip30_tags=None, nip30_author_pubkey=pk, ) assert "nip30-emoji" in html assert "example.com/chad.png" in html def test_event_emoji_tags_override_author_inventory() -> None: sk = bytes.fromhex("3501454135014541350145413501453fefb02227e449e57cf4d3a3ce05378683") pk = pubkey_hex_from_secret(sk) k0 = build_signed_event( sk, created_at=1, kind=0, tags=[["emoji", "x", "https://example.com/from0.png"]], content="{}", ) with tempfile.TemporaryDirectory() as td: db = Database(Path(td) / "nip30b.sqlite") db.connect() db.upsert_event(k0) html = markdown_html_fragment( ":x:", db=db, nip30_tags=[["emoji", "x", "https://example.com/from_note.png"]], nip30_author_pubkey=pk, ) assert "from_note.png" in html assert "from0.png" not in html def test_plain_summary_strips_inline_emoji_img() -> None: """List previews strip HTML; NIP-30 ```` does not leave the shortcode in plain text.""" tags = [["emoji", "x", "https://example.com/x.png"]] s = markdown_plain_summary("Hi :x: bye", max_len=200, nip30_tags=tags) assert "Hi" in s and "bye" in s assert ":x:" not in s