Skip to content

Commit 8e779f0

Browse files
committed
Added after song start event
1 parent 9d05782 commit 8e779f0

25 files changed

+220
-122
lines changed

TS3ABotUnitTests/TS3ABotUnitTests.csproj

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212

1313
<ItemGroup>
1414
<PackageReference Include="NUnit" Version="3.12.0" />
15-
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1" />
16-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
15+
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
16+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.0" />
1717
</ItemGroup>
1818

1919
<ItemGroup>

TS3AudioBot/Audio/FfmpegProducer.cs

+3-10
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ public void AudioStop()
6666
StopFfmpegProcess();
6767
}
6868

69-
public TimeSpan Length => GetCurrentSongLength();
69+
public TimeSpan? Length => GetCurrentSongLength();
7070

71-
public TimeSpan Position => ffmpegInstance?.AudioTimer.SongPosition ?? TimeSpan.Zero;
71+
public TimeSpan? Position => ffmpegInstance?.AudioTimer.SongPosition;
7272

7373
public Task Seek(TimeSpan position) { SetPosition(position); return Task.CompletedTask; }
7474

@@ -310,14 +310,7 @@ private void StopFfmpegProcess()
310310
}
311311
}
312312

313-
private TimeSpan GetCurrentSongLength()
314-
{
315-
var instance = ffmpegInstance;
316-
if (instance is null)
317-
return TimeSpan.Zero;
318-
319-
return instance.ParsedSongLength ?? TimeSpan.Zero;
320-
}
313+
private TimeSpan? GetCurrentSongLength() => ffmpegInstance?.ParsedSongLength;
321314

322315
private void AssertNotMainScheduler()
323316
{

TS3AudioBot/Audio/IPlayerSource.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ public interface IPlayerSource : IAudioPassiveProducer
1818
event EventHandler OnSongEnd;
1919
event EventHandler<SongInfoChanged> OnSongUpdated;
2020

21-
TimeSpan Length { get; }
22-
TimeSpan Position { get; }
21+
TimeSpan? Length { get; }
22+
TimeSpan? Position { get; }
2323

2424
Task Seek(TimeSpan position);
2525
}

TS3AudioBot/Audio/MetaData.cs TS3AudioBot/Audio/PlayInfo.cs

+9-9
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,23 @@
1313

