-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathApplicationHost.cs
187 lines (149 loc) · 7.61 KB
/
ApplicationHost.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
using CCTavern.Commands;
using CCTavern.Logger;
using DSharpPlus;
using DSharpPlus.CommandsNext;
using DSharpPlus.Entities;
using DSharpPlus.Interactivity;
using DSharpPlus.Interactivity.Enums;
using DSharpPlus.Interactivity.Extensions;
using Lavalink4NET;
using Lavalink4NET.Players;
using Lavalink4NET.Players.Queued;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace CCTavern {
sealed class ApplicationHost : BackgroundService {
private readonly DiscordClient discordClient;
private readonly IAudioService audioService;
private readonly ITavernSettings settings;
private readonly IServiceProvider serviceProvider;
private readonly ILogger<ApplicationHost> logger;
public ApplicationHost(IServiceProvider serviceProvider
, DiscordClient discordClient
, IAudioService audioService
, ITavernSettings settings
, ILogger<ApplicationHost> logger) {
ArgumentNullException.ThrowIfNull(discordClient);
ArgumentNullException.ThrowIfNull(audioService);
this.discordClient = discordClient;
this.audioService = audioService;
this.settings = settings;
this.serviceProvider = serviceProvider;
this.logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
discordClient.UseInteractivity(new InteractivityConfiguration() {
PollBehaviour = PollBehaviour.KeepEmojis,
Timeout = TimeSpan.FromSeconds(30)
});
// We can specify a status for our bot. Let's set it to "online" and set the activity to "with fire".
var status = new DiscordActivity("Loading :)", ActivityType.Playing);
await discordClient.ConnectAsync(status, UserStatus.DoNotDisturb)
.ConfigureAwait(false);
var commands = discordClient.UseCommandsNext(new CommandsNextConfiguration() {
CaseSensitive = settings.PrefixesCaseSensitive,
PrefixResolver = DiscordPrefixResolver,
EnableMentionPrefix = true,
DmHelp = false,
Services = serviceProvider
});
#if (ARCHIVAL_MODE)
// Archival import mode
commands.RegisterCommands<ArchiveImportModule>();
commands.RegisterCommands<BotCommandsModule>();
commands.RegisterCommands<MusicQueueModule>();
#else
commands.RegisterCommands<MusicCommandModule>();
commands.RegisterCommands<MusicPlayModule>();
commands.RegisterCommands<GuildSettingsModule>();
commands.RegisterCommands<BotCommandsModule>();
commands.RegisterCommands<MusicQueueModule>();
commands.RegisterCommands<StatusCommandModule>();
#endif
try {
// Initialize node connection
await audioService
.WaitForReadyAsync(stoppingToken)
.ConfigureAwait(false);
} catch (Exception ex) {
status = new($"Error LLC Failure ({DateTime.Now:dd/MM/yyyy HH:mm:ss})", ActivityType.Competing);
await discordClient.UpdateStatusAsync(status, UserStatus.Idle);
logger.LogError(TLE.Startup, ex, $"Failed to connect to Lavalink audio service backend: {ex.Message}");
throw;
}
status = new($"Ready ({DateTime.Now:dd/MM/yyyy HH:mm:ss})", ActivityType.Playing);
await discordClient.UpdateStatusAsync(status, UserStatus.Online);
discordClient.Zombied += DiscordClient_Zombied;
discordClient.SocketClosed += DiscordClient_SocketClosed;
logger.LogInformation(TLE.Startup, "Ready :)");
}
public override async Task StopAsync(CancellationToken cancellationToken) {
logger.LogCritical(TLE.Disconnected, "Disconnecting ApplicationHost, StopAsync.");
try { await discordClient.DisconnectAsync(); } catch { }
try { await audioService.StopAsync(); } catch { }
await base.StopAsync(cancellationToken);
}
private async Task DiscordClient_SocketClosed(DiscordClient sender, DSharpPlus.EventArgs.SocketCloseEventArgs e) {
logger.LogCritical(TLE.Disconnected, "Discord API disconnected (SocketClosed), Close code: {code}, message: {message}.", e.CloseCode, e.CloseMessage);
if (e.CloseCode == 4002) {
logger.LogCritical(TLE.Disconnected, "Restarting the bot in 20 seconds.");
try { await sender.DisconnectAsync().ConfigureAwait(false); } catch { }
await Task.Delay(20000); // Wait 20 seconds.
logger.LogCritical(TLE.Disconnected, "Shutting down...");
Program.Shutdown();
}
//else if (e.CloseCode <= 4003 || (e.CloseCode >= 4005 && e.CloseCode <= 4009) || e.CloseCode >= 5000) {
// logger.LogCritical(TLE.Disconnected, "Attempting to reconnect the bot in 10 seconds.");
// await Task.Delay(20000); // Wait 20 seconds.
//
// logger.LogCritical(TLE.Disconnected, "Reconnecting the bot.");
// await sender.ReconnectAsync().ConfigureAwait(false);
//}
}
private Task DiscordClient_Zombied(DiscordClient sender, DSharpPlus.EventArgs.ZombiedEventArgs args) {
logger.LogCritical(TLE.Disconnected, "Discord API disconnected (Zombied), retry attempts: {attempts}.", args.Failures);
return Task.FromResult(true);
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
public static async Task<int> DiscordPrefixResolver(DiscordMessage msg) {
//var c = msg.Content; var trimmed = c.Length > 4 ? c.Substring(0, 4) : c;
//logger.LogInformation(TLE.CmdDbg, $"Discord Prefix Resolver, {msg.Author.Username} : {trimmed}");
#if (ARCHIVAL_MODE)
const string archivalPrefix = "ccArchive?";
int mpos = msg.GetStringPrefixLength(archivalPrefix, StringComparison.OrdinalIgnoreCase);
return mpos;
#else
const string debugPrefix = "cc?";
int mpos = msg.GetStringPrefixLength(debugPrefix, StringComparison.OrdinalIgnoreCase);
#endif
#if (DEBUG && !ARCHIVAL_MODE)
// Get the prefix here, dont forget to have a default one.
return mpos;// Task.FromResult(mpos);
#elif (ARCHIVAL_MODE == false)
// If we are using the debugging prefix then we want to ignore this message in prod.
if (mpos != -1)
return -1;
// If direct message
if (msg.Channel.IsPrivate)
return 0;
var guildId = msg.Channel.Guild.Id;
IEnumerable<string> prefixes = Program.ServerPrefixes.ContainsKey(guildId)
? Program.ServerPrefixes[guildId] : Program.g_DefaultPrefixes;
foreach (var pfix in prefixes) {
if (mpos == -1 && !string.IsNullOrWhiteSpace(pfix)) {
mpos = msg.GetStringPrefixLength(pfix, Program.Settings.PrefixesCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase);
if (mpos != -1)
break;
}
}
return mpos;
#endif
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
}
}