소스 검색

Start of the bot and it's matrix connection

Gary Kramlich 3 년 전
부모
커밋
be3982e585
5개의 변경된 파일209개의 추가작업 그리고 6개의 파일을 삭제
  1. 41 0
      bridge/bot.go
  2. 71 5
      bridge/bridge.go
  3. 87 0
      bridge/matrix.go
  4. 7 0
      config/appservice.go
  5. 3 1
      run/cmd.go

+ 41 - 0
bridge/bot.go

@@ -0,0 +1,41 @@
+package bridge
+
+import (
+	"maunium.net/go/mautrix/id"
+)
+
+func (b *Bridge) updateBotProfile() {
+	cfg := b.config.Appservice.Bot
+
+	// Set the bot's avatar.
+	if cfg.Avatar != "" {
+		var err error
+		var mxc id.ContentURI
+
+		if cfg.Avatar == "remove" {
+			err = b.bot.SetAvatarURL(mxc)
+		} else {
+			mxc, err = id.ParseContentURI(cfg.Avatar)
+			if err == nil {
+				err = b.bot.SetAvatarURL(mxc)
+			}
+		}
+
+		b.log.Warnln("failed to update the bot's avatar: %v", err)
+	}
+
+	// Update the bot's display name.
+	if cfg.Displayname != "" {
+		var err error
+
+		if cfg.Displayname == "remove" {
+			err = b.bot.SetDisplayName("")
+		} else {
+			err = b.bot.SetDisplayName(cfg.Displayname)
+		}
+
+		if err != nil {
+			b.log.Warnln("failed to update the bot's display name: %v", err)
+		}
+	}
+}

+ 71 - 5
bridge/bridge.go

@@ -1,13 +1,22 @@
 package bridge
 
 import (
+	"errors"
+	"fmt"
+	"time"
+
 	log "maunium.net/go/maulogger/v2"
+	"maunium.net/go/mautrix"
 	"maunium.net/go/mautrix/appservice"
 
 	"gitlab.com/beeper/discord/config"
 	"gitlab.com/beeper/discord/version"
 )
 