1414
namespace TS3AudioBot.Audio
1515
{
16-
public sealed class MetaData
16+
public sealed class PlayInfo
1717
{
1818
/// <summary>Defaults to: invoker.Uid - Can be set if the owner of a song differs from the invoker.</summary>
1919
public Uid? ResourceOwnerUid { get; set; }
2020
/// <summary>Starts the song at the specified time if set.</summary>
2121
public TimeSpan? StartOffset { get; set; }
2222

23-
public MetaData(TimeSpan? startOffset = null)
23+
public PlayInfo(TimeSpan? startOffset = null)
2424
{
2525
StartOffset = startOffset;
2626
}
2727

28-
public MetaData Merge(MetaData other) => Merge(this, other);
28+
public PlayInfo Merge(PlayInfo other) => Merge(this, other);
2929

3030
[return: NotNullIfNotNull("self")]
3131
[return: NotNullIfNotNull("other")]
32-
public static MetaData? Merge(MetaData? self, MetaData? other)
32+
public static PlayInfo? Merge(PlayInfo? self, PlayInfo? other)
3333
{
3434
if (other is null)
3535
return self;
@@ -40,20 +40,20 @@ public MetaData(TimeSpan? startOffset = null)
4040
return self;
4141
}
4242

43-
public static MetaData MergeDefault(MetaData? self, MetaData? other)
44-
=> Merge(self, other) ?? new MetaData();
43+
public static PlayInfo MergeDefault(PlayInfo? self, PlayInfo? other)
44+
=> Merge(self, other) ?? new PlayInfo();
4545
}
4646

4747
public interface IMetaContainer
4848
{
49-
public MetaData? Meta { get; set; }
49+
public PlayInfo? PlayInfo { get; set; }
5050
}
5151

5252
public static class MetaContainerExtensions
5353
{
54-
public static T MergeMeta<T>(this T container, MetaData? other) where T : IMetaContainer
54+
public static T MergeMeta<T>(this T container, PlayInfo? other) where T : IMetaContainer
5555
{
56-
container.Meta = MetaData.Merge(container.Meta, other);
56+
container.PlayInfo = PlayInfo.Merge(container.PlayInfo, other);
5757
return container;
5858
}
5959
}

TS3AudioBot/Audio/PlayInfoEventArgs.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public sealed class PlayInfoEventArgs : EventArgs
1717
public InvokerData Invoker { get; }
1818
public PlayResource PlayResource { get; }
1919
public AudioResource ResourceData => PlayResource.AudioResource;
20-
public MetaData? MetaData => PlayResource.Meta;
20+
public PlayInfo? PlayInfo => PlayResource.PlayInfo;
2121
public string? SourceLink { get; }
2222

2323
public PlayInfoEventArgs(InvokerData invoker, PlayResource playResource, string? sourceLink)

TS3AudioBot/Audio/PlayManager.cs

+9-9
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ public PlayManager(ConfBot config, Player playerConnection, PlaylistManager play
5050
this.stats = stats;
5151
}
5252

53-
public Task Enqueue(InvokerData invoker, AudioResource ar, MetaData? meta = null) => Enqueue(invoker, new PlaylistItem(ar, meta));
54-
public async Task Enqueue(InvokerData invoker, string message, string? audioType = null, MetaData? meta = null)
53+
public Task Enqueue(InvokerData invoker, AudioResource ar, PlayInfo? meta = null) => Enqueue(invoker, new PlaylistItem(ar, meta));
54+
public async Task Enqueue(InvokerData invoker, string message, string? audioType = null, PlayInfo? meta = null)
5555
{
5656
PlayResource playResource;
5757
try { playResource = await resourceResolver.Load(message, audioType); }
@@ -77,8 +77,8 @@ public Task Enqueue(InvokerData invoker, PlaylistItem item)
7777

7878
private static PlaylistItem UpdateItem(InvokerData invoker, PlaylistItem item)
7979
{
80-
item.Meta ??= new MetaData();
81-
item.Meta.ResourceOwnerUid = invoker.ClientUid;
80+
item.PlayInfo ??= new PlayInfo();
81+
item.PlayInfo.ResourceOwnerUid = invoker.ClientUid;
8282
return item;
8383
}
8484

@@ -95,7 +95,7 @@ private async Task PostEnqueue(InvokerData invoker, int startIndex)
9595
/// <param name="ar">The resource to load and play.</param>
9696
/// <param name="meta">Allows overriding certain settings for the resource. Can be null.</param>
9797
/// <returns>Ok if successful, or an error message otherwise.</returns>
98-
public async Task Play(InvokerData invoker, AudioResource ar, MetaData? meta = null)
98+
public async Task Play(InvokerData invoker, AudioResource ar, PlayInfo? meta = null)
9999
{
100100
if (ar is null)
101101
throw new ArgumentNullException(nameof(ar));
@@ -116,7 +116,7 @@ public async Task Play(InvokerData invoker, AudioResource ar, MetaData? meta = n
116116
/// <param name="audioType">The associated resource type string to a factory.</param>
117117
/// <param name="meta">Allows overriding certain settings for the resource. Can be null.</param>
118118
/// <returns>Ok if successful, or an error message otherwise.</returns>
119-
public async Task Play(InvokerData invoker, string link, string? audioType = null, MetaData? meta = null)
119+
public async Task Play(InvokerData invoker, string link, string? audioType = null, PlayInfo? meta = null)
120120
{
121121
PlayResource playResource;
122122
try { playResource = await resourceResolver.Load(link, audioType); }
@@ -175,7 +175,7 @@ private async Task StartResource(InvokerData invoker, PlaylistItem item)
175175
throw;
176176
}
177177
stats.TrackSongLoad(item.AudioResource.AudioType, true, false);
178-
await StartResource(invoker, playResource.MergeMeta(item.Meta));
178+
await StartResource(invoker, playResource.MergeMeta(item.PlayInfo));
179179
}
180180

181181
private async Task StartResource(InvokerData invoker, PlayResource play)
@@ -299,12 +299,12 @@ public async Task Update(SongInfoChanged newInfo)
299299
}
300300
}
301301

302-
public static MetaData? ParseAttributes(string[] attrs)
302+
public static PlayInfo? ParseAttributes(string[] attrs)
303303
{
304304
if (attrs is null || attrs.Length == 0)
305305
return null;
306306

307-
var meta = new MetaData();
307+
var meta = new PlayInfo();
308308
foreach (var attr in attrs)
309309
{
310310
if (attr.StartsWith("@"))

TS3AudioBot/Audio/Player.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public async Task Play(PlayResource res)
6969
if (res is MediaPlayResource mres && mres.IsIcyStream)
7070
await FfmpegProducer.AudioStartIcy(res.PlayUri);
7171
else
72-
await FfmpegProducer.AudioStart(res.PlayUri, res.Meta?.StartOffset);
72+
await FfmpegProducer.AudioStart(res.PlayUri, res.PlayInfo?.StartOffset);
7373
Play(FfmpegProducer);
7474
}
7575

@@ -116,9 +116,9 @@ public void StopAll()
116116
MergePipe.Dispose();
117117
}
118118

119-
public TimeSpan Length => CurrentPlayerSource?.Length ?? TimeSpan.Zero;
119+
public TimeSpan? Length => CurrentPlayerSource?.Length;
120120

121-
public TimeSpan Position => CurrentPlayerSource?.Position ?? TimeSpan.Zero;
121+
public TimeSpan? Position => CurrentPlayerSource?.Position;
122122

123123
public Task Seek(TimeSpan position) => CurrentPlayerSource?.Seek(position) ?? Task.CompletedTask;
124124

TS3AudioBot/Audio/StreamAudioPlayerSource.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ public class StreamAudioPlayerSource : IPlayerSource, IAudioActiveConsumer
1818
private bool hasFired = false;
1919

2020
public IAudioPassiveProducer? InStream { get; set; }
21-
public TimeSpan Length => TimeSpan.Zero;
22-
public TimeSpan Position => TimeSpan.Zero;
21+
public TimeSpan? Length => null;
22+
public TimeSpan? Position => null;
2323

2424
public event EventHandler? OnSongEnd;
2525
event EventHandler<SongInfoChanged> IPlayerSource.OnSongUpdated { add { } remove { } }

TS3AudioBot/Bot.cs

+67-52
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,9 @@ public Bot(Id id, ConfBot config, BotInjector injector)
133133
// Update idle status events
134134
playManager.BeforeResourceStarted += (s, e) => { DisableIdleTickWorker(); return Task.CompletedTask; };
135135
playManager.PlaybackStopped += (s, e) => { EnableIdleTickWorker(); return Task.CompletedTask; };
136-
// Used for the voice_mode script
136+
// Used for custom scripts, like voice_mode, onsongstart
137137
playManager.BeforeResourceStarted += BeforeResourceStarted;
138+
playManager.AfterResourceStarted += AfterResourceStarted;
138139
// Update the own status text to the current song title
139140
playManager.AfterResourceStarted += (s, e) => UpdateBotStatus();
140141
playManager.PlaybackStopped += (s, e) => UpdateBotStatus();
@@ -143,8 +144,8 @@ public Bot(Id id, ConfBot config, BotInjector injector)
143144
if (Injector.TryGet<HistoryManager>(out var historyManager))
144145
playManager.AfterResourceStarted += (s, e) =>
145146
{
146-
if (e.MetaData != null)
147-
historyManager.LogAudioResource(new HistorySaveData(e.PlayResource.AudioResource, e.MetaData.ResourceOwnerUid));
147+
if (e.PlayInfo != null)
148+
historyManager.LogAudioResource(new HistorySaveData(e.PlayResource.AudioResource, e.PlayInfo.ResourceOwnerUid));
148149
return Task.CompletedTask;
149150
};
150151
// Update our thumbnail
@@ -338,6 +339,39 @@ private void OnClientLeftView(object? sender, ClientLeftView eventArgs)
338339
sessionManager.RemoveSession(eventArgs.ClientId);
339340
}
340341

