Repost.ts raw

   1  import { Event, kinds } from 'nostr-tools'
   2  import { EventId, Pubkey, Timestamp } from '../shared'
   3  
   4  /**
   5   * Repost Entity
   6   *
   7   * Represents a repost (kind 6 or 16) of another Nostr event.
   8   */
   9  export class Repost {
  10    private readonly _embeddedEvent: Event | null
  11  
  12    private constructor(
  13      private readonly _event: Event,
  14      private readonly _targetEventId: EventId,
  15      private readonly _targetAuthor: Pubkey,
  16      embeddedEvent: Event | null
  17    ) {
  18      this._embeddedEvent = embeddedEvent
  19    }
  20  
  21    /**
  22     * Create a Repost from a Nostr Event
  23     */
  24    static fromEvent(event: Event): Repost {
  25      if (event.kind !== kinds.Repost && event.kind !== kinds.GenericRepost) {
  26        throw new Error(`Expected kind ${kinds.Repost} or ${kinds.GenericRepost}, got ${event.kind}`)
  27      }
  28  
  29      // Find the target event (first 'e' tag)
  30      const eTag = event.tags.find((t) => t[0] === 'e')
  31      if (!eTag?.[1]) {
  32        throw new Error('Repost must have an e tag')
  33      }
  34  
  35      // Find the target author (first 'p' tag)
  36      const pTag = event.tags.find((t) => t[0] === 'p')
  37      if (!pTag?.[1]) {
  38        throw new Error('Repost must have a p tag')
  39      }
  40  
  41      const targetEventId = EventId.fromHex(eTag[1])
  42      const targetAuthor = Pubkey.fromHex(pTag[1])
  43  
  44      // Try to parse embedded event from content
  45      let embeddedEvent: Event | null = null
  46      if (event.content) {
  47        try {
  48          embeddedEvent = JSON.parse(event.content) as Event
  49        } catch {
  50          // Content is not valid JSON, that's fine
  51        }
  52      }
  53  
  54      return new Repost(event, targetEventId, targetAuthor, embeddedEvent)
  55    }
  56  
  57    /**
  58     * Try to create a Repost from an Event, returns null if invalid
  59     */
  60    static tryFromEvent(event: Event | null | undefined): Repost | null {
  61      if (!event) return null
  62      try {
  63        return Repost.fromEvent(event)
  64      } catch {
  65        return null
  66      }
  67    }
  68  
  69    /**
  70     * The underlying Nostr event
  71     */
  72    get event(): Event {
  73      return this._event
  74    }
  75  
  76    /**
  77     * The repost's event ID
  78     */
  79    get id(): EventId {
  80      return EventId.fromHex(this._event.id)
  81    }
  82  
  83    /**
  84     * The author who reposted
  85     */
  86    get author(): Pubkey {
  87      return Pubkey.fromHex(this._event.pubkey)
  88    }
  89  
  90    /**
  91     * The event ID being reposted
  92     */
  93    get targetEventId(): EventId {
  94      return this._targetEventId
  95    }
  96  
  97    /**
  98     * The author of the original event
  99     */
 100    get targetAuthor(): Pubkey {
 101      return this._targetAuthor
 102    }
 103  
 104    /**
 105     * When the repost was created
 106     */
 107    get createdAt(): Timestamp {
 108      return Timestamp.fromUnix(this._event.created_at)
 109    }
 110  
 111    /**
 112     * Whether this is a standard repost (kind 6)
 113     */
 114    get isStandardRepost(): boolean {
 115      return this._event.kind === kinds.Repost
 116    }
 117  
 118    /**
 119     * Whether this is a generic repost (kind 16)
 120     */
 121    get isGenericRepost(): boolean {
 122      return this._event.kind === kinds.GenericRepost
 123    }
 124  
 125    /**
 126     * The embedded/quoted event (if included in content)
 127     */
 128    get embeddedEvent(): Event | null {
 129      return this._embeddedEvent
 130    }
 131  
 132    /**
 133     * Whether the repost includes the embedded event
 134     */
 135    get hasEmbeddedEvent(): boolean {
 136      return this._embeddedEvent !== null
 137    }
 138  
 139    /**
 140     * Get the kind of the reposted event (from k tag)
 141     */
 142    get targetKind(): number | undefined {
 143      const kTag = this._event.tags.find((t) => t[0] === 'k')
 144      return kTag?.[1] ? parseInt(kTag[1], 10) : undefined
 145    }
 146  }
 147