func projectsAndRealmsToTrack(s *Service) map[string][]string {
	ridsToProjects := make(map[string][]string)
	for _, roomConfig := range s.Rooms {
		for realmID, realmConfig := range roomConfig.Realms {
			for projectKey, projectConfig := range realmConfig.Projects {
				if projectConfig.Track {
					ridsToProjects[realmID] = append(
						ridsToProjects[realmID], projectKey,
					)
				}
			}
		}
	}
	return ridsToProjects
}
Returns realm_id => [PROJ, ECT, KEYS]
package jira
import (
	"database/sql"
	"errors"
	"fmt"
	"html"
	"net/http"
	"regexp"
	"strings"
	gojira "github.com/andygrunwald/go-jira"
	"github.com/matrix-org/go-neb/database"
	"github.com/matrix-org/go-neb/matrix"
	"github.com/matrix-org/go-neb/realms/jira"
	"github.com/matrix-org/go-neb/realms/jira/urls"
	"github.com/matrix-org/go-neb/services/jira/webhook"
	"github.com/matrix-org/go-neb/services/utils"
	"github.com/matrix-org/go-neb/types"
	log "github.com/sirupsen/logrus"
	mevt "maunium.net/go/mautrix/event"
	"maunium.net/go/mautrix/id"
)
const ServiceType = "jira"
var issueKeyRegex = regexp.MustCompile("([A-z]+)-([0-9]+)")
var projectKeyRegex = regexp.MustCompile("^[A-z]+$")
type Service struct {
	types.DefaultService
	webhookEndpointURL string
	ClientUserID id.UserID
	Rooms map[id.RoomID]struct {
		Realms map[string]struct {
			Projects map[string]struct {
				Expand bool
				Track bool
			}
		}
	}
}
func (s *Service) Register(oldService types.Service, client types.MatrixClient) error {
	for realmID, pkeys := range projectsAndRealmsToTrack(s) {
		realm, err := database.GetServiceDB().LoadAuthRealm(realmID)
		if err != nil {
			return err
		}
		jrealm, ok := realm.(*jira.Realm)
		if !ok {
			return errors.New("Realm ID doesn't map to a JIRA realm")
		}
		if err = webhook.RegisterHook(jrealm, pkeys, s.ClientUserID, s.webhookEndpointURL); err != nil {
			return err
		}
	}
	return nil
}
func (s *Service) cmdJiraCreate(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
	if len(args) <= 1 {
		return nil, errors.New("Missing project key (e.g 'ABC') and/or title")
	}
	if !projectKeyRegex.MatchString(args[0]) {
		return nil, errors.New("Project key must only contain A-Z")
	}
	pkey := strings.ToUpper(args[0])  
	title := args[1]
	desc := ""
	if len(args) == 3 {
		desc = args[2]
	} else if len(args) > 3 {  
		joinedTitle := strings.Join(args[1:], " ")
		title = joinedTitle
	}
	r, err := s.projectToRealm(userID, pkey)
	if err != nil {
		log.WithError(err).Print("Failed to map project key to realm")
		return nil, errors.New("Failed to map project key to a JIRA endpoint")
	}
	if r == nil {
		return nil, errors.New("No known project exists with that project key")
	}
	iss := gojira.Issue{
		Fields: &gojira.IssueFields{
			Summary:     title,
			Description: desc,
			Project: gojira.Project{
				Key: pkey,
			},
			Type: gojira.IssueType{
				Name: "Bug",
			},
		},
	}
	cli, err := r.JIRAClient(userID, false)
	if err != nil {
		if err == sql.ErrNoRows {  
			return matrix.StarterLinkMessage{
				Body: fmt.Sprintf(
					"You need to OAuth with JIRA on %s before you can create issues.",
					r.JIRAEndpoint,
				),
				Link: r.StarterLink,
			}, nil
		}
		return nil, err
	}
	i, res, err := cli.Issue.Create(&iss)
	if err != nil {
		log.WithFields(log.Fields{
			log.ErrorKey: err,
			"user_id":    userID,
			"project":    pkey,
			"realm_id":   r.ID(),
		}).Print("Failed to create issue")
		return nil, errors.New("Failed to create issue")
	}
	if res.StatusCode < 200 || res.StatusCode >= 300 {
		return nil, fmt.Errorf("Failed to create issue: JIRA returned %d", res.StatusCode)
	}
	return &mevt.MessageEventContent{
		MsgType: mevt.MsgNotice,
		Body:    fmt.Sprintf("Created issue: %sbrowse/%s", r.JIRAEndpoint, i.Key),
	}, nil
}
func (s *Service) expandIssue(roomID id.RoomID, userID id.UserID, issueKeyGroups []string) interface{} {
	if len(issueKeyGroups) != 3 {
		log.WithField("groups", issueKeyGroups).Error("Bad number of groups")
		return nil
	}
	issueKey := strings.ToUpper(issueKeyGroups[0])
	logger := log.WithField("issue_key", issueKey)
	projectKey := strings.ToUpper(issueKeyGroups[1])
	realmID := s.realmIDForProject(roomID, projectKey)
	if realmID == "" {
		return nil
	}
	r, err := database.GetServiceDB().LoadAuthRealm(realmID)
	if err != nil {
		logger.WithFields(log.Fields{
			"realm_id":   realmID,
			log.ErrorKey: err,
		}).Print("Failed to load realm")
		return nil
	}
	jrealm, ok := r.(*jira.Realm)
	if !ok {
		logger.WithField("realm_id", realmID).Print(
			"Realm cannot be typecast to jira.Realm",
		)
	}
	logger.WithFields(log.Fields{
		"room_id": roomID,
		"user_id": s.ClientUserID,
	}).Print("Expanding issue")
	cli, err := jrealm.JIRAClient(s.ClientUserID, false)
	if err != nil {
		logger.WithFields(log.Fields{
			log.ErrorKey: err,
			"user_id":    s.ClientUserID,
		}).Print("Failed to retrieve client")
		return nil
	}
	issue, _, err := cli.Issue.Get(issueKey, nil)
	if err != nil {
		logger.WithError(err).Print("Failed to GET issue")
		return err
	}
	return utils.StrippedHTMLMessage(
		mevt.MsgNotice,
		fmt.Sprintf(
			"%sbrowse/%s : %s",
			jrealm.JIRAEndpoint, issueKey, htmlSummaryForIssue(issue),
		),
	)
}
func (s *Service) Commands(cli types.MatrixClient) []types.Command {
	return []types.Command{
		types.Command{
			Path: []string{"jira", "create"},
			Command: func(roomID id.RoomID, userID id.UserID, args []string) (interface{}, error) {
				return s.cmdJiraCreate(roomID, userID, args)
			},
		},
	}
}
func (s *Service) Expansions(cli types.MatrixClient) []types.Expansion {
	return []types.Expansion{
		{
			Regexp: issueKeyRegex,
			Expand: func(roomID id.RoomID, userID id.UserID, issueKeyGroups []string) interface{} {
				return s.expandIssue(roomID, userID, issueKeyGroups)
			},
		},
	}
}
func (s *Service) OnReceiveWebhook(w http.ResponseWriter, req *http.Request, cli types.MatrixClient) {
	eventProjectKey, event, httpErr := webhook.OnReceiveRequest(req)
	if httpErr != nil {
		log.Print("Failed to handle JIRA webhook")
		w.WriteHeader(httpErr.Code)
		return
	}
	jurl, err := urls.ParseJIRAURL(event.Issue.Self)
	if err != nil {
		log.WithError(err).Print("Failed to parse base JIRA URL")
		w.WriteHeader(500)
		return
	}
	htmlText := htmlForEvent(event, jurl.Base)
	if htmlText == "" {
		log.WithField("project", eventProjectKey).Print("Unable to process event for project")
		w.WriteHeader(200)
		return
	}
	for roomID, roomConfig := range s.Rooms {
		for _, realmConfig := range roomConfig.Realms {
			for pkey, projectConfig := range realmConfig.Projects {
				if pkey != eventProjectKey || !projectConfig.Track {
					continue
				}
				_, msgErr := cli.SendMessageEvent(
					roomID, mevt.EventMessage, utils.StrippedHTMLMessage(mevt.MsgNotice, htmlText),
				)
				if msgErr != nil {
					log.WithFields(log.Fields{
						log.ErrorKey: msgErr,
						"project":    pkey,
						"room_id":    roomID,
					}).Print("Failed to send notice into room")
				}
			}
		}
	}
	w.WriteHeader(200)
}
func (s *Service) realmIDForProject(roomID id.RoomID, projectKey string) string {
	for r, realmConfig := range s.Rooms[roomID].Realms {
		for pkey, projectConfig := range realmConfig.Projects {
			if pkey == projectKey && projectConfig.Expand {
				return r
			}
		}
	}
	return ""
}
func (s *Service) projectToRealm(userID id.UserID, pkey string) (*jira.Realm, error) {
	logger := log.WithFields(log.Fields{
		"user_id": userID,
		"project": pkey,
	})
	knownRealms, err := database.GetServiceDB().LoadAuthRealmsByType("jira")
	if err != nil {
		logger.WithError(err).Print("Failed to load jira auth realms")
		return nil, err
	}
	var queue []*jira.Realm
	var unauthRealms []*jira.Realm
	for _, r := range knownRealms {
		jrealm, ok := r.(*jira.Realm)
		if !ok {
			logger.WithField("realm_id", r.ID()).Print(
				"Failed to type-cast 'jira' type realm into jira.Realm",
			)
			continue
		}
		_, err := database.GetServiceDB().LoadAuthSessionByUser(r.ID(), userID)
		if err != nil {
			if err == sql.ErrNoRows {
				unauthRealms = append(unauthRealms, jrealm)
			} else {
				logger.WithError(err).WithField("realm_id", r.ID()).Print(
					"Failed to load auth sessions for user",
				)
			}
			continue  
		}
		queue = append(queue, jrealm)
	}
	queue = append(queue, unauthRealms...)
	for _, jr := range queue {
		exists, err := jr.ProjectKeyExists(userID, pkey)
		if err != nil {
			logger.WithError(err).WithField("realm_id", jr.ID()).Print(
				"Failed to check if project key exists on this realm.",
			)
			continue  
		}
		if exists {
			logger.Info("Project exists on ", jr.ID())
			return jr, nil
		}
	}
	return nil, nil
}