342+
private async Task BeforeResourceStarted(object? sender, PlayInfoEventArgs e)
343+
{
344+
const string DefaultVoiceScript = "!whisper off";
345+
const string DefaultWhisperScript = "!xecute (!whisper subscription) (!unsubscribe temporary) (!subscribe channeltemp (!getmy channel))";
346+
347+
var mode = config.Audio.SendMode.Value;
348+
string script;
349+
if (mode.StartsWith("!", StringComparison.Ordinal))
350+
script = mode;
351+
else if (mode.Equals("voice", StringComparison.OrdinalIgnoreCase))
352+
script = DefaultVoiceScript;
353+
else if (mode.Equals("whisper", StringComparison.OrdinalIgnoreCase))
354+
script = DefaultWhisperScript;
355+
else
356+
{
357+
Log.Error("Invalid voice mode");
358+
return;
359+
}
360+
361+
var info = CreateExecInfo(e.Invoker);
362+
await CallScript(info, script, false, true);
363+
}
364+
365+
private async Task AfterResourceStarted(object? sender, PlayInfoEventArgs e)
366+
{
367+
var onSongStart = config.Events.OnSongStart.Value;
368+
if (!string.IsNullOrEmpty(onSongStart))
369+
{
370+
var info = CreateExecInfo();
371+
await CallScript(info, onSongStart, false, true);
372+
}
373+
}
374+
341375
#region Status: Description, Avatar
342376

