grpc_plugins.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. package core
  2. import (
  3. "bufio"
  4. "crypto/sha1"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "os/exec"
  9. "path/filepath"
  10. "strings"
  11. "sync"
  12. "time"
  13. "github.com/cdle/sillyplus/core/common"
  14. "github.com/cdle/sillyplus/core/storage"
  15. "github.com/cdle/sillyplus/utils"
  16. "github.com/dop251/goja"
  17. "github.com/fsnotify/fsnotify"
  18. "github.com/google/uuid"
  19. cron "github.com/robfig/cron/v3"
  20. )
  21. func init() {
  22. go initNodePlugins()
  23. }
  24. var processes sync.Map
  25. func initNodePlugins() {
  26. initLanguage()
  27. root := strings.ReplaceAll(utils.ExecPath+"/plugins", "\\", "/")
  28. plugins := []string{root}
  29. os.Mkdir(root, 0755)
  30. // fmt.Println("root", root)
  31. files, _ := ioutil.ReadDir(root)
  32. for _, file := range files {
  33. if !file.IsDir() {
  34. continue
  35. }
  36. name := file.Name()
  37. path := root + "/" + name
  38. plugins = append(plugins, path)
  39. index, class := FindMainIndex(path)
  40. if index != "" {
  41. AddNodePlugin(index, name, class)
  42. }
  43. }
  44. watcher, err := fsnotify.NewWatcher()
  45. if err != nil {
  46. fmt.Println("创建监视器失败:", err)
  47. return
  48. }
  49. defer watcher.Close()
  50. // 要监控的文件夹路径
  51. for _, dir := range plugins {
  52. err = watcher.Add(dir)
  53. if err != nil {
  54. fmt.Println("添加监视目录失败:", err)
  55. return
  56. }
  57. }
  58. for {
  59. select {
  60. case event, ok := <-watcher.Events:
  61. if !ok {
  62. return
  63. }
  64. // fmt.Println(event.Name, "op", event.Op.String())
  65. event.Name = strings.ReplaceAll(event.Name, "\\", "/")
  66. files := strings.Split(strings.Replace(event.Name, root+"/", "", 1), "/")
  67. var plugin_dir = false
  68. var plugin_index = false
  69. var plugin_name = ""
  70. var class = ""
  71. switch len(files) {
  72. case 1:
  73. plugin_dir = true
  74. // fmt.Println("目录事件")
  75. plugin_name = files[0]
  76. case 2:
  77. class, plugin_index = CheckMainIndex(files[1])
  78. // if files[1] == "main.js" {
  79. // if files[1] == "main.js" {
  80. // plugin_index = true
  81. // }
  82. // // fmt.Println("入口文件事件")
  83. // }
  84. plugin_name = files[0]
  85. }
  86. if plugin_name == "." {
  87. continue
  88. }
  89. switch event.Op.String() {
  90. case "CREATE":
  91. if plugin_dir {
  92. info, err := os.Stat(event.Name)
  93. // fmt.Println(err)
  94. if err == nil && info.IsDir() {
  95. index, class := FindMainIndex(event.Name)
  96. switch class {
  97. case NODE:
  98. AddNodePlugin(index, plugin_name, class)
  99. case PYTHON:
  100. case "":
  101. // time.Sleep(time.Millisecond * 100) //如果没有
  102. // if info, err := os.Stat(event.Name + "/demo.main.js"); err == nil && !info.IsDir() {
  103. // }
  104. }
  105. // event_name := event.Name + "/main.js"
  106. // if info, err := os.Stat(event_name); err == nil && !info.IsDir() {
  107. // AddNodePlugin(event_name, plugin_name)
  108. // } else {
  109. // time.Sleep(time.Millisecond * 100)
  110. // if info, err := os.Stat(event_name); err == nil && !info.IsDir() {
  111. // AddNodePlugin(event_name, plugin_name)
  112. // }
  113. // }
  114. watcher.Add(event.Name)
  115. // fmt.Println("增加插件目录", event.Name)
  116. } else {
  117. // fmt.Println("非插件目录", event.Name)
  118. }
  119. tf := event.Name + "/node_modules/sillygirl.d.ts"
  120. ti := event.Name + "/demo.main.js"
  121. if _, err := os.Stat(tf); err != nil {
  122. os.Mkdir(event.Name+"/node_modules", 0700)
  123. os.WriteFile(tf, []byte(typeat), 0700)
  124. }
  125. go func() {
  126. time.Sleep(time.Second)
  127. if _, err := os.Stat(ti); err != nil {
  128. os.Mkdir(event.Name+"/node_modules", 0700)
  129. os.WriteFile(ti, []byte(defaultScript(plugin_name)), 0700)
  130. }
  131. }()
  132. } else if plugin_index {
  133. // fmt.Println("增加插件", event.Name)
  134. // RemNodePlugin(plugin_name)
  135. AddNodePlugin(event.Name, plugin_name, class)
  136. }
  137. case "REMOVE", "RENAME", "REMOVE|RENAME", "REMOVE|WRITE":
  138. if plugin_dir {
  139. watcher.Remove(event.Name)
  140. // fmt.Println("移除插件目录", event.Name)
  141. // fmt.Println("移除插件", plugin_name)
  142. AddNodePlugin(event.Name, plugin_name, UNKNOWN)
  143. } else if plugin_index {
  144. // fmt.Println("移除插件", plugin_name)
  145. AddNodePlugin(event.Name, plugin_name, class)
  146. }
  147. case "WRITE": //, "CHMOD"
  148. if plugin_index {
  149. AddNodePlugin(event.Name, plugin_name, class)
  150. // fmt.Println("变更插件", event.Name, plugin_name)
  151. }
  152. }
  153. case err, ok := <-watcher.Errors:
  154. if !ok {
  155. return
  156. }
  157. fmt.Println("错误:", err)
  158. }
  159. }
  160. }
  161. func nameUuid(name string) string {
  162. hash := sha1.Sum([]byte(name))
  163. return strings.ReplaceAll(uuid.NewSHA1(uuid.Nil, hash[:]).String(), "-", "_")
  164. }
  165. func isNameUuid(uuid string) bool {
  166. return strings.Contains(uuid, "_")
  167. }
  168. func AddNodePlugin(path, name, class string) error {
  169. if name == "" {
  170. return nil
  171. }
  172. uuid := nameUuid(name)
  173. pluginLock.Lock()
  174. defer pluginLock.Unlock()
  175. //移除
  176. var rf *common.Function
  177. for i := range Functions {
  178. if Functions[i].UUID == uuid {
  179. rf = Functions[i]
  180. DestroyAdapterByUUID(uuid)
  181. Functions[i].Running = false
  182. if len(Functions[i].CronIds) != 0 {
  183. for _, id := range Functions[i].CronIds {
  184. CRON.Remove(cron.EntryID(id))
  185. }
  186. }
  187. Functions = append(Functions[:i], Functions[i+1:]...)
  188. CancelPluginCrons(uuid)
  189. CancelPluginWebs(uuid)
  190. CancelPluginlistening(uuid)
  191. CancelHttpListen(uuid)
  192. remStatic(uuid)
  193. storage.DisableHandle(uuid)
  194. break
  195. }
  196. }
  197. file, err := os.Open(path)
  198. if err != nil {
  199. if rf != nil {
  200. console.Log("已卸载 %s%s", rf.Title, rf.Suffix)
  201. }
  202. return err
  203. }
  204. defer file.Close()
  205. data, err := ioutil.ReadAll(file)
  206. if err != nil {
  207. return err
  208. }
  209. script := string(data)
  210. if script == "" {
  211. return nil
  212. }
  213. // plugins_id.Store(uuid, path)
  214. // fmt.Println("add,", uuid, name)
  215. f, cbs := pluginParse(script, uuid)
  216. f.Reload = func() { //重载
  217. AddNodePlugin(path, name, class)
  218. }
  219. f.Type = class
  220. switch f.Type {
  221. case NODE:
  222. f.Suffix = ".js"
  223. case PYTHON:
  224. f.Suffix = ".py"
  225. }
  226. f.Path = path
  227. f.Handle = func(s common.Sender, _ func(vm *goja.Runtime)) interface{} {
  228. console := &Console{UUID: uuid}
  229. s.SetPluginID(uuid)
  230. plt := s.GetImType()
  231. bin := ""
  232. var cmd *exec.Cmd
  233. switch class {
  234. case NODE:
  235. bin = utils.ExecPath + "/language/node/node"
  236. cmd = exec.Command(bin, path)
  237. case PYTHON:
  238. bin = "python3"
  239. cmd = exec.Command(bin, "-u", path)
  240. cmd.Env = append(cmd.Env, "PYTHONPATH=/Users/a1-6/Code/sillyplus/proto3")
  241. }
  242. cmd.Dir = filepath.Dir(path)
  243. RUNTIME_ID := utils.GenUUID()
  244. cmd.Env = append(cmd.Env, "RUNTIME_ID="+RUNTIME_ID)
  245. cmd.Env = append(cmd.Env, "PLUGIN_ID="+uuid)
  246. // 获取标准输出和标准错误输出的管道
  247. stdout, err := cmd.StdoutPipe()
  248. if err != nil {
  249. // fmt.Printf("获取标准输出管道失败:%v\n", err)
  250. // os.Exit(1)
  251. }
  252. stderr, err := cmd.StderrPipe()
  253. if err != nil {
  254. // fmt.Printf("获取标准错误输出管道失败:%v\n", err)
  255. // os.Exit(1)
  256. }
  257. // file, err := os.Create("output.log")
  258. // if err != nil {
  259. // fmt.Printf("创建文件失败:%v\n", err)
  260. // os.Exit(1)
  261. // }
  262. // defer file.Close()
  263. var wg sync.WaitGroup
  264. wg.Add(2)
  265. // 处理标准输出
  266. go func() {
  267. defer wg.Done()
  268. scanner := bufio.NewScanner(stdout)
  269. for scanner.Scan() {
  270. data := scanner.Text()
  271. fmt.Println(data)
  272. // if _, err := file.WriteString(data + "\n"); err != nil {
  273. // fmt.Printf("写入文件失败:%v\n", err)
  274. // }
  275. }
  276. }()
  277. // 处理标准错误输出
  278. go func() {
  279. defer wg.Done()
  280. scanner := bufio.NewScanner(stderr)
  281. if f.OnStart {
  282. for scanner.Scan() {
  283. fmt.Println(scanner.Text())
  284. }
  285. } else {
  286. lines := []string{}
  287. for scanner.Scan() {
  288. data := scanner.Text()
  289. lines = append(lines, data)
  290. }
  291. if len(lines) != 0 {
  292. console.Error(strings.Join(lines, "\n"))
  293. }
  294. }
  295. }()
  296. processes.Store(cmd, s)
  297. register := createSenderRegister(RUNTIME_ID)
  298. if (plt) != "*" {
  299. cmd.Env = append(cmd.Env, "SENDER_ID="+register(s))
  300. err = cmd.Start()
  301. if err != nil {
  302. }
  303. defer deleteSenderRegister(RUNTIME_ID)
  304. defer processes.Delete(cmd)
  305. err = cmd.Wait()
  306. if err != nil {
  307. fmt.Println("命令执行失败:", err)
  308. return nil
  309. }
  310. } else {
  311. err = cmd.Start()
  312. if err != nil {
  313. }
  314. processes.Range(func(key, value any) bool {
  315. p := key.(*exec.Cmd)
  316. if p == cmd {
  317. return true
  318. }
  319. s := value.(common.Sender)
  320. if s.GetPluginID() == uuid {
  321. func() {
  322. defer func() {
  323. recover()
  324. }()
  325. if p.Process.Kill() == nil {
  326. processes.Delete(key)
  327. }
  328. }()
  329. }
  330. return true
  331. })
  332. go func() {
  333. defer deleteSenderRegister(RUNTIME_ID)
  334. defer processes.Delete(cmd)
  335. err = cmd.Wait()
  336. }()
  337. }
  338. return nil
  339. }
  340. for _, cb := range cbs {
  341. cb()
  342. }
  343. if !f.Disable { //!f.OnStart &&
  344. if rf == nil {
  345. // console.Log("已加载 %s%s", f.Title, f.Suffix)
  346. } else {
  347. console.Log("已重载 %s%s", f.Title, f.Suffix)
  348. }
  349. }
  350. AddCommand([]*common.Function{f})
  351. return nil
  352. }
  353. var typeat = `declare class Sender {
  354. private uuid;
  355. private destoried;
  356. constructor(uuid: string);
  357. destroy(): void;
  358. getUserId(): Promise<string>;
  359. getUserName(): Promise<string>;
  360. getChatId(): Promise<string>;
  361. getChatName(): Promise<string>;
  362. getMessageId(): Promise<string>;
  363. getPlatform(): Promise<string>;
  364. getBotId(): Promise<string>;
  365. getContent(): Promise<string>;
  366. isAdmin(): Promise<boolean>;
  367. param(key: number | string): Promise<string>;
  368. setContent(content: string): Promise<undefined>;
  369. continue(): Promise<undefined>;
  370. getAdapter(): Promise<Adapter>;
  371. listen(options?: {
  372. rules?: string[];
  373. timeout?: number;
  374. handle?: (s: Sender) => Promise<string | void> | string | void;
  375. listen_private?: boolean;
  376. listen_group?: boolean;
  377. allow_platforms?: string[];
  378. prohibit_platforms?: string[];
  379. allow_groups?: string[];
  380. prohibit_groups?: string[];
  381. allow_users?: string[];
  382. prohibit_users?: string[];
  383. }): Promise<Sender | undefined>;
  384. holdOn(str: string): string;
  385. reply(content: string): Promise<string>;
  386. doAction(options: Record<string, any>): Promise<any>;
  387. getEvent(): Promise<Record<string, any>>;
  388. }
  389. declare class Bucket {
  390. private name;
  391. constructor(name: string);
  392. transform(v: string | undefined): string | number | boolean | undefined;
  393. reverseTransform(value: any): string;
  394. get(key: string, defaultValue?: any): Promise<any>;
  395. set(key: string, value: any): Promise<{
  396. message?: string;
  397. changed?: boolean;
  398. }>;
  399. getAll(): Promise<Record<string, any>>;
  400. delete(key: string): Promise<{
  401. message?: string;
  402. changed?: boolean;
  403. }>;
  404. deleteAll(): Promise<undefined>;
  405. keys(): Promise<string[]>;
  406. len(): Promise<number>;
  407. buckets(): Promise<string[]>;
  408. watch(key: string, handle: (old: any, now: any, key: string) => StorageModifier | void): void;
  409. getName(): Promise<string>;
  410. }
  411. interface StorageModifier {
  412. echo?: string;
  413. now?: any;
  414. message?: string;
  415. error?: string;
  416. }
  417. interface Message {
  418. message_id?: string;
  419. user_id: string;
  420. chat_id?: string;
  421. content: string;
  422. user_name?: string;
  423. chat_name?: string;
  424. }
  425. declare class Adapter {
  426. platform: string;
  427. bot_id: string;
  428. call: any;
  429. constructor(options: {
  430. platform: string;
  431. bot_id: string;
  432. replyHandler?: (message: Message) => Promise<string | undefined>;
  433. actionHandler?: (message: Message) => Promise<string | undefined>;
  434. });
  435. receive(message: Message): Promise<undefined>;
  436. push(message: Message): Promise<string>;
  437. destroy(): Promise<void>;
  438. sender(options: any): Promise<Sender>;
  439. }
  440. declare let sender: Sender;
  441. declare function sleep(ms?: number): Promise<unknown>;
  442. interface CQItem {
  443. type: string;
  444. params: Record<string, string>;
  445. }
  446. interface CQParams {
  447. [key: string]: string | number | boolean;
  448. }
  449. declare let utils: {
  450. buildCQTag: (type: string, params: CQParams, prefix?: string) => string;
  451. parseCQText: (text: string, prefix?: string) => (string | CQItem)[];
  452. image: (url: string) => string;
  453. video: (url: string) => string;
  454. };
  455. declare let console: {
  456. log(...args: any[]): void;
  457. info(...args: any[]): void;
  458. error(...args: any[]): void;
  459. debug(...args: any[]): void;
  460. };
  461. export { Adapter, Bucket, sender, sleep, utils, console };
  462. `
  463. func defaultScript(title string) string {
  464. create_at := time.Now().Format("2006-01-02 15:04:05")
  465. return `/**
  466. * @title ` + title + `
  467. * @create_at ` + create_at + `
  468. * @description 🐒这个人很懒什么都没有留下
  469. * @author ` + sillyGirl.GetString("author", "佚名") + `
  470. * @version v1.0.0
  471. */
  472. const {
  473. sender: s,
  474. Bucket,
  475. utils: { buildCQTag, image, video },
  476. } = require("sillygirl");
  477. `
  478. }
  479. const (
  480. NODE = "node"
  481. PYTHON = "python3"
  482. UNKNOWN = "unknown"
  483. )
  484. func FindMainIndex(home string) (string, string) {
  485. if info, err := os.Stat(home + "/main.js"); err == nil && !info.IsDir() {
  486. return home + "/main.js", NODE
  487. }
  488. if info, err := os.Stat(home + "/main.py"); err == nil && !info.IsDir() {
  489. return home + "/main.py", PYTHON
  490. }
  491. return "", ""
  492. }
  493. func CheckMainIndex(filename string) (string, bool) {
  494. switch filename {
  495. case "main.js":
  496. return NODE, true
  497. case "main.py":
  498. return PYTHON, true
  499. }
  500. return "", false
  501. }