diff --git a/README.md b/README.md
index 808902b..6952a76 100644
--- a/README.md
+++ b/README.md
@@ -170,6 +170,26 @@ let relaysForEvent = pool.seenOn(
// relaysForEvent will be an array of URLs from relays a given event was seen on
```
+### Parsing references (mentions) from a content using NIP-10 and NIP-27
+
+```js
+import {parseReferences} from 'nostr-tools'
+
+let references = parseReferences(event)
+let simpleAugmentedContent = event.content
+for (let i = 0; i < references.length; i++) {
+ let {text, profile, event, address} = references[i]
+ let augmentedReference = profile
+ ? `@${profilesCache[profile.pubkey].name}`
+ : event
+ ? `${eventsCache[event.id].content.slice(0, 5)}`
+ : address
+ ? `[link]`
+ : text
+ simpleAugmentedContent.replaceAll(text, augmentedReference)
+}
+```
+
### Querying profile data from a NIP-05 address
```js
diff --git a/index.ts b/index.ts
index ac782a3..561a818 100644
--- a/index.ts
+++ b/index.ts
@@ -3,6 +3,7 @@ export * from './relay'
export * from './event'
export * from './filter'
export * from './pool'
+export * from './references'
export * as nip04 from './nip04'
export * as nip05 from './nip05'
diff --git a/references.test.js b/references.test.js
new file mode 100644
index 0000000..e07eedc
--- /dev/null
+++ b/references.test.js
@@ -0,0 +1,62 @@
+/* eslint-env jest */
+
+const {parseReferences} = require('./lib/nostr.cjs')
+
+test('parse mentions', () => {
+ let evt = {
+ tags: [
+ [
+ 'p',
+ 'c9d556c6d3978d112d30616d0d20aaa81410e3653911dd67787b5aaf9b36ade8',
+ 'wss://nostr.com'
+ ],
+ [
+ 'e',
+ 'a84c5de86efc2ec2cff7bad077c4171e09146b633b7ad117fffe088d9579ac33',
+ 'wss://other.com',
+ 'reply'
+ ],
+ [
+ 'e',
+ '31d7c2875b5fc8e6f9c8f9dc1f84de1b6b91d1947ea4c59225e55c325d330fa8',
+ ''
+ ]
+ ],
+ content:
+ 'hello #[0], have you seen #[2]? it was made by nostr:nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg on nostr:nevent1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8ychxp5v4! broken #[3]'
+ }
+
+ expect(parseReferences(evt)).toEqual([
+ {
+ text: '#[0]',
+ profile: {
+ pubkey:
+ 'c9d556c6d3978d112d30616d0d20aaa81410e3653911dd67787b5aaf9b36ade8',
+ relays: ['wss://nostr.com']
+ }
+ },
+ {
+ text: '#[2]',
+ event: {
+ id: '31d7c2875b5fc8e6f9c8f9dc1f84de1b6b91d1947ea4c59225e55c325d330fa8',
+ relays: []
+ }
+ },
+ {
+ text: 'nostr:nprofile1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8yc5usxdg',
+ profile: {
+ pubkey:
+ 'cc6b9fea033f59c3c39a0407c5f1bfee439b077508d918cfdc0d6fd431d39393',
+ relays: []
+ }
+ },
+ {
+ text: 'nostr:nevent1qqsvc6ulagpn7kwrcwdqgp797xl7usumqa6s3kgcelwq6m75x8fe8ychxp5v4',
+ event: {
+ id: 'cc6b9fea033f59c3c39a0407c5f1bfee439b077508d918cfdc0d6fd431d39393',
+ relays: [],
+ author: undefined
+ }
+ }
+ ])
+})
diff --git a/references.ts b/references.ts
new file mode 100644
index 0000000..c5e3e86
--- /dev/null
+++ b/references.ts
@@ -0,0 +1,104 @@
+import {Event} from './event'
+import {decode, AddressPointer, ProfilePointer, EventPointer} from './nip19'
+
+type Reference = {
+ text: string
+ profile?: ProfilePointer
+ event?: EventPointer
+ address?: AddressPointer
+}
+
+const mentionRegex =
+ /\bnostr:((note|npub|naddr|nevent|nprofile)1\w+)\b|#\[(\d+)\]/g
+
+export function parseReferences(evt: Event): Reference[] {
+ let references: Reference[] = []
+ for (let ref of evt.content.matchAll(mentionRegex)) {
+ if (ref[2]) {
+ // it's a NIP-27 mention
+ try {
+ let {type, data} = decode(ref[1])
+ switch (type) {
+ case 'npub': {
+ references.push({
+ text: ref[0],
+ profile: {pubkey: data as string, relays: []}
+ })
+ break
+ }
+ case 'nprofile': {
+ references.push({
+ text: ref[0],
+ profile: data as ProfilePointer
+ })
+ break
+ }
+ case 'note': {
+ references.push({
+ text: ref[0],
+ event: {id: data as string, relays: []}
+ })
+ break
+ }
+ case 'nevent': {
+ references.push({
+ text: ref[0],
+ event: data as EventPointer
+ })
+ break
+ }
+ case 'naddr': {
+ references.push({
+ text: ref[0],
+ address: data as AddressPointer
+ })
+ break
+ }
+ }
+ } catch (err) {
+ /***/
+ }
+ } else if (ref[3]) {
+ // it's a NIP-10 mention
+ let idx = parseInt(ref[3], 10)
+ let tag = evt.tags[idx]
+ if (!tag) continue
+
+ switch (tag[0]) {
+ case 'p': {
+ references.push({
+ text: ref[0],
+ profile: {pubkey: tag[1], relays: tag[2] ? [tag[2]] : []}
+ })
+ break
+ }
+ case 'e': {
+ references.push({
+ text: ref[0],
+ event: {id: tag[1], relays: tag[2] ? [tag[2]] : []}
+ })
+ break
+ }
+ case 'a': {
+ try {
+ let [kind, pubkey, identifier] = ref[1].split(':')
+ references.push({
+ text: ref[0],
+ address: {
+ identifier,
+ pubkey,
+ kind: parseInt(kind, 10),
+ relays: tag[2] ? [tag[2]] : []
+ }
+ })
+ } catch (err) {
+ /***/
+ }
+ break
+ }
+ }
+ }
+ }
+
+ return references
+}