343377
public Task UpdateBotStatus()
@@ -449,28 +483,7 @@ await resourceResolver.GetThumbnail(startEvent.PlayResource,
449483

450484
#endregion
451485

452-
private async Task BeforeResourceStarted(object? sender, PlayInfoEventArgs e)
453-
{
454-
const string DefaultVoiceScript = "!whisper off";
455-
const string DefaultWhisperScript = "!xecute (!whisper subscription) (!unsubscribe temporary) (!subscribe channeltemp (!getmy channel))";
456-
457-
var mode = config.Audio.SendMode.Value;
458-
string script;
459-
if (mode.StartsWith("!", StringComparison.Ordinal))
460-
script = mode;
461-
else if (mode.Equals("voice", StringComparison.OrdinalIgnoreCase))
462-
script = DefaultVoiceScript;
463-
else if (mode.Equals("whisper", StringComparison.OrdinalIgnoreCase))
464-
script = DefaultWhisperScript;
465-
else
466-
{
467-
Log.Error("Invalid voice mode");
468-
return;
469-
}
470-
471-
var info = CreateExecInfo(e.Invoker);
472-
await CallScript(info, script, false, true);
473-
}
486+
#region Script Execution
474487

475488
private async Task CallScript(ExecutionInformation info, string command, bool answer, bool skipRights)
476489
{
@@ -510,6 +523,35 @@ private ExecutionInformation CreateExecInfo(InvokerData? invoker = null, UserSes
510523
return info;
511524
}
512525

526+
private async Task TryCatchCommand(ExecutionInformation info, bool answer, Func<Task> action)
527+
{
528+
try
529+
{
530+
await action.Invoke();
531+
}
532+
catch (AudioBotException ex)
533+
{
534+
NLog.LogLevel commandErrorLevel = answer ? NLog.LogLevel.Debug : NLog.LogLevel.Warn;
535+
Log.Log(commandErrorLevel, ex, "Command Error ({0})", ex.Message);
536+
if (answer)
537+
{
538+
await info.Write(TextMod.Format(config.Commands.Color, strings.error_call_error.Mod().Color(Color.Red).Bold(), ex.Message))
539+
.CatchToLog(Log);
540+
}
541+
}
542+
catch (Exception ex)
543+
{
544+
Log.Error(ex, "Unexpected command error: {0}", ex.Message);
545+
if (answer)
546+
{
547+
await info.Write(TextMod.Format(config.Commands.Color, strings.error_call_unexpected_error.Mod().Color(Color.Red).Bold(), ex.Message))
548+
.CatchToLog(Log);
549+
}
550+
}
551+
}
552+
553+
#endregion
554+
513555
#region Event: Idle
514556

515557
private async void OnIdle()
@@ -570,33 +612,6 @@ private async Task OnAloneChanged(object? sender, AloneChanged e)
570612

571613
#endregion
572614

573-
private async Task TryCatchCommand(ExecutionInformation info, bool answer, Func<Task> action)
574-
{
575-
try
576-
{
577-
await action.Invoke();
578-
}
579-
catch (AudioBotException ex)
580-
{
581-
NLog.LogLevel commandErrorLevel = answer ? NLog.LogLevel.Debug : NLog.LogLevel.Warn;
582-
Log.Log(commandErrorLevel, ex, "Command Error ({0})", ex.Message);
583-
if (answer)
584-
{
585-
await info.Write(TextMod.Format(config.Commands.Color, strings.error_call_error.Mod().Color(Color.Red).Bold(), ex.Message))
586-
.CatchToLog(Log);
587-
}
588-
}
589-
catch (Exception ex)
590-
{
591-
Log.Error(ex, "Unexpected command error: {0}", ex.Message);
592-
if (answer)
593-
{
594-
await info.Write(TextMod.Format(config.Commands.Color, strings.error_call_unexpected_error.Mod().Color(Color.Red).Bold(), ex.Message))
595-
.CatchToLog(Log);
596-
}
597-
}
598-
}
599-
600615
public BotInfo GetInfo() => new BotInfo
601616
{
602617
Id = Id,

TS3AudioBot/Config/ConfigStructs.cs

+2
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,8 @@ public class ConfEvents : ConfigTable
291291
public ConfigValue<TimeSpan> PartyDelay { get; } = new ConfigValue<TimeSpan>("party_delay", TimeSpan.Zero,
292292
"Specifies how long the bot has to be alone until the 'onalone' event gets fired.\n" +
293293
"You can specify the time in the ISO-8601 format \"PT30S\" or like: 15s, 1h, 3m30s");
294+
public ConfigValue<string> OnSongStart { get; } = new ConfigValue<string>("onsongstart", "",
295+
"Called when a new song starts.");
294296
}
295297

296298
// Utility config structs

TS3AudioBot/Core.cs

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public async Task Run(ParameterData setup)
8282
if (!builder.Build())
8383
throw new Exception("Could not load all core modules");
8484

85+
Upgrader.PerformUpgrades(injector);
8586
YoutubeDlHelper.DataObj = config.Tools.YoutubeDl;
8687

8788
injector.GetModuleOrThrow<CommandManager>().RegisterCollection(MainCommands.Bag);

0 commit comments

Comments
 (0)