mirror of
https://github.com/FloatTech/ZeroBot-Plugin.git
synced 2025-12-19 22:00:11 +08:00
✏️ 分离 dyloader
This commit is contained in:
parent
d2bcd0bc9f
commit
cccea70db1
@ -1,16 +0,0 @@
|
|||||||
package dyloader
|
|
||||||
|
|
||||||
import zero "github.com/wdvxdr1123/ZeroBot"
|
|
||||||
|
|
||||||
func sendGroupMessage(ctx *zero.Ctx, groupID int64, message interface{}) int64 {
|
|
||||||
return ctx.SendGroupMessage(groupID, message)
|
|
||||||
}
|
|
||||||
func sendPrivateMessage(ctx *zero.Ctx, userID int64, message interface{}) int64 {
|
|
||||||
return ctx.SendPrivateMessage(userID, message)
|
|
||||||
}
|
|
||||||
func getMessage(ctx *zero.Ctx, messageID int64) zero.Message {
|
|
||||||
return ctx.GetMessage(messageID)
|
|
||||||
}
|
|
||||||
func parse(ctx *zero.Ctx, model interface{}) (err error) {
|
|
||||||
return ctx.Parse(model)
|
|
||||||
}
|
|
||||||
@ -1,57 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build linux,cgo darwin,cgo freebsd,cgo
|
|
||||||
// +build linux,cgo darwin,cgo freebsd,cgo
|
|
||||||
|
|
||||||
package plugin
|
|
||||||
|
|
||||||
/*
|
|
||||||
#cgo linux LDFLAGS: -ldl
|
|
||||||
#include <dlfcn.h>
|
|
||||||
#include <limits.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
static int pluginClose(void* handle, char** err) {
|
|
||||||
int res = dlclose(handle);
|
|
||||||
if (res != 0) {
|
|
||||||
*err = (char*)dlerror();
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
import "C"
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"sync"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
func unload(pg *Plugin) error {
|
|
||||||
filepath := pg.pluginpath
|
|
||||||
|
|
||||||
pluginsMu.Lock()
|
|
||||||
p := plugins[filepath]
|
|
||||||
if p != nil {
|
|
||||||
delete(plugins, filepath)
|
|
||||||
}
|
|
||||||
pluginsMu.Unlock()
|
|
||||||
if p != nil {
|
|
||||||
var cErr *C.char
|
|
||||||
res := C.pluginClose(unsafe.Pointer(p), &cErr)
|
|
||||||
if res != 0 {
|
|
||||||
return errors.New(`plugin.Close("` + filepath + `"): ` + C.GoString(cErr))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//go:linkname pluginsMu plugin.pluginsMu
|
|
||||||
//go:linkname plugins plugin.plugins
|
|
||||||
var (
|
|
||||||
pluginsMu sync.Mutex
|
|
||||||
plugins map[string]*Plugin
|
|
||||||
)
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
//go:build !linux,!freebsd,!darwin !cgo
|
|
||||||
// +build !linux,!freebsd,!darwin !cgo
|
|
||||||
|
|
||||||
package plugin
|
|
||||||
|
|
||||||
import "errors"
|
|
||||||
|
|
||||||
func lookup(p *Plugin, symName string) (Symbol, error) {
|
|
||||||
return nil, errors.New("plugin: not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func open(name string) (*Plugin, error) {
|
|
||||||
return nil, errors.New("plugin: not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func unload(name string) error {
|
|
||||||
return nil, errors.New("plugin: not implemented")
|
|
||||||
}
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package plugin implements loading and symbol resolution of Go plugins.
|
|
||||||
//
|
|
||||||
// A plugin is a Go main package with exported functions and variables that
|
|
||||||
// has been built with:
|
|
||||||
//
|
|
||||||
// go build -buildmode=plugin
|
|
||||||
//
|
|
||||||
// When a plugin is first opened, the init functions of all packages not
|
|
||||||
// already part of the program are called. The main function is not run.
|
|
||||||
// A plugin is only initialized once, and cannot be closed.
|
|
||||||
//
|
|
||||||
// Currently plugins are only supported on Linux, FreeBSD, and macOS.
|
|
||||||
// Please report any issues.
|
|
||||||
|
|
||||||
//go:build linux,cgo darwin,cgo freebsd,cgo
|
|
||||||
// +build linux,cgo darwin,cgo freebsd,cgo
|
|
||||||
|
|
||||||
package plugin
|
|
||||||
|
|
||||||
import (
|
|
||||||
pl "plugin"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Plugin is a loaded Go plugin.
|
|
||||||
type Plugin struct {
|
|
||||||
pluginpath string
|
|
||||||
err string // set if plugin failed to load
|
|
||||||
loaded chan struct{} // closed when loaded
|
|
||||||
syms map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open opens a Go plugin.
|
|
||||||
// If a path has already been opened, then the existing *Plugin is returned.
|
|
||||||
// It is safe for concurrent use by multiple goroutines.
|
|
||||||
func Open(path string) (*Plugin, error) {
|
|
||||||
p, err := pl.Open(path)
|
|
||||||
return (*Plugin)(unsafe.Pointer(p)), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup searches for a symbol named symName in plugin p.
|
|
||||||
// A symbol is any exported variable or function.
|
|
||||||
// It reports an error if the symbol is not found.
|
|
||||||
// It is safe for concurrent use by multiple goroutines.
|
|
||||||
func (p *Plugin) Lookup(symName string) (Symbol, error) {
|
|
||||||
return (*pl.Plugin)(unsafe.Pointer(p)).Lookup(symName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes a Go plugin.
|
|
||||||
// If a path is noth opened, it is ignored.
|
|
||||||
// It is safe for concurrent use by multiple goroutines.
|
|
||||||
func Close(p *Plugin) error {
|
|
||||||
return unload(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Symbol is a pointer to a variable or function.
|
|
||||||
//
|
|
||||||
// For example, a plugin defined as
|
|
||||||
//
|
|
||||||
// package main
|
|
||||||
//
|
|
||||||
// import "fmt"
|
|
||||||
//
|
|
||||||
// var V int
|
|
||||||
//
|
|
||||||
// func F() { fmt.Printf("Hello, number %d\n", V) }
|
|
||||||
//
|
|
||||||
// may be loaded with the Open function and then the exported package
|
|
||||||
// symbols V and F can be accessed
|
|
||||||
//
|
|
||||||
// p, err := plugin.Open("plugin_name.so")
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// v, err := p.Lookup("V")
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// f, err := p.Lookup("F")
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// *v.(*int) = 7
|
|
||||||
// f.(func())() // prints "Hello, number 7"
|
|
||||||
type Symbol interface{}
|
|
||||||
@ -1,83 +0,0 @@
|
|||||||
// Copyright 2016 The Go Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// Package plugin implements loading and symbol resolution of Go plugins.
|
|
||||||
//
|
|
||||||
// A plugin is a Go main package with exported functions and variables that
|
|
||||||
// has been built with:
|
|
||||||
//
|
|
||||||
// go build -buildmode=plugin
|
|
||||||
//
|
|
||||||
// When a plugin is first opened, the init functions of all packages not
|
|
||||||
// already part of the program are called. The main function is not run.
|
|
||||||
// A plugin is only initialized once, and cannot be closed.
|
|
||||||
//
|
|
||||||
// Currently plugins are only supported on Linux, FreeBSD, and macOS.
|
|
||||||
// Please report any issues.
|
|
||||||
|
|
||||||
//go:build windows,cgo
|
|
||||||
// +build windows,cgo
|
|
||||||
|
|
||||||
package plugin
|
|
||||||
|
|
||||||
// Plugin is a loaded Go plugin.
|
|
||||||
type Plugin struct {
|
|
||||||
pluginpath string
|
|
||||||
err string // set if plugin failed to load
|
|
||||||
loaded chan struct{} // closed when loaded
|
|
||||||
syms map[string]interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open opens a Go plugin.
|
|
||||||
// If a path has already been opened, then the existing *Plugin is returned.
|
|
||||||
// It is safe for concurrent use by multiple goroutines.
|
|
||||||
func Open(path string) (*Plugin, error) {
|
|
||||||
return open(path)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lookup searches for a symbol named symName in plugin p.
|
|
||||||
// A symbol is any exported variable or function.
|
|
||||||
// It reports an error if the symbol is not found.
|
|
||||||
// It is safe for concurrent use by multiple goroutines.
|
|
||||||
func (p *Plugin) Lookup(symName string) (Symbol, error) {
|
|
||||||
return lookup(p, symName)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close closes a Go plugin.
|
|
||||||
// If a path is noth opened, it is ignored.
|
|
||||||
// It is safe for concurrent use by multiple goroutines.
|
|
||||||
func Close(p *Plugin) error {
|
|
||||||
return unload(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Symbol is a pointer to a variable or function.
|
|
||||||
//
|
|
||||||
// For example, a plugin defined as
|
|
||||||
//
|
|
||||||
// package main
|
|
||||||
//
|
|
||||||
// import "fmt"
|
|
||||||
//
|
|
||||||
// var V int
|
|
||||||
//
|
|
||||||
// func F() { fmt.Printf("Hello, number %d\n", V) }
|
|
||||||
//
|
|
||||||
// may be loaded with the Open function and then the exported package
|
|
||||||
// symbols V and F can be accessed
|
|
||||||
//
|
|
||||||
// p, err := plugin.Open("plugin_name.so")
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// v, err := p.Lookup("V")
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// f, err := p.Lookup("F")
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// *v.(*int) = 7
|
|
||||||
// f.(func())() // prints "Hello, number 7"
|
|
||||||
type Symbol interface{}
|
|
||||||
189
dyloader/scan.go
189
dyloader/scan.go
@ -1,189 +0,0 @@
|
|||||||
// Package dyloader 动态插件加载器
|
|
||||||
package dyloader
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/fs"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
|
||||||
_ "unsafe"
|
|
||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
zero "github.com/wdvxdr1123/ZeroBot"
|
|
||||||
"github.com/wdvxdr1123/ZeroBot/extension"
|
|
||||||
"github.com/wdvxdr1123/ZeroBot/message"
|
|
||||||
|
|
||||||
"github.com/FloatTech/ZeroBot-Plugin/control"
|
|
||||||
"github.com/FloatTech/ZeroBot-Plugin/dyloader/plugin"
|
|
||||||
)
|
|
||||||
|
|
||||||
var typeIsSo bool
|
|
||||||
var visited bool
|
|
||||||
|
|
||||||
//go:linkname matcherList github.com/wdvxdr1123/ZeroBot.matcherList
|
|
||||||
//go:linkname matcherLock github.com/wdvxdr1123/ZeroBot.matcherLock
|
|
||||||
//go:linkname defaultEngine github.com/wdvxdr1123/ZeroBot.defaultEngine
|
|
||||||
var (
|
|
||||||
// matcherList 所有主匹配器列表
|
|
||||||
matcherList []*zero.Matcher
|
|
||||||
// Matcher 修改读写锁
|
|
||||||
matcherLock sync.RWMutex
|
|
||||||
// defaultEngine zero 的默认 engine
|
|
||||||
defaultEngine *zero.Engine
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
pluginsMu sync.Mutex
|
|
||||||
plugins = make(map[string]*plugin.Plugin)
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
zero.OnCommand("刷新插件", zero.SuperUserPermission).SetBlock(true).FirstPriority().
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
|
||||||
err := scan()
|
|
||||||
if err != nil {
|
|
||||||
ctx.SendChain(message.Text("Error: " + err.Error()))
|
|
||||||
} else {
|
|
||||||
ctx.SendChain(message.Text("成功!"))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
zero.OnCommand("加载插件", zero.SuperUserPermission).SetBlock(true).FirstPriority().
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
|
||||||
model := extension.CommandModel{}
|
|
||||||
_ = ctx.Parse(&model)
|
|
||||||
_, ok := control.Lookup(model.Args)
|
|
||||||
if !ok {
|
|
||||||
t := ".dll"
|
|
||||||
if typeIsSo {
|
|
||||||
t = ".so"
|
|
||||||
}
|
|
||||||
target := model.Args + t
|
|
||||||
logrus.Debugln("[dyloader]target:", target)
|
|
||||||
path := "plugins/" + target
|
|
||||||
err := open(path, target)
|
|
||||||
if err != nil {
|
|
||||||
ctx.SendChain(message.Text("Error: " + err.Error()))
|
|
||||||
} else {
|
|
||||||
ctx.SendChain(message.Text("成功!"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
zero.OnCommand("卸载插件", zero.SuperUserPermission).SetBlock(true).FirstPriority().
|
|
||||||
Handle(func(ctx *zero.Ctx) {
|
|
||||||
model := extension.CommandModel{}
|
|
||||||
_ = ctx.Parse(&model)
|
|
||||||
_, ok := control.Lookup(model.Args)
|
|
||||||
if ok {
|
|
||||||
t := ".dll"
|
|
||||||
if typeIsSo {
|
|
||||||
t = ".so"
|
|
||||||
}
|
|
||||||
target := model.Args + t
|
|
||||||
logrus.Debugln("[dyloader]target:", target)
|
|
||||||
pluginsMu.Lock()
|
|
||||||
p, ok := plugins[target]
|
|
||||||
pluginsMu.Unlock()
|
|
||||||
if ok {
|
|
||||||
err := plugin.Close(p)
|
|
||||||
control.Delete(model.Args)
|
|
||||||
pluginsMu.Lock()
|
|
||||||
delete(plugins, target)
|
|
||||||
pluginsMu.Unlock()
|
|
||||||
if err != nil {
|
|
||||||
ctx.SendChain(message.Text("Error: " + err.Error()))
|
|
||||||
} else {
|
|
||||||
ctx.SendChain(message.Text("成功!"))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ctx.SendChain(message.Text("没有这个插件!"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
go func() {
|
|
||||||
time.Sleep(time.Second * 2)
|
|
||||||
_ = scan()
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
func scan() error {
|
|
||||||
return filepath.WalkDir("plugins/", load)
|
|
||||||
}
|
|
||||||
|
|
||||||
func open(path, target string) error {
|
|
||||||
pluginsMu.Lock()
|
|
||||||
_, ok := plugins[target]
|
|
||||||
pluginsMu.Unlock()
|
|
||||||
if !ok {
|
|
||||||
p, err := plugin.Open(path)
|
|
||||||
var initfunc, hookfunc, ishooked plugin.Symbol
|
|
||||||
if err == nil {
|
|
||||||
ishooked, err = p.Lookup("IsHooked")
|
|
||||||
if err == nil {
|
|
||||||
if !*ishooked.(*bool) {
|
|
||||||
hookfunc, err = p.Lookup("Hook")
|
|
||||||
if err == nil {
|
|
||||||
logrus.Debugf("[dyloader]reg: %x, del: %x\n", control.Register, control.Delete)
|
|
||||||
logrus.Debugf("[dyloader]matlist: %p, matlock: %p\n", &matcherList, &matcherLock)
|
|
||||||
hookfunc.(func(interface{}, interface{}, interface{},
|
|
||||||
interface{}, interface{}, interface{},
|
|
||||||
interface{}, interface{},
|
|
||||||
interface{}, interface{}, interface{},
|
|
||||||
interface{},
|
|
||||||
interface{}, interface{}, interface{},
|
|
||||||
))(
|
|
||||||
&zero.BotConfig, &zero.APICallers, zero.New,
|
|
||||||
&matcherList, &matcherLock, defaultEngine,
|
|
||||||
control.Register, control.Delete,
|
|
||||||
sendGroupMessage, sendPrivateMessage, getMessage,
|
|
||||||
parse,
|
|
||||||
message.CustomNode, message.ParseMessage, message.ParseMessageFromArray,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
_ = plugin.Close(p)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
initfunc, err = p.Lookup("Inita")
|
|
||||||
if err == nil {
|
|
||||||
initfunc.(func())()
|
|
||||||
logrus.Infoln("[dyloader]加载插件", path, "成功")
|
|
||||||
pluginsMu.Lock()
|
|
||||||
plugins[target] = p
|
|
||||||
pluginsMu.Unlock()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ = plugin.Close(p)
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
logrus.Errorln("[dyloader]加载插件", path, "错误:", err)
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func load(path string, d fs.DirEntry, err error) error {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if d.IsDir() {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
n := d.Name()
|
|
||||||
if !visited {
|
|
||||||
if strings.HasSuffix(n, ".so") {
|
|
||||||
typeIsSo = true
|
|
||||||
visited = true
|
|
||||||
} else if strings.HasSuffix(n, ".dll") {
|
|
||||||
visited = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if strings.HasSuffix(n, ".so") || strings.HasSuffix(n, ".dll") {
|
|
||||||
target := path[strings.LastIndex(path, "/")+1:]
|
|
||||||
logrus.Debugln("[dyloader]target:", target)
|
|
||||||
return open(path, target)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
4
main.go
4
main.go
@ -48,7 +48,7 @@ import (
|
|||||||
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_setutime" // 来份涩图
|
_ "github.com/FloatTech/ZeroBot-Plugin/plugin_setutime" // 来份涩图
|
||||||
|
|
||||||
// 以下为内置依赖,勿动
|
// 以下为内置依赖,勿动
|
||||||
// _ "github.com/FloatTech/ZeroBot-Plugin/dyloader"
|
// _ "github.com/FloatTech/ZeroBot-Plugin-Dynamic/dyloader"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
zero "github.com/wdvxdr1123/ZeroBot"
|
zero "github.com/wdvxdr1123/ZeroBot"
|
||||||
"github.com/wdvxdr1123/ZeroBot/driver"
|
"github.com/wdvxdr1123/ZeroBot/driver"
|
||||||
@ -57,7 +57,7 @@ import (
|
|||||||
var (
|
var (
|
||||||
contents = []string{
|
contents = []string{
|
||||||
"* OneBot + ZeroBot + Golang",
|
"* OneBot + ZeroBot + Golang",
|
||||||
"* Version 1.1.6 - 2021-10-09 12:50:23 +0800 CST",
|
"* Version 1.1.7 - 2021-10-09 12:50:23 +0800 CST",
|
||||||
"* Copyright © 2020 - 2021 Kanri, DawnNights, Fumiama, Suika",
|
"* Copyright © 2020 - 2021 Kanri, DawnNights, Fumiama, Suika",
|
||||||
"* Project: https://github.com/FloatTech/ZeroBot-Plugin",
|
"* Project: https://github.com/FloatTech/ZeroBot-Plugin",
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user