bobrix

package module
v0.0.16 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 28, 2025 License: MIT Imports: 17 Imported by: 1

README

Go Reference Go Report Card

BOBRIX (Bot Bridge Matrix) - Service Bridge for Matrix Bots

Overview

BOBRIX (Bot Bridge Matrix) is a Go library designed to facilitate interaction with various services through a Matrix client in the form of a bot. BoBRIX abstracts the complexity of integrating multiple services by providing a unified set of interaction contracts and a convenient layer on top of the Matrix client, enabling seamless service integration.

Features
  • Interaction Contracts: Define and manage interaction protocols with various services.
  • Matrix Client Bot: A high-level abstraction over the Matrix client to simplify bot development.
  • Service Integration: Combine interaction contracts and the bot framework to interact with multiple services directly from the Matrix client.

Getting started

Prerequisites

BoBRIX requires Go version 1.21 or above.

BoBRIX Installation

To install BoBRIX, use the following command:

go get -u github.com/tensved/bobrix
Usage

A basic example of creating bobrix Engine:

package main

import (
	"context"
	"github.com/tensved/bobrix"
	"github.com/tensved/bobrix/examples/ada"
	"github.com/tensved/bobrix/mxbot"
	"log/slog"
	"os"
	"os/signal"
	"syscall"
)

func main() {

	engine := bobrix.NewEngine()

	botCredentials := &mxbot.BotCredentials{
		Username:      os.Getenv("MX_BOT_USERNAME"),
		Password:      os.Getenv("MX_BOT_PASSWORD"),
		HomeServerURL: os.Getenv("MX_BOT_HOMESERVER_URL"),
	}
	adaBot, err := ada.NewAdaBot(botCredentials)
	if err != nil {
		panic(err)
	}

	engine.ConnectBot(adaBot)

	ctx := context.Background()

	go func() {
		if err := engine.Run(ctx); err != nil {
			panic(err)
		}
	}()

	go func() {
		ada := engine.Bots()[0]

		slog.Info("starting sync")

		sub := ada.Healthchecker.Subscribe()

		for data := range sub.Sync() {

			slog.Debug("healthcheck", "data", data)
		}

	}()

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)

	<-quit

	if err := engine.Stop(ctx); err != nil {
		slog.Error("failed to stop engine", "error", err)
	}

	slog.Info("service shutdown")
}

A basic creating matrix bot:

package ada

import (
	"github.com/tensved/bobrix"
	"github.com/tensved/bobrix/contracts"
	"github.com/tensved/bobrix/mxbot"
	"log/slog"
)

func NewAdaBot(credentials *mxbot.BotCredentials) (*bobrix.Bobrix, error) {
	bot, err := mxbot.NewDefaultBot("ada", credentials)
	if err != nil {
		return nil, err
	}
	//
	bot.AddCommand(mxbot.NewCommand(
		"ping",
		func(c mxbot.CommandCtx) error {

			return c.TextAnswer("pong")
		}),
	)

	bot.AddEventHandler(
		mxbot.AutoJoinRoomHandler(bot),
	)

	bot.AddEventHandler(
		mxbot.NewLoggerHandler("ada"),
	)

	bobr := bobrix.NewBobrix(bot, bobrix.WithHealthcheck(bobrix.WithAutoSwitch()))

	bobr.SetContractParser(bobrix.DefaultContractParser())

	bobr.SetContractParser(bobrix.AutoRequestParser(&bobrix.AutoParserOpts{
		ServiceName: "ada",
		MethodName:  "generate",
		InputName:   "prompt",
	}))

	bobr.ConnectService(NewADAService("hilltwinssl.singularitynet.io"), func(ctx mxbot.Ctx, r *contracts.MethodResponse) {

		if r.Err != nil {
			slog.Error("failed to process request", "error", r.Err)

			if err := ctx.TextAnswer("error: " + r.Err.Error()); err != nil {
				slog.Error("failed to send message", "error", err)
			}

			return
		}

		answer, ok := r.GetString("answer")
		if !ok {
			answer = "I don't know"
		}

		err := ctx.TextAnswer(answer)

		if err != nil {
			slog.Error("failed to send message", "error", err)
		}
	})

	return bobr, nil
}

