| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543 |
- package core
- import (
- "errors"
- "io"
- "net/http"
- "os"
- "os/exec"
- "path/filepath"
- "reflect"
- "regexp"
- "strings"
- "sync"
- "time"
- "github.com/cdle/sillyplus/core/common"
- "github.com/cdle/sillyplus/core/storage"
- "github.com/cdle/sillyplus/utils"
- "github.com/dop251/goja"
- "github.com/dop251/goja/parser"
- "github.com/dop251/goja_nodejs/eventloop"
- "github.com/robfig/cron/v3"
- )
- var pluginLock = new(sync.Mutex)
- type myFieldNameMapper struct{}
- var mutexMap = make(map[string]*sync.Mutex)
- var mutexMapMutex sync.Mutex
- func GetMutex(uuid string) *sync.Mutex {
- mutexMapMutex.Lock()
- defer mutexMapMutex.Unlock()
- if mutex, ok := mutexMap[uuid]; ok {
- return mutex
- }
- mutex := &sync.Mutex{}
- mutexMap[uuid] = mutex
- return mutex
- }
- func (tfm myFieldNameMapper) FieldName(_ reflect.Type, f reflect.StructField) string {
- tag := f.Tag.Get(`json`)
- if idx := strings.IndexByte(tag, ','); idx != -1 {
- tag = tag[:idx]
- }
- if parser.IsIdentifier(tag) {
- return tag
- }
- return uncapitalize(f.Name)
- }
- func uncapitalize(s string) string {
- return strings.ToLower(s[0:1]) + s[1:]
- }
- func (tfm myFieldNameMapper) MethodName(_ reflect.Type, m reflect.Method) string {
- return uncapitalize(m.Name)
- }
- var RegistFuncs = map[string]interface{}{}
- var plugins = MakeBucket("plugins")
- type Route struct {
- Path string `json:"path"`
- Name string `json:"name"`
- Component string `json:"component,omitempty"`
- Routes []Route `json:"routes,omitempty"`
- // Key string `json:"key,omitempty"`
- CreateAt string `json:"create_at"`
- }
- func CancelPluginlistening(uuid string) {
- // logs.Info(`k, c.Function, c.Function.Rules`)
- for _, wait := range waits {
- wait.Foreach(func(k int64, c *Carry) bool {
- if uuid == c.UUID {
- c.Chan <- errors.New("uinstall")
- }
- return true
- })
- }
- }
- var debug = sillyGirl.GetBool("debug", false)
- func initPlugins() {
- storage.Watch(sillyGirl, "debug", func(old, new, key string) *storage.Final {
- debug = new == "true"
- return nil
- })
- plugins.Foreach(func(key, data []byte) error {
- uuid := string(key)
- if isNameUuid(uuid) {
- return nil
- }
- pluginLock.Lock()
- defer pluginLock.Unlock()
- f, cbs, err := initPlugin(string(data), uuid, "")
- if err != nil {
- console.Error("初始化插件%s错误: %v", key, err)
- }
- for _, cb := range cbs {
- cb()
- }
- AddCommand([]*common.Function{f})
- // os.WriteFile(fmt.Sprintf("%s/%s.js", pluginPath, f.Title), data, 0600)
- return nil
- })
- storage.Watch(plugins, nil, func(old, new, key string) (fin *storage.Final) {
- pluginLock.Lock()
- defer pluginLock.Unlock()
- if new == "uninstall" {
- new = ""
- fin = &storage.Final{
- Now: storage.EMPTY,
- }
- }
- if isNameUuid(key) {
- if new == "install" {
- for _, p := range plugin_list {
- if p.UUID != key {
- continue
- }
- var prefix = "?uuid=" + p.UUID
- address := p.Address
- if !strings.HasSuffix(address, "list.json") {
- address = address + "/api/plugins/download" + prefix
- } else {
- address = strings.ReplaceAll(address, "list.json", "download"+prefix)
- }
- resp, err := http.Get(address)
- if err != nil {
- return &storage.Final{
- Error: errors.New("插件源异常!"),
- }
- }
- zipfile := plugin_dir + "/" + utils.GenUUID() + ".zip"
- err = func() error {
- defer resp.Body.Close()
- f, err := os.OpenFile(zipfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
- if err != nil {
- return errors.New("打开文件异常:" + err.Error())
- }
- defer f.Close()
- _, err = io.Copy(f, resp.Body)
- if err != nil {
- return errors.New("文件写入异常:" + err.Error())
- }
- return nil
- }()
- if err != nil {
- return &storage.Final{
- Error: err,
- }
- }
- defer os.Remove(zipfile)
- if err := unzip(zipfile, 0755, true); err != nil {
- return &storage.Final{
- Error: errors.New("安装异常!" + err.Error()),
- }
- }
- return &storage.Final{
- Now: "",
- }
- }
- }
- {
- for _, f := range Functions {
- if f.UUID == key {
- filename := f.Path
- if new == "reload" { //重载
- go f.Reload()
- } else if new == "" {
- // fmt.Println("new", new)
- ss := strings.Split(filename, "/")
- processes.Range(func(key, value any) bool { //先停止脚本,避免windows锁定文件
- p := key.(*exec.Cmd)
- s := value.(common.Sender)
- if s.GetPluginID() == f.UUID {
- console.Log("已终止 % s", f.Title)
- func() {
- defer func() {
- recover()
- }()
- if p.Process.Kill() == nil {
- processes.Delete(key)
- }
- }()
- }
- return true
- })
- os.RemoveAll(filepath.Dir(filename))
- // fmt.Println(strings.Join(ss, " "))
- l := len(ss)
- if l > 2 {
- go AddNodePlugin(filename, ss[l-1], f.Type)
- }
- } else {
- err := os.WriteFile(filename, []byte(new), 0755)
- if err != nil {
- return &storage.Final{
- Error: err,
- }
- }
- }
- }
- }
- }
- if fin != nil {
- return fin
- }
- return &storage.Final{
- Now: "",
- }
- // }
- // return &storage.Final{
- // Error: errors.New("安装失败!!!"),
- // }
- }
- if new == "install" {
- for _, p := range plugin_list {
- // fmt.Println(p.UUID, p.UUID == key, p.Title)
- if p.UUID != key {
- continue
- }
- // fmt.Println("(p.Type", p.Type)
- script := string(fetchScript(p.Address, key))
- if f, _, _ := initPlugin(script, p.UUID, ""); f.CreateAt != "" {
- fin = &storage.Final{
- Now: script,
- }
- fin = &storage.Final{}
- su := &ScriptUtils{script: script}
- su.SetValue("origin", p.Organization)
- new = su.script
- break
- } else {
- return &storage.Final{
- Error: errors.New("订阅源异常"),
- }
- }
- }
- }
- if new != "" {
- if new == "reload" {
- new = old
- }
- fin = &storage.Final{}
- su := &ScriptUtils{script: new}
- if version := su.GetValue("version"); version == "" || regexp.MustCompile(`v\d+\.\d+\.\d`).FindString(version) != version {
- su.SetValue("version", "v1.0.0")
- }
- if auhtor := su.GetValue("author"); auhtor == "" {
- su.SetValue("author", "佚名")
- }
- if su.GetValue("description") == "" {
- su.SetValue("description", "🐒这个人很懒什么都没有留下")
- } //module
- title := su.GetValue("title")
- if title == "" {
- su.SetValue("title", "无名脚本")
- }
- if message := GetMessageByUUID(key); message != "" {
- su.SetValue("message", message)
- }
- if title != "无名脚本" && title != "" {
- onStart := su.GetValue("on_start")
- if onStart != "true" {
- module := su.GetValue("module")
- if module != "true" {
- if module != "" {
- su.DeleteValue("module")
- }
- web := su.GetValue("web")
- if web != "true" {
- if web != "" {
- su.DeleteValue("web")
- }
- // rule := su.GetValue("rule")
- // if rule == "" {
- // su.SetValue("rule", title)
- // }
- } else {
- su.DeleteValue("rule")
- su.DeleteValue("cron")
- // su.DeleteValue("admin")
- su.DeleteValue("priority")
- su.DeleteValue("platform")
- }
- } else {
- su.DeleteValue("rule")
- su.DeleteValue("cron")
- su.DeleteValue("web")
- // su.DeleteValue("admin")
- su.DeleteValue("priority")
- su.DeleteValue("platform")
- }
- } else {
- // su.DeleteValue("rule")
- // su.DeleteValue("cron")
- su.DeleteValue("web")
- // su.DeleteValue("admin")
- su.DeleteValue("priority")
- su.DeleteValue("platform")
- su.DeleteValue("module")
- }
- }
- create_at := su.GetValue("create_at")
- if _, err := time.Parse("2006-01-02 15:04:05", create_at); err != nil {
- su.SetValue("create_at", time.Now().Format("2006-01-02 15:04:05"))
- }
- fin.Now = su.script
- if su.script != new {
- fin.Message = su.script
- } else if title != (&ScriptUtils{script: old}).GetValue("title") {
- fin.Message = "标题变更。"
- }
- new = su.script
- }
- f, cbs, err := initPlugin(new, key, "")
- if err != nil && new != "" {
- pluginConsole(key).Error(err)
- }
- apd := false
- for i := range Functions {
- if Functions[i].UUID == key {
- DestroyAdapterByUUID(key)
- Functions[i].Running = false
- if len(Functions[i].CronIds) != 0 {
- for _, id := range Functions[i].CronIds {
- CRON.Remove(cron.EntryID(id))
- }
- }
- Functions = append(Functions[:i], Functions[i+1:]...)
- CancelPluginCrons(key)
- CancelPluginWebs(key)
- CancelPluginlistening(key)
- CancelHttpListen(key)
- remStatic(key)
- storage.DisableHandle(key)
- if new != "" {
- AddCommand([]*common.Function{f})
- if !f.Disable {
- if old == "" {
- // console.Log("已加载 %s%s", f.Title, f.Suffix)
- } else { //if !f.OnStart
- console.Log("已重载 %s%s", f.Title, f.Suffix)
- }
- }
- } else {
- of, _, _ := initPlugin(old, key, "")
- console.Log("已卸载 %s%s", of.Title, of.Suffix)
- }
- apd = true
- break
- }
- }
- for _, cb := range cbs {
- cb()
- }
- if !apd {
- AddCommand([]*common.Function{f})
- }
- // if f.UUID != "" && f.Public {
- // go func() {
- // os.WriteFile(fmt.Sprintf("%s/%s.js", plugin_download_file, f.UUID), []byte(publicScript(plugins.GetString(f.UUID))), 0666)
- // os.WriteFile(plugin_path+"list.json", utils.JsonMarshal(GetPublicResponse()), 0666)
- // }()
- // }
- return
- })
- }
- func initPlugin(data string, uuid string, scriptType string) (*common.Function, []func(), error) {
- f, cbs := pluginParse(data, uuid)
- f.Suffix = ".js"
- f.Type = "goja"
- script := ""
- if f.Encrypt {
- script = DecryptPlugin(string(data))
- } else {
- script = string(data)
- }
- script = halfDeEct(script)
- script = strings.ReplaceAll(script, "new Sender", "Sender")
- script = strings.ReplaceAll(script, "new Bucket", "Bucket")
- // script = regexp.MustCompile(`import\s+\{\s*([^\}]+)\s*\}\s*from\s*['"]([^'"]+)['"]\s*;`).ReplaceAllString(script, "const {$1} = require('$2');")
- // script = regexp.MustCompile(`import\s+\s*([^\}]+)\s*\s*from\s*['"]([^'"]+)['"]\s*;`).ReplaceAllString(script, "const $1 = require('$2');")
- var err error
- prg, err2 := goja.Compile(f.Title+".js", script, false)
- if err == nil && err2 != nil {
- err = err2
- }
- // if err == nil && len(rules) == 0 && cron != "" {
- // err = fmt.Errorf("无效的脚本%s", title)
- // }
- // if icon == "" {
- // icon = "https://joeschmoe.io/api/v1/random?t=" + fmt.Sprint(time.Now().Nanosecond())
- // }
- var running func() bool
- f.Handle = func(s common.Sender, set func(vm *goja.Runtime)) interface{} {
- if !debug {
- defer func() {
- err := recover()
- if err != nil {
- pluginConsole(uuid).Error(err)
- // s.Reply(fmt.Sprint(err))
- }
- }()
- }
- if err2 != nil {
- panic(err2)
- }
- loop := eventloop.NewEventLoop()
- loop.Run(func(vm *goja.Runtime) {
- SetPluginMethod(vm, uuid, f.OnStart, running)
- ss := &SenderJsIplm{
- Message: s,
- Vm: vm,
- Private: "private",
- Group: "group",
- Routine: "routine",
- Persistent: "persistent",
- UUID: uuid,
- }
- vm.Set("msg", goja.Undefined())
- vm.Set("message", goja.Undefined())
- vm.Set("res", goja.Undefined())
- vm.Set("req", goja.Undefined())
- vm.Set("action", goja.Undefined())
- vm.Set("sender", ss)
- vm.Set("run", func(uuid string) bool { //执行子脚本
- fs := Functions
- for i := range fs {
- if fs[i].UUID == uuid {
- fs[i].Handle(s, nil)
- return true
- }
- }
- return false
- })
- vm.Set("s", ss)
- vm.Set("InitAdapter", func(plt, botid string, params map[string]interface{}) *Factory {
- f := &Factory{
- uuid: uuid,
- vm: vm,
- }
- f.Init(plt, botid, params)
- return f
- })
- vm.Set("initAdapter", func(plt, botid string, params map[string]interface{}) *Factory {
- f := &Factory{
- uuid: uuid,
- vm: vm,
- }
- f.Init(plt, botid, params)
- return f
- })
- getAdapter := func(plt, botid string) map[string]interface{} {
- adapter, err := GetAdapter(plt, botid)
- errstr := ""
- if err != nil {
- errstr = err.Error()
- }
- return map[string]interface{}{
- "error": errstr,
- "adapter": adapter,
- }
- }
- vm.Set("GetAdapter", getAdapter)
- vm.Set("getAdapter", getAdapter)
- vm.Set("getAdapterBotsID", GetAdapterBotsID)
- vm.Set("getAdapterBotPlts", GetAdapterBotPlts)
- vm.Set("GetAdapterBotsID", GetAdapterBotsID)
- vm.Set("GetAdapterBotPlts", GetAdapterBotPlts)
- vm.Set("running", running)
- vm.Set("Running", running)
- vm.Set("uuid", func() string {
- return uuid
- })
- vm.Set("genUuid", func() string {
- return utils.GenUUID()
- })
- vm.Set("genUUID", func() string {
- return utils.GenUUID()
- })
- vm.Set("UUID", func() string {
- return uuid
- })
- if set != nil {
- set(vm)
- }
- _, err := vm.RunProgram(prg)
- if err != nil {
- pluginConsole(uuid).Error(strings.ReplaceAll(strings.ReplaceAll(err.Error(), "node_modules/", ""), "github.com/dop251/goja_nodejs/require", ""))
- }
- })
- return nil
- }
- running = func() bool {
- return f.Running
- }
- return f, cbs, err
- }
- func GetFunctionByUUID(uuid string) *common.Function {
- for _, f := range Functions {
- if f.UUID == uuid {
- return f
- }
- }
- return nil
- }
- func ChatID(p interface{}) string {
- switch p := p.(type) {
- case int:
- if p == 0 {
- return ""
- } else {
- return utils.Itoa(p)
- }
- case string:
- return p
- case nil:
- return ""
- default:
- return utils.Itoa(p)
- }
- }
|