Move interfaces to maubot package and other stuff to app/
This commit is contained in:
parent
ef8fffaff8
commit
3a27831112
14 changed files with 329 additions and 198 deletions
80
app/bot.go
Normal file
80
app/bot.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
// 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 (
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"maubot.xyz"
|
||||
"maubot.xyz/config"
|
||||
"maubot.xyz/database"
|
||||
"maubot.xyz/matrix"
|
||||
log "maunium.net/go/maulogger"
|
||||
)
|
||||
|
||||
type Bot struct {
|
||||
Config *config.MainConfig
|
||||
Database *database.Database
|
||||
Clients map[string]*matrix.Client
|
||||
PluginCreators map[string]*maubot.PluginCreator
|
||||
Plugins map[string]*PluginWrapper
|
||||
Server *http.Server
|
||||
}
|
||||
|
||||
func New(config *config.MainConfig) *Bot {
|
||||
return &Bot{
|
||||
Config: config,
|
||||
Clients: make(map[string]*matrix.Client),
|
||||
Plugins: make(map[string]*PluginWrapper),
|
||||
PluginCreators: make(map[string]*maubot.PluginCreator),
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *Bot) Init() {
|
||||
bot.initDatabase()
|
||||
bot.initClients()
|
||||
bot.initServer()
|
||||
bot.loadPlugins()
|
||||
bot.createPlugins()
|
||||
log.Debugln("Init func exit")
|
||||
}
|
||||
|
||||
func (bot *Bot) Start() {
|
||||
go bot.startClients()
|
||||
go bot.startServer()
|
||||
bot.startPlugins()
|
||||
log.Debugln("Start func exit")
|
||||
}
|
||||
|
||||
func (bot *Bot) Stop() {
|
||||
bot.stopPlugins()
|
||||
bot.stopServer()
|
||||
bot.stopClients()
|
||||
log.Debugln("Stop func exit")
|
||||
}
|
||||
|
||||
func (bot *Bot) initDatabase() {
|
||||
log.Debugln("Initializing database")
|
||||
bot.Database = &bot.Config.Database
|
||||
err := bot.Database.Connect()
|
||||
if err != nil {
|
||||
log.Fatalln("Failed to connect to database:", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
bot.Database.CreateTables()
|
||||
}
|
59
app/http.go
Normal file
59
app/http.go
Normal file
|
@ -0,0 +1,59 @@
|
|||
// 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 (
|
||||
"context"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
log "maunium.net/go/maulogger"
|
||||
)
|
||||
|
||||
func (bot *Bot) initServer() {
|
||||
log.Debugln("Initializing HTTP server")
|
||||
r := mux.NewRouter()
|
||||
http.Handle(bot.Config.Server.BasePath, r)
|
||||
bot.Server = &http.Server{
|
||||
Addr: bot.Config.Server.Listen,
|
||||
WriteTimeout: time.Second * 15,
|
||||
ReadTimeout: time.Second * 15,
|
||||
IdleTimeout: time.Second * 60,
|
||||
Handler: r,
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *Bot) startServer() {
|
||||
log.Debugf("Listening at http://%s%s\n", bot.Server.Addr, bot.Config.Server.BasePath)
|
||||
if err := bot.Server.ListenAndServe(); err != nil {
|
||||
log.Fatalln("HTTP server errored:", err)
|
||||
bot.Server = nil
|
||||
bot.Stop()
|
||||
os.Exit(10)
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *Bot) stopServer() {
|
||||
if bot.Server != nil {
|
||||
log.Debugln("Stopping HTTP server")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||
defer cancel()
|
||||
bot.Server.Shutdown(ctx)
|
||||
}
|
||||
}
|
54
app/matrix.go
Normal file
54
app/matrix.go
Normal 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
81
app/pluginloader.go
Normal 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
|
||||
}
|
112
app/plugins.go
Normal file
112
app/plugins.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
// 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 (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
log "maunium.net/go/maulogger"
|
||||
)
|
||||
|
||||
func (bot *Bot) loadPlugin(dir, fileName string) {
|
||||
ext := fileName[len(fileName)-4:]
|
||||
if ext != ".mbp" {
|
||||
return
|
||||
}
|
||||
|
||||
path := filepath.Join(dir, fileName)
|
||||
|
||||
pluginCreator, err := LoadPlugin(path)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to load plugin at %s: %v\n", path, err)
|
||||
os.Exit(4)
|
||||
}
|
||||
|
||||
_, exists := bot.PluginCreators[pluginCreator.Name]
|
||||
if exists {
|
||||
log.Debugf("Skipping plugin at %s: plugin with same name already loaded", path)
|
||||
return
|
||||
}
|
||||
|
||||
bot.PluginCreators[pluginCreator.Name] = pluginCreator
|
||||
log.Debugf("Loaded plugin creator %s v%s\n", pluginCreator.Name, pluginCreator.Version)
|
||||
}
|
||||
|
||||
func (bot *Bot) loadPlugins() {
|
||||
for _, dir := range bot.Config.PluginDirs {
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to read plugin directory %s: %v\n", dir, err)
|
||||
os.Exit(4)
|
||||
}
|
||||
for _, file := range files {
|
||||
bot.loadPlugin(dir, file.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *Bot) createPlugins() {
|
||||
log.Debugln("Creating plugin instances")
|
||||
plugins := bot.Database.Plugin.GetAll()
|
||||
for _, plugin := range plugins {
|
||||
if !plugin.Enabled {
|
||||
log.Debugln("Skipping disabled plugin", plugin.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
creator, ok := bot.PluginCreators[plugin.Type]
|
||||
if !ok {
|
||||
log.Errorln("Plugin creator", plugin.Type, "for", plugin.ID, "not found, disabling plugin...")
|
||||
plugin.Enabled = false
|
||||
plugin.Update()
|
||||
continue
|
||||
}
|
||||
|
||||
client, ok := bot.Clients[plugin.UserID]
|
||||
if !ok {
|
||||
log.Errorln("Client", plugin.UserID, "for", plugin.ID, "not found, disabling plugin...")
|
||||
plugin.Enabled = false
|
||||
plugin.Update()
|
||||
continue
|
||||
}
|
||||
|
||||
log.Debugf("Created plugin %s (type %s v%s)\n", plugin.ID, creator.Name, creator.Version)
|
||||
bot.Plugins[plugin.ID] = &PluginWrapper{
|
||||
Plugin: creator.Create(client),
|
||||
Creator: creator,
|
||||
DB: plugin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *Bot) startPlugins() {
|
||||
log.Debugln("Starting plugin instances...")
|
||||
for _, plugin := range bot.Plugins {
|
||||
log.Debugf("Starting plugin %s (type %s v%s)\n", plugin.DB.ID, plugin.Creator.Name, plugin.Creator.Version)
|
||||
go plugin.Start()
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *Bot) stopPlugins() {
|
||||
log.Debugln("Stopping plugin instances...")
|
||||
for _, plugin := range bot.Plugins {
|
||||
log.Debugf("Stopping plugin %s (type %s v%s)\n", plugin.DB.ID, plugin.Creator.Name, plugin.Creator.Version)
|
||||
plugin.Stop()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue