diff --git a/src/github.com/matrix-org/go-neb/database/db.go b/src/github.com/matrix-org/go-neb/database/db.go index da4e9a5..cdd6ad6 100644 --- a/src/github.com/matrix-org/go-neb/database/db.go +++ b/src/github.com/matrix-org/go-neb/database/db.go @@ -125,6 +125,19 @@ func (d *ServiceDB) LoadServicesForUser(serviceUserID string) (services []types. return } +// LoadServicesByType loads all the bot services configured for a given type. +// Returns an empty list if there aren't any services configured. +func (d *ServiceDB) LoadServicesByType(serviceType string) (services []types.Service, err error) { + err = runTransaction(d.db, func(txn *sql.Tx) error { + services, err = selectServicesByTypeTxn(txn, serviceType) + if err != nil { + return err + } + return nil + }) + return +} + // StoreService stores a service into the database either by inserting a new // service or updating an existing service. Returns the old service if there // was one. diff --git a/src/github.com/matrix-org/go-neb/database/schema.go b/src/github.com/matrix-org/go-neb/database/schema.go index 2f744ad..d8ae5cd 100644 --- a/src/github.com/matrix-org/go-neb/database/schema.go +++ b/src/github.com/matrix-org/go-neb/database/schema.go @@ -231,6 +231,33 @@ func selectServicesForUserTxn(txn *sql.Tx, userID string) (srvs []types.Service, return } +const selectServicesByTypeSQL = ` +SELECT service_id, service_user_id, service_json FROM services WHERE service_type=$1 ORDER BY service_id +` + +func selectServicesByTypeTxn(txn *sql.Tx, serviceType string) (srvs []types.Service, err error) { + rows, err := txn.Query(selectServicesByTypeSQL, serviceType) + if err != nil { + return + } + defer rows.Close() + for rows.Next() { + var s types.Service + var serviceID string + var serviceUserID string + var serviceJSON []byte + if err = rows.Scan(&serviceID, &serviceUserID, &serviceJSON); err != nil { + return + } + s, err = types.CreateService(serviceID, serviceType, serviceUserID, serviceJSON) + if err != nil { + return + } + srvs = append(srvs, s) + } + return +} + const deleteServiceSQL = ` DELETE FROM services WHERE service_id = $1 ` diff --git a/src/github.com/matrix-org/go-neb/services/echo/echo.go b/src/github.com/matrix-org/go-neb/services/echo/echo.go index 800108d..66a09ff 100644 --- a/src/github.com/matrix-org/go-neb/services/echo/echo.go +++ b/src/github.com/matrix-org/go-neb/services/echo/echo.go @@ -4,20 +4,18 @@ import ( "github.com/matrix-org/go-neb/matrix" "github.com/matrix-org/go-neb/plugin" "github.com/matrix-org/go-neb/types" - "net/http" "strings" ) type echoService struct { + types.DefaultService id string serviceUserID string } -func (e *echoService) ServiceUserID() string { return e.serviceUserID } -func (e *echoService) ServiceID() string { return e.id } -func (e *echoService) ServiceType() string { return "echo" } -func (e *echoService) Register(oldService types.Service, client *matrix.Client) error { return nil } -func (e *echoService) PostRegister(oldService types.Service) {} +func (e *echoService) ServiceUserID() string { return e.serviceUserID } +func (e *echoService) ServiceID() string { return e.id } +func (e *echoService) ServiceType() string { return "echo" } func (e *echoService) Plugin(cli *matrix.Client, roomID string) plugin.Plugin { return plugin.Plugin{ Commands: []plugin.Command{ @@ -30,9 +28,6 @@ func (e *echoService) Plugin(cli *matrix.Client, roomID string) plugin.Plugin { }, } } -func (e *echoService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) { - w.WriteHeader(200) // Do nothing -} func init() { types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service { diff --git a/src/github.com/matrix-org/go-neb/services/giphy/giphy.go b/src/github.com/matrix-org/go-neb/services/giphy/giphy.go index d66c13e..ca9d409 100644 --- a/src/github.com/matrix-org/go-neb/services/giphy/giphy.go +++ b/src/github.com/matrix-org/go-neb/services/giphy/giphy.go @@ -31,6 +31,7 @@ type giphySearch struct { } type giphyService struct { + types.DefaultService id string serviceUserID string APIKey string // beta key is dc6zaTOxFJmzC @@ -39,10 +40,6 @@ type giphyService struct { func (s *giphyService) ServiceUserID() string { return s.serviceUserID } func (s *giphyService) ServiceID() string { return s.id } func (s *giphyService) ServiceType() string { return "giphy" } -func (s *giphyService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) { -} -func (s *giphyService) Register(oldService types.Service, client *matrix.Client) error { return nil } -func (s *giphyService) PostRegister(oldService types.Service) {} func (s *giphyService) Plugin(client *matrix.Client, roomID string) plugin.Plugin { return plugin.Plugin{ diff --git a/src/github.com/matrix-org/go-neb/services/github/github.go b/src/github.com/matrix-org/go-neb/services/github/github.go index 3f25a57..0483e41 100644 --- a/src/github.com/matrix-org/go-neb/services/github/github.go +++ b/src/github.com/matrix-org/go-neb/services/github/github.go @@ -11,7 +11,6 @@ import ( "github.com/matrix-org/go-neb/realms/github" "github.com/matrix-org/go-neb/services/github/client" "github.com/matrix-org/go-neb/types" - "net/http" "regexp" "strconv" "strings" @@ -23,6 +22,7 @@ var ownerRepoIssueRegex = regexp.MustCompile(`(([A-z0-9-_]+)/([A-z0-9-_]+))?#([0 var ownerRepoRegex = regexp.MustCompile(`^([A-z0-9-_]+)/([A-z0-9-_]+)$`) type githubService struct { + types.DefaultService id string serviceUserID string RealmID string @@ -180,9 +180,6 @@ func (s *githubService) Plugin(cli *matrix.Client, roomID string) plugin.Plugin }, } } -func (s *githubService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) { - w.WriteHeader(400) -} // Register will create webhooks for the repos specified in Rooms // @@ -212,8 +209,6 @@ func (s *githubService) Register(oldService types.Service, client *matrix.Client return nil } -func (s *githubService) PostRegister(oldService types.Service) {} - // defaultRepo returns the default repo for the given room, or an empty string. func (s *githubService) defaultRepo(roomID string) string { logger := log.WithFields(log.Fields{ diff --git a/src/github.com/matrix-org/go-neb/services/github/github_webhook.go b/src/github.com/matrix-org/go-neb/services/github/github_webhook.go index 6f6daf3..a87304b 100644 --- a/src/github.com/matrix-org/go-neb/services/github/github_webhook.go +++ b/src/github.com/matrix-org/go-neb/services/github/github_webhook.go @@ -6,7 +6,6 @@ import ( "github.com/google/go-github/github" "github.com/matrix-org/go-neb/database" "github.com/matrix-org/go-neb/matrix" - "github.com/matrix-org/go-neb/plugin" "github.com/matrix-org/go-neb/services/github/client" "github.com/matrix-org/go-neb/services/github/webhook" "github.com/matrix-org/go-neb/types" @@ -17,10 +16,11 @@ import ( ) type githubWebhookService struct { + types.DefaultService id string serviceUserID string webhookEndpointURL string - ClientUserID string // optional; required for webhooks + ClientUserID string RealmID string SecretToken string Rooms map[string]struct { // room_id => {} @@ -33,9 +33,6 @@ type githubWebhookService struct { func (s *githubWebhookService) ServiceUserID() string { return s.serviceUserID } func (s *githubWebhookService) ServiceID() string { return s.id } func (s *githubWebhookService) ServiceType() string { return "github-webhook" } -func (s *githubWebhookService) Plugin(cli *matrix.Client, roomID string) plugin.Plugin { - return plugin.Plugin{} -} func (s *githubWebhookService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) { evType, repo, msg, err := webhook.OnReceiveRequest(req, s.SecretToken) if err != nil { diff --git a/src/github.com/matrix-org/go-neb/services/jira/jira.go b/src/github.com/matrix-org/go-neb/services/jira/jira.go index 97b2529..82eb18e 100644 --- a/src/github.com/matrix-org/go-neb/services/jira/jira.go +++ b/src/github.com/matrix-org/go-neb/services/jira/jira.go @@ -24,6 +24,7 @@ var issueKeyRegex = regexp.MustCompile("([A-z]+)-([0-9]+)") var projectKeyRegex = regexp.MustCompile("^[A-z]+$") type jiraService struct { + types.DefaultService id string serviceUserID string webhookEndpointURL string @@ -38,10 +39,9 @@ type jiraService struct { } } -func (s *jiraService) ServiceUserID() string { return s.serviceUserID } -func (s *jiraService) ServiceID() string { return s.id } -func (s *jiraService) ServiceType() string { return "jira" } -func (s *jiraService) PostRegister(oldService types.Service) {} +func (s *jiraService) ServiceUserID() string { return s.serviceUserID } +func (s *jiraService) ServiceID() string { return s.id } +func (s *jiraService) ServiceType() string { return "jira" } func (s *jiraService) Register(oldService types.Service, client *matrix.Client) error { // We only ever make 1 JIRA webhook which listens for all projects and then filter // on receive. So we simply need to know if we need to make a webhook or not. We diff --git a/src/github.com/matrix-org/go-neb/services/rss/rss.go b/src/github.com/matrix-org/go-neb/services/rss/rss.go new file mode 100644 index 0000000..39c05ce --- /dev/null +++ b/src/github.com/matrix-org/go-neb/services/rss/rss.go @@ -0,0 +1,35 @@ +package services + +import ( + "github.com/matrix-org/go-neb/matrix" + "github.com/matrix-org/go-neb/types" +) + +type rssService struct { + types.DefaultService + id string + serviceUserID string + ClientUserID string `json:"client_user_id"` + Rooms map[string]struct { // room_id => {} + Feeds map[string]struct { // URL => { } + PollIntervalMs int `json:"poll_interval_ms"` + } `json:"feeds"` + } `json:"rooms"` +} + +func (s *rssService) ServiceUserID() string { return s.serviceUserID } +func (s *rssService) ServiceID() string { return s.id } +func (s *rssService) ServiceType() string { return "rss" } + +// Register will check the liveness of each RSS feed given. If all feeds check out okay, no error is returned. +func (s *rssService) Register(oldService types.Service, client *matrix.Client) error { return nil } + +func init() { + types.RegisterService(func(serviceID, serviceUserID, webhookEndpointURL string) types.Service { + r := &rssService{ + id: serviceID, + serviceUserID: serviceUserID, + } + return r + }) +} diff --git a/src/github.com/matrix-org/go-neb/types/types.go b/src/github.com/matrix-org/go-neb/types/types.go index 1011e45..573cb79 100644 --- a/src/github.com/matrix-org/go-neb/types/types.go +++ b/src/github.com/matrix-org/go-neb/types/types.go @@ -40,6 +40,12 @@ type BotOptions struct { Options map[string]interface{} } +// Poller represents a thing that can be polled at a given rate. +type Poller interface { + IntervalSecs() int + OnPoll() +} + // A Service is the configuration for a bot service. type Service interface { ServiceUserID() string @@ -57,6 +63,32 @@ type Service interface { // concurrent modifications to this service whilst this function executes. This lifecycle hook should be used to clean // up resources which are no longer needed (e.g. removing old webhooks). PostRegister(oldService Service) + // Return a Poller object if you wish to be invoked every N seconds. + Poller() Poller +} + +// DefaultService NO-OPs the implementation of optional Service interface methods. Feel free to override them. +type DefaultService struct { + Service +} + +// Plugin returns no plugins. +func (s *DefaultService) Plugin(cli *matrix.Client, roomID string) plugin.Plugin { + return plugin.Plugin{} +} + +// Register does nothing and returns no error. +func (s *DefaultService) Register(oldService Service, client *matrix.Client) error { return nil } + +// PostRegister does nothing. +func (s *DefaultService) PostRegister(oldService Service) {} + +// Poller returns no poller. +func (s *DefaultService) Poller() Poller { return nil } + +// OnReceiveWebhook does nothing but 200 OK the request. +func (s *DefaultService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *matrix.Client) { + w.WriteHeader(200) // Do nothing } var baseURL = ""