Move interfaces to maubot package and other stuff to app/

This commit is contained in:
Tulir Asokan 2018-06-14 11:11:24 +03:00
parent ef8fffaff8
commit 3a27831112
14 changed files with 329 additions and 198 deletions

View File

@ -14,15 +14,15 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
package maubot package app
import ( import (
"net/http" "net/http"
"os" "os"
"maubot.xyz"
"maubot.xyz/config" "maubot.xyz/config"
"maubot.xyz/database" "maubot.xyz/database"
"maubot.xyz/interfaces"
"maubot.xyz/matrix" "maubot.xyz/matrix"
log "maunium.net/go/maulogger" log "maunium.net/go/maulogger"
) )
@ -31,7 +31,7 @@ type Bot struct {
Config *config.MainConfig Config *config.MainConfig
Database *database.Database Database *database.Database
Clients map[string]*matrix.Client Clients map[string]*matrix.Client
PluginCreators map[string]*interfaces.PluginCreator PluginCreators map[string]*maubot.PluginCreator
Plugins map[string]*PluginWrapper Plugins map[string]*PluginWrapper
Server *http.Server Server *http.Server
} }
@ -41,7 +41,7 @@ func New(config *config.MainConfig) *Bot {
Config: config, Config: config,
Clients: make(map[string]*matrix.Client), Clients: make(map[string]*matrix.Client),
Plugins: make(map[string]*PluginWrapper), Plugins: make(map[string]*PluginWrapper),
PluginCreators: make(map[string]*interfaces.PluginCreator), PluginCreators: make(map[string]*maubot.PluginCreator),
} }
} }

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
package maubot package app
import ( import (
"context" "context"

54
app/matrix.go Normal file
View File

@ -0,0 +1,54 @@
// maubot - A plugin-based Matrix bot system written in Go.
// Copyright (C) 2018 Tulir Asokan
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package app
import (
"os"
"maubot.xyz/matrix"
log "maunium.net/go/maulogger"
)
func (bot *Bot) initClients() {
log.Debugln("Initializing Matrix clients")
clients := bot.Database.MatrixClient.GetAll()
for _, client := range clients {
mxClient, err := matrix.NewClient(client)
if err != nil {
log.Fatalf("Failed to create client to %s as %s: %v\n", client.Homeserver, client.UserID, err)
os.Exit(3)
}
log.Debugln("Initialized user", client.UserID, "with homeserver", client.Homeserver)
bot.Clients[client.UserID] = mxClient
}
}
func (bot *Bot) startClients() {
log.Debugln("Starting Matrix syncer")
for _, client := range bot.Clients {
if client.DB.Sync {
client.Sync()
}
}
}
func (bot *Bot) stopClients() {
log.Debugln("Stopping Matrix syncers")
for _, client := range bot.Clients {
client.StopSync()
}
}

81
app/pluginloader.go Normal file
View File

@ -0,0 +1,81 @@
// maubot - A plugin-based Matrix bot system written in Go.
// Copyright (C) 2018 Tulir Asokan
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package app
import (
"fmt"
"plugin"
"maubot.xyz"
"maubot.xyz/database"
)
type PluginWrapper struct {
maubot.Plugin
Creator *maubot.PluginCreator
DB *database.Plugin
}
func LoadPlugin(path string) (*maubot.PluginCreator, error) {
rawPlugin, err := plugin.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open: %v", err)
}
pluginCreatorSymbol, err := rawPlugin.Lookup("Plugin")
if err == nil {
pluginCreator, ok := pluginCreatorSymbol.(*maubot.PluginCreator)
if ok {
pluginCreator.Path = path
return pluginCreator, nil
}
}
pluginCreatorFuncSymbol, err := rawPlugin.Lookup("Create")
if err != nil {
return nil, fmt.Errorf("symbol \"Create\" not found: %v", err)
}
pluginCreatorFunc, ok := pluginCreatorFuncSymbol.(maubot.PluginCreatorFunc)
if !ok {
return nil, fmt.Errorf("symbol \"Create\" does not implement maubot.PluginCreator")
}
nameSymbol, err := rawPlugin.Lookup("Name")
if err != nil {
return nil, fmt.Errorf("symbol \"Name\" not found: %v", err)
}
name, ok := nameSymbol.(string)
if !ok {
return nil, fmt.Errorf("symbol \"Name\" is not a string")
}
versionSymbol, err := rawPlugin.Lookup("Version")
if err != nil {
return nil, fmt.Errorf("symbol \"Version\" not found: %v", err)
}
version, ok := versionSymbol.(string)
if !ok {
return nil, fmt.Errorf("symbol \"Version\" is not a string")
}
return &maubot.PluginCreator{
Create: pluginCreatorFunc,
Name: name,
Version: version,
Path: path,
}, nil
}

View File

@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License // You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
package maubot package app
import ( import (
"io/ioutil" "io/ioutil"

View File

@ -23,7 +23,7 @@ import (
"syscall" "syscall"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"maubot.xyz" "maubot.xyz/app"
"maubot.xyz/config" "maubot.xyz/config"
flag "maunium.net/go/mauflag" flag "maunium.net/go/mauflag"
log "maunium.net/go/maulogger" log "maunium.net/go/maulogger"
@ -55,7 +55,7 @@ func main() {
cfg.Logging.Configure(log.DefaultLogger) cfg.Logging.Configure(log.DefaultLogger)
log.Debugln("Logger configured") log.Debugln("Logger configured")
bot := maubot.New(cfg) bot := app.New(cfg)
bot.Init() bot.Init()
bot.Start() bot.Start()

View File

@ -1,48 +0,0 @@
package interfaces
type Plugin interface {
Start()
Stop()
}
type EventHandler func(*Event) bool
type MatrixClient interface {
AddEventHandler(string, EventHandler)
}
type EventFuncs interface {
Reply(text string) (string, error)
SendMessage(text string) (string, error)
SendEvent(content map[string]interface{}) (string, error)
}
type Event struct {
EventFuncs
StateKey string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
Sender string `json:"sender"` // The user ID of the sender of the event
Type string `json:"type"` // The event type
Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
ID string `json:"event_id"` // The unique ID of this event
RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence)
Content map[string]interface{} `json:"content"` // The JSON content of the event.
Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event
Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver.
}
type Unsigned struct {
PrevContent map[string]interface{} `json:"prev_content,omitempty"`
PrevSender string `json:"prev_sender,omitempty"`
ReplacesState string `json:"replaces_state,omitempty"`
Age int64 `json:"age"`
}
type PluginCreatorFunc func(client MatrixClient) Plugin
type PluginCreator struct {
Create PluginCreatorFunc
Name string
Version string
Path string
}

126
matrix.go
View File

@ -16,39 +16,109 @@
package maubot package maubot
import ( type EventType string
"os" type MessageType string
"maubot.xyz/matrix" // State events
log "maunium.net/go/maulogger" const (
StateAliases EventType = "m.room.aliases"
StateCanonicalAlias = "m.room.canonical_alias"
StateCreate = "m.room.create"
StateJoinRules = "m.room.join_rules"
StateMember = "m.room.member"
StatePowerLevels = "m.room.power_levels"
StateRoomName = "m.room.name"
StateTopic = "m.room.topic"
StateRoomAvatar = "m.room.avatar"
StatePinnedEvents = "m.room.pinned_events"
) )
func (bot *Bot) initClients() { // Message events
log.Debugln("Initializing Matrix clients") const (
clients := bot.Database.MatrixClient.GetAll() EventRedaction EventType = "m.room.redaction"
for _, client := range clients { EventMessage = "m.room.message"
mxClient, err := matrix.NewClient(client) EventSticker = "m.sticker"
if err != nil { )
log.Fatalf("Failed to create client to %s as %s: %v\n", client.Homeserver, client.UserID, err)
os.Exit(3) // Msgtypes
} const (
log.Debugln("Initialized user", client.UserID, "with homeserver", client.Homeserver) MsgText MessageType = "m.text"
bot.Clients[client.UserID] = mxClient MsgEmote = "m.emote"
} MsgNotice = "m.notice"
MsgImage = "m.image"
MsgLocation = "m.location"
MsgVideo = "m.video"
MsgAudio = "m.audio"
)
const FormatHTML = "org.matrix.custom.html"
type EventHandler func(*Event) bool
type MatrixClient interface {
AddEventHandler(EventType, EventHandler)
} }
func (bot *Bot) startClients() { type EventFuncs interface {
log.Debugln("Starting Matrix syncer") Reply(string) (string, error)
for _, client := range bot.Clients { ReplyContent(Content) (string, error)
if client.DB.Sync { SendMessage(string) (string, error)
client.Sync() SendContent(Content) (string, error)
} SendRawEvent(EventType, interface{}) (string, error)
}
} }
func (bot *Bot) stopClients() { type Event struct {
log.Debugln("Stopping Matrix syncers") EventFuncs
for _, client := range bot.Clients {
client.StopSync() StateKey string `json:"state_key,omitempty"` // The state key for the event. Only present on State Events.
} Sender string `json:"sender"` // The user ID of the sender of the event
Type EventType `json:"type"` // The event type
Timestamp int64 `json:"origin_server_ts"` // The unix timestamp when this message was sent by the origin server
ID string `json:"event_id"` // The unique ID of this event
RoomID string `json:"room_id"` // The room the event was sent to. May be nil (e.g. for presence)
Content Content `json:"content"`
Redacts string `json:"redacts,omitempty"` // The event ID that was redacted if a m.room.redaction event
Unsigned Unsigned `json:"unsigned,omitempty"` // Unsigned content set by own homeserver.
}
type Unsigned struct {
PrevContent Content `json:"prev_content,omitempty"`
PrevSender string `json:"prev_sender,omitempty"`
ReplacesState string `json:"replaces_state,omitempty"`
Age int64 `json:"age"`
}
type Content struct {
Raw map[string]interface{} `json:"-"`
MsgType MessageType `json:"msgtype"`
Body string `json:"body"`
Format string `json:"format"`
FormattedBody string `json:"formatted_body"`
Info FileInfo `json:"info"`
URL string `json:"url"`
Membership string `json:"membership"`
RelatesTo RelatesTo `json:"m.relates_to"`
}
type FileInfo struct {
MimeType string `json:"mimetype"`
ThumbnailInfo *FileInfo `json:"thumbnail_info"`
ThumbnailURL string `json:"thumbnail_url"`
Height int `json:"h"`
Width int `json:"w"`
Size int `json:"size"`
}
type RelatesTo struct {
InReplyTo InReplyTo `json:"m.in_reply_to"`
}
type InReplyTo struct {
EventID string `json:"event_id"`
// Not required, just for future-proofing
RoomID string `json:"room_id"`
} }

View File

@ -17,7 +17,9 @@
package matrix package matrix
import ( import (
"maubot.xyz/interfaces" "encoding/json"
"maubot.xyz"
"maunium.net/go/gomatrix" "maunium.net/go/gomatrix"
) )
@ -26,43 +28,64 @@ type Event struct {
Client *Client Client *Client
} }
func (evt *Event) Interface() *interfaces.Event { func roundtripContent(rawContent map[string]interface{}) (content maubot.Content) {
if len(rawContent) == 0 {
content.Raw = rawContent
return
}
data, _ := json.Marshal(&rawContent)
json.Unmarshal(data, &content)
content.Raw = rawContent
return
}
func (evt *Event) Interface() *maubot.Event {
var stateKey string var stateKey string
if evt.StateKey != nil { if evt.StateKey != nil {
stateKey = *evt.StateKey stateKey = *evt.StateKey
} }
return &interfaces.Event{ mbEvent := &maubot.Event{
EventFuncs: evt, EventFuncs: evt,
StateKey: stateKey, StateKey: stateKey,
Sender: evt.Sender, Sender: evt.Sender,
Type: evt.Type, Type: maubot.EventType(evt.Type),
Timestamp: evt.Timestamp, Timestamp: evt.Timestamp,
ID: evt.ID, ID: evt.ID,
RoomID: evt.RoomID, RoomID: evt.RoomID,
Content: evt.Content, Content: roundtripContent(evt.Content),
Redacts: evt.Redacts, Redacts: evt.Redacts,
Unsigned: interfaces.Unsigned{ Unsigned: maubot.Unsigned{
PrevContent: evt.Unsigned.PrevContent, PrevContent: roundtripContent(evt.Unsigned.PrevContent),
PrevSender: evt.Unsigned.PrevSender, PrevSender: evt.Unsigned.PrevSender,
ReplacesState: evt.Unsigned.ReplacesState, ReplacesState: evt.Unsigned.ReplacesState,
Age: evt.Unsigned.Age, Age: evt.Unsigned.Age,
}, },
} }
RemoveReplyFallback(mbEvent)
return mbEvent
} }
func (evt *Event) Reply(text string) (string, error) { func (evt *Event) Reply(text string) (string, error) {
return evt.SendEvent( return evt.SendRawEvent(maubot.EventMessage,
SetReply( SetReply(
RenderMarkdown(text), RenderMarkdown(text),
evt.Event)) evt.Event))
} }
func (evt *Event) SendMessage(text string) (string, error) { func (evt *Event) ReplyContent(content maubot.Content) (string, error) {
return evt.SendEvent(RenderMarkdown(text)) return evt.SendRawEvent(maubot.EventMessage, SetReply(content, evt.Event))
} }
func (evt *Event) SendEvent(content map[string]interface{}) (string, error) { func (evt *Event) SendMessage(text string) (string, error) {
resp, err := evt.Client.SendMessageEvent(evt.RoomID, "m.room.message", content) return evt.SendRawEvent(maubot.EventMessage, RenderMarkdown(text))
}
func (evt *Event) SendContent(content maubot.Content) (string, error) {
return evt.SendRawEvent(maubot.EventMessage, content)
}
func (evt *Event) SendRawEvent(evtType maubot.EventType, content interface{}) (string, error) {
resp, err := evt.Client.SendMessageEvent(evt.RoomID, string(evtType), content)
if err != nil { if err != nil {
return "", err return "", err
} }

View File

@ -19,9 +19,10 @@ package matrix
import ( import (
"strings" "strings"
"gopkg.in/russross/blackfriday.v2" "gopkg.in/russross/blackfriday.v2"
"maubot.xyz"
) )
func RenderMarkdown(text string) map[string]interface{} { func RenderMarkdown(text string) maubot.Content {
parser := blackfriday.New( parser := blackfriday.New(
blackfriday.WithExtensions(blackfriday.NoIntraEmphasis | blackfriday.WithExtensions(blackfriday.NoIntraEmphasis |
blackfriday.Tables | blackfriday.Tables |
@ -43,10 +44,10 @@ func RenderMarkdown(text string) map[string]interface{} {
renderer.RenderFooter(&buf, ast) renderer.RenderFooter(&buf, ast)
htmlBody := buf.String() htmlBody := buf.String()
return map[string]interface{}{ return maubot.Content{
"formatted_body": htmlBody, FormattedBody: htmlBody,
"format": "org.matrix.custom.html", Format: maubot.FormatHTML,
"msgtype": "m.text", MsgType: maubot.MsgText,
"body": HTMLToText(htmlBody), Body: HTMLToText(htmlBody),
} }
} }

View File

@ -17,14 +17,15 @@
package matrix package matrix
import ( import (
"maubot.xyz"
"maubot.xyz/database" "maubot.xyz/database"
"maubot.xyz/interfaces"
"maunium.net/go/gomatrix" "maunium.net/go/gomatrix"
log "maunium.net/go/maulogger" log "maunium.net/go/maulogger"
) )
type Client struct { type Client struct {
*gomatrix.Client *gomatrix.Client
syncer *MaubotSyncer
DB *database.MatrixClient DB *database.MatrixClient
} }
@ -40,9 +41,10 @@ func NewClient(db *database.MatrixClient) (*Client, error) {
DB: db, DB: db,
} }
client.Syncer = NewMaubotSyncer(client, client.Store) client.syncer = NewMaubotSyncer(client, client.Store)
client.Client.Syncer = client.syncer
client.AddEventHandler(gomatrix.StateMember, client.onJoin) client.AddEventHandler(maubot.StateMember, client.onJoin)
return client, nil return client, nil
} }
@ -50,19 +52,19 @@ func NewClient(db *database.MatrixClient) (*Client, error) {
func (client *Client) ParseEvent(evt *gomatrix.Event) *Event { func (client *Client) ParseEvent(evt *gomatrix.Event) *Event {
return &Event{ return &Event{
Client: client, Client: client,
Event: evt, Event: evt,
} }
} }
func (client *Client) AddEventHandler(evt string, handler interfaces.EventHandler) { func (client *Client) AddEventHandler(evt maubot.EventType, handler maubot.EventHandler) {
client.Syncer.(*MaubotSyncer).OnEventType(evt, handler) client.syncer.OnEventType(evt, handler)
} }
func (client *Client) onJoin(evt *interfaces.Event) bool { func (client *Client) onJoin(evt *maubot.Event) bool {
if !client.DB.AutoJoinRooms || evt.StateKey != client.DB.UserID { if !client.DB.AutoJoinRooms || evt.StateKey != client.DB.UserID {
return true return true
} }
if membership, _ := evt.Content["membership"].(string); membership == "invite" { if evt.Content.Membership == "invite" {
client.JoinRoom(evt.RoomID) client.JoinRoom(evt.RoomID)
return false return false
} }

View File

@ -17,11 +17,13 @@
package matrix package matrix
import ( import (
"fmt"
"regexp" "regexp"
"strings" "strings"
"fmt"
"maunium.net/go/gomatrix"
"golang.org/x/net/html" "golang.org/x/net/html"
"maubot.xyz"
"maunium.net/go/gomatrix"
) )
var HTMLReplyFallbackRegex = regexp.MustCompile(`^<mx-reply>[\s\S]+?</mx-reply>`) var HTMLReplyFallbackRegex = regexp.MustCompile(`^<mx-reply>[\s\S]+?</mx-reply>`)
@ -42,13 +44,13 @@ func TrimReplyFallbackText(text string) string {
return strings.Join(lines, "\n") return strings.Join(lines, "\n")
} }
func RemoveReplyFallback(evt *gomatrix.Event) { func RemoveReplyFallback(evt *maubot.Event) {
if format, ok := evt.Content["format"].(string); ok && format == "org.matrix.custom.html" { if len(evt.Content.RelatesTo.InReplyTo.EventID) > 0 {
htmlBody, _ := evt.Content["formatted_body"].(string) if evt.Content.Format == maubot.FormatHTML {
evt.Content["formatted_body"] = TrimReplyFallbackHTML(htmlBody) evt.Content.FormattedBody = TrimReplyFallbackHTML(evt.Content.FormattedBody)
}
evt.Content.Body = TrimReplyFallbackText(evt.Content.Body)
} }
plainBody, _ := evt.Content["body"].(string)
evt.Content["body"] = TrimReplyFallbackText(plainBody)
} }
const ReplyFormat = `<mx-reply><blockquote> const ReplyFormat = `<mx-reply><blockquote>
@ -86,22 +88,18 @@ func ReplyFallbackText(evt *gomatrix.Event) string {
return fallbackText.String() return fallbackText.String()
} }
func SetReply(content map[string]interface{}, inReplyTo *gomatrix.Event) map[string]interface{} { func SetReply(content maubot.Content, inReplyTo *gomatrix.Event) maubot.Content {
content["m.relates_to"] = map[string]interface{}{ content.RelatesTo.InReplyTo.EventID = inReplyTo.ID
"m.in_reply_to": map[string]interface{}{ content.RelatesTo.InReplyTo.RoomID = inReplyTo.RoomID
"event_id": inReplyTo.ID,
"room_id": inReplyTo.RoomID, if content.MsgType == maubot.MsgText || content.MsgType == maubot.MsgNotice {
}, if len(content.FormattedBody) == 0 || content.Format != maubot.FormatHTML {
content.FormattedBody = html.EscapeString(content.Body)
content.Format = maubot.FormatHTML
}
content.FormattedBody = ReplyFallbackHTML(inReplyTo) + content.FormattedBody
content.Body = ReplyFallbackText(inReplyTo) + content.Body
} }
body, _ := content["body"].(string)
content["body"] = ReplyFallbackText(inReplyTo) + body
htmlBody, ok := content["formatted_body"].(string)
if !ok {
htmlBody = html.EscapeString(body)
content["format"] = "org.matrix.custom.html"
}
content["formatted_body"] = ReplyFallbackHTML(inReplyTo) + htmlBody
return content return content
} }

View File

@ -6,14 +6,14 @@ import (
"runtime/debug" "runtime/debug"
"time" "time"
"maubot.xyz/interfaces" "maubot.xyz"
"maunium.net/go/gomatrix" "maunium.net/go/gomatrix"
) )
type MaubotSyncer struct { type MaubotSyncer struct {
Client *Client Client *Client
Store gomatrix.Storer Store gomatrix.Storer
listeners map[string][]interfaces.EventHandler listeners map[maubot.EventType][]maubot.EventHandler
} }
// NewDefaultSyncer returns an instantiated DefaultSyncer // NewDefaultSyncer returns an instantiated DefaultSyncer
@ -21,7 +21,7 @@ func NewMaubotSyncer(client *Client, store gomatrix.Storer) *MaubotSyncer {
return &MaubotSyncer{ return &MaubotSyncer{
Client: client, Client: client,
Store: store, Store: store,
listeners: make(map[string][]interfaces.EventHandler), listeners: make(map[maubot.EventType][]maubot.EventHandler),
} }
} }
@ -73,10 +73,10 @@ func (s *MaubotSyncer) ProcessResponse(res *gomatrix.RespSync, since string) (er
// OnEventType allows callers to be notified when there are new events for the given event type. // OnEventType allows callers to be notified when there are new events for the given event type.
// There are no duplicate checks. // There are no duplicate checks.
func (s *MaubotSyncer) OnEventType(eventType string, callback interfaces.EventHandler) { func (s *MaubotSyncer) OnEventType(eventType maubot.EventType, callback maubot.EventHandler) {
_, exists := s.listeners[eventType] _, exists := s.listeners[eventType]
if !exists { if !exists {
s.listeners[eventType] = []interfaces.EventHandler{} s.listeners[eventType] = []maubot.EventHandler{}
} }
s.listeners[eventType] = append(s.listeners[eventType], callback) s.listeners[eventType] = append(s.listeners[eventType], callback)
} }
@ -130,7 +130,7 @@ func (s *MaubotSyncer) getOrCreateRoom(roomID string) *gomatrix.Room {
func (s *MaubotSyncer) notifyListeners(mxEvent *gomatrix.Event) { func (s *MaubotSyncer) notifyListeners(mxEvent *gomatrix.Event) {
event := s.Client.ParseEvent(mxEvent) event := s.Client.ParseEvent(mxEvent)
listeners, exists := s.listeners[event.Type] listeners, exists := s.listeners[maubot.EventType(event.Type)]
if !exists { if !exists {
return return
} }

View File

@ -16,66 +16,16 @@
package maubot package maubot
import ( type Plugin interface {
"fmt" Start()
"plugin" Stop()
"maubot.xyz/database"
"maubot.xyz/interfaces"
)
type PluginWrapper struct {
interfaces.Plugin
Creator *interfaces.PluginCreator
DB *database.Plugin
} }
func LoadPlugin(path string) (*interfaces.PluginCreator, error) { type PluginCreatorFunc func(client MatrixClient) Plugin
rawPlugin, err := plugin.Open(path)
if err != nil {
return nil, fmt.Errorf("failed to open: %v", err)
}
pluginCreatorSymbol, err := rawPlugin.Lookup("Plugin") type PluginCreator struct {
if err == nil { Create PluginCreatorFunc
pluginCreator, ok := pluginCreatorSymbol.(*interfaces.PluginCreator) Name string
if ok { Version string
pluginCreator.Path = path Path string
return pluginCreator, nil
}
}
pluginCreatorFuncSymbol, err := rawPlugin.Lookup("Create")
if err != nil {
return nil, fmt.Errorf("symbol \"Create\" not found: %v", err)
}
pluginCreatorFunc, ok := pluginCreatorFuncSymbol.(interfaces.PluginCreatorFunc)
if !ok {
return nil, fmt.Errorf("symbol \"Create\" does not implement maubot.PluginCreator")
}
nameSymbol, err := rawPlugin.Lookup("Name")
if err != nil {
return nil, fmt.Errorf("symbol \"Name\" not found: %v", err)
}
name, ok := nameSymbol.(string)
if !ok {
return nil, fmt.Errorf("symbol \"Name\" is not a string")
}
versionSymbol, err := rawPlugin.Lookup("Version")
if err != nil {
return nil, fmt.Errorf("symbol \"Version\" not found: %v", err)
}
version, ok := versionSymbol.(string)
if !ok {
return nil, fmt.Errorf("symbol \"Version\" is not a string")
}
return &interfaces.PluginCreator{
Create: pluginCreatorFunc,
Name: name,
Version: version,
Path: path,
}, nil
} }