Basic example of Service Description:

package ada

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/websocket"
	"github.com/tensved/bobrix/contracts"
	"log/slog"
	"net/url"
)

func NewADAService(adaHost string) *contracts.Service {

	return &contracts.Service{
		Name:        "ada",
		Description: "Ada is an AI language model trained by OpenAI.",
		Methods: map[string]*contracts.Method{
			"generate": {
				Name:        "generate",
				Description: "Generate text using Ada's language model.",
				Inputs: []contracts.Input{
					{
						Name:        "prompt",
						Description: "The prompt to generate text from.",
						Type:        "text",
					},
					{
						Name:         "response_type",
						Type:         "text",
						DefaultValue: "text",
					},
					{
						Name: "audio",
						Type: "audio",
					},
				},
				Outputs: []contracts.Output{
					{
						Name:        "text",
						Description: "The generated text.",
					},
				},
				Handler: NewADAHandler(adaHost),
			},
		},
		Pinger: contracts.NewWSPinger(
			contracts.WSOptions{Host: adaHost, Path: "/", Schema: "wss"},
		),
	}
}

For a more detailed description see Service Example

Documentation

Index

Constants

View Source
const BobrixPromptTag = "bobrix.prompt"

Variables

View Source
var (
	ErrInappropriateMimeType = errors.New("inappropriate MIME type of audiofile")
	ErrDownloadFile          = errors.New("failed to download audiofile")
	ErrParseMXCURI           = errors.New("failed to parse MXC URI")
)

Functions

func ConvertThreadToMessages

func ConvertThreadToMessages(thread *mxbot.MessagesThread, botName string) contracts.Messages

func ImageMessageContractParser

func ImageMessageContractParser(opts *ImageMessageParserOpts) func(evt *event.Event) *ServiceRequest

ImageMessageContractParser - image message contract parser It is required to specify strictly the name of the service and method, as well as the name for Input because when sending an image there is no possibility to specify parameters by text

Types

type AudioMessageParserOpts

type AudioMessageParserOpts struct {
	Downloader  Downloader
	ServiceName string
	MethodName  string
	InputName   string
}

type AutoParserOpts

type AutoParserOpts struct {
	Bot         mxbot.Bot
	ServiceName string
	MethodName  string
	InputName   string
}

type Bobrix

type Bobrix struct {
	Healthchecker Healthcheck
	// contains filtered or unexported fields
}

Bobrix - bot structure It is a connection structure between two components: it manages the bot and service contracts

func NewBobrix

func NewBobrix(mxBot mxbot.Bot, opts ...BobrixOpts) *Bobrix

NewBobrix - Bobrix constructor

func (*Bobrix) Bot

func (bx *Bobrix) Bot() mxbot.Bot

func (*Bobrix) ConnectService

func (bx *Bobrix) ConnectService(service *contracts.Service, handler func(ctx mxbot.Ctx, r *contracts.MethodResponse, extra any))

ConnectService - add service to the bot It is used for adding services It adds handler for processing the events of the service

func (*Bobrix) GetService

func (bx *Bobrix) GetService(name string) (*BobrixService, bool)

GetService - return service by name. If the service is not found, it returns nil It is case-insensitive. All services are stored in lowercase

func (*Bobrix) Name

func (bx *Bobrix) Name() string

func (*Bobrix) Run

func (bx *Bobrix) Run(ctx context.Context) error

func (*Bobrix) Services

func (bx *Bobrix) Services() []*BobrixService

func (*Bobrix) SetContractParser

func (bx *Bobrix) SetContractParser(parser func(evt *event.Event) *ServiceRequest, opts ...ContractParserOpts)

