You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

182 lines
7.3KB

  1. # encoding:utf-8
  2. import importlib
  3. import json
  4. import os
  5. from common.singleton import singleton
  6. from common.sorted_dict import SortedDict
  7. from .event import *
  8. from common.log import logger
  9. from config import conf
  10. @singleton
  11. class PluginManager:
  12. def __init__(self):
  13. self.plugins = SortedDict(lambda k,v: v.priority,reverse=True)
  14. self.listening_plugins = {}
  15. self.instances = {}
  16. self.pconf = {}
  17. def register(self, name: str, desire_priority: int = 0, **kwargs):
  18. def wrapper(plugincls):
  19. plugincls.name = name
  20. plugincls.priority = desire_priority
  21. plugincls.desc = kwargs.get('desc')
  22. plugincls.author = kwargs.get('author')
  23. plugincls.version = kwargs.get('version') if kwargs.get('version') != None else "1.0"
  24. plugincls.namecn = kwargs.get('namecn') if kwargs.get('namecn') != None else name
  25. plugincls.hidden = kwargs.get('hidden') if kwargs.get('hidden') != None else False
  26. plugincls.enabled = True
  27. self.plugins[name.upper()] = plugincls
  28. logger.info("Plugin %s_v%s registered" % (name, plugincls.version))
  29. return plugincls
  30. return wrapper
  31. def save_config(self):
  32. with open("./plugins/plugins.json", "w", encoding="utf-8") as f:
  33. json.dump(self.pconf, f, indent=4, ensure_ascii=False)
  34. def load_config(self):
  35. logger.info("Loading plugins config...")
  36. modified = False
  37. if os.path.exists("./plugins/plugins.json"):
  38. with open("./plugins/plugins.json", "r", encoding="utf-8") as f:
  39. pconf = json.load(f)
  40. pconf['plugins'] = SortedDict(lambda k,v: v["priority"],pconf['plugins'],reverse=True)
  41. else:
  42. modified = True
  43. pconf = {"plugins": SortedDict(lambda k,v: v["priority"],reverse=True)}
  44. self.pconf = pconf
  45. if modified:
  46. self.save_config()
  47. return pconf
  48. def scan_plugins(self):
  49. logger.info("Scaning plugins ...")
  50. plugins_dir = "./plugins"
  51. for plugin_name in os.listdir(plugins_dir):
  52. plugin_path = os.path.join(plugins_dir, plugin_name)
  53. if os.path.isdir(plugin_path):
  54. # 判断插件是否包含同名.py文件
  55. main_module_path = os.path.join(plugin_path, plugin_name+".py")
  56. if os.path.isfile(main_module_path):
  57. # 导入插件
  58. import_path = "plugins.{}.{}".format(plugin_name, plugin_name)
  59. try:
  60. main_module = importlib.import_module(import_path)
  61. except Exception as e:
  62. logger.warn("Failed to import plugin %s: %s" % (plugin_name, e))
  63. continue
  64. pconf = self.pconf
  65. new_plugins = []
  66. modified = False
  67. for name, plugincls in self.plugins.items():
  68. rawname = plugincls.name
  69. if rawname not in pconf["plugins"]:
  70. new_plugins.append(plugincls)
  71. modified = True
  72. logger.info("Plugin %s not found in pconfig, adding to pconfig..." % name)
  73. pconf["plugins"][rawname] = {"enabled": plugincls.enabled, "priority": plugincls.priority}
  74. else:
  75. self.plugins[name].enabled = pconf["plugins"][rawname]["enabled"]
  76. self.plugins[name].priority = pconf["plugins"][rawname]["priority"]
  77. self.plugins._update_heap(name) # 更新下plugins中的顺序
  78. if modified:
  79. self.save_config()
  80. return new_plugins
  81. def refresh_order(self):
  82. for event in self.listening_plugins.keys():
  83. self.listening_plugins[event].sort(key=lambda name: self.plugins[name].priority, reverse=True)
  84. def activate_plugins(self): # 生成新开启的插件实例
  85. for name, plugincls in self.plugins.items():
  86. if plugincls.enabled:
  87. if name not in self.instances:
  88. try:
  89. instance = plugincls()
  90. except Exception as e:
  91. logger.warn("Failed to create init %s, diabled. %s" % (name, e))
  92. self.disable_plugin(name)
  93. continue
  94. self.instances[name] = instance
  95. for event in instance.handlers:
  96. if event not in self.listening_plugins:
  97. self.listening_plugins[event] = []
  98. self.listening_plugins[event].append(name)
  99. self.refresh_order()
  100. def reload_plugin(self, name:str):
  101. name = name.upper()
  102. if name in self.instances:
  103. for event in self.listening_plugins:
  104. if name in self.listening_plugins[event]:
  105. self.listening_plugins[event].remove(name)
  106. del self.instances[name]
  107. self.activate_plugins()
  108. return True
  109. return False
  110. def load_plugins(self):
  111. self.load_config()
  112. self.scan_plugins()
  113. pconf = self.pconf
  114. logger.debug("plugins.json config={}".format(pconf))
  115. for name,plugin in pconf["plugins"].items():
  116. if name.upper() not in self.plugins:
  117. logger.error("Plugin %s not found, but found in plugins.json" % name)
  118. self.activate_plugins()
  119. def emit_event(self, e_context: EventContext, *args, **kwargs):
  120. if e_context.event in self.listening_plugins:
  121. for name in self.listening_plugins[e_context.event]:
  122. if self.plugins[name].enabled and e_context.action == EventAction.CONTINUE:
  123. logger.debug("Plugin %s triggered by event %s" % (name,e_context.event))
  124. instance = self.instances[name]
  125. instance.handlers[e_context.event](e_context, *args, **kwargs)
  126. return e_context
  127. def set_plugin_priority(self, name:str, priority:int):
  128. name = name.upper()
  129. if name not in self.plugins:
  130. return False
  131. if self.plugins[name].priority == priority:
  132. return True
  133. self.plugins[name].priority = priority
  134. self.plugins._update_heap(name)
  135. rawname = self.plugins[name].name
  136. self.pconf["plugins"][rawname]["priority"] = priority
  137. self.pconf["plugins"]._update_heap(rawname)
  138. self.save_config()
  139. self.refresh_order()
  140. return True
  141. def enable_plugin(self, name:str):
  142. name = name.upper()
  143. if name not in self.plugins:
  144. return False
  145. if not self.plugins[name].enabled :
  146. self.plugins[name].enabled = True
  147. rawname = self.plugins[name].name
  148. self.pconf["plugins"][rawname]["enabled"] = True
  149. self.save_config()
  150. self.activate_plugins()
  151. return True
  152. return True
  153. def disable_plugin(self, name:str):
  154. name = name.upper()
  155. if name not in self.plugins:
  156. return False
  157. if self.plugins[name].enabled :
  158. self.plugins[name].enabled = False
  159. rawname = self.plugins[name].name
  160. self.pconf["plugins"][rawname]["enabled"] = False
  161. self.save_config()
  162. return True
  163. return True
  164. def list_plugins(self):
  165. return self.plugins