mirror of
https://github.com/Vita3K/Vita3KBot.git
synced 2024-11-26 23:10:29 +00:00
feat: upgrade to discord.net v3 and remove unused music commands
This commit is contained in:
parent
a9bcd3800c
commit
aaf7ebfc47
2
.gitignore
vendored
2
.gitignore
vendored
@ -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
|
||||
|
@ -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(); }
|
||||
|
||||
|
@ -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();
|
||||
|
||||
await _client.LoginAsync(TokenType.Bot, _token);
|
||||
await _client.StartAsync();
|
||||
var client = services.GetRequiredService<DiscordSocketClient>();
|
||||
client.Log += LogAsync;
|
||||
services.GetRequiredService<CommandService>().Log += LogAsync;
|
||||
|
||||
_client.Ready += async () => {
|
||||
if (!lavaNode.IsConnected)
|
||||
await lavaNode.ConnectAsync();
|
||||
};
|
||||
lavaNode.OnTrackEnded += MusicModule.OnTrackEnded;
|
||||
lavaNode.OnTrackStarted += MusicModule.OnTrackStarted;
|
||||
await client.LoginAsync(TokenType.Bot, _token);
|
||||
await client.StartAsync();
|
||||
|
||||
await Task.Delay(-1);
|
||||
await services.GetRequiredService<CommandHandlingService>().InitializeAsync();
|
||||
services.GetRequiredService<MessageHandlingService>().Initialize();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -13,11 +13,11 @@ 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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,4 +58,4 @@ namespace Vita3KBot.Commands {
|
||||
await ReplyAsync("Term blacklisted successfully").ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,4 +109,4 @@ namespace Vita3KBot.Commands {
|
||||
await ReplyAsync(embed: helpEmbed.Build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
67
Vita3KBot/Services/CommandHandlingService.cs
Normal file
67
Vita3KBot/Services/CommandHandlingService.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user