Skip to content

Commit 7549fa3

Browse files
committed
Fixed plugin system
1 parent 08b753d commit 7549fa3

21 files changed

+364
-242
lines changed

TS3AudioBot/Bot.cs

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace TS3AudioBot
1414
using Helper;
1515
using History;
1616
using Newtonsoft.Json;
17+
using Plugins;
1718
using Sessions;
1819
using System;
1920
using System.IO;
@@ -43,27 +44,15 @@ public sealed class Bot : IDisposable
4344
public ResourceFactories.ResourceFactoryManager FactoryManager { get; set; }
4445
public CommandManager CommandManager { get; set; }
4546
public BotManager BotManager { get; set; }
47+
public PluginManager PluginManager { get; set; }
4648

4749
// Onw modules
4850

49-
/// <summary>Mangement for playlists.</summary>
50-
public PlaylistManager PlaylistManager { get; set; }
5151
/// <summary>Connection object for the current client.</summary>
5252
public TeamspeakControl QueryConnection { get; set; }
53-
/// <summary>Management for clients talking with the bot.</summary>
5453
public SessionManager SessionManager { get; set; }
55-
private HistoryManager historyManager = null;
56-
/// <summary>Stores all played songs. Can be used to search and restore played songs.</summary>
57-
public HistoryManager HistoryManager
58-
{
59-
get => historyManager ?? throw new CommandException("History has not been enabled", CommandExceptionReason.NotSupported);
60-
set => historyManager = value;
61-
}
62-
/// <summary>Redirects playing, enqueing and song events.</summary>
6354
public PlayManager PlayManager { get; set; }
64-
/// <summary>Used to specify playing mode and active targets to send to.</summary>
6555
public ITargetManager TargetManager { get; private set; }
66-
/// <summary>Slim interface to control the audio player.</summary>
6756
public IPlayerConnection PlayerConnection { get; private set; }
6857

6958
public R InitializeBot()
@@ -97,8 +86,9 @@ public R InitializeBot()
9786
Injector.RegisterModule(teamspeakClient);
9887
Injector.RegisterModule(teamspeakClient.GetLowLibrary<Ts3FullClient>());
9988
Injector.RegisterModule(new SessionManager());
89+
HistoryManager historyManager = null;
10090
if (hmd.EnableHistory)
101-
Injector.RegisterModule(new HistoryManager(hmd), x => x.Initialize());
91+
Injector.RegisterModule(historyManager = new HistoryManager(hmd), x => x.Initialize());
10292
Injector.RegisterModule(new PlayManager());
10393
Injector.RegisterModule(teamspeakClient.TargetPipe);
10494

@@ -121,7 +111,7 @@ public R InitializeBot()
121111
PlayManager.AfterResourceStopped += LoggedUpdateBotStatus;
122112
// Log our resource in the history
123113
if (hmd.EnableHistory)
124-
PlayManager.AfterResourceStarted += (s, e) => HistoryManager.LogAudioResource(new HistorySaveData(e.PlayResource.BaseData, e.Owner));
114+
PlayManager.AfterResourceStarted += (s, e) => historyManager.LogAudioResource(new HistorySaveData(e.PlayResource.BaseData, e.Owner));
125115
// Update our thumbnail
126116
PlayManager.AfterResourceStarted += GenerateStatusImage;
127117
PlayManager.AfterResourceStopped += GenerateStatusImage;
@@ -358,12 +348,15 @@ public void Dispose()
358348
else return;
359349
Log.Info("Bot disconnecting.");
360350

361-
PlayManager?.Stop();
351+
PluginManager.StopPlugins(this);
352+
353+
PlayManager.Stop();
354+
PlayManager = null;
362355

363-
PlayerConnection?.Dispose(); // before: logStream,
356+
PlayerConnection.Dispose(); // before: logStream,
364357
PlayerConnection = null;
365358

366-
QueryConnection?.Dispose(); // before: logStream,
359+
QueryConnection.Dispose(); // before: logStream,
367360
QueryConnection = null;
368361
}
369362
}

