mirror of
https://github.com/chylex/TweetDuck.git
synced 2024-11-14 17:42:47 +01:00
183 lines
6.3 KiB
C#
183 lines
6.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using CefSharp;
|
|
using TweetDck.Plugins.Enums;
|
|
using TweetDck.Plugins.Events;
|
|
using TweetDck.Resources;
|
|
|
|
namespace TweetDck.Plugins{
|
|
sealed class PluginManager{
|
|
public const string PluginBrowserScriptFile = "plugins.browser.js";
|
|
public const string PluginNotificationScriptFile = "plugins.notification.js";
|
|
public const string PluginGlobalScriptFile = "plugins.js";
|
|
|
|
private const int InvalidToken = 0;
|
|
|
|
public string PathOfficialPlugins { get { return Path.Combine(rootPath, "official"); } }
|
|
public string PathCustomPlugins { get { return Path.Combine(rootPath, "user"); } }
|
|
|
|
public IEnumerable<Plugin> Plugins { get { return plugins; } }
|
|
public PluginConfig Config { get; private set; }
|
|
public PluginBridge Bridge { get; private set; }
|
|
|
|
public event EventHandler<PluginErrorEventArgs> Reloaded;
|
|
public event EventHandler<PluginErrorEventArgs> Executed;
|
|
public event EventHandler<PluginChangedStateEventArgs> PluginChangedState;
|
|
|
|
private readonly string rootPath;
|
|
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
|
private readonly Dictionary<int, Plugin> tokens = new Dictionary<int, Plugin>();
|
|
private readonly Random rand = new Random();
|
|
|
|
private List<string> loadErrors;
|
|
|
|
public PluginManager(string path, PluginConfig config){
|
|
this.rootPath = path;
|
|
this.SetConfig(config);
|
|
this.Bridge = new PluginBridge(this);
|
|
|
|
Program.UserConfigReplaced += Program_UserConfigReplaced;
|
|
}
|
|
|
|
private void Program_UserConfigReplaced(object sender, EventArgs e){
|
|
SetConfig(Program.UserConfig.Plugins);
|
|
Reload();
|
|
}
|
|
|
|
private void Config_InternalPluginChangedState(object sender, PluginChangedStateEventArgs e){
|
|
if (PluginChangedState != null){
|
|
PluginChangedState(this, e);
|
|
}
|
|
}
|
|
|
|
private void SetConfig(PluginConfig config){
|
|
if (this.Config != null){
|
|
this.Config.InternalPluginChangedState -= Config_InternalPluginChangedState;
|
|
}
|
|
|
|
this.Config = config;
|
|
this.Config.InternalPluginChangedState += Config_InternalPluginChangedState;
|
|
}
|
|
|
|
public bool IsPluginInstalled(string identifier){
|
|
return plugins.Any(plugin => plugin.Identifier.Equals(identifier));
|
|
}
|
|
|
|
public IEnumerable<Plugin> GetPluginsByGroup(PluginGroup group){
|
|
return plugins.Where(plugin => plugin.Group == group);
|
|
}
|
|
|
|
public int CountPluginByGroup(PluginGroup group){
|
|
return plugins.Count(plugin => plugin.Group == group);
|
|
}
|
|
|
|
public bool HasAnyPlugin(PluginEnvironment environment){
|
|
return plugins.Any(plugin => plugin.Environments.HasFlag(environment));
|
|
}
|
|
|
|
public int GetTokenFromPlugin(Plugin plugin){
|
|
foreach(KeyValuePair<int, Plugin> kvp in tokens){
|
|
if (kvp.Value.Equals(plugin)){
|
|
return kvp.Key;
|
|
}
|
|
}
|
|
|
|
return InvalidToken;
|
|
}
|
|
|
|
public Plugin GetPluginFromToken(int token){
|
|
Plugin plugin;
|
|
return tokens.TryGetValue(token, out plugin) ? plugin : null;
|
|
}
|
|
|
|
public void Reload(){
|
|
plugins.Clear();
|
|
tokens.Clear();
|
|
|
|
loadErrors = new List<string>(2);
|
|
|
|
foreach(Plugin plugin in LoadPluginsFrom(PathOfficialPlugins, PluginGroup.Official)){
|
|
plugins.Add(plugin);
|
|
}
|
|
|
|
foreach(Plugin plugin in LoadPluginsFrom(PathCustomPlugins, PluginGroup.Custom)){
|
|
plugins.Add(plugin);
|
|
}
|
|
|
|
if (Reloaded != null){
|
|
Reloaded(this, new PluginErrorEventArgs(loadErrors));
|
|
}
|
|
}
|
|
|
|
public void ExecutePlugins(IFrame frame, PluginEnvironment environment, bool includeDisabled){
|
|
if (includeDisabled){
|
|
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GenerateConfig(Config), "gen:pluginconfig");
|
|
}
|
|
|
|
List<string> failedPlugins = new List<string>(1);
|
|
|
|
foreach(Plugin plugin in Plugins){
|
|
string path = plugin.GetScriptPath(environment);
|
|
if (string.IsNullOrEmpty(path) || !plugin.CanRun || (!includeDisabled && !Config.IsEnabled(plugin)))continue;
|
|
|
|
string script;
|
|
|
|
try{
|
|
script = File.ReadAllText(path);
|
|
}catch(Exception e){
|
|
failedPlugins.Add(plugin.Identifier+" ("+Path.GetFileName(path)+"): "+e.Message);
|
|
continue;
|
|
}
|
|
|
|
int token;
|
|
|
|
if (tokens.ContainsValue(plugin)){
|
|
token = GetTokenFromPlugin(plugin);
|
|
}
|
|
else{
|
|
token = GenerateToken();
|
|
tokens[token] = plugin;
|
|
}
|
|
|
|
ScriptLoader.ExecuteScript(frame, PluginScriptGenerator.GeneratePlugin(plugin.Identifier, script, token, environment), "plugin:"+plugin);
|
|
}
|
|
|
|
if (Executed != null){
|
|
Executed(this, new PluginErrorEventArgs(failedPlugins));
|
|
}
|
|
}
|
|
|
|
private IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
|
|
if (!Directory.Exists(path)){
|
|
yield break;
|
|
}
|
|
|
|
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
|
|
string error;
|
|
Plugin plugin = Plugin.CreateFromFolder(fullDir, group, out error);
|
|
|
|
if (plugin == null){
|
|
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+error);
|
|
}
|
|
else{
|
|
yield return plugin;
|
|
}
|
|
}
|
|
}
|
|
|
|
private int GenerateToken(){
|
|
for(int attempt = 0; attempt < 1000; attempt++){
|
|
int token = rand.Next();
|
|
|
|
if (!tokens.ContainsKey(token) && token != InvalidToken){
|
|
return token;
|
|
}
|
|
}
|
|
|
|
return -tokens.Count;
|
|
}
|
|
}
|
|
}
|