OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-social / hyper-daemon / lib / triggers.js

129 lines · javascript ref:
class TriggerEngine {
  constructor () {
    this._triggers = new Map()
    this._timers = new Map()
  }

  register (id, trigger) {
    this._triggers.set(id, {
      id,
      type: trigger.type,
      filter: trigger.filter || {},
      action: trigger.action,
      enabled: trigger.enabled ?? true
    })
  }

  unregister (id) {
    this._triggers.delete(id)
    if (this._timers.has(id)) {
      clearInterval(this._timers.get(id))
      this._timers.delete(id)
    }
  }

  enable (id) {
    const t = this._triggers.get(id)
    if (t) t.enabled = true
  }

  disable (id) {
    const t = this._triggers.get(id)
    if (t) t.enabled = false
  }

  // Fire event and run matching trigger actions
  async fire (eventType, data) {
    const results = []

    for (const [id, trigger] of this._triggers) {
      if (!trigger.enabled) continue
      if (trigger.type !== eventType) continue

      if (this._matchesFilter(trigger.filter, data)) {
        try {
          const result = await trigger.action(data)
          results.push({ id, ok: true, result })
        } catch (err) {
          results.push({ id, ok: false, error: err.message })
        }
      }
    }

    return results
  }

  _matchesFilter (filter, data) {
    if (!filter || Object.keys(filter).length === 0) return true

    // Channel filter
    if (filter.channel && data.channel !== filter.channel) return false

    // Author filter
    if (filter.author && data.author !== filter.author) return false

    // Content contains filter
    if (filter.contains && !data.text?.includes(filter.contains)) return false

    // Regex pattern filter
    if (filter.pattern) {
      const regex = new RegExp(filter.pattern)
      if (!regex.test(data.text || '')) return false
    }

    // Tag filter
    if (filter.tags) {
      const tags = filter.tags
      const text = (data.text || '').toLowerCase()
      if (!tags.some(tag => text.includes(tag.toLowerCase()))) return false
    }

    return true
  }

  // Start a scheduled trigger (cron-like)
  startSchedule (id, intervalMs, action) {
    this.register(id, {
      type: 'schedule',
      action
    })

    const timer = setInterval(async () => {
      const trigger = this._triggers.get(id)
      if (!trigger || !trigger.enabled) return
      try {
        await trigger.action({ timestamp: Date.now(), triggerId: id })
      } catch {}
    }, intervalMs)

    this._timers.set(id, timer)
  }

  list () {
    const result = []
    for (const [id, t] of this._triggers) {
      result.push({
        id,
        type: t.type,
        enabled: t.enabled,
        hasFilter: Object.keys(t.filter).length > 0
      })
    }
    return result
  }

  get count () {
    return this._triggers.size
  }

  destroy () {
    for (const timer of this._timers.values()) {
      clearInterval(timer)
    }
    this._timers.clear()
    this._triggers.clear()
  }
}

export { TriggerEngine }