SetContractParser - set contract parser. It is used for parsing events to service requests You can add hooks for pre-call and after-call with ContractParserOpts

func (*Bobrix) Stop

func (bx *Bobrix) Stop(ctx context.Context) error

func (*Bobrix) Use

func (bx *Bobrix) Use(handler mxbot.EventHandler)

Use - add handler to the bot It is used for adding event handlers (like middlewares or any other handler)

type BobrixOpts

type BobrixOpts func(*Bobrix)

func WithHealthcheck

func WithHealthcheck(healthCheckOpts ...HealthcheckOption) BobrixOpts

type BobrixService

type BobrixService struct {
	Service *contracts.Service
	Handler ServiceHandler

	IsOnline bool
}

BobrixService - service for bot Binds service and adds handler for processing

type BobrixStatus

type BobrixStatus struct {
	MatrixStatus Health            `json:"matrix"`
	Services     map[string]Health `json:"services"`
	Health
}

BobrixStatus - status of the bot. Contains the health of the bot and the health of the services

type ContractParser

type ContractParser func(evt *event.Event) *ServiceRequest

func AudioMessageContractParser

func AudioMessageContractParser(opts *AudioMessageParserOpts) ContractParser

AudioMessageContractParser - audio message contract parser It is required to specify strictly the name of the service and method, as well as the name for Input, because when sending a voice message there is no possibility to specify parameters by text

func AutoRequestParser

func AutoRequestParser(opts *AutoParserOpts) ContractParser

AutoRequestParser - auto request parser It is convenient to use it in cases when you need to handle situations when a user sends a message that should be sent immediately by a request

func BobrixContractParser

func BobrixContractParser(bot mxbot.Bot) ContractParser

BobrixContractParser - master contract parser It is used for parsing a request from the bot Request should be in the form of a JSON object with the following structure (ServiceRequest) And it should have a tag "bobrix.prompt" in event.Content

func DefaultContractParser

func DefaultContractParser(bot mxbot.Bot) ContractParser

DefaultContractParser - default contract parser it parses message text and extracts bot name, service name and method name it used specific regular expression for parsing it returns nil if message doesn't match the pattern message pattern example: @{botname} -service:{servicename} -method:{methodname} -{input1}:"{inputvalue1}" -{input2}:"{inputvalue2}"

type ContractParserOpts added in v0.0.10

type ContractParserOpts struct {
	PreCallHook   func(ctx mxbot.Ctx, req *ServiceRequest) (string, int, error)
	AfterCallHook func(ctx mxbot.Ctx, req *ServiceRequest, resp *contracts.MethodResponse) (string, int, error)
}

ContractParserOpts - options for contract parser You can set hooks for pre-call and after-call For example, you can add logging or validation

type DefaultHealthcheck

type DefaultHealthcheck struct {
	// contains filtered or unexported fields
}

DefaultHealthcheck - default healthcheck implementation

func NewHealthcheck

func NewHealthcheck(bobrix *Bobrix, opts ...HealthcheckOption) *DefaultHealthcheck

NewHealthcheck - healthcheck constructor bobrix - reference to the bot opts - options for healthcheck

func (*DefaultHealthcheck) GetHealth

func (h *DefaultHealthcheck) GetHealth() *BobrixStatus

GetHealth - returns current healthcheck status Returns healthcheck status

func (*DefaultHealthcheck) Subscribe

func (h *DefaultHealthcheck) Subscribe() *Subscriber

Subscribe - subscribe to healthcheck updates Returns subscriber that can be used to check healthcheck updates

func (*DefaultHealthcheck) Unsubscribe

func (h *DefaultHealthcheck) Unsubscribe(sub *Subscriber)

Unsubscribe - unsubscribe from healthcheck updates sub - subscriber to unsubscribe

type Downloader

type Downloader interface {
	Download(ctx context.Context, uri id.ContentURI) ([]byte, error)
}

