feat: upgrade to discord.net v3 and remove unused music commands

This commit is contained in:
IllusionMan1212 2022-05-03 03:43:00 +03:00 committed by Hesham Abourgheba
parent a9bcd3800c
commit aaf7ebfc47
12 changed files with 138 additions and 760 deletions

2
.gitignore vendored
View File

@ -4,6 +4,8 @@
Vita3KBot/obj
Vita3KBot/bin
Vita3KBot/bot.db
Vita3KBot/bot.db-shm
Vita3KBot/bot.db-wal
Vita3KBot/token.txt
Vita3KBot.sln.DotSettings.user
Vita3KBot.sln.DotSettings

View File

@ -2,6 +2,7 @@
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
@ -19,7 +20,7 @@ namespace APIClients {
0x06, 0x82, 0x1C, 0x52, 0xF2, 0xAB, 0x5D, 0x2B, 0x4A, 0xBD, 0x99, 0x54, 0x50, 0x35, 0x51, 0x14
};
private static HMACSHA256 HMAC = new HMACSHA256(HMACKey);
private static readonly string BaseURL = "https://gs-sec.ww.np.dl.playstation.net/pl/np/";
private static readonly string BaseURL = "http://gs-sec.ww.np.dl.playstation.net/pl/np/";
//all firmware regions are the same therefore use US as default
private static readonly string FirmwareXML = "http://fus01.psp2.update.playstation.net/update/psp2/list/us/psp2-updatelist.xml";
private static readonly XmlSerializer FWSerializer = new XmlSerializer(typeof(UpdateDataList));
@ -28,9 +29,6 @@ namespace APIClients {
public static Embed GetTitlePatch(string titleId) {
string url = ConvertTitleIDToHash(titleId);
// Needed to bypass certificate errors
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
var noUpdatesEmbed = new EmbedBuilder
{
Title = titleId,
@ -42,6 +40,11 @@ namespace APIClients {
// Almost all games with no updates don't return an empty XML so i'm forced to do this hack
// We also can't differentiate between valid IDs and games with no updates
try { xmlDoc.Load(url); }
catch (HttpRequestException e) {
if (e.StatusCode == HttpStatusCode.NotFound) {
return noUpdatesEmbed.Build();
}
}
catch (WebException) { return noUpdatesEmbed.Build(); }
catch (XmlException) { return noUpdatesEmbed.Build(); }

View File

@ -1,45 +1,34 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Victoria;
using Vita3KBot.Commands;
using Microsoft.Extensions.DependencyInjection;
using Vita3KBot.Services;
namespace Vita3KBot {
public class Bot {
private readonly string _token;
private DiscordSocketClient _client;
private MessageHandler _handler;
public static LavaNode lavaNode;
// Initializes Discord.Net
private async Task Start() {
_client = new DiscordSocketClient();
_handler = new MessageHandler(_client);
lavaNode = new LavaNode(_client, new LavaConfig {
Authorization = "youshallnotpass",
Hostname = "localhost",
Port = 2333,
ReconnectAttempts = 3,
ReconnectDelay = TimeSpan.FromSeconds(5)
});
using (var services = ConfigureServices()) {
await _handler.Init();
var client = services.GetRequiredService<DiscordSocketClient>();
client.Log += LogAsync;
services.GetRequiredService<CommandService>().Log += LogAsync;
await _client.LoginAsync(TokenType.Bot, _token);
await _client.StartAsync();
await client.LoginAsync(TokenType.Bot, _token);
await client.StartAsync();
_client.Ready += async () => {
if (!lavaNode.IsConnected)
await lavaNode.ConnectAsync();
};
lavaNode.OnTrackEnded += MusicModule.OnTrackEnded;
lavaNode.OnTrackStarted += MusicModule.OnTrackStarted;
await services.GetRequiredService<CommandHandlingService>().InitializeAsync();
services.GetRequiredService<MessageHandlingService>().Initialize();
await Task.Delay(-1);
await Task.Delay(Timeout.Infinite);
}
}
private Bot(string token) {
@ -56,9 +45,35 @@ namespace Vita3KBot {
try {
var bot = new Bot(File.ReadAllText("token.txt"));
bot.Start().GetAwaiter().GetResult();
} catch (IOException e) {
} catch (IOException) {
Console.WriteLine("Could not read from token.txt. Did you run `init <token>`?");
}
}
// Called by Discord.Net when it wants to log something.
private Task LogAsync(LogMessage log) {
Console.WriteLine(log.ToString());
return Task.CompletedTask;
}
private ServiceProvider ConfigureServices() {
var config = new DiscordSocketConfig
{
GatewayIntents = GatewayIntents.Guilds
| GatewayIntents.DirectMessages
| GatewayIntents.GuildMessages
| GatewayIntents.GuildMembers,
};
var client = new DiscordSocketClient(config);
return new ServiceCollection()
.AddSingleton(client)
.AddSingleton<CommandService>()
.AddSingleton<CommandHandlingService>()
.AddSingleton<MessageHandlingService>()
.BuildServiceProvider();
}
}
}

View File

@ -1,17 +0,0 @@
using System;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
namespace Vita3KBot.Commands.Attributes
{
public class InVoiceChannel : PreconditionAttribute
{
public override Task<PreconditionResult> CheckPermissionsAsync(ICommandContext context, CommandInfo command, IServiceProvider services)
{
return Task.FromResult((context.User as IGuildUser)?.VoiceChannel is null
? PreconditionResult.FromError("You must be in a voice channel before invoking this command")
: PreconditionResult.FromSuccess());
}
}
}

View File

@ -13,7 +13,7 @@ namespace Vita3KBot.Commands.Attributes {
if (RolesUtils.IsModerator(context, context.Guild as SocketGuild)) {
return Task.FromResult(PreconditionResult.FromSuccess());
} else {
return Task.FromResult(PreconditionResult.FromError("You lack the permissions to exectue this command"));
return Task.FromResult(PreconditionResult.FromError("You lack the permissions to execute this command"));
}
} else {
return Task.FromResult(PreconditionResult.FromError("You must be in a server to execute this command"));

View File

@ -1,590 +0,0 @@
using System.Threading;
using System.Collections.Concurrent;
using System;
using System.Linq;
using System.Threading.Tasks;
using Discord;
using Discord.Addons.Interactive;
using Discord.Commands;
using Victoria;
using Victoria.Enums;
using Victoria.EventArgs;
using Vita3KBot.Commands.Attributes;
namespace Vita3KBot.Commands
{
[Group("seek"), InVoiceChannel]
public class Seek : InteractiveBase
{
[Command, Name("seek")]
[Summary("Seeks to a particular position in the song (in sec)")]
public async Task SeekTask(int position)
{
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
await ReplyAsync("I need to be in a voice channel first");
return;
}
Console.WriteLine(
$"Track is seekable: {player.Track.CanSeek}\n" +
$"Now at: {player.Track.Position}" +
$"/{player.Track.Duration}");
if (player.Track.CanSeek)
{
Emoji emoji = new Emoji("✅");
await player.SeekAsync(TimeSpan.FromSeconds(position));
await Context.Message.AddReactionAsync(emoji);
}
else
{
await ReplyAndDeleteAsync("❌ Cant seek this track.");
}
}
}
[Group("volume"), InVoiceChannel]
[Alias("v")]
public class Volume : ModuleBase<SocketCommandContext>
{
[Command(RunMode = RunMode.Async), Name("volume")]
[Summary("Sets the volume for the current playing song 0-150")]
public async Task VolumeTask(ushort value)
{
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
await ReplyAsync("I need to be in a voice channel first");
return;
}
if (value < 0 || value > 150)
{
await ReplyAsync("Volume can only be set from 0 to 150");
return;
}
await player.UpdateVolumeAsync(value);
await ReplyAsync("Volume now is set to " + value + "/150");
}
[Command(RunMode = RunMode.Async), Name("volume")]
[Summary("Gets the current volume level")]
public async Task VolumeTask()
{
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
await ReplyAsync("I need to be in a voice channel first");
return;
}
await ReplyAsync("Volume is: " + player.Volume);
}
}
[Group("join"), InVoiceChannel]
public class Join : ModuleBase<SocketCommandContext>
{
[Command, Name("join")]
[Summary("Bot joins voice channel")]
public async Task JoinTask()
{
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
await ReplyAsync("I'm already in a voice channel");
}
else
{
player = await Bot.lavaNode.JoinAsync(voiceState.VoiceChannel, Context.Channel as ITextChannel);
await ReplyAsync($"Joined {voiceState.VoiceChannel}");
}
}
}
[Group("pause"), InVoiceChannel]
public class Pause : ModuleBase<SocketCommandContext>
{
[Command(RunMode = RunMode.Async), Name("pause")]
[Summary("Pauses the song")]
public async Task PauseTask()
{
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
await ReplyAsync("I need to be in a voice channel first");
return;
}
await player.PauseAsync();
await ReplyAsync("Paused");
}
}
[Group("resume"), InVoiceChannel]
[Alias("Unpause")]
public class Resume : ModuleBase<SocketCommandContext>
{
[Command(RunMode = RunMode.Async), Name("resume")]
[Summary("Resumes the song")]
public async Task ResumeTask()
{
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
await ReplyAsync("I need to be in a voice channel first");
return;
}
if (player.PlayerState == PlayerState.Playing)
{
await ReplyAsync("Already playing " + player.Track.Title);
}
else
{
await ReplyAsync($"Resumed {player.Track.Title}");
await player.ResumeAsync();
}
}
}
[Group("now playing"), InVoiceChannel]
[Alias("np")]
public class NowPlaying : ModuleBase<SocketCommandContext>
{
[Command(RunMode = RunMode.Async), Name("now playing")]
[Summary("Get the current playing song")]
public async Task NowPlayingTask()
{
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
await ReplyAsync("I need to be in a voice channel first");
return;
}
var playList = Context.Guild.Id.PlayList();
var my = player.Track.Title;
if (playList.Any()) my += "\nUp next: " + playList[0].Title;
var build = new EmbedBuilder
{
Title = "Now Playing",
Description = my,
Color = new Color(213, 0, 249)
}.Build();
await ReplyAsync(string.Empty, false, build);
}
}
[Group("clear"), InVoiceChannel]
public class Clear : ModuleBase<SocketCommandContext>
{
[Command, Name("clear")]
[Summary("Clears the queue")]
public async Task ClearTask()
{
Context.Guild.Id.PopAll();
await ReplyAsync("Queue cleared");
}
}
[Group("stop"), InVoiceChannel]
public class Stop : ModuleBase<SocketCommandContext>
{
[Command(RunMode = RunMode.Async), Name("stop")]
[Summary("Stops the current playing song.")]
public async Task StopTask()
{
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
await ReplyAsync("I need to be in a voice channel first");
return;
}
await player.StopAsync();
await ReplyAsync("✅ Stopped playing. Your queue is still intact though. Use `clear` to Destroy Queue");
}
}
[Group("disconnect"), InVoiceChannel]
[Alias("dc")]
public class Disconnect : ModuleBase<SocketCommandContext>
{
[Command(RunMode = RunMode.Async), Name("disconnect")]
[Summary("Disconnects bot from voice channel")]
public async Task LeaveTask()
{
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
await ReplyAsync("I'm not in a voice channel");
return;
}
if (player.PlayerState == PlayerState.Playing)
{
await player.StopAsync();
await ReplyAsync("✅ Stopped playing. Your queue is still intact though. Use `clear` to Destroy Queue");
}
await Bot.lavaNode.LeaveAsync(Bot.lavaNode.GetPlayer(Context.Guild).VoiceChannel);
}
}
[Group("queue"), InVoiceChannel]
[Alias("q")]
public class Queue : ModuleBase<SocketCommandContext>
{
[Command(RunMode = RunMode.Async), Name("queue")]
[Summary("Prints the current queue")]
public async Task QueueTask()
{
var my = string.Empty;
var p = Context.Guild.Id.PlayList();
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
await ReplyAsync("I need to be in a voice channel first");
return;
}
if (!p.Any() && (player.PlayerState != PlayerState.Playing))
{
await ReplyAsync("The Queue is Empty.");
}
else
{
if (player.PlayerState == PlayerState.Playing)
my += $"👉 [{player.Track.Title}]({player.Track.Url}) **{player.Track.Duration}**\n";
for (var i = 0; i < Math.Min(p.Count, 10); i++)
my += $"**{i + 1}**. [{p[i].Title}]({p[i].Url}) **{p[i].Duration}**\n";
var build = new EmbedBuilder
{
Title = "Current Queue",
Description = my,
Color = new Color(213, 0, 249),
Footer = new EmbedFooterBuilder
{
Text = p.Count + " songs in the queue"
}
}.Build();
await ReplyAsync(string.Empty, false, build);
}
}
}
[Group("skip"), InVoiceChannel]
public class Skip : InteractiveBase
{
[Command(RunMode = RunMode.Async), Name("skip")]
[Summary("Skips the current playing song.")]
public async Task SkipTask()
{
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
await ReplyAsync("I need to be in a voice channel first");
return;
}
var final = await ReplyAsync("🧐 Searching");
if (Context.Guild.Id.GetQueueCount() > 0)
{
// Context.Guild.Id.PopTrack();
var track = Context.Guild.Id.PopTrack();
var playing = new EmbedBuilder
{
Title = "Now Playing",
Description = track.Title,
Color = new Color(213, 0, 249)
}.Build();
// await player.StopAsync();
await player.PlayAsync(track);
await final.ModifyAsync(x =>
{
x.Embed = playing;
x.Content = null;
});
}
else
{
await final.ModifyAsync(x => x.Content = "Queue Empty");
}
}
}
[Group("play"), InVoiceChannel]
[Alias("p")]
public class Play : InteractiveBase
{
[Command(RunMode = RunMode.Async), Name("play")]
[Summary("Plays a song from one of sources")]
public async Task PlayTask([Remainder] string query)
{
var final = await ReplyAsync("🧐 Searching");
var voiceState = Context.User as IVoiceState;
LavaPlayer player;
if (Bot.lavaNode.HasPlayer(Context.Guild))
{
player = Bot.lavaNode.GetPlayer(Context.Guild);
}
else
{
player = await Bot.lavaNode.JoinAsync(voiceState.VoiceChannel, Context.Channel as ITextChannel);
}
var response = await Bot.lavaNode.SearchYouTubeAsync(query);
if (response.LoadStatus == LoadStatus.LoadFailed)
{
await final.ModifyAsync(x =>
{
x.Content = null;
x.Embed = new EmbedBuilder
{
Title = "Failed to load responses.",
Description =
"The url is not playable.",
Color = new Color(213, 0, 249),
Footer = new EmbedFooterBuilder
{
Text =
"To go specific youtube search try adding `ytsearch:`before your search string." +
" Eg: play ytsearch:Allen Walker"
}
}.Build();
});
return;
}
if (response.LoadStatus == LoadStatus.NoMatches)
{
await final.ModifyAsync(x =>
{
x.Content = null;
x.Embed = new EmbedBuilder
{
Title = "Failed to load responses.",
Description =
"The search found no such tracks",
Color = new Color(213, 0, 249),
Footer = new EmbedFooterBuilder
{
Text =
"To go specific soundcloud search try adding `scsearch:`before your search string." +
" Eg: play scsearch:Allen Walker"
}
}.Build();
});
return;
}
var allTracks = response.Tracks.ToList();
var tracks = response.LoadStatus == LoadStatus.PlaylistLoaded
? allTracks
: allTracks.Take(Math.Min(10, allTracks.Count)).ToList();
if (response.LoadStatus == LoadStatus.PlaylistLoaded)
{
foreach (var track in tracks) Context.Guild.Id.PushTrack(track);
if (player.PlayerState != PlayerState.Playing)
{
var lavalinkTrack = Context.Guild.Id.PopTrack();
await player.PlayAsync(lavalinkTrack);
await final.ModifyAsync(x =>
{
x.Embed = new EmbedBuilder
{
Description =
$"👉 **{lavalinkTrack.Title}** \nAdded {tracks.Count - 1} tracks to the queue",
Color = new Color(213, 0, 249),
Title = "Now Playing"
}.Build();
x.Content = null;
});
}
else
{
await final.ModifyAsync(x =>
{
x.Embed = null;
x.Content = $"Added **{tracks.Count}** songs to Queue.";
});
}
}
else
{
var my = string.Empty;
for (var i = 0; i < tracks.Count; i++)
my += $"{i + 1}. [{tracks[i].Title}]({tracks[i].Url}) **Duration: {tracks[i].Duration}**\n";
var build = new EmbedBuilder
{
Title = "Make your choice",
Description = my,
Color = new Color(213, 0, 249)
}.Build();
await final.ModifyAsync(x =>
{
x.Content = null;
x.Embed = build;
});
var reply = await NextMessageAsync();
if (!int.TryParse(reply.Content, out var good) || good > tracks.Count)
{
await final.ModifyAsync(x =>
{
x.Embed = null;
x.Content = "Invalid Response";
});
return;
}
var track = tracks[good - 1];
Context.Guild.Id.PushTrack(track);
if (player.PlayerState != PlayerState.Playing)
{
var lavalinkTrack = Context.Guild.Id.PopTrack();
await player.PlayAsync(lavalinkTrack);
await final.ModifyAsync(x =>
{
x.Embed = null;
x.Content = $"✅ Playing **{lavalinkTrack.Title}** now";
});
}
else
{
await final.ModifyAsync(x =>
{
x.Embed = null;
x.Content = $"✅ Added **{track.Title}** to Queue.";
});
}
}
}
}
public class MusicModule
{
private static readonly ConcurrentDictionary<ulong, CancellationTokenSource> _disconnectTokens =
new ConcurrentDictionary<ulong, CancellationTokenSource>();
public static async Task OnTrackStarted(TrackStartEventArgs arg)
{
if (!_disconnectTokens.TryGetValue(arg.Player.VoiceChannel.Id, out var value))
{
return;
}
if (value.IsCancellationRequested)
{
return;
}
value.Cancel();
}
private static async Task InitiateDisconnectAsync(LavaPlayer player, TimeSpan timeSpan)
{
if (!_disconnectTokens.TryGetValue(player.VoiceChannel.Id, out var value))
{
value = new CancellationTokenSource();
_disconnectTokens.TryAdd(player.VoiceChannel.Id, value);
}
else if (value.IsCancellationRequested)
{
_disconnectTokens.TryUpdate(player.VoiceChannel.Id, new CancellationTokenSource(), value);
value = _disconnectTokens[player.VoiceChannel.Id];
}
var isCancelled = SpinWait.SpinUntil(() => value.IsCancellationRequested, timeSpan);
if (isCancelled)
{
return;
}
await Bot.lavaNode.LeaveAsync(player.VoiceChannel);
}
public static async Task OnTrackEnded(TrackEndedEventArgs args)
{
var player = args.Player;
if (player.TextChannel.Guild.Id.GetQueueCount() > 0 && args.Reason == TrackEndReason.Finished)
{
var final = await player.TextChannel.SendMessageAsync("🧐 Playing the next song");
var track = player.TextChannel.Guild.Id.PopTrack();
var playing = new EmbedBuilder
{
Title = "Now Playing",
Description = track.Title,
Color = new Color(213, 0, 249)
}.Build();
await player.PlayAsync(track);
await final.ModifyAsync(x =>
{
x.Embed = playing;
x.Content = null;
});
return;
}
if (args.Reason != TrackEndReason.Replaced)
{
await InitiateDisconnectAsync(args.Player, TimeSpan.FromMinutes(2));
}
}
}
}

