func (s *WebhookService) Register(oldService types.Service, client types.MatrixClient) error {
	if s.RealmID == "" || s.ClientUserID == "" {
		return fmt.Errorf("RealmID and ClientUserID is required")
	}
	realm, err := s.loadRealm()
	if err != nil {
		return err
	}
	cli := s.githubClientFor(s.ClientUserID, false)
	if cli == nil {
		return fmt.Errorf(
			"User %s does not have a Github auth session with realm %s", s.ClientUserID, realm.ID())
	}
	var oldRepos []string
	if oldService != nil {
		old, ok := oldService.(*WebhookService)
		if !ok {
			log.WithFields(log.Fields{
				"service_id":   oldService.ServiceID(),
				"service_type": oldService.ServiceType(),
			}).Print("Cannot cast old github service to WebhookService")
		} else {
			oldRepos = old.repoList()
		}
	}
	reposForWebhooks := s.repoList()
	newRepos, removedRepos := difference(reposForWebhooks, oldRepos)
	if len(reposForWebhooks) == 0 && len(removedRepos) == 0 {
		return fmt.Errorf("No webhooks specified")
	}
	for _, r := range newRepos {
		logger := log.WithField("repo", r)
		err := s.createHook(cli, r)
		if err != nil {
			logger.WithError(err).Error("Failed to create webhook")
			return err
		}
		logger.Info("Created webhook")
	}
	if err := s.joinWebhookRooms(client); err != nil {
		return err
	}
	log.Infof("%+v", s)
	return nil
}
Register will create webhooks for the repos specified in Rooms

The hooks made are a delta between the old service and the current configuration. If all webhooks are made,
Register() succeeds. If any webhook fails to be created, Register() fails. A delta is used to allow clients to incrementally
build up the service config without recreating the hooks every time a change is made.

Hooks are deleted when this service receives a webhook event from Github for a repo which has no user configurations.

Hooks can get out of sync if a user manually deletes a hook in the Github UI. In this case, toggling the repo configuration will
force NEB to recreate the hook.
package github
import (
	"context"
	"fmt"
	"net/http"
	"sort"
	"strings"
	gogithub "github.com/google/go-github/github"
	"github.com/matrix-org/go-neb/database"
	"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"
	log "github.com/sirupsen/logrus"
	"maunium.net/go/mautrix/event"
	"maunium.net/go/mautrix/id"
)
const WebhookServiceType = "github-webhook"
type WebhookService struct {
	types.DefaultService
	webhookEndpointURL string
	ClientUserID id.UserID
	RealmID string
	Rooms map[id.RoomID]struct {
		Repos map[string]struct {  
			Events []string
		}
	}
	SecretToken string
}
func (s *WebhookService) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli types.MatrixClient) {
	evType, repo, msg, err := webhook.OnReceiveRequest(req, s.SecretToken)
	if err != nil {
		w.WriteHeader(err.Code)
		return
	}
	logger := log.WithFields(log.Fields{
		"event": evType,
		"repo":  *repo.FullName,
	})
	repoExistsInConfig := false
	for roomID, roomConfig := range s.Rooms {
		for ownerRepo, repoConfig := range roomConfig.Repos {
			if !strings.EqualFold(*repo.FullName, ownerRepo) {
				continue
			}
			repoExistsInConfig = true  
			notifyRoom := false
			for _, notifyType := range repoConfig.Events {
				if evType == notifyType {
					notifyRoom = true
					break
				}
			}
			if notifyRoom {
				logger.WithFields(log.Fields{
					"message": msg,
					"room_id": roomID,
				}).Print("Sending notification to room")
				if _, e := cli.SendMessageEvent(roomID, event.EventMessage, msg); e != nil {
					logger.WithError(e).WithField("room_id", roomID).Print(
						"Failed to send notification to room.")
				}
			}
		}
	}
	if !repoExistsInConfig {
		segs := strings.Split(*repo.FullName, "/")
		if len(segs) != 2 {
			logger.Error("Received event with malformed owner/repo.")
			w.WriteHeader(400)
			return
		}
		if err := s.deleteHook(segs[0], segs[1]); err != nil {
			logger.WithError(err).Print("Failed to delete webhook")
		} else {
			logger.Info("Deleted webhook")
		}
	}
	w.WriteHeader(200)
}