"""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