You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

159 lines
5.7 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
  1. package types
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "errors"
  6. "net/http"
  7. "strings"
  8. "time"
  9. "github.com/matrix-org/gomatrix"
  10. )
  11. // BotOptions for a given bot user in a given room
  12. type BotOptions struct {
  13. RoomID string
  14. UserID string
  15. SetByUserID string
  16. Options map[string]interface{}
  17. }
  18. // Poller represents a thing which can poll. Services should implement this method signature to support polling.
  19. type Poller interface {
  20. // OnPoll is called when the poller should poll. Return the timestamp when you want to be polled again.
  21. // Return 0 to never be polled again.
  22. OnPoll(client *gomatrix.Client) time.Time
  23. }
  24. // A Service is the configuration for a bot service.
  25. type Service interface {
  26. // Return the user ID of this service.
  27. ServiceUserID() string
  28. // Return an opaque ID used to identify this service.
  29. ServiceID() string
  30. // Return the type of service. This string MUST NOT change.
  31. ServiceType() string
  32. Commands(cli *gomatrix.Client) []Command
  33. Expansions(cli *gomatrix.Client) []Expansion
  34. OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *gomatrix.Client)
  35. // A lifecycle function which is invoked when the service is being registered. The old service, if one exists, is provided,
  36. // along with a Client instance for ServiceUserID(). If this function returns an error, the service will not be registered
  37. // or persisted to the database, and the user's request will fail. This can be useful if you depend on external factors
  38. // such as registering webhooks.
  39. Register(oldService Service, client *gomatrix.Client) error
  40. // A lifecycle function which is invoked after the service has been successfully registered and persisted to the database.
  41. // This function is invoked within the critical section for configuring services, guaranteeing that there will not be
  42. // concurrent modifications to this service whilst this function executes. This lifecycle hook should be used to clean
  43. // up resources which are no longer needed (e.g. removing old webhooks).
  44. PostRegister(oldService Service)
  45. }
  46. // DefaultService NO-OPs the implementation of optional Service interface methods. Feel free to override them.
  47. type DefaultService struct {
  48. id string
  49. serviceUserID string
  50. serviceType string
  51. }
  52. // NewDefaultService creates a new service with implementations for ServiceID(), ServiceType() and ServiceUserID()
  53. func NewDefaultService(serviceID, serviceUserID, serviceType string) DefaultService {
  54. return DefaultService{serviceID, serviceUserID, serviceType}
  55. }
  56. // ServiceID returns the service's ID. In order for this to return the ID, DefaultService MUST have been
  57. // initialised by NewDefaultService, the zero-initialiser is NOT enough.
  58. func (s *DefaultService) ServiceID() string {
  59. return s.id
  60. }
  61. // ServiceUserID returns the user ID that the service sends events as. In order for this to return the
  62. // service user ID, DefaultService MUST have been initialised by NewDefaultService, the zero-initialiser
  63. // is NOT enough.
  64. func (s *DefaultService) ServiceUserID() string {
  65. return s.serviceUserID
  66. }
  67. // ServiceType returns the type of service. See each individual service package for the ServiceType constant
  68. // to find out what this value actually is. In order for this to return the Type, DefaultService MUST have been
  69. // initialised by NewDefaultService, the zero-initialiser is NOT enough.
  70. func (s *DefaultService) ServiceType() string {
  71. return s.serviceType
  72. }
  73. // Commands returns no commands.
  74. func (s *DefaultService) Commands(cli *gomatrix.Client) []Command {
  75. return []Command{}
  76. }
  77. // Expansions returns no expansions.
  78. func (s *DefaultService) Expansions(cli *gomatrix.Client) []Expansion {
  79. return []Expansion{}
  80. }
  81. // Register does nothing and returns no error.
  82. func (s *DefaultService) Register(oldService Service, client *gomatrix.Client) error { return nil }
  83. // PostRegister does nothing.
  84. func (s *DefaultService) PostRegister(oldService Service) {}
  85. // OnReceiveWebhook does nothing but 200 OK the request.
  86. func (s *DefaultService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli *gomatrix.Client) {
  87. w.WriteHeader(200) // Do nothing
  88. }
  89. var baseURL = ""
  90. // BaseURL sets the base URL of NEB to the url given. This URL must be accessible from the
  91. // public internet.
  92. func BaseURL(u string) error {
  93. if u == "" {
  94. return errors.New("BASE_URL not found")
  95. }
  96. if !strings.HasPrefix(u, "http://") && !strings.HasPrefix(u, "https://") {
  97. return errors.New("BASE_URL must start with http[s]://")
  98. }
  99. if !strings.HasSuffix(u, "/") {
  100. u = u + "/"
  101. }
  102. baseURL = u
  103. return nil
  104. }
  105. var servicesByType = map[string]func(string, string, string) Service{}
  106. var serviceTypesWhichPoll = map[string]bool{}
  107. // RegisterService registers a factory for creating Service instances.
  108. func RegisterService(factory func(string, string, string) Service) {
  109. s := factory("", "", "")
  110. servicesByType[s.ServiceType()] = factory
  111. if _, ok := s.(Poller); ok {
  112. serviceTypesWhichPoll[s.ServiceType()] = true
  113. }
  114. }
  115. // PollingServiceTypes returns a list of service types which meet the Poller interface
  116. func PollingServiceTypes() (types []string) {
  117. for t := range serviceTypesWhichPoll {
  118. types = append(types, t)
  119. }
  120. return
  121. }
  122. // CreateService creates a Service of the given type and serviceID.
  123. // Returns an error if the Service couldn't be created.
  124. func CreateService(serviceID, serviceType, serviceUserID string, serviceJSON []byte) (Service, error) {
  125. f := servicesByType[serviceType]
  126. if f == nil {
  127. return nil, errors.New("Unknown service type: " + serviceType)
  128. }
  129. base64ServiceID := base64.RawURLEncoding.EncodeToString([]byte(serviceID))
  130. webhookEndpointURL := baseURL + "services/hooks/" + base64ServiceID
  131. service := f(serviceID, serviceUserID, webhookEndpointURL)
  132. if err := json.Unmarshal(serviceJSON, service); err != nil {
  133. return nil, err
  134. }
  135. return service, nil
  136. }