Add emoji parsing and utils

This commit is contained in:
Jon Staab
2025-05-12 15:09:00 -07:00
parent ba48789a89
commit 7ab746c30d
4 changed files with 50 additions and 9 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/content", "name": "@welshman/content",
"version": "0.2.1", "version": "0.2.2",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of utilities for parsing nostr note content.", "description": "A collection of utilities for parsing nostr note content.",
+38 -6
View File
@@ -42,6 +42,7 @@ export enum ParsedType {
Cashu = "cashu", Cashu = "cashu",
Code = "code", Code = "code",
Ellipsis = "ellipsis", Ellipsis = "ellipsis",
Emoji = "emoji",
Event = "event", Event = "event",
Invoice = "invoice", Invoice = "invoice",
Link = "link", Link = "link",
@@ -70,6 +71,17 @@ export type ParsedEllipsis = {
raw: string raw: string
} }
export type ParsedEmojiValue = {
name: string
url?: string
}
export type ParsedEmoji = {
type: ParsedType.Emoji
value: ParsedEmojiValue
raw: string
}
export type ParsedInvoice = { export type ParsedInvoice = {
type: ParsedType.Invoice type: ParsedType.Invoice
value: string value: string
@@ -138,6 +150,7 @@ export type Parsed =
| ParsedCashu | ParsedCashu
| ParsedCode | ParsedCode
| ParsedEllipsis | ParsedEllipsis
| ParsedEmoji
| ParsedEvent | ParsedEvent
| ParsedInvoice | ParsedInvoice
| ParsedLink | ParsedLink
@@ -155,6 +168,7 @@ export const isCashu = (parsed: Parsed): parsed is ParsedCashu => parsed.type ==
export const isCode = (parsed: Parsed): parsed is ParsedCode => parsed.type === ParsedType.Code export const isCode = (parsed: Parsed): parsed is ParsedCode => parsed.type === ParsedType.Code
export const isEllipsis = (parsed: Parsed): parsed is ParsedEllipsis => export const isEllipsis = (parsed: Parsed): parsed is ParsedEllipsis =>
parsed.type === ParsedType.Ellipsis parsed.type === ParsedType.Ellipsis
export const isEmoji = (parsed: Parsed): parsed is ParsedEmoji => parsed.type === ParsedType.Emoji
export const isEvent = (parsed: Parsed): parsed is ParsedEvent => parsed.type === ParsedType.Event export const isEvent = (parsed: Parsed): parsed is ParsedEvent => parsed.type === ParsedType.Event
export const isInvoice = (parsed: Parsed): parsed is ParsedInvoice => export const isInvoice = (parsed: Parsed): parsed is ParsedInvoice =>
parsed.type === ParsedType.Invoice parsed.type === ParsedType.Invoice
@@ -195,18 +209,28 @@ export const parseCashu = (text: string, context: ParseContext): ParsedCashu | v
} }
export const parseCodeBlock = (text: string, context: ParseContext): ParsedCode | void => { export const parseCodeBlock = (text: string, context: ParseContext): ParsedCode | void => {
const [code, value] = text.match(/^```([^]*?)```/i) || [] const [raw, value] = text.match(/^```([^]*?)```/i) || []
if (code) { if (raw) {
return {type: ParsedType.Code, value, raw: code} return {type: ParsedType.Code, value, raw}
} }
} }
export const parseCodeInline = (text: string, context: ParseContext): ParsedCode | void => { export const parseCodeInline = (text: string, context: ParseContext): ParsedCode | void => {
const [code, value] = text.match(/^`(.*?)`/i) || [] const [raw, value] = text.match(/^`(.*?)`/i) || []
if (code) { if (raw) {
return {type: ParsedType.Code, value, raw: code} return {type: ParsedType.Code, value, raw}
}
}
export const parseEmoji = (text: string, context: ParseContext): ParsedEmoji | void => {
const [raw, name] = text.match(/^:(\w+):/i) || []
if (raw) {
const url = context.tags.find(t => t[0] === "emoji" && t[1] === name)?.[2]
return {type: ParsedType.Emoji, value: {name, url}, raw}
} }
} }
@@ -331,6 +355,7 @@ export const parsers = [
parseCodeInline, parseCodeInline,
parseAddress, parseAddress,
parseProfile, parseProfile,
parseEmoji,
parseEvent, parseEvent,
parseCashu, parseCashu,
parseInvoice, parseInvoice,
@@ -407,6 +432,8 @@ export const truncate = (
case ParsedType.Address: case ParsedType.Address:
case ParsedType.Profile: case ParsedType.Profile:
return entityLength return entityLength
case ParsedType.Emoji:
return parsed.value.name.length
default: default:
return parsed.value.length return parsed.value.length
} }
@@ -549,6 +576,8 @@ export const renderCode = (p: ParsedCode, r: Renderer) => r.addText(p.value)
export const renderEllipsis = (p: ParsedEllipsis, r: Renderer) => r.addText("…") export const renderEllipsis = (p: ParsedEllipsis, r: Renderer) => r.addText("…")
export const renderEmoji = (p: ParsedEmoji, r: Renderer) => r.addText(p.raw)
export const renderInvoice = (p: ParsedInvoice, r: Renderer) => export const renderInvoice = (p: ParsedInvoice, r: Renderer) =>
r.addLink("lightning:" + p.value, p.value.slice(0, 16) + "…") r.addLink("lightning:" + p.value, p.value.slice(0, 16) + "…")
@@ -584,6 +613,9 @@ export const renderOne = (parsed: Parsed, renderer: Renderer) => {
case ParsedType.Ellipsis: case ParsedType.Ellipsis:
renderEllipsis(parsed as ParsedEllipsis, renderer) renderEllipsis(parsed as ParsedEllipsis, renderer)
break break
case ParsedType.Emoji:
renderEmoji(parsed as ParsedEmoji, renderer)
break
case ParsedType.Event: case ParsedType.Event:
renderEvent(parsed as ParsedEvent, renderer) renderEvent(parsed as ParsedEvent, renderer)
break break
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@welshman/util", "name": "@welshman/util",
"version": "0.2.2", "version": "0.2.3",
"author": "hodlbod", "author": "hodlbod",
"license": "MIT", "license": "MIT",
"description": "A collection of nostr-related utilities.", "description": "A collection of nostr-related utilities.",
+10 -1
View File
@@ -1,4 +1,4 @@
import {uniqBy, mapVals, nth, nthEq, ensurePlural} from "@welshman/lib" import {uniqBy, first, spec, mapVals, nth, nthEq, ensurePlural} from "@welshman/lib"
import {isRelayUrl} from "./Relay.js" import {isRelayUrl} from "./Relay.js"
import {Address} from "./Address.js" import {Address} from "./Address.js"
@@ -106,6 +106,15 @@ export const getReplyTags = (tags: string[][]) => {
export const getReplyTagValues = (tags: string[][]) => export const getReplyTagValues = (tags: string[][]) =>
mapVals(tags => tags.map(nth(1)), getReplyTags(tags)) mapVals(tags => tags.map(nth(1)), getReplyTags(tags))
export const getEmojiTags = (name: string, tags: string[][]) => tags.filter(spec(["emoji", name]))
export const getEmojiTag = (name: string, tags: string[][]) => first(getEmojiTags(name, tags))
export const getEmojiTagUrls = (name: string, tags: string[][]) =>
getEmojiTags(name, tags).map(nth(2))
export const getEmojiTagUrl = (name: string, tags: string[][]) => first(getEmojiTagUrls(name, tags))
export const uniqTags = (tags: string[][]) => uniqBy(t => t.slice(0, 2).join(":"), tags) export const uniqTags = (tags: string[][]) => uniqBy(t => t.slice(0, 2).join(":"), tags)
export const tagsFromIMeta = (imeta: string[]) => imeta.map((m: string) => m.split(" ")) export const tagsFromIMeta = (imeta: string[]) => imeta.map((m: string) => m.split(" "))