juni-b-queer / bsky-event-handlers

Typescript based framework for building Bluesky bots. Designed to be easily extendable, this bot framework can be used to make almost any bot you could want.
13 stars 0 forks source link

Add Polling/Interval ability for Handlers, alternative to firehose consumtion #47

Open juni-b-queer opened 1 month ago

juni-b-queer commented 1 month ago

bsky-event-handlers was originally designed to be used for bots that consume the firehose, but there are many times that we would instead want to run actions on an interval, or as a response to external integrations/web hooks.

I'd love to be able to re-use actions and validations, but since they rely so heavily on the firehose, it may require another rewrite. I could also just add an additional possible type to the message parameter in the handle functions so it could be JetstreamMessage or IntervalParameters or WebhookData.

Similar to the JetstreamSubscription class, IntervalSubscription and WebhookSubscription would accept handlers and have standardized functions through abstract Subscription and Handler classes

Abstract Classes

abstract class AbstractSubscription {
   constructor(handlers){
   }

   abstract createSubscription(): self; // This is where the handlers handle functions will be called
}

abstract class AbstractHandler {
   constructor(validators: Array<NewAbstractValidator>, actions: Array<AbstractAction>, handlerAgent: HandlerAgent){}

   abstract async shouldTrigger(message: AbstractMessage): Promise<boolean>;

   abstract runActions(message: AbstractMessage);

   abstract handle(message: AbstractMessage);
}

Interval subscription and handler

class IntervalSubscription extends AbstractSubscription {
   constructor(intervalHandlers: Array<AbstractIntervalHandler>){
   }

   createSubscription(){
      // for each AbstractIntervalHandler, 
      //   create an interval for it based on it's interval time in order to call their handle function
   }
}

abstract class AbstractIntervalHandler {
   protected interval;
   constructor(private validators: Array<NewAbstractValidator>, private actions: Array<AbstractIntervalAction>, private handlerAgent: HandlerAgent, intervalTime: number){}

   startInterval(){
      this.interval = setInterval( () => {
         this.handle() // I'm not sure if we actually need to pass a message here
      }, this.intervalTime);
   }

   stopInterval(){
      clearInterval(this.interval)
   }

  async shouldTrigger(message: IntervalMessage): Promise<boolean> {
    // Run validators 
  }

  async runActions(message: IntervalMessage) {
    for (const action of this.actions) {
      await action.handle(message, this.handlerAgent);
    }
  }

   abstract handle(intervalMessage: IntervalMessage){} // may differ between request method
}

Webhook subscription and handler

class WebhookSubscription extends AbstractSubscription {
   constructor(webhookHandlers: Array<AbstractWebhookHandler>){
   }

   createSubscription(){
      // setup a web server with routes for each webhook handler,
      //   call it's handle function when it's route is called.
   }
}

abstract class AbstractWebhookHandler {
   constructor(private validators: Array<NewAbstractValidator>, private actions: Array<AbstractWebhookAction>, private handlerAgent: HandlerAgent, private route: string, private method: "POST" | "GET" | "PATCH" | "PUT" | "DELETE"){}

  async shouldTrigger(message: JetstreamMessage): Promise<boolean> {
    // Run validators 
  }

  async runActions(message: JetstreamMessage) {
    for (const action of this.actions) {
      await action.handle(message, this.handlerAgent);
    }
  }

   abstract handle(webhookMessage: WebhookMessage){} // may differ between request method
}