1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-07 17:34:06 +02:00

Partially move TweetDuck plugin & update system to TweetLib.Core

This commit is contained in:
chylex 2019-05-10 22:01:01 +02:00
parent dd6e712755
commit 458ee203f3
30 changed files with 99 additions and 88 deletions

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Events;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Events;
namespace TweetDuck.Configuration{
sealed class PluginConfig : ConfigManager.BaseConfig, IPluginConfig{

View File

@ -2,6 +2,7 @@
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Updates;
using TweetLib.Core.Features.Updates;
namespace TweetDuck.Core.Bridge{
class UpdateBridge{

View File

@ -14,9 +14,10 @@
using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Events;
using TweetDuck.Resources;
using TweetDuck.Updates;
using TweetLib.Core.Features.Plugins.Events;
using TweetLib.Core.Features.Updates;
namespace TweetDuck.Core{
sealed partial class FormBrowser : Form, AnalyticsFile.IProvider{

View File

@ -4,8 +4,9 @@
using System.Linq;
using TweetDuck.Core.Other;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetLib.Core.Data;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Core.Management{
sealed class ProfileManager{

View File

@ -7,9 +7,9 @@
using TweetDuck.Core.Handling;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetDuck.Resources;
using TweetLib.Core.Data;
using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Core.Notification{
abstract partial class FormNotificationMain : FormNotificationBase{

View File

@ -11,7 +11,8 @@
using TweetDuck.Core.Notification;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Utils;
namespace TweetDuck.Core.Other.Analytics{

View File

@ -6,6 +6,7 @@
using TweetDuck.Configuration;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Controls;
using TweetLib.Core.Features.Plugins;
namespace TweetDuck.Core.Other{
sealed partial class FormPlugins : Form, FormManager.IAppDialog{

View File

@ -7,6 +7,7 @@
using TweetDuck.Core.Other.Settings.Dialogs;
using TweetDuck.Core.Utils;
using TweetDuck.Updates;
using TweetLib.Core.Features.Updates;
using TweetLib.Core.Utils;
namespace TweetDuck.Core.Other.Settings{

View File

@ -12,8 +12,8 @@
using TweetDuck.Core.Notification;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetDuck.Resources;
using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Core{
sealed class TweetDeckBrowser : IDisposable{

View File

@ -3,7 +3,8 @@
using System.Windows.Forms;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins.Enums;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Plugins.Controls{
sealed partial class PluginControl : UserControl{

View File

@ -1,5 +0,0 @@
namespace TweetDuck.Plugins.Enums{
enum PluginFolder{
Root, Data
}
}

View File

@ -2,10 +2,11 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
using TweetLib.Core.Collections;
using TweetLib.Core.Data;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Features.Plugins.Events;
using TweetLib.Core.Utils;
namespace TweetDuck.Plugins{

View File

@ -6,10 +6,11 @@
using System.Linq;
using System.Windows.Forms;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
using TweetDuck.Resources;
using TweetLib.Core.Data;
using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Features.Plugins.Enums;
using TweetLib.Core.Features.Plugins.Events;
namespace TweetDuck.Plugins{
sealed class PluginManager{
@ -126,12 +127,19 @@ IEnumerable<Plugin> LoadPluginsFrom(string path, PluginGroup group){
}
foreach(string fullDir in Directory.EnumerateDirectories(path, "*", SearchOption.TopDirectoryOnly)){
string name = Path.GetFileName(fullDir);
if (string.IsNullOrEmpty(name)){
loadErrors.Add($"{group.GetIdentifierPrefix()}(?): Could not extract directory name from path: {fullDir}");
continue;
}
Plugin plugin;
try{
plugin = PluginLoader.FromFolder(fullDir, group);
plugin = PluginLoader.FromFolder(name, fullDir, Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name), group);
}catch(Exception e){
loadErrors.Add(group.GetIdentifierPrefix()+Path.GetFileName(fullDir)+": "+e.Message);
loadErrors.Add($"{group.GetIdentifierPrefix()}{name}: {e.Message}");
continue;
}

View File

@ -250,25 +250,15 @@
<Compile Include="Plugins\Controls\PluginListFlowLayout.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="Plugins\Enums\PluginFolder.cs" />
<Compile Include="Plugins\IPluginConfig.cs" />
<Compile Include="Plugins\Plugin.cs" />
<Compile Include="Plugins\Events\PluginChangedStateEventArgs.cs" />
<Compile Include="Plugins\PluginBridge.cs" />
<Compile Include="Configuration\PluginConfig.cs" />
<Compile Include="Plugins\Enums\PluginEnvironment.cs" />
<Compile Include="Plugins\Enums\PluginGroup.cs" />
<Compile Include="Plugins\Events\PluginErrorEventArgs.cs" />
<Compile Include="Plugins\PluginLoader.cs" />
<Compile Include="Plugins\PluginManager.cs" />
<Compile Include="Plugins\PluginScriptGenerator.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Reporter.cs" />
<Compile Include="Updates\UpdateCheckEventArgs.cs" />
<Compile Include="Updates\FormUpdateDownload.cs">
<SubType>Form</SubType>
</Compile>
@ -285,9 +275,7 @@
<Compile Include="Core\Utils\BrowserUtils.cs" />
<Compile Include="Core\Utils\NativeMethods.cs" />
<Compile Include="Updates\UpdateCheckClient.cs" />
<Compile Include="Updates\UpdateDownloadStatus.cs" />
<Compile Include="Updates\UpdateHandler.cs" />
<Compile Include="Updates\UpdateInfo.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Resources\ScriptLoader.cs" />

View File

@ -1,5 +1,6 @@
using System;
using System.Windows.Forms;
using TweetLib.Core.Features.Updates;
namespace TweetDuck.Updates{
sealed partial class FormUpdateDownload : Form{

View File

@ -6,6 +6,7 @@
using System.Threading.Tasks;
using System.Web.Script.Serialization;
using TweetDuck.Core.Utils;
using TweetLib.Core.Features.Updates;
using TweetLib.Core.Utils;
using JsonObject = System.Collections.Generic.IDictionary<string, object>;

View File

@ -2,6 +2,7 @@
using System.Threading;
using System.Threading.Tasks;
using TweetLib.Core.Data;
using TweetLib.Core.Features.Updates;
using Timer = System.Windows.Forms.Timer;
namespace TweetDuck.Updates{

View File

@ -4,15 +4,15 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace TweetDuck.Plugins.Enums{
namespace TweetLib.Core.Features.Plugins.Enums{
[Flags]
enum PluginEnvironment{
public enum PluginEnvironment{
None = 0,
Browser = 1,
Notification = 2
}
static class PluginEnvironmentExtensions{
public static class PluginEnvironmentExtensions{
public static IEnumerable<PluginEnvironment> Values{
get{
yield return PluginEnvironment.Browser;
@ -24,7 +24,7 @@ public static bool IncludesDisabledPlugins(this PluginEnvironment environment){
return environment == PluginEnvironment.Browser;
}
public static string GetPluginScriptFile(this PluginEnvironment environment){
public static string? GetPluginScriptFile(this PluginEnvironment environment){
switch(environment){
case PluginEnvironment.Browser: return "browser.js";
case PluginEnvironment.Notification: return "notification.js";
@ -73,7 +73,7 @@ public bool TryGetValue(PluginEnvironment key, out T value){
return true;
}
else{
value = default(T);
value = default;
return false;
}
}

View File

@ -0,0 +1,5 @@
namespace TweetLib.Core.Features.Plugins.Enums{
public enum PluginFolder{
Root, Data
}
}

View File

@ -1,9 +1,9 @@
namespace TweetDuck.Plugins.Enums{
enum PluginGroup{
namespace TweetLib.Core.Features.Plugins.Enums{
public enum PluginGroup{
Official, Custom
}
static class PluginGroupExtensions{
public static class PluginGroupExtensions{
public static string GetIdentifierPrefix(this PluginGroup group){
switch(group){
case PluginGroup.Official: return "official/";

View File

@ -1,7 +1,7 @@
using System;
namespace TweetDuck.Plugins.Events{
sealed class PluginChangedStateEventArgs : EventArgs{
namespace TweetLib.Core.Features.Plugins.Events{
public sealed class PluginChangedStateEventArgs : EventArgs{
public Plugin Plugin { get; }
public bool IsEnabled { get; }

View File

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
namespace TweetDuck.Plugins.Events{
sealed class PluginErrorEventArgs : EventArgs{
namespace TweetLib.Core.Features.Plugins.Events{
public sealed class PluginErrorEventArgs : EventArgs{
public bool HasErrors => Errors.Count > 0;
public IList<string> Errors { get; }

View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using TweetDuck.Plugins.Events;
using TweetLib.Core.Features.Plugins.Events;
namespace TweetDuck.Plugins{
interface IPluginConfig{
namespace TweetLib.Core.Features.Plugins{
public interface IPluginConfig{
IEnumerable<string> DisabledPlugins { get; }
event EventHandler<PluginChangedStateEventArgs> PluginChangedState;

View File

@ -1,10 +1,10 @@
using System;
using System.IO;
using TweetDuck.Plugins.Enums;
using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Plugins{
sealed class Plugin{
private static readonly Version AppVersion = new Version(Program.VersionTag);
namespace TweetLib.Core.Features.Plugins{
public sealed class Plugin{
private static readonly Version AppVersion = new Version(Lib.VersionTag);
public string Identifier { get; }
public PluginGroup Group { get; }
@ -62,7 +62,7 @@ private Plugin(PluginGroup group, string identifier, string pathRoot, string pat
public string GetScriptPath(PluginEnvironment environment){
if (Environments.HasFlag(environment)){
string file = environment.GetPluginScriptFile();
string? file = environment.GetPluginScriptFile();
return file != null ? Path.Combine(pathRoot, file) : string.Empty;
}
else{
@ -124,7 +124,7 @@ public override bool Equals(object obj){
public sealed class Builder{
private static readonly Version DefaultRequiredVersion = new Version(0, 0, 0, 0);
public string Name { get; set; }
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
public string Author { get; set; } = "(anonymous)";
public string Version { get; set; } = string.Empty;
@ -144,7 +144,7 @@ public Builder(PluginGroup group, string name, string pathRoot, string pathData)
this.group = group;
this.pathRoot = pathRoot;
this.pathData = pathData;
this.identifier = group.GetIdentifierPrefix()+name;
this.identifier = group.GetIdentifierPrefix() + name;
}
public void AddEnvironment(PluginEnvironment environment){

View File

@ -2,40 +2,35 @@
using System.IO;
using System.Linq;
using System.Text;
using TweetDuck.Plugins.Enums;
using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Plugins{
static class PluginLoader{
namespace TweetLib.Core.Features.Plugins{
public static class PluginLoader{
private static readonly string[] EndTag = { "[END]" };
public static Plugin FromFolder(string path, PluginGroup group){
string name = Path.GetFileName(path);
public static Plugin FromFolder(string name, string pathRoot, string pathData, PluginGroup group){
Plugin.Builder builder = new Plugin.Builder(group, name, pathRoot, pathData);
if (string.IsNullOrEmpty(name)){
throw new ArgumentException("Could not extract directory name from path: "+path);
}
Plugin.Builder builder = new Plugin.Builder(group, name, path, Path.Combine(Program.PluginDataPath, group.GetIdentifierPrefix(), name));
foreach(string file in Directory.EnumerateFiles(path, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName)){
builder.AddEnvironment(PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal)));
foreach(var environment in Directory.EnumerateFiles(pathRoot, "*.js", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).Select(EnvironmentFromFileName)){
builder.AddEnvironment(environment);
}
string metaFile = Path.Combine(path, ".meta");
string metaFile = Path.Combine(pathRoot, ".meta");
if (!File.Exists(metaFile)){
throw new ArgumentException("Plugin is missing a .meta file");
}
string currentTag = null, currentContents = string.Empty;
string? currentTag = null;
string currentContents = string.Empty;
foreach(string line in File.ReadAllLines(metaFile, Encoding.UTF8).Concat(EndTag).Select(line => line.TrimEnd()).Where(line => line.Length > 0)){
if (line[0] == '[' && line[line.Length-1] == ']'){
if (line[0] == '[' && line[line.Length - 1] == ']'){
if (currentTag != null){
SetProperty(builder, currentTag, currentContents);
}
currentTag = line.Substring(1, line.Length-2).ToUpper();
currentTag = line.Substring(1, line.Length - 2).ToUpper();
currentContents = string.Empty;
if (line.Equals(EndTag[0])){
@ -43,16 +38,20 @@ public static Plugin FromFolder(string path, PluginGroup group){
}
}
else if (currentTag != null){
currentContents = currentContents.Length == 0 ? line : currentContents+Environment.NewLine+line;
currentContents = currentContents.Length == 0 ? line : currentContents + Environment.NewLine + line;
}
else{
throw new FormatException("Missing metadata tag before value: "+line);
throw new FormatException($"Missing metadata tag before value: {line}");
}
}
return builder.BuildAndSetup();
}
private static PluginEnvironment EnvironmentFromFileName(string file){
return PluginEnvironmentExtensions.Values.FirstOrDefault(env => file.Equals(env.GetPluginScriptFile(), StringComparison.Ordinal));
}
private static void SetProperty(Plugin.Builder builder, string tag, string value){
switch(tag){
case "NAME": builder.Name = value; break;
@ -62,8 +61,8 @@ private static void SetProperty(Plugin.Builder builder, string tag, string value
case "WEBSITE": builder.Website = value; break;
case "CONFIGFILE": builder.ConfigFile = value; break;
case "CONFIGDEFAULT": builder.ConfigDefault = value; break;
case "REQUIRES": builder.RequiredVersion = Version.TryParse(value, out Version version) ? version : throw new FormatException("Invalid required minimum version: "+value); break;
default: throw new FormatException("Invalid metadata tag: "+tag);
case "REQUIRES": builder.RequiredVersion = Version.TryParse(value, out Version version) ? version : throw new FormatException($"Invalid required minimum version: {value}"); break;
default: throw new FormatException($"Invalid metadata tag: {tag}");
}
}
}

View File

@ -1,10 +1,10 @@
using System.Linq;
using TweetDuck.Plugins.Enums;
using TweetLib.Core.Features.Plugins.Enums;
namespace TweetDuck.Plugins{
static class PluginScriptGenerator{
namespace TweetLib.Core.Features.Plugins{
public static class PluginScriptGenerator{
public static string GenerateConfig(IPluginConfig config){
return "window.TD_PLUGINS.disabled = ["+string.Join(",", config.DisabledPlugins.Select(id => $"\"{id}\""))+"]";
return "window.TD_PLUGINS.disabled = [" + string.Join(",", config.DisabledPlugins.Select(id => '"' + id + '"')) + "]";
}
public static string GeneratePlugin(string pluginIdentifier, string pluginContents, int pluginToken, PluginEnvironment environment){

View File

@ -1,8 +1,8 @@
using System;
using TweetLib.Core.Data;
namespace TweetDuck.Updates{
sealed class UpdateCheckEventArgs : EventArgs{
namespace TweetLib.Core.Features.Updates{
public sealed class UpdateCheckEventArgs : EventArgs{
public int EventId { get; }
public Result<UpdateInfo> Result { get; }

View File

@ -1,4 +1,4 @@
namespace TweetDuck.Updates{
namespace TweetLib.Core.Features.Updates{
public enum UpdateDownloadStatus{
None = 0,
InProgress,

View File

@ -1,21 +1,20 @@
using System;
using System.IO;
using System.Net;
using TweetDuck.Core.Utils;
using TweetLib.Core.Utils;
namespace TweetDuck.Updates{
sealed class UpdateInfo{
namespace TweetLib.Core.Features.Updates{
public sealed class UpdateInfo{
public string VersionTag { get; }
public string ReleaseNotes { get; }
public string InstallerPath { get; }
public UpdateDownloadStatus DownloadStatus { get; private set; }
public Exception DownloadError { get; private set; }
public Exception? DownloadError { get; private set; }
private readonly string downloadUrl;
private readonly string installerFolder;
private WebClient currentDownload;
private WebClient? currentDownload;
public UpdateInfo(string versionTag, string releaseNotes, string downloadUrl, string installerFolder){
this.downloadUrl = downloadUrl;
@ -23,7 +22,7 @@ public UpdateInfo(string versionTag, string releaseNotes, string downloadUrl, st
this.VersionTag = versionTag;
this.ReleaseNotes = releaseNotes;
this.InstallerPath = Path.Combine(installerFolder, $"TweetDuck.{versionTag}.exe");
this.InstallerPath = Path.Combine(installerFolder, $"{Lib.BrandName}.{versionTag}.exe");
}
public void BeginSilentDownload(){
@ -49,7 +48,7 @@ public void BeginSilentDownload(){
return;
}
WebClient client = WebUtils.NewClient(BrowserUtils.UserAgentVanilla);
WebClient client = WebUtils.NewClient($"{Lib.BrandName} {Lib.VersionTag}");
client.DownloadFileCompleted += WebUtils.FileDownloadCallback(InstallerPath, () => {
DownloadStatus = UpdateDownloadStatus.Done;

6
lib/TweetLib.Core/Lib.cs Normal file
View File

@ -0,0 +1,6 @@
namespace TweetLib.Core{
public static class Lib{
public const string BrandName = "TweetDuck";
public const string VersionTag = "1.17.4";
}
}