View File

@ -1,47 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Victoria;
namespace Vita3KBot
{
public static class QueueManager
{
private static readonly Dictionary<ulong, Queue<LavaTrack>> Queue =
new Dictionary<ulong, Queue<LavaTrack>>();
public static string PushTrack(this ulong guildId, LavaTrack track)
{
Queue.TryAdd(guildId, new Queue<LavaTrack>());
Queue[guildId].Enqueue(track);
return "Successfully added to queue.";
}
public static LavaTrack PopTrack(this ulong guildId)
{
Queue.TryAdd(guildId, new Queue<LavaTrack>());
if (!Queue[guildId].Any())
{
throw new InvalidOperationException("Queue empty");
}
return Queue[guildId].Dequeue();
}
public static void PopAll(this ulong guildId)
{
Queue.TryAdd(guildId, new Queue<LavaTrack>());
Queue[guildId].Clear();
}
public static List<LavaTrack> PlayList(this ulong guildId)
{
Queue.TryAdd(guildId, new Queue<LavaTrack>());
return Queue[guildId].ToList();
}
public static int GetQueueCount(this ulong guildId) {
return Queue[guildId].Count;
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.Reflection;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.WebSocket;
using Microsoft.Extensions.DependencyInjection;
namespace Vita3KBot.Services {
public class CommandHandlingService {
// Config
private const char Prefix = '-';
private const bool ShowStackTrace = false;
private readonly DiscordSocketClient _client;
private readonly CommandService _commands;
private readonly IServiceProvider _services;
// Called by Discord.Net when the bot receives a message.
private async Task HandleCommand(SocketMessage message) {
if (!(message is SocketUserMessage userMessage)) return;
var prefixStart = 0;
if (userMessage.HasCharPrefix(Prefix, ref prefixStart)) {
// Create Context and Execute Commands
var context = new SocketCommandContext(_client, userMessage);
await _commands.ExecuteAsync(context, prefixStart, _services);
}
}
// This event is triggered when a command finishes executing (whether successful or not)
private async Task CommandExecutedAsync(Optional<CommandInfo> command, ICommandContext context, IResult result) {
// Handle any errors.
if (!result.IsSuccess && result.Error != CommandError.UnknownCommand) {
if (ShowStackTrace && result.Error == CommandError.Exception
&& result is Discord.Commands.ExecuteResult execution) {
await context.Channel.SendMessageAsync(
Utils.Code(execution.Exception.Message + "\n\n" + execution.Exception.StackTrace));
} else {
var currentCommand = command.GetValueOrDefault();
await context.Channel.SendMessageAsync(
"Halt! We've hit an error." + Utils.Code(result.ErrorReason));
if (result.ErrorReason == "The input text has too few parameters.") {
await context.Channel.SendMessageAsync($"Try `-help {currentCommand.Name}` for the command's usage");
}
}
}
}
// Initializes the Message Handler, subscribe to events, etc.
public async Task InitializeAsync() {
_client.MessageReceived += HandleCommand;
_commands.CommandExecuted += CommandExecutedAsync;
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
}
public CommandHandlingService(IServiceProvider services) {
_client = services.GetRequiredService<DiscordSocketClient>();
_commands = services.GetRequiredService<CommandService>();
_services = services;
}
}
}

View File

@ -1,27 +1,17 @@
using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using Discord;
using Discord.Commands;
using Discord.Addons.Interactive;
using Discord.WebSocket;
using Vita3KBot.Database;
using Microsoft.Extensions.DependencyInjection;
using Victoria;
namespace Vita3KBot {
public class MessageHandler {
// Config
private const char Prefix = '-';
private const bool ShowStackTrace = true;
namespace Vita3KBot.Services {
public class MessageHandlingService {
private readonly DiscordSocketClient _client;
private readonly CommandService _commands;
private readonly ServiceProvider _services;
private readonly IServiceProvider _services;
// Called for each user message. Use it to collect stats, or silently observe stuff, etc.
private static async Task MonitorMessage(SocketUserMessage message) {
@ -49,7 +39,7 @@ namespace Vita3KBot {
// User join event
private async Task HandleUserJoinedAsync(SocketGuildUser j_user) {
if (j_user.IsBot || j_user.IsWebhook) return;
var dmChannel = await j_user.GetOrCreateDMChannelAsync();
var dmChannel = await j_user.CreateDMChannelAsync();
await dmChannel.SendMessageAsync("Welcome to Vita3k! \n " +
"Please read the server <#415122640051896321> and <#486173784135696418> thoroughly before posting. \n " + "\n " +
"For the latest up-to-date guide on game installation and hardware requirements, please visit <https://vita3k.org/quickstart.html> \n " + "\n " +
@ -57,66 +47,22 @@ namespace Vita3KBot {
"For current issues with the emulator visit the GitHub repo at https://github.com/Vita3K/Vita3K/issues");
}
// Called by Discord.Net when it wants to log something.
private static Task Log(LogMessage message) {
Console.WriteLine(message.Message);
return Task.CompletedTask;
}
// Called by Discord.Net when the bot receives a message.
private async Task CheckMessage(SocketMessage message) {
if (!(message is SocketUserMessage userMessage)) return;
await MonitorMessage(userMessage);
var prefixStart = 0;
if (userMessage.HasCharPrefix(Prefix, ref prefixStart)) {
// Create Context and Execute Commands
var context = new SocketCommandContext(_client, userMessage);
var result = await _commands.ExecuteAsync(context, prefixStart, _services);
// Handle any errors.
if (!result.IsSuccess && result.Error != CommandError.UnknownCommand) {
if (ShowStackTrace && result.Error == CommandError.Exception
&& result is ExecuteResult execution) {
await userMessage.Channel.SendMessageAsync(
Utils.Code(execution.Exception.Message + "\n\n" + execution.Exception.StackTrace));
} else {
var currentCommand = _commands.Commands.Where(s => {
return userMessage.Content.Contains(s.Name);
}).First();
await userMessage.Channel.SendMessageAsync(
"Halt! We've hit an error." + Utils.Code(result.ErrorReason));
if (result.ErrorReason == "The input text has too few parameters.") {
await userMessage.Channel.SendMessageAsync($"Try `-help {currentCommand.Name}` for the command's usage");
}
}
}
}
}
// Initializes the Message Handler, subscribe to events, etc.
public async Task Init() {
_client.Log += Log;
public void Initialize() {
_client.MessageReceived += CheckMessage;
_client.UserJoined += HandleUserJoinedAsync;
await _commands.AddModulesAsync(Assembly.GetEntryAssembly(), _services);
}
public MessageHandler(DiscordSocketClient client) {
_client = client;
_commands = new CommandService();
_services = new ServiceCollection()
.AddSingleton(_client)
.AddSingleton(_commands)
.AddSingleton(new InteractiveService(_client))
.AddLavaNode(x => {
x.SelfDeaf = false;
})
.BuildServiceProvider();
public MessageHandlingService(IServiceProvider services) {
_client = services.GetRequiredService<DiscordSocketClient>();
_services = services;
}
}
}

View File

@ -4,21 +4,20 @@
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<TargetFramework>net6.0</TargetFramework>
<RootNamespace>Vita3KBot</RootNamespace>
<LangVersion>latest</LangVersion>
<Configurations>Debug;Release;Testing</Configurations>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Addons.Interactive" Version="2.0.0" />
<PackageReference Include="Discord.Net" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.8">
<PackageReference Include="Discord.Net" Version="3.6.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="6.0.4">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.8" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="6.0.4" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Octokit" Version="0.48.0" />
<PackageReference Include="Victoria" Version="5.1.8" />
</ItemGroup>