type Engine

type Engine struct {
	// contains filtered or unexported fields
}

Engine is the main component of the library Bots can be attached to it (see Bobrix). It is also responsible for launching all bots

func NewEngine

func NewEngine() *Engine

NewEngine - Bobrix engine constructor

func (*Engine) Bots

func (e *Engine) Bots() []*Bobrix

Bots - return all bots

func (*Engine) ConnectBot

func (e *Engine) ConnectBot(bot *Bobrix)

ConnectBot - add bot to the engine. It is used for adding bots to the engine

func (*Engine) ConnectService

func (e *Engine) ConnectService(service *BobrixService)

ConnectService - add service to the store in engine.

func (*Engine) GetBot

func (e *Engine) GetBot(name string) *Bobrix

GetBot - return bot by name. If the bot is not found, it returns nil

func (*Engine) GetService

func (e *Engine) GetService(name string) *BobrixService

GetService - return service by name. If the service is not found, it returns nil

func (*Engine) Run

func (e *Engine) Run(ctx context.Context) error

Run - launch all bots. It uses semaphore to limit the number of bots that can start (login) at the same time

func (*Engine) Services

func (e *Engine) Services() []*BobrixService

Services - return all services

func (*Engine) Stop

func (e *Engine) Stop(ctx context.Context) error

Stop - stop all bots

type Health

type Health struct {
	LastChecked time.Time `json:"last_checked"`
	Status      string    `json:"status" enums:"healthy,unhealthy"` // "healthy" or "unhealthy"
	Error       string    `json:"error,omitempty"`                  // only if status is "unhealthy". contains the error message
}

Health - status of bot and service

type Healthcheck

type Healthcheck interface {
	Subscribe() *Subscriber      // subscribe to healthcheck updates
	Unsubscribe(sub *Subscriber) // unsubscribe from healthcheck updates

	GetHealth() *BobrixStatus // get current healthcheck status
}

Healthcheck - healthcheck interface for bobrix.

type HealthcheckOption

type HealthcheckOption func(h *DefaultHealthcheck)

HealthcheckOption - options for healthcheck. Used in NewHealthcheck

func WithAutoSwitch

func WithAutoSwitch() HealthcheckOption

WithAutoSwitch - turn on option to switch offline/online status for services automatically based on health

func WithInterval

func WithInterval(interval time.Duration) HealthcheckOption

WithInterval - set healthcheck interval. Default value described in defaultInterval

type ImageMessageParserOpts

type ImageMessageParserOpts struct {
	Downloader  mxbot.Bot
	ServiceName string
	MethodName  string
	InputName   string
}

type ServiceHandle

type ServiceHandle func(evt *event.Event) *ServiceRequest

type ServiceHandler

type ServiceHandler func(ctx mxbot.Ctx, r *contracts.MethodResponse, extra any)

type ServiceRequest

type ServiceRequest struct {
	ServiceName string         `json:"service"`
	MethodName  string         `json:"method"`
	InputParams map[string]any `json:"inputs"`
}

type Subscriber

type Subscriber struct {
	// contains filtered or unexported fields
}

Subscriber - subscriber for healthcheck. Receives BobrixStatus updates Can be used to subscribe to healthcheck updates

func NewSubscriber

func NewSubscriber() *Subscriber

NewSubscriber - creates new subscriber. Used to subscribe to healthcheck updates

func (*Subscriber) Close

func (s *Subscriber) Close()

Close - closes subscriber. No more updates will be sent

func (*Subscriber) Read

func (s *Subscriber) Read() *BobrixStatus

Read - reads single BobrixStatus from subscriber. Will block until data is available

func (*Subscriber) Sync

func (s *Subscriber) Sync() <-chan *BobrixStatus

Sync - returns channel with BobrixStatus updates Can be used to subscribe to healthcheck updates

Directories

Path Synopsis
examples
ada
ada/cmd command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL