Skip to content

Commit

Permalink
Fixed plugin system
Browse files Browse the repository at this point in the history
  • Loading branch information
Splamy committed Feb 22, 2018
1 parent 08b753d commit 7549fa3
Show file tree
Hide file tree
Showing 21 changed files with 364 additions and 242 deletions.
29 changes: 11 additions & 18 deletions TS3AudioBot/Bot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace TS3AudioBot
using Helper;
using History;
using Newtonsoft.Json;
using Plugins;
using Sessions;
using System;
using System.IO;
Expand Down Expand Up @@ -43,27 +44,15 @@ public sealed class Bot : IDisposable
public ResourceFactories.ResourceFactoryManager FactoryManager { get; set; }
public CommandManager CommandManager { get; set; }
public BotManager BotManager { get; set; }
public PluginManager PluginManager { get; set; }

// Onw modules

/// <summary>Mangement for playlists.</summary>
public PlaylistManager PlaylistManager { get; set; }
/// <summary>Connection object for the current client.</summary>
public TeamspeakControl QueryConnection { get; set; }
/// <summary>Management for clients talking with the bot.</summary>
public SessionManager SessionManager { get; set; }
private HistoryManager historyManager = null;
/// <summary>Stores all played songs. Can be used to search and restore played songs.</summary>
public HistoryManager HistoryManager
{
get => historyManager ?? throw new CommandException("History has not been enabled", CommandExceptionReason.NotSupported);
set => historyManager = value;
}
/// <summary>Redirects playing, enqueing and song events.</summary>
public PlayManager PlayManager { get; set; }
/// <summary>Used to specify playing mode and active targets to send to.</summary>
public ITargetManager TargetManager { get; private set; }
/// <summary>Slim interface to control the audio player.</summary>
public IPlayerConnection PlayerConnection { get; private set; }

public R InitializeBot()
Expand Down Expand Up @@ -97,8 +86,9 @@ public R InitializeBot()
Injector.RegisterModule(teamspeakClient);
Injector.RegisterModule(teamspeakClient.GetLowLibrary<Ts3FullClient>());
Injector.RegisterModule(new SessionManager());
HistoryManager historyManager = null;
if (hmd.EnableHistory)
Injector.RegisterModule(new HistoryManager(hmd), x => x.Initialize());
Injector.RegisterModule(historyManager = new HistoryManager(hmd), x => x.Initialize());
Injector.RegisterModule(new PlayManager());
Injector.RegisterModule(teamspeakClient.TargetPipe);

Expand All @@ -121,7 +111,7 @@ public R InitializeBot()
PlayManager.AfterResourceStopped += LoggedUpdateBotStatus;
// Log our resource in the history
if (hmd.EnableHistory)
PlayManager.AfterResourceStarted += (s, e) => HistoryManager.LogAudioResource(new HistorySaveData(e.PlayResource.BaseData, e.Owner));
PlayManager.AfterResourceStarted += (s, e) => historyManager.LogAudioResource(new HistorySaveData(e.PlayResource.BaseData, e.Owner));
// Update our thumbnail
PlayManager.AfterResourceStarted += GenerateStatusImage;
PlayManager.AfterResourceStopped += GenerateStatusImage;
Expand Down Expand Up @@ -358,12 +348,15 @@ public void Dispose()
else return;
Log.Info("Bot disconnecting.");

PlayManager?.Stop();
PluginManager.StopPlugins(this);

PlayManager.Stop();
PlayManager = null;

PlayerConnection?.Dispose(); // before: logStream,
PlayerConnection.Dispose(); // before: logStream,
PlayerConnection = null;

QueryConnection?.Dispose(); // before: logStream,
QueryConnection.Dispose(); // before: logStream,
QueryConnection = null;
}
}
Expand Down
3 changes: 3 additions & 0 deletions TS3AudioBot/CommandSystem/BotCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

namespace TS3AudioBot.CommandSystem
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -100,6 +101,8 @@ public CommandBuildInfo(object p, MethodInfo m, CommandAttribute comAtt, Require
{
Parent = p;
Method = m;
if (!m.IsStatic && p == null)
throw new ArgumentException("Got instance method without accociated object");
CommandData = comAtt;
ReqiredParameters = reqAtt;
}
Expand Down
4 changes: 2 additions & 2 deletions TS3AudioBot/CommandSystem/CommandException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ public class CommandException : Exception
protected CommandException() : this(CommandExceptionReason.Unknown) { }
protected CommandException(CommandExceptionReason reason) { Reason = reason; }

public CommandException(string message, CommandExceptionReason reason = CommandExceptionReason.Unknown)
public CommandException(string message, CommandExceptionReason reason)
: base(message) { Reason = reason; }

public CommandException(string message, Exception inner, CommandExceptionReason reason = CommandExceptionReason.Unknown)
public CommandException(string message, Exception inner, CommandExceptionReason reason)
: base(message, inner) { Reason = reason; }

protected CommandException(
Expand Down
20 changes: 15 additions & 5 deletions TS3AudioBot/CommandSystem/CommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,11 @@ public static IEnumerable<CommandBuildInfo> GetCommandMethods(object obj, Type t
{
var comAtt = method.GetCustomAttribute<CommandAttribute>();
if (comAtt == null) continue;
if (obj == null && !method.IsStatic)
{
Log.Warn("Method '{0}' needs an instance, but no instance was provided. It will be ignored.", method.Name);
continue;
}
var reqAtt = method.GetCustomAttribute<RequiredParametersAttribute>();
yield return new CommandBuildInfo(obj, method, comAtt, reqAtt);
}
Expand Down Expand Up @@ -222,14 +227,16 @@ private static R InsertInto(CommandGroup group, ICommand com, string name)

switch (subCommand)
{
// the group we are trying to insert has no element with the current
// name, so just insert it
case null:
// the group we are trying to insert has no element with the current
// name, so just insert it
group.AddCommand(name, com);
return R.OkR;

case CommandGroup insertCommand:
// to add a command to CommandGroup will have to treat it as a subcommand
// with an empty string as a name
var noparamCommand = insertCommand.GetCommand(string.Empty);

if (noparamCommand == null)
{
insertCommand.AddCommand(string.Empty, com);
Expand All @@ -244,20 +251,23 @@ private static R InsertInto(CommandGroup group, ICommand com, string name)
if (!(com is FunctionCommand funcCom))
return $"The command cannot be inserted into a complex node ({name}).";

// if we have is a simple function, we need to create a overlaoder
// and then add both functions to it
switch (subCommand)
{
case FunctionCommand subFuncCommand:
// if we have is a simple function, we need to create a overlaoder
// and then add both functions to it
group.RemoveCommand(name);
var overloader = new OverloadedFunctionCommand();
overloader.AddCommand(subFuncCommand);
overloader.AddCommand(funcCom);
group.AddCommand(name, overloader);
break;

case OverloadedFunctionCommand insertCommand:
// if we have a overloaded function, we can simply add it
insertCommand.AddCommand(funcCom);
break;

default:
return "Unknown node to insert to.";
}
Expand Down
61 changes: 35 additions & 26 deletions TS3AudioBot/Commands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public static JsonObject CommandBotId(BotManager bots)
public static void CommandBotMove(TeamspeakControl queryConnection, CallerInfo caller, ulong? channel, string password = null)
{
if (!channel.HasValue)
channel = (CommandGetChannel(caller) as JsonSingleValue<ulong>)?.Value;
channel = caller.InvokerData.ChannelId;
if (!channel.HasValue)
throw new CommandException("No target channel found", CommandExceptionReason.CommandError);
queryConnection.MoveTo(channel.Value, password).UnwrapThrow();
Expand Down Expand Up @@ -159,7 +159,7 @@ public static JsonObject CommandConnect(BotManager bots)
{
var botInfo = bots.CreateBot();
if (botInfo == null)
throw new CommandException("Could not create new instance");
throw new CommandException("Could not create new instance", CommandExceptionReason.CommandError);
return new JsonSingleValue<BotInfo>(botInfo);
}

Expand Down Expand Up @@ -260,7 +260,7 @@ public static JsonObject CommandHelp(CommandManager commandManager, params strin
strb.Append("\n========= Welcome to the TS3AudioBot ========="
+ "\nIf you need any help with a special command use !help <commandName>."
+ "\nHere are all possible commands:\n");
var botComList = commandManager.AllCommands.Select(c => c.InvokeName).GroupBy(n => n.Split(' ')[0]).Select(x => x.Key).ToArray();
var botComList = commandManager.AllCommands.Select(c => c.InvokeName).OrderBy(x => x).GroupBy(n => n.Split(' ')[0]).Select(x => x.Key).ToArray();
foreach (var botCom in botComList)
strb.Append(botCom).Append(", ");
strb.Length -= 2;
Expand Down Expand Up @@ -418,27 +418,24 @@ public static JsonObject CommandHistoryId(HistoryManager historyManager, string
throw new CommandException("Unrecognized name descriptor", CommandExceptionReason.CommandError);
}

[Command("history last", "<count> Gets the last <count> played songs.")]
public static JsonObject CommandHistoryLast(HistoryManager historyManager, int amount)
{
var query = new SeachQuery { MaxResults = amount };
var results = historyManager.Search(query).ToArray();
return new JsonArray<AudioLogEntry>(historyManager.Format(results), results);
}

[Command("history last", "Plays the last song again")]
[Usage("<count>", "Gets the last <count> played songs.")]
[RequiredParameters(0)]
public static JsonObject CommandHistoryLast(PlayManager playManager, HistoryManager historyManager, CallerInfo caller, int? amount)
public static JsonObject CommandHistoryLast(PlayManager playManager, HistoryManager historyManager, CallerInfo caller)
{
if (amount.HasValue)
var ale = historyManager.Search(new SeachQuery { MaxResults = 1 }).FirstOrDefault();
if (ale != null)
{
var query = new SeachQuery { MaxResults = amount.Value };
var results = historyManager.Search(query).ToArray();
return new JsonArray<AudioLogEntry>(historyManager.Format(results), results);
}
else
{
var ale = historyManager.Search(new SeachQuery { MaxResults = 1 }).FirstOrDefault();
if (ale != null)
{
playManager.Play(caller.InvokerData, ale.AudioResource).UnwrapThrow();
return null;
}
else return new JsonEmpty("There is no song in the history");
playManager.Play(caller.InvokerData, ale.AudioResource).UnwrapThrow();
return null;
}
else return new JsonEmpty("There is no song in the history");
}

[Command("history play", "<id> Playes the song with <id>")]
Expand Down Expand Up @@ -880,24 +877,36 @@ public static void CommandPlay(IPlayerConnection playerConnection, PlayManager p
}

[Command("plugin list", "Lists all found plugins.")]
public static JsonArray<PluginStatusInfo> CommandPluginList(PluginManager pluginManager)
public static JsonArray<PluginStatusInfo> CommandPluginList(ExecutionInformation info, PluginManager pluginManager)
{
var overview = pluginManager.GetPluginOverview();
// Get bot instance dynamically since it might be optional
// Requesting it via parameter injection would prevent some plugin types to be loaded from a higher context like 'Core'
if (!info.TryGet<Bot>(out var bot))
bot = null;
var overview = pluginManager.GetPluginOverview(bot);
return new JsonArray<PluginStatusInfo>(PluginManager.FormatOverview(overview), overview);
}

[Command("plugin unload", "Unloads a plugin.")]
public static void CommandPluginUnload(PluginManager pluginManager, string identifier)
public static void CommandPluginUnload(ExecutionInformation info, PluginManager pluginManager, string identifier)
{
var result = pluginManager.StopPlugin(identifier);
// Get bot instance dynamically since it might be optional
// Requesting it via parameter injection would prevent some plugin types to be loaded from a higher context like 'Core'
if (!info.TryGet<Bot>(out var bot))
bot = null;
var result = pluginManager.StopPlugin(identifier, bot);
if (result != PluginResponse.Ok)
throw new CommandException("Plugin error: " + result, CommandExceptionReason.CommandError);
}

[Command("plugin load", "Unloads a plugin.")]
public static void CommandPluginLoad(PluginManager pluginManager, string identifier)
public static void CommandPluginLoad(ExecutionInformation info, PluginManager pluginManager, string identifier)
{
var result = pluginManager.StartPlugin(identifier);
// Get bot instance dynamically since it might be optional
// Requesting it via parameter injection would prevent some plugin types to be loaded from a higher context like 'Core'
if (!info.TryGet<Bot>(out var bot))
bot = null;
var result = pluginManager.StartPlugin(identifier, bot);
if (result != PluginResponse.Ok)
throw new CommandException("Plugin error: " + result, CommandExceptionReason.CommandError);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,10 @@ public static int ReadId3Int(this BinaryReader br)
}
}

internal class BitConverterBigEndian
internal static class BitConverterBigEndian
{
private const int BitsInByte = 8;

private BitConverterBigEndian() { }

public static short ToInt16(byte[] bytes)
{
return (short)(
Expand Down
6 changes: 3 additions & 3 deletions TS3AudioBot/Helper/WebWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ internal static class WebWrapper
private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(3);

public static bool DownloadString(out string site, Uri link, params Tuple<string, string>[] optionalHeaders)
public static bool DownloadString(out string site, Uri link, params (string name, string value)[] optionalHeaders)
{
var request = WebRequest.Create(link);
foreach (var header in optionalHeaders)
request.Headers.Add(header.Item1, header.Item2);
foreach (var (name, value) in optionalHeaders)
request.Headers.Add(name, value);
try
{
using (var response = request.GetResponse())
Expand Down
2 changes: 1 addition & 1 deletion TS3AudioBot/History/HistoryManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ namespace TS3AudioBot.History
using System.Collections.Generic;
using System.Linq;

/// <summary>Stores all played songs. Can be used to search and restore played songs.</summary>
public sealed class HistoryManager
{
private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
Expand Down Expand Up @@ -271,7 +272,6 @@ public void RemoveBrokenLinks()
/// Goes through a list of <see cref="AudioLogEntry"/> and checks if the contained <see cref="AudioResource"/>
/// is playable/resolveable.
/// </summary>
/// <param name="info">Session object to inform the user about the current cleaning status.</param>
/// <param name="list">The list to iterate.</param>
/// <returns>A new list with all working items.</returns>
private List<AudioLogEntry> FilterList(IReadOnlyCollection<AudioLogEntry> list)
Expand Down
1 change: 1 addition & 0 deletions TS3AudioBot/IPlayerConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace TS3AudioBot
{
using System;

/// <summary>Slim interface to control the audio player.</summary>
public interface IPlayerConnection : IDisposable
{
event EventHandler OnSongEnd;
Expand Down
1 change: 1 addition & 0 deletions TS3AudioBot/ITargetManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace TS3AudioBot
using TS3Client;
using TS3Client.Full.Audio;

/// <summary>Used to specify playing mode and active targets to send to.</summary>
public interface ITargetManager
{
TargetSendMode SendMode { get; set; }
Expand Down
1 change: 1 addition & 0 deletions TS3AudioBot/PlayManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace TS3AudioBot
using System;
using System.Collections.Generic;

/// <summary>Provides a convenient inferface for enqueing, playing and registering song events.</summary>
public class PlayManager
{
private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
Expand Down
11 changes: 5 additions & 6 deletions TS3AudioBot/Plugins/ITabPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@ namespace TS3AudioBot.Plugins
{
using System;

public interface ITabPlugin : IDisposable
public interface ICorePlugin : IDisposable
{
void Initialize(Core core);
void Initialize();
}

// TODO
public interface IBotPlugin : ICorePlugin { }

public interface ICoreModule { }

public interface IBotModule { }
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public sealed class StaticPluginAttribute : Attribute { }
}
Loading

0 comments on commit 7549fa3

Please sign in to comment.