-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathplugin_manager.py
More file actions
292 lines (249 loc) · 10.1 KB
/
plugin_manager.py
File metadata and controls
292 lines (249 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
#!/usr/bin/env python3
"""
Plugin Manager - Dynamic loading and management of AlleyBot plugins
"""
import json
import sys
import importlib
import inspect
from pathlib import Path
from typing import Dict, Any, Callable, List
class AlleyBotPlugin:
"""Base class for all AlleyBot plugins"""
def __init__(self, config: Dict[str, Any]):
self.config = config
self.name = self.__class__.__name__
self.initialized = False
self.api = None
self.core = None
super().__init__() # Enable cooperative multiple inheritance
def initialize(self, api, core):
"""Initialize plugin with API and core access"""
self.api = api
self.core = core
self.initialized = True
def get_tasks(self) -> Dict[str, Dict[str, Any]]:
"""Return scheduled tasks for this plugin"""
return {}
def get_commands(self) -> Dict[str, Callable]:
"""Return CLI commands for this plugin"""
return {}
def get_endpoints(self) -> Dict[str, Callable]:
"""Return web endpoints for this plugin"""
return {}
def cleanup(self):
"""Cleanup resources"""
pass
class PluginManager:
"""Manages loading and execution of plugins"""
def __init__(self, plugin_dir: str = 'plugins'):
self.plugin_dir = Path(plugin_dir)
self.plugins: Dict[str, AlleyBotPlugin] = {}
self.tasks: Dict[str, Dict[str, Any]] = {}
self.commands: Dict[str, Callable] = {}
self.endpoints: Dict[str, Callable] = {}
def load_plugins(self, config_file: str, api, core):
"""Load all enabled plugins from configuration"""
config_path = Path(config_file)
# Create default config if it doesn't exist
if not config_path.exists():
self._create_default_config(config_path)
# Load configuration
with open(config_path, 'r') as f:
config = json.load(f)
# Load each enabled plugin
for plugin_name, plugin_config in config.items():
if plugin_config.get('enabled', True):
self.load_plugin(plugin_name, plugin_config, api, core)
else:
print(f"⏸️ Plugin disabled: {plugin_name}")
def _create_default_config(self, config_path: Path):
"""Create default plugin configuration"""
default_config = {
"intelligence": {
"enabled": True,
"config": {
"engagement_threshold": 40,
"learning_rate": 0.1
}
},
"engagement": {
"enabled": True,
"config": {
"comment_support": True,
"own_post_upvoting": True
}
},
"content": {
"enabled": True,
"config": {
"auto_posting": True,
"post_interval": 2
}
},
"analytics": {
"enabled": True,
"config": {
"dashboard_port": 7001,
"refresh_interval": 120
}
},
"moltx": {
"enabled": True,
"config": {}
},
"moltbook": {
"enabled": True,
"config": {}
},
"moltchan": {
"enabled": True,
"config": {}
},
"moltroad": {
"enabled": True,
"config": {}
},
"clawbr": {
"enabled": True,
"config": {}
},
"onchain": {
"enabled": True,
"config": {}
},
"crypto": {
"enabled": True,
"config": {}
},
"telegram": {
"enabled": True,
"config": {}
},
"a2a": {
"enabled": True,
"config": {}
},
"selfimprove": {
"enabled": True,
"config": {}
},
"skills": {
"enabled": True,
"config": {}
},
"brain": {
"enabled": True,
"config": {}
}
}
config_path.parent.mkdir(exist_ok=True)
with open(config_path, 'w') as f:
json.dump(default_config, f, indent=2)
print(f"📝 Created default plugin config: {config_path}")
def load_plugin(self, plugin_name: str, plugin_config: Dict[str, Any], api, core):
"""Load individual plugin"""
try:
# Import plugin module
if plugin_name == 'mcp':
module_path = f'plugins.{plugin_name}.mcp_plugin'
else:
module_path = f'plugins.{plugin_name}.{plugin_name}'
# Force reload to get latest version
if module_path in sys.modules:
module = importlib.reload(sys.modules[module_path])
else:
module = importlib.import_module(module_path)
# Find plugin class
plugin_class = None
for name, obj in inspect.getmembers(module):
if (inspect.isclass(obj) and
issubclass(obj, AlleyBotPlugin) and
obj != AlleyBotPlugin):
plugin_class = obj
break
if not plugin_class:
print(f"❌ No plugin class found in {plugin_name}")
return
# Initialize plugin
plugin = plugin_class(plugin_config.get('config', {}))
plugin.initialize(api, core)
# Register plugin
self.plugins[plugin_name] = plugin
# Register tasks, commands, and endpoints
self.tasks.update(plugin.get_tasks())
self.commands.update(plugin.get_commands())
self.endpoints.update(plugin.get_endpoints())
print(f"✅ Loaded plugin: {plugin_name}")
except ImportError as e:
print(f"❌ Failed to import plugin {plugin_name}: {e}")
except Exception as e:
print(f"❌ Failed to load plugin {plugin_name}: {e}")
def unload_plugin(self, plugin_name: str):
"""Unload a specific plugin"""
if plugin_name in self.plugins:
plugin = self.plugins[plugin_name]
# Cleanup plugin
if hasattr(plugin, 'cleanup'):
plugin.cleanup()
# Remove from registries
del self.plugins[plugin_name]
# Remove tasks, commands, and endpoints
tasks_to_remove = []
for task_name, task_config in self.tasks.items():
if hasattr(task_config.get('function'), '__self__') and \
task_config['function'].__self__ == plugin:
tasks_to_remove.append(task_name)
for task_name in tasks_to_remove:
del self.tasks[task_name]
commands_to_remove = []
for command_name, command_func in self.commands.items():
if hasattr(command_func, '__self__') and command_func.__self__ == plugin:
commands_to_remove.append(command_name)
for command_name in commands_to_remove:
del self.commands[command_name]
endpoints_to_remove = []
for endpoint_name, endpoint_func in self.endpoints.items():
if hasattr(endpoint_func, '__self__') and endpoint_func.__self__ == plugin:
endpoints_to_remove.append(endpoint_name)
for endpoint_name in endpoints_to_remove:
del self.endpoints[endpoint_name]
print(f"🗑️ Unloaded plugin: {plugin_name}")
else:
print(f"❌ Plugin not found: {plugin_name}")
def reload_plugin(self, plugin_name: str, plugin_config: Dict[str, Any], api, core):
"""Reload a plugin"""
if plugin_name in self.plugins:
self.unload_plugin(plugin_name)
self.load_plugin(plugin_name, plugin_config, api, core)
def get_plugin_info(self, plugin_name: str) -> Dict[str, Any]:
"""Get information about a specific plugin"""
if plugin_name not in self.plugins:
return {"error": "Plugin not found"}
plugin = self.plugins[plugin_name]
return {
"name": plugin.name,
"initialized": plugin.initialized,
"config": plugin.config,
"tasks": len([t for t in self.tasks.keys() if hasattr(self.tasks[t].get('function'), '__self__') and self.tasks[t]['function'].__self__ == plugin]),
"commands": len([c for c in self.commands.keys() if hasattr(self.commands[c], '__self__') and self.commands[c].__self__ == plugin]),
"endpoints": len([e for e in self.endpoints.keys() if hasattr(self.endpoints[e], '__self__') and self.endpoints[e].__self__ == plugin])
}
def get_plugin(self, plugin_name: str):
"""Get a plugin instance by name"""
return self.plugins.get(plugin_name)
def list_loaded(self) -> List[str]:
"""List names of all loaded plugins"""
return list(self.plugins.keys())
def list_plugins(self) -> Dict[str, Dict[str, Any]]:
"""List all loaded plugins with their info"""
return {name: self.get_plugin_info(name) for name in self.plugins.keys()}
def get_stats(self) -> Dict[str, Any]:
"""Get plugin manager statistics"""
return {
"total_plugins": len(self.plugins),
"total_tasks": len(self.tasks),
"total_commands": len(self.commands),
"total_endpoints": len(self.endpoints),
"plugins": list(self.plugins.keys())
}