+const (
+	reconnectDelay = 10 * time.Second
+)
+
 type Bridge struct {
 	config *config.Config
 
@@ -15,6 +24,7 @@ type Bridge struct {
 
 	as             *appservice.AppService
 	eventProcessor *appservice.EventProcessor
+	matrixHandler  *matrixHandler
 	bot            *appservice.IntentAPI
 }
 
@@ -27,27 +37,83 @@ func New(cfg *config.Config) (*Bridge, error) {
 
 	logger.Infoln("Initializing version", version.String)
 
-	// Create the app service.
+	// Create and initalize the app service.
 	appservice, err := cfg.CreateAppService()
 	if err != nil {
 		return nil, err
 	}
 	appservice.Log = log.Sub("matrix")
 
+	appservice.Init()
+
+	// Create the bot.
+	bot := appservice.BotIntent()
+
 	// Create the bridge.
 	bridge := &Bridge{
+		as:     appservice,
+		bot:    bot,
 		config: cfg,
 		log:    logger,
-		as:     appservice,
 	}
 
+	// Setup the event processors
+	bridge.setupEvents()
+
 	return bridge, nil
 }
 
-func (b *Bridge) Start() {
-	b.log.Infoln("bridge started")
+func (b *Bridge) connect() error {
+	b.log.Debugln("Checking connection to homeserver")
+
+	for {
+		resp, err := b.bot.Whoami()
+		if err != nil {
+			if errors.Is(err, mautrix.MUnknownToken) {
+				b.log.Fatalln("Access token invalid. Is the registration installed in your homeserver correctly?")
+
+				return fmt.Errorf("invalid access token")
+			}
+
+			b.log.Errorfln("Failed to connect to homeserver : %v", err)
+			b.log.Errorfln("reconnecting in %s", reconnectDelay)
+
+			time.Sleep(reconnectDelay)
+		} else if resp.UserID != b.bot.UserID {
+			b.log.Fatalln("Unexpected user ID in whoami call: got %s, expected %s", resp.UserID, b.bot.UserID)
+
+			return fmt.Errorf("expected user id %q but got %q", b.bot.UserID, resp.UserID)
+		} else {
+			break
+		}
+	}
+
+	b.log.Debugln("Connected to homeserver")
+
+	return nil
+}
+
+func (b *Bridge) Start() error {
+	b.log.Infoln("Bridge started")
+
+	if err := b.connect(); err != nil {
+		return err
+	}
+
+	b.log.Debugln("Starting application service HTTP server")
+	go b.as.Start()
+
+	b.log.Debugln("Starting event processor")
+	go b.eventProcessor.Start()
+
+	go b.updateBotProfile()
+
+	// Finally tell the appservice we're ready
+	b.as.Ready = true
+
+	return nil
 }
 
 func (b *Bridge) Stop() {
-	b.log.Infoln("bridge stopped")
+	b.log.Infoln("Bridge stopped")
 }

+ 87 - 0
bridge/matrix.go

@@ -0,0 +1,87 @@
+package bridge
+
+import (
+	"maunium.net/go/maulogger/v2"
+	"maunium.net/go/mautrix/appservice"
+	"maunium.net/go/mautrix/event"
+)
+
+type matrixHandler struct {
+	as     *appservice.AppService
+	bridge *Bridge
+	log    maulogger.Logger
+}
+
+func (b *Bridge) setupEvents() {
+	b.eventProcessor = appservice.NewEventProcessor(b.as)
+
+	b.matrixHandler = &matrixHandler{
+		as:     b.as,
+		bridge: b,
+		log:    b.log.Sub("Matrix"),
+	}
+
+	b.eventProcessor.On(event.EventMessage, b.matrixHandler.handleMessage)
+	b.eventProcessor.On(event.StateMember, b.matrixHandler.handleMembership)
+}
+
+func (mh *MatrixHandler) join(evt *event.Event, intent *appservice.IntentAPI) *mautrix.RespJoinedMembers {
+	resp, err := intent.JoinRoomByID(evt.RoomID)
+	if err != nil {
+		mh.log.Debugfln("Failed to join room %s as %s with invite from %s: %v", evt.RoomID, intent.UserID, evt.Sender, err)
+
+		return nil
+	}
+
+	members, err := intent.JoinedMembers(resp.RoomID)
+	if err != nil {
+		intent.LeaveRoom(resp.RoomID)
+
+		mh.log.Debugfln("Failed to get members in room %s after accepting invite from %s as %s: %v", resp.RoomID, evt.Sender, intent.UserID, err)
+
+		return nil
+	}
+
+	if len(members.Joined) < 2 {
+		intent.LeaveRoom(resp.RoomID)
+
+		mh.log.Debugln("Leaving empty room", resp.RoomID, "after accepting invite from", evt.Sender, "as", intent.UserID)
+
+		return nil
+	}
+
+	return members
+}
+
+func (mh *matrixHandler) ignoreEvent(evt *event.Event) bool {
+	return false
+}
+
+func (mh *matrixHandler) handleMessage(evt *event.Event) {
+	if mh.ignoreEvent(evt) {
+		return
+	}
+
+	mh.log.Debugfln("received message from %q: %q", evt.Sender, evt.Content.AsMessage())
+}
+
+func (mh *matrixHandler) handleMembership(evt *event.Event) {
+	mh.log.Debugfln("recevied invite %#v\n", evt)
+
+	// Return early if we're supposed to ignore the event.
+	if mh.ignoreEvent(evt) {
+		return
+	}
+
+	// Grab the content of the event.
+	content := evt.Content.AsMessage()
+
+	// TODO: handle invites from ourselfs?
+
+	isSelf := id.UserID(evt.GetStateKey()) == evt.Sender
+
+	// Handle matrix invites.
+	if content.Membership == event.MembershipInvite && !isSelf {
+		//
+	}
+}

+ 7 - 0
config/appservice.go

@@ -64,5 +64,12 @@ func (cfg *Config) CreateAppService() (*as.AppService, error) {
 	appservice.Host.Port = cfg.Appservice.Port
 	appservice.DefaultHTTPRetries = 4
 
+	reg, err := cfg.getRegistration()
+	if err != nil {
+		return nil, err
+	}
+
+	appservice.Registration = reg
+
 	return appservice, nil
 }

+ 3 - 1
run/cmd.go

@@ -23,7 +23,9 @@ func (c *Cmd) Run(g *globals.Globals) error {
 		return err
 	}
 
-	bridge.Start()
+	if err := bridge.Start(); err != nil {
+		return err
+	}
 
 	ch := make(chan os.Signal)
 	signal.Notify(ch, os.Interrupt, syscall.SIGTERM)