TS3AudioBot/CommandSystem/BotCommand.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
namespace TS3AudioBot.CommandSystem
1111
{
12+
using System;
1213
using System.Collections.Generic;
1314
using System.Linq;
1415
using System.Reflection;
@@ -100,6 +101,8 @@ public CommandBuildInfo(object p, MethodInfo m, CommandAttribute comAtt, Require
100101
{
101102
Parent = p;
102103
Method = m;
104+
if (!m.IsStatic && p == null)
105+
throw new ArgumentException("Got instance method without accociated object");
103106
CommandData = comAtt;
104107
ReqiredParameters = reqAtt;
105108
}

TS3AudioBot/CommandSystem/CommandException.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ public class CommandException : Exception
2020
protected CommandException() : this(CommandExceptionReason.Unknown) { }
2121
protected CommandException(CommandExceptionReason reason) { Reason = reason; }
2222

23-
public CommandException(string message, CommandExceptionReason reason = CommandExceptionReason.Unknown)
23+
public CommandException(string message, CommandExceptionReason reason)
2424
: base(message) { Reason = reason; }
2525

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

2929
protected CommandException(

TS3AudioBot/CommandSystem/CommandManager.cs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ public static IEnumerable<CommandBuildInfo> GetCommandMethods(object obj, Type t
134134
{
135135
var comAtt = method.GetCustomAttribute<CommandAttribute>();
136136
if (comAtt == null) continue;
137+
if (obj == null && !method.IsStatic)
138+
{
139+
Log.Warn("Method '{0}' needs an instance, but no instance was provided. It will be ignored.", method.Name);
140+
continue;
141+
}
137142
var reqAtt = method.GetCustomAttribute<RequiredParametersAttribute>();
138143
yield return new CommandBuildInfo(obj, method, comAtt, reqAtt);
139144
}
@@ -222,14 +227,16 @@ private static R InsertInto(CommandGroup group, ICommand com, string name)
222227

223228
switch (subCommand)
224229
{
225-
// the group we are trying to insert has no element with the current
226-
// name, so just insert it
227230
case null:
231+
// the group we are trying to insert has no element with the current
232+
// name, so just insert it
228233
group.AddCommand(name, com);
229234
return R.OkR;
235+
230236
case CommandGroup insertCommand:
237+
// to add a command to CommandGroup will have to treat it as a subcommand
238+
// with an empty string as a name
231239
var noparamCommand = insertCommand.GetCommand(string.Empty);
232-
233240
if (noparamCommand == null)
234241
{
235242
insertCommand.AddCommand(string.Empty, com);
@@ -244,20 +251,23 @@ private static R InsertInto(CommandGroup group, ICommand com, string name)
244251
if (!(com is FunctionCommand funcCom))
245252
return $"The command cannot be inserted into a complex node ({name}).";
246253

247-
// if we have is a simple function, we need to create a overlaoder
248-
// and then add both functions to it
249254
switch (subCommand)
250255
{
251256
case FunctionCommand subFuncCommand:
257+
// if we have is a simple function, we need to create a overlaoder
258+
// and then add both functions to it
252259
group.RemoveCommand(name);
253260
var overloader = new OverloadedFunctionCommand();
254261
overloader.AddCommand(subFuncCommand);
255262
overloader.AddCommand(funcCom);
256263
group.AddCommand(name, overloader);
257264
break;
265+
258266
case OverloadedFunctionCommand insertCommand:
267+
// if we have a overloaded function, we can simply add it
259268
insertCommand.AddCommand(funcCom);
260269
break;
270+
261271
default:
262272
return "Unknown node to insert to.";
263273
}

TS3AudioBot/Commands.cs

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public static JsonObject CommandBotId(BotManager bots)
115115
public static void CommandBotMove(TeamspeakControl queryConnection, CallerInfo caller, ulong? channel, string password = null)
116116
{
117117
if (!channel.HasValue)
118-
channel = (CommandGetChannel(caller) as JsonSingleValue<ulong>)?.Value;
118+
channel = caller.InvokerData.ChannelId;
119119
if (!channel.HasValue)
120120
throw new CommandException("No target channel found", CommandExceptionReason.CommandError);
121121
queryConnection.MoveTo(channel.Value, password).UnwrapThrow();
@@ -159,7 +159,7 @@ public static JsonObject CommandConnect(BotManager bots)
159159
{
160160
var botInfo = bots.CreateBot();
161161
if (botInfo == null)
162-
throw new CommandException("Could not create new instance");
162+
throw new CommandException("Could not create new instance", CommandExceptionReason.CommandError);
163163
return new JsonSingleValue<BotInfo>(botInfo);
164164
}
165165

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

421+
[Command("history last", "<count> Gets the last <count> played songs.")]
422+
public static JsonObject CommandHistoryLast(HistoryManager historyManager, int amount)
423+
{
424+
var query = new SeachQuery { MaxResults = amount };
425+
var results = historyManager.Search(query).ToArray();
426+
return new JsonArray<AudioLogEntry>(historyManager.Format(results), results);
427+
}
428+
421429
[Command("history last", "Plays the last song again")]
422-
[Usage("<count>", "Gets the last <count> played songs.")]
423-
[RequiredParameters(0)]
424-
public static JsonObject CommandHistoryLast(PlayManager playManager, HistoryManager historyManager, CallerInfo caller, int? amount)
430+
public static JsonObject CommandHistoryLast(PlayManager playManager, HistoryManager historyManager, CallerInfo caller)
425431
{
426-
if (amount.HasValue)
432+
var ale = historyManager.Search(new SeachQuery { MaxResults = 1 }).FirstOrDefault();
433+
if (ale != null)
427434
{
428-
var query = new SeachQuery { MaxResults = amount.Value };
429-
var results = historyManager.Search(query).ToArray();
430-
return new JsonArray<AudioLogEntry>(historyManager.Format(results), results);
431-
}
432-
else
433-
{
434-
var ale = historyManager.Search(new SeachQuery { MaxResults = 1 }).FirstOrDefault();
435-
if (ale != null)
436-
{
437-
playManager.Play(caller.InvokerData, ale.AudioResource).UnwrapThrow();
438-
return null;
439-
}
440-
else return new JsonEmpty("There is no song in the history");
435+
playManager.Play(caller.InvokerData, ale.AudioResource).UnwrapThrow();
436+
return null;
441437
}
438+
else return new JsonEmpty("There is no song in the history");
442439
}
443440

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

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

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

897902
[Command("plugin load", "Unloads a plugin.")]
898-
public static void CommandPluginLoad(PluginManager pluginManager, string identifier)
903+
public static void CommandPluginLoad(ExecutionInformation info, PluginManager pluginManager, string identifier)
899904
{
900-
var result = pluginManager.StartPlugin(identifier);
905+
// Get bot instance dynamically since it might be optional
906+
// Requesting it via parameter injection would prevent some plugin types to be loaded from a higher context like 'Core'
907+
if (!info.TryGet<Bot>(out var bot))
908+
bot = null;
909+
var result = pluginManager.StartPlugin(identifier, bot);
901910
if (result != PluginResponse.Ok)
902911
throw new CommandException("Plugin error: " + result, CommandExceptionReason.CommandError);
903912
}

TS3AudioBot/Helper/AudioTags/BinaryReaderBigEndianExtensions.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,10 @@ public static int ReadId3Int(this BinaryReader br)
5555
}
5656
}
5757

58-
internal class BitConverterBigEndian
58+
internal static class BitConverterBigEndian
5959
{
6060
private const int BitsInByte = 8;
6161

62-
private BitConverterBigEndian() { }
63-
6462
public static short ToInt16(byte[] bytes)
6563
{
6664
return (short)(

TS3AudioBot/Helper/WebWrapper.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ internal static class WebWrapper
1818
private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
1919
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(3);
2020

21-
public static bool DownloadString(out string site, Uri link, params Tuple<string, string>[] optionalHeaders)
21+
public static bool DownloadString(out string site, Uri link, params (string name, string value)[] optionalHeaders)
2222
{
2323
var request = WebRequest.Create(link);
24-
foreach (var header in optionalHeaders)
25-
request.Headers.Add(header.Item1, header.Item2);
24+
foreach (var (name, value) in optionalHeaders)
25+
request.Headers.Add(name, value);
2626
try
2727
{
2828
using (var response = request.GetResponse())

TS3AudioBot/History/HistoryManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ namespace TS3AudioBot.History
1616
using System.Collections.Generic;
1717
using System.Linq;
1818

19+
/// <summary>Stores all played songs. Can be used to search and restore played songs.</summary>
1920
public sealed class HistoryManager
2021
{
2122
private static readonly NLog.Logger Log = NLog.LogManager.GetCurrentClassLogger();
@@ -271,7 +272,6 @@ public void RemoveBrokenLinks()
271272
/// Goes through a list of <see cref="AudioLogEntry"/> and checks if the contained <see cref="AudioResource"/>
272273
/// is playable/resolveable.
273274
/// </summary>
274-
/// <param name="info">Session object to inform the user about the current cleaning status.</param>
275275
/// <param name="list">The list to iterate.</param>
276276
/// <returns>A new list with all working items.</returns>
277277
private List<AudioLogEntry> FilterList(IReadOnlyCollection<AudioLogEntry> list)

TS3AudioBot/IPlayerConnection.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ namespace TS3AudioBot
1111
{
1212
using System;
1313

14+
/// <summary>Slim interface to control the audio player.</summary>
1415
public interface IPlayerConnection : IDisposable
1516
{
1617
event EventHandler OnSongEnd;

TS3AudioBot/ITargetManager.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace TS3AudioBot
1212
using TS3Client;
1313
using TS3Client.Full.Audio;
1414

15+
/// <summary>Used to specify playing mode and active targets to send to.</summary>
1516
public interface ITargetManager
1617
{
1718
TargetSendMode SendMode { get; set; }

0 commit comments

Comments
 (0)